From 85197bcc0bbb2d186c05b9825444598fc10e2e9c Mon Sep 17 00:00:00 2001 From: le4vee Date: Sun, 12 Oct 2025 18:04:52 +0530 Subject: [PATCH] updated tic tac toe --- projects/tic-tac-toe/index.html | 45 +++++++---- projects/tic-tac-toe/main.js | 131 ++++++++++++++++++++++++++++---- projects/tic-tac-toe/styles.css | 61 ++++++++++----- 3 files changed, 188 insertions(+), 49 deletions(-) diff --git a/projects/tic-tac-toe/index.html b/projects/tic-tac-toe/index.html index b25fb66..9851039 100644 --- a/projects/tic-tac-toe/index.html +++ b/projects/tic-tac-toe/index.html @@ -1,20 +1,35 @@ - + - - - - Tic-Tac-Toe - + + +Tic-Tac-Toe + - -
-

Tic-Tac-Toe

-
-

Add two-player, AI, and UI themes.

-
- - +
+

Tic-Tac-Toe

+
- \ No newline at end of file +
+ + + +
+ +
+

X Wins: 0

+

O Wins: 0

+

Draws: 0

+
+
+ + + + diff --git a/projects/tic-tac-toe/main.js b/projects/tic-tac-toe/main.js index 453cb03..8db379e 100644 --- a/projects/tic-tac-toe/main.js +++ b/projects/tic-tac-toe/main.js @@ -1,15 +1,116 @@ -const boardEl = document.getElementById('board'); -let b = Array(9).fill(null), turn = 'X'; -function render() { - boardEl.innerHTML = ''; - b.forEach((v, i) => { - const btn = document.createElement('button'); - btn.className = 'cell'; - btn.textContent = v || ''; - btn.addEventListener('click', () => move(i)); - boardEl.appendChild(btn); - }); -} -function move(i) { if (b[i]) return; b[i] = turn; turn = turn === 'X' ? 'O' : 'X'; render(); } -render(); -// TODOs: detect wins/draws; score; restart; 2P; simple AI; themes +document.addEventListener("DOMContentLoaded", () => { + const boardEl = document.getElementById('board'); + const restartBtn = document.getElementById('restartBtn'); + const modeSelect = document.getElementById('modeSelect'); + const themeSelect = document.getElementById('themeSelect'); + const xScoreEl = document.getElementById('xScore'); + const oScoreEl = document.getElementById('oScore'); + const drawScoreEl = document.getElementById('drawScore'); + + let b = Array(9).fill(null); + let turn = 'X'; + let gameOver = false; + let mode = '2P'; + let scores = { X: 0, O: 0, Draw: 0 }; + + const winningCombos = [ + [0,1,2],[3,4,5],[6,7,8], + [0,3,6],[1,4,7],[2,5,8], + [0,4,8],[2,4,6] + ]; + + function checkWin() { + for (const combo of winningCombos) { + const [a,bIndex,c] = combo; + if (b[a] && b[a] === b[bIndex] && b[a] === b[c]) return b[a]; + } + return null; + } + + function checkDraw() { + return b.every(cell => cell !== null); + } + + function render() { + boardEl.innerHTML = ''; + b.forEach((v,i) => { + const btn = document.createElement('button'); + btn.className = 'cell'; + btn.textContent = v || ''; + btn.addEventListener('click', () => move(i)); + boardEl.appendChild(btn); + }); + } + + function move(i) { + if (b[i] || gameOver) return; + + b[i] = turn; + const winner = checkWin(); + + if (winner) { + alert(`${winner} wins!`); + scores[winner]++; + gameOver = true; + } else if (checkDraw()) { + alert("It's a draw!"); + scores.Draw++; + gameOver = true; + } else { + turn = turn === 'X' ? 'O' : 'X'; + if (mode === 'AI' && turn === 'O' && !gameOver) aiMove(); + } + + updateScoreboard(); + render(); + } + + // Smart AI + function aiMove() { + let moveIndex = findBestMove('O'); // Win if possible + if (moveIndex === null) moveIndex = findBestMove('X'); // Block X + if (moveIndex === null && b[4] === null) moveIndex = 4; // Take center + if (moveIndex === null) { + const corners = [0,2,6,8].filter(i => b[i] === null); + if (corners.length) moveIndex = corners[Math.floor(Math.random()*corners.length)]; + } + if (moveIndex === null) { + const empty = b.map((v,i) => v===null? i:null).filter(v=>v!==null); + moveIndex = empty[Math.floor(Math.random()*empty.length)]; + } + move(moveIndex); + } + + function findBestMove(player) { + for (const combo of winningCombos) { + const [a,bIndex,c] = combo; + const line = [b[a], b[bIndex], b[c]]; + if (line.filter(v => v===player).length === 2 && line.includes(null)) { + return combo[line.indexOf(null)]; + } + } + return null; + } + + function updateScoreboard() { + xScoreEl.textContent = scores.X; + oScoreEl.textContent = scores.O; + drawScoreEl.textContent = scores.Draw; + } + + function restart() { + b = Array(9).fill(null); + turn = 'X'; + gameOver = false; + render(); + } + + // Event listeners + restartBtn.addEventListener('click', restart); + modeSelect.addEventListener('change', e => { mode = e.target.value; restart(); }); + themeSelect.addEventListener('change', e => { document.body.className = e.target.value; }); + + // Initial render + render(); + updateScoreboard(); +}); diff --git a/projects/tic-tac-toe/styles.css b/projects/tic-tac-toe/styles.css index 4181846..f4d29e0 100644 --- a/projects/tic-tac-toe/styles.css +++ b/projects/tic-tac-toe/styles.css @@ -1,36 +1,59 @@ body { - font-family: system-ui; + font-family: Arial, sans-serif; background: #0f0f12; color: #eef1f8; margin: 0; - padding: 2rem; - display: grid; - place-items: center + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; } main { - max-width: 560px; - width: 100% + text-align: center; } .board { display: grid; - grid-template-columns: repeat(3, 80px); - gap: .5rem + grid-template-columns: repeat(3, 100px); + grid-template-rows: repeat(3, 100px); + gap: 5px; + margin: 20px auto; } .cell { - width: 80px; - height: 80px; - border-radius: .5rem; - border: 1px solid #262631; background: #17171c; - color: #eef1f8; - font-size: 2rem; - cursor: pointer + border: 2px solid #262631; + border-radius: 8px; + font-size: 3rem; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + user-select: none; } -.notes { - color: #a6adbb; - font-size: .9rem -} \ No newline at end of file +.controls, .scoreboard { + display: flex; + justify-content: center; + gap: 10px; + margin: 10px 0; +} + +button, select { + padding: 0.5rem 1rem; + border-radius: 5px; + border: none; + cursor: pointer; +} + +body.light { + background: #eef1f8; + color: #0f0f12; +} + +body.light .cell { + background: #ffffff; + color: #0f0f12; + border: 1px solid #ccc; +}