diff --git a/projects/tic-tac-toe/index.html b/projects/tic-tac-toe/index.html
index 9851039..a18a969 100644
--- a/projects/tic-tac-toe/index.html
+++ b/projects/tic-tac-toe/index.html
@@ -1,35 +1,92 @@
-
-
-
-Tic-Tac-Toe
-
-
-
-
- Tic-Tac-Toe
-
-
-
-
-
-
-
-
-
+
+
+
+
Tic-Tac-Toe
+
+
+
+
+
+
+
X Wins: 0
O Wins: 0
Draws: 0
-
-
+
Mode: player
+
+
+
Tic-Tac-Toe
+
+
+
+
Player 1
+
+ chevron_left
+
+
+ chevron_right
+
+
Player 2
+
+
+
+ settings
+
+
+
+
+
Play against:
+
+
+
Player
+ Computer
+
+
+
+
-
-
+
+
diff --git a/projects/tic-tac-toe/main.js b/projects/tic-tac-toe/main.js
index 8db379e..7f7cca0 100644
--- a/projects/tic-tac-toe/main.js
+++ b/projects/tic-tac-toe/main.js
@@ -1,116 +1,196 @@
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;
- }
+ const boardEl = document.getElementById("board");
+ const restartBtn = document.getElementById("restartBtn");
+ const xScoreEl = document.getElementById("xScore");
+ const oScoreEl = document.getElementById("oScore");
+ const Currentmode = document.getElementById("Currentmode");
+ const drawScoreEl = document.getElementById("drawScore");
+ const leftArrow = document.querySelector(".orange-marker"); // Player 1 arrow
+ const rightArrow = document.querySelector(".pink-marker"); // Player 2 arrow
+ let b = Array(9).fill(null);
+ let turn = "X";
+ let gameOver = false;
+ let mode = "2P";
+ let scores = { X: 0, O: 0, Draw: 0 };
- function checkDraw() {
- return b.every(cell => cell !== null);
- }
+ 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 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 checkWin() {
+ for (const combo of winningCombos) {
+ const [a, bIndex, c] = combo;
+ if (b[a] && b[a] === b[bIndex] && b[a] === b[c]) {
+ b[a] += "w";
+ b[bIndex] += "w";
+ b[c] += "w";
+ return b[a];
+ }
}
+ return null;
+ }
- function move(i) {
- if (b[i] || gameOver) return;
-
- b[i] = turn;
- const winner = checkWin();
+ function checkDraw() {
+ return b.every((cell) => cell !== null);
+ }
- if (winner) {
- alert(`${winner} wins!`);
- scores[winner]++;
- gameOver = true;
- } else if (checkDraw()) {
- alert("It's a draw!");
- scores.Draw++;
- gameOver = true;
+ function render() {
+ boardEl.innerHTML = "";
+ b.forEach((v, i) => {
+ const btn = document.createElement("button");
+ if (v && v.includes("w")) {
+ v = v[0];
+ if (v === "X") {
+ document.querySelector(".one").classList.add("xWin");
+ btn.classList.add("xWin");
} else {
- turn = turn === 'X' ? 'O' : 'X';
- if (mode === 'AI' && turn === 'O' && !gameOver) aiMove();
+ document.querySelector(".two").classList.add("oWin");
+ btn.classList.add("oWin");
}
+ }
+ btn.classList.add("cell");
- updateScoreboard();
- render();
+ btn.textContent = v || "";
+ btn.addEventListener("click", () => move(i));
+ boardEl.appendChild(btn);
+ });
+ updateTurnIndicator();
+ }
+ function updateTurnIndicator() {
+ if (turn === "X") {
+ leftArrow.style.visibility = "visible";
+ rightArrow.style.visibility = "hidden";
+ } else {
+ leftArrow.style.visibility = "hidden";
+ rightArrow.style.visibility = "visible";
}
+ }
+ function move(i) {
+ if (b[i] || gameOver) return;
- // 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);
- }
+ b[i] = turn;
+ const winner = checkWin();
- 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;
+ if (winner) {
+ scores[winner[0]]++;
+ gameOver = true;
+ setTimeout(() => {
+ alert(`${winner[0]==='X'?"Player 1":"Player 2"} wins!`);
+ }, 200);
+ } else if (checkDraw()) {
+ scores.Draw++;
+ gameOver = true;
+ setTimeout(() => {
+ alert("It's a draw!");
+
+ }, 200);
+ } else {
+ turn = turn === "X" ? "O" : "X";
+
+ if (mode === "AI" && turn === "O" && !gameOver) aiMove();
}
+ render();
+ updateScoreboard();
+ }
- function updateScoreboard() {
- xScoreEl.textContent = scores.X;
- oScoreEl.textContent = scores.O;
- drawScoreEl.textContent = scores.Draw;
+ // 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 restart() {
- b = Array(9).fill(null);
- turn = 'X';
- gameOver = false;
- render();
+ 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;
+ }
- // 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; });
+ function updateScoreboard() {
+ xScoreEl.textContent = scores.X;
+ oScoreEl.textContent = scores.O;
+ drawScoreEl.textContent = scores.Draw;
+ }
- // Initial render
+ function restart() {
+ b = Array(9).fill(null);
+ turn = "X";
+ gameOver = false;
+ document.querySelector(".one").classList.remove("xWin")
+ document.querySelector(".two").classList.remove("oWin")
render();
- updateScoreboard();
+ }
+
+ // Event listeners
+ restartBtn.addEventListener("click", restart);
+ const choices = document.querySelector(".choices");
+ const playerMode = document.querySelector(".select-player");
+ const compMode = document.querySelector(".select-computer");
+ playerMode.addEventListener("click", (e) => {
+ mode = playerMode.getAttribute("value");
+ Currentmode.innerText="Player"
+ document.querySelector(".start-modal").style.display = "none";
+ restart();
+ });
+ compMode.addEventListener("click", (e) => {
+ mode = compMode.getAttribute("value");
+ Currentmode.innerText="Computer"
+
+ document.querySelector(".start-modal").style.display = "none";
+ restart();
+ });
+
+ const modeSettings = document.querySelector(".exit");
+ modeSettings.addEventListener("click", () => {
+ document.querySelector(".start-modal").style.display = "block";
+ });
+
+
+ // Initial render
+ render();
+ updateScoreboard();
});
+
+// theme toggle
+const themeToggle = document.getElementById("themeToggle");
+const body = document.body;
+
+const currentTheme = localStorage.getItem("theme") || "light";
+if (currentTheme === "dark") {
+ body.classList.add("dark-mode");
+}
+
+themeToggle.addEventListener("click", () => {
+ body.classList.toggle("dark-mode");
+
+ const theme = body.classList.contains("dark-mode") ? "dark" : "light";
+ localStorage.setItem("theme", theme);
+});
+
diff --git a/projects/tic-tac-toe/styles.css b/projects/tic-tac-toe/styles.css
index f4d29e0..ae0421c 100644
--- a/projects/tic-tac-toe/styles.css
+++ b/projects/tic-tac-toe/styles.css
@@ -1,59 +1,435 @@
+:root {
+ --primary: #1f1f2f;
+ --secondary: #4c495f;
+ --secondary: #4c495f;
+ --tertiary: #4c495f;
+ --hover: #87839f;
+ --player: #15151c;
+ --text: #f5f5ff;
+ --marker: #f59972;
+ --marker2: #d877aa;
+ --shadow: 0, 0, 0;
+}
body {
- font-family: Arial, sans-serif;
- background: #0f0f12;
- color: #eef1f8;
- margin: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 100vh;
+ background-color: #c7c7ea;
+ font-family: Arial, sans-serif;
+ color: #040404;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+}
+body.dark-mode {
+ font-family: Arial, sans-serif;
+ background: #1f1f2f;
+ color: #eef1f8;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
}
main {
- text-align: center;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
}
.board {
- display: grid;
- grid-template-columns: repeat(3, 100px);
- grid-template-rows: repeat(3, 100px);
- gap: 5px;
- margin: 20px auto;
+ display: grid;
+ grid-template-columns: repeat(3, 100px);
+ grid-template-rows: repeat(3, 100px);
+ gap: 8px;
+ margin: 20px auto;
}
.cell {
- background: #17171c;
- border: 2px solid #262631;
- border-radius: 8px;
- font-size: 3rem;
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
- user-select: none;
+ background: #4a495e;
+ border: 2px solid #262631;
+ border-radius: 2.5vw;
+ font-size: 4rem;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ user-select: none;
+ box-shadow: 0 1px 1px rgba(var(--shadow), 0.15),
+ 0 2px 2px rgba(var(--shadow), 0.15), 0 4px 4px rgba(var(--shadow), 0.15),
+ 0 8px 8px rgba(var(--shadow), 0.15);
+ transition: 0.2s;
+ color: var(--text);
}
-.controls, .scoreboard {
- display: flex;
- justify-content: center;
- gap: 10px;
- margin: 10px 0;
+.controls {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
}
-
-button, select {
- padding: 0.5rem 1rem;
- border-radius: 5px;
- border: none;
- cursor: pointer;
+.exit {
+ margin-top: 15px;
+ color: var(--hover);
+ transition: 0.2s;
+ cursor: pointer;
+}
+.exit:hover {
+ transform: scale(1.3) rotate(-360deg);
+}
+.scoreboard {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin: 10px 0;
}
-body.light {
- background: #eef1f8;
- color: #0f0f12;
+button,
+select {
+ border-radius: 5px;
+ border: none;
+ cursor: pointer;
}
body.light .cell {
- background: #ffffff;
- color: #0f0f12;
- border: 1px solid #ccc;
+ background: #4a495e;
+ color: #0f0f12;
+
+ border: 1px solid #ccc;
+}
+
+.HeadingContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 75px;
+ padding: 0 20px;
+ background-color: var(--secondary);
+ border-radius: 25px;
+ box-shadow: 0 1px 1px rgba(var(--shadow), 0.15),
+ 0 2px 2px rgba(var(--shadow), 0.15), 0 4px 4px rgba(var(--shadow), 0.15),
+ 0 8px 8px rgba(var(--shadow), 0.15);
+}
+.HeadingContainer h1 {
+ font-size: 38px;
+ font-weight: 800;
+ margin: 0;
+ text-align: center;
+ color: var(--text);
+ background-image: linear-gradient(
+ to right,
+ var(--marker) 30%,
+ var(--marker2) 100%
+ );
+ background-size: 100%;
+ background-clip: text;
+ -webkit-background-clip: text;
+ -moz-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ -moz-text-fill-color: transparent;
+ z-index: 1;
+}
+.cell:hover {
+ transform: scale(1.1);
+ background: var(--hover);
+}
+.xWin {
+ background: #ef927f;
+}
+.xWin:hover {
+ background: #ef927f;
+}
+.oWin {
+ background: #dd7f9f;
+}
+.oWin:hover {
+ background: #dd7f9f;
+}
+.player.oWin {
+ background: #dd7f9f;
+}
+.player.xWin {
+ background: #ef927f;
+}
+.players {
+ display: flex;
+ justify-content: center;
+
+ height: 35px;
+ line-height: 35px;
+ gap: 10px;
+}
+.player {
+ width: 100px;
+ height: 35px;
+ text-align: center;
+ background: #4a495e;
+ border-radius: 5px 25px 5px 25px;
+ cursor: pointer;
+}
+.player:hover {
+ transform: scale(1.1);
+}
+.material-icons {
+ font-family: "Material Icons";
+ font-weight: normal;
+ font-style: normal;
+ font-size: 30px;
+ line-height: 1;
+ letter-spacing: normal;
+ text-transform: none;
+ display: inline-block;
+ white-space: nowrap;
+ word-wrap: normal;
+ direction: ltr;
+ -webkit-font-feature-settings: "liga";
+ -webkit-font-smoothing: antialiased;
+ cursor: pointer;
+}
+.material-icons.md-36 {
+ font-size: 40px;
+}
+.orange-marker {
+ color: var(--marker);
+ margin-left: -10px;
+ transform: scale(0.5);
+ transform: translateY(-3px);
+}
+.pink-marker {
+ visibility: hidden;
+ color: var(--marker2);
+ margin-right: -10px;
+ transform: scale(0.5);
+ transform: translateY(-3px);
+}
+#restartBtn {
+ font-size: 1.3vw;
+ text-align: center;
+ font-weight: 600;
+ width: 80px;
+ height: 40px;
+ line-height: 40px;
+ border-radius: 30px;
+ transition: 0.2s;
+ color: var(--primary);
+ background-image: linear-gradient(
+ to right,
+ var(--marker) 0%,
+ var(--marker2) 51%,
+ var(--marker) 100%
+ );
+ background-size: 200% auto;
+}
+#restartBtn:hover {
+ transform: scale(1.05);
+ background-position: right center;
+ transition: 0.3s;
+}
+/* Theme toggle */
+.theme-toggle {
+ background: var(--card);
+ border: 2px solid var(--border, #262631);
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s ease;
+ position: fixed;
+ top: 1rem;
+ right: 1rem;
+ z-index: 1000;
+ color: var(--text);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.theme-toggle:hover {
+ transform: rotate(15deg) scale(1.1);
+ border-color: var(--accent);
+ box-shadow: 0 6px 16px rgba(110, 231, 183, 0.2);
+}
+
+.theme-toggle svg {
+ position: absolute;
+ transition: all 0.3s ease;
+}
+
+.theme-toggle .sun-icon {
+ opacity: 0;
+ transform: rotate(90deg);
+}
+
+.theme-toggle .moon-icon {
+ opacity: 1;
+ transform: rotate(0deg);
+}
+
+body:not(.dark-mode) .theme-toggle .sun-icon {
+ opacity: 1;
+ transform: rotate(0deg);
+}
+
+body:not(.dark-mode) .theme-toggle .moon-icon {
+ opacity: 0;
+ transform: rotate(-90deg);
+}
+/* start memu options */
+.start-modal {
+ display: none;
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ overflow: auto;
+ background-color: rgba(var(--shadow), 0.75);
+}
+.start-screen {
+ border: 1px solid var(--hover);
+ margin: 0 auto;
+ margin-top: 200px;
+ width: 310px;
+ height: 130px;
+ background-color: var(--primary);
+ border-radius: 20px;
+ padding-top: 23px;
+}
+.choices {
+ display: flex;
+ justify-content: center;
+ gap: 35px;
+ cursor: pointer;
+}
+.select-player {
+ box-shadow: 0 1px 1px rgba rgba(0, 0, 0, 0.15) #00000026 (var(--shadow), 0.15),
+ 0 2px 2px rgba(var(--shadow), 0.15), 0 4px 4px rgba(var(--shadow), 0.15),
+ 0 8px 8px rgba(var(--shadow), 0.15);
+}
+h3 {
+ display: block;
+ font-size: 1.17em;
+ font-weight: bold;
+ unicode-bidi: isolate;
+ /* height: 15px; */
+ font-size: 16px;
+ text-align: center;
+ color: var(--text);
+}
+.choices h3 {
+ background-color: var(--secondary);
+ width: 110px;
+ height: 40px;
+ line-height: 40px;
+ border-radius: 14px;
+ transition: 0.2s;
+}
+
+/* Responsiveness */
+
+/* Tablets and small laptops */
+@media (max-width: 900px) {
+ .board {
+ grid-template-columns: repeat(3, minmax(80px, 22vw));
+ }
+ .cell {
+ border-radius: 5vw;
+ font-size: clamp(1.8rem, 10vw, 3rem);
+ }
+}
+
+/* Phones */
+@media (max-width: 600px) {
+ .board {
+ grid-template-columns: repeat(3, minmax(60px, 28vw));
+ gap: 2vw;
+ }
+
+ .cell {
+ border-radius: 8vw;
+ font-size: clamp(1.8rem, 10vw, 3rem);
+ }
+
+ .HeadingContainer {
+ width: 90%;
+ padding: 0.5rem;
+ }
+
+ .theme-toggle {
+ top: 0.5rem;
+ right: 0.5rem;
+ }
+
+ .players {
+ gap: 1rem;
+ }
+
+ .start-screen {
+ width: 90%;
+ max-width: 300px;
+ }
+}
+
+/* 🌞 Light mode theme */
+body:not(.dark-mode) {
+ --primary: #ffffff;
+ --secondary: #e8e7f5;
+ --tertiary: #dcdaf0;
+ --hover: #c4c1e0;
+ --player: #f2f1fb;
+ --text: #1e1d2f;
+ --marker: #823013;
+ --marker2: #e6a5ca;
+ --shadow: 0, 0, 0;
+
+ background-color: #fafaff;
+ color: var(--text);
+}
+
+body:not(.dark-mode) .cell {
+ background: var(--secondary);
+ border: 2px solid #ccc;
+ color: var(--text);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+/* Light mode heading box */
+body:not(.dark-mode) .HeadingContainer {
+ background-color: var(--secondary);
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
+}
+
+/* Light mode player boxes */
+body:not(.dark-mode) .player {
+ background-color: var(--player);
+ color: var(--text);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+/* Light mode restart button */
+body:not(.dark-mode) #restartBtn {
+ color: #f9f9f9;
+ background-image: linear-gradient(
+ to right,
+ var(--marker) 0%,
+ var(--marker2) 51%,
+ var(--marker) 100%
+ );
+ background-size: 200% auto;
+}
+body:not(.dark-mode) .xWin {
+ background: #45c14f;
+}
+
+body:not(.dark-mode) .oWin {
+ background: #5b6bc9;
+}
+body:not(.dark-mode) .player.oWin {
+ background: #5b6bc9;
+}
+body:not(.dark-mode) .player.xWin {
+ background: #45c14f;
}