前言
想学习下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()
}
最终效果