前端人工智能:canvas 绘制棋盘及落子逻辑

快捷目录

前端人工智能:来谈谈前端人工智能

前端人工智能:前端AI研发之算法

前端人工智能:canvas 绘制棋盘线条

前端人工智能:canvas 绘制黑白棋子


前两节中,我们利用 canvas 绘制出了线条和棋子。那么在这一节当中,我们将利用前两节所学的知识把棋盘构建出来。把布局全部搭建完毕后,用于去承载我们后续用于人工智能的逻辑性代码。

使用 canvas 绘制棋盘

我们在前两节中知道应该怎么去画线了,而一个棋盘是由 15 * 15 的盘面组成的。那按照正常逻辑来说,我们肯定是通过循环去绘制这个盘面,而不是一根根去画对吧。

那接下来我们就开始正式绘制棋盘,而为了跟棋子区分开来,我就不使用黑色去绘制了,改用灰色(context.strokeStyle),并且要跟它说一声,我们要开始画了(context.beginPath()):

xml 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>

<body>
  <canvas id="chess" width=800 height=800></canvas>
</body>
<script type="text/javascript">
  var chess = document.getElementById("chess");
  var context = canvas.getContext("2d");
  context.strokeStyle = "#ccc";
  context.beginPath()
</script>
</html>

我们需要绘制 15 条线,也就是说我们需要去循环 15 次:

scss 复制代码
for (var i = 0; i < 15; i++) {
  // 起点
  context.moveTo(15, 15 + i * 30)
  // 终点
  context.lineTo(435, 15 + i * 30)
  // 结束绘画
  context.stroke()
}

然后我们再看看效果:

我们可以清楚地看到横线都出来了,那接下来我们来解释一下代码为什么要这么去写。我们需要绘制15条横线,那我们就从15开始( context.moveTo(15, 15 + i * 30)),这里的15是x轴横坐标。那么纵坐标呢,我们就希望它慢慢地往下涨,每次涨 30,所以是15 + i * 30

那么我们终点的context.lineTo(435, 15 + i * 30)为什么是 435 呢?我们每一行往下涨 30,然后一共涨 14 行,也就是 30 乘以 14 ,再加上我们第一行原本的 15,是不是就得到 435 了。不管是起点还是终点,我们绘制的都是横线,所以y轴都是不变的,就保持15 + i * 30

那我们的横线就解决了,接下来我们就得画竖线。那竖线其实跟我们的横线是差不多的,横线有了,那竖线是不是把x轴跟y轴反过来就可以了:

ini 复制代码
var chess = document.getElementById("chess");
var context = chess.getContext("2d");
context.strokeStyle = "#ccc";
context.beginPath()
for (var i = 0; i < 15; i++) {
  // 横线起点
  context.moveTo(15, 15 + i * 30)
  // 横线终点
  context.lineTo(435, 15 + i * 30)
  // 竖线起点
  context.moveTo(15 + i * 30, 15)
  // 竖线终点
  context.lineTo(15 + i * 30, 435)
  // 结束绘画
  context.stroke()
}

我们来看看效果:

那我们棋盘就完成了,颜色有点淡的我们再调整一下。那我们棋盘做好了,接下来是不是就得落子了呢?哎,这个就稍微有点难度了。为什么麻烦呢?因为这个就需要涉及到计算了。在我们落子的时候,你肯定没办法点得那么准对吧,很难去做到刚好就落到我们期望的位置。

基本落子逻辑处理

我们可以弄一个 3030 的范围,只要你落在这个范围上,我都会认为你就是落到这个点上了。也就是说每个点都会有自己 3030 的范围,即中心点上下左右 15 的范围,这就是我们解决落子点的一个判断方法。

那接下来我们就开始写落子点了,首先这个肯定会有一个 onclick 点击事件,这个没问题吧。但是我们使用的是canvas,它不像 div,没办法单独给每个点去添加一个点击事件。所以我们只能给 canvas 去添加:

ini 复制代码
chess.onclick = function (e) {
    console.log(e.offsetX, e.offsetY);
}

上面的代码中,我们打印了一个offsetXoffsetYoffsetXoffsetY是相对于点击元素的左上坐标。然后我们定义变量把点击的坐标值除以我们格子的大小 30,再通过向下取整(Math.floor):

ini 复制代码
chess.onclick = function (e) {
  // console.log(e.offsetX, e.offsetY);
  var i = Math.floor(e.offsetX / 30)
  var j = Math.floor(e.offsetY / 30)
  console.log(i, j);
}

接下来我们来看看这个得到的坐标值是不是我们想要的效果:

可以看到当我们点击左上角的时候,我们得到的坐标值为(0,0);点击第二行第二列时,我们可以得到坐标值(2,2)。那我们得到坐标值x和y后,我们现在就可以去进行绘画了。

那我们画这个圆的时候,首先得留意一下,我们这个棋盘距离左边是不是有 15 的距离。因为每个格子的中心点上下左右都是 15,那么我们画圆的时候就得把这个 15 给算进去。结合下图看一下:

比如我们点击红框的范围,此时我们要框选出这个盒子,是不是就得拿i乘以盒子的大小 30。但是我们还得加上棋盘距离左边那 15 的距离,所以我们这个圆的起始点x就得等于15+i*30,同理起始点y就得等于15+j*30

半径的话我们使用 13,因为 15 刚好就是盒子的半径,我们尽量取比半径小点的值。

scss 复制代码
// 复习一下
context.arc(圆心x轴的位置,圆心y轴的位置,半径,起始角度,终点角度,是否逆时针)

context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);

然后我们再按照黑棋的做法给它加反光的效果:

ini 复制代码
chess.onclick = function (e) {
 
  var i = Math.floor(e.offsetX / 30)
  var j = Math.floor(e.offsetY / 30)

  var context = chess.getContext("2d");
  context.beginPath();
  // 画圆
  context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
  // 增加反光效果
  var grd = context.createRadialGradient(15 + i * 30, 15 + j * 30, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0)
  // 给反光效果加颜色
  grd.addColorStop(0, 'black')
  grd.addColorStop(1, 'white')
  context.fillStyle = grd
  context.fill()
  context.stroke()
}

保存代码运行一下,我们来看看有没有落子成功。

我们可以看到棋子已经完美落地,而截至目前为止呢,我们把基本的布局基本都做完了。基本的落子逻辑也处理完了,但是这个逻辑目前还是存在问题的,我们只是简单地把落子的位置处理好了。

在本节中,我们完善了整个布局,并且已经开始运用简单的算法去处理落子的位置。那么在下一节中,我们将完善我们的落子逻辑,并且要对游戏的胜利规则进行算法的计算。


本节代码

xml 复制代码
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title></title>
</head>

<body>
  <canvas id="chess" width=800 height=800></canvas>
</body>
<script type="text/javascript">
  var chess = document.getElementById("chess");
  var context = chess.getContext("2d");
  context.strokeStyle = "#666";
  context.beginPath()

  // 绘制棋盘
  for (var i = 0; i < 15; i++) {
    // 横线起点
    context.moveTo(15, 15 + i * 30)
    // 横线终点
    context.lineTo(435, 15 + i * 30)
    // 竖线起点
    context.moveTo(15 + i * 30, 15)
    // 竖线终点
    context.lineTo(15 + i * 30, 435)
    // 结束绘画
    context.stroke()
  }


  // 落子
  chess.onclick = function (e) {
    // 横坐标
    var i = Math.floor(e.offsetX / 30)
    // 纵坐标
    var j = Math.floor(e.offsetY / 30)
    var context = chess.getContext("2d");
    context.beginPath();
    context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
    var grd = context.createRadialGradient(15 + i * 30, 15 + j * 30, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0)
    grd.addColorStop(0, 'black')
    grd.addColorStop(1, 'white')
    context.fillStyle = grd
    context.fill()
    context.stroke()
  }

  

</script>

</html>
相关推荐
Power20246664 分钟前
NLP论文速读|LongReward:基于AI反馈来提升长上下文大语言模型
人工智能·深度学习·机器学习·自然语言处理·nlp
数据猎手小k7 分钟前
AIDOVECL数据集:包含超过15000张AI生成的车辆图像数据集,目的解决旨在解决眼水平分类和定位问题。
人工智能·分类·数据挖掘
好奇龙猫12 分钟前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
开心工作室_kaic16 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
沉下心来学鲁班27 分钟前
复现LLM:带你从零认识语言模型
人工智能·语言模型
数据猎手小k27 分钟前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
有梦想的刺儿35 分钟前
webWorker基本用法
前端·javascript·vue.js
YRr YRr36 分钟前
深度学习:循环神经网络(RNN)详解
人工智能·rnn·深度学习
sp_fyf_20241 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
多吃轻食1 小时前
大模型微调技术 --> 脉络
人工智能·深度学习·神经网络·自然语言处理·embedding