天天看點

原生 Javascript 編寫五子棋

原文位址:原生 Javascript 編寫五子棋

部落格位址:http://www.extlight.com

一、背景

近一個月沒寫 Javascript 代碼,有點生疏。正好浏覽網頁時彈出五子棋的遊戲廣告,于是想通過編寫這個小遊戲練練手。

二、簡單介紹

2.1 效果展示

2.2 實作思路

  1. 棋盤:通過圖檔(chessboard.png)和 div 标簽渲染出棋盤。
  2. 棋子:通過圖檔(black_flag.png、white_flag.png等)渲染出黑白棋子。落子前,滑鼠出會出現一個可以随滑鼠移動的棋子。我們建立一個浮動的 div,動态設定其 top 和 left 。
  3. 落子:給容器(class="container")添加 click 事件,給其添加對應的 classname。即被點選的單元格設定棋子背景圖檔。此外,需要判斷落子點是否存在棋子。
  4. 輸赢:使用二維數組儲存棋盤(棋子)狀态,通過橫向、縱向、左上到右下和右上到左下四個方向進行判斷是否有 5 個以上連續同顔色(樣式)的棋子。

2.3 涉及技術

DOM操作、面向對象、事件操作和間隔函數 setInterval

2.4 項目結構

三、實作步驟

3.1 繪制棋盤

style.css 内容:

html,body {
    padding: 0;
    margin: 0;
}

.container {
    position: relative;
    width: 540px;
    height: 540px;
    margin: 10px auto;
    padding-top: 7px;
    padding-left: 7px;
    background: url("../images/chessboard.png") no-repeat;
    cursor: pointer;
}

.none {
    position: absolute;
    width: 36px;
    height: 36px;
    box-sizing: border-box;
    /*border: 1px solid #fff;*/
}

.black_flag {
    position: absolute;
    width: 36px;
    height: 36px;
    background: url("../images/black_flag.png") no-repeat;
}

.black_flag_cur {
    position: absolute;
    background: url("../images/black_flag_cur.png") no-repeat;
    /*設定點選無效*/
    pointer-events: none;
}

.white_flag {
    position: absolute;
    width: 36px;
    height: 36px;
    background: url("../images/white_flag.png") no-repeat;
}

.white_flag_cur {
    position: absolute;
    background: url("../images/white_flag_cur.png") no-repeat;
    /*設定點選無效*/
    pointer-events: none;
}


           

chessboard.js 代碼:

var Chessboard = function() {
    // 儲存棋盤棋子狀态
    this.flagArr = [];
    this.size = 36;
}

// 初始化棋盤
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");

    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }

}
           

game.js 代碼:

var Game = function() {

}

Game.prototype.start = function() {
    var chessboard = new Chessboard();
    chessboard.init();
}
           

最終效果如下:

為了友善檢視 div 與棋盤圖檔中格子之間的對應關系,筆者将 div 邊框設定成白色。

從圖中我們可以看到,div 大小正好對應棋盤的落子點。我們将 div 背景設定成棋子圖檔就實作了落子操作。

3.2 繪制棋子

var Chessboard = function() {
     // 儲存棋盤棋子狀态
    this.flagArr = [];
    this.size = 36;
    
    // 預設黑色為先手
    this.currentFlag = true;

    // 儲存落子前的樣式映射
    this.flagCurMap = [];
    // 黑子
    this.flagCurMap[true] = "black_flag_cur";
    // 白子
    this.flagCurMap[false] = "white_flag_cur";
}

// 初始化棋盤
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");

    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }

    // 添加事件監聽器
    this.addListener(container);
}

// 落子事件監聽器
Chessboard.prototype.addListener = function() {
    var that = this;

    // 設定落子前的滑鼠樣式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }
}

           

結果如下圖:

3.3 落子

在 chessboard.js 的監聽器方法中添加落子的點選事件:

var Chessboard = function() {
    // 儲存棋盤棋子狀态
    this.flagArr = [];
    this.size = 36;

    // 預設黑色為先手
    this.currentFlag = true;

    // 儲存落子前的樣式映射
    this.flagCurMap = [];
    // 黑子
    this.flagCurMap[true] = "black_flag_cur";
    // 白子
    this.flagCurMap[false] = "white_flag_cur";

    // 儲存落子後的樣式映射
    this.flagMap = [];
    // 黑子
    this.flagMap[true] = "black_flag";
    // 白子
    this.flagMap[false] = "white_flag";

    // 儲存結果映射關系
    this.resultMap = [];
    this.resultMap[true] = "黑子勝利";
    this.resultMap[false] = "白子勝利";
}

// 初始化棋盤
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");

    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }

    // 添加事件監聽器
    this.addListener(container);
}

// 落子事件監聽器
Chessboard.prototype.addListener = function(container) {
    var that = this;

    // 設定落子前的滑鼠樣式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }

    // 落子監聽
    container.onclick = function(event) {
        // 判斷落子點是否存在棋子
        if (event.target.className != "none") {
            alert("此處不能落子!");
            return;
        }

        // 落子,設定棋子圖檔
        event.target.className = that.flagMap[that.currentFlag];

        // 換棋手
        that.currentFlag = !that.currentFlag;
    }
}

           

運作結果如下:

3.4 判斷輸赢

在 chessboard.js 的落子監聽實踐代碼中,判斷是否五連子:

// 落子事件監聽器
Chessboard.prototype.addListener = function(container) {
    var that = this;

    // 設定落子前的滑鼠樣式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }

    // 落子監聽
    container.onclick = function(event) {
        // 判斷落子點是否存在棋子
        if (event.target.className != "none") {
            alert("此處不能落子!");
            return;
        }

        // 落子,設定棋子圖檔
        event.target.className = that.flagMap[that.currentFlag];

        // 目前落子坐标
        var x = Math.floor(event.target.offsetLeft / that.size);
        var y = Math.floor(event.target.offsetTop / that.size);

        // 判斷是否勝利
        if (that._checkSuccess(x, y)) {
            document.getElementById("mouse").style.display = "none";
            container.onclick = null;
            document.body.onmousemove = null;
            alert(that.resultMap[that.currentFlag]);
            return;
        }

        // 換棋手
        that.currentFlag = !that.currentFlag;
    }
}

// 判斷棋局
Chessboard.prototype._checkSuccess = function(x, y) {
    var result = false;
    // 目前落子的樣式/顔色
    var className = this.flagArr[y][x].className;

    // 橫向判斷
    var count = 0;
    for (var i = 0; i < 15; i++) {
        if (className == this.flagArr[y][i].className) {
            count++;
            if (count >= 5) {
                return true;
            }
        } else {
            count = 0;
        }
    }

    // 縱向判斷
    for (var j = 0; j < 15; j++) {
        if (className == this.flagArr[j][x].className) {
            count++;
            if (count >= 5) {
                return true;
            }
        } else {
            count = 0;
        }
    }

    // 左上到右下判斷
    var a = y - x;
    var index = 0;
    if (a > 0) {
        for (a; a < 15; a++) {
            if (className == this.flagArr[a][index++].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    } else {
        a = Math.abs(a);
        for (a; a < 15; a++) {
            if (className == this.flagArr[index++][a].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    }

    // 右上到左下判斷
    var b = 14 - y -x;
    var index2 = 14;
    if (b > 0) {
        b = 14 - b;
        index2 = 0;
        for (b; b >= 0; b--) {
            if (className == this.flagArr[index2++][b].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    } else {
        b = Math.abs(b);
        for (b; b < 15; b++) {
            if (className == this.flagArr[index2--][b].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    }

    if (count >= 5) {
        result = true;
    }

    return result;
}
           

示範結果:

剩餘的一些文本提示,倒計時就不在此處介紹。具體代碼可以在下邊提供的連結中下載下傳。

四、源碼下載下傳

  • 源碼下載下傳

繼續閱讀