canvas学习之五子棋规则法部分实现

前言

想学习下vue中使用canvas,直接学习感觉太无聊,通过实现五子棋增加些乐趣,过程中发现规则算法实现起来还是有一些复杂度的

代码实现

初步代码结构

<template>
    <canvas ref="myCanvas" width="640" height="640" @click="func.next"></canvas>
    <div>
        <button @click="func.start">开始</button>
        <span style="color: red">{{property.message}}</span>
    </div>
</template>

<script>
import {ref, reactive, onMounted} from 'vue'
export default {
    name: "GoBang",
    setup(){
        const myCanvas = ref(null)

        const property = reactive({
            size: 40, //棋盘每格宽高
            rowNum: 15, //棋盘行数列数
            message: '',
        })

        const ai = reactive({

        })

        const func = reactive({
            start: () => {

            },
            next: () => {
                
            }
        })

        onMounted(() => {

        })

        return{
            myCanvas,
            property,
            func
        }
    }
}
</script>

<style scoped>

</style>

canvas组件context要在所有组件加载完才能调用,所以要放到onMunted中

let ctx = ref(null)
const func = reactive({
    init: () => {
        ctx = myCanvas.value.getContext('2d')
    },
    drawChessBoard: () => {
        ctx.beginPath()
        ctx.rect(0, 0, (property.size * (property.rowNum + 1)), (property.size * (property.rowNum + 1)))
        ctx.strokeStyle = 'green'
        ctx.stroke()
        //棋盘
        for(let i = 1; i <= property.rowNum; i++){
            ctx.moveTo(i * property.size, property.size)
            ctx.lineTo(i * property.size, property.size * property.rowNum)
        }
        for(let j = 1; j <= property.rowNum; j++){
            ctx.moveTo(property.size, property.size * j)
            ctx.lineTo(property.size * property.rowNum, property.size * j)
        }
        ctx.stroke()
        ctx.closePath()
    },
    start: () => {

    },
    next: () => {

    }
})
onMounted(() => {
    func.init()
    func.drawChessBoard()
})

效果图

画棋子

const property = reactive({
    size: 40, //棋盘每格宽高
    rowNum: 15, //棋盘行数列数
    message: '',
    validRange: 0.3  //棋子的半径占每格比
})
//func中
drawChess: (x, y, color) => {
    ctx.beginPath()
    ctx.moveTo(x+ property.size * property.validRange, y )
    ctx.arc(x, y, property.size * property.validRange, 0, 2 * Math.PI)
    ctx.strokeStyle = color
    ctx.fillStyle = color
    ctx.fill()
    ctx.stroke()
    ctx.closePath()
}

ai逻辑

const operate = reactive({
    computer: 1,
    people: 2
})
const ai = reactive({
    //检测是否五子
    checkFive: () => {},
    //查找单4的位置
    findBySingleFour: () => {},
    //查找下一步棋后将会双4的位置
    findWillBeDoubleFour: () => {},
    //查找下一步棋后将会单4且双三的位置 暂不实现
    findWillBeSingleFourAndDoubleThree: () => {},
    //查找下一步棋后将会两个双三的位置 暂不实现
    findWillBeTwoDoubleThree: () => {},
    //查找下两步棋后将会是双4的位置 代替上面两个未实现的方法,但减少了智能
    findWillBeDoubleFourStep2: () => {},
    //查找跟随位置,跟随人下棋的位置 落子到旁边
    findFollowPosition:() => {},
    //周边找不到位置 随机
    findRandomPosition: () => {}
})

checkFive

checkFive: (a, rowNum, operator) => {
    for(let i = 0; i < rowNum; i++){
        for(let j = 0; j < rowNum ; j++){
            if(i >= 2 && i < (rowNum - 2) && (a[i][j] & a[i + 1][j] & a[i + 2][j] & a[i - 1][j] & a[i - 2][j]) == operator ||
                j >= 2 && j < (rowNum - 2) && (a[i][j] & a[i][j + 1] & a[i][j + 2] & a[i][ j - 1] & a[i][j - 2]) == operator ||
                i >= 2 && j >= 2 && i < (rowNum - 2) && j < (rowNum - 2) && (a[i][j] & a[i + 1][j + 1] & a[i + 2][j + 2] & a[i - 1][j - 1] & a[i - 2][j - 2]) == operator ||
                i >= 2 && j >= 2 && i < (rowNum - 2) && j < (rowNum - 2) && (a[i][j] & a[i - 1][j + 1] & a[i - 2][j + 2] & a[i + 1][j - 1] & a[i + 2][j - 2]) == operator){
                return true
            }
        }
    }
    return false
}

findBySingleFour

//查找单4的位置
findBySingleFour: (a, rowNum, operator) => {
    let sum = 4 * operator
    for(let i = 0; i < rowNum; i++){
        for(let j = 0; j < rowNum; j++){
            //横向判断
            if(i >= 2 && i < rowNum - 2 && (a[i][j] + a[i + 1][j] + a[i + 2][j] + a[i - 1][j] + a[i - 2][j]) == sum &&
                (a[i][j] | a[i + 1][j] | a[i + 2][j] | a[i - 1][j] | a[i - 2][j]) == operator){
                for(let index = 0; index < 5; index++){
                    if(a[i - 2 + index][j] == 0){
                        return {
                            x: i - 2 + index,
                            y: j
                        }
                    }
                }
            }
            //纵向判断
            if(j >= 2 && j < rowNum - 2 && (a[i][j] + a[i][j + 1] + a[i][j + 2] + a[i][ j - 1] + a[i][j - 2]) == sum &&
                (a[i][j] | a[i][j + 1] | a[i][j + 2] | a[i][j - 1] | a[i][j - 2]) == operator){
                for(let index = 0; index < 5; index++){
                    if(a[i][j - 2 + index] == 0){
                        return {
                            x: i,
                            y: j - 2 + index
                        }
                    }
                }
            }
            //正斜向判断
            if(i >= 2 && i < rowNum - 2 && j >= 2 && j < rowNum - 2 && (a[i][j] + a[i + 1][j + 1] + a[i + 2][j + 2] + a[i - 1][j - 1] + a[i - 2][j - 2]) == sum &&
                (a[i][j] | a[i + 1][j + 1] | a[i + 2][j + 2] | a[i - 1][j - 1] | a[i - 2][j - 2]) == operator){
                for(let index = 0; index < 5; index++){
                    if(a[i - 2 + index][j - 2 + index] == 0){
                        return {
                            x: i - 2 + index,
                            y: j - 2 + index
                        }
                    }
                }
            }
            //反斜向判断
            if(i >= 2 && i < rowNum - 2 && j >= 2 && j < rowNum - 2 && (a[i][j] + a[i - 1][j + 1] + a[i - 2][j + 2] + a[i + 1][j - 1] + a[i + 2][j - 2]) == sum &&
                (a[i][j] | a[i - 1][j + 1] | a[i - 2][j + 2] | a[i + 1][j - 1] | a[i + 2][j - 2]) == operator){
                for(let index = 0; index < 5; index++){
                    if(a[i - 2 + index][j + 2 - index] == 0){
                        return {
                            x: i - 2 + index,
                            y: j + 2 - index
                        }
                    }
                }
            }
        }
    }
    return null
}

findWillBeDoubleFour

findWillBeDoubleFour: (a, rowNum, operator) => {
    let sum = 3 * operator
    for(let i = 0; i < rowNum; i++){
        for(let j = 0; j < rowNum; j++){
            if(i >= 2 && i < rowNum - 2 && (a[i][j] + a[i + 1][j] + a[i + 2][j] + a[i - 1][j] + a[i - 2][j]) == sum &&
                (a[i][j] | a[i + 1][j] | a[i + 2][j] | a[i - 1][j] | a[i - 2][j]) == operator && (a[i - 2][j] == 0 || a[i + 2][j] == 0)){
                //两边界必须为空
                if(a[i - 2][j] == 0 && i < rowNum - 3 && a[i + 3][j] == 0 ||
                    a[i + 2][j] == 0 && i > 2 && a[i - 3][j] == 0){
                    for(let index = 0; index < 3; index++){
                        if(a[i - 1 + index][j] == 0){
                            return {
                                x: i - 1 + index,
                                y: j
                            }
                        }
                    }
                    //中间三个满了
                    return a[i - 2][j] == 0 && i < rowNum - 3 && a[i + 3][j] == 0 ?
                        {
                            x: i + 2,
                            y: j
                        } :
                        {
                            x: i - 2,
                            y: j
                        }
                }
            }
            //纵向判断
            if(j >= 2 && j < rowNum - 2 && (a[i][j] + a[i][j + 1] + a[i][j + 2] + a[i][ j - 1] + a[i][j - 2]) == sum &&
                (a[i][j] | a[i][j + 1] | a[i][j + 2] | a[i][j - 1] | a[i][j - 2]) == operator && (a[i][j - 2] == 0 || a[i][j + 2] == 0)){
                //两边界必须为空
                if(a[i][j - 2] == 0 && j < rowNum - 3 && a[i][j + 3] == 0 ||
                    a[i][j + 2] == 0 && j > 2 && a[i][j - 3] == 0){
                    for(let index = 0; index < 3; index++){
                        if(a[i][j - 1 + index] == 0){
                            return {
                                x: i,
                                y: j - 1 + index
                            }
                        }
                    }
                    //中间三个满了
                    return a[i][j - 2] == 0 && j < rowNum - 3 && a[i][j + 3] == 0 ?
                        {
                            x: i,
                            y: j + 2
                        } :
                        {
                            x: i,
                            y: j - 2
                        }
                }
            }
            //正斜向判断
            if(i >= 2 && i < rowNum - 2 && j >= 2 && j < rowNum - 2 && (a[i][j] + a[i + 1][j + 1] + a[i + 2][j + 2] + a[i - 1][j - 1] + a[i - 2][j - 2]) == sum &&
                (a[i][j] | a[i + 1][j + 1] | a[i + 2][j + 2] | a[i - 1][j - 1] | a[i - 2][j - 2]) == operator && (a[i - 2][j - 2] == 0 || a[i + 2][j + 2] == 0)){
                //两边界必须为空
                if(a[i - 2][j - 2] == 0 && i < rowNum - 3 && j < rowNum - 3 && a[i + 3][j + 3] == 0 ||
                    a[i + 2][j + 2] == 0 && i > 2 && j > 2 && a[i - 3][j - 3] == 0){
                    for(let index = 0; index < 3; index++){
                        if(a[i - 1 + index][j - 1 + index] == 0){
                            return {
                                x: i - 1 + index,
                                y: j - 1 + index
                            }
                        }
                    }
                    //中间三个满了
                    return a[i - 2][j - 2] == 0 && i < rowNum - 3 && j < rowNum - 3 && a[i + 3][j + 3] == 0 ?
                        {
                            x: i + 2,
                            y: j + 2
                        } :
                        {
                            x: i - 2,
                            y: j - 2
                        }
                }
            }
            //反斜向判断
            if(i >= 2 && i < rowNum - 2 && j >= 2 && j < rowNum - 2 && (a[i][j] + a[i - 1][j + 1] + a[i - 2][j + 2] + a[i + 1][j - 1] + a[i + 2][j - 2]) == sum &&
                (a[i][j] | a[i - 1][j + 1] | a[i - 2][j + 2] | a[i + 1][j - 1] | a[i + 2][j - 2]) == operator && (a[i + 2][j - 2] == 0 || a[i - 2][j + 2] == 0)){
                //两边界必须为空
                if(a[i - 2][j + 2] == 0 && i < rowNum - 3 && j > 2 && a[i + 3][j - 3] == 0 ||
                    a[i + 2][j - 2] == 0 && i > 2 && j < rowNum - 3 && a[i - 3][j + 3] == 0){
                    for(let index = 0; index < 3; index++){
                        if(a[i - 1 + index][j + 1 - index] == 0){
                            return {
                                x: i - 1 + index,
                                y: j + 1 - index
                            }
                        }
                    }
                    //中间三个满了
                    return a[i - 2][j + 2] == 0 && i < rowNum - 3 && j > 2 && a[i + 3][j - 3] == 0 ?
                        {
                            x: i - 2,
                            y: j + 2
                        } :
                        {
                            x: i + 2,
                            y: j - 2
                        }
                }
            }
        }
    }
    return null
}

findWillBeDoubleFourStep2

//查找下两步棋后将会是双4的位置 代替上面两个未实现的方法,但减少了智能
findWillBeDoubleFourStep2: (a, rowNum, operator) => {
    for(let i = 0; i < rowNum; i++){
        for(let j = 0; j < rowNum; j++){
            if(a[i][j] == 0){
                a[i][j] = operator
                let p = ai.findWillBeDoubleFour(a, rowNum, operator)
                a[i][j] = 0
                if(p){
                    return {
                        x: i,
                        y: j
                    }
                }
            }
        }
    }
}

findFollowPosition

//查找跟随位置,跟随人下棋的位置 落子到旁边
findFollowPosition: (a, i, j, rowNum) => {
    if(i >= 1 && a[i - 1][j] == 0){
        return {
            x: i - 1,
            y: j
        }
    }
    if(j >= 1 && a[i][j - 1] == 0){
        return {
            x: i,
            y: j - 1
        }
    }
    if(i < rowNum - 1 && a[i + 1][j] == 0){
        return {
            x: i + 1,
            y: j
        }
    }
    if(j < rowNum - 1 && a[i][j + 1] == 0){
        return {
            x: i,
            y: j + 1
        }
    }
    if(i >= 1 && j >= 1 && a[i - 1][j - 1] == 0){
        return {
            x: i - 1,
            y: j - 1
        }
    }
    if(i >= 1 && j < rowNum - 1 && a[i - 1][j + 1] == 0){
        return {
            x: i - 1,
            y: j + 1
        }
    }
    if(i < rowNum - 1 && j >= 1 && a[i + 1][j - 1] == 0){
        return {
            x: i + 1,
            y: j - 1
        }
    }
    if(i < rowNum - 1 && j < rowNum - 1 && a[i + 1][j + 1] == 0){
        return {
            x: i + 1,
            y: j + 1
        }
    }
}

findRandomPosition

//周边找不到位置 随机
findRandomPosition: (rowNum, dressNum) => {
    let value = 0
    let totalNum = rowNum * rowNum
    while(dressNum < totalNum){
        value = Math.floor(Math.random() * rowNum * rowNum)
        let i = Math.floor(value / rowNum)
        let j = value % property.rowNum
        if(property.data[x][y] == 0){
            return {
                x : i,
                y : j
            }
        }
    }
    return null
}

人落子相关

const property = reactive({
    size: 40, //棋盘每格宽高
    rowNum: 15, //棋盘行数列数
    message: '',
    validRange: 0.3,  //棋子的半径占每格比
    canMove: false,
    data: [],
    dressNum: 0
})

//人最后落子的位置
const lastPosition = {
    i: 0,
    j: 0
}

start: () => {
    property.canMove = true
},
next: (p) => {
    if(!property.canMove){
        return
    }
    property.canMove = false
    let x = p.offsetX
    let y = p.offsetY
    x = func.calcPosition(x, property.validRange, property.size)
    y = func.calcPosition(y, property.validRange, property.size)
    if(x == -1 || y == -1){
        return
    }
    func.drawChess(x, y, 'red')

    //记忆落子位置
    let i = x / property.size - 1
    let j = y / property.size - 1
    lastPosition.i = i
    lastPosition.j = j
    property.data[i][j] = operate.people

    if(ai.checkFive(property.data, property.rowNum, operate.people)){
        func.win(operate.people)
        return
    }
    func.aiNext()
},
calcPosition: (x, validRange, size) => {
    let xRemainder = x % size
    return xRemainder < validRange * size ? (x - xRemainder) :
        (xRemainder < (1 - validRange) * size ? - 1 : (x + size - xRemainder))
},

ai落子

aiNext: () => {
    //检查自己单4
    let p = ai.findBySingleFour(property.data, property.rowNum, operate.computer)
    if(p){
        func.drawChess((p.x + 1) * property.size, (p.y + 1) * property.size, 'black')
        func.win(operate.computer)
        return
    }
    //查找对方单4
    p = ai.findBySingleFour(property.data, property.rowNum, operate.people)
    if(p){
        func.drawBlackChess(p)
        return
    }
    //查找自己将要双4的位置
    p = ai.findWillBeDoubleFour(property.data, property.rowNum, operate.computer)
    if(p){
        func.drawBlackChess(p)
        return
    }
    //查找对方将要双4的位置
    p = ai.findWillBeDoubleFour(property.data, property.rowNum, operate.people)
    if(p){
        func.drawBlackChess(p)
        return
    }
    //查找对方两步将要双4的位置
    p = ai.findWillBeDoubleFourStep2(property.data, property.rowNum, operate.people)
    if(p){
        func.drawBlackChess(p)
        return
    }
    //查找自己两步将要双4的位置
    p = ai.findWillBeDoubleFourStep2(property.data, property.rowNum, operate.computer)
    if(p){
        func.drawBlackChess(p)
        return
    }
    //跟随位置
    p = ai.findFollowPosition(property.data, lastPosition.i, lastPosition.j, property.rowNum)
    if(p){
        func.drawBlackChess(p)
        return
    }
    p = ai.findRandomPosition(property.rowNum, property.dressNum)
    func.drawBlackChess(p)

},
win: (operator) => {
    property.message = operator == 1 ? "黑方胜" : "红方胜"
    property.canMove = false
},
drawBlackChess: (p) => {
    func.drawChess((p.x + 1) * property.size, (p.y + 1) * property.size, 'black')
    property.data[p.x][p.y] = 1
    property.canMove = true
},
checkDressNum: () => {
    if(property.dressNum == property.rowNum * property.rowNum){
        property.message = "和棋了"
    }
},
drawChess: (x, y, color) => {
    ctx.beginPath()
    ctx.moveTo(x+ property.size * property.validRange, y )
    ctx.arc(x, y, property.size * property.validRange, 0, 2 * Math.PI)
    ctx.strokeStyle = color
    ctx.fillStyle = color
    ctx.fill()
    ctx.stroke()
    ctx.closePath()
    property.dressNum++
    func.checkDressNum()
}

最终效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mitpast

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值