1、閒情奕趣html
少時,聞奕而不知奕之趣,觀棋而不識棋之髓。近日,略習奕之規矩,演練一二,始覺其妙。今見各手談之軟件,心生一念,自編一演習軟件,以調閒暇之情,培對弈之趣,故取一名,曰:「閒情奕趣」。算法
——雪飄七月 chrome
近日忙裏偷閒得以編寫此對弈軟件,以HTML5爲基礎,canvas畫布繪製展現棋盤棋子,localStorage本地存儲本局的各個步驟。今日程序初具雛形,寫此日誌,以供你們交流學習。數據庫
下面上圖一張:canvas
2、棋佈星羅小程序
下面就來說棋盤棋子的繪製,咱們的繪製都是在canvas中一條線一個圓地繪製成的。數組
棋盤是19*19的線條與9個星位組成,9個星位就是9個以星位爲圓心的圓。socket
棋子的繪製也是畫圓,只是圓半徑較星位大,而棋子是經過一個19*19的數組存儲標記位來實現的,數組中361個值與棋盤上的361個位置一一對應。若數值爲0,表示沒有落子;數值若爲1,表示黑方落子;數值若爲2,表示白方落子。ide
- //獲取canvas畫布
- var canvas=document.getElementById('myCanvas');
- canvas.height = total_height;
- canvas.width = total_width;
- var cxt=canvas.getContext('2d');
- //畫棋盤
- var drawBoard = function(){
- //每次重畫棋盤以前清楚canvas
- cxt.clearRect(0, 0, canvas.width, canvas.height);
- cxt.beginPath();
- //描繪橫線
- for(var i = 0 ; i < 19 ; i++){
- var start_company_x = chessboard_start_x;
- var start_company_y = chessboard_start_y + company_y*i;
- var end_company_x = chessboard_end_x;
- var end_company_y = chessboard_start_y + company_y*i;
- cxt.lineWidth = 2;
- cxt.moveTo(start_company_x,start_company_y);
- cxt.lineTo(end_company_x,end_company_y);
- }
- //描繪豎線
- for(var j = 0 ; j < 19 ; j++){
- var start_company_x = chessboard_start_x + company_x*j;
- var start_company_y = chessboard_start_y;
- var end_company_x = chessboard_start_x + company_x*j;
- var end_company_y = chessboard_end_y;
- cxt.moveTo(start_company_x,start_company_y);
- cxt.lineTo(end_company_x,end_company_y);
- }
- cxt.stroke();
- //畫九星
- for(var i = 0 ; i < 3 ; i ++ ){
- var star_y = chessboard_start_y + company_y*(i*6+3);
- for(var j = 0 ; j < 3 ; j++){
- var star_x = chessboard_start_x + company_y*(j*6+3);
- cxt.fillStyle="black";
- cxt.beginPath();
- cxt.arc(star_x, star_y, 4, 0, Math.PI*2, false);
- cxt.stroke();
- cxt.fill();
- }
- }
- }
- //根據arr數組畫棋子
- var drawPiece = function(){
- for(var i in arr){
- for(var j in arr[i]){
- if(arr[i][j] == 1){
- //畫黑子
- var start_x = i*company_x + chessboard_start_x;//點擊的x座標起始位置
- var start_y = j*company_y + chessboard_start_y;//點擊的y座標起始位置
- cxt.fillStyle= "black";
- cxt.beginPath();
- cxt.arc(start_x, start_y, piece_radius, 0, Math.PI*2, false);
- cxt.stroke();
- cxt.fill();
- } else if(arr[i][j] == 2){
- //畫白子
- var start_x = i*company_x + chessboard_start_x;//點擊的x座標起始位置
- var start_y = j*company_y + chessboard_start_y;//點擊的y座標起始位置
- cxt.fillStyle= "white";
- cxt.beginPath();
- cxt.arc(start_x, start_y, piece_radius, 0, Math.PI*2, false);
- cxt.stroke();
- cxt.fill();
- }
- }
- }
- }
3、旁觀者清學習
當局者迷,旁觀者清。
這一塊其實我想說的是對弈的步驟,固然不是真的對弈的步驟,而是對弈程序中對弈提子的算法。
下面這塊代碼就是用來計算落子處是否能夠提子的方法,
首先,方法中依次判斷該子上方、左方、右方、下方的棋子是否可以被提,若是能夠被提,就提子,而後返回true,這個方法的名稱是checkOthersidePiece(x,y,side);
而後,查看本子是否會致使本方棋子被提,若會被提,提子,而後返回true,這個方法依然是checkOthersidePiece(x,y,side);
- //檢查該落子是否能夠提子,能夠就提子
- var checkPiece = function(coordinate_x,coordinate_y){
- var myside = 0;
- var otherside = 0;
- var iskill = false;
- if(side == "black"){
- myside = 1;
- otherside = 2;
- }else if(side == "white"){
- myside = 2;
- otherside = 1;
- }
- //若是該子上方是對方棋子或者爲第一行
- if((coordinate_y>0 && arr[coordinate_x][coordinate_y-1] == otherside)){
- if(checkOthersidePiece(coordinate_x,coordinate_y-1,otherside)){
- iskill = true;
- }
- }
- //若是該子左方是對方棋子或者爲第一行
- if((coordinate_x>0 && arr[coordinate_x-1][coordinate_y] == otherside)){
- if(checkOthersidePiece(coordinate_x-1,coordinate_y,otherside)){
- iskill = true;
- }
- }
- //若是該子右方是對方棋子或者爲第十九行
- if((coordinate_x<18 && arr[coordinate_x+1][coordinate_y] == otherside)){
- if(checkOthersidePiece(coordinate_x+1,coordinate_y,otherside)){
- iskill = true;
- }
- }
- //若是該子下方是對方棋子或者爲第十九行
- if((coordinate_y<18 && arr[coordinate_x][coordinate_y+1] == otherside)){
- if(checkOthersidePiece(coordinate_x,coordinate_y+1,otherside)){
- iskill = true;
- }
- }
- //若是有提掉對方棋子
- if(iskill == true){
- //監測本子落子後己方棋子是否會被提
- }else {
- //alert(JSON.stringify(arr));
- checkOthersidePiece(coordinate_x,coordinate_y,myside);
- }
- }
接下來咱們就來看看這個神通廣大的checkOthersidePiece(x,y,side);方法
該方法中新建一個19*19的數組,而後從該棋子位置循環遍歷上、左、右、下的棋子,
若是周邊棋子有空的,那將該空的位置標誌位改成3;
若是周邊棋子爲己方的繼續遍歷該已方棋子的周邊棋子,循環往復,直到遍歷完與本子相連的已方棋子
- /**
- * 檢查該子是否會被提,若能被提,提子
- * @param coordinate_x (該子x座標)
- * @param coordinate_y (該子y座標)
- * @param side 1(黑子) or 2(白子)
- */
- var checkOthersidePiece = function(coordinate_x,coordinate_y,side){
- //新建一個二維數組,用於排放與該子鏈接的本方棋子
- var connection_arr = new Array(19);
- for(var i = 0 ; i < 19 ; i++){
- var connection_arrj = new Array(19);
- for(var j = 0 ; j < 19 ; j++){
- connection_arrj[j] = 0;
- }
- connection_arr[i] = connection_arrj;
- }
- var isdead = true;//是否被提,默認爲被提
- var deadcount = 0;
- //alert("1111"+JSON.stringify(connection_arr)+coordinate_x+":"+coordinate_y);
- //將全部與本子相連的同色棋子組成本數組
- connection_arr = setconnection_arr(connection_arr,coordinate_x,coordinate_y,side);
- //alert("2222"+JSON.stringify(connection_arr));
- //遍歷該connection_arr數組,如有3則,不死
- for(var i in connection_arr){
- for(var j in connection_arr[i]){
- if(connection_arr[i][j] == 3){
- //console.log("i+j:"+i+"+"+j);
- isdead = false;
- }
- }
- }
- //若是會被提,則提子
- if(isdead == true){
- for(var i in connection_arr){
- for(var j in connection_arr[i]){
- if(connection_arr[i][j] == side){
- arr[i][j] = 0;
- deadcount++;
- }
- }
- }
- }
- console.log("isdead:"+isdead+ ":提子數量:"+ deadcount);
- //如有提子返回true
- if(deadcount > 0){
- return true;
- }else {
- return false;
- }
- }
再而後,咱們來看一看這個遞歸遍歷的方法吧setconnection_arr(arr,x,y,side);
在本方法中作的就是將本落子周圍的己方棋子添加到connection_arr中,同時把周圍的氣以3爲標誌位添加到connection_arr中。
- /**
- * 遞歸組織鏈接的此方棋子
- * @param connection_arr
- * @param coordinate_x
- * @param coordinate_y
- * @param side
- * @return {*}
- */
- var setconnection_arr = function(connection_arr,coordinate_x,coordinate_y,side){
- //設置數組爲本塊相連的
- if(connection_arr[coordinate_x][coordinate_y] != side && arr[coordinate_x][coordinate_y] == side){
- connection_arr[coordinate_x][coordinate_y] = side;
- console.log(coordinate_x + "====" + coordinate_y);
- //若是該黑子上方是白子而且不爲第一行
- if(coordinate_y>0 && arr[coordinate_x][coordinate_y-1] == side){
- connection_arr = setconnection_arr(connection_arr,coordinate_x,coordinate_y-1,side);
- }else if(coordinate_y>0 && arr[coordinate_x][coordinate_y-1] == 0){
- connection_arr[coordinate_x][coordinate_y-1] = 3;
- }
- //若是該黑子左方是白子或者爲第一行
- if(coordinate_x>0 && arr[coordinate_x-1][coordinate_y] == side){
- connection_arr = setconnection_arr(connection_arr,coordinate_x-1,coordinate_y,side);
- }else if(coordinate_x>0 && arr[coordinate_x-1][coordinate_y] == 0){
- connection_arr[coordinate_x-1][coordinate_y] = 3;
- }
- //若是該黑子右方是白子或者爲第十九行
- if(coordinate_x<18 && arr[coordinate_x+1][coordinate_y] == side){
- connection_arr = setconnection_arr(connection_arr,coordinate_x+1,coordinate_y,side);
- }else if(coordinate_x<18 && arr[coordinate_x+1][coordinate_y] == 0){
- connection_arr[coordinate_x+1][coordinate_y] = 3;
- }
- //若是該黑子下方是白子或者爲第十九行
- if(coordinate_y<18 && arr[coordinate_x][coordinate_y+1] == side){
- connection_arr = setconnection_arr(connection_arr,coordinate_x,coordinate_y+1,side);
- }else if(coordinate_y<18 && arr[coordinate_x][coordinate_y+1] == 0){
- connection_arr[coordinate_x][coordinate_y+1] = 3;
- }
- }
- //console.log("x:"+coordinate_x+"y:"+coordinate_y);
- return connection_arr;
- }
4、白璧微瑕
如今雖然能夠順暢地落子了,可是對於一些規則仍是沒有做限制,好比說:
一、剛被吃掉一子的地方不可立馬落子(這須要添加一個機制)
二、不可悔棋,雖然說落子無悔大丈夫,只是不當心點錯位置致使整盤棋都廢掉,實在有點惋惜(這個能夠引入數據庫來解決)
等等等等,這些我會在以後的時間裏慢慢改進
將這些能想到的改進以後,打算以socketio爲基礎作一個在線的對弈的小程序。
最後,歡迎各位IT大神以及各位喜歡圍棋的大神們不吝賜教……
固然,有問題的也歡迎多多提問……
…………
什麼?源碼?好吧,點擊下面的附件go.rar,下載到本地以chrome或firefox打開canvasgo.html便可跑起來啦!
效果?想看效果?給你個傳送門吧!記得用chrome或firefox打開。