diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index 48e2f55..5ad7a75 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -3,6 +3,7 @@ import './root/root.ts' import './chat/chat.ts' import './login/login.ts' import './signin/signin.ts' +import './ttt/ttt.ts' // ---- Initial load ---- setTitle(""); diff --git a/frontend/src/pages/ttt/README.md b/frontend/src/pages/ttt/README.md new file mode 100644 index 0000000..73001a2 --- /dev/null +++ b/frontend/src/pages/ttt/README.md @@ -0,0 +1,3 @@ +# TO-DO +For now I am prohibited from working on the backend as per Maieul's request. +[ ] - Task for December 08 or 09: Tic-tac-toe should lock up once it's finished (i.e., draw or win) and print who won, how many turns and which row/col/diag the win happened in \ No newline at end of file diff --git a/frontend/src/pages/ttt/ttt.html b/frontend/src/pages/ttt/ttt.html new file mode 100644 index 0000000..48e7efa --- /dev/null +++ b/frontend/src/pages/ttt/ttt.html @@ -0,0 +1,13 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/pages/ttt/ttt.ts b/frontend/src/pages/ttt/ttt.ts new file mode 100644 index 0000000..e34856c --- /dev/null +++ b/frontend/src/pages/ttt/ttt.ts @@ -0,0 +1,137 @@ +import { addRoute, setTitle, type RouteHandlerReturn } from "@app/routing"; +import tttPage from "./ttt.html?raw"; +import { showError, showInfo, showSuccess } from "@app/toast"; + +type CellState = 'O' | 'X' | null + +class TTC { + private board: [ + CellState, CellState, CellState, + CellState, CellState, CellState, + CellState, CellState, CellState]; + private currentPlayer: 'O' | 'X'; + + constructor() { + this.board = [null,null,null,null,null,null,null,null,null]; + this.currentPlayer = 'X'; + } + + private changePlayer() + { + if (this.currentPlayer === 'X') + this.currentPlayer = 'O'; + else + this.currentPlayer = 'X'; + } + + private checkState(): 'winX' | 'winO' | 'draw' | 'ongoing' + { + const checkRow = (row: number): ('X' | 'O' | null) => { + if (this.board[row * 3] === null) + return null; + if (this.board[row * 3] === this.board[row * 3+1] && this.board[row + 1] === this.board[row * 3 + 2]) + return this.board[row * 3]; + return null; + } + + const checkCol = (col: number): ('X' | 'O' | null) => { + if (this.board[col] === null) return null; + + if (this.board[col] === this.board[col + 3] && this.board[col + 3] === this.board[col + 6]) + return this.board[col]; + return null; + } + + const checkDiag = (): ('X' | 'O' | null) => { + if (this.board[4] === null) return null + + if (this.board[0] === this.board[4] && this.board[4] === this.board[8]) + return this.board[4] + + if (this.board[2] === this.board[4] && this.board[4] === this.board[6]) + return this.board[4] + return null; + } + + + const row = (checkRow(0) ?? checkRow(1)) ?? checkRow(2); + const col = (checkCol(0) ?? checkCol(1)) ?? checkCol(2); + const diag = checkDiag(); + + if (row !== null) return `win${row}`; + if (col !== null) return `win${col}`; + if (diag !== null ) return `win${diag}`; + + if (this.board.filter(c => c === null).length === 0) + return 'draw'; + return 'ongoing'; + } + + public makeMove(idx: number): 'winX' | 'winO' | 'draw' | 'ongoing' | 'invalidMove' { + if (idx < 0 || idx >= this.board.length) + return 'invalidMove'; + if (this.board[idx] !== null) + return 'invalidMove'; + this.board[idx] = this.currentPlayer; + this.changePlayer(); + return this.checkState(); + } + + public getBoard(): [ + CellState, CellState, CellState, + CellState, CellState, CellState, + CellState, CellState, CellState] + { + return this.board; + } +} + + +async function handleTTT(): Promise +{ + let board = new TTC(); + + return { + html: tttPage, + postInsert: async (app) => { + let cells = app?.querySelectorAll(".ttt-grid-cell"); + + console.log(cells); + + cells?.forEach(function (c, idx) { + c.addEventListener('click', () => + { + const result = board.makeMove(idx); + switch(result) + { + case ('draw'): { + showInfo('Game is a draw'); + break; + } + case ('invalidMove'): { + showError('Move is invalid'); + break; + } + + case ('winX'): { + showSuccess('X won'); + break; + } + case ('winO'): { + showSuccess('O won'); + break; + } + } + + const board_state = board.getBoard(); + board_state.forEach( function (cell_state, cell_idx) { + cells[cell_idx].innerText = cell_state !== null ? cell_state : " "; + }) + }) + }) + } + } +} + + +addRoute('/ttt', handleTTT) \ No newline at end of file