kolaslab commited on
Commit
32a8ea3
1 Parent(s): 811d202

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +304 -189
index.html CHANGED
@@ -1,5 +1,5 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -10,128 +10,239 @@
10
  padding: 0;
11
  box-sizing: border-box;
12
  }
 
13
  body {
 
 
 
14
  display: flex;
15
- flex-direction: column;
16
  align-items: center;
17
- min-height: 100vh;
18
- background: #f0f0f0;
19
- font-family: Arial, sans-serif;
20
  padding: 20px;
21
  }
 
22
  .game-container {
23
- background: #DEB887;
24
- padding: 20px;
25
- border-radius: 10px;
26
- box-shadow: 0 0 20px rgba(0,0,0,0.1);
 
27
  }
 
28
  .board {
29
  display: grid;
30
  grid-template-columns: repeat(19, 30px);
31
  grid-template-rows: repeat(19, 30px);
32
- gap: 0px;
33
- background: #DEB887;
34
- border: 2px solid #000;
35
- position: relative;
 
 
 
36
  }
 
37
  .intersection {
38
  width: 30px;
39
  height: 30px;
40
  position: relative;
41
  cursor: pointer;
 
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  .intersection::before {
44
  content: '';
45
  position: absolute;
46
- top: 50%;
47
  left: 0;
 
48
  width: 100%;
49
  height: 1px;
50
- background: #000;
 
51
  }
 
52
  .intersection::after {
53
  content: '';
54
  position: absolute;
55
- left: 50%;
56
  top: 0;
 
57
  height: 100%;
58
  width: 1px;
59
- background: #000;
 
60
  }
61
- .stone {
 
 
62
  position: absolute;
63
- width: 26px;
64
- height: 26px;
 
65
  border-radius: 50%;
66
- top: 2px;
67
- left: 2px;
68
- z-index: 1;
69
- transition: all 0.2s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
 
71
  .black {
72
- background: #000;
73
- box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
74
  }
 
75
  .white {
76
- background: #fff;
77
- box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
78
  }
 
79
  .controls {
80
- margin-top: 20px;
81
  display: flex;
82
- gap: 10px;
 
 
83
  }
 
84
  button {
85
  padding: 10px 20px;
86
  font-size: 16px;
87
  border: none;
88
- border-radius: 5px;
89
- cursor: pointer;
90
- background: #4CAF50;
91
  color: white;
92
- transition: background 0.3s;
 
 
93
  }
 
94
  button:hover {
95
- background: #45a049;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  }
97
- .score {
98
- margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
99
  font-size: 18px;
100
- display: flex;
101
- gap: 20px;
102
  }
 
 
 
 
 
 
103
  .mode-select {
 
 
 
104
  margin-bottom: 20px;
105
  }
106
- select {
107
- padding: 8px;
108
- font-size: 16px;
109
- border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
  </style>
112
  </head>
113
  <body>
114
- <div class="mode-select">
115
- <select id="gameMode">
116
- <option value="pvp">Player vs Player</option>
117
- <option value="ai-easy">Player vs AI (Easy)</option>
118
- <option value="ai-middle">Player vs AI (Middle)</option>
119
- <option value="ai-hard">Player vs AI (Hard)</option>
120
- </select>
121
- </div>
122
-
123
  <div class="game-container">
124
- <div class="board" id="board"></div>
125
- </div>
 
 
 
 
 
 
 
 
126
 
127
- <div class="score">
128
- <div>Black Score: <span id="blackScore">0</span></div>
129
- <div>White Score: <span id="whiteScore">0</span></div>
130
- </div>
131
 
132
- <div class="controls">
133
- <button id="passBtn">Pass</button>
134
- <button id="resetBtn">Reset</button>
 
 
 
 
 
 
 
135
  </div>
136
 
137
  <script>
@@ -140,41 +251,59 @@
140
  this.size = 19;
141
  this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
142
  this.currentPlayer = 'black';
 
143
  this.lastMove = null;
144
- this.passes = 0;
145
  this.gameMode = 'pvp';
146
- this.aiDifficulty = 'easy';
147
- this.score = { black: 0, white: 0 };
148
- this.capturedStones = { black: 0, white: 0 };
149
-
150
  this.initialize();
151
  }
 
152
  initialize() {
153
  const boardElement = document.getElementById('board');
154
  boardElement.innerHTML = '';
155
-
156
  for(let i = 0; i < this.size; i++) {
157
  for(let j = 0; j < this.size; j++) {
158
  const intersection = document.createElement('div');
159
  intersection.className = 'intersection';
160
  intersection.dataset.row = i;
161
  intersection.dataset.col = j;
 
 
 
 
 
162
  intersection.addEventListener('click', (e) => this.handleMove(e));
163
  boardElement.appendChild(intersection);
164
  }
165
  }
 
166
  document.getElementById('passBtn').addEventListener('click', () => this.pass());
167
  document.getElementById('resetBtn').addEventListener('click', () => this.reset());
168
  document.getElementById('gameMode').addEventListener('change', (e) => {
169
- const mode = e.target.value;
170
- this.gameMode = mode.startsWith('ai') ? 'ai' : 'pvp';
171
- this.aiDifficulty = mode.split('-')[1] || 'easy';
172
  this.reset();
173
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
 
175
  handleMove(e) {
176
  const row = parseInt(e.target.dataset.row);
177
  const col = parseInt(e.target.dataset.col);
 
178
  if(this.isValidMove(row, col)) {
179
  this.placeStone(row, col);
180
 
@@ -183,46 +312,78 @@
183
  }
184
  }
185
  }
 
186
  isValidMove(row, col) {
187
  if(this.board[row][col] !== null) return false;
188
 
189
  this.board[row][col] = this.currentPlayer;
190
- const group = this.getGroup(row, col);
191
- const liberties = this.countLiberties(group);
192
 
193
  this.board[row][col] = null;
194
 
195
- return liberties > 0;
196
  }
 
197
  placeStone(row, col) {
198
- if(this.board[row][col] === null) {
199
- this.board[row][col] = this.currentPlayer;
200
- this.renderStone(row, col);
201
- this.captures();
202
- this.passes = 0;
203
- this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
204
- this.updateScore();
205
- }
 
206
  }
 
207
  renderStone(row, col) {
208
  const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
209
  const stone = document.createElement('div');
210
  stone.className = `stone ${this.currentPlayer}`;
211
  intersection.appendChild(stone);
212
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  getGroup(row, col) {
214
  const color = this.board[row][col];
215
- const group = new Set();
 
216
  const stack = [[row, col]];
217
 
218
  while(stack.length > 0) {
219
  const [r, c] = stack.pop();
220
  const key = `${r},${c}`;
221
 
222
- if(!group.has(key)) {
223
- group.add(key);
224
- const neighbors = this.getNeighbors(r, c);
225
 
 
226
  for(const [nr, nc] of neighbors) {
227
  if(this.board[nr][nc] === color) {
228
  stack.push([nr, nc]);
@@ -233,140 +394,94 @@
233
 
234
  return group;
235
  }
236
- countLiberties(group) {
237
- const liberties = new Set();
238
-
239
- for(const pos of group) {
240
- const [row, col] = pos.split(',').map(Number);
241
  const neighbors = this.getNeighbors(row, col);
242
-
243
- for(const [nr, nc] of neighbors) {
244
- if(this.board[nr][nc] === null) {
245
- liberties.add(`${nr},${nc}`);
246
- }
247
  }
248
  }
249
-
250
- return liberties.size;
251
  }
 
252
  getNeighbors(row, col) {
253
  const neighbors = [];
254
  const directions = [[-1,0], [1,0], [0,-1], [0,1]];
255
 
256
- for(const [dr, dc] of directions) {
257
- const newRow = row + dr;
258
- const newCol = col + dc;
259
 
260
- if(newRow >= 0 && newRow < this.size && newCol >= 0 && newCol < this.size) {
 
261
  neighbors.push([newRow, newCol]);
262
  }
263
  }
264
 
265
  return neighbors;
266
  }
267
- evaluatePosition(row, col) {
268
- let score = 0;
 
 
 
 
 
 
 
269
 
270
- // 보드 중앙에 가까울수록 높은 점수
271
- const centerDistance = Math.abs(row - 9) + Math.abs(col - 9);
272
- score += (18 - centerDistance) * 2;
273
- // 주변 돌의 상태 평가
274
- const neighbors = this.getNeighbors(row, col);
275
- let friendlyStones = 0;
276
- let enemyStones = 0;
277
- let liberties = 0;
278
- for (const [nr, nc] of neighbors) {
279
- if (this.board[nr][nc] === this.currentPlayer) friendlyStones++;
280
- else if (this.board[nr][nc] === null) liberties++;
281
- else enemyStones++;
282
  }
283
- score += friendlyStones * 10;
284
- score += liberties * 5;
285
- score += enemyStones * 3;
286
- return score;
287
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  makeAIMove() {
289
- const validMoves = [];
290
- const scoredMoves = [];
291
- for (let i = 0; i < this.size; i++) {
292
- for (let j = 0; j < this.size; j++) {
293
- if (this.isValidMove(i, j)) {
294
- const score = this.evaluatePosition(i, j);
295
- scoredMoves.push({ row: i, col: j, score: score });
296
- validMoves.push([i, j]);
297
- }
298
- }
299
- }
300
- if (validMoves.length === 0) {
301
- this.pass();
302
- return;
303
  }
304
- let selectedMove;
305
- switch (this.aiDifficulty) {
306
- case 'easy':
307
- selectedMove = validMoves[Math.floor(Math.random() * validMoves.length)];
308
- break;
309
- case 'middle':
310
- scoredMoves.sort((a, b) => b.score - a.score);
311
- const middlePool = scoredMoves.slice(0, Math.floor(scoredMoves.length / 2));
312
- const middleChoice = middlePool[Math.floor(Math.random() * middlePool.length)];
313
- selectedMove = [middleChoice.row, middleChoice.col];
314
- break;
315
- case 'hard':
316
- scoredMoves.sort((a, b) => b.score - a.score);
317
- const bestMove = scoredMoves[0];
318
- selectedMove = [bestMove.row, bestMove.col];
319
- break;
320
- }
321
- this.placeStone(selectedMove[0], selectedMove[1]);
322
  }
323
- captures() {
 
 
324
  for(let i = 0; i < this.size; i++) {
325
  for(let j = 0; j < this.size; j++) {
326
- if(this.board[i][j] !== null) {
327
- const group = this.getGroup(i, j);
328
- if(this.countLiberties(group) === 0) {
329
- for(const pos of group) {
330
- const [row, col] = pos.split(',').map(Number);
331
- const capturedColor = this.board[row][col];
332
- this.board[row][col] = null;
333
- const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
334
- intersection.innerHTML = '';
335
- this.capturedStones[this.currentPlayer]++;
336
- }
337
- }
338
  }
339
  }
340
  }
341
- }
342
- pass() {
343
- this.passes++;
344
- if(this.passes === 2) {
345
- alert('Game Over!');
346
- return;
347
- }
348
- this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
349
- if(this.gameMode === 'ai' && this.currentPlayer === 'white') {
350
- setTimeout(() => this.makeAIMove(), 500);
351
  }
352
  }
353
- reset() {
354
- this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
355
- this.currentPlayer = 'black';
356
- this.passes = 0;
357
- this.score = { black: 0, white: 0 };
358
- this.capturedStones = { black: 0, white: 0 };
359
- const intersections = document.querySelectorAll('.intersection');
360
- intersections.forEach(intersection => intersection.innerHTML = '');
361
- this.updateScore();
362
- }
363
- updateScore() {
364
- document.getElementById('blackScore').textContent =
365
- this.score.black + this.capturedStones.black;
366
- document.getElementById('whiteScore').textContent =
367
- this.score.white + this.capturedStones.white;
368
  }
369
  }
 
370
  const game = new GoGame();
371
  </script>
372
  </body>
 
1
  <!DOCTYPE html>
2
+ <html>
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
10
  padding: 0;
11
  box-sizing: border-box;
12
  }
13
+
14
  body {
15
+ font-family: 'Segoe UI', system-ui, sans-serif;
16
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
17
+ min-height: 100vh;
18
  display: flex;
19
+ justify-content: center;
20
  align-items: center;
 
 
 
21
  padding: 20px;
22
  }
23
+
24
  .game-container {
25
+ max-width: 800px;
26
+ background: rgba(255, 255, 255, 0.95);
27
+ border-radius: 20px;
28
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
29
+ padding: 30px;
30
  }
31
+
32
  .board {
33
  display: grid;
34
  grid-template-columns: repeat(19, 30px);
35
  grid-template-rows: repeat(19, 30px);
36
+ gap: 1px;
37
+ background: #dcb35c;
38
+ border: 2px solid #2c3e50;
39
+ border-radius: 4px;
40
+ margin: 20px auto;
41
+ padding: 10px;
42
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
43
  }
44
+
45
  .intersection {
46
  width: 30px;
47
  height: 30px;
48
  position: relative;
49
  cursor: pointer;
50
+ transition: background 0.2s;
51
  }
52
+
53
+ .intersection:hover::before {
54
+ content: '';
55
+ position: absolute;
56
+ width: 28px;
57
+ height: 28px;
58
+ border-radius: 50%;
59
+ background: rgba(0, 0, 0, 0.1);
60
+ top: 1px;
61
+ left: 1px;
62
+ z-index: 2;
63
+ }
64
+
65
  .intersection::before {
66
  content: '';
67
  position: absolute;
 
68
  left: 0;
69
+ top: 50%;
70
  width: 100%;
71
  height: 1px;
72
+ background: rgba(0, 0, 0, 0.7);
73
+ z-index: 1;
74
  }
75
+
76
  .intersection::after {
77
  content: '';
78
  position: absolute;
 
79
  top: 0;
80
+ left: 50%;
81
  height: 100%;
82
  width: 1px;
83
+ background: rgba(0, 0, 0, 0.7);
84
+ z-index: 1;
85
  }
86
+
87
+ .star-point::after {
88
+ content: '';
89
  position: absolute;
90
+ width: 8px;
91
+ height: 8px;
92
+ background: #2c3e50;
93
  border-radius: 50%;
94
+ top: 50%;
95
+ left: 50%;
96
+ transform: translate(-50%, -50%);
97
+ z-index: 2;
98
+ }
99
+
100
+ .stone {
101
+ width: 28px;
102
+ height: 28px;
103
+ border-radius: 50%;
104
+ position: absolute;
105
+ top: 1px;
106
+ left: 1px;
107
+ z-index: 3;
108
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
109
+ animation: placeStone 0.3s ease-out;
110
+ }
111
+
112
+ @keyframes placeStone {
113
+ from {
114
+ transform: scale(1.2);
115
+ opacity: 0;
116
+ }
117
+ to {
118
+ transform: scale(1);
119
+ opacity: 1;
120
+ }
121
  }
122
+
123
  .black {
124
+ background: radial-gradient(circle at 35% 35%, #4a4a4a 0%, #000000 100%);
125
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
126
  }
127
+
128
  .white {
129
+ background: radial-gradient(circle at 35% 35%, #ffffff 0%, #e0e0e0 100%);
130
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
131
  }
132
+
133
  .controls {
 
134
  display: flex;
135
+ justify-content: center;
136
+ gap: 15px;
137
+ margin: 20px 0;
138
  }
139
+
140
  button {
141
  padding: 10px 20px;
142
  font-size: 16px;
143
  border: none;
144
+ border-radius: 8px;
145
+ background: #3498db;
 
146
  color: white;
147
+ cursor: pointer;
148
+ transition: all 0.3s;
149
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
150
  }
151
+
152
  button:hover {
153
+ background: #2980b9;
154
+ transform: translateY(-2px);
155
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ select {
159
+ padding: 8px 16px;
160
+ font-size: 16px;
161
+ border: 1px solid #ddd;
162
+ border-radius: 8px;
163
+ background: white;
164
+ cursor: pointer;
165
+ transition: all 0.3s;
166
+ }
167
+
168
+ select:hover {
169
+ border-color: #3498db;
170
  }
171
+
172
+ .game-info {
173
+ text-align: center;
174
+ margin: 20px 0;
175
+ padding: 15px;
176
+ background: #f8f9fa;
177
+ border-radius: 10px;
178
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
179
+ }
180
+
181
+ .game-info div {
182
+ margin: 10px 0;
183
  font-size: 18px;
184
+ color: #2c3e50;
 
185
  }
186
+
187
+ #currentPlayer {
188
+ font-weight: bold;
189
+ color: #3498db;
190
+ }
191
+
192
  .mode-select {
193
+ display: flex;
194
+ justify-content: center;
195
+ gap: 15px;
196
  margin-bottom: 20px;
197
  }
198
+
199
+ @media (max-width: 768px) {
200
+ .game-container {
201
+ padding: 15px;
202
+ }
203
+
204
+ .board {
205
+ transform: scale(0.8);
206
+ transform-origin: center;
207
+ }
208
+
209
+ .controls {
210
+ flex-direction: column;
211
+ align-items: center;
212
+ }
213
+
214
+ button {
215
+ width: 100%;
216
+ max-width: 200px;
217
+ }
218
  }
219
  </style>
220
  </head>
221
  <body>
 
 
 
 
 
 
 
 
 
222
  <div class="game-container">
223
+ <div class="mode-select">
224
+ <select id="gameMode">
225
+ <option value="pvp">Player vs Player</option>
226
+ <option value="ai">Player vs AI</option>
227
+ </select>
228
+ <select id="difficulty">
229
+ <option value="easy">Easy</option>
230
+ <option value="hard">Hard</option>
231
+ </select>
232
+ </div>
233
 
234
+ <div id="board" class="board"></div>
 
 
 
235
 
236
+ <div class="game-info">
237
+ <div>Current Player: <span id="currentPlayer">Black</span></div>
238
+ <div>Black Captures: <span id="blackCaptures">0</span></div>
239
+ <div>White Captures: <span id="whiteCaptures">0</span></div>
240
+ </div>
241
+
242
+ <div class="controls">
243
+ <button id="passBtn">Pass</button>
244
+ <button id="resetBtn">Reset</button>
245
+ </div>
246
  </div>
247
 
248
  <script>
 
251
  this.size = 19;
252
  this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
253
  this.currentPlayer = 'black';
254
+ this.captures = { black: 0, white: 0 };
255
  this.lastMove = null;
 
256
  this.gameMode = 'pvp';
257
+ this.difficulty = 'easy';
 
 
 
258
  this.initialize();
259
  }
260
+
261
  initialize() {
262
  const boardElement = document.getElementById('board');
263
  boardElement.innerHTML = '';
264
+
265
  for(let i = 0; i < this.size; i++) {
266
  for(let j = 0; j < this.size; j++) {
267
  const intersection = document.createElement('div');
268
  intersection.className = 'intersection';
269
  intersection.dataset.row = i;
270
  intersection.dataset.col = j;
271
+
272
+ if(this.isStarPoint(i, j)) {
273
+ intersection.classList.add('star-point');
274
+ }
275
+
276
  intersection.addEventListener('click', (e) => this.handleMove(e));
277
  boardElement.appendChild(intersection);
278
  }
279
  }
280
+
281
  document.getElementById('passBtn').addEventListener('click', () => this.pass());
282
  document.getElementById('resetBtn').addEventListener('click', () => this.reset());
283
  document.getElementById('gameMode').addEventListener('change', (e) => {
284
+ this.gameMode = e.target.value;
 
 
285
  this.reset();
286
  });
287
+ document.getElementById('difficulty').addEventListener('change', (e) => {
288
+ this.difficulty = e.target.value;
289
+ });
290
+
291
+ this.updateDisplay();
292
+ }
293
+
294
+ isStarPoint(row, col) {
295
+ const starPoints = [
296
+ [3,3], [3,9], [3,15],
297
+ [9,3], [9,9], [9,15],
298
+ [15,3], [15,9], [15,15]
299
+ ];
300
+ return starPoints.some(point => point[0] === row && point[1] === col);
301
  }
302
+
303
  handleMove(e) {
304
  const row = parseInt(e.target.dataset.row);
305
  const col = parseInt(e.target.dataset.col);
306
+
307
  if(this.isValidMove(row, col)) {
308
  this.placeStone(row, col);
309
 
 
312
  }
313
  }
314
  }
315
+
316
  isValidMove(row, col) {
317
  if(this.board[row][col] !== null) return false;
318
 
319
  this.board[row][col] = this.currentPlayer;
320
+ const captures = this.checkCaptures(row, col);
321
+ const suicide = this.isSuicideMove(row, col);
322
 
323
  this.board[row][col] = null;
324
 
325
+ return !suicide || captures.length > 0;
326
  }
327
+
328
  placeStone(row, col) {
329
+ this.board[row][col] = this.currentPlayer;
330
+ this.renderStone(row, col);
331
+
332
+ const captures = this.checkCaptures(row, col);
333
+ this.removeCaptures(captures);
334
+
335
+ this.lastMove = [row, col];
336
+ this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
337
+ this.updateDisplay();
338
  }
339
+
340
  renderStone(row, col) {
341
  const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
342
  const stone = document.createElement('div');
343
  stone.className = `stone ${this.currentPlayer}`;
344
  intersection.appendChild(stone);
345
  }
346
+ checkCaptures(row, col) {
347
+ const captures = [];
348
+ const opponent = this.currentPlayer === 'black' ? 'white' : 'black';
349
+ const neighbors = this.getNeighbors(row, col);
350
+
351
+ for(const [nRow, nCol] of neighbors) {
352
+ if(this.board[nRow][nCol] === opponent) {
353
+ const group = this.getGroup(nRow, nCol);
354
+ if(this.isGroupCaptured(group)) {
355
+ captures.push(...group);
356
+ }
357
+ }
358
+ }
359
+
360
+ return captures;
361
+ }
362
+
363
+ removeCaptures(captures) {
364
+ for(const [row, col] of captures) {
365
+ this.board[row][col] = null;
366
+ const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
367
+ intersection.innerHTML = '';
368
+ this.captures[this.currentPlayer]++;
369
+ }
370
+ }
371
+
372
  getGroup(row, col) {
373
  const color = this.board[row][col];
374
+ const group = [];
375
+ const visited = new Set();
376
  const stack = [[row, col]];
377
 
378
  while(stack.length > 0) {
379
  const [r, c] = stack.pop();
380
  const key = `${r},${c}`;
381
 
382
+ if(!visited.has(key)) {
383
+ visited.add(key);
384
+ group.push([r, c]);
385
 
386
+ const neighbors = this.getNeighbors(r, c);
387
  for(const [nr, nc] of neighbors) {
388
  if(this.board[nr][nc] === color) {
389
  stack.push([nr, nc]);
 
394
 
395
  return group;
396
  }
397
+
398
+ isGroupCaptured(group) {
399
+ for(const [row, col] of group) {
 
 
400
  const neighbors = this.getNeighbors(row, col);
401
+ for(const [nRow, nCol] of neighbors) {
402
+ if(this.board[nRow][nCol] === null) return false;
 
 
 
403
  }
404
  }
405
+ return true;
 
406
  }
407
+
408
  getNeighbors(row, col) {
409
  const neighbors = [];
410
  const directions = [[-1,0], [1,0], [0,-1], [0,1]];
411
 
412
+ for(const [dRow, dCol] of directions) {
413
+ const newRow = row + dRow;
414
+ const newCol = col + dCol;
415
 
416
+ if(newRow >= 0 && newRow < this.size &&
417
+ newCol >= 0 && newCol < this.size) {
418
  neighbors.push([newRow, newCol]);
419
  }
420
  }
421
 
422
  return neighbors;
423
  }
424
+
425
+ isSuicideMove(row, col) {
426
+ const group = this.getGroup(row, col);
427
+ return this.isGroupCaptured(group);
428
+ }
429
+
430
+ pass() {
431
+ this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
432
+ this.updateDisplay();
433
 
434
+ if(this.gameMode === 'ai' && this.currentPlayer === 'white') {
435
+ setTimeout(() => this.makeAIMove(), 500);
 
 
 
 
 
 
 
 
 
 
436
  }
 
 
 
 
437
  }
438
+
439
+ reset() {
440
+ this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
441
+ this.currentPlayer = 'black';
442
+ this.captures = { black: 0, white: 0 };
443
+ this.lastMove = null;
444
+ this.initialize();
445
+ }
446
+
447
+ updateDisplay() {
448
+ document.getElementById('currentPlayer').textContent =
449
+ this.currentPlayer.charAt(0).toUpperCase() + this.currentPlayer.slice(1);
450
+ document.getElementById('blackCaptures').textContent = this.captures.black;
451
+ document.getElementById('whiteCaptures').textContent = this.captures.white;
452
+ }
453
+
454
  makeAIMove() {
455
+ if(this.difficulty === 'easy') {
456
+ this.makeRandomMove();
457
+ } else {
458
+ this.makeStrategicMove();
 
 
 
 
 
 
 
 
 
 
459
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  }
461
+
462
+ makeRandomMove() {
463
+ const validMoves = [];
464
  for(let i = 0; i < this.size; i++) {
465
  for(let j = 0; j < this.size; j++) {
466
+ if(this.isValidMove(i, j)) {
467
+ validMoves.push([i, j]);
 
 
 
 
 
 
 
 
 
 
468
  }
469
  }
470
  }
471
+
472
+ if(validMoves.length > 0) {
473
+ const [row, col] = validMoves[Math.floor(Math.random() * validMoves.length)];
474
+ this.placeStone(row, col);
475
+ } else {
476
+ this.pass();
 
 
 
 
477
  }
478
  }
479
+
480
+ makeStrategicMove() {
481
+ this.makeRandomMove();
 
 
 
 
 
 
 
 
 
 
 
 
482
  }
483
  }
484
+
485
  const game = new GoGame();
486
  </script>
487
  </body>