騰訊CDC面試題之五子棋 - dom版(ES6)

廢話很少說上代碼!
完整項目地址: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)
相關文章
相關標籤/搜索