落子看似简单,实则暗藏玄机——一个placePiece方法的设计哲学

界面设计:

在这里插入图片描述

placePiece方法完整解析

placePiece(row: number, col: number, player: number): boolean {
  // 1. 游戏是否已结束
  if (this.result !== GameResult.PLAYING) {
    return false;
  }
  // 2. 坐标是否越界
  if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) {
    return false;
  }
  // 3. 位置是否已被占用
  if (this.board[row][col] !== EMPTY) {
    return false;
  }

  // 4. 落子
  this.board[row][col] = player;
  this.lastMove = new Move(row, col);
  this.history.push(new Move(row, col));

  // 5. 检查胜负
  if (this.checkWin(row, col, player)) {
    this.result = player === BLACK ? GameResult.BLACK_WIN : GameResult.WHITE_WIN;
  } else if (this.isBoardFull()) {
    this.result = GameResult.DRAW;
  } else {
    // 6. 切换玩家
    this.currentPlayer = player === BLACK ? WHITE : BLACK;
  }
  return true;
}

三重验证机制

验证1:游戏状态检查

if (this.result !== GameResult.PLAYING) {
  return false;
}

游戏结束后不允许继续落子。这防止了UI层的意外调用。

验证2:边界检查

if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) {
  return false;
}

防止数组越界。虽然UI层已经做了坐标转换,但引擎不能信任调用方——防御式编程

验证3:占用检查

if (this.board[row][col] !== EMPTY) {
  return false;
}

同一位置不能重复落子。

落子后的状态更新

this.board[row][col] = player;              // 更新棋盘
this.lastMove = new Move(row, col);          // 记录最后一手
this.history.push(new Move(row, col));       // 添加到历史

注意lastMovehistory分别用两个new Move对象——虽然内容相同,但独立对象避免了引用污染。

胜负判定流程

if (this.checkWin(row, col, player)) {
  this.result = player === BLACK ? GameResult.BLACK_WIN : GameResult.WHITE_WIN;
} else if (this.isBoardFull()) {
  this.result = GameResult.DRAW;
} else {
  this.currentPlayer = player === BLACK ? WHITE : BLACK;
}

三个分支的优先级:

  1. 先检查是否获胜——五子连珠
  2. 再检查是否平局——棋盘已满且无人获胜
  3. 最后切换玩家——游戏继续

为什么先检查胜负再检查平局? 因为最后一手既可能形成五连,也可能填满棋盘。必须优先判断获胜。

返回值设计

placePiece返回boolean而非void

  • true:落子成功
  • false:落子失败(游戏结束/越界/已占用)

调用方根据返回值决定是否刷新UI:

// TwoPlayerPage中
const success = this.engine.placePiece(row, col, this.currentPlayer);
if (success) {
  this.refreshBoardData();  // 只有成功才刷新
}

对比AI对战中的落子

// AIBattlePage中AI落子
const move = this.aiPlayer.getMove(this.engine.board);
this.engine.placePiece(move.row, move.col, WHITE);

AI的落子也通过同一个placePiece方法——统一的入口保证了规则一致性。

设计启示

  1. 防御式编程:引擎不信任调用方,所有输入都要验证
  2. 原子操作:落子+判定+状态更新在一个方法内完成,不存在中间状态
  3. 返回值有意义:boolean让调用方知道操作结果
  4. 历史记录:每次落子都记录到history,为悔棋功能埋下伏笔

总结

placePiece是引擎中最核心的方法,它在一个方法内完成了验证、落子、判定、状态更新四步操作。这种"原子性"设计保证了游戏状态的一致性——要么全部成功,要么什么都不做。

Logo

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

更多推荐