VUE3+Canvas绘制五子棋(二)

在昨天的基础上,对五子棋进行了调整

1、增加了背景颜色

2、增加了修改棋盘大小功能

3、增加了重新开始功能

仍未进行代码优化,同时对vue的data内的数据修改、读取,未发现好的方式,既可以直接读取带来的方便,但感觉也会带来随意修改数据产生的意外,会影响健壮性。

完整代码Game.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
<script>
export default {
data() {
return {
board: [],
player: 1,//1--白棋;2--黑棋
winner: null,
restart: false,
boardDetail: {
width: 0,//棋盘大小
lineNumber: 0,//棋盘线数
space: 0,//间隙
}
};
},
// 实例刚创建,但未挂载dom
created() {
document.title = "五子棋";
},
// 实例创建完成,仅在首次加载时完成
mounted() {
this.boardDetail = {
width: document.getElementById("board").clientWidth,
lineNumber: 20,//棋盘线数
space: 30,//间隙
}
this.drawBoard(this.boardDetail.width,
this.boardDetail.lineNumber,
this.boardDetail.space);

},
methods: {
/**
* 画整体棋盘
* @param width 所需棋盘整体大小,上下左右预留一半space空间
* @param lineNumber 线条数,线条数*间距=width
* @param space 间距
*/
drawBoard(width, lineNumber, space) {
const halfSpace = space / 2;

const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d");
// 设置线条颜色
ctx.strokeStyle = "black";

for (let i = 0; i < lineNumber; i++) {
// 绘制横线
ctx.beginPath();
ctx.moveTo(halfSpace, i * space + halfSpace);
ctx.lineTo(width - halfSpace, i * space + halfSpace);
ctx.stroke();
// 绘制竖线
ctx.beginPath();
ctx.moveTo(i * space + halfSpace, halfSpace);
ctx.lineTo(i * space + halfSpace, width - halfSpace);
ctx.stroke();
//填充数组,重置为0
this.board.push(new Array(lineNumber).fill(0))
}
},
/**
* 监听每步棋子,需要传入棋盘信息
* @param event
* @param detail 棋盘信息
*/
handleClickAndDraw(event, detail) {
//存在输赢以后,不允许在落子
if (this.winner != null) {
return;
}
const lineNumber = detail.lineNumber
const space = detail.space
const halfSpace = space / 2;

// let x = event.offsetX;
// let y = event.offsetY;
// console.log(x + ' ' + y)
// 计算棋子落在哪个方格中
const cellX = Math.floor((event.offsetX) / space);
const cellY = Math.floor((event.offsetY) / space);
// console.log(cellX, cellY)
// 判断该位置是否有棋子
if (this.board[cellX][cellY] !== 0) {
alert("该位置已有棋子")
return;
}
const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d");
//画带渐变色的棋子,同心圆形式
//考虑起点为2,因半径为space一半,避免太大,截止1/3大小
let grd = ctx.createRadialGradient(
cellX * space + halfSpace,
cellY * space + halfSpace,
2,
cellX * space + halfSpace,
cellY * space + halfSpace,
space / 3
)
grd.addColorStop(0, this.player === 1 ? '#FFFFFF' : '#4C4C4C')
grd.addColorStop(1, this.player === 1 ? '#DADADA' : '#000000')
ctx.beginPath()
ctx.fillStyle = grd
//画圆,半径设置为space/3,同上r1参数一致
ctx.arc(
cellX * space + halfSpace,
cellY * space + halfSpace,
space / 3,
0,
2 * Math.PI,
false
);
ctx.fill();
ctx.closePath();
this.board[cellX][cellY] = this.player; //将黑白棋信息存储

this.winner = this.checkWinner(this.board, lineNumber) //判断输赢
if (this.winner != null) {
alert(this.winner)
}

this.player = this.player === 1 ? 2 : 1//交换
},
/**
* 胜负检查
* @param board X*X 二维数组
* @param lineNumber 线条数
* @returns {number|null}
*/
checkWinner(board, lineNumber) {
// 检查横向是否有五子连线
for (let i = 0; i < lineNumber; i++) {
let count = 0;
for (let j = 0; j < lineNumber; j++) {
if (board[i][j] === this.player) {
count++;
} else {
count = 0;
}

if (count >= 5) return this.player;
}
}

// 检查纵向是否有五子连线
for (let j = 0; j < lineNumber; j++) {
let count = 0;
for (let i = 0; i < lineNumber; i++) {
if (board[i][j] === this.player) {
count++;
} else {
count = 0;
}

if (count >= 5) return this.player;

}
}

// 检查右斜线是否有五子连线
for (let i = 0; i < lineNumber - 5; i++) {
for (let j = 0; j < lineNumber - 5; j++) {
let count = 0;
for (let k = 0; k < 5; k++) {
if (board[i + k][j + k] === this.player) {
count++;
} else {
count = 0;
}

if (count >= 5) return this.player;

}
}
}

// 检查左斜线是否有五子连线
for (let i = 0; i < lineNumber - 5; i++) {
for (let j = 4; j < lineNumber; j++) {
let count = 0;
for (let k = 0; k < 5; k++) {
if (board[i + k][j - k] === this.player) {
count++;
} else {
count = 0;
}

if (count >= 5) return this.player;
}
}
}

// 如果没有五子连线,则游戏继续
return null;
},
/**
* 重置游戏
*/
restartGame() {
//清空基础数据
this.board = []
this.player = 1
this.winner = null
this.restart = false
//清空画布
const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height)
//重新绘制
this.drawBoard(this.boardDetail.width,
this.boardDetail.lineNumber,
this.boardDetail.space);
},
updateDetail(square, lineNumber) {
//重新设置board大小
let canvas = document.getElementById("board")
canvas.width = square
canvas.height = square
//重新设置棋盘大小
this.boardDetail = {
width: square,
lineNumber: lineNumber,
space: square / lineNumber
}
//重新绘制游戏
this.restartGame()
}
}
};
</script>

<template>
<div class="board">
<canvas id="board" width="600" height="600" class="board-chess"
@click="handleClickAndDraw($event,this.boardDetail)"></canvas>
<div class="board-detail">
<div>
<span>棋盘信息:</span><br>
<span>棋盘长宽:<input v-model="this.boardDetail.width"/></span><br>
<span>棋盘线条数:<input v-model="this.boardDetail.lineNumber"/></span><br>
<span>棋盘间距:<input v-model="this.boardDetail.space" disabled/></span><br>
<button @click="updateDetail(this.boardDetail.width,this.boardDetail.lineNumber)">修改</button>
</div>
<button @click="restartGame">重新开始</button>
<div>当前落子:{{ this.player === 1 ? "白" : "黑" }}</div>
<div>胜利方:{{ this.winner === 1 ? "白棋" : this.winner === 2 ? "黑棋" : "" }}</div>
</div>
</div>
</template>

<style scoped>
.board {
padding: 0 0;
margin: 10px;
display: flex;
align-items: center;
justify-content: center;
}

.board-chess {
border: 3px solid black;
padding: 0 0;
margin: 0 0;
}

.board-detail {
margin: 0 20px;

}

.board-detail > div input {
width: 50px;
}
</style>

效果图,测试中发现了一个bug,斜线的胜负检验逻辑有问题
效果图


VUE3+Canvas绘制五子棋(二)
http://060800.xyz/2024/11/29/VUE3-Canvas绘制五子棋(二)/
作者
砖头
发布于
2024年11月29日
许可协议