有了基础界面,下一步是让游戏真正运转。这一讲围绕 CodeBreaker 的数据模型、交互状态与匹配算法,练习如何组织代码并解释自己的设计。
阅读目录 · 10 节
学习重心。 把“会写代码”升级为“能组织结构并解释设计”。
核心能力。 值类型思维、状态驱动 UI、可维护算法实现。
项目主线。 围绕 CodeBreaker:从 Model 到交互再到反馈计算。
课后方向。 模式扩展、参数化重构、视觉风格升级。
建议阅读方式: 先看每节“结论”,再看代码与图片。遇到疑问时回到第一讲对照基础概念。
1) 架构设计回顾:MVVM 模式
1. 把界面和规则拆开,是可维护性的起点
- Model: 数据与规则(例如密码、判定逻辑、历史记录)。
- View: 界面展示(颜色块、按钮、布局)。
- ViewModel: 连接 View 与 Model,处理“用户操作 -> 状态更新”。
本项目规模不大,可以简化实现,但思维上仍要坚持关注点分离。

2) Swift 类型系统复习:Struct / Class、Enum / Optional
2. Struct vs Class:值语义与引用语义
struct 是值类型,赋值时拷贝;class 是引用类型,多个变量可指向同一对象。能用 Struct 时优先 Struct,尤其适合状态与数据模型。

swift
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution: Resolution
var interlaced: Bool
init(resolution: Resolution, interlaced: Bool) {
self.resolution = resolution
self.interlaced = interlaced
}
}3. Enum 与 Optional:表达状态,不赌运气
枚举不仅能列选项,还能携带关联值。Optional 代表“可能为空”,请优先 if let 安全解包,避免强制解包崩溃。

swift
enum Menu {
case hamburger
case coke(sugar: Int)
}
if let safeValue = maybeValue {
print(safeValue)
}3) 从函数到闭包(Closure)
4. 闭包简写不是炫技,是降低噪音
在不损失可读性的前提下,闭包可逐步简化:完整参数写法 -> 类型推断 -> $0 隐式参数。

swift
// 完整写法
let exactCount1 = matches.filter { match in
match == .exact
}.count
// 简写
let exactCount2 = matches.filter { $0 == .exact }.count4) CodeBreaker Model 设计
本讲把游戏逻辑收敛到 Model,View 负责展示,避免把业务细节堆到界面层。
swift
typealias Peg = Color
struct CodeBreaker {
var masterCode = Code(kind: .master)
var guessCode = Code(kind: .guess)
var attempts: [Code] = []
let pegChoices: [Peg] = [.blue, .red, .green, .yellow]
}
struct Code {
var pegs: [Peg] = Array(repeating: .clear, count: 4)
var kind: Kind
enum Kind: Equatable {
case master
case guess
case attempt(matches: [Match])
}
}5) 交互逻辑:mutating 与 @State
5. 数据要改得动,界面要跟得上
mutating:允许值类型方法修改自身属性。@State:告诉 SwiftUI 这是会驱动界面刷新的状态。

swift
mutating func changeGuessPeg(at index: Int) {
let existingPeg = guessCode.pegs[index]
if let currentIndex = pegChoices.firstIndex(of: existingPeg) {
let nextIndex = (currentIndex + 1) % pegChoices.count
guessCode.pegs[index] = pegChoices[nextIndex]
} else {
guessCode.pegs[index] = pegChoices.first ?? .clear
}
}6) 核心算法:匹配逻辑(Exact / Inexact)
6. 两阶段判定:先精确,再模糊
先处理位置和颜色都正确(Exact),并从待匹配序列中移除;再处理颜色对但位置错(Inexact)。删除元素时使用倒序遍历,避免索引错乱。

swift
func match(against otherCode: Code) -> [Match] {
var results = Array(repeating: Match.noMatch, count: pegs.count)
var pegsToMatch = otherCode.pegs
for index in pegs.indices.reversed() {
if pegsToMatch[index] == pegs[index] {
results[index] = .exact
pegsToMatch.remove(at: index)
}
}
// 第二阶段:在剩余元素中处理 inexact
return results
}7) UI 完善与工程化细节
- 历史记录使用
ScrollView,避免内容超出屏幕。 - 用
indices.reversed()控制最近一次尝试显示顺序。 - 提交动作配合
withAnimation提升反馈感。 - 透明区域用
.contentShape(Rectangle())提高可点性。 - 随机初始化题目,提升重复游玩体验。
8) 课后任务(必做 + 挑战)
必做任务。
- 复现课堂 Lecture 1-4 功能,确保可运行。
- 忽略无效猜测(空输入、重复尝试)。
- 新增 Restart 并随机 Peg 数量(3~6)。
- 支持颜色模式与 Emoji 模式随机切换。
界面任务。
- 让 Emoji 在不同 Peg 数量下可读(缩放策略)。
- 加入 iOS 风格液态玻璃视觉(Material + 高光)。
- 深色模式下保持边界与对比度清晰。
挑战任务。
- 扩展颜色库并做字符串到颜色映射。
- 主题系统(车辆 / 脸部 / 大地色等)。
- 界面显示当前主题名称。
工程要求。
- 代码命名清晰、无警告、可读性优先。
- 至少使用一次
static成员。 - Model 层保持 UI 无关,减少
SwiftUI依赖。
9) 实现约束与检查清单
- 状态建模优先
enum,不要散落魔法数字。 - ForEach 需稳定 id(
indices或Identifiable)。 - 涉及可变状态时,明确
mutating与@State边界。 - 算法代码先追求正确性,再谈短写法。
第二讲核心收获
把讲义中的概念用到自己的项目里,在修改、运行与解释代码的过程中巩固理解。
