腾讯CDC面试题之五子棋 - dom版(ES6)

废话不多说上代码!
完整项目地址:GitHub项目地址


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
                }, () => ``).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)

你可能感兴趣的:(腾讯CDC面试题之五子棋 - dom版(ES6))