Update index.html
Browse files- index.html +198 -51
index.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Go Game</title>
|
7 |
<style>
|
8 |
* {
|
9 |
margin: 0;
|
@@ -255,6 +255,7 @@
|
|
255 |
this.lastMove = null;
|
256 |
this.gameMode = 'pvp';
|
257 |
this.difficulty = 'easy';
|
|
|
258 |
this.initialize();
|
259 |
}
|
260 |
|
@@ -262,14 +263,14 @@
|
|
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 |
|
@@ -293,45 +294,50 @@
|
|
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 |
-
|
310 |
-
if(this.gameMode === 'ai' && this.currentPlayer === 'white') {
|
311 |
-
|
|
|
|
|
|
|
|
|
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();
|
@@ -343,25 +349,26 @@
|
|
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 = '';
|
@@ -374,32 +381,32 @@
|
|
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]);
|
390 |
}
|
391 |
}
|
392 |
}
|
393 |
}
|
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;
|
@@ -407,18 +414,18 @@
|
|
407 |
|
408 |
getNeighbors(row, col) {
|
409 |
const neighbors = [];
|
410 |
-
const directions = [[-1,0], [1,0], [0
|
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 |
-
|
418 |
neighbors.push([newRow, newCol]);
|
419 |
}
|
420 |
}
|
421 |
-
|
422 |
return neighbors;
|
423 |
}
|
424 |
|
@@ -428,11 +435,16 @@
|
|
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 |
-
|
|
|
|
|
|
|
|
|
436 |
}
|
437 |
}
|
438 |
|
@@ -441,18 +453,19 @@
|
|
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();
|
@@ -461,15 +474,15 @@
|
|
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 {
|
@@ -478,11 +491,145 @@
|
|
478 |
}
|
479 |
|
480 |
makeStrategicMove() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
this.makeRandomMove();
|
482 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
}
|
484 |
|
485 |
const game = new GoGame();
|
486 |
</script>
|
487 |
</body>
|
488 |
-
</html>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Go Game with Strategic AI</title>
|
7 |
<style>
|
8 |
* {
|
9 |
margin: 0;
|
|
|
255 |
this.lastMove = null;
|
256 |
this.gameMode = 'pvp';
|
257 |
this.difficulty = 'easy';
|
258 |
+
this.isProcessing = false; // NEW: μμ
μ€ μ¬λΆ νλκ·Έ
|
259 |
this.initialize();
|
260 |
}
|
261 |
|
|
|
263 |
const boardElement = document.getElementById('board');
|
264 |
boardElement.innerHTML = '';
|
265 |
|
266 |
+
for (let i = 0; i < this.size; i++) {
|
267 |
+
for (let j = 0; j < this.size; j++) {
|
268 |
const intersection = document.createElement('div');
|
269 |
intersection.className = 'intersection';
|
270 |
intersection.dataset.row = i;
|
271 |
intersection.dataset.col = j;
|
272 |
|
273 |
+
if (this.isStarPoint(i, j)) {
|
274 |
intersection.classList.add('star-point');
|
275 |
}
|
276 |
|
|
|
294 |
|
295 |
isStarPoint(row, col) {
|
296 |
const starPoints = [
|
297 |
+
[3, 3], [3, 9], [3, 15],
|
298 |
+
[9, 3], [9, 9], [9, 15],
|
299 |
+
[15, 3], [15, 9], [15, 15]
|
300 |
];
|
301 |
return starPoints.some(point => point[0] === row && point[1] === col);
|
302 |
}
|
303 |
|
304 |
handleMove(e) {
|
305 |
+
if (this.isProcessing) return; // NEW: ν΄λ¦ λ°©μ§
|
306 |
const row = parseInt(e.target.dataset.row);
|
307 |
const col = parseInt(e.target.dataset.col);
|
308 |
|
309 |
+
if (this.isValidMove(row, col)) {
|
310 |
this.placeStone(row, col);
|
311 |
+
|
312 |
+
if (this.gameMode === 'ai' && this.currentPlayer === 'white') {
|
313 |
+
this.isProcessing = true; // AI μμ
μμ
|
314 |
+
setTimeout(() => {
|
315 |
+
this.makeAIMove();
|
316 |
+
this.isProcessing = false; // AI μμ
μλ£
|
317 |
+
}, 500);
|
318 |
}
|
319 |
}
|
320 |
}
|
321 |
|
322 |
isValidMove(row, col) {
|
323 |
+
if (this.board[row][col] !== null) return false;
|
324 |
+
|
325 |
this.board[row][col] = this.currentPlayer;
|
326 |
const captures = this.checkCaptures(row, col);
|
327 |
const suicide = this.isSuicideMove(row, col);
|
328 |
+
|
329 |
this.board[row][col] = null;
|
330 |
+
|
331 |
return !suicide || captures.length > 0;
|
332 |
}
|
333 |
|
334 |
placeStone(row, col) {
|
335 |
this.board[row][col] = this.currentPlayer;
|
336 |
this.renderStone(row, col);
|
337 |
+
|
338 |
const captures = this.checkCaptures(row, col);
|
339 |
this.removeCaptures(captures);
|
340 |
+
|
341 |
this.lastMove = [row, col];
|
342 |
this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
|
343 |
this.updateDisplay();
|
|
|
349 |
stone.className = `stone ${this.currentPlayer}`;
|
350 |
intersection.appendChild(stone);
|
351 |
}
|
352 |
+
|
353 |
checkCaptures(row, col) {
|
354 |
const captures = [];
|
355 |
const opponent = this.currentPlayer === 'black' ? 'white' : 'black';
|
356 |
const neighbors = this.getNeighbors(row, col);
|
357 |
+
|
358 |
+
for (const [nRow, nCol] of neighbors) {
|
359 |
+
if (this.board[nRow][nCol] === opponent) {
|
360 |
const group = this.getGroup(nRow, nCol);
|
361 |
+
if (this.isGroupCaptured(group)) {
|
362 |
captures.push(...group);
|
363 |
}
|
364 |
}
|
365 |
}
|
366 |
+
|
367 |
return captures;
|
368 |
}
|
369 |
|
370 |
removeCaptures(captures) {
|
371 |
+
for (const [row, col] of captures) {
|
372 |
this.board[row][col] = null;
|
373 |
const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
|
374 |
intersection.innerHTML = '';
|
|
|
381 |
const group = [];
|
382 |
const visited = new Set();
|
383 |
const stack = [[row, col]];
|
384 |
+
|
385 |
+
while (stack.length > 0) {
|
386 |
const [r, c] = stack.pop();
|
387 |
const key = `${r},${c}`;
|
388 |
+
|
389 |
+
if (!visited.has(key)) {
|
390 |
visited.add(key);
|
391 |
group.push([r, c]);
|
392 |
+
|
393 |
const neighbors = this.getNeighbors(r, c);
|
394 |
+
for (const [nr, nc] of neighbors) {
|
395 |
+
if (this.board[nr][nc] === color) {
|
396 |
stack.push([nr, nc]);
|
397 |
}
|
398 |
}
|
399 |
}
|
400 |
}
|
401 |
+
|
402 |
return group;
|
403 |
}
|
404 |
|
405 |
isGroupCaptured(group) {
|
406 |
+
for (const [row, col] of group) {
|
407 |
const neighbors = this.getNeighbors(row, col);
|
408 |
+
for (const [nRow, nCol] of neighbors) {
|
409 |
+
if (this.board[nRow][nCol] === null) return false;
|
410 |
}
|
411 |
}
|
412 |
return true;
|
|
|
414 |
|
415 |
getNeighbors(row, col) {
|
416 |
const neighbors = [];
|
417 |
+
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
|
418 |
+
|
419 |
+
for (const [dRow, dCol] of directions) {
|
420 |
const newRow = row + dRow;
|
421 |
const newCol = col + dCol;
|
422 |
+
|
423 |
+
if (newRow >= 0 && newRow < this.size &&
|
424 |
+
newCol >= 0 && newCol < this.size) {
|
425 |
neighbors.push([newRow, newCol]);
|
426 |
}
|
427 |
}
|
428 |
+
|
429 |
return neighbors;
|
430 |
}
|
431 |
|
|
|
435 |
}
|
436 |
|
437 |
pass() {
|
438 |
+
if (this.isProcessing) return; // NEW: ν΄λ¦ λ°©μ§
|
439 |
this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
|
440 |
this.updateDisplay();
|
441 |
+
|
442 |
+
if (this.gameMode === 'ai' && this.currentPlayer === 'white') {
|
443 |
+
this.isProcessing = true; // AI μμ
μμ
|
444 |
+
setTimeout(() => {
|
445 |
+
this.makeAIMove();
|
446 |
+
this.isProcessing = false; // AI μμ
μλ£
|
447 |
+
}, 500);
|
448 |
}
|
449 |
}
|
450 |
|
|
|
453 |
this.currentPlayer = 'black';
|
454 |
this.captures = { black: 0, white: 0 };
|
455 |
this.lastMove = null;
|
456 |
+
this.isProcessing = false; // μ΄κΈ°ν μ μμ
μν ν΄μ
|
457 |
this.initialize();
|
458 |
}
|
459 |
|
460 |
updateDisplay() {
|
461 |
+
document.getElementById('currentPlayer').textContent =
|
462 |
this.currentPlayer.charAt(0).toUpperCase() + this.currentPlayer.slice(1);
|
463 |
document.getElementById('blackCaptures').textContent = this.captures.black;
|
464 |
document.getElementById('whiteCaptures').textContent = this.captures.white;
|
465 |
}
|
466 |
|
467 |
makeAIMove() {
|
468 |
+
if (this.difficulty === 'easy') {
|
469 |
this.makeRandomMove();
|
470 |
} else {
|
471 |
this.makeStrategicMove();
|
|
|
474 |
|
475 |
makeRandomMove() {
|
476 |
const validMoves = [];
|
477 |
+
for (let i = 0; i < this.size; i++) {
|
478 |
+
for (let j = 0; j < this.size; j++) {
|
479 |
+
if (this.isValidMove(i, j)) {
|
480 |
validMoves.push([i, j]);
|
481 |
}
|
482 |
}
|
483 |
}
|
484 |
|
485 |
+
if (validMoves.length > 0) {
|
486 |
const [row, col] = validMoves[Math.floor(Math.random() * validMoves.length)];
|
487 |
this.placeStone(row, col);
|
488 |
} else {
|
|
|
491 |
}
|
492 |
|
493 |
makeStrategicMove() {
|
494 |
+
const opponent = this.currentPlayer === 'black' ? 'white' : 'black';
|
495 |
+
const captureMoves = this.findCaptureMoves(opponent);
|
496 |
+
if (captureMoves.length > 0) {
|
497 |
+
const [row, col] = captureMoves[0];
|
498 |
+
this.placeStone(row, col);
|
499 |
+
return;
|
500 |
+
}
|
501 |
+
|
502 |
+
const territoryMove = this.findTerritoryMove();
|
503 |
+
if (territoryMove) {
|
504 |
+
const [row, col] = territoryMove;
|
505 |
+
this.placeStone(row, col);
|
506 |
+
return;
|
507 |
+
}
|
508 |
+
|
509 |
+
const connectionMove = this.findConnectionMove();
|
510 |
+
if (connectionMove) {
|
511 |
+
const [row, col] = connectionMove;
|
512 |
+
this.placeStone(row, col);
|
513 |
+
return;
|
514 |
+
}
|
515 |
+
|
516 |
+
const separationMove = this.findSeparationMove(opponent);
|
517 |
+
if (separationMove) {
|
518 |
+
const [row, col] = separationMove;
|
519 |
+
this.placeStone(row, col);
|
520 |
+
return;
|
521 |
+
}
|
522 |
+
|
523 |
+
const eyeMove = this.findEyeMakingMove();
|
524 |
+
if (eyeMove) {
|
525 |
+
const [row, col] = eyeMove;
|
526 |
+
this.placeStone(row, col);
|
527 |
+
return;
|
528 |
+
}
|
529 |
+
|
530 |
this.makeRandomMove();
|
531 |
}
|
532 |
+
|
533 |
+
findCaptureMoves(opponent) {
|
534 |
+
const captureMoves = [];
|
535 |
+
|
536 |
+
for (let row = 0; row < this.size; row++) {
|
537 |
+
for (let col = 0; col < this.size; col++) {
|
538 |
+
if (this.board[row][col] === opponent) {
|
539 |
+
const group = this.getGroup(row, col);
|
540 |
+
if (this.isGroupCaptured(group)) {
|
541 |
+
const liberties = this.getLiberties(group);
|
542 |
+
captureMoves.push(...liberties);
|
543 |
+
}
|
544 |
+
}
|
545 |
+
}
|
546 |
+
}
|
547 |
+
|
548 |
+
return captureMoves.filter(([r, c]) => this.isValidMove(r, c));
|
549 |
+
}
|
550 |
+
|
551 |
+
findTerritoryMove() {
|
552 |
+
const importantPoints = [
|
553 |
+
[9, 9],
|
554 |
+
[3, 3], [3, 15], [15, 3], [15, 15],
|
555 |
+
[9, 3], [9, 15], [3, 9], [15, 9]
|
556 |
+
];
|
557 |
+
|
558 |
+
for (const [row, col] of importantPoints) {
|
559 |
+
if (this.isValidMove(row, col)) {
|
560 |
+
return [row, col];
|
561 |
+
}
|
562 |
+
}
|
563 |
+
|
564 |
+
return null;
|
565 |
+
}
|
566 |
+
|
567 |
+
findConnectionMove() {
|
568 |
+
for (let row = 0; row < this.size; row++) {
|
569 |
+
for (let col = 0; col < this.size; col++) {
|
570 |
+
if (this.isValidMove(row, col)) {
|
571 |
+
const neighbors = this.getNeighbors(row, col);
|
572 |
+
for (const [nRow, nCol] of neighbors) {
|
573 |
+
if (this.board[nRow][nCol] === this.currentPlayer) {
|
574 |
+
return [row, col];
|
575 |
+
}
|
576 |
+
}
|
577 |
+
}
|
578 |
+
}
|
579 |
+
}
|
580 |
+
|
581 |
+
return null;
|
582 |
+
}
|
583 |
+
|
584 |
+
findSeparationMove(opponent) {
|
585 |
+
for (let row = 0; row < this.size; row++) {
|
586 |
+
for (let col = 0; col < this.size; col++) {
|
587 |
+
if (this.isValidMove(row, col)) {
|
588 |
+
const neighbors = this.getNeighbors(row, col);
|
589 |
+
const opponentNeighbors = neighbors.filter(([nRow, nCol]) => this.board[nRow][nCol] === opponent);
|
590 |
+
if (opponentNeighbors.length >= 2) {
|
591 |
+
return [row, col];
|
592 |
+
}
|
593 |
+
}
|
594 |
+
}
|
595 |
+
}
|
596 |
+
|
597 |
+
return null;
|
598 |
+
}
|
599 |
+
|
600 |
+
findEyeMakingMove() {
|
601 |
+
for (let row = 0; row < this.size; row++) {
|
602 |
+
for (let col = 0; col < this.size; col++) {
|
603 |
+
if (this.isValidMove(row, col)) {
|
604 |
+
const group = this.getGroup(row, col);
|
605 |
+
const liberties = this.getLiberties(group);
|
606 |
+
if (liberties.length >= 2) {
|
607 |
+
return [row, col];
|
608 |
+
}
|
609 |
+
}
|
610 |
+
}
|
611 |
+
}
|
612 |
+
|
613 |
+
return null;
|
614 |
+
}
|
615 |
+
|
616 |
+
getLiberties(group) {
|
617 |
+
const liberties = new Set();
|
618 |
+
|
619 |
+
for (const [row, col] of group) {
|
620 |
+
const neighbors = this.getNeighbors(row, col);
|
621 |
+
for (const [nRow, nCol] of neighbors) {
|
622 |
+
if (this.board[nRow][nCol] === null) {
|
623 |
+
liberties.add(`${nRow},${nCol}`);
|
624 |
+
}
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
return Array.from(liberties).map((key) => key.split(',').map(Number));
|
629 |
+
}
|
630 |
}
|
631 |
|
632 |
const game = new GoGame();
|
633 |
</script>
|
634 |
</body>
|
635 |
+
</html>
|