<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>16 Queens Puzzle</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>16 Queens Puzzle</h1>

    <div id="instructions">
        <h2>How to Play:</h2>
        <p>The goal is to place 16 queens on the 16x16 chessboard such that no two queens threaten each other.</p>
        <ul>
            <li>No two queens can be in the same row.</li>
            <li>No two queens can be in the same column.</li>
            <li>No two queens can be on the same diagonal.</li>
        </ul>
        <p>Click on an empty square to place a queen. Click on a square with a queen to remove it.</p>
        <p>Use the "Show Hints" button to highlight squares currently under attack by existing queens.</p>
    </div>

    <div class="controls">
        <button id="reset-button">Reset Game</button>
        <button id="hint-button">Show Hints</button>
        <p id="message">Place 16 queens so that none attack each other.</p>
        <p>Queens placed: <span id="queen-count">0</span> / 16</p>
    </div>

    <div id="chessboard">
        <!-- Squares will be generated by JavaScript -->
    </div>

    <script src="script.js"></script>
</body>
</html>
/* --- Import Google Fonts --- */
@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&family=Lato:wght@400;700&display=swap');

/* --- Global Styles & Variables --- */
:root {
    --font-primary: 'Lato', sans-serif;
    --font-heading: 'Merriweather', serif;
    --color-bg: #f4f0e8; /* Creamy background */
    --color-dark-wood: #8B4513; /* Saddle Brown */
    --color-light-wood: #DEB887; /* Burly Wood */
    --color-dark-wood-gradient: linear-gradient(145deg, #a0522d, #8b4513);
    --color-light-wood-gradient: linear-gradient(145deg, #f5deb3, #deb887);
    --color-queen: #111;
    --color-hint: rgba(255, 50, 50, 0.55); /* Brighter, slightly more opaque red */
    --color-win: #28a745; /* Success Green */
    --color-button-reset: #dc3545; /* Danger Red */
    --color-button-reset-hover: #c82333;
    --color-button-hint: #17a2b8; /* Info Blue */
    --color-button-hint-hover: #138496;
    --shadow-light: 0 2px 5px rgba(0, 0, 0, 0.1);
    --shadow-medium: 0 5px 15px rgba(0, 0, 0, 0.2);
    --shadow-heavy: 0 10px 25px rgba(0, 0, 0, 0.3);
}

body {
    font-family: var(--font-primary);
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: var(--color-bg);
    background-image: linear-gradient(to bottom, #ffffff, var(--color-bg) 150px); /* Subtle gradient */
    margin: 0;
    padding: 30px 15px 60px 15px;
    color: #333;
    line-height: 1.6;
}

h1, h2 {
    font-family: var(--font-heading);
    color: #4a2c1a; /* Darker brown for headings */
    text-align: center;
    margin-bottom: 0.75em;
}

h1 {
    font-size: 2.8em;
    text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
}

h2 {
    font-size: 1.6em;
    border-bottom: 1px solid #d4c8b4;
    padding-bottom: 5px;
}

/* --- Instructions Area --- */
#instructions {
    background-color: #fffaf0; /* Floral White */
    padding: 20px 25px;
    border-radius: 12px;
    margin-bottom: 30px;
    max-width: 800px;
    box-shadow: var(--shadow-medium);
    border: 1px solid #eaddc5;
}

#instructions ul {
    list-style-type: disc; /* Use standard bullets */
    list-style-position: outside; /* Proper indentation */
    padding-left: 25px; /* Indent list items */
    margin-top: 10px;
}
#instructions li {
    margin-bottom: 8px;
}

/* --- Controls Area --- */
.controls {
    margin-bottom: 30px;
    text-align: center;
    width: 100%;
    max-width: 800px;
}

.controls p {
    margin: 12px 0;
    font-size: 1.1em;
}

.controls button {
    padding: 12px 25px;
    font-size: 1.1em;
    font-family: var(--font-primary);
    font-weight: bold;
    cursor: pointer;
    color: white;
    border: none;
    border-radius: 8px;
    margin: 5px 8px 15px 8px;
    transition: all 0.25s ease-out;
    box-shadow: var(--shadow-light);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.controls button:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.controls button:active {
    transform: translateY(-1px);
    box-shadow: var(--shadow-light);
}

#reset-button {
    background-color: var(--color-button-reset);
}
#reset-button:hover {
    background-color: var(--color-button-reset-hover);
}

#hint-button {
    background-color: var(--color-button-hint);
}
#hint-button:hover {
    background-color: var(--color-button-hint-hover);
}

#message {
    font-style: italic;
    color: #555;
    min-height: 25px;
    font-size: 1.1em;
    font-weight: bold;
    transition: color 0.3s ease;
}

#queen-count {
    font-weight: bold;
    background-color: #e9ecef;
    padding: 2px 8px;
    border-radius: 4px;
}

/* --- Chessboard Styling --- */
#chessboard {
    /* Keep dimensions from previous step (e.g., 640px) */
    width: 640px;
    height: 640px;
    display: grid;
    grid-template-columns: repeat(16, 1fr);
    grid-template-rows: repeat(16, 1fr);
    border: 10px solid #603813; /* Thicker, darker wood frame */
    box-shadow: var(--shadow-heavy), 0 0 0 1px #9d7a5c; /* Heavy shadow + subtle edge */
    border-radius: 6px; /* Slightly rounded board corners */
    background-color: var(--color-dark-wood); /* Fallback/gap color */
}

/* --- Square Styling --- */
.square {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 1.8em; /* Keep queen size reasonable */
    cursor: pointer;
    user-select: none;
    transition: transform 0.15s ease-out, box-shadow 0.15s ease-out, background-color 0.2s ease;
    position: relative; /* Needed for pseudo-elements and z-index */
    box-sizing: border-box;
    /* Remove border, use gradients */
}

.square.light {
    background: var(--color-light-wood-gradient);
}

.square.dark {
    background: var(--color-dark-wood-gradient);
}

/* --- Square Hover Effect --- */
.square:not(.queen):hover {
    transform: scale(1.08);
    z-index: 10; /* Bring hovered square to front */
    box-shadow: var(--shadow-medium);
    filter: brightness(1.1); /* Slightly brighten on hover */
}

/* --- Queen Styling --- */
.square.queen::before {
    content: '♛';
    color: var(--color-queen);
    text-shadow: 1px 1px 0px #fff, /* White highlight top-left */
                 -1px -1px 0px rgba(0,0,0,0.2); /* Dark shadow bottom-right */
    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* Bounce effect */
    /* Animation on placement */
    animation: placeQueen 0.3s ease-out;
}

/* Animation for queen placement */
@keyframes placeQueen {
    0% { transform: scale(0.5); opacity: 0; }
    80% { transform: scale(1.1); opacity: 1; }
    100% { transform: scale(1); opacity: 1; }
}

/* Slight scale effect when hovering over a queen */
.square.queen:hover::before {
     transform: scale(1.1);
}


/* --- Hint Styling --- */
.square.hint-attacked {
    /* Use a pseudo-element for the overlay to avoid affecting gradients */
    position: relative; /* Needed for pseudo-element positioning */
}

.square.hint-attacked::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: var(--color-hint);
    border-radius: 3px; /* Match square rounding if any */
    pointer-events: none; /* Allow clicks to pass through */
    animation: pulseHint 1.5s infinite ease-in-out;
    z-index: 5; /* Above square background, below queen */
}

/* Animation for hint pulse */
@keyframes pulseHint {
    0% { opacity: 0.5; }
    50% { opacity: 0.8; }
    100% { opacity: 0.5; }
}

/* --- Win State Styling --- */
.win #message {
    color: var(--color-win);
    font-weight: bold;
    font-size: 1.4em; /* Make win message stand out */
    text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
    animation: fadeInWin 0.5s ease-in;
}
@keyframes fadeInWin {
     from { opacity: 0; transform: translateY(10px); }
     to { opacity: 1; transform: translateY(0); }
}

.win #hint-button {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none; /* Disable hover transform */
    box-shadow: var(--shadow-light); /* Reset shadow */
}

/* Optional: Add subtle shake for invalid move feedback (requires JS change to add/remove class) */
@keyframes shake {
  10%, 90% { transform: translate3d(-1px, 0, 0); }
  20%, 80% { transform: translate3d(2px, 0, 0); }
  30%, 50%, 70% { transform: translate3d(-3px, 0, 0); }
  40%, 60% { transform: translate3d(3px, 0, 0); }
}
.invalid-shake {
    animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
}
document.addEventListener('DOMContentLoaded', () => {
    const boardSize = 16; // <-- Change board size to 16
    const chessboard = document.getElementById('chessboard');
    const messageElement = document.getElementById('message');
    const queenCountElement = document.getElementById('queen-count');
    const resetButton = document.getElementById('reset-button');
    const hintButton = document.getElementById('hint-button');
    const bodyElement = document.body;

    let boardState = Array(boardSize).fill(null).map(() => Array(boardSize).fill(0)); // 0: empty, 1: queen
    let queenCount = 0;
    let gameOver = false;
    let hintActive = false; // To manage hint state

    // --- Board Generation ---
    function createBoard() {
        chessboard.innerHTML = ''; // Clear previous board
        for (let row = 0; row < boardSize; row++) {
            for (let col = 0; col < boardSize; col++) {
                const square = document.createElement('div');
                square.classList.add('square');
                square.classList.add(((row + col) % 2 === 0) ? 'light' : 'dark');
                square.dataset.row = row;
                square.dataset.col = col;
                square.addEventListener('click', handleSquareClick);
                chessboard.appendChild(square);
            }
        }
    }

    // --- Game Logic ---

    // Checks if placing a queen at (row, col) is SAFE
    function isSafe(row, col) {
        // Check if the square itself already has a queen (needed for hint logic)
        if (boardState[row][col] === 1) return true; // It's safe relative to itself

        // Check row and column for other queens
        for (let i = 0; i < boardSize; i++) {
            if (boardState[row][i] === 1) return false; // Check row
            if (boardState[i][col] === 1) return false; // Check column
        }

        // Check diagonals
        for (let i = 1; i < boardSize; i++) {
            // Top-left
            if (row - i >= 0 && col - i >= 0 && boardState[row - i][col - i] === 1) return false;
            // Top-right
            if (row - i >= 0 && col + i < boardSize && boardState[row - i][col + i] === 1) return false;
            // Bottom-left
            if (row + i < boardSize && col - i >= 0 && boardState[row + i][col - i] === 1) return false;
            // Bottom-right
            if (row + i < boardSize && col + i < boardSize && boardState[row + i][col + i] === 1) return false;
        }

        return true;
    }

    // Checks if a specific square (row, col) is attacked by ANY existing queen
    function isAttacked(row, col) {
        // Check row and column
        for (let i = 0; i < boardSize; i++) {
            if (i !== col && boardState[row][i] === 1) return true; // Check row (exclude self implicitly as we only check empty squares for hints)
            if (i !== row && boardState[i][col] === 1) return true; // Check column
        }
        // Check diagonals
        for (let i = 1; i < boardSize; i++) {
            if (row - i >= 0 && col - i >= 0 && boardState[row - i][col - i] === 1) return true;
            if (row - i >= 0 && col + i < boardSize && boardState[row - i][col + i] === 1) return true;
            if (row + i < boardSize && col - i >= 0 && boardState[row + i][col - i] === 1) return true;
            if (row + i < boardSize && col + i < boardSize && boardState[row + i][col + i] === 1) return true;
        }
        return false;
    }


    function handleSquareClick(event) {
        if (gameOver) return; // Don't allow changes after winning

        const square = event.target;
        const row = parseInt(square.dataset.row);
        const col = parseInt(square.dataset.col);

        clearHints(); // Clear hints on any valid click
        resetMessage();

        if (boardState[row][col] === 1) {
            // Remove queen
            boardState[row][col] = 0;
            square.classList.remove('queen');
            queenCount--;
            messageElement.textContent = "Queen removed.";
        } else {
            // Try to place queen
            if (isSafe(row, col)) {
                boardState[row][col] = 1;
                square.classList.add('queen');
                queenCount++;
                messageElement.textContent = "Queen placed.";

                if (queenCount === boardSize) {
                    checkWinCondition();
                }
            } else {
                messageElement.textContent = "Invalid move! A queen here would be attacked.";
                // Briefly flash the clicked square or attacking queens (optional enhancement)
            }
        }
        updateQueenCount();
    }

    function updateQueenCount() {
        queenCountElement.textContent = `${queenCount} / ${boardSize}`;
    }

    function checkWinCondition() {
        // Since we prevent invalid moves by checking isSafe before placement,
        // reaching boardSize queens means success.
        if (queenCount === boardSize) {
            messageElement.textContent = `Congratulations! You solved the ${boardSize} Queens puzzle!`;
            bodyElement.classList.add('win');
            gameOver = true;
            hintButton.disabled = true; // Disable hint button on win
        }
    }

    // --- Hint System ---
    function showHints() {
        if (gameOver || hintActive) return; // Don't show hints if game over or already showing

        clearHints(); // Clear previous hints first
        hintActive = true;
        let hintsShown = 0;

        messageElement.textContent = "Highlighting attacked empty squares...";

        const squares = chessboard.querySelectorAll('.square');
        squares.forEach(square => {
            const r = parseInt(square.dataset.row);
            const c = parseInt(square.dataset.col);

            // Only check empty squares
            if (boardState[r][c] === 0) {
                if (isAttacked(r, c)) {
                    square.classList.add('hint-attacked');
                    hintsShown++;
                }
            }
        });

        if (hintsShown === 0 && queenCount > 0) {
             messageElement.textContent = "No empty squares are currently under attack.";
        } else if (queenCount === 0) {
             messageElement.textContent = "Place a queen first to see attacked squares.";
        }

        // Automatically clear hints after a delay
        setTimeout(clearHints, 2500); // Clear hints after 2.5 seconds
    }

    function clearHints() {
        if (!hintActive) return;
        const hintedSquares = chessboard.querySelectorAll('.square.hint-attacked');
        hintedSquares.forEach(sq => sq.classList.remove('hint-attacked'));
        resetMessage(); // Reset message after clearing hints
        hintActive = false;
    }

    // --- Reset & Utility ---
     function resetMessage() {
        if (!gameOver && !hintActive) { // Don't reset win message or hint message immediately
             messageElement.textContent = `Place ${boardSize} queens so that none attack each other.`;
        }
    }

    function resetGame() {
        boardState = Array(boardSize).fill(null).map(() => Array(boardSize).fill(0));
        queenCount = 0;
        gameOver = false;
        clearHints(); // Ensure hints are cleared
        bodyElement.classList.remove('win');
        hintButton.disabled = false; // Re-enable hint button
        updateQueenCount();
        resetMessage(); // Set the default message

        // Clear visual representation
        const squares = chessboard.querySelectorAll('.square');
        squares.forEach(square => {
            square.classList.remove('queen');
            square.classList.remove('hint-attacked'); // Ensure hints are visually cleared
        });
    }

    // --- Initialization ---
    createBoard();
    resetButton.addEventListener('click', resetGame);
    hintButton.addEventListener('click', showHints);
    updateQueenCount(); // Initial count
    resetMessage(); // Initial message
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.