秉承着會就分享,不會就折騰的技術宗旨。本身利用週末的時間將休閒小遊戲-五子棋
從新梳理了一下,整理成一個小的教程,分享出來給你們指點指點。javascript
五子棋的規則我簡單梳理而且改造以下哈:java
正式比賽的規則,能夠戳百度百科瞭解下哈--五子棋。git
這裏實現的五子棋
小遊戲是使用javascript語言進行編寫的,使用到了es6語法,面向對象的思想進行。es6
// 設置五子棋類
class Gobang {
constructor(options={}){
this.options = options;
this.init();
}
init() {
const { options } = this;
}
}
// 實例化對象
let gobang = new Gobang({});
複製代碼
上面的Gobang類中,包含了一個constructor和init方法。其中constructor方法是類默認的方法,經過new命令生成對象實例時候,自動調用該方法。一個類必須有一個constructor方法,若是沒有顯式定義,一個空的constructor方法會默認添加。而後就是init方法了,這裏我是整個類的初始化的入口方法。使用類進行的面向對象方法進行編寫,比較好管理代碼和功能的擴展。github
棋盤分爲兩種,一種是視覺(物理)上的棋盤,另一個是邏輯上的棋盤,你是看不見的。下面的一張圖就很形象地展現了20*20棋盤的物理和邏輯方式。canvas
繪製物理棋盤,咱們這裏使用到了canvas
的相關知識點,控制畫筆繪製棋盤:數組
// 繪製出物理棋盤
drawChessBoard() {
const context = this.chessboard.getContext('2d');
const {padding, count, borderColor} = this.options.gobangStyle;
let half_padding = padding/2;
this.chessboard.width = this.chessboard.height = padding * count;
context.strokeStyle = borderColor;
// 畫棋盤
for(var i = 0; i < count; i++){
context.moveTo(half_padding+i*padding, half_padding);
context.lineTo(half_padding+i*padding, padding*count-half_padding);
context.stroke(); // 這裏繪製出的是豎軸
context.moveTo(half_padding, half_padding+i*padding);
context.lineTo(count*padding-half_padding, half_padding+i*padding);
context.stroke(); // 這裏繪製出的是橫軸
}
}
複製代碼
這裏使用到的padding,count,borderColor
等都是在實例化的時候傳進去的。這樣提升了可配置性和管理。上面的代碼是繪製物理上的棋盤,那麼邏輯上的棋盤雖然不可以繪製出來,可是咱們能夠表示出來。這裏咱們使用了二維數組的方法去記錄邏輯位置,好比(0,0)
點對應的數組下標是[0][0]
;而後(1,2)
點對應的下標是[1][2]
...以此類推。而後咱們再爲這個邏輯點賦值爲0,表示當前點沒有落子。ui
// 繪製邏輯矩陣棋盤
initChessboardMatrix(){
const {count} = this.options.gobangStyle;
const checkerboard = [];
for(let x = 0; x < count; x++){
checkerboard[x] = [];
for(let y = 0; y < count; y++){
checkerboard[x][y] = 0;
}
}
}
複製代碼
物理棋盤和邏輯棋盤有了以後,就能夠考慮到將物理棋盤和邏輯棋盤關聯起來了。這個比較簡單,就是要計算真實的單元格位置進行除法操做便可。這步的管理在後面的落子步驟有提到。this
五子棋的棋子有且僅有兩種--黑色棋子或者白色棋子。這裏也是使用canvas的知識點來繪製棋子。spa
// 繪製黑棋或白棋
drawChessman(x , y, isBlack){
const context = this.chessboard.getContext('2d');
let gradient = context.createRadialGradient(x, y, 10, x-5, y-5, 0);
context.beginPath();
context.arc(x, y, 10, 0, 2 * Math.PI);
context.closePath();
if(isBlack){
gradient.addColorStop(0,'#0a0a0a');
gradient.addColorStop(1,'#636766');
}else{
gradient.addColorStop(0,'#d1d1d1');
gradient.addColorStop(1,'#f9f9f9');
}
context.fillStyle = gradient;
context.fill();
}
複製代碼
上一節的繪製黑棋和白棋的方法是在單獨一個頁面出來繪製的。如今咱們將繪製棋子和棋盤整合,並實現人人對戰的下棋模式。
咱們要監聽點擊在棋盤上的事件,而後關聯物理棋盤和邏輯棋盤點,以後在相應的地方刻畫棋子便可。
// 監聽落子
listenDownChessman() {
// 監聽點擊棋盤對象事件
this.chessboard.onclick = event => {
let {padding} = this.options.gobangStyle;
let {
offsetX: x,
offsetY: y,
} = event;
x = Math.abs(Math.round((x-padding/2)/this.lattice.width));
y = Math.abs(Math.round((y-padding/2)/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.role = Object.is(this.role , 1) ? 2 : 1;
}
}
}
複製代碼
在雙方下棋的時候,容許雙方對已經下的棋子進行調整,也就是悔棋。以下截圖展現功能:
實現悔棋功能的時候,須要知道下棋的歷史記錄和當前的落子步數和角色。對於歷史的記錄,這裏對每一步的落子都使用一個對象進行存儲,並放到一個history
的數組裏面進行保存。
// 悔棋
regretChess() {
if(this.history.length){
const prev = this.history[this.currentStep - 1];
if(prev){
const {
x,
y,
role
} = prev;
this.minusStep(x,y);
this.checkerboard[prev.x][prev.y] = 0;
this.currentStep--;
this.role = Object.is(role,1) ? 1 : 2;
}
}
}
// 銷燬棋子
minusStep(x, y) {
const context = this.chessboard.getContext('2d');
const {padding, count} = this.options.gobangStyle;
context.clearRect(x*padding, y*padding, padding,padding);
}
複製代碼
上面的代碼確實是實現了悔棋功能,可是,在實現悔棋的時候,已經破壞掉了棋盤的UI,由於咱們是使用canvas的clearRect
方法,將撤銷的棋子使用新的四邊形進行覆蓋,那也就覆蓋了撤銷棋子處的物理棋盤了。爲了彌補這個被覆蓋的物理棋盤,咱們得從新繪製出此處座標的新物理棋盤線條。這裏的修復要考慮到落子在棋盤的不一樣位置,要分九種不一樣的狀況進行修復:
// 修補刪除後的棋盤,將九種狀況的不一樣參數傳過來便可
fixchessboard (a , b, c , d , e , f , g , h){
const context = this.chessboard.getContext('2d');
const {borderColor, lineWidth} = this.options.gobangStyle;
context.strokeStyle = borderColor;
context.lineWidth = lineWidth;
context.beginPath();
context.moveTo(a , b);
context.lineTo(c , d);
context.moveTo(e, f);
context.lineTo(g , h);
context.stroke();
}
複製代碼
有容許悔棋,那麼就有容許撤銷悔棋這樣子才合理。同悔棋功能,撤銷悔棋是須要知道下棋的歷史記錄和當前的步驟和棋子角色的。
// 撤銷悔棋
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;
}
}
複製代碼
五子棋的的結束也就是必需要決出勝利者,或者是棋盤沒有位置能夠下棋了。這裏考慮決出勝利爲遊戲結束的切入點,上面也說到了如何纔算是一方獲勝--橫線、豎線或者斜線上有連續五個同一色的棋子
。那麼咱們就對這四種狀況進行處理,咱們在矩陣中記錄當前點擊的數組點中是否有連續的五個1(黑子)或者連續的五個2(白子)便可。以下截圖的x軸上的白子獲勝狀況,注意gif圖右側打印出來的數組內容:
// 裁判觀察棋子,判斷獲勝一方
checkReferee(x , y , role) {
if((x == undefined)||(y == undefined)||(role==undefined)) return;
const XContinuous = this.checkerboard.map(x => x[y]); // x軸上連殺
const YContinuous = this.checkerboard[x]; // y軸上連殺
const S1Continuous = []; // 存儲左斜線連殺
const S2Continuous = []; // 存儲右斜線連殺
this.checkerboard.forEach((_y,i) => {
// 左斜線
const S1Item = _y[y - (x - i)];
if(S1Item !== undefined){
S1Continuous.push(S1Item);
}
// 右斜線
const S2Item = _y[y + (x - i)];
if(S2Item !== undefined) {
S2Continuous.push(S2Item);
}
});
}
複製代碼
至此,已經一步步講解完如何開發一個可以在pc上愉快玩耍的休閒小遊戲-五子棋了。不妥之處還請指正哈 @~@
後話
五子棋的體驗地址--休閒遊戲-五子棋
文章首發地址--github-五子棋遊戲
代碼倉庫地址--github-五子棋教程
創做文章不易,既然都看到這裏了,留個贊再走唄~