在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、游戏概述

24点是一款经典的数学益智游戏,玩家需要使用4个数字通过四则运算(加、减、乘、除)得到结果24。本项目基于HarmonyOS ArkTS技术栈实现,采用深蓝色背景配金色主题的视觉设计,包含20个难度递进的关卡。

1.1 游戏规则

  • 游戏开始时,系统随机生成4个数字(1-9或更大范围)
  • 玩家使用这4个数字,通过加(+)、减(-)、乘(×)、除(÷)运算组合
  • 可以使用括号改变运算顺序
  • 每个数字必须使用且只能使用一次
  • 运算结果等于24即为通关

1.2 关卡设计

难度等级 数字范围 运算复杂度 关卡范围
入门 1~9 基础四则运算 1-5
简单 1~13 包含两位数 6-10
中等 包含分数 需要分数运算 11-15
困难 复杂运算 需要多步复杂运算 16-20

1.3 技术架构

┌─────────────────────────────────────────────────────────────┐
│                      24点游戏架构                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────┐      ┌──────────────────┐            │
│  │ TwentyFour       │──────▶│ TwentyFour       │            │
│  │ LevelPage.ets    │      │ Page.ets         │            │
│  │ (关卡选择页面)    │      │ (游戏主页面)      │            │
│  └──────────────────┘      └──────────┬────────┘            │
│                                       │                     │
│                                       ▼                     │
│  ┌────────────────────────────────────────────────────────┐ │
│  │              ExpressionEvaluator                       │ │
│  │  (递归下降表达式解析器)                                 │ │
│  │  - evaluate(): 表达式求值                              │ │
│  │  - parseAddSub(): 解析加减运算                          │ │
│  │  - parseMulDiv(): 解析乘除运算                          │ │
│  │  - parsePrimary(): 解析数字和括号                       │ │
│  └────────────────────────────────────────────────────────┘ │
│                                       │                     │
│                                       ▼                     │
│  ┌────────────────────────────────────────────────────────┐ │
│  │                    AppState.ets                        │ │
│  │  - twentyFourCompletedLevels: Array<number>            │ │
│  │  - twentyFourLevelTimes: Array<number>                 │ │
│  └────────────────────────────────────────────────────────┘ │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、关卡选择页面实现

2.1 页面结构

关卡选择页面采用标准的分类展示模式,将20个关卡分为4个难度类别,每个类别展示5个关卡。页面顶部包含返回按钮、标题和解锁按钮,中间区域展示分类关卡网格,底部预留60px的背景覆盖区域。

2.2 数据模型设计

class LevelInfo {
  level: number = 0;
  mode: string = '';
  label: string = '';

  constructor(level: number, mode: string, label: string) {
    this.level = level;
    this.mode = mode;
    this.label = label;
  }
}

class Category {
  name: string = '';
  description: string = '';
  color: string = '';
  levels: Array<LevelInfo> = [];

  constructor(name: string, description: string, color: string) {
    this.name = name;
    this.description = description;
    this.color = color;
    this.levels = [];
  }
}

设计说明

  • LevelInfo类封装了关卡的核心信息:关卡编号、游戏模式、显示标签
  • Category类组织了难度分类:名称、描述、主题色和所属关卡列表
  • 通过面向对象的方式管理关卡数据,便于扩展和维护

2.3 分类初始化逻辑

private initCategories(): void {
  let cat1: Category = new Category('入门', '1~9 数字', '#32CD32');
  for (let i = 1; i <= 5; i++) {
    cat1.levels.push(new LevelInfo(i, 'easy', '入门'));
  }

  let cat2: Category = new Category('简单', '1~13 数字', '#00CED1');
  for (let i = 6; i <= 10; i++) {
    cat2.levels.push(new LevelInfo(i, 'normal', '简单'));
  }

  let cat3: Category = new Category('中等', '包含分数', '#FFD700');
  for (let i = 11; i <= 15; i++) {
    cat3.levels.push(new LevelInfo(i, 'medium', '中等'));
  }

  let cat4: Category = new Category('困难', '复杂运算', '#FF6347');
  for (let i = 16; i <= 20; i++) {
    cat4.levels.push(new LevelInfo(i, 'hard', '困难'));
  }

  this.categories = [cat1, cat2, cat3, cat4];
}

设计要点

  • 每个难度类别的数字范围递增:1~9 → 1~13 → 包含分数 → 复杂运算
  • 主题色根据难度递增变化:绿色→青色→金色→红色,直观区分难度
  • 使用循环批量创建关卡信息,减少重复代码

三、游戏主页面实现

3.1 状态管理

@Entry
@Component
struct TwentyFourPage {
  @State isPlaying: boolean = false;
  @State isGameWon: boolean = false;
  @State currentLevel: number = 1;
  @State numbers: Array<number> = [];
  @State expression: string = '';
  @State result: number = 0;
  @State score: number = 0;
  @State timerSeconds: number = 0;
  @State bestTime: number = 0;
  private timerId: number = 0;

状态变量说明

变量名 类型 作用
isPlaying boolean 游戏是否进行中
isGameWon boolean 是否通关
currentLevel number 当前关卡编号
numbers Array<number> 随机生成的4个数字
expression string 用户输入的表达式
result number 表达式计算结果
score number 当前分数
timerSeconds number 计时秒数
bestTime number 最佳通关时间
timerId number 计时器标识

3.2 游戏开始逻辑

private startGame(): void {
  this.isPlaying = true;
  this.score = 0;
  this.timerSeconds = 0;
  this.numbers = [];
  this.expression = '';
  this.result = 0;
  for (let i = 0; i < 4; i++) {
    this.numbers.push(Math.floor(Math.random() * 9) + 1);
  }
  this.startTimer();
}

游戏初始化流程

  1. 设置游戏状态为进行中
  2. 重置分数、计时器、数字数组、表达式和结果
  3. 随机生成4个1-9的数字
  4. 启动计时器

3.3 核心技术:递归下降表达式解析器

24点游戏的核心挑战在于实现一个能够正确解析和计算数学表达式的解析器。本项目采用递归下降解析器(Recursive Descent Parser)来处理表达式求值。

3.3.1 解析器类设计
class ExpressionEvaluator {
  private expression: string = '';
  private pos: number = 0;

  evaluate(expr: string): number {
    this.expression = expr;
    this.pos = 0;
    let result: number = this.parseAddSub();
    if (this.pos < this.expression.length) {
      return 0;
    }
    return result;
  }

设计说明

  • expression:存储待解析的表达式字符串
  • pos:当前解析位置指针
  • evaluate:公共入口方法,初始化解析器并开始解析
3.3.2 运算符优先级处理

递归下降解析器通过分层解析来处理运算符优先级:

优先级层次:
┌─────────────────────────────────────┐
│         parseAddSub()               │  ← 第一层:处理加减运算(优先级最低)
│  ┌───────────────────────────────┐  │
│  │       parseMulDiv()           │  │  ← 第二层:处理乘除运算(优先级较高)
│  │  ┌─────────────────────────┐  │  │
│  │  │    parsePrimary()       │  │  │  ← 第三层:处理数字和括号(优先级最高)
│  │  └─────────────────────────┘  │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘
3.3.3 加减运算解析
private parseAddSub(): number {
  let left: number = this.parseMulDiv();
  while (this.pos < this.expression.length) {
    let op: string = this.expression.charAt(this.pos);
    if (op === '+') {
      this.pos++;
      left = left + this.parseMulDiv();
    } else if (op === '-') {
      this.pos++;
      left = left - this.parseMulDiv();
    } else {
      break;
    }
  }
  return left;
}

解析逻辑

  1. 先解析左侧的乘除表达式(调用parseMulDiv
  2. 循环检查当前位置是否为加减运算符
  3. 如果是加号,解析右侧表达式并执行加法
  4. 如果是减号,解析右侧表达式并执行减法
  5. 如果不是加减运算符,退出循环并返回结果
3.3.4 乘除运算解析
private parseMulDiv(): number {
  let left: number = this.parsePrimary();
  while (this.pos < this.expression.length) {
    let op: string = this.expression.charAt(this.pos);
    if (op === '*') {
      this.pos++;
      left = left * this.parsePrimary();
    } else if (op === '/') {
      this.pos++;
      let right: number = this.parsePrimary();
      if (right === 0) {
        return 0;
      }
      left = left / right;
    } else {
      break;
    }
  }
  return left;
}

解析逻辑

  1. 先解析左侧的基本表达式(调用parsePrimary
  2. 循环检查当前位置是否为乘除运算符
  3. 如果是乘号,解析右侧表达式并执行乘法
  4. 如果是除号,解析右侧表达式并执行除法(注意除数不能为0)
  5. 如果不是乘除运算符,退出循环并返回结果
3.3.5 基本表达式解析
private parsePrimary(): number {
  this.skipWhitespace();
  if (this.pos >= this.expression.length) {
    return 0;
  }
  let ch: string = this.expression.charAt(this.pos);
  if (ch === '(') {
    this.pos++;
    let result: number = this.parseAddSub();
    if (this.pos < this.expression.length && this.expression.charAt(this.pos) === ')') {
      this.pos++;
    }
    return result;
  }
  if (ch >= '0' && ch <= '9') {
    let num: number = 0;
    while (this.pos < this.expression.length && this.expression.charAt(this.pos) >= '0' && this.expression.charAt(this.pos) <= '9') {
      num = num * 10 + (this.expression.charCodeAt(this.pos) - 48);
      this.pos++;
    }
    return num;
  }
  return 0;
}

解析逻辑

  1. 跳过空白字符
  2. 如果遇到左括号(,递归调用parseAddSub解析括号内的表达式
  3. 如果遇到数字,逐位解析得到完整数字值
  4. 其他情况返回0
3.3.6 空白字符处理
private skipWhitespace(): void {
  while (this.pos < this.expression.length && (this.expression.charAt(this.pos) === ' ' || this.expression.charAt(this.pos) === '\t')) {
    this.pos++;
  }
}

设计说明

  • 跳过空格和制表符,提高表达式输入的灵活性
  • 允许用户在表达式中添加空格分隔

3.4 表达式求值流程

private handleOperator(op: string): void {
  if (op === 'C') {
    this.expression = '';
    this.result = 0;
  } else if (op === '=') {
    let evalExpr: string = this.expression.replace('×', '*').replace('÷', '/');
    let evaluator: ExpressionEvaluator = new ExpressionEvaluator();
    this.result = evaluator.evaluate(evalExpr);
    if (this.result === 24) {
      // 通关处理逻辑...
    }
  } else {
    this.expression += op;
  }
}

求值流程

  1. 用户点击运算符按钮
  2. 如果是C,清空表达式和结果
  3. 如果是=,执行表达式求值:
    • ×替换为*÷替换为/
    • 创建解析器实例并调用evaluate方法
    • 如果结果等于24,触发通关逻辑
  4. 其他运算符直接追加到表达式

3.5 通关验证逻辑

if (this.result === 24) {
  this.score += 100;
  this.stopTimer();
  let completedLevels: Array<number> = appState.twentyFourCompletedLevels;
  let exists: boolean = false;
  for (let i = 0; i < completedLevels.length; i++) {
    if (completedLevels[i] === this.currentLevel) {
      exists = true;
      break;
    }
  }
  if (!exists) {
    completedLevels.push(this.currentLevel);
    completedLevels.sort((a: number, b: number) => a - b);
    appState.twentyFourCompletedLevels = completedLevels;
  }
  let levelTimes: Array<number> = appState.twentyFourLevelTimes;
  while (levelTimes.length <= this.currentLevel) {
    levelTimes.push(0);
  }
  if (levelTimes[this.currentLevel] === 0 || this.timerSeconds < levelTimes[this.currentLevel]) {
    levelTimes[this.currentLevel] = this.timerSeconds;
    appState.twentyFourLevelTimes = levelTimes;
    this.bestTime = this.timerSeconds;
  }
  this.isPlaying = false;
  this.isGameWon = true;
}

通关处理流程

  1. 检查计算结果是否等于24
  2. 如果通关,增加分数并停止计时器
  3. 更新全局通关进度和最佳时间记录
  4. 设置isGameWon为true,显示通关界面

四、UI界面设计

4.1 页面布局结构

build() {
  Column({ space: 16 }) {
    Row() {
      Button('← 返回')
        .fontSize(14)
        .fontColor('#FFD700')
        .backgroundColor('transparent')
        .borderColor('#FFD700')
        .borderWidth(1)
        .padding({ left: 12, right: 12 })
        .onClick(() => {
          this.stopTimer();
          router.back();
        })

      Text('24点')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFD700')

      Column({ space: 2 }) {
        Text('第' + this.currentLevel + '关')
          .fontSize(12)
          .fontColor('#87CEEB')
        Text('分数: ' + this.score)
          .fontSize(10)
          .fontColor('#87CEEB')
        Text('最佳: ' + this.formatTime(this.bestTime))
          .fontSize(10)
          .fontColor('#87CEEB')
      }
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 20, right: 20 })
    .margin({ top: 40 })

    Text(this.formatTime(this.timerSeconds))
      .fontSize(32)
      .fontColor('#87CEEB')
      .fontWeight(FontWeight.Bold)

    if (!this.isPlaying && !this.isGameWon) {
      Button('开始游戏')
        .fontSize(18)
        .fontColor('#0A192F')
        .backgroundColor('#FFD700')
        .borderRadius(12)
        .padding({ left: 40, right: 40, top: 12, bottom: 12 })
        .onClick(() => this.startGame())
    } else if (this.isGameWon) {
      Column({ space: 20 }) {
        Text('🎉 恭喜通关!')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFD700')

        Text('第' + this.currentLevel + '关完成')
          .fontSize(18)
          .fontColor('#87CEEB')

        Text('用时: ' + this.formatTime(this.timerSeconds))
          .fontSize(16)
          .fontColor('#FFFFFF')

        if (this.timerSeconds === this.bestTime) {
          Text('🏆 新纪录!')
            .fontSize(16)
            .fontColor('#FFD700')
        }

        Button('下一关')
          .fontSize(18)
          .fontColor('#0A192F')
          .backgroundColor('#FFD700')
          .borderRadius(12)
          .padding({ left: 40, right: 40, top: 12, bottom: 12 })
          .onClick(() => router.back())
      }
    } else {
      Column() {
        Text('用 4 个数字和 + - × ÷ 算出 24')
          .fontSize(14)
          .fontColor('#87CEEB')

        Row({ space: 12 }) {
          ForEach(this.numbers, (num: number, index: number) => {
            Button(num.toString())
              .fontSize(24)
              .fontColor('#FFFFFF')
              .backgroundColor('rgba(255, 215, 0, 0.1)')
              .borderColor('#FFD700')
              .borderWidth(1)
              .borderRadius(8)
              .width(60)
              .height(60)
              .onClick(() => this.selectNumber(index))
          })
        }

        Text(this.expression)
          .fontSize(24)
          .fontColor('#FFD700')
          .fontWeight(FontWeight.Bold)
          .lineHeight(40)

        Text('结果: ' + this.result)
          .fontSize(20)
          .fontColor(this.result === 24 ? '#32CD32' : '#FFFFFF')

        Grid() {
          ForEach(['+', '-', '×', '÷', '(', ')', 'C', '='], (op: string) => {
            GridItem() {
              Button(op)
                .fontSize(24)
                .fontColor(op === 'C' ? '#FF6B6B' : (op === '=' ? '#32CD32' : '#FFFFFF'))
                .backgroundColor('rgba(255, 215, 0, 0.1)')
                .borderColor('#FFD700')
                .borderWidth(1)
                .borderRadius(8)
                .width('100%')
                .height('100%')
                .onClick(() => this.handleOperator(op))
            }
          })
        }
        .width('90%')
        .height(160)
        .columnsTemplate('1fr 1fr 1fr 1fr')
        .rowsTemplate('1fr 1fr')

        Column({ space: 8 }) {
          Text('游戏说明')
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
            .fontColor('#FFD700')
          Text('用4个数字通过四则运算得到24')
            .fontSize(12)
            .fontColor('#87CEEB')
            .textAlign(TextAlign.Center)
        }
        .width('90%')
        .padding({ top: 8, bottom: 16 })
      }
    }
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#0A192F')
  .padding({ bottom: 80 })
}

4.2 三态界面设计

游戏页面包含三种状态:

状态一:未开始游戏

  • 显示"开始游戏"按钮

状态二:游戏进行中

  • 显示实时计时器
  • 显示4个数字按钮(点击可添加到表达式)
  • 显示表达式输入框
  • 显示计算结果(结果为24时显示绿色)
  • 显示运算符键盘(2行×4列网格)
  • 显示游戏说明

状态三:通关界面

  • 大号金色字体显示"🎉 恭喜通关!"
  • 显示关卡信息、用时
  • 新纪录提示(如果创造了最佳时间)
  • "下一关"按钮返回关卡选择页面

4.3 运算符键盘布局

运算符键盘采用2行×4列的网格布局:

+ - × ÷
( ) C =

按键功能说明

按键 功能 颜色
+ 加法运算 白色
- 减法运算 白色
× 乘法运算 白色
÷ 除法运算 白色
( 左括号 白色
) 右括号 白色
C 清空表达式 红色
= 计算结果 绿色

4.4 视觉设计规范

  • 背景色:深蓝色#0A192F
  • 主题色:金色#FFD700用于标题、按钮和高亮元素
  • 辅助色:天蓝色#87CEEB用于提示文字和次要信息
  • 成功色:绿色#32CD32用于正确结果和等于按钮
  • 警告色:红色#FF6B6B用于清空按钮
  • 边框:金色边框,圆角设计
  • 按钮:金色背景配深蓝色文字,圆角12px

五、表达式解析器实现原理

5.1 递归下降解析器的工作原理

递归下降解析器是一种自顶向下的解析方法,通过递归调用函数来处理语法规则。对于数学表达式,解析器按照运算符优先级分层处理:

表达式 → 加法表达式
加法表达式 → 乘法表达式 ('+' 乘法表达式 | '-' 乘法表达式)*
乘法表达式 → 基本表达式 ('*' 基本表达式 | '/' 基本表达式)*
基本表达式 → 数字 | '(' 表达式 ')'

5.2 运算符优先级处理

通过分层递归调用,解析器自动处理运算符优先级:

  1. 先处理括号内的表达式(最高优先级)
  2. 再处理乘除运算(中等优先级)
  3. 最后处理加减运算(最低优先级)

5.3 表达式求值示例

以表达式 (6+6)×(8-6) 为例:

evaluate("(6+6)*(8-6)")
  └── parseAddSub()
        └── parseMulDiv()
              └── parsePrimary()
                    └── '(' → parseAddSub()
                          └── parseMulDiv() → parsePrimary() → 6
                          └── '+' → parseMulDiv() → parsePrimary() → 6
                          └── 返回 12
                    └── ')'
              └── '*'
              └── parsePrimary()
                    └── '(' → parseAddSub()
                          └── parseMulDiv() → parsePrimary() → 8
                          └── '-' → parseMulDiv() → parsePrimary() → 6
                          └── 返回 2
                    └── ')'
              └── 返回 12 × 2 = 24
        └── 返回 24
  └── 返回 24

5.4 为什么不用eval函数

在JavaScript中,可以使用eval()函数直接执行表达式字符串,但在ArkTS中不推荐使用,原因如下:

  1. 安全性eval()可以执行任意代码,存在安全风险
  2. 性能:字符串解析和执行开销较大
  3. 可控性:无法精确控制运算过程和错误处理
  4. 类型安全:ArkTS强调类型安全,eval()破坏了类型系统

因此,本项目采用手写递归下降解析器,实现精确的表达式求值。

六、难度递进设计

6.1 难度参数

难度 数字范围 运算特点 解题策略
入门 1~9 简单四则运算 直接组合
简单 1~13 包含两位数 需要组合运算
中等 包含分数 需要分数运算 使用括号改变顺序
困难 复杂运算 多步复杂运算 需要高级策略

6.2 经典24点题目示例

入门难度

  • 1, 2, 3, 4 → (1+2+3)×4 = 24
  • 3, 3, 8, 8 → 8÷(3-8÷3) = 24

简单难度

  • 5, 5, 5, 1 → 5×(5-1÷5) = 24
  • 7, 7, 3, 3 → 7×(3+3÷7) = 24

中等难度

  • 10, 10, 4, 4 → (10×10-4)÷4 = 24
  • 3, 3, 7, 7 → 7×(3+3÷7) = 24

困难难度

  • 1, 5, 5, 5 → 5×(5-1÷5) = 24
  • 2, 7, 8, 9 → (2×(9+7))-8 = 24

6.3 解题策略

24点游戏的常见解题策略:

  1. 凑数法:先凑出接近24的数,如12、18、30等
  2. 分解法:将24分解为两个数的运算,如3×8、4×6、12+12等
  3. 分数法:使用分数运算,如8÷(3-8÷3)
  4. 逆推法:从24出发,逆向推导需要的数字

七、全局状态集成

7.1 数据持久化

24点游戏通过AppState单例管理全局状态:

// AppState.ets中定义
twentyFourCompletedLevels: Array<number> = [];
twentyFourLevelTimes: Array<number> = [];

7.2 进度更新流程

// 通关时更新进度
let completedLevels: Array<number> = appState.twentyFourCompletedLevels;
let exists: boolean = false;
for (let i = 0; i < completedLevels.length; i++) {
  if (completedLevels[i] === this.currentLevel) {
    exists = true;
    break;
  }
}
if (!exists) {
  completedLevels.push(this.currentLevel);
  completedLevels.sort((a: number, b: number) => a - b);
  appState.twentyFourCompletedLevels = completedLevels;
}

设计要点

  • 使用数组存储已完成的关卡编号
  • 通过重新赋值数组触发UI响应式更新
  • 保持数组有序,便于查找和显示

八、性能优化考虑

8.1 解析器性能优化

递归下降解析器的时间复杂度为O(n),其中n为表达式长度:

  • 每个字符只被访问一次
  • 递归调用栈深度最多为表达式的嵌套层数
  • 对于24点游戏,表达式长度通常不超过20个字符,性能非常好

8.2 响应式更新

ArkTS中数组的直接修改不会触发UI更新,必须创建新数组引用:

// 正确方式:创建新数组
completedLevels.push(this.currentLevel);
completedLevels.sort((a: number, b: number) => a - b);
appState.twentyFourCompletedLevels = completedLevels;

8.3 计时器优化

使用递归setTimeout而非setInterval,避免累积误差:

private tick(): void {
  if (this.timerId !== 0) {
    this.timerSeconds++;
    setTimeout(() => {
      this.tick();
    }, 1000);
  }
}

九、扩展功能建议

9.1 游戏增强功能

  1. 提示功能:自动生成正确解法
  2. 多解法显示:显示所有可能的解法
  3. 难度选择:允许自定义数字范围
  4. 音效反馈:添加运算和成功音效
  5. 历史记录:保存解题历史

9.2 技术优化方向

  1. 解法生成算法:实现自动求解24点的算法
  2. 表达式验证:实时验证表达式的正确性
  3. 错误提示:提供表达式错误的具体提示
  4. 键盘输入:支持物理键盘输入

十、24点解法生成算法

10.1 算法原理

24点解法生成可以通过穷举所有可能的运算组合来实现:

  1. 生成4个数字的所有排列
  2. 生成所有可能的运算符组合(4个数字需要3个运算符)
  3. 生成所有可能的括号组合
  4. 对每个组合进行计算,判断是否等于24

10.2 实现思路

private solve24(numbers: Array<number>): Array<string> {
  let solutions: Array<string> = [];
  let ops: Array<string> = ['+', '-', '*', '/'];
  
  let permutations: Array<Array<number>> = this.permute(numbers);
  
  for (let perm of permutations) {
    for (let op1 of ops) {
      for (let op2 of ops) {
        for (let op3 of ops) {
          let expressions: Array<string> = [
            `(((${perm[0]}${op1}${perm[1]})${op2}${perm[2]})${op3}${perm[3]})`,
            `(${perm[0]}${op1}${perm[1]})${op2}(${perm[2]}${op3}${perm[3]})`,
            `(${perm[0]}${op1}(${perm[1]}${op2}${perm[2]}))${op3}${perm[3]}`,
            `${perm[0]}${op1}(((${perm[1]}${op2}${perm[2]})${op3}${perm[3]}))`,
            `${perm[0]}${op1}(${perm[1]}${op2}(${perm[2]}${op3}${perm[3]}))`,
          ];
          
          for (let expr of expressions) {
            let evaluator: ExpressionEvaluator = new ExpressionEvaluator();
            let result: number = evaluator.evaluate(expr);
            if (Math.abs(result - 24) < 0.0001) {
              solutions.push(expr);
            }
          }
        }
      }
    }
  }
  
  return solutions;
}

算法复杂度

  • 数字排列:4! = 24种
  • 运算符组合:4³ = 64种
  • 括号组合:5种基本形式
  • 总组合数:24 × 64 × 5 = 7680种

对于计算机来说,7680次计算非常快,可以在毫秒级完成。

十一、总结

24点游戏是一款经典的数学益智游戏,本项目基于HarmonyOS ArkTS技术栈实现了完整的游戏功能:

  1. 核心玩法:使用4个数字通过四则运算得到24
  2. 表达式解析器:手写递归下降解析器,支持运算符优先级和括号
  3. 关卡系统:20个难度递进的关卡,从入门到困难
  4. 进度管理:全局状态管理通关进度和最佳时间
  5. 视觉设计:深蓝色背景配金色主题,清晰的表达式显示
  6. 用户体验:实时计时、分数系统、通关界面

游戏的核心难点在于:

  • 实现正确的表达式解析器,处理运算符优先级和括号
  • 设计合理的难度递进曲线
  • 确保游戏的趣味性和挑战性

通过本项目的实践,可以深入理解ArkTS的状态管理机制、UI组件的动态布局、以及表达式解析器的实现方法。24点游戏不仅是一款有趣的益智游戏,也是学习递归下降解析器的绝佳案例。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐