“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

preface

Hello everyone, MY name is Lin Sanxin. I believe you have read my previous entry to Canvas article. In order to let her enter canvas for 10 minutes, I stayed up late and wrote three small projects and this article. Today, I have written three interesting little games on Canvas to make you happy. Yes, I only have you in my heart, not her.

Now is midnight, let’s open to 🐍 🐍 🐍 🐍 🐍 🐍 🐍 🐍 🐍 🐍, write this article while debugging!!!!!!

Snake 🐍

The final result is as follows:The implementation steps are as follows:

  • 1. Draw the snake
  • 2. Get the snake moving
  • 3, random food
  • 4. Snakes eat food
  • 5, edge detection and collision detection

1. Draw the snake

In fact, it is very simple to draw a snake. A snake consists of a head and a body, which can be represented by a square. The head of a snake is a square, while the body of a snake can be many squares

You can draw a gridctx.fillRectTo draw, use the head of the snakeheadWhile the snake body is usedAn array of bodyTo represent the

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

// js


draw()

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

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

    // The small square constructor
    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)
    }

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

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

        / / snake
        this.body = []

        let x = this.head.x - 40
        let y = this.head.y

        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 the head of the snake
        this.head.draw()
        // Draw the body of the snake
        for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw()
        }
    }

    const snake = new Snake(3)
    snake.drawSnake()
}
Copy the code

2. Get the snake moving

There are two ways a snake can move:

  • 1. Snakes start by default by moving to the right
  • 2, through the direction key control, to move in different directions

In both cases, you move one square per second

To get the snake to move, the principle is very simple. Let me use the snake moving to the right as an example:

  • 1. The head of the snake moves one square distance to the right first, and the body of the snake does not move
  • 2, the snakeThe firstAdd a square
  • 3, snakeThe tailSquare off
  • 4, use the timer, resulting in the snake moving to the right of the vision
    Snake.prototype.moveSnake = function () {
        // Put the head of the snake on the head of the snake
        const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')
        this.body.unshift(rect)

        this.body.pop()

        // Control the coordinates of the snake head according to the direction
        switch (this.direction) {
            case 0:
                this.head.x -= this.head.width
                break
            case 1:
                this.head.y -= this.head.height
                break
            case 2:
                this.head.x += this.head.width
                break
            case 3:
                this.head.y += this.head.height
                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)
                // The three-way expression prevents pressing left when moving right.
                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, which is right
    snake.direction = 2
    snake.drawSnake()

    function animate() {
        / / empty first
        ctx.clearRect(0.0, canvas.width, canvas.height)
        / / move
        snake.moveSnake()
        / / draw
        snake.drawSnake()
    }

    var timer = setInterval(() = > {
        animate()
    }, 100)}Copy the code

The results are as follows:

3. Randomly drop food

To randomly place food, that is, to draw a random square on the canvas, pay attention to the following two points:

  • 1. Coordinates should be on the canvasWithin the scope of
  • 2, foodNot on the body or the head of a snake(This will knock the snake out)
    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)
            // Make sure it's a multiple of 40
            rect = new Rect(x, y, 40.40.'blue')
            // Determine if the food overlaps with the snakehead
            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, which is right
    snake.direction = 2
    snake.drawSnake()
    // Create a random food instance
    var food = randomFood(snake)
    // Draw the food
    food.draw()

    function animate() {
        / / empty first
        ctx.clearRect(0.0, canvas.width, canvas.height)
        / / move
        snake.moveSnake()
        / / draw
        snake.drawSnake()
        food.draw()
    }
Copy the code

The effect is as follows: 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’s head moves to the coordinate overlap with the food, it is considered to have eaten the food. Note two points:

  • 1, after eating the food, the snake body toExtend by one space
  • 2, after eating food, random food toCommutation position
const canvas = document.getElementById('canvas')

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

// A variable that defines whether food is eaten globally
let isEatFood = false
    

    Snake.prototype.moveSnake = function () {
        // Put the head of the snake on the head of the snake
        const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')
        this.body.unshift(rect)

        // Determine whether the snake head overlaps with the food. If it overlaps, it is eaten; if it does not overlap, it is not eaten
        isEatFood = food && this.head.x === food.x && this.head.y === food.y

        // Let's insert a square at the head of the snake
        if(! isEatFood) {// The tail is removed before it is eaten, which is equivalent to the whole snake not getting longer
            this.body.pop()
        } else {
            // After eating, the snake does not remove its tail, which is equivalent to lengthening the whole snake by one 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:
                this.head.x -= this.head.width
                break
            case 1:
                this.head.y -= this.head.height
                break
            case 2:
                this.head.x += this.head.width
                break
            case 3:
                this.head.y += this.head.height
                break}}Copy the code

5. Touch boundaries and touch yourself

We all know that when the head of the snake hits the edge, or the body of the snake, the game ends

    Snake.prototype.drawSnake = function () {
        // If you do
        if (isHit(this)) {
            // Clear the timer
            clearInterval(timer)
            const con = confirm('All toldThe ${this.body.length - this.length}Food, start over)
            // Whether to reopen
            if (con) {
                draw()
            }
            return
        }
        // Draw the head of the snake
        this.head.draw()
        // Draw the body of the snake
        for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw()
        }
    }
    
    
    function isHit(snake) {
        const head = snake.head
        // Whether the left and right borders are touched
        const xLimit = head.x < 0 || head.x >= canvas.width
        // Whether the upper and lower boundaries are touched
        const yLimit = head.y < 0 || head.y >= canvas.height
        // Whether to hit the snake body
        const hitSelf = snake.body.find(({ x, y }) = > head.x === x && head.y === y)
        If one of the three is true, the game is over
        return xLimit || yLimit || hitSelf
    }
Copy the code

Since then, snake 🐍 small game complete:

6. All codes:


draw()

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

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

    // A variable that defines whether food is eaten globally
    let isEatFood = false

    // The small square constructor
    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)
    }

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

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

        / / snake
        this.body = []

        let x = this.head.x - 40
        let y = this.head.y

        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 the timer
            clearInterval(timer)
            const con = confirm('All toldThe ${this.body.length - this.length}Food, start over)
            // Whether to reopen
            if (con) {
                draw()
            }
            return
        }
        // Draw the head of the snake
        this.head.draw()
        // Draw the body of the snake
        for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw()
        }
    }

    Snake.prototype.moveSnake = function () {
        // Put the head of the snake on the head of the snake
        const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')
        this.body.unshift(rect)

        // Determine whether the snake head overlaps with the food. If it overlaps, it is eaten; if it does not overlap, it is not eaten
        isEatFood = food && this.head.x === food.x && this.head.y === food.y

        // Let's insert a square at the head of the snake
        if(! isEatFood) {// The tail is removed before it is eaten, which is equivalent to the whole snake not getting longer
            this.body.pop()
        } else {
            // After eating, the snake does not remove its tail, which is equivalent to lengthening the whole snake by one 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:
                this.head.x -= this.head.width
                break
            case 1:
                this.head.y -= this.head.height
                break
            case 2:
                this.head.x += this.head.width
                break
            case 3:
                this.head.y += this.head.height
                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)
                // The three-way expression prevents pressing left when moving right.
                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)
            // Make sure it's a multiple of 40
            rect = new Rect(x, y, 40.40.'blue')
            // Determine if the food overlaps with the snakehead
            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) {
        const head = snake.head
        // Whether the left and right borders are touched
        const xLimit = head.x < 0 || head.x >= canvas.width
        // Whether the upper and lower boundaries are touched
        const yLimit = head.y < 0 || head.y >= canvas.height
        // Whether to hit the snake body
        const hitSelf = snake.body.find(({ x, y }) = > head.x === x && head.y === y)
        If one of the three is true, the game is over
        return xLimit || yLimit || hitSelf
    }

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

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

    var timer = setInterval(() = > {
        animate()
    }, 100)}Copy the code

The stars of attachment

The effect is as follows, isn’t it cool, guys (you can download the background image yourself) :

This small game can be divided into the following steps:

  • 1. Draw a single little star and make itmobile
  • 2, buildOne hundredLittle stars
  • 3. When the stars are close to each other, proceedattachment
  • 4, the mouseThe mobile generationLittle stars
  • 5. Mouse clickFive little stars

1. Draw a single little star and move it

In fact, moving the stars is very simple, is to clear after redrawing the stars, and use the timer, there will be a moving vision. Note this: bounce when you hit the boundary.

// html
<style>
    #canvas {
            background: URL (./ light emissary.jpg)0 0/cover no-repeat;
        }
</style>
<canvas id="canvas"></canvas>

// js

const canvas = document.getElementById('canvas')

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

// Get 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 as 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
}

// The color of the game is white, both solid and line
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'

function Star(x, y, r) {
    // x, y are coordinates, r is radius
    this.x = x
    this.y = y
    this.r = r
    // The speed parameter ranges from -3 to 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 it hits the boundary, it bounces. Just reverse speed
    if (this.x < 0 || this.x > aw) this.speedX *= -1
    if (this.y < 0 || this.y > ah) this.speedY *= -1
}

// Draw a random star in the canvas
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)
Copy the code

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++) {
    // Draw a random star in the canvas
    stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))}// The movement of stars
setInterval(() = > {
    ctx.clearRect(0.0, aw, ah)
    // Iterate over the moving render
    stars.forEach(star= > {
        star.move()
        star.draw()
    })
}, 50)
Copy the code

The effect is as follows:

3. When the stars are close, line up

When the difference between x and y of two stars is less than 50, a line is drawn. Just use ctx.moveTo and ctx.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)
    // Iterate over the moving render
    stars.forEach(star= > {
        star.move()
        star.draw()
    })
    stars.forEach((star, index) = > {
        // Similar to bubble sort, make sure all the stars are compared to each other
        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)
Copy the code

You can think about why two forEach’s can’t be executed together. That’s a good question to think about, or you can combine them together, try it out, and get it. Be to leave a homework for everybody ha!

The effect is as follows:

4. The mouse moves with the little star

That is, the mouse to where, the little star to where, and the little star to go where will be close to the little star line

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()
    // Iterate over the moving render
    stars.forEach(star= > {
        star.move()
        star.draw()
    })
    stars.forEach((star, index) = > {
        // Similar to bubble sort, make sure all the stars are compared to each other
        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)
            }
        }
        // Determine the line between the mouse stars
        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)
Copy the code

The effect is as follows:

5 mouse click to generate five little stars

The idea is, click the mouse, generate 5 small stars, and add to the array stars

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

The effect is as follows:

Final result:

6. Full code

const canvas = document.getElementById('canvas')

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

// Get 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 as 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
}

// The color of the game is white, both solid and line
ctx.fillStyle = 'white'
ctx.strokeStyle = 'white'

function Star(x, y, r) {
    // x, y are coordinates, r is radius
    this.x = x
    this.y = y
    this.r = r
    // The speed parameter ranges from -3 to 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 it hits the boundary, it bounces. Just reverse 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++) {
    // Draw a random star in the canvas
    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()
    // Iterate over the moving render
    stars.forEach(star= > {
        star.move()
        star.draw()
    })
    stars.forEach((star, index) = > {
        // Similar to bubble sort, make sure all the stars are compared to each other
        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)
Copy the code

3. The gobang

See what will happen:

Gobang is divided into the following steps:

  • 1. Draw the chessboard
  • 2, black and white chess switch next,Cannot cover a pit that has been placed
  • 3. Determine whetherFive son evenIf you are, you win
  • 4. Easter Egg: FollowAI playing chess(Implement a single player game)

1. Draw the board

MoveTo and CTX. LineTo are used to draw 15 lines horizontally and 15 lines vertically.

// 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 the chessboard

    // Horizontal, a total of 15 lines
    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, a total of 15 lines
    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()
    }
}
Copy the code

Thus the chessboard is drawn:

2. Switch between black and white

  • 1, click the mouse event, get the coordinates, draw the chess (ctx.arc)
  • 2. Ensure that the position played cannot be repeated

The first step is to get the mouse coordinates, but we need to notice one thing, the chess pieces can only cross the line, so after getting the mouse coordinates, we need to do something, round to the nearest line crossing point as the center of the circle

Step two, how do you make sure you don’t repeat your position? We could use a two-dimensional array, which starts at 0, becomes 1 after black, and becomes 2 after white, but one thing to note here is that the x and y of the array index are the opposite of the x and y of the canvas coordinates, so I want you to think about why the coordinates are reversed in the code.

// Whether to play black
    // Black moves first
    let isBlack = true


    // The board is a two-dimensional 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 are at the intersection
        const x = Math.round((clientX - 20) / 40) * 40 + 20
        const y = Math.round((clientY - 20) / 40) * 40 + 20
        // cheeks Specifies the index of a two-dimensional array
        // This is a little redundant, but it makes sense to write it this way
        const cheeksX = (x - 20) / 40
        const cheeksY = (y - 20) / 40
        // The corresponding element is not 0, indicating that there is a move in this place, return
        if (cheeks[cheeksY][cheeksX]) return
        Black is 1 and white is 2
        cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2
        ctx.beginPath()
        / / draw circles
        ctx.arc(x, y, 20.0.2 * Math.PI)
        // Decide whether to go black or white
        ctx.fillStyle = isBlack ? 'black' : 'white'
        ctx.fill()
        ctx.closePath()
        // Switch to black and whiteisBlack = ! isBlack }Copy the code

The effect is as follows:

3. Judge whether it is five consecutive sons

How to tell? There are four situations: up and down five even son, left and right wu Lianzi, left and right five even son, right and left five even son, as long as we each time when all the judgment of a time.

I enclose all the code

play()

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

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

    // Draw the chessboard

    // Horizontal, a total of 15 lines
    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, a total of 15 lines
    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()
    }

    // Whether to play black
    // Black moves first
    let isBlack = true


    // The board is a two-dimensional 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 are at the intersection
        const x = Math.round((clientX - 20) / 40) * 40 + 20
        const y = Math.round((clientY - 20) / 40) * 40 + 20
        // cheeks Specifies the index of a two-dimensional array
        // This is a little redundant, but it makes sense to write it this way
        const cheeksX = (x - 20) / 40
        const cheeksY = (y - 20) / 40
        // The corresponding element is not 0, indicating that there is a move in this place, return
        if (cheeks[cheeksY][cheeksX]) return
        Black is 1 and white is 2
        cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2
        ctx.beginPath()
        / / draw circles
        ctx.arc(x, y, 20.0.2 * Math.PI)
        // Decide whether to go black or white
        ctx.fillStyle = isBlack ? 'black' : 'white'
        ctx.fill()
        ctx.closePath()

        // Canvas is asynchronously drawn. Make sure to draw and then check for wins and losses
        setTimeout(() = > {
            if (isWin(cheeksX, cheeksY)) {
                const con = confirm(`${isBlack ? 'black' : 'white'}Win! Do you want to start again? `)
                // Start again
                ctx.clearRect(0.0.600.600)
                con && play()
            }
            // Switch to black and whiteisBlack = ! isBlack },0)}// Determine whether there are five links
    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
        / / to find 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
        }
        / / 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
        / / to the left to find
        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
        }
        / / to the 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 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 right down
        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 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}}Copy the code

4. Easter Egg: Play chess with AI

In fact, it is very simple, after each game, set a function: find a random position to play chess. In this way, the realization and computer chess, single player game function, this function I have achieved, but I do not write out, to everyone, as you consolidate this article homework. Ha ha ha ha

conclusion

Sleep sleep, this article has written me for 7 hours, in fact, these three small games have a lot of places can be optimized, you can put forward, learn from each other. Like brothers and sisters, thank you!! For study group, please click here