Tìm kiếm bài viết

Hướng Dẫn Tạo Trò Chơi Cờ Caro Nâng Cao bằng HTML

19.03.2025

5.0/5 (2 Reviews)

Chào các bạn! Trong bài viết này, chúng ta sẽ cùng nhau xây dựng một trò chơi Cờ Caro đơn giản nhưng có nhiều tính năng thú vị như giới hạn nước đi, pháo hoa khi chiến thắng, chọn chế độ chơi và đếm thời gian.

    Hướng Dẫn Tạo Trò Chơi Cờ Caro Nâng Cao

    Chào các bạn! Trong bài viết này, chúng ta sẽ cùng nhau xây dựng một trò chơi Cờ Caro đơn giản nhưng có nhiều tính năng thú vị như giới hạn nước đi, pháo hoa khi chiến thắng, chọn chế độ chơi và đếm thời gian.

    1. Tạo File HTML (index.html)

    Đầu tiên, chúng ta cần tạo một file HTML để xây dựng cấu trúc của trò chơi.

    <!DOCTYPE html>
    <html>
    <head>
        <title>Tic-Tac-Toe</title>
        <style>
            /* Các style CSS sẽ được thêm ở bước sau */
        </style>
    </head>
    <body>
        <div class="container">
            <h1>Tic-Tac-Toe</h1>
            <div id="mode-select">
                <button data-mode="human">Chơi với người</button>
                <button data-mode="computer">Chơi với máy</button>
            </div>
            <div id="gameboard">
                <div class="cell" data-cell-index="0"></div>
                <div class="cell" data-cell-index="1"></div>
                <div class="cell" data-cell-index="2"></div>
                <div class="cell" data-cell-index="3"></div>
                <div class="cell" data-cell-index="4"></div>
                <div class="cell" data-cell-index="5"></div>
                <div class="cell" data-cell-index="6"></div>
                <div class="cell" data-cell-index="7"></div>
                <div class="cell" data-cell-index="8"></div>
            </div>
            <div id="timer-container">
                Thời gian còn lại: <span id="timer">15</span> giây
            </div>
            <button id="reset-button">Reset</button>
            <div id="message-container"></div>
            <div id="win-modal">
                <div id="win-modal-content">
                    <h2 id="win-modal-title"></h2>
                    <p id="win-modal-text"></p>
                    <button id="win-modal-close">Chơi lại</button>
                </div>
            </div>
        </div>
        <script>
            /* Các script JavaScript sẽ được thêm ở bước sau */
        </script>
    </body>
    </html>

    2. Thêm CSS (<style>)

    Tiếp theo, chúng ta sẽ thêm các style CSS để trò chơi có giao diện đẹp hơn.

    <style>
    body {
        font-family: 'Arial', sans-serif;
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
        margin: 0;
        background-color: #f0f0f0;
    }
    
    .container {
        text-align: center;
        background-color: #fff;
        padding: 20px;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        position: relative;
    }
    
    h1 {
        color: #333;
        margin-bottom: 20px;
    }
    
    #gameboard {
        display: grid;
        grid-template-columns: repeat(3, 100px);
        grid-template-rows: repeat(3, 100px);
        gap: 8px;
        margin-bottom: 20px;
        position: relative;
    }
    
    .cell {
        width: 100px;
        height: 100px;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 24px;
        cursor: pointer;
        border: 1px solid #ccc;
        border-radius: 4px;
        transition: background-color 0.2s ease;
    }
    
    .cell:hover {
        background-color: #f0f0f0;
    }
    
    .cell.x {
        color: #e55353;
    }
    
    .cell.o {
        color: #3880ff;
    }
    
    .cell.fade {
        opacity: 0.5;
    }
    
    #reset-button {
        padding: 10px 20px;
        font-size: 16px;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s ease;
    }
    
    #reset-button:hover {
        background-color: #45a049;
    }
    
    #message-container {
        margin-top: 20px;
        font-size: 18px;
        color: #333;
    }
    
    /* Modal styles */
    #win-modal {
        display: none;
        position: fixed;
        z-index: 1000;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        background-color: rgba(0,0,0,0.5);
        justify-content: center;
        align-items: center;
    }
    
    #win-modal-content {
        background-color: #fff;
        color: #333;
        padding: 20px;
        border-radius: 8px;
        text-align: center;
        box-shadow: 0 2px 10px rgba(0,0,0,0.5);
        transform: translateY(-50px);
        animation: slideIn 0.3s forwards;
    }
    
    @keyframes slideIn {
        from {
            transform: translateY(-50px);
        }
        to {
            transform: translateY(0);
        }
    }
    
    #win-modal-content h2 {
        margin-bottom: 10px;
        font-size: 24px;
    }
    
    #win-modal-content p {
        margin-bottom: 20px;
        font-size: 18px;
    }
    
    #win-modal-close {
        padding: 10px 20px;
        font-size: 16px;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s ease;
        margin-top: 10px;
    }
    
    #win-modal-close:hover {
        background-color: #45a049;
    }
    
    /* Fireworks styles */
    .firework {
        position: absolute;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background-color: #f00;
        animation: explode 1s ease-out forwards, flicker 0.1s infinite;
        z-index: 1001;
    }
    
    @keyframes explode {
        from {
            transform: translate(0, 0);
            opacity: 1;
        }
        to {
            transform: translate(200px, -100px);
            opacity: 0;
        }
    }
    
    @keyframes flicker {
      0% { opacity: 1; }
      50% { opacity: 0.6; }
      100% { opacity: 1; }
    }
    
    /* Game mode selection styles */
    #mode-select {
        display: flex;
        justify-content: center;
        margin-bottom: 20px;
    }
    #mode-select button {
        padding: 10px 20px;
        font-size: 16px;
        background-color: #e0e0e0;
        color: #333;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        margin: 0 10px;
        transition: background-color 0.3s ease;
    }
    
    #mode-select button:hover {
        background-color: #ccc;
    }
    #mode-select button.selected {
        background-color: #4CAF50;
        color: white;
    }
    
    /* Timer styles */
    #timer-container {
        font-size: 18px;
        color: #333;
        margin-bottom: 10px;
    }
    #timer-container span {
        font-weight: bold;
    }
    </style>

    3. Thêm JavaScript (<script>)

    Cuối cùng, chúng ta sẽ thêm các script JavaScript để xử lý logic của trò chơi.

    const gameboard = document.getElementById("gameboard");
    const cells = document.querySelectorAll(".cell");
    const resetButton = document.getElementById("reset-button");
    const messageContainer = document.getElementById("message-container");
    const winModal = document.getElementById("win-modal");
    const winModalTitle = document.getElementById("win-modal-title");
    const winModalText = document.getElementById("win-modal-text");
    const winModalClose = document.getElementById("win-modal-close");
    const modeSelect = document.getElementById("mode-select");
    const modeButtons = modeSelect.querySelectorAll("button");
    const timerDisplay = document.getElementById("timer");
    const timerContainer = document.getElementById("timer-container");
    
    let currentPlayer = "X";
    let board = ["", "", "", "", "", "", "", "", ""];
    let gameOver = false;
    let moveCounts = { X: 0, O: 0 };
    let moveHistory = { X: [], O: [] };
    let gameMode = "computer";
    let timeLeft = 15;
    let timerId;
    
    modeButtons.forEach(button => {
        button.addEventListener("click", function() {
            gameMode = this.dataset.mode;
            modeButtons.forEach(b => b.classList.remove("selected"));
            this.classList.add("selected");
            resetGame();
            if (gameMode === "computer" && currentPlayer === "O") {
                startTimer();
                setTimeout(computerMove, 500);
            }
        });
    });
    modeSelect.querySelector(`[data-mode="${gameMode}"]`).classList.add("selected");
    
    cells.forEach((cell) => {
        cell.addEventListener("click", handleCellClick);
    });
    resetButton.addEventListener("click", resetGame);
    winModalClose.addEventListener("click", resetGame);
    
    function handleCellClick(event) {
        if (gameOver) {
            return;
        }
    
        const cellIndex = event.target.dataset.cellIndex;
    
        if (board[cellIndex] !== "") {
            return;
        }
    
        board[cellIndex] = currentPlayer;
        event.target.textContent = currentPlayer;
        event.target.classList.add(currentPlayer.toLowerCase());
    
        moveCounts[currentPlayer]++;
        moveHistory[currentPlayer].push(cellIndex);
    
        if (moveCounts[currentPlayer] > 3) {
            const removedCellIndex = moveHistory[currentPlayer].shift();
            board[removedCellIndex] = "";
            const removedCell = document.querySelector(`[data-cell-index="${removedCellIndex}"]`);
            removedCell.textContent = "";
            removedCell.classList.remove(currentPlayer.toLowerCase(), "fade");
        }
        if (moveCounts[currentPlayer] >= 3 && currentPlayer === "X") {
            const cellToFadeIndex = moveHistory[currentPlayer][0];
            const cellToFade = document.querySelector(`[data-cell-index="${cellToFadeIndex}"]`);
            cellToFade.classList.add("fade");
        }
    
        const winner = checkWinner();
        if (winner) {
            gameOver = true;
            stopTimer();
            showWinModal(winner);
        } else if (isBoardFull()) {
            gameOver = true;
            stopTimer();
            showWinModal(null);
        } else {
            switchPlayer();
            startTimer();
            if (gameMode === "computer" && currentPlayer === "O") {
                setTimeout(computerMove, 500);
            }
        }
    }
    
    function switchPlayer() {
        currentPlayer = currentPlayer === "X" ? "O" : "X";
    }
    
    function checkWinner() {
        const winningCombinations = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6],
        ];
    
        for (let combination of winningCombinations) {
            const [a, b, c] = combination;
            if (board[a] && board[a] === board[b] && board[a] === board[c]) {
                return board[a];
            }
        }
        return null;
    }
    
    function isBoardFull() {
        return board.every((cell) => cell !== "");
    }
    
    function resetGame() {
        board = ["", "", "", "", "", "", "", "", ""];
        gameOver = false;
        currentPlayer = "X";
        messageContainer.textContent = "";
        cells.forEach((cell) => {
            cell.textContent = "";
            cell.classList.remove("x", "o", "fade");
            cell.addEventListener("click", handleCellClick);
        });
        moveCounts = { X: 0, O: 0 };
        moveHistory = { X: [], O: [] };
        winModal.style.display = "none";
        document.querySelectorAll('.firework').forEach(fw => fw.remove());
        stopTimer();
        timeLeft = 15;
        timerDisplay.textContent = timeLeft;
        if (gameMode === "computer" && currentPlayer === "O") {
            startTimer();
            setTimeout(computerMove, 500);
        }
    }
    
    function computerMove() {
        if (gameOver) return;
    
        let bestMove = findBestMove();
        board[bestMove] = "O";
        const cell = document.querySelector(`[data-cell-index="${bestMove}"]`);
        cell.textContent = "O";
        cell.classList.add("o");
    
        moveCounts["O"]++;
        moveHistory["O"].push(bestMove);
    
        if (moveCounts["O"] > 3) {
            const removedCellIndex = moveHistory["O"].shift();
            board[removedCellIndex] = "";
            const removedCell = document.querySelector(`[data-cell-index="${removedCellIndex}"]`);
            removedCell.textContent = "";
            removedCell.classList.remove("o", "fade");
        }
        if (moveCounts["O"] >= 3) {
        }
    
        const winner = checkWinner();
        if (winner) {
            gameOver = true;
            stopTimer();
            showWinModal(winner);
        } else if (isBoardFull()) {
            gameOver = true;
            stopTimer();
            showWinModal(null);
        } else {
            switchPlayer();
            startTimer();
        }
    }
    
    function findBestMove() {
        let bestMove = -1;
        let bestScore = -Infinity;
    
        for (let i = 0; i < board.length; i++) {
            if (board[i] === "") {
                board[i] = "O";
                let score = minimax(board, 0, false);
                board[i] = "";
                if (score > bestScore) {
                    bestScore = score;
                    bestMove = i;
                }
            }
        }
        return bestMove;
    }
    
    function minimax(board, depth, isMaximizing) {
        const scores = {
            X: -1,
            O: 1,
            tie: 0,
        };
    
        const winner = checkWinner();
        if (winner) {
            return scores[winner];
        }
        if (isBoardFull()) {
            return scores.tie;
        }
    
        if (isMaximizing) {
            let bestScore = -Infinity;
            for (let i = 0; i < board.length; i++) {
                if (board[i] === "") {
                    board[i] = "O";
                    let score = minimax(board, depth + 1, false);
                    board[i] = "";
                    bestScore = Math.max(score, bestScore);
                }
            }
            return bestScore;
        } else {
            let bestScore = Infinity;
            for (let i = 0; i < board.length; i++) {
                if (board[i] === "") {
                    board[i] = "X";
                    let score = minimax(board, depth + 1, true);
                    board[i] = "";
                    bestScore = Math.min(score, bestScore);
                }
            }
            return bestScore;
        }
    }
    
    function showWinModal(winner) {
        winModal.style.display = "flex";
        if (winner) {
            winModalTitle.textContent = `Người chơi ${winner} thắng!`;
            winModalText.textContent = "Chúc mừng!";
            for (let i = 0; i < 20; i++) {
                createFirework();
            }
    
        } else {
            winModalTitle.textContent = "Hòa!";
            winModalText.textContent = "Ván đấu kết thúc với tỉ số hòa.";
        }
    }
    
    function createFirework() {
        const firework = document.createElement('div');
        firework.className = 'firework';
        const startX = Math.random() * gameboard.offsetWidth;
        const startY = gameboard.offsetHeight;
    
        const endX = startX + (Math.random() - 0.5) * 300;
        const endY = Math.random() * -200;
        const duration = 800 + Math.random() * 600;
        const delay = Math.random() * 200;
    
        firework.style.left = `${startX}px`;
        firework.style.top = `${startY}px`;
    
        firework.style.setProperty('--end-x', `${endX}px`);
        firework.style.setProperty('--end-y', `${endY}px`);
        firework.style.animationDuration = `${duration}ms`;
        firework.style.animationDelay = `${delay}ms`;
    
        const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];
        firework.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
    
        gameboard.appendChild(firework);
    
        setTimeout(() => {
            firework.remove();
        }, duration + delay);
    }
    
    function startTimer() {
        stopTimer();
        timeLeft = 15;
        timerDisplay.textContent = timeLeft;
        timerContainer.style.color = "#333";
        timerId = setInterval(updateTimer, 1000);
    }
    
    function updateTimer() {
        if (timeLeft > 0) {
            timeLeft--;
            timerDisplay.textContent = timeLeft;
            if (timeLeft <= 5) {
                timerContainer.style.color = "red";
            }
        } else {
            stopTimer();
            const winner = currentPlayer === "X" ? "O" : "X";
            gameOver = true;
            showWinModal(winner);
        }
    }
    
    function stopTimer() {
        clearInterval(timerId);
    }
    </script>

    Giải Thích Code

    • HTML: - Tạo cấu trúc trò chơi với các phần tử như bảng game, nút reset, thông báo, modal và bộ đếm thời gian.

      - Thêm các nút chọn chế độ chơi "Chơi với người" và "Chơi với máy".

    • CSS: - Style cho bảng game, các ô cờ, nút bấm, modal và hiệu ứng pháo hoa.

      - Tạo hiệu ứng mờ cho các ô cờ khi đến lượt bị xóa.

    • JavaScript: - handleCellClick(): Xử lý khi người chơi click vào một ô cờ.

      - switchPlayer(): Chuyển lượt chơi giữa người chơi X và O.

      - checkWinner(): Kiểm tra xem có người chiến thắng hay không.

      - isBoardFull(): Kiểm tra xem bảng cờ đã đầy chưa.

      - resetGame(): Khởi tạo lại trò chơi.

      - computerMove(): Xử lý lượt đi của máy (nếu chọn chế độ chơi với máy).

      - findBestMove()minimax(): Tìm kiếm nước đi tốt nhất cho máy sử dụng thuật toán Minimax.

      - showWinModal(): Hiển thị modal thông báo người chiến thắng hoặc hòa.

      - createFirework(): Tạo hiệu ứng pháo hoa.

      - startTimer(), updateTimer(), stopTimer(): Quản lý bộ đếm thời gian cho mỗi lượt đi.

    Cách Chơi

    1. Chọn chế độ chơi:

      - Nhấn vào nút "Chơi với người" để chơi cùng bạn bè.

      - Nhấn vào nút "Chơi với máy" để chơi với máy tính.

    2. Chơi:

      - Người chơi X đi trước, sau đó đến người chơi O.

      - Mỗi người chơi có 15 giây để đi một nước, nếu không sẽ bị xử thua.

      - Sau 3 nước đi, nước đi đầu tiên sẽ bị làm mờ và sau đó bị xóa ở nước đi tiếp theo.

    3. Kết thúc:

      - Trò chơi kết thúc khi có người thắng hoặc hòa.

      - Modal thông báo kết quả sẽ hiện lên cùng với pháo hoa nếu có người thắng.

      - Nhấn nút "Chơi lại" để bắt đầu ván mới.

    Mở Rộng

    Bạn có thể mở rộng trò chơi này bằng cách thêm các tính năng sau:

    • Thêm âm thanh khi có người đi, khi thắng, khi hết giờ.

    • Tùy chỉnh kích thước bảng cờ.

    • Thêm các cấp độ khó cho máy tính.

    • Lưu lại lịch sử các ván chơi.

    Chúc các bạn có những giây phút thư giãn thú vị với trò chơi Cờ Caro này!

    CÓ THỂ BẠN QUAN TÂM

    Bài Viết Cùng Chuyên Mục

    XEM THÊM
    thumbnail

    Kubernetes bài 6 - Vận hành k8s Day-Two Operations và Quản trị bằng GitOps

    22.05.2026

    Khi cụm Kubernetes của bạn đã được bảo mật cấu hình, tối ưu tài nguyên và thiết lập tự phục hồi, câu hỏi đặt ra là làm sao để duy trì sự ổn định đó trong nhiều năm tiếp theo mà không bị phụ thuộc

    thumbnail

    Kubernetes bài 5 - bảo mật Cloud Native và chuẩn DevSecOps cho K8s

    22.05.2026

    Việc siết chặt an ninh (Hardening) không phải là cấu hình một vài thông số rồi bỏ đó, mà là một tư duy phòng thủ chiều sâu.

    thumbnail

    Kubernetes bài 4 - Tối ưu Resource Auto-Healing và Scale Zero-Downtime

    22.05.2026

    Bài viết này sẽ đi sâu vào các cơ chế ở tầng Kernel giúp hệ thống tự phục hồi, chống lại các đợt tấn công cạn kiệt tài nguyên và cập nhật phiên bản mới mà người dùng không hề hay biết.

    thumbnail

    Kubernetes bài 3 - Bảo mật cấu hình k8s và config Security trên Production

    22.05.2026

    Kubernetes giải quyết bài toán này bằng hai đối tượng chuyên biệt nhưng nếu không hiểu rõ bản chất bảo mật ở tầng dưới, bạn đang tự tay dâng toàn bộ chìa khóa hệ thống cho hacker.

    thumbnail

    Kubernetes bài 2 - Mạng lưới k8s và luồng Traffic ở Packet Level

    22.05.2026

    Pod không chỉ là một container: Rất nhiều người nhầm lẫn Pod 1-1 với Container. Thực chất, Pod là đơn vị triển khai nhỏ nhất, có thể chứa một hoặc nhiều container

    Mục lục bài viết