Initial commit
This commit is contained in:
+152
@@ -0,0 +1,152 @@
|
||||
<title>Tic Tac Toe</title>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
<style>
|
||||
body,
|
||||
td,
|
||||
input {
|
||||
font-size: 24px;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
td {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
table {
|
||||
margin: 5rem auto;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body,
|
||||
input {
|
||||
background: #222;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Tic Tac Toe</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td><input type="checkbox" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2 hidden>Game over</h2>
|
||||
|
||||
<script>
|
||||
// Initialization
|
||||
const boxes = Array.from(document.querySelectorAll("td"));
|
||||
const inputs = Array.from(document.querySelectorAll("[type=checkbox]"));
|
||||
const rows = [1, 2, 3].map((n) =>
|
||||
Array.from(document.querySelectorAll(`tr:nth-child(${n}) td`))
|
||||
);
|
||||
const columns = [1, 2, 3].map((n) =>
|
||||
Array.from(document.querySelectorAll(`td:nth-child(${n})`))
|
||||
);
|
||||
const diagonals = [
|
||||
[
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
],
|
||||
[
|
||||
[3, 1],
|
||||
[2, 2],
|
||||
[1, 3],
|
||||
],
|
||||
].map((set) =>
|
||||
set.map(([x, y]) =>
|
||||
document.querySelector(`tr:nth-child(${x}) td:nth-child(${y})`)
|
||||
)
|
||||
);
|
||||
const gameOver = document.querySelector("h2");
|
||||
const HUMAN = "🧑💻";
|
||||
const ROBOT = "🤖";
|
||||
|
||||
// Decide who goes first
|
||||
if (Math.random() > 0.5) {
|
||||
runRobotTurn();
|
||||
}
|
||||
|
||||
// Handle when a user clicks an input
|
||||
for (const input of inputs) {
|
||||
input.addEventListener("click", handleClickInput);
|
||||
}
|
||||
|
||||
// The user chose a box, check for win, robot turn, check for win
|
||||
function handleClickInput(evt) {
|
||||
evt.target.closest("td").innerHTML = HUMAN;
|
||||
if (hasWin()) {
|
||||
return endGame();
|
||||
}
|
||||
runRobotTurn();
|
||||
if (hasWin()) {
|
||||
return endGame();
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a random available box
|
||||
function runRobotTurn() {
|
||||
const robotBoxes = shuffle(boxes);
|
||||
while (robotBoxes.length) {
|
||||
const box = robotBoxes.shift();
|
||||
if (box.querySelector("input")) {
|
||||
box.innerHTML = ROBOT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine win
|
||||
function hasWin() {
|
||||
return (
|
||||
rows.some(hasAllSame) ||
|
||||
columns.some(hasAllSame) ||
|
||||
diagonals.some(hasAllSame)
|
||||
);
|
||||
}
|
||||
|
||||
// Determine if every cell matches
|
||||
function hasAllSame(arr) {
|
||||
return arr.every(
|
||||
(td) => !td.querySelector("input") && td.innerHTML === arr[0].innerHTML
|
||||
);
|
||||
}
|
||||
|
||||
// Display game over, cancel events
|
||||
function endGame() {
|
||||
gameOver.hidden = false;
|
||||
for (const input of inputs) {
|
||||
input.removeEventListener("click", handleClickInput);
|
||||
input.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle a copy of the input array
|
||||
function shuffle(array) {
|
||||
array = array.slice(0);
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * i);
|
||||
const temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user