# In order to make you advanced Canvas, I spent 7 hours writing 3 interesting games!!!

Posted by spicey on Wed, 19 Jan 2022 07:09:16 +0100

## preface

Hello, I'm Lin Sanxin. I believe you have read my previous introduction to canvas In order to get her started with canvas in 10 minutes, I stayed up late and wrote three small projects and this article , I already have an entry-level understanding of canvas. Today, I wrote three interesting games with canvas to make you happy. Yes, I have only you in my heart, not her.

It's 0:15 in the morning. Let's do it 🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍， Write this article while debugging!!!

## Greedy snake 🐍

The final effect is as follows:

The implementation steps are divided into the following steps:

• 1. Draw the snake
• 2. Let the snake move
• 3. Random feeding of food
• 4. Snakes eat food
• 5. Edge detection and collision detection

### 1. Draw the snake

In fact, drawing a snake is very simple. A snake is composed of a snake head and a snake body. In fact, it can be represented by a square. The snake head is a square, and the snake body can be many squares

You can draw squares with CTX FillRect is used to draw the snake head, and the snake body is represented by the array body

```// html
<canvas id="canvas" width="800" height="800"></canvas>

// js

draw()

function draw() {
const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// Constructor for small squares
function Rect(x, y, width, height, color) {
this.x = x
this.y = y
this.width = width
this.height = height
this.color = color
}

Rect.prototype.draw = function () {
ctx.beginPath()
ctx.fillStyle = this.color
ctx.fillRect(this.x, this.y, this.width, this.height)
ctx.strokeRect(this.x, this.y, this.width, this.height)
}

// Snake constructor
function Snake(length = 0) {

this.length = length
this.head = new Rect(canvas.width / 2, canvas.height / 2, 40, 40, 'red')

// Snake body
this.body = []

let x = this.head.x - 40

for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, 'yellow')
this.body.push(rect)
x -= 40
}
}

Snake.prototype.drawSnake = function () {
// Draw snake body
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw()
}
}

const snake = new Snake(3)
snake.drawSnake()
}```

### 2. Let the snake move

There are two situations when a snake moves:

• 1. The snake moves to the right by default at the beginning
• 2. Through the direction key control, move in different directions
In both cases, the position of a square is moved every second

The principle of making the snake move is actually very simple. Let me take the snake moving to the right as an example:

• 1. First move the snake's head a square distance to the right, and the snake doesn't move
• 3. Square removal of snake tail
• 4. Use the timer to cause the snake to move to the right continuously

```  Snake.prototype.moveSnake = function () {
// Put the snake head in the last state and spell it to the head of the snake
this.body.unshift(rect)

this.body.pop()

// Control the coordinates of the snake head according to the direction
switch (this.direction) {
case 0:
break
case 1:
break
case 2:
break
case 3:
break
}
}

document.onkeydown = function (e) {
// Keyboard events
e = e || window.event
// Left 37 upper 38 right 39 lower 40
switch (e.keyCode) {
case 37:
console.log(37)
// Ternary expression to prevent the left from moving to the right. The same is true below (greedy snakes can't turn around directly)
snake.direction = snake.direction === 2 ? 2 : 0
snake.moveSnake()
break
case 38:
console.log(38)
snake.direction = snake.direction === 3 ? 3 : 1
break
case 39:
console.log(39)
snake.direction = snake.direction === 0 ? 0 : 2
break
case 40:
console.log(40)
snake.direction = snake.direction === 1 ? 1 : 3
break

}
}

const snake = new Snake(3)
// The default direction is 2, that is, right
snake.direction = 2
snake.drawSnake()

function animate() {
// Empty first
ctx.clearRect(0, 0, canvas.width, canvas.height)
// move
snake.moveSnake()
// Repaint
snake.drawSnake()
}

var timer = setInterval(() => {
animate()
}, 100)
}```

The results are as follows:

### 3. Put food at random

Put food at random, that is, draw a square on the canvas at random. Pay attention to the following two points:

• 1. The coordinates should be within the canvas
• 2. Food can't be thrown on the snake's body or head (it will knock the snake unconscious, hehe)

```  function randomFood(snake) {
let isInSnake = true
let rect
while (isInSnake) {
const x = Math.round(Math.random() * (canvas.width - 40) / 40) * 40
const y = Math.round(Math.random() * (canvas.height - 40) / 40) * 40
console.log(x, y)
// It's guaranteed to be a multiple of 40
rect = new Rect(x, y, 40, 40, 'blue')
// Judge whether the food overlaps with the head and body of the snake
if ((snake.head.x === x && snake.head.y === y) || snake.body.find(item => item.x === x && item.y === y)) {
isInSnake = true
continue
} else {
isInSnake = false
}
}
return rect
}

const snake = new Snake(3)
// The default direction is 2, that is, right
snake.direction = 2
snake.drawSnake()
// Create random food instances
var food = randomFood(snake)
// Draw the food
food.draw()

function animate() {
// Empty first
ctx.clearRect(0, 0, canvas.width, canvas.height)
// move
snake.moveSnake()
// Repaint
snake.drawSnake()
food.draw()
}```

The effect is as follows: the random food is drawn:

### 4. Snakes eat food

In fact, the snake eats food, which is very simple to understand, that is, when the snake head moves to overlap with the coordinates of food, even if it eats food, pay attention to two points:

• 1. After eating food, the snake's body should be extended by one space
• 2. After eating the food, the random food should change its position
```const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// A variable that defines whether a global eats food
let isEatFood = false

Snake.prototype.moveSnake = function () {
// Put the snake head in the last state and spell it to the head of the snake
this.body.unshift(rect)

// Judge whether the snake head overlaps with the food. If it overlaps, it means you have eaten it. If it doesn't overlap, it means you haven't eaten it

// Let's put a square above the head of the snake
if (!isEatFood) {
// If you don't eat it, you have to go to the tail, which is equivalent to that the whole snake hasn't grown
this.body.pop()
} else {
// If you eat it, you don't go to the tail, which is equivalent to extending the whole snake by a square

// And when you eat it, you have to regenerate a random food
food = randomFood(this)
food.draw()
isEatFood = false
}

// Control the coordinates of the snake head according to the direction
switch (this.direction) {
case 0:
break
case 1:
break
case 2:
break
case 3:
break
}
}```

### 5. Touch the border and touch yourself

As we all know, when the snake head touches the border or the snake body, it will terminate the game

```    Snake.prototype.drawSnake = function () {
// If you do
if (isHit(this)) {
// Clear timer
clearInterval(timer)
const con = confirm(`A total of \${this.body.length - this.length}A food, start over`)
// Reopen
if (con) {
draw()
}
return
}
// Draw snake body
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw()
}
}

function isHit(snake) {
// Does it touch the left and right boundaries
// Does it touch the upper and lower boundaries
// Did you hit the snake
const hitSelf = snake.body.find(({ x, y }) => head.x === x && head.y === y)
// If one of the three is true, the game ends
return xLimit || yLimit || hitSelf
}```

Since then, greedy snake 🐍 The game is complete:

### 6. All codes:

```draw()

function draw() {
const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// A variable that defines whether a global eats food
let isEatFood = false

// Constructor for small squares
function Rect(x, y, width, height, color) {
this.x = x
this.y = y
this.width = width
this.height = height
this.color = color
}

Rect.prototype.draw = function () {
ctx.beginPath()
ctx.fillStyle = this.color
ctx.fillRect(this.x, this.y, this.width, this.height)
ctx.strokeRect(this.x, this.y, this.width, this.height)
}

// Snake constructor
function Snake(length = 0) {

this.length = length
this.head = new Rect(canvas.width / 2, canvas.height / 2, 40, 40, 'red')

// Snake body
this.body = []

let x = this.head.x - 40

for (let i = 0; i < this.length; i++) {
const rect = new Rect(x, y, 40, 40, 'yellow')
this.body.push(rect)
x -= 40
}
}

Snake.prototype.drawSnake = function () {
// If you do
if (isHit(this)) {
// Clear timer
clearInterval(timer)
const con = confirm(`A total of \${this.body.length - this.length}A food, start over`)
// Reopen
if (con) {
draw()
}
return
}
// Draw snake body
for (let i = 0; i < this.body.length; i++) {
this.body[i].draw()
}
}

Snake.prototype.moveSnake = function () {
// Put the snake head in the last state and spell it to the head of the snake
this.body.unshift(rect)

// Judge whether the snake head overlaps with the food. If it overlaps, it means you have eaten it. If it doesn't overlap, it means you haven't eaten it

// Let's put a square above the head of the snake
if (!isEatFood) {
// If you don't eat it, you have to go to the tail, which is equivalent to that the whole snake hasn't grown
this.body.pop()
} else {
// If you eat it, you don't go to the tail, which is equivalent to extending the whole snake by a square

// And when you eat it, you have to regenerate a random food
food = randomFood(this)
food.draw()
isEatFood = false
}

// Control the coordinates of the snake head according to the direction
switch (this.direction) {
case 0:
break
case 1:
break
case 2:
break
case 3:
break
}
}

document.onkeydown = function (e) {
// Keyboard events
e = e || window.event
// Left 37 upper 38 right 39 lower 40
switch (e.keyCode) {
case 37:
console.log(37)
// Ternary expression to prevent the left from moving to the right. The same is true below (greedy snakes can't turn around directly)
snake.direction = snake.direction === 2 ? 2 : 0
snake.moveSnake()
break
case 38:
console.log(38)
snake.direction = snake.direction === 3 ? 3 : 1
break
case 39:
console.log(39)
snake.direction = snake.direction === 0 ? 0 : 2
break
case 40:
console.log(40)
snake.direction = snake.direction === 1 ? 1 : 3
break

}
}

function randomFood(snake) {
let isInSnake = true
let rect
while (isInSnake) {
const x = Math.round(Math.random() * (canvas.width - 40) / 40) * 40
const y = Math.round(Math.random() * (canvas.height - 40) / 40) * 40
console.log(x, y)
// It's guaranteed to be a multiple of 40
rect = new Rect(x, y, 40, 40, 'blue')
// Judge whether the food overlaps with the head and body of the snake
if ((snake.head.x === x && snake.head.y === y) || snake.body.find(item => item.x === x && item.y === y)) {
isInSnake = true
continue
} else {
isInSnake = false
}
}
return rect
}

function isHit(snake) {
// Does it touch the left and right boundaries
// Does it touch the upper and lower boundaries
// Did you hit the snake
const hitSelf = snake.body.find(({ x, y }) => head.x === x && head.y === y)
// If one of the three is true, the game ends
return xLimit || yLimit || hitSelf
}

const snake = new Snake(3)
// The default direction is 2, that is, right
snake.direction = 2
snake.drawSnake()
// Create random food instances
var food = randomFood(snake)
// Draw the food
food.draw()

function animate() {
// Empty first
ctx.clearRect(0, 0, canvas.width, canvas.height)
// move
snake.moveSnake()
// Repaint
snake.drawSnake()
food.draw()
}

var timer = setInterval(() => {
animate()
}, 100)
}```

## Star connection

The effect is as follows. Isn't it cool, brothers( Background picture You can download it yourself):

This little game can be divided into the following steps:

• 1. Draw a single little star and make it move
• 2. Make a hundred little stars
• 3. Connect when the stars are close
• 4. Move the mouse to generate small stars
• 5. Click the mouse to generate 5 small stars

### 1. Draw a single little star and make it move

In fact, moving the stars is very simple, that is, after clearing, redrawing the stars and using the timer, you will have the vision of moving. The point to note is: bounce when you hit the boundary.

```// html
<style>
#canvas {
background: url(./Light messenger.jpg) 0 0/cover no-repeat;
}
</style>
<canvas id="canvas"></canvas>

// js

const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// Gets the width and height of the current view
let aw = document.documentElement.clientWidth || document.body.clientWidth
let ah = document.documentElement.clientHeight || document.body.clientHeight
// Assign to canvas
canvas.width = aw
canvas.height = ah

// Monitor the real-time width and height when the screen changes
window.onresize = function () {
aw = document.documentElement.clientWidth || document.body.clientWidth
ah = document.documentElement.clientHeight || document.body.clientHeight
// Assign to canvas
canvas.width = aw
canvas.height = ah
}

// Whether solid or line, the color tone of this game is white
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'

function Star(x, y, r) {
// x. y is the coordinate and r is the radius
this.x = x
this.y = y
this.r = r
// speed parameter, value between - 3 and 3
this.speedX = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))
this.speedY = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))
}

Star.prototype.draw = function () {
ctx.beginPath()
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)
ctx.fill()
ctx.closePath()
}

Star.prototype.move = function () {
this.x -= this.speedX
this.y -= this.speedY
// When you hit the boundary, you just need to reverse the speed
if (this.x < 0 || this.x > aw) this.speedX *= -1
if (this.y < 0 || this.y > ah) this.speedY *= -1
}

// Randomly find a coordinate within the canvas and draw a star
const star = new Star(Math.random() * aw, Math.random() * ah, 3)
star

// The movement of stars
setInterval(() => {
ctx.clearRect(0, 0, aw, ah)
star.move()
star.draw()
}, 50)```

Achieve the following movement and rebound effects:

### 2. Draw 100 little stars

Create an array stars to store the stars

```const stars = []
for (let i = 0; i < 100; i++) {
// Randomly find a coordinate within the canvas and draw a star
stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))
}

// The movement of stars
setInterval(() => {
ctx.clearRect(0, 0, aw, ah)
// Traverse mobile rendering
stars.forEach(star => {
star.move()
star.draw()
})
}, 50)```

The effects are as follows:

### 3. Connect when the stars are close

When the difference between x and y of the two stars is less than 50, the connection is carried out. The connection only needs to use CTX Moveto and CTX Just lineto

```function drawLine(startX, startY, endX, endY) {
ctx.beginPath()
ctx.moveTo(startX, startY)
ctx.lineTo(endX, endY)
ctx.stroke()
ctx.closePath()
}

// The movement of stars
setInterval(() => {
ctx.clearRect(0, 0, aw, ah)
// Traverse mobile rendering
stars.forEach(star => {
star.move()
star.draw()
})
stars.forEach((star, index) => {
// Similar to bubble sorting, compare and make sure all stars are compared between two
for (let i = index + 1; i < stars.length; i++) {
if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {
drawLine(star.x, star.y, stars[i].x, stars[i].y)
}
}
})
}, 50)```

You can think about why two forEach cannot be executed together. This is a question worth thinking about, or you can combine it together, try the effect and get it. It's an assignment for everyone!

The effects are as follows:

### 4. The mouse moves with a small star

That is, the little star will go wherever the mouse goes, and the little star will connect with the little star close to it wherever it goes

```const mouseStar = new Star(0, 0, 3)

canvas.onmousemove = function (e) {
mouseStar.x = e.clientX
mouseStar.y = e.clientY
}

// The movement of stars
setInterval(() => {
ctx.clearRect(0, 0, aw, ah)
// Mouse star rendering
mouseStar.draw()
// Traverse mobile rendering
stars.forEach(star => {
star.move()
star.draw()
})
stars.forEach((star, index) => {
// Similar to bubble sorting, compare and make sure all stars are compared between two
for (let i = index + 1; i < stars.length; i++) {
if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {
drawLine(star.x, star.y, stars[i].x, stars[i].y)
}
}
// Judge mouse star connection
if (Math.abs(mouseStar.x - star.x) < 50 && Math.abs(mouseStar.y - star.y) < 50) {
drawLine(mouseStar.x, mouseStar.y, star.x, star.y)
}
})
}, 50)```

The effects are as follows:

### 5. Click the mouse to generate five small stars

The idea is to click the mouse to generate five small stars and add them to the array stars

```window.onclick = function (e) {
for (let i = 0; i < 5; i++) {
stars.push(new Star(e.clientX, e.clientY, 3))
}
}```

The effects are as follows:

Final effect:

### 6. All codes

```const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// Gets the width and height of the current view
let aw = document.documentElement.clientWidth || document.body.clientWidth
let ah = document.documentElement.clientHeight || document.body.clientHeight
// Assign to canvas
canvas.width = aw
canvas.height = ah

// Monitor the real-time width and height when the screen changes
window.onresize = function () {
aw = document.documentElement.clientWidth || document.body.clientWidth
ah = document.documentElement.clientHeight || document.body.clientHeight
// Assign to canvas
canvas.width = aw
canvas.height = ah
}

// Whether solid or line, the color tone of this game is white
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'

function Star(x, y, r) {
// x. y is the coordinate and r is the radius
this.x = x
this.y = y
this.r = r
// speed parameter, value between - 3 and 3
this.speedX = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))
this.speedY = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))
}

Star.prototype.draw = function () {
ctx.beginPath()
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)
ctx.fill()
ctx.closePath()
}

Star.prototype.move = function () {
this.x -= this.speedX
this.y -= this.speedY
// When you hit the boundary, you just need to reverse the speed
if (this.x < 0 || this.x > aw) this.speedX *= -1
if (this.y < 0 || this.y > ah) this.speedY *= -1
}

function drawLine(startX, startY, endX, endY) {
ctx.beginPath()
ctx.moveTo(startX, startY)
ctx.lineTo(endX, endY)
ctx.stroke()
ctx.closePath()
}

const stars = []
for (let i = 0; i < 100; i++) {
// Randomly find a coordinate within the canvas and draw a star
stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))
}

const mouseStar = new Star(0, 0, 3)

canvas.onmousemove = function (e) {
mouseStar.x = e.clientX
mouseStar.y = e.clientY
}
window.onclick = function (e) {
for (let i = 0; i < 5; i++) {
stars.push(new Star(e.clientX, e.clientY, 3))
}
}

// The movement of stars
setInterval(() => {
ctx.clearRect(0, 0, aw, ah)
// Mouse star rendering
mouseStar.draw()
// Traverse mobile rendering
stars.forEach(star => {
star.move()
star.draw()
})
stars.forEach((star, index) => {
// Similar to bubble sorting, compare and make sure all stars are compared between two
for (let i = index + 1; i < stars.length; i++) {
if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {
drawLine(star.x, star.y, stars[i].x, stars[i].y)
}
}

if (Math.abs(mouseStar.x - star.x) < 50 && Math.abs(mouseStar.y - star.y) < 50) {
drawLine(mouseStar.x, mouseStar.y, star.x, star.y)
}
})
}, 50)```

## 3. Gobang

See what will happen:

Gobang is divided into the following steps:

• 1. Draw a chessboard
• 2. When black and white chess is switched, it cannot cover the pit
• 3. Judge whether five children are connected. If so, you win
• 4. Egg: play chess with AI (realize single player game)

### 1. Draw a chessboard

It's actually very simple. Use CTX Moveto and CTX Lineto, draw 15 lines horizontally and 15 lines vertically, and it's OK.

```// html
#canvas {
background: #e3cdb0;
}
<canvas id="canvas" width="600" height="600"></canvas>

// js
play()

function play() {
const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// Draw chessboard

// Horizontal, 15 lines in total
for (let i = 0; i < 15; i++) {
ctx.beginPath()
ctx.moveTo(20, 20 + i * 40)
ctx.lineTo(580, 20 + i * 40)
ctx.stroke()
ctx.closePath()
}

// Vertical, 15 lines in total
for (let i = 0; i < 15; i++) {
ctx.beginPath()
ctx.moveTo(20 + i * 40, 20)
ctx.lineTo(20 + i * 40, 580)
ctx.stroke()
ctx.closePath()
}
}```

This draws the chessboard:

### 2. Switch between black and white chess

• 1. Click the event with the mouse to obtain the coordinates and draw the chess (ctx.arc)
• 2. Ensure that the played chess position cannot be played repeatedly

The first step is to obtain the mouse coordinates, but we should pay attention to one thing. The chess pieces can only be placed at the intersection of the line. Therefore, after obtaining the mouse coordinates, we should do some processing and round them to the nearest line intersection as the center of the circle

The second step, how to ensure that the chess position is not repeated? We can use a two-dimensional array to record. The initial value is 0. After playing black chess, it becomes 1 and after playing white chess, it becomes 2. However, it should be noted that the x and y of the array index are opposite to the x and y of the canvas coordinates, so the coordinates in the following code are reversed. I hope you can think about why.

```// Whether to play black chess
// Black chess go first
let isBlack = true

// Checkerboard 2D array
let cheeks = []

for (let i = 0; i < 15; i++) {
cheeks[i] = new Array(15).fill(0)
}

canvas.onclick = function (e) {
const clientX = e.clientX
const clientY = e.clientY
// Round 40 to make sure the pieces fall at the intersection
const x = Math.round((clientX - 20) / 40) * 40 + 20
const y = Math.round((clientY - 20) / 40) * 40 + 20
// Index of cheeks two-dimensional array
// It's a little redundant. You can understand it better
const cheeksX = (x - 20) / 40
const cheeksY = (y - 20) / 40
// The corresponding element is not 0, indicating that there is chess in this place. Return
if (cheeks[cheeksY][cheeksX]) return
// Black chess is 1 and white chess is 2
cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2
ctx.beginPath()
// Draw a circle
ctx.arc(x, y, 20, 0, 2 * Math.PI)
// Judge whether to go black or white
ctx.fillStyle = isBlack ? 'black' : 'white'
ctx.fill()
ctx.closePath()
// Switch black and white
isBlack = !isBlack
}```

The effects are as follows:

### 3. Judge whether it is wulianzi

How to judge? There are four situations: upper and lower five Lianzi, left and right Wu Lianzi, upper left and lower right five Lianzi, upper right and lower left five Lianzi. As long as we judge them all every time we fall.

By the way, all codes are attached

```play()

function play() {
const canvas = document.getElementById('canvas')

const ctx = canvas.getContext('2d')

// Draw chessboard

// Horizontal, 15 lines in total
for (let i = 0; i < 15; i++) {
ctx.beginPath()
ctx.moveTo(20, 20 + i * 40)
ctx.lineTo(580, 20 + i * 40)
ctx.stroke()
ctx.closePath()
}

// Vertical, 15 lines in total
for (let i = 0; i < 15; i++) {
ctx.beginPath()
ctx.moveTo(20 + i * 40, 20)
ctx.lineTo(20 + i * 40, 580)
ctx.stroke()
ctx.closePath()
}

// Do you play black chess
// Black chess go first
let isBlack = true

// Checkerboard 2D array
let cheeks = []

for (let i = 0; i < 15; i++) {
cheeks[i] = new Array(15).fill(0)
}

canvas.onclick = function (e) {
const clientX = e.clientX
const clientY = e.clientY
// Round 40 to make sure the pieces fall at the intersection
const x = Math.round((clientX - 20) / 40) * 40 + 20
const y = Math.round((clientY - 20) / 40) * 40 + 20
// Index of cheeks two-dimensional array
// It's a little redundant, so you can understand it better
const cheeksX = (x - 20) / 40
const cheeksY = (y - 20) / 40
// The corresponding element is not 0, indicating that there is chess in this place. Return
if (cheeks[cheeksY][cheeksX]) return
// Black chess is 1 and white chess is 2
cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2
ctx.beginPath()
// Draw a circle
ctx.arc(x, y, 20, 0, 2 * Math.PI)
// Judge whether to go black or white
ctx.fillStyle = isBlack ? 'black' : 'white'
ctx.fill()
ctx.closePath()

// canvas drawing is asynchronous, so you can draw it and then test whether you win or lose
setTimeout(() => {
if (isWin(cheeksX, cheeksY)) {
const con = confirm(`\${isBlack ? 'black ' : 'White chess'}Win! Start again?`)
// Restart
ctx.clearRect(0, 0, 600, 600)
con && play()
}
// Switch black and white
isBlack = !isBlack
}, 0)
}
// Judge whether five connected children
function isWin(x, y) {
const flag = isBlack ? 1 : 2
// Up and down
if (up_down(x, y, flag)) {
return true
}

// left and right
if (left_right(x, y, flag)) {
return true
}
// Upper left and lower right
if (lu_rd(x, y, flag)) {
return true
}

// Upper right and lower left
if (ru_ld(x, y, flag)) {
return true
}

return false
}

function up_down(x, y, flag) {
let num = 1
// Look up
for (let i = 1; i < 5; i++) {
let tempY = y - i
console.log(x, tempY)
if (tempY < 0 || cheeks[tempY][x] !== flag) break
if (cheeks[tempY][x] === flag) num += 1
}
// Look down
for (let i = 1; i < 5; i++) {
let tempY = y + i
console.log(x, tempY)
if (tempY > 14 || cheeks[tempY][x] !== flag) break
if (cheeks[tempY][x] === flag) num += 1
}
return num >= 5
}

function left_right(x, y, flag) {
let num = 1
// Look left
for (let i = 1; i < 5; i++) {
let tempX = x - i
if (tempX < 0 || cheeks[y][tempX] !== flag) break
if (cheeks[y][tempX] === flag) num += 1
}
// Look right
for (let i = 1; i < 5; i++) {
let tempX = x + i
if (tempX > 14 || cheeks[y][tempX] !== flag) break
if (cheeks[y][tempX] === flag) num += 1
}
return num >= 5

}

function lu_rd(x, y, flag) {
let num = 1
// Look up to the left
for (let i = 1; i < 5; i++) {
let tempX = x - i
let tempY = y - i
if (tempX < 0 || tempY < 0 || cheeks[tempY][tempX] !== flag) break
if (cheeks[tempY][tempX] === flag) num += 1
}
// Look down to the right
for (let i = 1; i < 5; i++) {
let tempX = x + i
let tempY = y + i
if (tempX > 14 || tempY > 14 || cheeks[tempY][tempX] !== flag) break
if (cheeks[tempY][tempX] === flag) num += 1
}

return num >= 5
}

function ru_ld(x, y, flag) {
let num = 1
// Look up to the right
for (let i = 1; i < 5; i++) {
let tempX = x - i
let tempY = y + i
if (tempX < 0 || tempY > 14 || cheeks[tempY][tempX] !== flag) break
if (cheeks[tempY][tempX] === flag) num += 1
}
// Look down to the left
for (let i = 1; i < 5; i++) {
let tempX = x + i
let tempY = y - i
if (tempX > 14 || tempY < 0 || cheeks[tempY][tempX] !== flag) break
if (cheeks[tempY][tempX] === flag) num += 1
}

return num >= 5
}

}
```

### 4. Colored eggs: playing chess with AI

In fact, it is very simple. After playing chess, set a function: randomly find a position to play chess. In this way, the functions of playing chess and single player games with computers are realized. I have realized this function, but I won't write it out. Let's give it to you as your homework to consolidate this article. Ha ha ha ha

## epilogue

I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are self-motivated, like the front-end and want to learn from the front-end, we can make friends and fish together. Ha ha, fish school, add me, please note [Si no]

Topics: Javascript Front-end canvas