# 实战五子棋

近来看到一个关于五子棋的游戏,就想自己实现一个简易的五子棋小游戏。效果如下:

微信截图_20200510163310.png

# 先思考下

先整理下思路,拆分一下功能点。主要实现方法有:

  • init(): 初始化棋盘
  • chess(): 下棋
  • check): 检查棋盘结果
  • reset(): 重置棋盘

# 开始

这里使用 ts 进行编码,新建 gobang.ts 文件。 写一个类 gobang,将所有的方法都放在这个类中。

class Gobang { 
  constructor() {

  }

  init(){

  }

  chess() {

  }

  check() {

  }

  reset() {

  }
}

# constructor

constructor 中进行配置和数据的初始化。

constructor (options: Options) {
  // 保存配置
  this.options = options
  // 初始化棋盘数据
  this.chessboard = []
  // 白棋先走还是黑棋先走
  this.flag = options.flag || true
  // 棋盘大小
  this.length = options.length
}

# init

init 中需要对棋盘初始化,将数据保存在 chessboard 变量中。返回值为棋牌数据

init ():Array<Array<number>> {
  const { length } = this.options
  this.chessboard = new Array(length).fill(0).map(() => new Array(length).fill(0))

  return this.chessboard
}

# chess

落棋函数,接收x, y的落点坐标。在下棋之前要判断该位置是否已经有棋子。若当前位置的值为0,代表没有棋子,可以落棋。返回为棋盘数据。

chess (x: number, y: number): Array<Array<number>> {
  const val = this.chessboard[x][y]
  if (val === 0) {
    // 落子
    this.chessboard[x][y] = this.flag ? 1 : -1
    // 检查胜负结果
    this.check(x, y)
    // 换对方落子
    this.flag = !this.flag
  }
  return this.chessboard
}

# check

该方法为检查胜负结果。根据五子棋的规则,在横竖以及对角线五子连城一线则胜出。
在落子之后,需要对所在横轴,纵轴,左对角线以及右对角线进行检查,对相同的棋子进行统计,若等于5个在胜出。

check (x: number, y: number): void {
  const res = this.judge(this.chessboard[x][y], x, y)
  if (res !== 0 && res !== undefined) {
    setTimeout(() => {
      alert(this._currentFlag() + '赢了')
    }, 100)
  }
}

judge (num: number, x: number, y: number) {
  const n1 = this._checkXLine(num, x, y) // 左右方向
  const n2 = this._checkYLine(num, x, y) // 上下方向
  const n3 = this._checkLeftRight(num, x, y) // 左上到右下方向
  const n4 = this._checkRightLeft(num, x, y) // 右上到左下方向
  if (Math.max(n1, n2, n3, n4) >= 5) {
    return num
  }
}

具体检查各方向的结果,可以分左右两次进行遍历,遇到相同棋子,则对 n 标记棋子数量进行加 1 操作。

/**
  * 判断x轴
  * @param num
  * @param x
  * @param y
  */
_checkXLine (num: number, x: number, y: number): number {
  let n = 0
  for (let i = x; i >= 0; i--) {
    if (this.chessboard[i][y] !== num) break
    n++
  }
  for (let i = x + 1; i < this.length; i++) {
    if (this.chessboard[i][y] !== num) break
    n++
  }
  return n
}

/**
  * 判断y轴
  * @param num
  * @param x
  * @param y
  */
_checkYLine (num: number, x: number, y: number): number {
  let n = 0
  for (let i = y; i >= 0; i--) {
    if (this.chessboard[x][i] !== num) break
    n++
  }
  for (let i = y + 1; i < this.length; i++) {
    if (this.chessboard[x][i] !== num) break
    n++
  }

  return n
}

/**
  * 判断左对角线
  * @param num
  * @param x
  * @param y
  */
_checkLeftRight (num: number, x: number, y: number): number {
  let n = 0
  for (let i = x, j = y; j >= 0; i--, j--) {
    if (i < 0 || j < 0 || this.chessboard[i][j] !== num) break
    n++
  }
  for (let i = x + 1, j = y + 1; i < this.length || j < this.length; i++, j++) {
    if (i >= this.length || j >= this.length || this.chessboard[i][j] !== num) break
    n++
  }

  return n
}

/**
  * 判断右对角线
  * @param num
  * @param x
  * @param y
  */
_checkRightLeft (num: number, x: number, y: number): number {
  let n = 0
  for (let i = x, j = y; j < this.length; i--, j++) {
    if (i < 0 || j >= this.length || this.chessboard[i][j] !== num) break
    n++
  }
  for (let i = x + 1, j = y - 1; i < this.length || j >= 0; i++, j--) {
    if (i >= this.length || j < 0 || this.chessboard[i][j] !== num) break
    n++
  }
  return n
}

需要注意的是,在落子后,进行胜负检测,在页面还没有渲染完成已经检测完成,需要添加延时器解决。

# reset

重置就比较简单了。将数据初始化一下就行了。重新生成新的棋盘。返回值为棋盘数据。

reset (): Array<Array<number>> {
  return this.init()
}

上述就基本完成了,附上全部代码。

interface Options {
  length: number;
  flag: boolean;
}

class Gobang {
  options: Options
  chessboard: Array<Array<number>>
  flag: boolean
  max = 5
  length: number

  constructor (options: Options) {
    this.options = options
    this.chessboard = []
    this.flag = options.flag || true
    this.length = options.length
  }

  // 初始化棋盘
  init () {
    const { length } = this.options
    this.chessboard = new Array(length).fill(0).map(() => new Array(length).fill(0))

    return this.chessboard
  }

  /**
   * 落棋
   * @param x
   * @param y
   */
  chess (x: number, y: number): Array<Array<number>> {
    const val = this.chessboard[x][y]
    if (val === 0) {
      this.chessboard[x][y] = this.flag ? 1 : -1
      this.check(x, y)
      this.flag = !this.flag
    }
    return this.chessboard
  }

  /**
   * 重置棋盘
   */
  reset () {
    return this.init()
  }

  /**
   * 检查结果
   */
  check (x: number, y: number): void {
    const res = this.judge(this.chessboard[x][y], x, y)
    if (res !== 0 && res !== undefined) {
      setTimeout(() => {
        alert(this._currentFlag() + '赢了')
      }, 100)
    }
  }

  judge (num: number, x: number, y: number) {
    const n1 = this._checkXLine(num, x, y) // 左右方向
    const n2 = this._checkYLine(num, x, y) // 上下方向
    const n3 = this._checkLeftRight(num, x, y) // 左上到右下方向
    const n4 = this._checkRightLeft(num, x, y) // 右上到左下方向
    if (Math.max(n1, n2, n3, n4) >= 5) {
      return num
    }
  }

  _currentFlag () {
    return this.flag ? '白棋' : '黑棋'
  }

  /**
   * 判断x轴
   * @param num
   * @param x
   * @param y
   */
  _checkXLine (num: number, x: number, y: number): number {
    let n = 0
    for (let i = x; i >= 0; i--) {
      if (this.chessboard[i][y] !== num) break
      n++
    }
    for (let i = x + 1; i < this.length; i++) {
      if (this.chessboard[i][y] !== num) break
      n++
    }
    return n
  }

  /**
   * 判断y轴
   * @param num
   * @param x
   * @param y
   */
  _checkYLine (num: number, x: number, y: number): number {
    let n = 0
    for (let i = y; i >= 0; i--) {
      if (this.chessboard[x][i] !== num) break
      n++
    }
    for (let i = y + 1; i < this.length; i++) {
      if (this.chessboard[x][i] !== num) break
      n++
    }

    return n
  }

  /**
   * 判断左对角线
   * @param num
   * @param x
   * @param y
   */
  _checkLeftRight (num: number, x: number, y: number): number {
    let n = 0
    for (let i = x, j = y; j >= 0; i--, j--) {
      if (i < 0 || j < 0 || this.chessboard[i][j] !== num) break
      n++
    }
    for (let i = x + 1, j = y + 1; i < this.length || j < this.length; i++, j++) {
      if (i >= this.length || j >= this.length || this.chessboard[i][j] !== num) break
      n++
    }

    return n
  }

  /**
   * 判断右对角线
   * @param num
   * @param x
   * @param y
   */
  _checkRightLeft (num: number, x: number, y: number): number {
    let n = 0
    for (let i = x, j = y; j < this.length; i--, j++) {
      if (i < 0 || j >= this.length || this.chessboard[i][j] !== num) break
      n++
    }
    for (let i = x + 1, j = y - 1; i < this.length || j >= 0; i++, j--) {
      if (i >= this.length || j < 0 || this.chessboard[i][j] !== num) break
      n++
    }
    return n
  }
}

export default Gobang
Last Updated: 5/10/2020, 8:35:52 AM