MeetXCode.swift
import SwiftUI
@main
struct MeetXCodeApp: App {
var body: some Scene {
WindowGroup {
ContentView3()
}
}
}ContentView3.swift
import SwiftUI
struct ContentView3: View {
var body: some View {
VStack{
peges(colors: [Color.green, Color.blue, Color.blue, Color.red])
peges(colors: [Color.green, Color.blue, Color.red, Color.red])
peges(colors: [Color.yellow, Color.blue, Color.yellow, Color.green])
peges(colors: [Color.yellow, Color.green, Color.yellow, Color.green])
}.padding()
}
@ViewBuilder
func peges(colors: Array<Color>)-> some View {
HStack{
ForEach(colors.indices, id: \.self) { index in
RoundedRectangle(cornerRadius: 10).aspectRatio(1, contentMode: .fit)
.foregroundStyle(colors[index])
}
MatchMarkers3(matches: [Match3.exact, Match3.inexact, Match3.nomatch, Match3.exact])
}
}
}
#Preview {
ContentView3()
}MatchMarkers3.swift
import SwiftUI
enum Match3 {
case nomatch
case exact
case inexact
}
struct MatchMarkers3: View {
// var matches: [Match]
var matches: Array<Match3>
var body: some View {
HStack {
VStack {
matchMarker(peg: 0)
matchMarker(peg: 1)
}
VStack {
matchMarker(peg: 2)
matchMarker(peg: 3)
}
}
}
func isNotNomatch(matchItem: Match3)-> Bool {
return matchItem != Match3.nomatch
}
func matchMarker(peg: Int) -> some View {
let exactCount = self.matches.count(where: { matchItem in matchItem == Match3.exact})
// 传递函数写法:
// let foundCount = self.matches.count(where: isNotNomatch)
// foundCount 改为内联写法:
// let foundCount = self.matches.count(where: {(matchItem: Match) -> Bool in
// matchItem != Match.nomatch
// })
// 极限简化后写法用 $number 代表函数参数
// let foundCount = matches.count(where: {$0 != Match.nomatch})
// 终极简化
let foundCount = matches.count{ $0 != Match3.nomatch }
// 普通模式下 如果 exactCount > peg 则填充为黑色
// 如果 foundCount > peg 边框为黑色当白钉子
// 填充为白 边框为白则表示没有钉子
return Circle().fill(exactCount > peg ? Color.primary : Color.clear)
.strokeBorder(foundCount > peg ? Color.primary : Color.clear, lineWidth: 2)
.aspectRatio(1, contentMode: ContentMode.fit)
}
}
#Preview {
// exactCount为1 foundCount为3 一个黑钉 两个白钉
MatchMarkers3(matches: [Match3.exact, Match3.inexact, Match3.nomatch, Match3.inexact])
}import Foundation基本准备就绪
CodeBreaker.swift
import SwiftUI
// 这里直接用了Color,其实与UI的东西解耦才更好,比如用字符串表示颜色
typealias Peg = Color
struct CodeBreaker {
// 答案
var masterCode: Code = Code(kind: Code.Kind.master)
// 玩家猜
var guess: Code = Code(kind: Code.Kind.guess)
// 猜过的历史结果
var attempts: [Code] = []
// 能选的钉子
let pegChoices: [Peg]
// 默认参数构造函数
init(pegChoices: [Peg] = [.green, .red, .blue, .yellow]) {
self.pegChoices = pegChoices
masterCode.randomize(from: pegChoices)
print(masterCode)
}
mutating func attemptGuess() {
var attempt = guess
attempt.kind = Code.Kind.attempt(guess.match(against: masterCode))
attempts.append(attempt)
}
mutating func changeGuessPeg(at index: Int) {
let existingPeg = guess.pegs[index]
if let indexOfExistingPegInPegChoics = pegChoices.firstIndex(of: existingPeg) {
let newPeg = pegChoices[(indexOfExistingPegInPegChoics + 1) % pegChoices.count]
guess.pegs[index] = newPeg
} else {
guess.pegs[index] = pegChoices.first ?? Code.missing
}
}
}
struct Code {
var kind: Kind // Kind?
// .clear 触控会消失 无法触控回调
var pegs: [Peg] = Array(repeating: Code.missing, count: 4)
static let missing: Peg = .clear
enum Kind: Equatable {
case master
case guess
case attempt([Match3])
case unknow
}
var matches: [Match3] {
switch self.kind {
case .attempt(let matches): return matches
default: return []
}
}
mutating func randomize(from pegChoices: [Peg]) {
for index in pegChoices.indices {
pegs[index] = pegChoices.randomElement() ?? Code.missing
}
}
func match(against otherCode: Code) -> [Match3] {
var results: [Match3] = Array(repeating: Match3.nomatch, count: pegs.count)
var pegsToMatch = otherCode.pegs
for index in pegs.indices.reversed() {
if pegsToMatch.count > index, pegsToMatch[index] == pegs[index] {
results[index] = Match3.exact
pegsToMatch.remove(at: index)
}
}
for index in pegs.indices {
if results[index] != Match3.exact {
if let matchIndex = pegsToMatch.firstIndex(of: pegs[index]) {
results[index] = Match3.inexact
pegsToMatch.remove(at: matchIndex)
}
}
}
return results
}
}MatchMarker3.swift
import SwiftUI
enum Match3 {
case nomatch
case exact
case inexact
}
struct MatchMarkers3: View {
// var matches: [Match]
var matches: Array<Match3>
var body: some View {
HStack {
VStack {
matchMarker(peg: 0)
matchMarker(peg: 1)
}
VStack {
matchMarker(peg: 2)
matchMarker(peg: 3)
}
}
}
func isNotNomatch(matchItem: Match3)-> Bool {
return matchItem != Match3.nomatch
}
func matchMarker(peg: Int) -> some View {
let exactCount = self.matches.count(where: { matchItem in matchItem == Match3.exact})
// 传递函数写法:
// let foundCount = self.matches.count(where: isNotNomatch)
// foundCount 改为内联写法:
// let foundCount = self.matches.count(where: {(matchItem: Match) -> Bool in
// matchItem != Match.nomatch
// })
// 极限简化后写法用 $number 代表函数参数
// let foundCount = matches.count(where: {$0 != Match.nomatch})
// 终极简化
let foundCount = matches.count{ $0 != Match3.nomatch }
// 普通模式下 如果 exactCount > peg 则填充为黑色
// 如果 foundCount > peg 边框为黑色当白钉子
// 填充为白 边框为白则表示没有钉子
return Circle().fill(exactCount > peg ? Color.primary : Color.clear)
.strokeBorder(foundCount > peg ? Color.primary : Color.clear, lineWidth: 2)
.aspectRatio(1, contentMode: ContentMode.fit)
}
}
#Preview {
// exactCount为1 foundCount为3 一个黑钉 两个白钉
MatchMarkers3(matches: [Match3.exact, Match3.inexact, Match3.nomatch, Match3.inexact])
}ContentView3.swift
import SwiftUI
struct ContentView3: View {
@State var game = CodeBreaker(pegChoices: [.green, .red, .blue, .yellow]) // 自动推断类型
var body: some View {
VStack{
view(for: game.masterCode)
ScrollView {
view(for: game.guess)
ForEach(game.attempts.indices.reversed(), id: \.self) { index in
view(for: game.attempts[index])
}
}
}.padding()
}
var guessButton: some View {
Button("Guess") {
withAnimation {
game.attemptGuess()
}
}.font(.system(size: 80))
.minimumScaleFactor(0.1)
}
@ViewBuilder
func view(for code: Code)-> some View {
HStack{
ForEach(code.pegs.indices, id: \.self) { index in
RoundedRectangle(cornerRadius: 10)
.overlay {
if code.pegs[index] == Code.missing {
RoundedRectangle(cornerRadius: 10).strokeBorder(Color.gray)
}
}
.contentShape(Rectangle()) // 为了解决在.clear下触控
.aspectRatio(1, contentMode: .fit)
.foregroundStyle(code.pegs[index])
.onTapGesture {
if code.kind == Code.Kind.guess {
game.changeGuessPeg(at: index)
}
}
}
MatchMarkers3(matches: code.matches).overlay {
if code.kind == Code.Kind.guess {
guessButton
}
}
}
}
}
#Preview {
ContentView3()
}