-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsketch.js
273 lines (237 loc) · 7.13 KB
/
sketch.js
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
// This project is developed based on the code and concepts from
// Daniel Shiffman's video on Tic Tac Toe AI with Minimax Algorithm
// that is online here: https://www.youtube.com/watch?v=trKjYdBASyQ
// global variables
let gameBoard = [
[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1]
];
let module;
let offset = 20;
let strokeWidth = 6;
let player0 = 0; // human player
let player1 = 1; // ai player
let openMoves = 9;
let currentPlayer = 1;
let winner = -1;
function setup() {
createCanvas(400, 400);
module = (width - (offset * 2)) / 3;
ellipseMode(CORNER);
stroke(255);
strokeWeight(strokeWidth);
smooth();
}
function updateBoard() {
if (winner == -1 && openMoves > 0) {
background(74, 182, 212);
// draw current board
for (let i = 1; i < 3; i++) {
line(offset, module * i + offset, width - offset, module * i + offset);
line(module * i + offset, offset, module * i + offset, height - offset);
}
// draw board players
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (gameBoard[j][i] === 1) {
line(2 * offset + module * i, offset * 2 + module * j, module * (i + 1), module * (j + 1));
line(module * (i + 1), 2 * offset + module * j, 2 * offset + module * i, module * (j + 1));
} else if (gameBoard[j][i] === 0) {
ellipse(2 * offset + module * i, offset * 2 + module * j, module - offset * 2);
}
}
}
} else {
background(74, 182, 212);
// draw current board;
for (let i = 1; i < 3; i++) {
line(offset, module * i + offset, width - offset, module * i + offset);
line(module * i + offset, offset, module * i + offset, height - offset);
}
// draw board players
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (gameBoard[j][i] === 1) {
line(2 * offset + module * i, offset * 2 + module * j, module * (i + 1), module * (j + 1));
line(module * (i + 1), 2 * offset + module * j, 2 * offset + module * i, module * (j + 1));
} else if (gameBoard[j][i] === 0) {
ellipse(2 * offset + module * i, offset * 2 + module * j, module - offset * 2);
}
}
}
// draw winners
background(74, 182, 212, 99);
// draw board players
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (gameBoard[j][i] === 1) {
if (gameBoard[j][i] == winner) {
line(2 * offset + module * i, offset * 2 + module * j, module * (i + 1), module * (j + 1));
line(module * (i + 1), 2 * offset + module * j, 2 * offset + module * i, module * (j + 1));
}
} else if (gameBoard[j][i] === 0) {
if (gameBoard[j][i] == winner) {
ellipse(2 * offset + module * i, offset * 2 + module * j, module - offset * 2);
}
}
}
}
}
}
function matchCheck(x, y, z) {
if (x != -1 && x == y && y == z) {
// console.log(a,b,c);
return true;
} else {
// console.log(a,b,c);
return false;
}
}
function checkWinner(board, moves) {
for (let i = 0; i < 3; i++) {
// check all horizontal
if (matchCheck(board[i][0], board[i][1], board[i][2])) {
return gameBoard[i][0];
}
// check all vertical
if (matchCheck(board[0][i], board[1][i], board[2][i])) {
// winner
return board[0][i];
}
// check all diagnol
if (matchCheck(board[0][0], board[1][1], board[2][2])) {
// winner
return gameBoard[0][0];
}
if (matchCheck(board[2][0], board[1][1], board[0][2])) {
// winner
return board[2][0];
}
}
if (moves > 0) {
// no current winner so return -1
return -1;
} else {
// no winner and no moves so return -2 for tie
return -2;
}
}
function aiMove() {
// get best possible move
let highScore = -Infinity;
let smartMove;
if (openMoves > 0) {
if (currentPlayer == 1) {
// loop through all possible moves
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (gameBoard[j][i] == -1) {
gameBoard[j][i] = player1;
let curScore = minimax(gameBoard, openMoves - 1, 0, false);
gameBoard[j][i] = -1;
// test for max score
if (highScore < curScore) {
highScore = curScore;
smartMove = [j,i];
}
}
}
}
// make move
gameBoard[smartMove[0]][smartMove[1]] = currentPlayer;
currentPlayer = (currentPlayer + 1) % 2;
openMoves -= 1;
return true;
}
}
}
// board is array of ints representing state of board to score
// moves is the number of moves left on the given board
// level is the recursive depth of the game state
// if maximize is true then return a max score for winning of 1
// else maximize is false return a min score for winning -1
// when there is a tie return a neutral score of 0
function minimax(board, moves, level, maximize) {
// check for winner
let result = checkWinner(board, moves);
if (result == -2) {
// tie. return neutral points.
return 0;
} else if (result == player1 ) {
// ai wins return max points
return 10;
} else if (result == player0) {
// human wins return min points
return -10;
} else {
// no result. call all submoves.
if (maximize) {
// maximize true means player1 gets next move
// try for max possible score
// loop through all moves return max score
let highScore = -Infinity;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[j][i] == -1) {
board[j][i] = player1;
let curScore = minimax(board, moves - 1, 0 + 1, !maximize);
board[j][i] = -1;
highScore = max(highScore, curScore);
}
}
}
return highScore;
} else {
// maximize false means player0 gets next move
// try for min possible score
// loop through all moves return min score
let lowScore = Infinity;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[j][i] == -1) {
board[j][i] = player0;
let curScore = minimax(board, moves - 1, 0 + 1, !maximize);
board[j][i] = -1;
lowScore = min(lowScore, curScore);
}
}
}
return lowScore;
}
}
// recursive case: call minimax for all possible moves
// get score for all possible moves
// evaluate max move score
// evaluate min move score
}
function mouseClicked() {
if (openMoves > 0 && currentPlayer == 0 && winner == -1) {
let minDist = Infinity;
let cury = floor(mouseY / module);
let curx = floor(mouseX / module);
if (gameBoard[cury][curx] == -1) {
gameBoard[cury][curx] = currentPlayer;
currentPlayer = (currentPlayer + 1) % 2;
openMoves -= 1;
}
} else {
// new game
gameBoard = [
[-1, -1, -1],
[-1, -1, -1],
[-1, -1, -1]
];
openMoves = 9;
currentPlayer = 1;
winner = -1;
}
}
function draw() {
// console.log(checkWinner());
// check for winner
winner = checkWinner(gameBoard, openMoves);
// check for play
aiMove();
updateBoard();
}