t3core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +69 -0
  2. package/dist/eslint.config.d.mts +2 -0
  3. package/dist/eslint.config.mjs +86 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +5 -0
  6. package/dist/src/app.d.ts +1 -0
  7. package/dist/src/app.js +9 -0
  8. package/dist/src/core/Board.d.ts +34 -0
  9. package/dist/src/core/Board.js +51 -0
  10. package/dist/src/core/Game.d.ts +42 -0
  11. package/dist/src/core/Game.js +83 -0
  12. package/dist/src/core/constants/index.d.ts +2 -0
  13. package/dist/src/core/constants/index.js +14 -0
  14. package/dist/src/core/index.d.ts +30 -0
  15. package/dist/src/core/index.js +14 -0
  16. package/dist/src/core/tests/gameCore.test.d.ts +1 -0
  17. package/dist/src/core/tests/gameCore.test.js +50 -0
  18. package/dist/src/core/tests/getWinnerFromFields.test.d.ts +1 -0
  19. package/dist/src/core/tests/getWinnerFromFields.test.js +24 -0
  20. package/dist/src/core/types/Board.d.ts +9 -0
  21. package/dist/src/core/types/Board.js +2 -0
  22. package/dist/src/core/types/Game.d.ts +21 -0
  23. package/dist/src/core/types/Game.js +2 -0
  24. package/dist/src/core/types/Symbol.d.ts +3 -0
  25. package/dist/src/core/types/Symbol.js +2 -0
  26. package/dist/src/core/utils/getWinnerFromFields.d.ts +2 -0
  27. package/dist/src/core/utils/getWinnerFromFields.js +16 -0
  28. package/dist/src/ui/components/Header/Header.d.ts +1 -0
  29. package/dist/src/ui/components/Header/Header.js +20 -0
  30. package/dist/src/ui/components/Header/index.d.ts +1 -0
  31. package/dist/src/ui/components/Header/index.js +17 -0
  32. package/dist/src/ui/components/UserInput/UserInput.d.ts +2 -0
  33. package/dist/src/ui/components/UserInput/UserInput.js +20 -0
  34. package/dist/src/ui/components/UserInput/index.d.ts +1 -0
  35. package/dist/src/ui/components/UserInput/index.js +17 -0
  36. package/dist/src/ui/features/game/components/Board/Board.d.ts +1 -0
  37. package/dist/src/ui/features/game/components/Board/Board.js +21 -0
  38. package/dist/src/ui/features/game/components/Board/index.d.ts +1 -0
  39. package/dist/src/ui/features/game/components/Board/index.js +17 -0
  40. package/dist/src/ui/features/game/components/GameEntryMessage/GameEntryMessage.d.ts +1 -0
  41. package/dist/src/ui/features/game/components/GameEntryMessage/GameEntryMessage.js +22 -0
  42. package/dist/src/ui/features/game/components/GameEntryMessage/index.d.ts +1 -0
  43. package/dist/src/ui/features/game/components/GameEntryMessage/index.js +17 -0
  44. package/dist/src/ui/features/game/components/GameHeader/GameHeader.d.ts +1 -0
  45. package/dist/src/ui/features/game/components/GameHeader/GameHeader.js +17 -0
  46. package/dist/src/ui/features/game/components/GameHeader/index.d.ts +1 -0
  47. package/dist/src/ui/features/game/components/GameHeader/index.js +17 -0
  48. package/dist/src/ui/features/game/components/GameStatusMessage/GameStatusMessage.d.ts +1 -0
  49. package/dist/src/ui/features/game/components/GameStatusMessage/GameStatusMessage.js +28 -0
  50. package/dist/src/ui/features/game/components/GameStatusMessage/index.d.ts +1 -0
  51. package/dist/src/ui/features/game/components/GameStatusMessage/index.js +17 -0
  52. package/dist/src/ui/features/game/components/PlayerEntry/PlayerEntry.d.ts +1 -0
  53. package/dist/src/ui/features/game/components/PlayerEntry/PlayerEntry.js +37 -0
  54. package/dist/src/ui/features/game/components/PlayerEntry/index.d.ts +1 -0
  55. package/dist/src/ui/features/game/components/PlayerEntry/index.js +17 -0
  56. package/dist/src/ui/features/game/components/PlayerEntry/utils/getPlayerAnswer.d.ts +1 -0
  57. package/dist/src/ui/features/game/components/PlayerEntry/utils/getPlayerAnswer.js +34 -0
  58. package/dist/src/ui/features/game/components/PlayerEntry/utils/playAgain.d.ts +1 -0
  59. package/dist/src/ui/features/game/components/PlayerEntry/utils/playAgain.js +20 -0
  60. package/dist/src/ui/features/game/components/PlayerEntry/utils/validatePlayerEntry.d.ts +1 -0
  61. package/dist/src/ui/features/game/components/PlayerEntry/utils/validatePlayerEntry.js +18 -0
  62. package/dist/src/ui/features/game/services/gameSession.d.ts +3 -0
  63. package/dist/src/ui/features/game/services/gameSession.js +19 -0
  64. package/dist/src/ui/features/game/util/colorLabelSymbol.d.ts +2 -0
  65. package/dist/src/ui/features/game/util/colorLabelSymbol.js +15 -0
  66. package/dist/src/ui/features/menu/components/MenuEntry/MenuEntry.d.ts +1 -0
  67. package/dist/src/ui/features/menu/components/MenuEntry/MenuEntry.js +36 -0
  68. package/dist/src/ui/features/menu/components/MenuEntry/index.d.ts +1 -0
  69. package/dist/src/ui/features/menu/components/MenuEntry/index.js +17 -0
  70. package/dist/src/ui/features/menu/components/MenuHeader/MenuHeader.d.ts +1 -0
  71. package/dist/src/ui/features/menu/components/MenuHeader/MenuHeader.js +18 -0
  72. package/dist/src/ui/features/menu/components/MenuHeader/index.d.ts +1 -0
  73. package/dist/src/ui/features/menu/components/MenuHeader/index.js +17 -0
  74. package/dist/src/ui/features/menu/components/MenuOptions/MenuOptions.d.ts +1 -0
  75. package/dist/src/ui/features/menu/components/MenuOptions/MenuOptions.js +18 -0
  76. package/dist/src/ui/features/menu/components/MenuOptions/index.d.ts +1 -0
  77. package/dist/src/ui/features/menu/components/MenuOptions/index.js +17 -0
  78. package/dist/src/ui/features/menu/constants/menuItems.d.ts +4 -0
  79. package/dist/src/ui/features/menu/constants/menuItems.js +8 -0
  80. package/dist/src/ui/features/settings/components/SettingsEntry/SettingsEntry.d.ts +1 -0
  81. package/dist/src/ui/features/settings/components/SettingsEntry/SettingsEntry.js +42 -0
  82. package/dist/src/ui/features/settings/components/SettingsEntry/index.d.ts +1 -0
  83. package/dist/src/ui/features/settings/components/SettingsEntry/index.js +17 -0
  84. package/dist/src/ui/features/settings/components/SettingsHeader/SettingsHeader.d.ts +1 -0
  85. package/dist/src/ui/features/settings/components/SettingsHeader/SettingsHeader.js +18 -0
  86. package/dist/src/ui/features/settings/components/SettingsHeader/index.d.ts +1 -0
  87. package/dist/src/ui/features/settings/components/SettingsHeader/index.js +17 -0
  88. package/dist/src/ui/features/settings/components/SettingsOptions/SettingsOptions.d.ts +1 -0
  89. package/dist/src/ui/features/settings/components/SettingsOptions/SettingsOptions.js +34 -0
  90. package/dist/src/ui/features/settings/components/SettingsOptions/index.d.ts +1 -0
  91. package/dist/src/ui/features/settings/components/SettingsOptions/index.js +17 -0
  92. package/dist/src/ui/features/settings/constants/settingsOptions.d.ts +8 -0
  93. package/dist/src/ui/features/settings/constants/settingsOptions.js +7 -0
  94. package/dist/src/ui/global/settings.global.d.ts +12 -0
  95. package/dist/src/ui/global/settings.global.js +24 -0
  96. package/dist/src/ui/navigation/index.d.ts +3 -0
  97. package/dist/src/ui/navigation/index.js +27 -0
  98. package/dist/src/ui/navigation/routes.d.ts +6 -0
  99. package/dist/src/ui/navigation/routes.js +8 -0
  100. package/dist/src/ui/screens/Game.screen.d.ts +1 -0
  101. package/dist/src/ui/screens/Game.screen.js +20 -0
  102. package/dist/src/ui/screens/Menu.screen.d.ts +1 -0
  103. package/dist/src/ui/screens/Menu.screen.js +25 -0
  104. package/dist/src/ui/screens/Settings.screen.d.ts +1 -0
  105. package/dist/src/ui/screens/Settings.screen.js +16 -0
  106. package/dist/src/ui/utils/beepAndClear.d.ts +1 -0
  107. package/dist/src/ui/utils/beepAndClear.js +9 -0
  108. package/dist/src/ui/utils/beepSound.d.ts +1 -0
  109. package/dist/src/ui/utils/beepSound.js +10 -0
  110. package/dist/src/ui/utils/styledLabel.d.ts +9 -0
  111. package/dist/src/ui/utils/styledLabel.js +76 -0
  112. package/dist/vitest.config.d.mts +2 -0
  113. package/dist/vitest.config.mjs +6 -0
  114. package/package.json +59 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # TIC-TAC-TOE-CORE (t3c)
2
+
3
+ A reusable TypeScript core library for Tic Tac Toe games with a built-in CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install t3c
9
+ ```
10
+
11
+ ## Usage as a Library
12
+
13
+ ```typescript
14
+ import { Game } from 't3c';
15
+
16
+ // Create a game with default symbols 'O' and 'X'
17
+ const game = new Game();
18
+
19
+ // Make a move (field 1-9)
20
+ game.savePlayerSelection(5);
21
+
22
+ // Check game status
23
+ console.log(game.gameStatus); // { status: 'running' } | { status: 'win', winner: 'O' } | { status: 'draw' }
24
+ console.log(game.currentPlayer); // 'O' or 'X'
25
+
26
+ // Check if field is already selected
27
+ console.log(game.isFieldSelected(5)); // true
28
+
29
+ // Access the board
30
+ console.log(game.getBoard()); // [1, 2, 3, 4, 'O', 6, 7, 8, 9]
31
+
32
+ // Reset the game
33
+ game.reset();
34
+ ```
35
+
36
+ ### API
37
+
38
+ #### `Game`
39
+
40
+ | Property/Method | Description |
41
+ | --------------- | ------------------------------ |
42
+ | `constructor()` | Create a new game with default symbols `['O', 'X']` |
43
+ | `currentPlayer` | Get the current player's symbol |
44
+ | `gameStatus` | Get current game status |
45
+ | `getBoard()` | Returns the current board state as `(number \| PlayerSymbol)[]` |
46
+ | `savePlayerSelection(field: number)` | Place current player's symbol on field 1-9 |
47
+ | `isFieldSelected(field: number)` | Check if a field is already occupied |
48
+ | `reset()` | Reset the game to initial state |
49
+
50
+ ## CLI Usage
51
+
52
+ Run the interactive console game:
53
+
54
+ ```bash
55
+ npx tic-tac-toe-core
56
+ ```
57
+
58
+ ## Exports
59
+
60
+ ```typescript
61
+ // Core class
62
+ export { Game } from 't3c';
63
+
64
+ // Constants
65
+ export { DEFAULT_GAME_SYMBOLS } from 't3c';
66
+
67
+ // Types
68
+ export type { IGame, GameStatus, PlayerSymbol, PlayerSymbols } from 't3c';
69
+ ```
@@ -0,0 +1,2 @@
1
+ declare const _default: import("eslint/config").Config[];
2
+ export default _default;
@@ -0,0 +1,86 @@
1
+ import js from "@eslint/js";
2
+ import { importX } from "eslint-plugin-import-x";
3
+ import perfectionist from "eslint-plugin-perfectionist";
4
+ import { defineConfig } from "eslint/config";
5
+ import globals from "globals";
6
+ import path from "path";
7
+ import tseslint from "typescript-eslint";
8
+ import { fileURLToPath } from "url";
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ export default defineConfig([
12
+ {
13
+ ignores: ["dist/**", "node_modules/**"],
14
+ },
15
+ {
16
+ languageOptions: {
17
+ parserOptions: {
18
+ tsconfigRootDir: __dirname,
19
+ },
20
+ },
21
+ },
22
+ {
23
+ files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
24
+ plugins: { js },
25
+ extends: ["js/recommended"],
26
+ languageOptions: { globals: globals.browser },
27
+ },
28
+ tseslint.configs.recommended,
29
+ {
30
+ plugins: {
31
+ "import-x": importX,
32
+ perfectionist,
33
+ },
34
+ settings: {
35
+ "import-x/internal-regex": "^@/",
36
+ "import-x/resolver": {
37
+ typescript: true,
38
+ },
39
+ },
40
+ rules: {
41
+ "no-case-declarations": "off",
42
+ "@typescript-eslint/consistent-type-imports": [
43
+ "error",
44
+ {
45
+ prefer: "type-imports",
46
+ fixStyle: "separate-type-imports",
47
+ },
48
+ ],
49
+ "@typescript-eslint/no-explicit-any": "warn",
50
+ "@typescript-eslint/no-unused-vars": [
51
+ "error",
52
+ {
53
+ argsIgnorePattern: "^_",
54
+ varsIgnorePattern: "^_",
55
+ caughtErrorsIgnorePattern: "^_",
56
+ },
57
+ ],
58
+ "no-console": "off",
59
+ "perfectionist/sort-imports": [
60
+ "error",
61
+ {
62
+ type: "alphabetical",
63
+ order: "asc",
64
+ ignoreCase: true,
65
+ newlinesBetween: 1,
66
+ tsconfig: { rootDir: "." },
67
+ internalPattern: ["^@/"],
68
+ groups: [
69
+ "type-import",
70
+ ["value-builtin", "value-external"],
71
+ "value-internal",
72
+ ["value-parent", "value-sibling", "value-index"],
73
+ "side-effect",
74
+ "unknown",
75
+ ],
76
+ },
77
+ ],
78
+ "import-x/no-cycle": [
79
+ "error",
80
+ {
81
+ ignoreExternal: true,
82
+ },
83
+ ],
84
+ },
85
+ },
86
+ ]);
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const app_1 = require("./src/app");
5
+ (0, app_1.app)();
@@ -0,0 +1 @@
1
+ export declare const app: () => void;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.app = void 0;
4
+ const navigation_1 = require("./ui/navigation");
5
+ const routes_1 = require("./ui/navigation/routes");
6
+ const app = () => {
7
+ (0, navigation_1.navigateTo)(routes_1.ROUTES.MENU);
8
+ };
9
+ exports.app = app;
@@ -0,0 +1,34 @@
1
+ import type { IBoard } from "./types/Board";
2
+ import type { PlayerSymbol } from "./types/Symbol";
3
+ export declare const BOARD_SIZE = 9;
4
+ export declare class Board implements IBoard {
5
+ private fields;
6
+ /**
7
+ * Returns the current board state.
8
+ * @returns The current board state.
9
+ * @type {number[] | PlayerSymbol[]}
10
+ */
11
+ getFields(): (number | "O" | "X")[];
12
+ /**
13
+ * Returns the value of a field by its number.
14
+ * @param fieldNumber The field number (1-9) to get.
15
+ * @returns The value of the field.
16
+ * @type {number | TSymbol}
17
+ */
18
+ getFieldByNumber(fieldNumber: number): number | "O" | "X";
19
+ /**
20
+ * Checks if the board is full.
21
+ * @returns `true` if the board is full, `false` otherwise.
22
+ */
23
+ isFull(): boolean;
24
+ /**
25
+ * Sets a field's value by its number.
26
+ * @param fieldNumber The field number (1-9) to set.
27
+ * @param symbol The symbol to set.
28
+ */
29
+ setFieldByNumber(fieldNumber: number, symbol: PlayerSymbol): void;
30
+ /**
31
+ * Resets the board to its initial state.
32
+ */
33
+ reset(): void;
34
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Board = exports.BOARD_SIZE = void 0;
4
+ const fillFields = (_, idx) => idx + 1;
5
+ exports.BOARD_SIZE = 9;
6
+ class Board {
7
+ constructor() {
8
+ this.fields = new Array(exports.BOARD_SIZE)
9
+ .fill(0)
10
+ .map(fillFields);
11
+ }
12
+ /**
13
+ * Returns the current board state.
14
+ * @returns The current board state.
15
+ * @type {number[] | PlayerSymbol[]}
16
+ */
17
+ getFields() {
18
+ return this.fields;
19
+ }
20
+ /**
21
+ * Returns the value of a field by its number.
22
+ * @param fieldNumber The field number (1-9) to get.
23
+ * @returns The value of the field.
24
+ * @type {number | TSymbol}
25
+ */
26
+ getFieldByNumber(fieldNumber) {
27
+ return this.fields[fieldNumber - 1];
28
+ }
29
+ /**
30
+ * Checks if the board is full.
31
+ * @returns `true` if the board is full, `false` otherwise.
32
+ */
33
+ isFull() {
34
+ return this.fields.every((field) => typeof field === "string");
35
+ }
36
+ /**
37
+ * Sets a field's value by its number.
38
+ * @param fieldNumber The field number (1-9) to set.
39
+ * @param symbol The symbol to set.
40
+ */
41
+ setFieldByNumber(fieldNumber, symbol) {
42
+ this.fields[fieldNumber - 1] = symbol;
43
+ }
44
+ /**
45
+ * Resets the board to its initial state.
46
+ */
47
+ reset() {
48
+ this.fields = new Array(exports.BOARD_SIZE).fill(0).map(fillFields);
49
+ }
50
+ }
51
+ exports.Board = Board;
@@ -0,0 +1,42 @@
1
+ import type { GameStatus, IGame } from "./types/Game";
2
+ import type { PlayerSymbol } from "./types/Symbol";
3
+ export declare class Game implements IGame {
4
+ private _currentPlayer;
5
+ private _gameStatus;
6
+ private _symbols;
7
+ private _board;
8
+ private _togglePlayer;
9
+ private _updateGameStatus;
10
+ /**
11
+ * Returns the current player.
12
+ * @returns The current player.
13
+ */
14
+ get currentPlayer(): PlayerSymbol;
15
+ /**
16
+ * Returns the current game status.
17
+ * @returns The current game status.
18
+ * @type {GameStatus<PlayerSymbol>}
19
+ */
20
+ get gameStatus(): GameStatus;
21
+ /**
22
+ * Returns the current board state.
23
+ * @returns The current board state.
24
+ * @type {(number | PlayerSymbol)[]}
25
+ */
26
+ getBoard(): (number | "O" | "X")[];
27
+ /**
28
+ * Checks if a field is already selected by a player.
29
+ * @param field The field number to check.
30
+ * @returns `true` if the field is selected, `false` otherwise.
31
+ */
32
+ isFieldSelected(field: number): boolean;
33
+ /**
34
+ * Saves a player's selection on the board.
35
+ * @param field The field number to mark.
36
+ */
37
+ savePlayerSelection(field: number): void;
38
+ /**
39
+ * Resets the game to its initial state.
40
+ */
41
+ reset(): void;
42
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Game = void 0;
4
+ const Board_1 = require("./Board");
5
+ const constants_1 = require("./constants");
6
+ const getWinnerFromFields_1 = require("./utils/getWinnerFromFields");
7
+ class Game {
8
+ constructor() {
9
+ this._currentPlayer = constants_1.DEFAULT_GAME_SYMBOLS[0];
10
+ this._gameStatus = { status: "running" };
11
+ this._symbols = constants_1.DEFAULT_GAME_SYMBOLS;
12
+ this._board = new Board_1.Board();
13
+ }
14
+ _togglePlayer() {
15
+ this._currentPlayer =
16
+ this._currentPlayer === this._symbols[0]
17
+ ? this._symbols[1]
18
+ : this._symbols[0];
19
+ }
20
+ _updateGameStatus() {
21
+ const board = this._board;
22
+ const winner = (0, getWinnerFromFields_1.getWinnerFromFields)(board.getFields());
23
+ const isDraw = board.isFull() && !winner;
24
+ if (winner) {
25
+ this._gameStatus = { status: "win", winner };
26
+ return;
27
+ }
28
+ if (isDraw) {
29
+ this._gameStatus = { status: "draw" };
30
+ return;
31
+ }
32
+ this._gameStatus = { status: "running" };
33
+ }
34
+ /**
35
+ * Returns the current player.
36
+ * @returns The current player.
37
+ */
38
+ get currentPlayer() {
39
+ return this._currentPlayer;
40
+ }
41
+ /**
42
+ * Returns the current game status.
43
+ * @returns The current game status.
44
+ * @type {GameStatus<PlayerSymbol>}
45
+ */
46
+ get gameStatus() {
47
+ return this._gameStatus;
48
+ }
49
+ /**
50
+ * Returns the current board state.
51
+ * @returns The current board state.
52
+ * @type {(number | PlayerSymbol)[]}
53
+ */
54
+ getBoard() {
55
+ return this._board.getFields();
56
+ }
57
+ /**
58
+ * Checks if a field is already selected by a player.
59
+ * @param field The field number to check.
60
+ * @returns `true` if the field is selected, `false` otherwise.
61
+ */
62
+ isFieldSelected(field) {
63
+ return typeof this._board.getFieldByNumber(field) === "string";
64
+ }
65
+ /**
66
+ * Saves a player's selection on the board.
67
+ * @param field The field number to mark.
68
+ */
69
+ savePlayerSelection(field) {
70
+ this._board.setFieldByNumber(field, this._currentPlayer);
71
+ this._togglePlayer();
72
+ this._updateGameStatus();
73
+ }
74
+ /**
75
+ * Resets the game to its initial state.
76
+ */
77
+ reset() {
78
+ this._gameStatus = { status: "running" };
79
+ this._currentPlayer = this._symbols[0];
80
+ this._board.reset();
81
+ }
82
+ }
83
+ exports.Game = Game;
@@ -0,0 +1,2 @@
1
+ export declare const WINNING_COMBINATIONS_INDEXES: number[][];
2
+ export declare const DEFAULT_GAME_SYMBOLS: readonly ["O", "X"];
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_GAME_SYMBOLS = exports.WINNING_COMBINATIONS_INDEXES = void 0;
4
+ exports.WINNING_COMBINATIONS_INDEXES = [
5
+ [0, 1, 2],
6
+ [3, 4, 5],
7
+ [6, 7, 8],
8
+ [0, 3, 6],
9
+ [1, 4, 7],
10
+ [2, 5, 8],
11
+ [0, 4, 8],
12
+ [2, 4, 6],
13
+ ];
14
+ exports.DEFAULT_GAME_SYMBOLS = ["O", "X"];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Core game logic for Tic Tac Toe.
3
+ * Manages player turns, board state, win/draw detection, and game lifecycle.
4
+ */
5
+ export { Game } from "./Game";
6
+ /**
7
+ * Default player symbols ['O', 'X'] for convenience.
8
+ */
9
+ export { DEFAULT_GAME_SYMBOLS } from "./constants";
10
+ /**
11
+ * Interface describing the Game class contract.
12
+ */
13
+ export type { IGame } from "./types/Game";
14
+ /**
15
+ * Union type representing possible game states:
16
+ * - `{ status: 'running' }` - Game in progress
17
+ * - `{ status: 'win', winner: TSymbol }` - A player won
18
+ * - `{ status: 'draw' }` - Board full, no winner
19
+ */
20
+ export type { GameStatus } from "./types/Game";
21
+ /**
22
+ * Tuple type for player symbols: `[symbol1, symbol2]`.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * type Symbols = ['O', 'X']; // PlayerSymbols
27
+ * type Symbol = 'O' | 'X'; // PlayerSymbol
28
+ * ```
29
+ */
30
+ export type { PlayerSymbols, PlayerSymbol } from "./types/Symbol";
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_GAME_SYMBOLS = exports.Game = void 0;
4
+ /**
5
+ * Core game logic for Tic Tac Toe.
6
+ * Manages player turns, board state, win/draw detection, and game lifecycle.
7
+ */
8
+ var Game_1 = require("./Game");
9
+ Object.defineProperty(exports, "Game", { enumerable: true, get: function () { return Game_1.Game; } });
10
+ /**
11
+ * Default player symbols ['O', 'X'] for convenience.
12
+ */
13
+ var constants_1 = require("./constants");
14
+ Object.defineProperty(exports, "DEFAULT_GAME_SYMBOLS", { enumerable: true, get: function () { return constants_1.DEFAULT_GAME_SYMBOLS; } });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const Game_1 = require("../Game");
5
+ /*
6
+ O X O
7
+ O X X
8
+ X O O
9
+ */
10
+ (0, vitest_1.test)("Check if the game ends in a draw", () => {
11
+ const game = new Game_1.Game();
12
+ game.savePlayerSelection(1);
13
+ game.savePlayerSelection(2);
14
+ game.savePlayerSelection(3);
15
+ game.savePlayerSelection(5);
16
+ game.savePlayerSelection(4);
17
+ game.savePlayerSelection(6);
18
+ game.savePlayerSelection(8);
19
+ game.savePlayerSelection(7);
20
+ game.savePlayerSelection(9);
21
+ (0, vitest_1.expect)(game.gameStatus).toEqual({ status: "draw" });
22
+ });
23
+ (0, vitest_1.test)("Reset the game", () => {
24
+ const game = new Game_1.Game();
25
+ game.savePlayerSelection(5);
26
+ game.savePlayerSelection(1);
27
+ game.savePlayerSelection(2);
28
+ game.savePlayerSelection(3);
29
+ game.savePlayerSelection(8);
30
+ game.reset();
31
+ (0, vitest_1.expect)(game.getBoard()).toEqual(new Array(9).fill(0).map((_, idx) => idx + 1));
32
+ (0, vitest_1.expect)(game.gameStatus.status).toBe("running");
33
+ (0, vitest_1.expect)(game.currentPlayer).toBe("O");
34
+ });
35
+ /*
36
+ X O X
37
+ 4 O 6
38
+ 7 O 9
39
+ */
40
+ (0, vitest_1.test)("Check if the game is won", () => {
41
+ const game = new Game_1.Game();
42
+ game.savePlayerSelection(5);
43
+ game.savePlayerSelection(1);
44
+ game.savePlayerSelection(2);
45
+ game.savePlayerSelection(3);
46
+ game.savePlayerSelection(8);
47
+ (0, vitest_1.expect)(game.gameStatus).toEqual({ status: "win", winner: "O" });
48
+ (0, vitest_1.expect)(game.isFieldSelected(5)).toBe(true);
49
+ (0, vitest_1.expect)(game.isFieldSelected(9)).toBe(false);
50
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const getWinnerFromFields_1 = require("../utils/getWinnerFromFields");
5
+ (0, vitest_1.test)("getWinnerFromFields: top row all X wins", () => {
6
+ const fields = ["X", "X", "X", 4, 5, 6, 7, 8, 9];
7
+ (0, vitest_1.expect)((0, getWinnerFromFields_1.getWinnerFromFields)(fields)).toBe("X");
8
+ });
9
+ (0, vitest_1.test)("getWinnerFromFields: middle column all O wins", () => {
10
+ const fields = [1, "O", 3, 4, "O", 6, 7, "O", 9];
11
+ (0, vitest_1.expect)((0, getWinnerFromFields_1.getWinnerFromFields)(fields)).toBe("O");
12
+ });
13
+ (0, vitest_1.test)("getWinnerFromFields: diagonal 0-4-8 wins", () => {
14
+ const fields = ["X", 2, 3, 4, "X", 6, 7, 8, "X"];
15
+ (0, vitest_1.expect)((0, getWinnerFromFields_1.getWinnerFromFields)(fields)).toBe("X");
16
+ });
17
+ (0, vitest_1.test)("getWinnerFromFields: fresh labels — no winner", () => {
18
+ const fields = [1, 2, 3, 4, 5, 6, 7, 8, 9];
19
+ (0, vitest_1.expect)((0, getWinnerFromFields_1.getWinnerFromFields)(fields)).toBe(null);
20
+ });
21
+ (0, vitest_1.test)("getWinnerFromFields: mixed line — no winner", () => {
22
+ const fields = ["X", "O", "X", 4, 5, 6, 7, 8, 9];
23
+ (0, vitest_1.expect)((0, getWinnerFromFields_1.getWinnerFromFields)(fields)).toBe(null);
24
+ });
@@ -0,0 +1,9 @@
1
+ import type { PlayerSymbol } from "./Symbol";
2
+ export interface IBoard {
3
+ /** Cell value for field **1–9** (grid label shown to the player). */
4
+ getFieldByNumber: (fieldNumber: number) => number | PlayerSymbol;
5
+ setFieldByNumber: (fieldNumber: number, symbol: PlayerSymbol) => void;
6
+ getFields: () => (number | PlayerSymbol)[];
7
+ isFull: () => boolean;
8
+ reset: () => void;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,21 @@
1
+ import type { PlayerSymbol } from "./Symbol";
2
+ type GameStatusWin = {
3
+ status: "win";
4
+ winner: PlayerSymbol;
5
+ };
6
+ type GameStatusDraw = {
7
+ status: "draw";
8
+ };
9
+ type GameStatusRunning = {
10
+ status: "running";
11
+ };
12
+ export type GameStatus = GameStatusWin | GameStatusDraw | GameStatusRunning;
13
+ export interface IGame {
14
+ readonly gameStatus: GameStatus;
15
+ readonly currentPlayer: PlayerSymbol;
16
+ savePlayerSelection: (field: number) => void;
17
+ reset: () => void;
18
+ isFieldSelected: (field: number) => boolean;
19
+ getBoard: () => (number | PlayerSymbol)[];
20
+ }
21
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import type { DEFAULT_GAME_SYMBOLS } from "../constants";
2
+ export type PlayerSymbols = typeof DEFAULT_GAME_SYMBOLS;
3
+ export type PlayerSymbol = PlayerSymbols[number];
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ /** Winner from a 9-cell row-major slice matching {@link Board} (`number` = empty slot label). */
2
+ export declare function getWinnerFromFields<TSymbol extends string>(fields: readonly (number | TSymbol)[]): TSymbol | null;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWinnerFromFields = getWinnerFromFields;
4
+ const constants_1 = require("../constants");
5
+ /** Winner from a 9-cell row-major slice matching {@link Board} (`number` = empty slot label). */
6
+ function getWinnerFromFields(fields) {
7
+ for (const combination of constants_1.WINNING_COMBINATIONS_INDEXES) {
8
+ const firstIdx = combination[0];
9
+ const field = fields[firstIdx];
10
+ const isWinningCombination = combination.every((i) => fields[i] === field);
11
+ if (typeof field === "string" && isWinningCombination) {
12
+ return field;
13
+ }
14
+ }
15
+ return null;
16
+ }
@@ -0,0 +1 @@
1
+ export declare const Header: () => void;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Header = void 0;
4
+ const styledLabel_1 = require("../../../ui/utils/styledLabel");
5
+ const borderStyle = { color: "red" };
6
+ const Header = () => {
7
+ console.log(`\t`);
8
+ const gameName = (0, styledLabel_1.styledLabel)("Tic Tac Toe Game", {
9
+ color: "yellow",
10
+ textStyle: "bold",
11
+ });
12
+ const border = (0, styledLabel_1.styledLabel)("============================================", borderStyle);
13
+ const sideBorder = (0, styledLabel_1.styledLabel)("|", borderStyle);
14
+ const headerLabel = (0, styledLabel_1.styledLabel)(`${border}
15
+ ${sideBorder} ${gameName} ${sideBorder}
16
+ ${border}`);
17
+ console.log(headerLabel);
18
+ console.log("\t");
19
+ };
20
+ exports.Header = Header;
@@ -0,0 +1 @@
1
+ export * from "./Header";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Header"), exports);
@@ -0,0 +1,2 @@
1
+ export declare const UserInput: (query: string) => Promise<unknown>;
2
+ export declare const closeInput: () => void;