js五子棋游戏

开篇导读:最近面试遇到一个笔试题使用js和dom实现一个五子棋游戏,包含悔棋和撤销悔棋功能,对人机对战不做要求,这里分享一下我个人的实现方案

HTML部分(未使用原型的封装方式)

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>title>
        <style>
            .container{
                width: 500px;
                height: 500px;
                border: 1px solid black;
            }
            .container button{
                background: transparent;
                height: 50px;
                width: 50px;
                margin: 0;
                padding: 0;
                outline: none;
                border: 1px solid #000000;
                float: left;
                font-size: 26px;
            }
            #bank,#revokeBank{
                position: fixed;
                width: 80px;
                height: 30px;
                top: 200px;
                left: 700px;
            }
            #revokeBank{
                top: 240px;
            }
        style>
    head>
    <body>
        <div class="container">div>
        <button id="bank">悔棋button>
        <button id="revokeBank">撤销悔棋button>
    body>
  //js逻辑部分 <script src="index.js">script> html>

JS部分(未使用原型的封装方式)

        //创建一个容器
        let dom = document.createDocumentFragment();
        let container = document.querySelector(".container");
        for(let i=1;i<=100;i++){
            let btn = document.createElement("button");
            btn.className = i;
            dom.appendChild(btn);
        }
        container.appendChild(dom);
        //红方选手
        let redSquare = "O";
        // 黑方选手
        let blackSquare = "X";
        //判断轮到谁下
        let flag = true;
        // 每次落子的位置
        let array = [];
        // 棋子的历史记录
        let historys = [];
        // 悔棋的记录
        let unhistorys = [];
        //选择撤销一步悔或撤销所有悔棋
        let revocationType = false;
        //调用事件委派
        delegate(container,'click','button',function(e){
            //如果此处已下棋子,则不执行方法
            if(e.target.innerText) return;
            flag?isRedOrblack(e,redSquare):isRedOrblack(e,blackSquare);
        });
        //判断哪方下棋
        function isRedOrblack(e,square){
            // 保存当前事件的对象
            let nowDom = e.target;
            nowDom.innerText = square;
            // 记录在数组的对应类名序号的下标下
            array[nowDom.className] = square;
            historys.push(nowDom.className);
            //将悔棋记录清空
            unhistorys.length = 0;
            action(parseInt(nowDom.className),square);
            flag=!flag;
        }
        //判断胜负循环检索的方法
        function decide(value,Operator,str,count){
            for(i=1;i<5;i++){
                if(array[value+Operator*i] === str)
                 count++;
                 else break;
            }
            return count;
        }
        //判断胜负
        function action(value,str){
            //垂直方向上的棋子
            let Hcount=1;
            //水平方向上的棋子
            let Vcount=1;
            //左对角方向上的棋子
            let LTacr = 1;
            //右方向上的棋子
            let RTacr = 1;
            //垂直方向
            Hcount = decide(value,-10,str,Hcount);
            Hcount = decide(value,10,str,Hcount);
            // 水平方向
            Vcount = decide(value,-1,str,Vcount);
            Vcount = decide(value,1,str,Vcount);
            //上左边对角方向
            LTacr = decide(value,-11,str,LTacr);
            LTacr = decide(value,11,str,LTacr);
            //上右边对角方向
            RTacr = decide(value,-9,str,RTacr);
            RTacr = decide(value,9,str,RTacr);
            (Hcount === 5 || Vcount === 5 || LTacr === 5 || RTacr === 5) && alert(str + "胜利");
        };
        // 悔棋
        let BankDom = document.querySelector("#bank");
        let containerBtn = document.querySelectorAll("button");
        BankDom.onclick = function (){
            // 可以悔棋多次
            if(historys.length){
                let number = setBank();
                // 对应的button按钮元素下标需要类名值减一
                containerBtn[number].innerText= "";
            }
        }
        //设置悔棋的相应数据
        function setBank(){
            //拿到记录中的棋子下标
            let number = parseInt(historys.pop());
            // 记录需要悔棋棋子的下标
            unhistorys.push(number);
            // 记录棋子值的数组中的下标对应按钮的类名的值
            array[number] = "";
            // 更改下棋的选手
            flag = !flag;
            // 返回棋子位置的数组在末尾拿掉对应的值减去一就是该btn按钮的下标
            return number-1;
        }
        //将悔掉的棋重新赋值
        function setValue(number,square,isAction){
            let target = containerBtn[number-1];
            target.innerText= square;
            array[number] = square;
            isAction && action(parseInt(target.className),square);
            // 更改下棋的选手
            flag = !flag;
        }
        //撤销悔棋
        let revokeBank = document.querySelector("#revokeBank");
        revokeBank.onclick = function (){
            if(!unhistorys.length) return;
            if(revocationType){
                for(let i=unhistorys.length-1;i>=0;i--){
                    historys.push(unhistorys[i]);
                    flag?setValue(unhistorys[i],redSquare,false):setValue(unhistorys[i],blackSquare,false)
                }
                //将悔棋记录清空
                unhistorys.length = 0;
            }
            else{
                let add = unhistorys.pop();
                historys.push(add);
                flag?setValue(add,redSquare,true):setValue(add,blackSquare,true);
            }
        }

上述JS部分调用了一个封装好了的delegate事件委派的函数,详细注释js实现事件委派

    //事件委派方法
    function delegate (element, eventType, selector, fn) {
        element.addEventListener(eventType, e => {
            let el = e.target;
            while (!el.matches(selector)) {
                if (element === el) {
                   el = null;
                   break;
                }
                el = el.parentNode;
            }
            el && fn.call(el, e, el)
        });
        return element
    };

HTML部分(使用原型的封装方式)

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>title>
        <style>
            .container{
                width: 500px;
                height: 500px;
                border: 1px solid black;
            }
            .container button{
                background: transparent;
                height: 50px;
                width: 50px;
                margin: 0;
                padding: 0;
                outline: none;
                border: 1px solid #000000;
                float: left;
                font-size: 26px;
            }
            #bank,#revokeBank{
                position: fixed;
                width: 80px;
                height: 30px;
                top: 200px;
                left: 700px;
            }
            #revokeBank{
                top: 240px;
            }
        style>
    head>
    <body>
        <div class="container">div>
        <button id="bank">悔棋button>
        <button id="revokeBank">撤销悔棋button>
    body>
  //js逻辑部分 <script src="index.js">script> <script> //创建游戏并初始化 new Game().initGame(); script> html>

JS部分(使用原型的封装方式)

    // 声明一个游戏函数
    function Game(){
        //红方选手
        this.redSquare = "O";
        // 黑方选手
        this.blackSquare = "X";
        //判断轮到谁下
        this.flag = true;
        // 每次落子的位置
        this.array = [];
        // 棋子的历史记录
        this.historys = [];
        // 悔棋的记录
        this.unhistorys = [];
        //选择撤销一步悔或撤销所有悔棋
        this.revocationType = false;
        //获取到.container元素
        this.container = document.querySelector(".container");
        //保存所有button元素
        let containerBtn = null;
    }
    
    //初始化游戏
    Game.prototype.initGame = function(){
        this.createBoard();
        this.addEventListener();
        this.needBank();
        this.needRevokeBank();
    }
    
    //在游戏函数原型上增加方法,创建棋盘
    Game.prototype.createBoard = function(){
        //创建一个容器
        let dom = document.createDocumentFragment();
        for(let i=1;i<=100;i++){
            let btn = document.createElement("button");
            btn.className = i;
            dom.appendChild(btn);
        }
        // 将棋盘渲染
        this.container.appendChild(dom);
        //获到所有button元素
        this.containerBtn = document.querySelectorAll("button");
    }
    
    //为棋盘每个格子绑定点击事件
    Game.prototype.addEventListener = function(){
        //调用事件委派
        this.delegate(this.container,'click','button',(e) =>{
            //如果此处已下棋子,则不执行方法
            if(e.target.innerText) return;
            this.flag?this.isRedOrblack(e,this.redSquare):this.isRedOrblack(e,this.blackSquare);
        });
    }
    
    //判断哪方下棋
    Game.prototype.isRedOrblack = function(e,square){
        // 保存当前事件的对象
        let nowDom = e.target;
        nowDom.innerText = square;
        // 记录在数组的对应类名序号的下标下
        this.array[nowDom.className] = square;
        this.historys.push(nowDom.className);
        //将悔棋记录清空
        this.unhistorys.length = 0;
        this.action(parseInt(nowDom.className),square);
        this.flag = !this.flag;
    }
    
    //判断胜负循环检索的方法
    Game.prototype.decide = function(value,Operator,str,count){
        for(i=1;i<5;i++){
            if(this.array[value+Operator*i] === str)
             count++;
             else break;
        }
        return count;
    }
    
    //判断胜负
    Game.prototype.action = function(value,str){
        //垂直方向上的棋子
        let Hcount=1;
        //水平方向上的棋子
        let Vcount=1;
        //左对角方向上的棋子
        let LTacr = 1;
        //右方向上的棋子
        let RTacr = 1;
        //垂直方向
        Hcount = this.decide(value,-10,str,Hcount);
        Hcount = this.decide(value,10,str,Hcount);
        // 水平方向
        Vcount = this.decide(value,-1,str,Vcount);
        Vcount = this.decide(value,1,str,Vcount);
        //上左边对角方向
        LTacr = this.decide(value,-11,str,LTacr);
        LTacr = this.decide(value,11,str,LTacr);
        //上右边对角方向
        RTacr = this.decide(value,-9,str,RTacr);
        RTacr = this.decide(value,9,str,RTacr);
        (Hcount === 5 || Vcount === 5 || LTacr === 5 || RTacr === 5) && alert(str + "胜利");
    }
    
    // 悔棋
    Game.prototype.needBank = function(){
        let BankDom = document.querySelector("#bank");
        //设置悔棋的相应数据
        let setBank = () =>{
            //拿到记录中的棋子下标
            let number = parseInt(this.historys.pop());
            // 记录需要悔棋棋子的下标
            this.unhistorys.push(number);
            // 记录棋子值的数组中的下标对应按钮的类名的值
            this.array[number] = "";
            // 更改下棋的选手
            this.flag = !this.flag;
            // 返回棋子位置的数组在末尾拿掉对应的值减去一就是该btn按钮的下标
            return number-1;
        }
        BankDom.onclick = () =>{
            if(this.historys.length){
                let number = setBank();
                // 对应的button按钮元素下标需要类名值减一
                this.containerBtn[number].innerText= "";
            }
        }
    }
    
    //撤销悔棋
    Game.prototype.needRevokeBank = function(){
        let revokeBank = document.querySelector("#revokeBank");
        //将悔掉的棋重新赋值
        function setValue(number,square,isAction){
            let target = this.containerBtn[number-1];
            target.innerText= square;
            this.array[number] = square;
            isAction && this.action(parseInt(target.className),square);
            // 更改下棋的选手
            this.flag = !this.flag;
        }
        //绑定事件
        revokeBank.onclick = () =>{
            if(!this.unhistorys.length) return;
            if(this.revocationType){
                for(let i=this.unhistorys.length-1;i>=0;i--){
                    this.historys.push(this.unhistorys[i]);
                    this.flag?setValue.call(this,this.unhistorys[i],this.redSquare,false):setValue.call(this,this.unhistorys[i],this.blackSquare,false)
                }
                //将悔棋记录清空
                this.unhistorys.length = 0;
            }
            else{
                let add = this.unhistorys.pop();
                this.historys.push(add);
                this.flag?setValue.call(this,add,this.redSquare,true):setValue.call(this,add,this.blackSquare,true);
            }
        }
    }
    //事件委派方法
    Game.prototype.delegate = function(element, eventType, selector, fn) {
        element.addEventListener(eventType, e => {
            let el = e.target;
            while (!el.matches(selector)) {
                if (element === el) {
                   el = null;
                   break;
                }
                el = el.parentNode;
            }
            el && fn.call(el, e, el)
        });
        return element
    };

如果发现这个方案中有问题,请在下方留言,欢迎随时指点

js五子棋游戏_第1张图片

你可能感兴趣的:(js五子棋游戏)