aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Goetz <dev@leogtz.de>2026-03-21 16:16:36 +0100
committerLeo Goetz <dev@leogtz.de>2026-03-21 16:16:36 +0100
commit82969cac5d6f9626bef681ef7d5cea60ed686eb0 (patch)
treee5a110d2d6de0e47f65802d073f570ca4d5aacef
inital commitHEADmaster
-rw-r--r--LICENSE21
-rw-r--r--README.md14
-rw-r--r--git-daemon-export-ok0
-rw-r--r--index.js3
-rw-r--r--package.json13
-rw-r--r--src/game.js118
-rw-r--r--src/utils.js14
7 files changed, 183 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..22a53b5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Leo Goetz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..44f95da
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+Tic Tac Toe
+---
+
+Like the name says. It is a little Tic Tac Toe game you can play in your
+terminal via node.js
+
+Just clone the repo, go in its directory and type:
+```bash
+npm run start
+// or
+node index.js
+```
+
+Enjoy :)
diff --git a/git-daemon-export-ok b/git-daemon-export-ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/git-daemon-export-ok
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..df235b2
--- /dev/null
+++ b/index.js
@@ -0,0 +1,3 @@
+import start from "./src/game.js";
+
+await start();
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..9cb3d75
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "tic-tac-toe",
+ "version": "1.0.0",
+ "description": "a terminal tic tac toe game",
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "keywords": [],
+ "author": "Leo Goetz",
+ "license": "MIT",
+ "type": "module"
+}
diff --git a/src/game.js b/src/game.js
new file mode 100644
index 0000000..0820f8c
--- /dev/null
+++ b/src/game.js
@@ -0,0 +1,118 @@
+import { getInput } from "./utils.js";
+
+let game = {
+ player1: {
+ symbol: "X",
+ name: undefined,
+ },
+ player2: {
+ symbol: "O",
+ name: undefined,
+ },
+ turn: "player1",
+ state: "setup",
+ board: [" ", " ", " ", " ", " ", " ", " ", " ", " "],
+};
+
+const winningPositions = [
+ [0, 1, 2],
+ [0, 3, 6],
+ [0, 4, 8],
+ [1, 4, 7],
+ [2, 5, 8],
+ [2, 4, 6],
+ [3, 4, 5],
+ [6, 7, 8],
+];
+
+function checkForWin() {
+ let winner = "";
+ for (let position of winningPositions) {
+ if (
+ // ignore field empty
+ game.board[position[0]] != " " &&
+ game.board[position[1]] != " " &&
+ game.board[position[2]] != " " &&
+ // check if three in a row
+ game.board[position[0]] == game.board[position[1]] &&
+ game.board[position[1]] == game.board[position[2]]
+ ) {
+ winner =
+ game.player1.symbol == game.board[position[0]]
+ ? game.player1.name
+ : game.player2.name;
+ }
+ }
+ return winner;
+}
+
+async function gameLoop() {
+ while (game.state == "running") {
+ outputBoard();
+
+ let winner = checkForWin();
+ if (winner) {
+ console.log(`${winner} has won!`);
+ game.state = "finish";
+ return;
+ }
+
+ await questionLoop();
+ }
+
+ async function questionLoop() {
+ let playerMove = await getInput(
+ `Where would you want to place (${game[game.turn].name})?: `,
+ );
+
+ while (game.board[playerMove - 1] != " ") {
+ console.log("\nThere is already a Symbol. Try again.");
+ playerMove = await getInput(
+ `Where would you want to place (${game[game.turn].name})?: `,
+ );
+ }
+
+ if (game.turn == "player2") {
+ move(game.player2, playerMove);
+ game.turn = "player1";
+ } else {
+ move(game.player1, playerMove);
+ game.turn = "player2";
+ }
+ }
+}
+
+async function setupGame() {
+ console.log("Let's Start a Game of Tic Tac Toe! \n");
+ game.player1.name = await getInput("Name of Player 1: ");
+ game.player2.name = await getInput("Name of Player 2: ");
+ game.state = "running";
+}
+
+function move(player, position) {
+ game.board[position - 1] = player.symbol;
+}
+
+export default async function start() {
+ if (game.state == "setup") {
+ await setupGame();
+ }
+
+ if (game.state == "running") {
+ await gameLoop();
+ }
+
+ if (game.state == "finish") {
+ process.exit();
+ }
+}
+
+function outputBoard() {
+ console.log("\n");
+ console.log(`${game.board[0]} | ${game.board[1]} | ${game.board[2]}`);
+ console.log("---------");
+ console.log(`${game.board[3]} | ${game.board[4]} | ${game.board[5]}`);
+ console.log("---------");
+ console.log(`${game.board[6]} | ${game.board[7]} | ${game.board[8]}`);
+ console.log("\n");
+}
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..ecb2c4a
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,14 @@
+import readline from "readline";
+
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+});
+
+export function getInput(question) {
+ return new Promise((resolve) => {
+ rl.question(question, (userInput) => {
+ resolve(userInput);
+ });
+ });
+}