廢話很少說上代碼!
完整項目地址:GitHub項目地址java
class Gobang { constructor(options) { this.options = options this.gobang = document.getElementById(options.canvas || 'gobang') this.chessboard = this.gobang.children[0] this.chessmans = this.gobang.children[1] // 棋盤樣式 this.gobangStyle = Object.assign({ padding: 30, count: 15 }, options.gobangStyle || {}) // 棋盤元素 this.lattice = { width: (this.gobang.clientWidth - this.gobangStyle.padding * 2) / this.gobangStyle.count, height: (this.gobang.clientHeight - this.gobangStyle.padding * 2) / this.gobangStyle.count } // 初始化 this.resetAndInit() } // 初始化 resetAndInit() { const {options} = this // 角色 => 1黑旗子 2白旗 this.role = options.role || this.role || 1 // 是否已分出勝負 this.win = false // 走棋記錄 this.history = [] // 當前步 this.currentStep = 0 // 清空棋子和事件 this.chessmans.onclick = null this.chessmans.innerHTML = '' // 初始化 this.drawChessboard() this.listenDownChessman() this.initChessboardMatrix() } // 棋盤矩陣 initChessboardMatrix() { const checkerboard = [] for(let x = 0; x < this.gobangStyle.count + 1; x++) { checkerboard[x] = [] for(let y = 0; y < this.gobangStyle.count + 1; y++) { checkerboard[x][y] = 0 } } this.checkerboard = checkerboard } // 刻畫棋盤 drawChessboard() { const { gobangStyle, gobang } = this // 棋盤網格 const lattices = Array.from({ length: gobangStyle.count * gobangStyle.count }, () => `<span class="lattice"></span>`).join('') this.chessboard.className = `chessboard lattice-${gobangStyle.count}` this.chessboard.innerHTML = lattices this.gobang.style.border = `${gobangStyle.padding}px solid #ddd` } // 刻畫棋子 drawChessman(x, y, isBlack) { const { gobangStyle, lattice, gobang } = this const newChessman = document.createElement('div') newChessman.setAttribute('id', `x${x}-y${y}-r${isBlack ? 1 : 2}`) newChessman.className = isBlack ? 'chessman black' : 'chessman white' newChessman.style.width = lattice.width * 0.6 newChessman.style.height = lattice.height * 0.6 newChessman.style.left = (x * lattice.width) - lattice.width * 0.3 newChessman.style.top = (y * lattice.height) - lattice.height * 0.3 this.chessmans.appendChild(newChessman) // 每次落子結束都要判斷輸贏 setTimeout(() => { this.checkReferee(x, y, isBlack ? 1 : 2) }, 0) } // 落子 listenDownChessman(isBlack = false) { this.chessmans.onclick = event => { // 若是點擊的是棋子則中斷 if(event.target.className.includes('chessman ')) { return false } let { offsetX: x, offsetY: y } = event x = Math.round(x / this.lattice.width) y = Math.round(y / this.lattice.height) // 空的棋位纔可落子 if(this.checkerboard[x][y] !== undefined && Object.is(this.checkerboard[x][y], 0)) { // 落子後,更新矩陣,切換角色,並記錄 this.checkerboard[x][y] = this.role this.drawChessman(x, y, Object.is(this.role, 1)) // 落子完畢後,有多是悔棋以後落子的,這種狀況下就該重置歷史記錄 this.history.length = this.currentStep this.history.push({ x, y, role: this.role }) // 保存座標,角色,快照 this.currentStep++ this.role = Object.is(this.role, 1) ? 2 : 1 } } } // 判斷輸贏 checkReferee(x, y, role) { if((x == undefined) || (y == undefined) || (role == undefined)) return // 連殺分數 let countContinuous = 0 // 所在矩陣數據 const XContinuous = this.checkerboard.map(x => x[y]) const YContinuous = this.checkerboard[x] const S1Continuous = [] const S2Continuous = [] this.checkerboard.forEach((_y, i) => { // 左斜線 const S1Item = _y[y - (x - i)] // alert(S1Item) if(S1Item !== undefined) { S1Continuous.push(S1Item) } // 右斜線 const S2Item = _y[y + (x - i)] if(S2Item !== undefined) { S2Continuous.push(S2Item) } }) // 當前落棋點所在的X軸/Y軸/交叉斜軸,只要有能連起來的5個子的角色即有勝者 ; [XContinuous, YContinuous, S1Continuous, S2Continuous].forEach(axis => { if(axis.some((x, i) => axis[i] !== 0 && axis[i - 2] === axis[i - 1] && axis[i - 1] === axis[i] && axis[i] === axis[i + 1] && axis[i + 1] === axis[i + 2])) { countContinuous++ } }) // 若是贏了,則解綁事件 if(countContinuous) { this.chessmans.onclick = null this.win = true alert((role == 1 ? '黑' : '白') + '子勝') } } // 悔棋 regretChess() { // 找到最後一次的記錄,回滾UI,更新矩陣 if(this.history.length && !this.win) { const prev = this.history[this.currentStep - 1] if(prev) { const { x, y, role } = prev const targetChessman = document.getElementById(`x${x}-y${y}-r${role}`) targetChessman.parentNode.removeChild(targetChessman) this.checkerboard[prev.x][prev.y] = 0 this.currentStep-- this.role = Object.is(this.role, 1) ? 2 : 1 } } } // 撤銷悔棋 revokedRegretChess() { const next = this.history[this.currentStep] if(next) { this.drawChessman(next.x, next.y, next.role === 1) this.checkerboard[next.x][next.y] = next.role this.currentStep++ this.role = Object.is(this.role, 1) ? 2 : 1 } } } // 實例化遊戲 const gobangGame = new Gobang({ role: 2, canvas: 'game', gobangStyle: { padding: 30, count: 16 } }) console.log(gobangGame)