纯JS实现五子棋游戏兼容各浏览器(附源码)

下面是详细讲解“纯JS实现五子棋游戏兼容各浏览器(附源码)”的完整攻略。

1. 实现概述

该游戏是基于纯JS实现的,实现思路如下:

  1. 初始化画布,绘制棋盘;
  2. 监听鼠标点击事件,判断点击位置是否合法;
  3. 判断当前玩家是否胜利;
  4. 实现AI逻辑,即电脑自动下棋的过程。

2. 实现过程

2.1 初始化画布,绘制棋盘

首先,在HTML中定义一个canvas元素用于绘制游戏界面,然后通过JS获取canvas元素,设置画布大小、线条颜色等样式。接着,绘制棋盘。代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>五子棋游戏</title>
  <style>
    canvas {
      border: 1px solid #999;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="600" height="600"></canvas>
  <script>
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var ROW = 15;
    var GAP = canvas.width / (ROW + 1);
    var qizi = {};

    for (var i = 0; i < ROW; i++) {
      ctx.moveTo(GAP, GAP * (i + 1));
      ctx.lineTo(canvas.width - GAP, GAP * (i + 1));
      ctx.stroke();
      ctx.moveTo(GAP * (i + 1), GAP);
      ctx.lineTo(GAP * (i + 1), canvas.width - GAP);
      ctx.stroke();
    }
  </script>
</body>
</html>

2.2 监听鼠标点击事件,判断点击位置是否合法

我们需要监听鼠标点击事件,获取鼠标点击的坐标,并将坐标转换成棋盘交叉点的坐标。由于棋盘是一个二维数组,因此将获取的坐标作为数组的下标,将棋子信息存储在二维数组中。完整代码如下:

canvas.onclick = function(e) {
  var x = e.offsetX;
  var y = e.offsetY;
  var i = Math.round(x / GAP) - 1;
  var j = Math.round(y / GAP) - 1;
  if (qizi[i + '-' + j]) {
    return;
  }
  drawQizi(i, j, true);
  qizi[i + '-' + j] = true;
}

其中,drawQizi函数用于绘制棋子。

2.3 判断当前玩家是否胜利

当玩家下完一步棋时,需要判断当前玩家是否胜利。这里使用一个五位数的二进制数表示棋盘上的落子情况,其中1表示黑子,0表示白子。比如,如果一行连成了五个黑子,则该行的二进制数为11111,即31;而如果该行只连成了四个黑子,则该行的二进制数为11110,即30。判断胜负时,只需要检查当前玩家的落子情况是否满足五子连珠即可。完整代码如下:

// 判断当前玩家是否胜利
function check(pos, color) {
  var table = {};
  for (var i in qizi) {
    if (qizi[i] === color) {
      table[i] = true;
    }
  }
  var sp = pos.split('-');
  var i = sp[0], j = sp[1];
  var num1 = 1, num2 = 1, num3 = 1, num4 = 1;
  var table2 = {};
  table2[i + '-' + j] = true;
  // 横向
  for (var k = 1; table[(+i + k) + '-' + j]; k++) {
    num1++;
    table2[(+i + k) + '-' + j] = true;
  }
  for (var k = 1; table[(+i - k) + '-' + j]; k++) {
    num1++;
    table2[(+i - k) + '-' + j] = true;
  }
  if (num1 >= 5) return true;
  // 竖向
  for (var k = 1; table[i + '-' + (+j + k)]; k++) {
    num2++;
    table2[i + '-' + (+j + k)] = true;
  }
  for (var k = 1; table[i + '-' + (+j - k)]; k++) {
    num2++;
    table2[i + '-' + (+j - k)] = true;
  }
  if (num2 >= 5) return true;
  // 左斜线
  for (var k = 1; table[(+i + k) + '-' + (+j + k)]; k++) {
    num3++;
    table2[(+i + k) + '-' + (+j + k)] = true;
  }
  for (var k = 1; table[(+i - k) + '-' + (+j - k)]; k++) {
    num3++;
    table2[(+i - k) + '-' + (+j - k)] = true;
  }
  if (num3 >= 5) return true;
  // 右斜线
  for (var k = 1; table[(+i - k) + '-' + (+j + k)]; k++) {
    num4++;
    table2[(+i - k) + '-' + (+j + k)] = true;
  }
  for (var k = 1; table[(+i + k) + '-' + (+j - k)]; k++) {
    num4++;
    table2[(+i + k) + '-' + (+j - k)] = true;
  }
  if (num4 >= 5) return true;
  return false;
}

2.4 实现AI逻辑

实现AI逻辑,让电脑自动下棋。思路是根据已有的落子情况,计算每个空位的得分,选择得分最高的点为电脑下一步落子的位置。完整代码如下:

canvas.onclick = function(e) {
  var x = e.offsetX;
  var y = e.offsetY;
  var i = Math.round(x / GAP) - 1;
  var j = Math.round(y / GAP) - 1;
  if (qizi[i + '-' + j]) {
    return;
  }
  drawQizi(i, j, true);
  qizi[i + '-' + j] = true;
  if (check(i + '-' + j, true)) {
    alert('恭喜您,您赢了!');
    canvas.onclick = null;
    return;
  } else {
    paita();
    if (check(ai.x + '-' + ai.y, false)) {
      alert('很遗憾,您输了!');
      canvas.onclick = null;
      return;
    }
  }
}
function paita() {
  var zuobiao = {};
  var max = -Infinity;
  for (var i in qizi) {
    var x = parseInt(i.split('-')[0]);
    var y = parseInt(i.split('-')[1]);
    var hang = {}, lie = {}, x1xie = {}, x2xie = {};
    for (var j in qizi) {
      if (parseInt(j.split('-')[0]) === x) {
        hang[j] = true;
      }
      if (parseInt(j.split('-')[1]) === y) {
        lie[j] = true;
      }
      if (parseInt(j.split('-')[0]) - x === parseInt(j.split('-')[1]) - y) {
        x1xie[j] = true;
      }
      if (parseInt(j.split('-')[0]) - x === y - parseInt(j.split('-')[1])) {
        x2xie[j] = true;
      }
    }
    var hs = check2(hang);
    var ls = check2(lie);
    var s1s = check2(x1xie);
    var s2s = check2(x2xie);
    if (hs > max) {
      max = hs;
      zuobiao = {x: x, y: y};
    }
    if (ls > max) {
      max = ls;
      zuobiao = {x: x, y: y};
    }
    if (s1s > max) {
      max = s1s;
      zuobiao = {x: x, y: y};
    }
    if (s2s > max) {
      max = s2s;
      zuobiao = {x: x, y: y};
    }
  }
  drawQizi(zuobiao.x, zuobiao.y, false);
  qizi[zuobiao.x + '-' + zuobiao.y] = false;
  ai.x = zuobiao.x;
  ai.y = zuobiao.y;
}
function check2(obj) {
  var max = -Infinity, count = 0;
  for (var i = 0; i < ROW; i++) {
    if (obj[i + '-0'] && obj[i + '-1'] && obj[i + '-2'] && obj[i + '-3'] && obj[i + '-4']) {
      count++;
    } else {
      max = count > max ? count : max;
      count = 0;
    }
  }
  max = count > max ? count : max;
  count = 0;
  for (var i = 0; i < ROW; i++) {
    if (obj['0-' + i] && obj['1-' + i] && obj['2-' + i] && obj['3-' + i] && obj['4-' + i]) {
      count++;
    } else {
      max = count > max ? count : max;
      count = 0;
    }
  }
  max = count > max ? count : max;
  count = 0;
  for (var i = 0; i < ROW; i++) {
    if (obj[i + '-' + i]) {
      count++;
    } else {
      max = count > max ? count : max;
      count = 0;
    }
  }
  max = count > max ? count : max;
  count = 0;
  for (var i = 0; i < ROW; i++) {
    if (obj[(ROW - 1 - i) + '-' + i]) {
      count++;
    } else {
      max = count > max ? count : max;
      count = 0;
    }
  }
  max = count > max ? count : max;
  count = 0;
  return max;
}

3. 示例说明

3.1 示例一:实现对战模式

可以通过添加对战模式,让两个玩家进行互动。例如,添加一个按钮用于切换到对战模式,点击后需要提示先后手,并且落子颜色会随着先后手的不同而变化。修改后的完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>五子棋游戏</title>
  <style>
    canvas {
      border: 1px solid #999;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="600" height="600"></canvas>
  <button onclick="modeChange()">切换到对战模式</button>
  <script>
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var ROW = 15;
    var GAP = canvas.width / (ROW + 1);
    var qizi = {};
    var mode = 1;

    for (var i = 0; i < ROW; i++) {
      ctx.moveTo(GAP, GAP * (i + 1));
      ctx.lineTo(canvas.width - GAP, GAP * (i + 1));
      ctx.stroke();
      ctx.moveTo(GAP * (i + 1), GAP);
      ctx.lineTo(GAP * (i + 1), canvas.width - GAP);
      ctx.stroke();
    }

    canvas.onclick = function(e) {
      var x = e.offsetX;
      var y = e.offsetY;
      var i = Math.round(x / GAP) - 1;
      var j = Math.round(y / GAP) - 1;
      if (qizi[i + '-' + j]) {
        return;
      }
      if (mode === 1) {
        drawQizi(i, j, true);
        qizi[i + '-' + j] = true;
        if (check(i + '-' + j, true)) {
          alert('恭喜您,您赢了!');
          canvas.onclick = null;
          return;
        }
        mode = 2;
      } else if (mode === 2) {
        drawQizi(i, j, false);
        qizi[i + '-' + j] = false;
        if (check(i + '-' + j, false)) {
          alert('恭喜电脑赢了!');
          canvas.onclick = null;
          return;
        }
        mode = 1;
      }
    }

    function modeChange() {
      alert('您执黑,电脑执白。');
      mode = 1;
    }
  </script>
</body>
</html>

3.2 示例二:添加音效和动画效果

可以通过添加音效和动画效果,提高游戏的趣味性。例如,添加音效用于提示棋子落下的声音;添加动画效果用于提示玩家哪里可以下棋。修改后的完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>五子棋游戏</title>
  <style>
    canvas {
      border: 1px solid #999;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="600" height="600"></canvas>
  <button onclick="modeChange()">切换到对战模式</button>
  <audio src="https://www.w3school.com.cn/i/horse.ogg" id="music"></audio>
  <script>
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var ROW = 15;
    var GAP = canvas.width / (ROW + 1);
    var qizi = {};
    var mode = 1;
    var music = document.getElementById("music");

    for (var i = 0; i < ROW; i++) {
      ctx.moveTo(GAP, GAP * (i + 1));
      ctx.lineTo(canvas.width - GAP, GAP * (i + 1));
      ctx.stroke();
      ctx.moveTo(GAP * (i + 1), GAP);
      ctx.lineTo(GAP * (i + 1), canvas.width - GAP);
      ctx.stroke();
    }

    canvas.onclick = function(e) {
      var x = e.offsetX;
      var y = e.offsetY;
      var i = Math.round(x / GAP) - 1;
      var j = Math.round(y / GAP) - 1;
      if (qizi[i + '-' + j]) {
        return;
      }
      if (mode === 1) {
        drawQizi(i, j, true);
        qizi[i + '-' + j] = true;
        if (check(i + '-' + j, true)) {
          alert('恭喜您,您赢了!');
          canvas.onclick = null;
          return;
        }
        mode = 2;
      } else if (mode === 2) {
        drawQizi(i, j, false);
        qizi[i + '-' + j] = false;
        if (check(i + '-' + j, false)) {
          alert('恭喜电脑赢了!');
          canvas.onclick = null;
          return;
        }
        mode = 1;
      }
      music.play();
      animate(i, j);
    }

    function modeChange() {
      alert('您执黑,电脑执白。');
      mode = 1;
    }

    function animate(x, y) {
      ctx.save();
      ctx.beginPath();
      ctx.arc((x + 1) * GAP, (y + 1) * GAP, GAP / 3, 0, Math.PI * 2);
      ctx.closePath();
      ctx.fillStyle = "rgba(0, 0, 0, 0.2)";
      ctx.strokeStyle = "rgba(0, 0, 0, 0.6)";
      ctx.fill();
      ctx.stroke();
      ctx.restore();
    }
  </script>
</body>
</html>

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:纯JS实现五子棋游戏兼容各浏览器(附源码) - Python技术站

(0)
上一篇 2023年6月11日
下一篇 2023年6月11日

相关文章

  • 使用javaScript动态加载Js文件和Css文件

    使用JavaScript动态加载JS文件和CSS文件是一种常见的前端技巧,可以帮助优化网页加载速度,提升用户体验。下面是实现这个技巧的完整攻略: 动态加载JS文件 动态加载JS文件通常分为两种方式:利用script标签动态插入和利用XMLHttpRequest请求代码并执行。 利用script标签动态插入 通过在HTML文档中添加一个script标签,并设置…

    JavaScript 2023年5月27日
    00
  • JS数组方法slice()用法实例分析

    JS数组方法slice()用法实例分析 简介 slice() 方法返回一个新的数组对象,这个对象是由 begin 和 end 决定的原数组的浅拷贝。原数组不会被修改。常用于数组的复制或提取。 语法 array.slice(begin, end) 参数描述: begin:一个零开始的索引,提取起始处的元素。 end(可选):一个零开始的索引,提取终止处的元素。…

    JavaScript 2023年5月27日
    00
  • JS传值出现中文参数乱码的解决方法

    JS传值出现中文参数乱码的解决方法 当我们在JS中传递参数中包含中文时,有时会出现乱码的问题。这篇攻略将介绍如何解决这个问题。 方法一:使用encodeURIComponent()和decodeURIComponent() 通过使用JavaScript内置的encodeURIComponent()函数对字符串进行编码,再通过decodeURIComponen…

    JavaScript 2023年5月19日
    00
  • ES6新特性之解构、参数、模块和记号用法示例

    ES6新特性之解构、参数、模块和记号用法示例 解构 解构是从数组和对象中提取值并对变量进行赋值的语法。它可以让我们写出更简洁、更易读的代码。 数组解构 可以使用方括号和逗号来解构数组。下面是一个例子: const arr = [1, 2, 3, 4]; const [a, b, …rest] = arr; console.log(a); // 1 con…

    JavaScript 2023年6月10日
    00
  • JavaScript中的纯函数与偏函数你了解吗

    JavaScript中的纯函数和偏函数是两个非常重要的概念。它们都可以提高代码的可读性、可维护性和可测试性。下面我们来详细讲解一下这两个概念。 纯函数 纯函数是指不依赖于外部状态(如全局变量、文件等)并且给定相同的输入,总是返回相同的输出的函数。简而言之,纯函数不会影响外界,也不受外界影响。纯函数具有以下优点: 可以更加方便的进行测试,因为不需要考虑外部状态…

    JavaScript 2023年5月27日
    00
  • JS获取对象属性API汇总枚举symbol

    下面我将详细讲解“JS获取对象属性API汇总枚举symbol”的完整攻略,主要分成以下几个部分: 一、前言 在JS中,获取对象属性是非常常见的操作。而JS提供了很多方法来获取对象的属性,不同的方法适用于不同的场景。本篇文章将会围绕着JS获取对象属性的API进行梳理,并着重讲解其中一个比较新颖的方法——枚举symbol类型的属性。 二、API汇总 下面我们来总…

    JavaScript 2023年5月27日
    00
  • 10行原生JS实现文字无缝滚动(超简单)

    当根据文章中提供的攻略,我们可以用不到十行的原生JS代码实现文字无缝滚动的效果。下面逐步解读这个攻略的实现过程: 第一步:获取DOM元素 首先,需要获取到需要滚动的文字所在的DOM元素。这可以通过document.querySelector()方法来获取。在示例中,需要滚动的文字是包含在一个<div>元素中的,其class为scroll-wrap…

    JavaScript 2023年6月11日
    00
  • js正则表达式replace替换变量方法

    JS正则表达式replace替换变量方法是一种常见的字符串替换方式。可以利用正则表达式匹配字符串中需要替换的部分,并将其替换为新的内容。下面详细讲解这种方法的步骤和示例。 1. 替换方法的语法 JS中正则表达式replace替换变量方法的语法如下: str.replace(regexp|substr, newSubStr|function) 其中, str …

    JavaScript 2023年6月10日
    00
合作推广
合作推广
分享本页
返回顶部