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打开。