카테고리 없음

기보 흑차례에는 금수 지점에 붉은색 원으로 표시

눈길에발자욱 2024. 5. 20. 11:05
<!DOCTYPE html>
<html>
<head>
<title>Omok Game Visualizer with Automatic Move Parsing</title>
<style>
body {
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  margin: 0;
  background-color: #f4f4f4;
  /* overflow-y: hidden;  - 스크롤바를 숨기는 경우 */
}

.board {
  display: grid;
  grid-template-columns: repeat(14, 1fr); 
  grid-template-rows: repeat(14, 1fr); 
  border: 2px solid #333;
  width: 420px;
  height: 420px;
  position: relative;
  background-color: #ffebcd;
  background-image: 
    linear-gradient(to right, #333 1px, transparent 1px),
    linear-gradient(to bottom, #333 1px, transparent 1px);
  background-size: 30px 30px;
  margin-bottom: 5px; /* 버튼과의 간격 조절 */
}

.cell {
  width: 100%;
  height: 100%;
  position: relative;
}

.stone {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  position: absolute;
  transform: translate(-50%, -50%);
}

.black {
  background-color: black;
  width: 32px;
  height: 32px;
}

.white {
  background-color: white;
  border: 2px solid black;
}

.forbidden {
  width: 20px;
  height: 20px;
  background-color: red;
  position: absolute;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

.controls {
  display: flex;
  justify-content: center;
  gap: 10px;
}

.controls button {
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 10px 15px; /* 버튼 크기 조절 */
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 14px; /* 글자 크기 조절 */
  margin: 4px 2px;
  transition-duration: 0.4s;
  cursor: pointer;
  border-radius: 8px;
}

.controls button:hover {
  background-color: #45a049;
}

.controls button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.status {
  margin-top: 10px;
  font-size: 18px;
}
</style>
</head>
<body>
  <div class="board" id="board"></div>
  <div class="controls">
    <button onclick="reset()">Reset</button>
    <button onclick="previousMove()">Previous Move</button>
    <button onclick="nextMove()">Next Move</button>
    <button onclick="lastMove()">Last Move</button>
    <button id="playPauseBtn" onclick="togglePlayPause()">Play</button>
  </div>
  <div class="status" id="status"></div>
  <script>
    const inputMoves = "h8h9h6i8j7g9j8j9i9g6h7g8g5g10";
    const gameRecord = parseMoves(inputMoves);
    const black_stone = 1;
    const white_stone = 2;
    const empty = 0;

    const board = document.getElementById('board');
    const status = document.getElementById('status');
    const playPauseBtn = document.getElementById('playPauseBtn');
    let currentMove = -1;
    let isPlaying = false;
    let intervalId; 
    const delay = 1000; // 1 second delay for auto-play

    // Renju rule logic
    class Renju_Rule {
      constructor(board) {
        this.board = board;
        this.board_size = board.length;
      }

      is_invalid(x, y) {
        return (x < 0 || x >= this.board_size || y < 0 || y >= this.board_size);
      }

      set_stone(x, y, stone) {
        this.board[y][x] = stone;
      }

      get_xy(direction) {
        const list_dx = [-1, 1, -1, 1, 0, 0, 1, -1];
        const list_dy = [0, 0, -1, 1, -1, 1, -1, 1];
        return [list_dx[direction], list_dy[direction]];
      }

      get_stone_count(x, y, stone, direction) {
        let x1 = x, y1 = y, cnt = 1;
        for (let i = 0; i < 2; i++) {
          const [dx, dy] = this.get_xy(direction * 2 + i);
          x = x1, y = y1;
          while (true) {
            x += dx;
            y += dy;
            if (this.is_invalid(x, y) || this.board[y][x] !== stone) break;
            else cnt++;
          }
        }
        return cnt;
      }

      is_gameover(x, y, stone) {
        for (let i = 0; i < 4; i++) {
          const cnt = this.get_stone_count(x, y, stone, i);
          if (cnt >= 5) return true;
        }
        return false;
      }

      is_six(x, y, stone) {
        for (let i = 0; i < 4; i++) {
          const cnt = this.get_stone_count(x, y, stone, i);
          if (cnt > 5) return true;
        }
        return false;
      }

      is_five(x, y, stone) {
        for (let i = 0; i < 4; i++) {
          const cnt = this.get_stone_count(x, y, stone, i);
          if (cnt === 5) return true;
        }
        return false;
      }

      is_five_or_more(x, y, stone) {
        for (let i = 0; i < 4; i++) {
          const cnt = this.get_stone_count(x, y, stone, i);
          if (cnt >= 5) return true;
        }
        return false;
      }

      find_empty_point(x, y, stone, direction) {
        const [dx, dy] = this.get_xy(direction);
        while (true) {
          x += dx;
          y += dy;
          if (this.is_invalid(x, y) || this.board[y][x] !== stone) break;
        }
        if (!this.is_invalid(x, y) && this.board[y][x] === empty) {
          return [x, y];
        } else {
          return null;
        }
      }

      open_three(x, y, stone, direction) {
        for (let i = 0; i < 2; i++) {
          const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
          if (coord) {
            const [dx, dy] = coord;
            this.set_stone(dx, dy, stone);
            if (1 === this.open_four(dx, dy, stone, direction)) {
              if (!this.forbidden_point(dx, dy, stone)) {
                this.set_stone(dx, dy, empty);
                return true;
              }
            }
            this.set_stone(dx, dy, empty);
          }
        }
        return false;
      }

      open_four(x, y, stone, direction) {
        if (this.is_five(x, y, stone)) {
          return false;
        }
        let cnt = 0;
        for (let i = 0; i < 2; i++) {
          const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
          if (coord) {
            if (this.five(coord[0], coord[1], stone, direction)) {
              cnt++;
            }
          }
        }
        if (cnt === 2) {
          if (4 === this.get_stone_count(x, y, stone, direction)) {
            cnt = 1;
          }
        } else cnt = 0;
        return cnt;
      }

      four(x, y, stone, direction) {
        for (let i = 0; i < 2; i++) {
          const coord = this.find_empty_point(x, y, stone, direction * 2 + i);
          if (coord) {
            if (this.five(coord[0], coord[1], stone, direction)) {
              return true;
            }
          }
        }
        return false;
      }

      five(x, y, stone, direction) {
        return 5 === this.get_stone_count(x, y, stone, direction);
      }

      double_three(x, y, stone) {
        let cnt = 0;
        this.set_stone(x, y, stone);
        for (let i = 0; i < 4; i++) {
          if (this.open_three(x, y, stone, i)) cnt++;
        }
        this.set_stone(x, y, empty);
        return cnt >= 2;
      }

      double_four(x, y, stone) {
        let cnt = 0;
        this.set_stone(x, y, stone);
        for (let i = 0; i < 4; i++) {
          if (this.open_four(x, y, stone, i) === 2) cnt += 2;
          else if (this.four(x, y, stone, i)) cnt++;
        }
        this.set_stone(x, y, empty);
        return cnt >= 2;
      }

      forbidden_point(x, y, stone) {
        if (this.is_five(x, y, stone)) return false;
        if (this.is_six(x, y, stone)) return true;
        if (this.double_three(x, y, stone) || this.double_four(x, y, stone)) return true;
        return false;
      }
    }

    const gameBoard = Array.from({ length: 14 }, () => Array(14).fill(empty));
    const renjuRule = new Renju_Rule(gameBoard);

    function parseMoves(moveString) {
      const moveArray = [];
      const regex = /[a-zA-Z][0-9]{1,2}/g;
      let matches;
      while ((matches = regex.exec(moveString)) !== null) {
        moveArray.push([
          14 - parseInt(matches[0].substring(1)), // Row index (1=14, 2=13, ...)
          matches[0].charCodeAt(0) - 'a'.charCodeAt(0) - 1 // Column index (A=0, B=1, ...)
        ]);
      }
      return moveArray;
    }

    function createBoard() {
      board.innerHTML = '';
      for (let i = 0; i < 14; i++) {
        for (let j = 0; j < 14; j++) {
          const cell = document.createElement('div');
          cell.classList.add('cell');
          cell.id = `cell-${i}-${j}`;
          board.appendChild(cell);
        }
      }
    }

    function placeStone(row, col, color) {
      const stone = document.createElement('div');
      stone.classList.add('stone', color);
      stone.style.top = `${(row + 1) * 30}px`; 
      stone.style.left = `${(col + 1) * 30}px`;
      board.appendChild(stone);
    }

    function placeForbidden(row, col) {
      const forbidden = document.createElement('div');
      forbidden.classList.add('forbidden');
      forbidden.style.top = `${(row + 1) * 30}px`; 
      forbidden.style.left = `${(col + 1) * 30}px`;
      board.appendChild(forbidden);
    }

    function updateBoard() {
      createBoard();
      for (let i = 0; i <= currentMove; i++) {
        const move = gameRecord[i];
        const color = i % 2 === 0 ? 'black' : 'white';
        placeStone(move[0], move[1], color);
        gameBoard[move[0]][move[1]] = color === 'black' ? black_stone : white_stone;
      }

      if (currentMove >= 0 && currentMove % 2 === 1) { // Check only for white's turn
        for (let i = 0; i < 14; i++) {
          for (let j = 0; j < 14; j++) {
            if (gameBoard[i][j] === empty && renjuRule.forbidden_point(j, i, black_stone)) {
              placeForbidden(i, j);
            }
          }
        }
      }

      status.textContent = `Move ${currentMove + 1} of ${gameRecord.length}`;
    }

    function reset() {
      currentMove = -1;
      gameBoard.forEach(row => row.fill(empty)); // 게임 보드를 초기 상태로 재설정
      updateBoard();
      if (isPlaying) {
        togglePlayPause();
      }
    }

    function previousMove() {
      currentMove = Math.max(currentMove - 1, -1);
      updateBoard();
    }

    function nextMove() {
      currentMove = Math.min(currentMove + 1, gameRecord.length - 1);
      updateBoard();
    }

    function lastMove() {
      currentMove = gameRecord.length - 1;
      updateBoard();
    }

    function togglePlayPause() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        intervalId = setInterval(nextMove, delay); 
        playPauseBtn.textContent = 'Pause';
      } else {
        clearInterval(intervalId);
        playPauseBtn.textContent = 'Play';
      }
    }

    createBoard();
    updateBoard();
  </script>
</body>
</html>

혹시 사용하는 분이 있을까 소스

 

h8h9h6i8j7g9j8j9i9g6h7g8g5g10 이 부분을 나에 맞게 기보를 넣으면 됩니다.

렌주넷같은 곳에서 하단부분의 역삼각형을 클릭하면 아래 수순이 나오는 기보를 얻을 수 있는데 위의 코드에 저걸 넣고 html파일로 저장하면 웹브라우저로 볼 수 있습니다.

1.htm
0.01MB

 

 

수순을 진행하면 흑차례 금수가 되는 지점은 붉은색 원 확인이 됩니다.

반응형