diff --git a/projects/number-guessing-game/index.html b/projects/number-guessing-game/index.html index f70b83a..3dcd566 100644 --- a/projects/number-guessing-game/index.html +++ b/projects/number-guessing-game/index.html @@ -4,16 +4,20 @@ Number Guessing Game +

Number Guessing Game

-
- - - - - +
+

I'm thinking of a number between 1 and 100. Can you guess it?

+
+ + +
+

+

Attempts: 0

+
diff --git a/projects/number-guessing-game/main.js b/projects/number-guessing-game/main.js index 64d8fa1..21865d6 100644 --- a/projects/number-guessing-game/main.js +++ b/projects/number-guessing-game/main.js @@ -1,15 +1,92 @@ -// TODO: Generate a random number -// TODO: Handle user guess input -// TODO: Compare guess to target number -// TODO: Provide feedback (too high, too low, correct) -// TODO: Track number of attempts -// TODO: Restart game functionality +// Utility to generate a random integer in [min, max] +function generateRandomInteger(min, max) { + const minCeil = Math.ceil(min); + const maxFloor = Math.floor(max); + return Math.floor(Math.random() * (maxFloor - minCeil + 1)) + minCeil; +} function initNumberGuessingGame() { - // TODO: Generate random number - // TODO: Handle user input and feedback - // TODO: Track attempts - // TODO: Restart game + const input = document.getElementById('guess-input'); + const guessBtn = document.getElementById('guess-btn'); + const feedback = document.getElementById('feedback'); + const attemptsText = document.getElementById('attempts'); + const attemptCount = document.getElementById('attempt-count'); + const restartBtn = document.getElementById('restart-btn'); + + const MIN = 1; + const MAX = 100; + + let targetNumber = generateRandomInteger(MIN, MAX); + let attempts = 0; + let gameOver = false; + + function setFeedback(message, type) { + feedback.textContent = message; + feedback.dataset.type = type || ''; + } + + function setGameOver(over) { + gameOver = over; + input.disabled = over; + guessBtn.disabled = over; + if (over) { + input.blur(); + } + } + + function validateGuess(value) { + if (value === '') return { ok: false, msg: 'Please enter a number.' }; + const num = Number(value); + if (!Number.isFinite(num)) return { ok: false, msg: 'That is not a valid number.' }; + if (!Number.isInteger(num)) return { ok: false, msg: 'Please enter a whole number.' }; + if (num < MIN || num > MAX) return { ok: false, msg: `Enter a number between ${MIN} and ${MAX}.` }; + return { ok: true, value: num }; + } + + function handleGuess() { + if (gameOver) return; + const { ok, msg, value } = validateGuess(input.value.trim()); + if (!ok) { + setFeedback(msg, 'error'); + return; + } + attempts += 1; + attemptCount.textContent = String(attempts); + + if (value === targetNumber) { + setFeedback(`Correct! The number was ${targetNumber}.`, 'success'); + setGameOver(true); + return; + } + if (value < targetNumber) { + setFeedback('Too low. Try a higher number.', 'low'); + } else { + setFeedback('Too high. Try a lower number.', 'high'); + } + input.select(); + } + + function restartGame() { + targetNumber = generateRandomInteger(MIN, MAX); + attempts = 0; + attemptCount.textContent = '0'; + setFeedback('', ''); + input.value = ''; + setGameOver(false); + input.focus(); + } + + guessBtn.addEventListener('click', handleGuess); + input.addEventListener('keydown', function onKeyDown(event) { + if (event.key === 'Enter') { + handleGuess(); + } + }); + restartBtn.addEventListener('click', restartGame); + + // Init state + attemptsText.hidden = false; + setFeedback('', ''); } window.addEventListener('DOMContentLoaded', initNumberGuessingGame); \ No newline at end of file diff --git a/projects/number-guessing-game/styles.css b/projects/number-guessing-game/styles.css index 95f3d47..c45c7ef 100644 --- a/projects/number-guessing-game/styles.css +++ b/projects/number-guessing-game/styles.css @@ -1 +1,68 @@ -/* TODO: Style game container, input, button, feedback, attempts, restart button */ \ No newline at end of file +/* Basic reset */ +/* Page-level layout is handled by assets/styles.css; only tweak specifics here */ +html, body { height: 100%; } +body { + display: flex; + flex-direction: column; + align-items: center; + background: var(--bg); + color: var(--text); +} + +h1 { margin: 24px 0 12px; font-size: 24px; font-weight: 700; color: var(--text); } + +#game-container { + width: 100%; + max-width: 420px; + background: var(--card); + border: 1px solid #262631; + border-radius: 12px; + padding: 20px; + box-shadow: 0 0 0 1px rgba(255,255,255,0.02) inset; +} + +#instructions { margin: 0 0 12px; color: var(--muted); } + +#controls { + display: flex; + gap: 8px; +} + +#guess-input { + flex: 1 1 auto; + padding: 10px 12px; + border: 1px solid #2a2a33; + border-radius: 8px; + font-size: 14px; + outline: none; + background: #0e0e13; + color: var(--text); +} +#guess-input::placeholder { color: var(--muted); } +#guess-input:focus { border-color: #93c5fd; box-shadow: 0 0 0 3px rgba(147,197,253,0.2); } + +button { + padding: 10px 14px; + border-radius: 8px; + border: 1px solid #1e3a8a; + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + color: #0b1020; + font-weight: 600; + cursor: pointer; +} +button:hover { filter: brightness(1.05); } +button:disabled { background: #1f1f27; border-color: #2a2a33; color: var(--muted); cursor: not-allowed; } + +#feedback { min-height: 22px; margin: 12px 0 8px; } +#feedback[data-type="error"] { color: #f87171; } +#feedback[data-type="low"], #feedback[data-type="high"] { color: #fbbf24; } +#feedback[data-type="success"] { color: var(--accent); } + +#attempts { margin: 0 0 12px; color: var(--muted); } + +#restart-btn { + width: 100%; + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + border-color: #1e3a8a; +} +#restart-btn:hover { filter: brightness(1.05); } \ No newline at end of file