使用原生 JS 寫五子棋
作者:Tumars
源自:http://www.ferecord.com/create-gobang-with-javascriot.html
前言
聽說這是一道騰訊的面試題,可能網上已經有不少答案了,晚上沒事看到這道題就自己做了下。邏輯很簡單,考慮到是面試題,使用了 ES6 的語法。本文介紹下核心邏輯跟部分程式碼。
目錄
- 前言
- 目錄
- 思路
- 儲存已下的棋
- 判斷落子後是否勝利
- 對外 API 與 UI 繫結
- 結語
思路
核心邏輯大致如下:
- 棋盤上的點就是
x, y
的座標值; - 每次落子就給該點賦顏色值;
- 落子後判斷該點為中心的 4 條軸,每條軸上的 9 個點,含中心點是否能連續 5 個點顏色相同;
- 是的話就結束遊戲並判斷為該顏色值的持方勝利,否的話就改變顏色值繼續遊戲。
儲存已下的棋
我們建立個物件儲存已下的棋,該物件的結構形為{x6y9: 1, x3y8: 2, x20y1: 2 ...}
,初始為空,每次落子都給它新增屬性,屬性名錶示座標,屬性值表示棋子顏色,1
為白色,2
為黑色,未配置的屬性查詢返回0
。同時設兩個方法用來操作該物件。
var board = new Proxy({},{
get: function(target, property) {
if (property in target) {
return target[property];
} else {
return 0
}
}
})
//--------//
getProps(x,y) {
return 'x'+x+'y'+y
}
getColor(board, x, y) {
return board[this.getProps(x,y)]
}
判斷落子後是否勝利
首先獲取 4 條軸上的座標,我們設落子點的座標為[x, y],那該點左上角的左邊為 [x – 1, y – 1],右下角為 [x +1, y +1],所以每個方向都是 x,y 與 1, -1, 0 三個數兩兩形成的陣列加 4 次。如我們設落點座標為[0,0],則左上角的4個位置就是:
var
var lt = []
for(var i = 1;i<5;i++) {
lt.push([0-i, 0-i])
}
console.log(lt) // [[-1,-1], [-2, -2], [-3, -3], [-4, -4]]
可以看出8個方向的相加值分別為[-1,-1], [-1,0], [1, -1], [-1, 0],以及它們的相反值。
var roundDirect = [[-1,-1], [-1,0], [1, -1], [-1, 0]]
接下來寫個函式checkRoundDirect
用來判斷是否勝利,接收roundDirect
陣列作為引數:
// 判斷 4 個軸中是否有一個成立
checkRoundDirect(x, y, board, roundDirect) {
return roundDirect.some(direct => this.checkSingleDirect(x, y, board, direct))
}
這裡把每條軸的判斷交給checkSingleDirect
函式判斷:
// 判斷單個軸(兩個方向)是否成立
checkSingleDirect(x, y, board, direct) {
var leftDirect = direct
var rightDirect = direct.map(v=>-v)
var getNum = this.getDirectSameColorNum.bind(this, x, y, board)
return (getNum(leftDirect) + 1 + getNum(rightDirect)) >= 5
}
判斷單軸是否成立的邏輯是以落子為起點,它左側與右側的連續相同顏色的點棋子數目相加大於 4,再加上落子本身,就是形成了 5 子。
中心點與一側的連續相同顏色棋子的數量通過getDirectSameColorNum
函式獲取:
// 返回每個方向的顏色值相同的棋子數
getDirectSameColorNum(x, y, board, direct) {
var result = 0
var bindGetcolor = this.getColor.bind(this, board)
var activeColor = bindGetcolor(x,y)
for (var i = 1; i < 5; i++) {
var nextColor = bindGetcolor(x + i * direct[0], y + i * direct[1])
if (activeColor == nextColor) { result++ } else {break}
}
return result
}
這樣通過呼叫checkRoundDirect
函式並傳入當前點的x, y
引數以及已知的board, roundDirect
引數,就可以得到落子後是否勝利連成 5 子。
對外 API 與 UI 繫結
每個棋盤例項對外輸出以下屬性與介面。
屬性:
-
board
物件,每個屬性為已下棋子的座標,屬性值為該座標的顏色值; -
palyChess(x, y, colorNumber)
方法,是執行下棋的呼叫方法,引數分別為x,y
座標值與要下棋子的顏色值;
介面:
-
onEnd(color)
與onKeep(color)
,兩個回撥函式,每次落子後遊戲勝利結束及遊戲繼續的回撥事件,color是下次落子的顏色值。
同時構建一個棋盤的 UI 物件並返回一個配置函式用來初始化棋盤例項,這裡我就不寫 UI 物件的具體實現了,各位有興趣的可以檢視原始碼,UI 物件的主要目的是繫結棋盤例項、渲染頁面、繫結事件。
棋盤 UI 物件主要接收以下引數:
-
num
,棋盤大小,會輸出num * num
大小的棋盤
棋盤例項與 UI 繫結後共同輸出一個函式用來初始化棋盤,按如下方式呼叫:
// 初始化遊戲,(棋盤大小,{結束事件,落子事件})
Game.start(15,{
onEnd(color) {
alert(`遊戲結束, ${color === 1 ? '白子' : '黑子'}勝利`)
},
onKeep(color) {
document.getElementById('info').innerHTML = `${color !== 1 ? '白子' : '黑子'}回合`
}
})
結語
這個五子棋很簡單,只要有基本的 js 基礎跟清晰的思路就能很快做出來。原始碼中涉及到了作用域、原型、閉包、多型、柯里化等基礎知識,以及對物件、函式、陣列等方法的運用,同時使用了 ES6 的語法。
相關文章
- 原生JS+Canvas實現五子棋遊戲JSCanvas遊戲
- 一個比較牛的Js寫的五子棋JS
- [譯]使用 Rust 編寫快速安全的原生 Node.js 模組RustNode.js
- 使用Node.js原生API寫一個web伺服器Node.jsAPIWeb伺服器
- 用原生js手寫實現promiseJSPromise
- js五子棋效果JS
- 放棄jQuery, 使用原生jsjQueryJS
- JS之五子棋遊戲JS遊戲
- electron 使用 Node.js 原生模組Node.js
- 原生JS 編寫移動端 tab選項卡JS
- mysql支援原生json使用說明MySqlJSON
- 授人以漁式解析原生JS寫輪播圖JS
- 一道面試題,手寫原生ajax和jsonp面試題JSON
- javaScript(js)手寫原生任務定時器原始碼JavaScriptJS定時器原始碼
- 儘可能的使用原生js,而不是jQueryJSjQuery
- 儘可能的使用原生js而不是jQueryJSjQuery
- js_原生js獲取當前的使用者ipJS
- Vue使用JSBridge與原生APP通訊VueJSAPP
- 原生JS拖拽效果JS
- JS原生互動JS
- JS原生練習JS
- 原生JS 操作 DOMJS
- 原生JS DOM方法JS
- 編寫Node原生模組
- js原生dom物件和jQuery物件可以混合使用嗎?JS物件jQuery
- 用 go 寫的五子棋預測演算法Go演算法
- 如何用原生js來寫一個swiper滑塊外掛(上)原理JS
- 怎樣用原生js配合css的transition寫個無縫滾動JSCSS
- 初學練習,用Perl寫的命令列五子棋命令列
- ajax原生js封裝JS封裝
- js 輪播圖 (原生)JS
- js原生節點操作JS
- 原生js封裝AjaxJS封裝
- 使用原生js實現選項卡功能例項教程JS
- 如何使用原生 JS 實現一個文字劃線功能JS
- 使用原生JS開發一個搶紅包的小遊戲JS遊戲
- 原生JS和jQuery分別使用jsonp來獲取“當前天氣資訊”jQueryJSON
- Delphi原生JSON框架(一) TJsonValueJSON框架