VUE+Canvas realizes the whole process of a simple Gobang game

VUE+Canvas realizes the whole process of a simple Gobang game

Preface

In terms of layout, Gobang is much simpler to implement than those games whose goal is random movement, and the ideas are also clear. It is divided into:

(1) Draw a chessboard;

(2) Listen for click events and draw black and white chess pieces;

(3) After each move, determine whether there are 5 connected pieces. If so, you win.

The most complicated thing is probably how to judge whether Gobang has won, so let’s start with the simple one and draw a chessboard.

1. Draw a chessboard

The chessboard is very simple. Let's draw a 15*15 chessboard with intersecting horizontal and vertical lines:

drawCheckerboard() {
      // Draw the chessboard let _this = this;
      _this.ctx.beginPath();
      _this.ctx.fillStyle = "#fff";
      _this.ctx.rect(0, 0, 450, 450);
      _this.ctx.fill();
      for (var i = 0; i < 15; i++) {
        _this.ctx.beginPath();
        _this.ctx.strokeStyle = "#D6D1D1";
        _this.ctx.moveTo(15 + i * 30, 15); //Draw 15 lines vertically, 30px apart;
        _this.ctx.lineTo(15 + i * 30, 435);
        _this.ctx.stroke();
        _this.ctx.moveTo(15, 15 + i * 30); //Draw 15 lines horizontally, 30px apart;
        _this.ctx.lineTo(435, 15 + i * 30);
        _this.ctx.stroke();
 
        _this.resultArr.push(new Array(15).fill(0));
      }
}

First use a 450 * 450 square as the base, leave a 15 width blank around it, and then draw lines with an interval of 30. In the for loop, we also initialized a 15 * 15 two-dimensional array and filled it with 0s. Yes, it is used to record the moves.

2. Listen for click events to draw black and white chess pieces

OK, when we get the DOM, we listen to the click event to draw the chess piece:

let container = document.getElementById("gobang");

container.addEventListener("click", _this.handleClick);

handleClick(event) {
      let x = event.offsetX - 70;
      let y = event.offsetY - 70;
      if (x < 15 || x > 435 || y < 15 || y > 435) {
        // Click out of bounds return;
      }
      this.drawChess(x, y);
      if(this.winGame){
        this.drawResult();
        return;
      }
      this.whiteTurn = !this.whiteTurn;
      this.drawText();
}

Code for drawing chess pieces:

drawChess(x, y) {
      let _this = this;
      let xLine = Math.round((x - 15) / 30); // vertical line number x let yLine = Math.round((y - 15) / 30); // horizontal line number y if(_this.resultArr[xLine][yLine] !== 0){
        return;
      }
      let grd = _this.ctx.createRadialGradient(
        xLine * 30 + 15,
        yLine * 30 + 15,
        4,
        xLine * 30 + 15,
        yLine * 30 + 15,
        10
      );
      grd.addColorStop(0, _this.whiteTurn ? "#fff" : "#4c4c4c");
      grd.addColorStop(1, _this.whiteTurn ? "#dadada" : "#000");
      _this.ctx.beginPath();
      _this.ctx.fillStyle = grd;
      _this.ctx.arc(
        xLine * 30 + 15,
        yLine * 30 + 15,
        10,
        0,
        2 * Math.PI,
        false
      );
      _this.ctx.fill();
      _this.ctx.closePath();
 
      _this.setResultArr(xLine, yLine);
      _this.checkResult(xLine, yLine);
}

It is easy to calculate the chessboard intersection closest to the click coordinates. Of course, if a piece has already been placed there, you have to return. Then draw white or black pieces at the intersection, and use gradient fill here to make the chess pieces look more realistic. Next, record the chess piece status in the corresponding two-dimensional array:

setResultArr(m, n) {
      let _this = this;
      _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // White is 1; Black is 2
 
}

3. Check the winning and losing results of Gobang

How to determine the outcome of winning or losing? To the naked eye, it is nothing more than establishing a coordinate system with the current move as the 0,0 origin, and then determining whether there are 5 consecutive pieces on the four lines of 0°, 180°, 45° and 135°. Compared with directly traversing and counting, a better way is to take out the data on four lines and then determine whether there are 5 consecutive 1 or 2 characters.

Suppose the array coordinates of our placement are [m, n].

(1) Result array string of horizontal line: this.resultArr[m].join('');

(2) Result array string of vertical line:

for(let i = 0; i<15; i++){
        lineHorizontal.push(_this.resultArr[i][n]);

}

(3) 135° (top left to bottom right): j ranges from 0 to 15, take this.resultArr[m - j][n - j] and unshift it into the head of the temporary array, take this.resultArr[m + j][n + j] and put it at the end of the temporary array to form the result;

(4) 45° (lower left to upper right): j ranges from 0 to 15, take this.resultArr[m + j][n - j] and unshift it into the head of the temporary array, take this.resultArr[m - j][n + j] and put it at the end of the temporary array to form the result;

Of course, we need to judge whether the array is out of bounds.

After getting the result string, we check whether there is a string like "22222" or "11111". If there is, it means victory.

checkResult(m ,n){ // Check whether there are 5 connected pieces let _this = this;
      let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;
      // Take out the one-dimensional array of four lines [m,n] let lineVertical = _this.resultArr[m].join('');
      if (lineVertical.indexOf(checkStr) > -1) {
        _this.winGame = true;
        return;
      }
      let lineHorizontal = [];
      for(let i = 0; i<15; i++){
        lineHorizontal.push(_this.resultArr[i][n]);
      }
      lineHorizontal = lineHorizontal.join('');
      if (lineHorizontal.indexOf(checkStr) > -1) {
        _this.winGame = true;
        return;
      }
      let line135 = [];
      for(let j = 0; j < 15; j++){
        if(m - j >= 0 && n - j >= 0){ // Upper left corner line135.unshift(_this.resultArr[m - j][n -j]);
        }
        if(j > 0 && m + j < 15 && n + j < 15){ // Lower right corner line135.push(_this.resultArr[m + j][n + j]);
        }
      }
      line135 = line135.join('');
      if(line135.indexOf(checkStr) > -1){
        _this.winGame = true;
        return;
      }
      let line45 = [];
      for(let j = 0; j < 15; j++){
        if(m + j < 15 && n - j >= 0){ // Upper right corner line45.unshift(_this.resultArr[m + j][n -j]);
        }
        if(j > 0 && m - j >=0 && n + j < 15){ // Lower left corner line45.push(_this.resultArr[m - j][n + j]);
        }
      }
      line45 = line45.join('');
      if(line45.indexOf(checkStr) > -1){
        _this.winGame = true;
        return;
      }
}

Finally, we show which side wins.

At this point, a simple black and white chess game is ready~~~~~

As usual, here is the source code:

<template>
  <div class="gobang">
    <canvas id="gobang" width="800" height="600"></canvas>
  </div>
</template>
 
<script>
const CheckStrWhite = "11111";
const CheckStrBlack = "22222";
export default {
  name: "Gobang",
  data() {
    return {
      ctx: null,
      winGame: false,
      whiteTurn: false, // White turn; true - Black turnresultArr: [] // Array recording chess piece positions};
  },
  mounted() {
    let _this = this;
    let container = document.getElementById("gobang");
 
    container.addEventListener("click", _this.handleClick);
 
    _this.ctx = container.getContext("2d");
    _this.ctx.translate(70,70);
    _this.drawCheckerboard();
  },
  computed:{
    chessText(){
      return this.whiteTurn ? 'White Chess' : 'Black Chess';
    }
  },
  methods: {
    drawCheckerboard() {
      // Draw the chessboard let _this = this;
      _this.ctx.beginPath();
      _this.ctx.fillStyle = "#fff";
      _this.ctx.rect(0, 0, 450, 450);
      _this.ctx.fill();
      for (var i = 0; i < 15; i++) {
        _this.ctx.beginPath();
        _this.ctx.strokeStyle = "#D6D1D1";
        _this.ctx.moveTo(15 + i * 30, 15); //Draw 15 lines vertically, 30px apart;
        _this.ctx.lineTo(15 + i * 30, 435);
        _this.ctx.stroke();
        _this.ctx.moveTo(15, 15 + i * 30); // Draw 15 lines horizontally, 30px apart; the chessboard is 14*14;
        _this.ctx.lineTo(435, 15 + i * 30);
        _this.ctx.stroke();
 
        _this.resultArr.push(new Array(15).fill(0));
      }
      _this.drawText();
    },
    drawChess(x, y) {
      let _this = this;
      let xLine = Math.round((x - 15) / 30); // vertical line number x let yLine = Math.round((y - 15) / 30); // horizontal line number y if(_this.resultArr[xLine][yLine] !== 0){
        return;
      }
      let grd = _this.ctx.createRadialGradient(
        xLine * 30 + 15,
        yLine * 30 + 15,
        4,
        xLine * 30 + 15,
        yLine * 30 + 15,
        10
      );
      grd.addColorStop(0, _this.whiteTurn ? "#fff" : "#4c4c4c");
      grd.addColorStop(1, _this.whiteTurn ? "#dadada" : "#000");
      _this.ctx.beginPath();
      _this.ctx.fillStyle = grd;
      _this.ctx.arc(
        xLine * 30 + 15,
        yLine * 30 + 15,
        10,
        0,
        2 * Math.PI,
        false
      );
      _this.ctx.fill();
      _this.ctx.closePath();
 
      _this.setResultArr(xLine, yLine);
      _this.checkResult(xLine, yLine);
    },
    setResultArr(m, n) {
      let _this = this;
      _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // White is 1; Black is 2
 
    },
    
    checkResult(m ,n){ // Check whether there are 5 connected pieces let _this = this;
      let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;
      // Take out the one-dimensional array of four lines [m,n] let lineVertical = _this.resultArr[m].join('');
      if (lineVertical.indexOf(checkStr) > -1) {
        _this.winGame = true;
        return;
      }
      let lineHorizontal = [];
      for(let i = 0; i<15; i++){
        lineHorizontal.push(_this.resultArr[i][n]);
      }
      lineHorizontal = lineHorizontal.join('');
      if (lineHorizontal.indexOf(checkStr) > -1) {
        _this.winGame = true;
        return;
      }
      let line135 = [];
      for(let j = 0; j < 15; j++){
        if(m - j >= 0 && n - j >= 0){ // Upper left corner line135.unshift(_this.resultArr[m - j][n -j]);
        }
        if(j > 0 && m + j < 15 && n + j < 15){ // Lower right corner line135.push(_this.resultArr[m + j][n + j]);
        }
      }
      line135 = line135.join('');
      if(line135.indexOf(checkStr) > -1){
        _this.winGame = true;
        return;
      }
      let line45 = [];
      for(let j = 0; j < 15; j++){
        if(m + j < 15 && n - j >= 0){ // Upper right corner line45.unshift(_this.resultArr[m + j][n -j]);
        }
        if(j > 0 && m - j >=0 && n + j < 15){ // Lower left corner line45.push(_this.resultArr[m - j][n + j]);
        }
      }
      line45 = line45.join('');
      if(line45.indexOf(checkStr) > -1){
        _this.winGame = true;
        return;
      }
    },
    drawText(){
      let _this = this;
      _this.ctx.clearRect(435 + 60, 0, 100, 70);
      _this.ctx.fillStyle = "#fff";
      _this.ctx.font="20px Arial";
      _this.ctx.fillText('This round:' + _this.chessText, 435 + 70, 35);
    },
    drawResult(){
      let _this = this;
      _this.ctx.fillStyle = "#ff2424";
      _this.ctx.font="20px Arial";
      _this.ctx.fillText(_this.chessText+'Win!', 435 + 70, 70);
    },
    handleClick(event) {
      let x = event.offsetX - 70;
      let y = event.offsetY - 70;
      if (x < 15 || x > 435 || y < 15 || y > 435) {
        // Click out of bounds return;
      }
      this.drawChess(x, y);
      if(this.winGame){
        this.drawResult();
        return;
      }
      this.whiteTurn = !this.whiteTurn;
      this.drawText();
    }
  }
};
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.gobang {
  #gobang {
    background: #2a4546;
  }
}
</style>

Summarize

This is the end of this article about VUE+Canvas to implement a simple Gobang game. For more related VUE+Canvas Gobang game content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Vue implements Gobang game
  • Vue implements Gobang game

<<:  Detailed explanation of how to install PHP curl extension under Linux

>>:  Detailed explanation of the relationship between the primary key being 0 and the primary key self-selection constraint in MySQL (details)

Recommend

How to set the position of the block element in the middle of the window

How to set the position of the block element in t...

Pitfall notes of vuex and pinia in vue3

Table of contents introduce Installation and Usag...

Vue achieves seamless carousel effect (marquee)

This article example shares the specific code of ...

WeChat applet implements user login module server construction

I chose node.js to build the server. Friends who ...

Why does MySQL paging become slower and slower when using limit?

Table of contents 1. Test experiment 2. Performan...

Introduction to the process of building your own FTP and SFTP servers

FTP and SFTP are widely used as file transfer pro...

Problems with installing mysql and mysql.sock under linux

Recently, I encountered many problems when instal...

v-html rendering component problem

Since I have parsed HTML before, I want to use Vu...

Detailed explanation of Vue filter implementation and application scenarios

1. Brief Introduction Vue.js allows you to define...

Native js to achieve accordion effect

In actual web page development, accordions also a...

Zabbix's psk encryption combined with zabbix_get value

Since Zabbix version 3.0, it has supported encryp...