index.html

17.22 KB
27/05/2025 10:22
HTML
<!DOCTYPE html>
<html lang="th">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>เกมหมากล้อม - Go Game</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      margin: 0;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
    }

    .game-container {
      max-width: 1200px;
      margin: 0 auto;
      background: white;
      border-radius: 15px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
      padding: 30px;
    }

    .game-header {
      text-align: center;
      margin-bottom: 30px;
    }

    .game-header h1 {
      color: #333;
      margin: 0 0 10px 0;
      font-size: 2.5em;
    }

    .game-info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      padding: 15px;
      background: #f8f9fa;
      border-radius: 10px;
    }

    .player-info {
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .stone-indicator {
      width: 30px;
      height: 30px;
      border-radius: 50%;
      border: 2px solid #333;
    }

    .black-stone {
      background: #333;
    }

    .white-stone {
      background: #fff;
    }

    .current-turn {
      box-shadow: 0 0 10px rgba(0, 123, 255, 0.5);
    }

    .board-container {
      display: flex;
      justify-content: center;
      margin-bottom: 20px;
    }

    .go-board {
      background: #deb887;
      border: 3px solid #8b4513;
      border-radius: 10px;
      padding: 20px;
      box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.1);
    }

    .board-grid {
      display: inline-block;
      position: relative;
    }

    .grid-line {
      position: absolute;
      background: #333;
    }

    .grid-line.horizontal {
      height: 1px;
      width: 100%;
      left: 0;
    }

    .grid-line.vertical {
      width: 1px;
      height: 100%;
      top: 0;
    }

    .intersection {
      position: absolute;
      width: 40px;
      height: 40px;
      border-radius: 50%;
      cursor: pointer;
      transform: translate(-50%, -50%);
      transition: all 0.2s ease;
    }

    .intersection:hover {
      background: rgba(0, 123, 255, 0.3);
    }

    .stone {
      width: 36px;
      height: 36px;
      border-radius: 50%;
      border: 2px solid #333;
      box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }

    .stone.black {
      background: linear-gradient(135deg, #333 0%, #000 100%);
    }

    .stone.white {
      background: linear-gradient(135deg, #fff 0%, #f0f0f0 100%);
    }

    .controls {
      text-align: center;
      margin-top: 20px;
    }

    .btn {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 25px;
      cursor: pointer;
      font-size: 16px;
      margin: 0 10px;
      transition: all 0.3s ease;
    }

    .btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    }

    .btn:disabled {
      opacity: 0.6;
      cursor: not-allowed;
      transform: none;
    }

    .difficulty-selector {
      margin: 20px 0;
      text-align: center;
    }

    .difficulty-selector select {
      padding: 8px 16px;
      border: 2px solid #ddd;
      border-radius: 5px;
      font-size: 16px;
    }

    .status-message {
      text-align: center;
      margin-top: 20px;
      padding: 10px;
      border-radius: 5px;
      font-weight: bold;
    }

    .status-message.info {
      background: #d1ecf1;
      color: #0c5460;
      border: 1px solid #b8daff;
    }

    .status-message.success {
      background: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }

    .status-message.error {
      background: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }

    .loading {
      display: inline-block;
      width: 20px;
      height: 20px;
      border: 3px solid #f3f3f3;
      border-top: 3px solid #3498db;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(360deg);
      }
    }
  </style>
</head>

<body>
  <div class="game-container">
    <div class="game-header">
      <h1>🔵⚪ เกมหมากล้อม</h1>
      <p>เล่นกับคอมพิวเตอร์ด้วย Monte Carlo Tree Search AI</p>
    </div>

    <div class="game-info">
      <div class="player-info">
        <div class="stone-indicator black-stone" id="black-indicator"></div>
        <div>
          <div>ผู้เล่น (ดำ)</div>
          <div>จับได้: <span id="black-captures">0</span> ตัว</div>
        </div>
      </div>

      <div class="current-player">
        ตาของ: <span id="current-player-text">ผู้เล่น (ดำ)</span>
      </div>

      <div class="player-info">
        <div class="stone-indicator white-stone" id="white-indicator"></div>
        <div>
          <div>คอมพิวเตอร์ (ขาว)</div>
          <div>จับได้: <span id="white-captures">0</span> ตัว</div>
        </div>
      </div>
    </div>

    <div class="difficulty-selector">
      <label for="difficulty">ระดับความยาก: </label>
      <select id="difficulty">
        <option value="500">ง่าย (500 simulations)</option>
        <option value="1000" selected>ปานกลาง (1000 simulations)</option>
        <option value="2000">ยาก (2000 simulations)</option>
        <option value="5000">ยากมาก (5000 simulations)</option>
      </select>
    </div>

    <div class="board-container">
      <div class="go-board">
        <div class="board-grid" id="board-grid">
          <!-- Grid lines จะถูกสร้างด้วย JavaScript -->
        </div>
      </div>
    </div>

    <div class="controls">
      <button class="btn" onclick="newGame()">เกมใหม่</button>
      <button class="btn" onclick="undoMove()" id="undo-btn">ย้อนกลับ</button>
      <button class="btn" onclick="pass()" id="pass-btn">ผ่าน</button>
      <button class="btn" onclick="surrender()" id="surrender-btn">ยอมแพ้</button>
    </div>

    <div id="status-message" class="status-message info">
      คลิกที่จุดบนกระดานเพื่อวางหิน
    </div>
  </div>

  <script>
    class GoGameClient {
      constructor() {
        this.boardSize = 13; // ใช้ขนาด 13x13 เพื่อความเร็ว (19x19 สำหรับมืออาชีพ)
        this.gameState = null;
        this.isPlayerTurn = true;
        this.isAIThinking = false;
        this.gameHistory = [];
        this.cellSize = 40;
        this.apiUrl = 'api/game.php';

        this.initializeBoard();
        this.newGame();
      }

      initializeBoard() {
        const boardGrid = document.getElementById('board-grid');
        const boardWidth = (this.boardSize - 1) * this.cellSize;
        const boardHeight = (this.boardSize - 1) * this.cellSize;

        boardGrid.style.width = boardWidth + 'px';
        boardGrid.style.height = boardHeight + 'px';

        // สร้าง grid lines
        this.createGridLines(boardGrid);

        // สร้าง intersections
        this.createIntersections(boardGrid);
      }

      createGridLines(container) {
        // Horizontal lines
        for (let i = 0; i < this.boardSize; i++) {
          const line = document.createElement('div');
          line.className = 'grid-line horizontal';
          line.style.top = (i * this.cellSize) + 'px';
          container.appendChild(line);
        }

        // Vertical lines
        for (let i = 0; i < this.boardSize; i++) {
          const line = document.createElement('div');
          line.className = 'grid-line vertical';
          line.style.left = (i * this.cellSize) + 'px';
          container.appendChild(line);
        }
      }

      createIntersections(container) {
        for (let x = 0; x < this.boardSize; x++) {
          for (let y = 0; y < this.boardSize; y++) {
            const intersection = document.createElement('div');
            intersection.className = 'intersection';
            intersection.style.left = (x * this.cellSize) + 'px';
            intersection.style.top = (y * this.cellSize) + 'px';
            intersection.setAttribute('data-x', x);
            intersection.setAttribute('data-y', y);

            intersection.addEventListener('click', (e) => {
              this.handleIntersectionClick(x, y);
            });

            container.appendChild(intersection);
          }
        }
      }

      async newGame() {
        try {
          const response = await fetch(this.apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              action: 'new_game',
              size: this.boardSize
            })
          });

          this.gameState = await response.json();
          this.gameHistory = [JSON.parse(JSON.stringify(this.gameState))];
          this.isPlayerTurn = true;
          this.isAIThinking = false;

          this.updateDisplay();
          this.showMessage('เกมใหม่เริ่มต้นแล้ว! คุณเป็นฝ่ายดำ', 'info');

        } catch (error) {
          console.error('Error starting new game:', error);
          this.showMessage('เกิดข้อผิดพลาดในการเริ่มเกมใหม่', 'error');
        }
      }

      async handleIntersectionClick(x, y) {
        if (!this.isPlayerTurn || this.isAIThinking ||
          this.gameState.board[x][y] !== 0) {
          return;
        }

        try {
          const response = await fetch(this.apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              action: 'make_move',
              gameState: this.gameState,
              x: x,
              y: y,
              player: 1, // ผู้เล่นเป็นฝ่ายดำ (1)
              size: this.boardSize
            })
          });

          const result = await response.json();

          if (result.success) {
            this.gameState = result.gameState;
            this.gameHistory.push(JSON.parse(JSON.stringify(this.gameState)));
            this.isPlayerTurn = false;

            this.updateDisplay();
            this.showMessage('รอ AI คิด...', 'info');

            // ให้ AI เล่น
            setTimeout(() => this.makeAIMove(), 500);

          } else {
            this.showMessage(result.message || 'การเดินไม่ถูกต้อง', 'error');
          }

        } catch (error) {
          console.error('Error making move:', error);
          this.showMessage('เกิดข้อผิดพลาดในการเดิน', 'error');
        }
      }

      async makeAIMove() {
        if (this.isAIThinking) return;

        this.isAIThinking = true;
        this.showMessage('<div class="loading"></div> AI กำลังคิด...', 'info');

        try {
          const difficulty = document.getElementById('difficulty').value;

          const response = await fetch(this.apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              action: 'ai_move',
              gameState: this.gameState,
              difficulty: parseInt(difficulty),
              size: this.boardSize
            })
          });

          const result = await response.json();

          if (result.success) {
            this.gameState = result.gameState;
            this.gameHistory.push(JSON.parse(JSON.stringify(this.gameState)));
            this.isPlayerTurn = true;
            this.isAIThinking = false;

            this.updateDisplay();
            this.showMessage(`AI เดินที่ (${result.move.x}, ${result.move.y})`, 'success');

          } else {
            this.isAIThinking = false;
            this.showMessage(result.message || 'AI ไม่สามารถเดินได้', 'error');
          }

        } catch (error) {
          console.error('Error making AI move:', error);
          this.isAIThinking = false;
          this.showMessage('เกิดข้อผิดพลาดในการคิดของ AI', 'error');
        }
      }

      updateDisplay() {
        this.updateBoard();
        this.updateGameInfo();
        this.updateControls();
      }

      updateBoard() {
        // ลบหินเก่าทั้งหมด
        const stones = document.querySelectorAll('.stone');
        stones.forEach(stone => stone.remove());

        // วางหินใหม่ตามสถานะปัจจุบัน
        for (let x = 0; x < this.boardSize; x++) {
          for (let y = 0; y < this.boardSize; y++) {
            const cellValue = this.gameState.board[x][y];
            if (cellValue !== 0) {
              this.placeStone(x, y, cellValue);
            }
          }
        }
      }

      placeStone(x, y, player) {
        const intersection = document.querySelector(
          `[data-x="${x}"][data-y="${y}"]`
        );

        if (intersection) {
          const stone = document.createElement('div');
          stone.className = `stone ${player === 1 ? 'black' : 'white'}`;
          intersection.appendChild(stone);
        }
      }

      updateGameInfo() {
        document.getElementById('black-captures').textContent =
          this.gameState.blackCaptures;
        document.getElementById('white-captures').textContent =
          this.gameState.whiteCaptures;

        const currentPlayerText = document.getElementById('current-player-text');
        const blackIndicator = document.getElementById('black-indicator');
        const whiteIndicator = document.getElementById('white-indicator');

        // ลบ class current-turn ทั้งหมด
        blackIndicator.classList.remove('current-turn');
        whiteIndicator.classList.remove('current-turn');

        if (this.isPlayerTurn && !this.isAIThinking) {
          currentPlayerText.textContent = 'ผู้เล่น (ดำ)';
          blackIndicator.classList.add('current-turn');
        } else {
          currentPlayerText.textContent = 'คอมพิวเตอร์ (ขาว)';
          whiteIndicator.classList.add('current-turn');
        }
      }

      updateControls() {
        const undoBtn = document.getElementById('undo-btn');
        const passBtn = document.getElementById('pass-btn');
        const surrenderBtn = document.getElementById('surrender-btn');

        const canInteract = this.isPlayerTurn && !this.isAIThinking;

        undoBtn.disabled = !canInteract || this.gameHistory.length <= 1;
        passBtn.disabled = !canInteract;
        surrenderBtn.disabled = !canInteract;
      }

      showMessage(message, type = 'info') {
        const statusElement = document.getElementById('status-message');
        statusElement.innerHTML = message;
        statusElement.className = `status-message ${type}`;
      }

      undoMove() {
        if (this.gameHistory.length > 1 && this.isPlayerTurn && !this.isAIThinking) {
          // ย้อนกลับ 2 ตา (ผู้เล่นและ AI)
          if (this.gameHistory.length >= 3) {
            this.gameHistory.pop(); // ลบตาของ AI
            this.gameHistory.pop(); // ลบตาของผู้เล่น
          } else {
            this.gameHistory.pop(); // ลบตาแรกของผู้เล่น
          }

          this.gameState = JSON.parse(JSON.stringify(
            this.gameHistory[this.gameHistory.length - 1]
          ));
          this.isPlayerTurn = true;
          this.updateDisplay();
          this.showMessage('ย้อนกลับแล้ว', 'info');
        }
      }

      pass() {
        if (this.isPlayerTurn && !this.isAIThinking) {
          this.showMessage('คุณผ่านตา', 'info');
          this.isPlayerTurn = false;

          // ให้ AI เล่นต่อ
          setTimeout(() => this.makeAIMove(), 1000);
        }
      }

      surrender() {
        if (this.isPlayerTurn && !this.isAIThinking) {
          this.showMessage('คุณยอมแพ้! คอมพิวเตอร์ชนะ', 'error');
          this.isPlayerTurn = false;
        }
      }
    }

    // Global functions สำหรับปุ่มต่างๆ
    let gameClient;

    function newGame() {
      gameClient.newGame();
    }

    function undoMove() {
      gameClient.undoMove();
    }

    function pass() {
      gameClient.pass();
    }

    function surrender() {
      gameClient.surrender();
    }

    // เริ่มต้นเกมเมื่อหน้าเว็บโหลดเสร็จ
    document.addEventListener('DOMContentLoaded', function() {
      gameClient = new GoGameClient();
    });
  </script>
</body>

</html>