JS实现五子棋——UI篇

介绍内容

前些时间,阿尔法狗对战柯洁围棋大赛很热门,那只是人工智能中的一个方向,展示了机器能代替人做某些事情。
而围棋是很讲究智力的游戏,所以实现起来也是很难的,Google花了很多钱这在方面。
那段时间我也用JS写了一个小游戏——五子棋,五子棋相对来讲简单很多。我那时候在公众号上展示给大家,好像大家兴趣不大,可能是因为五子棋游戏太过简单,又或者是对我推的东西不感兴趣。那个公众号已经被封杀了,现在我又重新开了一个公众号。下面有二维码,如果有兴趣可以推一下。
现在我要写一下教程,你们可以点击这里体验一番。
这个实例完全是用JS实现的,有UI部分也有AI部分,我们需要有一定的canvas和JS的知识就足够了。

  • 棋盘的实现
    1.canvas绘画直线。
    2.设置画笔的颜色。

  • 棋子的实现
    1.canvas画圆。
    2.填充渐变色。

页面结构

页面上有一个正方形的棋盘,我们用画布canvas来实现棋盘。

<canvas id="chess" width="450px" height="450px"></canvas>

棋盘有一定的阴影效果,使棋盘更美观些。我们通过定义CSS样式来实现。

canvas {
    display: block;
    margin: 50px auto;
    box-shadow: -2px -2px 2px #EFEFEF, 5px 5px 5px #B9B9B9;
}

这个时候的效果如下,有一定的阴影效果:

阴影效果

画棋盘的网格

这里用到JS来控制canvas画棋盘,我们知道五子棋的棋盘是由些纵线和横线组成的,棋盘的样子如下:

网格的实现

分别有15条纵线和横线,每个格子为30px的正方形,棋盘边缘有15px的补白。

在棋盘中实现画线

在JS中用画笔画一条线:

var chess = document.getElementById("chess") ;//获取canvas
var context = chess.getContext("2d");
context.strokeStyle = "#aaa" ;//画笔的颜色
context.moveTo(15,15);
context.lineTo(435,15);
context.stroke();

效果:

一条横线的画法

开始画棋盘的网线

我们回顾了一下画线,那接下来我们用循环方式画15条纵线和15条横线:

var chess = document.getElementById("chess") ;//获取canvas
var context = chess.getContext("2d");

context.strokeStyle = "#aaa" ;//画笔的颜色

for (var i=0; i<15; i++) {//通过循环画网格
    context.moveTo(15,15+i*30);
    context.lineTo(435,15+i*30);
    context.stroke();
    context.moveTo(15+i*30,15);
    context.lineTo(15+i*30,435);
    context.stroke();
}

效果:

完成网格

棋盘背景

可能你已经发现了,白色的棋盘背景视觉非常不好,那么接下来我们就来为棋盘添加背景。我们选择一张木色的图片,如果你想为棋盘添加你特有的水印,可以通过制图软件添加。
H5添加图片的方法是通过画图的方式,画上去就会覆盖掉之前画的网格,所以我们通过对画网格的代码进行封装成一个函数,画完背景后再调用画网格的函数来达到不被覆盖的效果。总的代码如下:

var img = new Image();
img.src = "img/2.png" ; 
img.onload = function (){
    context.drawImage(img,0,0,450,450);
    drawLine();
}
function drawLine () {//把画线封装成函数
    for (var i=0; i<15; i++) {//通过循环画网格
        context.moveTo(15,15+i*30);
        context.lineTo(435,15+i*30);
        context.stroke();
        context.moveTo(15+i*30,15);
        context.lineTo(15+i*30,435);
        context.stroke();
    }
}

最终的效果:

棋盘效果

画棋子

我们是在背景上画棋子的,所以画棋子的代码应该放在onload方法里面。

棋子的画法

    context.beginPath() ;
    context.arc(200,200,100,0,2*Math.PI);
    context.closePath() ;
    context.fill();

解释一下特别的代码context.arc(200,200,100,0,2*Math.PI);四个参数分别是圆心横坐标、圆心纵坐标、半径、开始弧度、结束弧度。
context.fill();给圆填充颜色。
效果:

一个棋子

这个效果还不像棋子,棋子中间要有些发亮才行的,我们给棋子中间加一个亮度的渐变:
我们直接看onload方法里的代码,再解释其中重要的代码:

img.onload = function (){
    context.drawImage(img,0,0,450,450);
    drawLine();
    //画棋子  
    context.beginPath() ;
    context.arc(200,200,100,0,2*Math.PI);
    context.closePath() ;
    var gradient = context.createRadialGradient(200, 200, 50, 200, 200, 20);
    gradient.addColorStop(0, "#0a0a0a");
    gradient.addColorStop(1, "#636766");
    context.fillStyle = gradient ;
    context.fill();
}

这些代码的操作非常简单,首先画一个圆,然后填充渐变色,var gradient = context.createRadialGradient(200, 200, 50, 200, 200, 20);这是定义一个有渐变的颜色变量,前三个参数是圆心在(200,200)处,半径为50的一个圆,同理,后三个参数是圆心在(200,200)处,半径为20的一个圆。

gradient.addColorStop(0, "#0a0a0a");
gradient.addColorStop(1, "#636766");
context.fillStyle = gradient ;

这三行代码分别设置上面的第一个圆的颜色,第二个圆的颜色,和把渐变色填充给棋子。最终的填充效果是在圆心为(200,200)内径为20,外径为50的一个圆环上产生渐变。
棋子最后的效果:

中间发亮

落子样式

那我们通过上面的学习就会画一个棋子啦,接下来我们改变棋子的半径大小和颜色就能得到我们想要的棋子了。
代码放在onload里面会显得很杂乱,这是我们不想看到的,所以我们必须封装成函数再使用。
封装成以下的函数:

var oneStep = function (i, j, me){//i,j分别是在棋盘中的定位,me代表白棋还是黑棋
    context.beginPath() ;
    context.arc(15+i*30, 15+j*30, 13, 0, 2*Math.PI);//圆心会变的,半径改为13
    context.closePath() ;
    var gradient = context.createRadialGradient(15+i*30+2, 15+j*30-2, 15, 15+i*30, 15+j*30, 0);
    if(me){
        gradient.addColorStop(0, "#0a0a0a");
        gradient.addColorStop(1, "#636766");
    }else{
        gradient.addColorStop(0, "#D1D1D1");
        gradient.addColorStop(1, "#F9F9F9");
    }
    context.fillStyle = gradient ;
    context.fill();
}

主要改变了三部分,改变圆心和半径,根据接收到的参数确定圆心,判断是黑子还是白子。
然后通过在onload方法里调用函数来落子:

oneStep(0,0,true) ;
oneStep(1,1,false) ;

效果:

改变棋子大小

实现鼠标落子

实现用鼠标点击棋盘就落下一颗棋子,我们用在画布上绑定单击事件来实现,代码如下:

var me = true ;
chess.onclick = function (e){
    var x = e.offsetX ;
    var y = e.offsetY ;
    var i = Math.floor(x/30) ;
    var j = Math.floor(y/30) ;
    oneStep(i,j,me);
    me = !me ;
}

通过e.offsetXe.offsetY两个属性得到坐标,后转化成i和j,再调用oneStep()方法,定义一个变量me来决定是黑子还是白子,每点击一次就改变一次me的值。
效果:

鼠标落子

vzsf
这时候还有一个问题,已经下了黑子的点,重新点击还会被白子覆盖掉。那怎么解决呢?
首先我们定义一个二维数组,存放所有的落子点,如果有落子,就给其记录下来。落子的时候再判断是否已经落子,如果已经落子了就不允许重新落子。思路就是这样。
二维数组代码:

var chessBoard = [] ;
for (var i=0; i<15; i++) {
    chessBoard[i] = [] ;
    for (var j=0; j<15; j++) {
        chessBoard[i][j] = 0;
    }
}

二维数组的初始值都是0,然后在单击事件的方法里添加一个判断:

chess.onclick = function (e){
    var x = e.offsetX ;
    var y = e.offsetY ;
    var i = Math.floor(x/30) ;
    var j = Math.floor(y/30) ;
    if(chessBoard[i][j] == 0){
        oneStep(i,j,me);
        if(me){
            chessBoard[i][j] = 1 ;
        }else{
            chessBoard[i][j] = 2 ;
        }
        me = !me ;  
    }
}

落子位置等于0才可以落子,落完子后给相应的点附非0值,黑子就附1,白子附2。
效果:

落子不能覆盖

总结

这是UI篇,你继续翻翻我的主页,肯定能找到AI篇的。

  • 棋盘的实现
    通过循环画直线

  • 棋子的实现
    画出你想要的棋子,渐变填充颜色,封装成一个函数供调用。

  • 落子的实现
    用数组存放每一个落子点,满足条件就落下对应的子。

UI篇到此就告一段落了,这里用到的知识并不多,相应的方法想了解更多可以到W3上看。AI篇将会在后期推出,到时候就可以实现人机交互了。跟自己打的代码比试五子棋不再只是一种想法,只要你动手,一定能实现。另外,想跟我的代码比试一下可以点击这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 为什突然做这个,因为这是个笔试题,拖了一个月才写(最近终于闲了O(∩_∩)O),废话不多说,说说这个题吧 题目要求...
    Stevenzwzhai阅读 2,688评论 0 5
  • 最近一直在学习Android自定义View方面的知识,正好看到一个讲解制作五子棋小游戏的案例,遂学习一番,记录下学...
    冰鉴IT阅读 2,957评论 5 16
  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,606评论 2 32
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 20161203 11:00 业果不会错乱。【念佛杂话】 12:00 恭敬,来自内心的渴求。【念佛杂话】 13:0...
    整点念佛阅读 248评论 0 0