Spaces:
Running
Running
Update index.html
Browse files- index.html +264 -1
index.html
CHANGED
@@ -478,9 +478,272 @@
|
|
478 |
}
|
479 |
|
480 |
makeStrategicMove() {
|
481 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
482 |
}
|
483 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
484 |
|
485 |
const game = new GoGame();
|
486 |
</script>
|
|
|
478 |
}
|
479 |
|
480 |
makeStrategicMove() {
|
481 |
+
const depth = this.difficulty === 'hard' ? 3 : 1;
|
482 |
+
let bestMove = this.findBestMove(depth);
|
483 |
+
|
484 |
+
if(bestMove) {
|
485 |
+
this.placeStone(bestMove[0], bestMove[1]);
|
486 |
+
} else {
|
487 |
+
this.pass();
|
488 |
+
}
|
489 |
+
}
|
490 |
+
|
491 |
+
findBestMove(depth) {
|
492 |
+
let bestScore = -Infinity;
|
493 |
+
let bestMove = null;
|
494 |
+
|
495 |
+
// 전체 보드를 평가
|
496 |
+
for(let i = 0; i < this.size; i++) {
|
497 |
+
for(let j = 0; j < this.size; j++) {
|
498 |
+
if(this.isValidMove(i, j)) {
|
499 |
+
// 임시로 돌을 놓아보고 평가
|
500 |
+
this.board[i][j] = 'white';
|
501 |
+
const score = this.minimax(depth - 1, false);
|
502 |
+
this.board[i][j] = null;
|
503 |
+
|
504 |
+
if(score > bestScore) {
|
505 |
+
bestScore = score;
|
506 |
+
bestMove = [i, j];
|
507 |
+
}
|
508 |
+
}
|
509 |
+
}
|
510 |
+
}
|
511 |
+
|
512 |
+
return bestMove;
|
513 |
+
}
|
514 |
+
|
515 |
+
minimax(depth, isMaximizing) {
|
516 |
+
if(depth === 0) {
|
517 |
+
return this.evaluateBoard();
|
518 |
+
}
|
519 |
+
|
520 |
+
if(isMaximizing) {
|
521 |
+
let maxScore = -Infinity;
|
522 |
+
for(let i = 0; i < this.size; i++) {
|
523 |
+
for(let j = 0; j < this.size; j++) {
|
524 |
+
if(this.isValidMove(i, j)) {
|
525 |
+
this.board[i][j] = 'white';
|
526 |
+
const score = this.minimax(depth - 1, false);
|
527 |
+
this.board[i][j] = null;
|
528 |
+
maxScore = Math.max(maxScore, score);
|
529 |
+
}
|
530 |
+
}
|
531 |
+
}
|
532 |
+
return maxScore;
|
533 |
+
} else {
|
534 |
+
let minScore = Infinity;
|
535 |
+
for(let i = 0; i < this.size; i++) {
|
536 |
+
for(let j = 0; j < this.size; j++) {
|
537 |
+
if(this.isValidMove(i, j)) {
|
538 |
+
this.board[i][j] = 'black';
|
539 |
+
const score = this.minimax(depth - 1, true);
|
540 |
+
this.board[i][j] = null;
|
541 |
+
minScore = Math.min(minScore, score);
|
542 |
+
}
|
543 |
+
}
|
544 |
+
}
|
545 |
+
return minScore;
|
546 |
+
}
|
547 |
+
}
|
548 |
+
|
549 |
+
evaluateBoard() {
|
550 |
+
return this.evaluateTerritory() +
|
551 |
+
this.evaluateInfluence() +
|
552 |
+
this.evaluateCaptureThreats() +
|
553 |
+
this.evaluateConnectivity();
|
554 |
+
}
|
555 |
+
|
556 |
+
evaluateTerritory() {
|
557 |
+
let score = 0;
|
558 |
+
const visited = new Set();
|
559 |
+
|
560 |
+
for(let i = 0; i < this.size; i++) {
|
561 |
+
for(let j = 0; j < this.size; j++) {
|
562 |
+
if(!visited.has(`${i},${j}`) && this.board[i][j] === null) {
|
563 |
+
const territory = this.floodFillTerritory(i, j, visited);
|
564 |
+
const owner = this.determineTerritorySide(territory);
|
565 |
+
if(owner === 'white') score += territory.size;
|
566 |
+
else if(owner === 'black') score -= territory.size;
|
567 |
+
}
|
568 |
+
}
|
569 |
+
}
|
570 |
+
|
571 |
+
return score;
|
572 |
+
}
|
573 |
+
|
574 |
+
floodFillTerritory(row, col, visited) {
|
575 |
+
const territory = new Set();
|
576 |
+
const stack = [[row, col]];
|
577 |
+
const borders = new Set();
|
578 |
+
|
579 |
+
while(stack.length > 0) {
|
580 |
+
const [r, c] = stack.pop();
|
581 |
+
const key = `${r},${c}`;
|
582 |
+
|
583 |
+
if(!visited.has(key)) {
|
584 |
+
visited.add(key);
|
585 |
+
if(this.board[r][c] === null) {
|
586 |
+
territory.add(key);
|
587 |
+
|
588 |
+
const neighbors = this.getNeighbors(r, c);
|
589 |
+
for(const [nr, nc] of neighbors) {
|
590 |
+
if(this.board[nr][nc] === null) {
|
591 |
+
stack.push([nr, nc]);
|
592 |
+
} else {
|
593 |
+
borders.add(`${nr},${nc}`);
|
594 |
+
}
|
595 |
+
}
|
596 |
+
}
|
597 |
+
}
|
598 |
+
}
|
599 |
+
|
600 |
+
territory.borders = borders;
|
601 |
+
return territory;
|
602 |
+
}
|
603 |
+
|
604 |
+
determineTerritorySide(territory) {
|
605 |
+
let blackCount = 0;
|
606 |
+
let whiteCount = 0;
|
607 |
+
|
608 |
+
for(const pos of territory.borders) {
|
609 |
+
const [row, col] = pos.split(',').map(Number);
|
610 |
+
if(this.board[row][col] === 'black') blackCount++;
|
611 |
+
else if(this.board[row][col] === 'white') whiteCount++;
|
612 |
+
}
|
613 |
+
|
614 |
+
if(blackCount > whiteCount) return 'black';
|
615 |
+
if(whiteCount > blackCount) return 'white';
|
616 |
+
return null;
|
617 |
+
}
|
618 |
+
|
619 |
+
evaluateInfluence() {
|
620 |
+
let score = 0;
|
621 |
+
|
622 |
+
for(let i = 0; i < this.size; i++) {
|
623 |
+
for(let j = 0; j < this.size; j++) {
|
624 |
+
if(this.board[i][j] === 'white') {
|
625 |
+
score += this.calculateInfluence(i, j, 3);
|
626 |
+
} else if(this.board[i][j] === 'black') {
|
627 |
+
score -= this.calculateInfluence(i, j, 3);
|
628 |
}
|
629 |
}
|
630 |
+
}
|
631 |
+
|
632 |
+
return score;
|
633 |
+
}
|
634 |
+
|
635 |
+
calculateInfluence(row, col, radius) {
|
636 |
+
let influence = 0;
|
637 |
+
|
638 |
+
for(let i = -radius; i <= radius; i++) {
|
639 |
+
for(let j = -radius; j <= radius; j++) {
|
640 |
+
const distance = Math.abs(i) + Math.abs(j);
|
641 |
+
if(distance <= radius) {
|
642 |
+
const newRow = row + i;
|
643 |
+
const newCol = col + j;
|
644 |
+
|
645 |
+
if(newRow >= 0 && newRow < this.size &&
|
646 |
+
newCol >= 0 && newCol < this.size) {
|
647 |
+
influence += (radius - distance + 1) / radius;
|
648 |
+
}
|
649 |
+
}
|
650 |
+
}
|
651 |
+
}
|
652 |
+
|
653 |
+
return influence;
|
654 |
+
}
|
655 |
+
|
656 |
+
evaluateCaptureThreats() {
|
657 |
+
let score = 0;
|
658 |
+
|
659 |
+
for(let i = 0; i < this.size; i++) {
|
660 |
+
for(let j = 0; j < this.size; j++) {
|
661 |
+
if(this.board[i][j]) {
|
662 |
+
const group = this.getGroup(i, j);
|
663 |
+
const liberties = this.countLiberties(group);
|
664 |
+
|
665 |
+
if(this.board[i][j] === 'white') {
|
666 |
+
score += liberties * 2;
|
667 |
+
if(liberties === 1) score -= 10;
|
668 |
+
} else {
|
669 |
+
score -= liberties * 2;
|
670 |
+
if(liberties === 1) score += 10;
|
671 |
+
}
|
672 |
+
}
|
673 |
+
}
|
674 |
+
}
|
675 |
+
|
676 |
+
return score;
|
677 |
+
}
|
678 |
+
|
679 |
+
countLiberties(group) {
|
680 |
+
const liberties = new Set();
|
681 |
+
|
682 |
+
for(const [row, col] of group) {
|
683 |
+
const neighbors = this.getNeighbors(row, col);
|
684 |
+
for(const [nRow, nCol] of neighbors) {
|
685 |
+
if(this.board[nRow][nCol] === null) {
|
686 |
+
liberties.add(`${nRow},${nCol}`);
|
687 |
+
}
|
688 |
+
}
|
689 |
+
}
|
690 |
+
|
691 |
+
return liberties.size;
|
692 |
+
}
|
693 |
+
|
694 |
+
evaluateConnectivity() {
|
695 |
+
let score = 0;
|
696 |
+
|
697 |
+
for(let i = 0; i < this.size; i++) {
|
698 |
+
for(let j = 0; j < this.size; j++) {
|
699 |
+
if(this.board[i][j] === 'white') {
|
700 |
+
score += this.evaluateStoneConnectivity(i, j);
|
701 |
+
} else if(this.board[i][j] === 'black') {
|
702 |
+
score -= this.evaluateStoneConnectivity(i, j);
|
703 |
+
}
|
704 |
+
}
|
705 |
+
}
|
706 |
+
|
707 |
+
return score;
|
708 |
+
}
|
709 |
+
|
710 |
+
evaluateStoneConnectivity(row, col) {
|
711 |
+
let connectivity = 0;
|
712 |
+
const neighbors = this.getNeighbors(row, col);
|
713 |
+
const diagonalNeighbors = this.getDiagonalNeighbors(row, col);
|
714 |
+
|
715 |
+
for(const [nRow, nCol] of neighbors) {
|
716 |
+
if(this.board[nRow][nCol] === this.board[row][col]) {
|
717 |
+
connectivity += 2;
|
718 |
+
}
|
719 |
+
}
|
720 |
+
|
721 |
+
for(const [nRow, nCol] of diagonalNeighbors) {
|
722 |
+
if(this.board[nRow][nCol] === this.board[row][col]) {
|
723 |
+
connectivity += 1;
|
724 |
+
}
|
725 |
+
}
|
726 |
+
|
727 |
+
return connectivity;
|
728 |
+
}
|
729 |
+
|
730 |
+
getDiagonalNeighbors(row, col) {
|
731 |
+
const neighbors = [];
|
732 |
+
const directions = [[-1,-1], [-1,1], [1,-1], [1,1]];
|
733 |
+
|
734 |
+
for(const [dRow, dCol] of directions) {
|
735 |
+
const newRow = row + dRow;
|
736 |
+
const newCol = col + dCol;
|
737 |
+
|
738 |
+
if(newRow >= 0 && newRow < this.size &&
|
739 |
+
newCol >= 0 && newCol < this.size) {
|
740 |
+
neighbors.push([newRow, newCol]);
|
741 |
+
}
|
742 |
+
}
|
743 |
+
|
744 |
+
return neighbors;
|
745 |
+
}
|
746 |
+
}
|
747 |
|
748 |
const game = new GoGame();
|
749 |
</script>
|