【HarmonyOS 5】 鸿蒙应用开发---五子棋游戏

14 阅读4分钟

五子棋游戏

五子棋是一款传统的两人策略型棋类游戏,游戏的目的是在棋盘上首先形成连续的五个同色棋子的玩家获胜。游戏规则简单,易于上手,但要达到高手水平则需要相当的策略和技巧。

游戏设计思路

  1. 游戏核心组件

    • 棋盘组件:绘制15×15的网格
    • 棋子组件:显示黑白棋子
    • 游戏状态管理:处理落子、胜负判断
    • 控制面板:显示当前玩家和游戏控制
  2. 游戏流程

    • 黑方先行,玩家轮流在交叉点落子
    • 当形成5个同色棋子连线时游戏结束
    • 显示胜利方并提供重新开始选项

完整代码实现

import { display } from '@kit.ArkUI';

// 游戏状态管理类 - 简化版
class GomokuGame {
  // 棋盘尺寸
  static BOARD_SIZE = 15;
  
  // 棋盘状态:0=空, 1=黑子, 2=白子
  board: number[][];
  
  // 当前玩家:1=黑子, 2=白子
  currentPlayer: number;
  
  // 游戏状态:true=已结束, false=进行中
  gameOver: boolean;
  
  // 胜利者:0=无, 1=黑子, 2=白子
  winner: number;
  
  constructor() {
    this.resetGame();
  }
  
  // 重置游戏状态
  resetGame() {
    // 创建空棋盘
    this.board = Array(GomokuGame.BOARD_SIZE).fill(0)
      .map(() => Array(GomokuGame.BOARD_SIZE).fill(0));
    
    this.currentPlayer = 1; // 黑子先行
    this.gameOver = false;
    this.winner = 0;
  }
  
  // 在指定位置落子
  placePiece(row: number, col: number): boolean {
    // 如果游戏结束或位置已有棋子,返回false
    if (this.gameOver || this.board[row][col] !== 0) {
      return false;
    }
    
    // 放置棋子
    this.board[row][col] = this.currentPlayer;
    
    // 检查是否获胜
    if (this.checkWin(row, col)) {
      this.gameOver = true;
      this.winner = this.currentPlayer;
      return true;
    }
    
    // 切换玩家
    this.currentPlayer = this.currentPlayer === 1 ? 2 : 1;
    return true;
  }
  
  // 检查是否获胜
  private checkWin(row: number, col: number): boolean {
    // 定义检查方向:水平、垂直、两条对角线
    const directions = [
      [[0, 1], [0, -1]],   // 水平
      [[1, 0], [-1, 0]],   // 垂直
      [[1, 1], [-1, -1]],  // 右下对角线
      [[1, -1], [-1, 1]]   // 左下对角线
    ];
    
    for (const direction of directions) {
      let count = 1; // 当前位置已经有一个棋子
      
      // 检查两个相反方向
      for (const [dx, dy] of direction) {
        let r = row + dx;
        let c = col + dy;
        
        // 沿着方向计数连续的同色棋子
        while (
          r >= 0 && r < GomokuGame.BOARD_SIZE && 
          c >= 0 && c < GomokuGame.BOARD_SIZE &&
          this.board[r][c] === this.currentPlayer
        ) {
          count++;
          r += dx;
          c += dy;
        }
      }
      
      // 如果连续棋子数达到5,获胜
      if (count >= 5) {
        return true;
      }
    }
    
    return false;
  }
}

// 棋盘组件
@Component
struct GameBoard {
  // 引用游戏状态
  @ObjectLink game: GomokuGame;
  
  // 棋盘尺寸计算
  @State private boardSize: number = 0;
  @State private cellSize: number = 0;
  
  // 棋盘边距
  private boardPadding: number = 20;
  
  // 屏幕尺寸
  private screenWidth: number = display.getDefaultDisplaySync().width;
  
  aboutToAppear() {
    this.calculateBoardSize();
  }
  
  // 计算棋盘尺寸
  private calculateBoardSize() {
    // 棋盘宽度 = 屏幕宽度 - 两边边距
    this.boardSize = this.screenWidth - 2 * this.boardPadding;
    // 每个格子的大小
    this.cellSize = this.boardSize / (GomokuGame.BOARD_SIZE - 1);
  }
  
  // 处理点击事件
  private handleTap(event: ClickEvent) {
    if (this.game.gameOver) return;
    
    // 计算点击位置对应的棋盘坐标
    const col = Math.round((event.x - this.boardPadding) / this.cellSize);
    const row = Math.round((event.y - this.boardPadding) / this.cellSize);
    
    // 确保坐标在棋盘范围内
    if (row >= 0 && row < GomokuGame.BOARD_SIZE && 
        col >= 0 && col < GomokuGame.BOARD_SIZE) {
      this.game.placePiece(row, col);
    }
  }
  
  // 绘制棋盘网格
  private drawGrid(ctx: CanvasRenderingContext2D) {
    ctx.strokeStyle = '#000000';
    ctx.lineWidth = 1;
    
    // 绘制横线
    for (let i = 0; i < GomokuGame.BOARD_SIZE; i++) {
      const y = this.boardPadding + i * this.cellSize;
      ctx.beginPath();
      ctx.moveTo(this.boardPadding, y);
      ctx.lineTo(this.boardPadding + this.boardSize, y);
      ctx.stroke();
    }
    
    // 绘制竖线
    for (let i = 0; i < GomokuGame.BOARD_SIZE; i++) {
      const x = this.boardPadding + i * this.cellSize;
      ctx.beginPath();
      ctx.moveTo(x, this.boardPadding);
      ctx.lineTo(x, this.boardPadding + this.boardSize);
      ctx.stroke();
    }
    
    // 绘制星位点(天元和星)
    const starPoints = [3, 7, 11];
    ctx.fillStyle = '#000000';
    
    for (const i of starPoints) {
      for (const j of starPoints) {
        const x = this.boardPadding + i * this.cellSize;
        const y = this.boardPadding + j * this.cellSize;
        ctx.beginPath();
        ctx.arc(x, y, 4, 0, Math.PI * 2);
        ctx.fill();
      }
    }
  }
  
  build() {
    // 棋盘背景设置
    const settings = new RenderingContextSettings(true);
    const ctx = new CanvasRenderingContext2D(settings);
    
    Stack() {
      // 棋盘背景
      Rect()
        .width(this.boardSize + 2 * this.boardPadding)
        .height(this.boardSize + 2 * this.boardPadding)
        .fill('#E8C9A1') // 木色棋盘
        .radius(8)
      
      // 棋盘网格
      Canvas(ctx)
        .width(this.boardSize + 2 * this.boardPadding)
        .height(this.boardSize + 2 * this.boardPadding)
        .onReady(() => this.drawGrid(ctx))
        .onClick((event) => this.handleTap(event))
      
      // 绘制棋子
      ForEach(this.game.board, (row, rowIndex) => {
        ForEach(row, (cell, colIndex) => {
          if (cell !== 0) {
            // 棋子
            Circle()
              .fill(cell === 1 ? '#000000' : '#FFFFFF') // 黑子/白子
              .stroke(cell === 1 ? '#000000' : '#CCCCCC')
              .strokeWidth(1)
              .width(this.cellSize * 0.8)
              .height(this.cellSize * 0.8)
              .position({
                x: this.boardPadding + colIndex * this.cellSize - this.cellSize * 0.4,
                y: this.boardPadding + rowIndex * this.cellSize - this.cellSize * 0.4
              })
          }
        })
      })
      
      // 游戏结束提示
      if (this.game.gameOver) {
        Column() {
          Text(this.game.winner === 1 ? '黑棋胜利!' : '白棋胜利!')
            .fontSize(24)
            .fontWeight(FontWeight.Bold)
            .fontColor('#FF0000')
            .margin({ bottom: 20 })
          
          Button('再来一局')
            .width(120)
            .height(40)
            .backgroundColor('#4A86E8')
            .onClick(() => this.game.resetGame())
        }
        .width(260)
        .height(150)
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .justifyContent(FlexAlign.Center)
        .position({
          x: (this.boardSize + 2 * this.boardPadding) / 2 - 130,
          y: (this.boardSize + 2 * this.boardPadding) / 2 - 75
        })
      }
    }
    .width(this.boardSize + 2 * this.boardPadding)
    .height(this.boardSize + 2 * this.boardPadding)
  }
}

// 主页面
@Entry
@Component
struct GomokuApp {
  // 创建游戏实例
  @State game: GomokuGame = new GomokuGame();
  
  build() {
    Column() {
      // 标题
      Text('五子棋')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 })
      
      // 当前玩家状态
      Row() {
        Circle()
          .width(20)
          .height(20)
          .fill(this.game.currentPlayer === 1 ? '#000000' : '#FFFFFF')
          .margin({ right: 10 })
        
        Text(this.game.currentPlayer === 1 ? '黑方回合' : '白方回合')
          .fontSize(18)
      }
      .padding(10)
      .backgroundColor('#F0F0F0')
      .borderRadius(20)
      .margin({ bottom: 20 })
      
      // 游戏棋盘
      GameBoard({ game: this.game })
      
      // 控制按钮
      Row() {
        Button('重新开始')
          .width(120)
          .height(40)
          .backgroundColor('#4A86E8')
          .onClick(() => this.game.resetGame())
        
        Button('悔棋')
          .width(120)
          .height(40)
          .backgroundColor('#999999')
          .margin({ left: 20 })
          .enabled(false) // 暂未实现悔棋功能
      }
      .margin({ top: 20 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Start)
  }
}

游戏特点说明

  1. 直观的棋盘设计

    • 使用木色背景模拟真实棋盘
    • 清晰的网格线和星位点
    • 黑白棋子对比鲜明
  2. 简洁的游戏状态显示

    • 顶部显示当前玩家
    • 棋子下方显示当前回合状态
    • 游戏结束时有明显提示
  3. 清晰的游戏逻辑

    • 游戏状态管理类封装了所有游戏逻辑
    • 胜负判断使用四个方向检查
    • 棋盘点击处理精确到交叉点
  4. 响应式布局

    • 棋盘大小根据屏幕尺寸自动计算
    • 在不同尺寸设备上都能良好显示
  5. 用户友好的交互

    • 落子后有视觉反馈
    • 游戏结束提示明显
    • 控制按钮布局合理