diff --git a/React/index.html b/React/index.html index cae99fe..75551e4 100644 --- a/React/index.html +++ b/React/index.html @@ -4,6 +4,11 @@ + + + dooz (Tic Tac Toe) React diff --git a/React/src/app/App.tsx b/React/src/app/App.tsx index 9f0906b..e4946e2 100644 --- a/React/src/app/App.tsx +++ b/React/src/app/App.tsx @@ -1,11 +1,26 @@ +import { createContext, useState } from "react"; import classes from "./App.module.scss"; import { AppRouter } from "./AppRouter"; +interface IContextValue { + selectedSize: number; + setSelectedSize: React.Dispatch> +} + +export const boardSizeContext = createContext({ selectedSize: 3, setSelectedSize: () => { } }); + function App() { + const [selectedSize, setSelectedSize] = useState(3); + const contextValue = { + selectedSize, + setSelectedSize + } return ( -
- -
+ +
+ +
+
); } diff --git a/React/src/app/pages/game/PlayerVsBotPage.module.scss b/React/src/app/pages/game/PlayerVsBotPage.module.scss index 6037a56..9893101 100644 --- a/React/src/app/pages/game/PlayerVsBotPage.module.scss +++ b/React/src/app/pages/game/PlayerVsBotPage.module.scss @@ -10,15 +10,6 @@ background-repeat: no-repeat; } -.header { - display: flex; - gap: 16px; - margin-top: 36px; - align-items: center; - font-family: Arial, Helvetica, sans-serif; - color: white; -} - .body { display: flex; justify-content: center; diff --git a/React/src/app/pages/game/PlayerVsBotPage.tsx b/React/src/app/pages/game/PlayerVsBotPage.tsx index 98daa93..d26bd90 100644 --- a/React/src/app/pages/game/PlayerVsBotPage.tsx +++ b/React/src/app/pages/game/PlayerVsBotPage.tsx @@ -1,31 +1,39 @@ -import { useState, useRef, useEffect } from "react"; +import { useState, useRef, useEffect, useContext } from "react"; import { checkBoard } from "~/utils/check-board"; import { P1, P2, generateRandomTurn } from "~/utils/players"; import { Footer } from "./components/Footer"; -import { Header } from "./components/Header"; +import { GamePageHeader } from "./components/Header"; import { ResultModal } from "./components/Modal/ResultModal"; import { GameMode } from "~/utils/game-mode"; import classes from "./PlayerVsBotPage.module.scss"; import { anyMovesLeft } from "~/utils/any-moves-left"; import { findBestBotMove } from "~/utils/find-best-bot-move"; import { Board } from "./components/Board"; -const INITIAL_BOARD = Array(3) - .fill(null) - .map(() => Array(3).fill(null)); +import { boardSizeContext } from "~/App"; +import { createBoard } from "~/utils/create-board"; export function PlayerVsBotPage() { - const [boardData, setBoardData] = useState(structuredClone(INITIAL_BOARD)); + const { selectedSize } = useContext(boardSizeContext); + const [boardData, setBoardData] = useState(createBoard(selectedSize)); const [currentPlayer, setCurrentPlayer] = useState(generateRandomTurn()); const [winResult, setWinResult] = useState(); const [showModal, setShowModal] = useState(false); const isFirstMove = useRef(true); useEffect(() => { - if (isFirstMove.current && currentPlayer === P2) { + if (!isFirstMove.current) { + refreshBoard(); + } + }, [selectedSize]) + + useEffect(() => { + if (currentPlayer === P2 && !winResult) { handleBot(); } isFirstMove.current = false; - }, [isFirstMove.current]) + }, [currentPlayer]); + + const handleClick = (i: number, j: number) => { if (boardData[i][j] || winResult) return; @@ -38,22 +46,22 @@ export function PlayerVsBotPage() { return; } setCurrentPlayer(P2); - handleBot(); } }; - const handleBot = () => { - - let movement = findBestBotMove(boardData) - if (movement) { - boardData[movement.i][movement.j] = P2; - } - setBoardData([...boardData]); - if (checkWinner(P2)) return; - if (anyMovesLeft(boardData)) { - setShowModal(true); - } - setCurrentPlayer(P1); + const handleBot = async () => { + setTimeout(() => { + let bestBotMove = findBestBotMove(boardData); + if (bestBotMove) { + boardData[bestBotMove.i][bestBotMove.j] = P2; + } + setBoardData([...boardData]); + if (checkWinner(P2)) return; + if (anyMovesLeft(boardData)) { + setShowModal(true); + } + setCurrentPlayer(P1); + }, 500); }; const checkWinner = (currentPlayer: string) => { @@ -65,25 +73,20 @@ export function PlayerVsBotPage() { }; const refreshBoard = () => { - let newRandomTurnGenerator = generateRandomTurn(); - setBoardData(structuredClone(INITIAL_BOARD)); - setCurrentPlayer(newRandomTurnGenerator); - if (newRandomTurnGenerator === P2) { - isFirstMove.current = true; - } + setBoardData(createBoard(selectedSize)); + let cp = generateRandomTurn(); + setCurrentPlayer(cp); + isFirstMove.current = true; setWinResult(undefined); setShowModal(false); } return (
-
-
-
- +
- {showModal && } -
diff --git a/React/src/app/pages/game/PlayerVsPlayerPage.module.scss b/React/src/app/pages/game/PlayerVsPlayerPage.module.scss index 6037a56..9893101 100644 --- a/React/src/app/pages/game/PlayerVsPlayerPage.module.scss +++ b/React/src/app/pages/game/PlayerVsPlayerPage.module.scss @@ -10,15 +10,6 @@ background-repeat: no-repeat; } -.header { - display: flex; - gap: 16px; - margin-top: 36px; - align-items: center; - font-family: Arial, Helvetica, sans-serif; - color: white; -} - .body { display: flex; justify-content: center; diff --git a/React/src/app/pages/game/PlayerVsPlayerPage.tsx b/React/src/app/pages/game/PlayerVsPlayerPage.tsx index d8711c7..f48c5c0 100644 --- a/React/src/app/pages/game/PlayerVsPlayerPage.tsx +++ b/React/src/app/pages/game/PlayerVsPlayerPage.tsx @@ -1,24 +1,27 @@ -import { useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { checkBoard } from "~/utils/check-board"; import { P1, P2, generateRandomTurn } from "~/utils/players"; import { Board } from "./components/Board"; import { Footer } from "./components/Footer"; -import { Header } from "./components/Header"; +import { GamePageHeader } from "./components/Header"; import { ResultModal } from "./components/Modal/ResultModal"; import { GameMode } from "~/utils/game-mode"; import classes from "./PlayerVsPlayerPage.module.scss"; import { anyMovesLeft } from "~/utils/any-moves-left"; - -const INITIAL_BOARD = Array(3) - .fill(null) - .map(() => Array(3).fill(null)); +import { boardSizeContext } from "~/App"; +import { createBoard } from "~/utils/create-board"; export function PlayerVsPlayerPage() { - const [boardData, setBoardData] = useState(structuredClone(INITIAL_BOARD)); + const { selectedSize } = useContext(boardSizeContext); + const [boardData, setBoardData] = useState(createBoard(selectedSize)); const [currentPlayer, setCurrentPlayer] = useState(generateRandomTurn()); const [winResult, setWinResult] = useState(); const [showModal, setShowModal] = useState(false); + useEffect(() => { + refreshBoard(); + }, [selectedSize]); + const handleClick = (i: number, j: number) => { if (boardData[i][j] || winResult) return; boardData[i][j] = currentPlayer; @@ -36,13 +39,13 @@ export function PlayerVsPlayerPage() { const checkWinner = () => { const result = checkBoard(boardData, currentPlayer); if (!result) return false; - setWinResult(winResult); + setWinResult(result); setShowModal(true); return true; }; const refreshBoard = () => { - setBoardData(structuredClone(INITIAL_BOARD)); + setBoardData(createBoard(selectedSize)); setCurrentPlayer(generateRandomTurn()); setWinResult(undefined); setShowModal(false); @@ -50,13 +53,10 @@ export function PlayerVsPlayerPage() { return (
-
-
-
- +
- {showModal && } -
diff --git a/React/src/app/pages/game/components/Board/Board.module.scss b/React/src/app/pages/game/components/Board/Board.module.scss index 2cbd245..b84bce9 100644 --- a/React/src/app/pages/game/components/Board/Board.module.scss +++ b/React/src/app/pages/game/components/Board/Board.module.scss @@ -3,59 +3,54 @@ height: 312px; border: 1px solid #5570fd; backdrop-filter: blur(3px); - border-radius: 64px; padding: 12px; } -.container { +.template { display: flex; flex-direction: column; background-color: #191685; - border-radius: 56px; width: 100%; height: 100%; overflow: hidden; position: relative; +} +.container { + display: flex; + flex-direction: column; + height: 100%; } .row { display: flex; + height: 100%; } .cell { - border: 1px dashed #d9d9d9; - height: 96px; + border-top: 1px dashed #d9d9d9; + border-right: 1px dashed #d9d9d9; + height: 100%; display: flex; - flex-basis: 96px; + flex-basis: 100%; justify-content: center; align-items: center; cursor: pointer; -webkit-tap-highlight-color: transparent; } -.row:first-child :first-child, -.row:first-child :last-child { - border: none; -} - -.row:last-child :first-child, -.row:last-child :last-child { - border: none; -} - -.row:first-child :nth-child(2), -.row:last-child :nth-child(2) { +.container .row:first-child .cell { border-top: none; - border-bottom: none; } -.row:nth-child(2) :first-child, -.row:nth-child(2) :last-child { - border-left: none; +.container .row :last-child { border-right: none; } +.cell img { + width: 70%; +} + .winLine { border: 1px solid inherit; width: 4px; diff --git a/React/src/app/pages/game/components/Board/Board.tsx b/React/src/app/pages/game/components/Board/Board.tsx index f0fb5d3..60d9606 100644 --- a/React/src/app/pages/game/components/Board/Board.tsx +++ b/React/src/app/pages/game/components/Board/Board.tsx @@ -1,5 +1,7 @@ import { winLineStyleMaker } from "~/utils/winline-style-maker"; import classes from "./Board.module.scss"; +import { useEffect, useState } from "react"; +import { getBoardRadius } from "~/utils/getBoardRadius"; interface Props { boardData: string[][]; @@ -10,11 +12,17 @@ interface Props { export function Board({ boardData, onClick, currentPlayer, result }: Props) { - const winLineStyle = winLineStyleMaker(currentPlayer, result); + const winLineStyle = winLineStyleMaker(currentPlayer, boardData.length, result); + const [boardRadius, setBoardRadius] = useState(getBoardRadius(boardData.length)); + + useEffect(() => { + setBoardRadius(getBoardRadius(boardData.length)); + }, [boardData.length]); + return ( -
-
-
+
+
+
{boardData.map((row, i) => (
{row.map((col, j) => ( diff --git a/React/src/app/pages/game/components/Header/GamePageHeader.module.scss b/React/src/app/pages/game/components/Header/GamePageHeader.module.scss new file mode 100644 index 0000000..ceb9030 --- /dev/null +++ b/React/src/app/pages/game/components/Header/GamePageHeader.module.scss @@ -0,0 +1,104 @@ +.main { + display: flex; + margin-top: 36px; + align-items: center; + color: white; + gap: 10px; + +} + +.modeContainer { + height: 80px; + transform: translateY(20px); + width: 70px; +} + +.selectBox { + font-weight: 400; + font-size: 16px; + border-radius: 16px; + background-color: rgba(85, 112, 253, 0.2); + height: 40px; + overflow: hidden; + border: 1px solid #5570fd; + transition: 0.5s; + cursor: pointer; +} + +.selectBoxShowed { + height: 162px !important; +} + + + +.selectBox div { + padding: 0 6px; + height: 40px; + border-top: 1px solid #5570fd; + display: flex; + align-items: center; + cursor: pointer; +} + +.selectBox div:hover { + background-color: rgba(85, 112, 253, 0.2); + +} + +.selectBox .selectedSize { + width: 70px; + height: 38px; + gap: 4px; + display: flex; + align-items: center; + border-top: none; +} + +.player1, +.player2 { + position: relative; + width: 98px; + height: 130px; + padding: 4px; + border: 1px solid #5570fd; + border-radius: 24px; + + &.current { + .currentImg { + display: block; + } + } +} + +.player1.current { + border-color: #F3339E; +} + +.player2.current { + border-color: #F9BD13; +} + +.playerContainer { + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + width: 100%; + height: 100%; + font-weight: 400; + font-size: 16px; + background-color: #5570fd; + border-radius: 20px; + + img { + max-width: 48px; + } +} + +.currentImg { + max-width: 14px; + position: absolute; + bottom: -15px; + left: 40px; + display: none; +} \ No newline at end of file diff --git a/React/src/app/pages/game/components/Header/Header.tsx b/React/src/app/pages/game/components/Header/GamePageHeader.tsx similarity index 51% rename from React/src/app/pages/game/components/Header/Header.tsx rename to React/src/app/pages/game/components/Header/GamePageHeader.tsx index 7b1b869..fcc1581 100644 --- a/React/src/app/pages/game/components/Header/Header.tsx +++ b/React/src/app/pages/game/components/Header/GamePageHeader.tsx @@ -1,16 +1,26 @@ import { P1, P2 } from "~/utils/players"; -import classes from "./Header.module.scss"; +import classes from "./GamePageHeader.module.scss"; import { GameMode } from "~/utils/game-mode"; +import { boardSizeContext } from "~/App"; +import { useContext, useState } from "react"; interface Props { currentPlayer: string; gameMode: GameMode; } -export function Header({ currentPlayer, gameMode }: Props) { +export function GamePageHeader({ currentPlayer, gameMode }: Props) { + const { selectedSize, setSelectedSize } = useContext(boardSizeContext); + const [isSelectBoxOpen, setIsSelectBoxOpen] = useState(false); + + function changeBoardSize(size: number) { + if (selectedSize === size) return; + setSelectedSize(size); + setIsSelectBoxOpen(false); + } return ( - <> +
Player 1 Turn
@@ -19,9 +29,17 @@ export function Header({ currentPlayer, gameMode }: Props) { X
- -
VS
- +
+
+
setIsSelectBoxOpen(!isSelectBoxOpen)}> + {selectedSize} x {selectedSize} + arrow-down +
+
changeBoardSize(3)}>3 x 3
+
changeBoardSize(6)}>6 x 6
+
changeBoardSize(9)}>9 x 9
+
+
Player 2 Turn
@@ -30,6 +48,6 @@ export function Header({ currentPlayer, gameMode }: Props) { O
- +
); } diff --git a/React/src/app/pages/game/components/Header/Header.module.scss b/React/src/app/pages/game/components/Header/Header.module.scss deleted file mode 100644 index 0522c15..0000000 --- a/React/src/app/pages/game/components/Header/Header.module.scss +++ /dev/null @@ -1,53 +0,0 @@ -.vs { - font-weight: 400; - font-size: 32px; -} - -.player1, -.player2 { - position: relative; - width: 98px; - height: 130px; - padding: 4px; - border: 1px solid #5570fd; - border-radius: 24px; - - &.current { - .currentImg { - display: block; - } - } -} - -.player1.current { - border-color: #F3339E; -} - -.player2.current { - border-color: #F9BD13; -} - -.playerContainer { - display: flex; - flex-direction: column; - justify-content: space-around; - align-items: center; - width: 100%; - height: 100%; - font-weight: 400; - font-size: 16px; - background-color: #5570fd; - border-radius: 20px; - - img { - max-width: 48px; - } -} - -.currentImg { - max-width: 14px; - position: absolute; - bottom: -15px; - left: 40px; - display: none; -} \ No newline at end of file diff --git a/React/src/app/pages/game/components/Header/index.ts b/React/src/app/pages/game/components/Header/index.ts index 2180af6..36a3d4a 100644 --- a/React/src/app/pages/game/components/Header/index.ts +++ b/React/src/app/pages/game/components/Header/index.ts @@ -1 +1 @@ -export * from "./Header" \ No newline at end of file +export * from "./GamePageHeader" \ No newline at end of file diff --git a/React/src/app/pages/game/components/Modal/ResultModal/ResultModal.tsx b/React/src/app/pages/game/components/Modal/ResultModal/ResultModal.tsx index dccf864..0466108 100644 --- a/React/src/app/pages/game/components/Modal/ResultModal/ResultModal.tsx +++ b/React/src/app/pages/game/components/Modal/ResultModal/ResultModal.tsx @@ -10,7 +10,6 @@ interface Props { } export function ResultModal({ winner, onRefresh, gameMode }: Props) { - let resultModalIconSrc = ""; let resultModalText = "Draw"; if (winner) { diff --git a/React/src/app/pages/home/HomePage.module.scss b/React/src/app/pages/home/HomePage.module.scss index b54b916..f3ae4b4 100644 --- a/React/src/app/pages/home/HomePage.module.scss +++ b/React/src/app/pages/home/HomePage.module.scss @@ -8,12 +8,12 @@ } .title { - justify-content: center; position: relative; } .titleImg { - width: 170px; + width: 130px; + margin-top: 30px; } .titleBg1 { @@ -30,14 +30,17 @@ .body { gap: 16px; + justify-content: flex-end; + padding-bottom: 30px; background-image: url("static/images/bg.png"); background-repeat: no-repeat; background-size: cover; + } -.palyNowButton, -.palyBotButton, -.palyFriendsButton { +.playNowButton, +.playBotButton, +.playFriendsButton { width: 265px; height: 56px; border-radius: 24px; @@ -51,7 +54,7 @@ user-select: none; } -.palyNowButton { +.playNowButton { display: flex; justify-content: center; align-items: center; @@ -62,8 +65,8 @@ linear-gradient(to right, rgba(255, 255, 255, 0.33), #191685) border-box; } -.palyBotButton, -.palyFriendsButton { +.playBotButton, +.playFriendsButton { display: flex; align-items: center; justify-content: center; @@ -72,4 +75,61 @@ font-family: Arial, Helvetica, sans-serif; background: linear-gradient(#4b61e9, #4b61e9) padding-box, linear-gradient(to right, rgba(255, 255, 255, 0.5), #5570fd) border-box; +} + +.boardSizesContainerBackDrop { + position: absolute; + top: 50%; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + overflow: hidden; + transform: translateY(-65%); + height: 200px; +} + +.boardSizesContainer { + gap: 85px; + height: 200px; + display: flex; + align-items: center; + justify-content: center; +} + +.boardSize { + width: 126px; + height: auto; +} + +.vectorContainer { + position: absolute; + width: 215px; + top: 46.5%; + left: 50%; + transform: translate(-50.5%, 0%); + z-index: 2; +} + +.vectorContainer img { + position: absolute; + width: 17px; + height: 32px; + top: 50%; + z-index: 1; + transform: translate3d(0, -20%, 0); + transition: .3s; + cursor: pointer; +} + +.vectorContainer img:hover { + scale: 1.1; +} + +.leftVector { + left: 0; +} + +.rightVector { + right: 0; } \ No newline at end of file diff --git a/React/src/app/pages/home/HomePage.tsx b/React/src/app/pages/home/HomePage.tsx index 9d04cb0..b866a90 100644 --- a/React/src/app/pages/home/HomePage.tsx +++ b/React/src/app/pages/home/HomePage.tsx @@ -1,7 +1,21 @@ import { Link } from "@tanstack/react-router"; +import { useContext } from "react"; import classes from "./HomePage.module.scss"; +import { globals } from "~/utils/globals"; +import { boardSizeContext } from "~/App"; export function HomePage() { + + const { selectedSize, setSelectedSize } = useContext(boardSizeContext) + + function clickLeftVector() { + selectedSize != 3 && setSelectedSize(selectedSize - 3); + } + + function clickRightVector() { + selectedSize != 9 && setSelectedSize(selectedSize + 3); + } + return ( <>
@@ -21,15 +35,26 @@ export function HomePage() { className={classes.titleBg2} />
+
+ leftVector + rightVector +
+
+
+ {globals.boardSizes.map((board, i) => ( + + ))} +
+
- + Play Now! - + Play with bot - diff --git a/React/src/app/utils/check-board.ts b/React/src/app/utils/check-board.ts index 46c6a70..6d2b4d9 100644 --- a/React/src/app/utils/check-board.ts +++ b/React/src/app/utils/check-board.ts @@ -1,9 +1,7 @@ -import { createWinLines } from "./create-winlines"; -const boardSize = 3; -const winLines = createWinLines(boardSize); +import { getWinLines } from "./get-winLines" export function checkBoard(board: string[][], currentPlayer: string): number[][] | void { - - return winLines.find(line => line.every(cell => board[cell[0]][cell[1]] == currentPlayer)) + const winLines = getWinLines(board.length); + if (winLines) return winLines.find(line => line.every(cell => board[cell[0]][cell[1]] == currentPlayer)) } \ No newline at end of file diff --git a/React/src/app/utils/create-board.ts b/React/src/app/utils/create-board.ts new file mode 100644 index 0000000..3daa3a7 --- /dev/null +++ b/React/src/app/utils/create-board.ts @@ -0,0 +1,6 @@ +export function createBoard(selectedSize: number) { + return (Array(selectedSize) + .fill(null) + .map(() => Array(selectedSize).fill(null)) + ) +} \ No newline at end of file diff --git a/React/src/app/utils/create-winlines.ts b/React/src/app/utils/create-winlines.ts deleted file mode 100644 index ef780d3..0000000 --- a/React/src/app/utils/create-winlines.ts +++ /dev/null @@ -1,28 +0,0 @@ -export function createWinLines(boardSize: number) { - let winLines = []; - let diagonalOne = []; - let diagonalTwo = []; - for (let row = 0; row < boardSize; row++) { - let horizontal = []; - for (let col = 0; col < boardSize; col++) { - horizontal.push([row, col]); - } - winLines.push(horizontal) - } - for (let col = 0; col < boardSize; col++) { - let vertical = []; - for (let row = 0; row < boardSize; row++) { - vertical.push([row, col]); - } - winLines.push(vertical) - } - - for (let i = 0; i < boardSize; i++) { - diagonalOne.push([i, i]); - diagonalTwo.push([i, boardSize - i - 1]); - } - winLines.push(diagonalOne); - winLines.push(diagonalTwo); - - return winLines -} \ No newline at end of file diff --git a/React/src/app/utils/find-best-bot-move.ts b/React/src/app/utils/find-best-bot-move.ts index 7ba9eb9..0048620 100644 --- a/React/src/app/utils/find-best-bot-move.ts +++ b/React/src/app/utils/find-best-bot-move.ts @@ -1,30 +1,34 @@ import { P1, P2 } from "./players"; import { checkBoard } from "./check-board"; import { anyMovesLeft } from "./any-moves-left"; +import { getWinLines } from "./get-winLines"; + +let winLinesArray: number[][][]; export function findBestBotMove(board: any[][]) { + winLinesArray = getWinLines(board.length); let bestScore = -Infinity; - let movement; - for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { + let bestBotMove; + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board.length; j++) { if (board[i][j] == null) { board[i][j] = P2; let score = minimax(board, 0, false); board[i][j] = null; if (score > bestScore) { bestScore = score; - movement = { i, j }; + bestBotMove = { i, j }; } } } } - return movement; + return bestBotMove; } function minimax(board: any[][], depth: number, isMaximizing: boolean) { let current = isMaximizing ? P2 : P1; - let winResult = checkBoard(board, current); + let winResult = winLinesArray && checkBoard(board, current); if (winResult) return isMaximizing ? 1 : -1; if (anyMovesLeft(board)) return 0; @@ -33,10 +37,12 @@ function minimax(board: any[][], depth: number, isMaximizing: boolean) { return findMinMove(board, depth) } + function findMaxMove(board: any[][], depth: number) { + let bestScore = -Infinity; - for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board.length; j++) { if (board[i][j] == null) { board[i][j] = P2; let score = minimax(board, depth + 1, false); @@ -50,8 +56,8 @@ function findMaxMove(board: any[][], depth: number) { function findMinMove(board: any[][], depth: number) { let bestScore = Infinity; - for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board.length; j++) { if (board[i][j] == null) { board[i][j] = P1; let score = minimax(board, depth + 1, true); diff --git a/React/src/app/utils/get-winLines.ts b/React/src/app/utils/get-winLines.ts new file mode 100644 index 0000000..c223b1e --- /dev/null +++ b/React/src/app/utils/get-winLines.ts @@ -0,0 +1,53 @@ +const winLines: { [key: number]: number[][][] } = { + 3: [], + 6: [], + 9: [] +} + +export function getWinLines(boardSize: number) { + + const winLineSize: { [key: number]: number } = { + 3: 3, + 6: 4, + 9: 5 + } + + + if (winLines[boardSize].length != 0) return winLines[boardSize]; + + for (let i = 0; i < boardSize; i++) { + for (let j = 0; j <= boardSize - winLineSize[boardSize]; j++) { + let horizontal = []; + for (let k = j; k < j + winLineSize[boardSize]; k++) { + horizontal.push([i, k]); + } + winLines[boardSize].push(horizontal); + } + } + + for (let j = 0; j < boardSize; j++) { + for (let i = 0; i <= boardSize - winLineSize[boardSize]; i++) { + let vertical = []; + for (let k = i; k < i + winLineSize[boardSize]; k++) { + vertical.push([k, j]); + } + winLines[boardSize].push(vertical); + } + } + + for (let i = 0; i <= boardSize - winLineSize[boardSize]; i++) { + for (let j = 0; j <= boardSize - winLineSize[boardSize]; j++) { + let diagonalOne = []; + let diagonalTwo = []; + for (let k = 0; k < winLineSize[boardSize]; k++) { + diagonalOne.push([i + k, j + k]); + diagonalTwo.push([j + k, boardSize - k - i - 1]); + } + winLines[boardSize].push(diagonalOne); + winLines[boardSize].push(diagonalTwo); + + } + } + return winLines[boardSize]; +} + diff --git a/React/src/app/utils/getBoardRadius.ts b/React/src/app/utils/getBoardRadius.ts new file mode 100644 index 0000000..4dd65a0 --- /dev/null +++ b/React/src/app/utils/getBoardRadius.ts @@ -0,0 +1,19 @@ +interface BoardStyles { + mainRadius: string; + templateRadius: string; +} + +export function getBoardRadius(selectedSize: number) { + const boardStyles: BoardStyles = { mainRadius: "64px", templateRadius: "56px" }; + switch (selectedSize) { + case 6: + boardStyles.mainRadius = "42px"; + boardStyles.templateRadius = "30px"; + break; + case 9: + boardStyles.mainRadius = "36px"; + boardStyles.templateRadius = "20px"; + break; + } + return boardStyles;; +} \ No newline at end of file diff --git a/React/src/app/utils/globals.ts b/React/src/app/utils/globals.ts new file mode 100644 index 0000000..0dc692a --- /dev/null +++ b/React/src/app/utils/globals.ts @@ -0,0 +1,7 @@ + +export const globals = { + boardSizes: [3, 6, 9], + padding: 12, + boardWidth: 312, + boardBorderWidth: 1 +} \ No newline at end of file diff --git a/React/src/app/utils/winline-style-maker.ts b/React/src/app/utils/winline-style-maker.ts index bdf286c..75bf894 100644 --- a/React/src/app/utils/winline-style-maker.ts +++ b/React/src/app/utils/winline-style-maker.ts @@ -1,18 +1,22 @@ -import React, { CSSProperties } from "react"; +import React from "react"; +import { globals } from "./globals"; import { P1 } from "./players"; +const allSizeCellWidth: { [key: number]: number } = { + 3: 0, + 6: 0, + 9: 0 +} -const boardWidth = 312; -const mainBorderSize = 1; -const boardSize = 3; -const winLineSize = 3; -const padding = 12; -const cellWidth = (boardWidth - 2 * (padding + mainBorderSize)) / boardSize; +globals.boardSizes.map(size => { + allSizeCellWidth[size] = (globals.boardWidth - 2 * (globals.padding + globals.boardBorderWidth)) / size; +}) -export function winLineStyleMaker(currentPlayer: string, result?: number[][]) { +export function winLineStyleMaker(currentPlayer: string, selectedSize: number, result?: number[][]) { if (!result) return; + const cellWidth = allSizeCellWidth[selectedSize]; const winLineStyle = { visibility: "visible", top: "", @@ -26,22 +30,21 @@ export function winLineStyleMaker(currentPlayer: string, result?: number[][]) { let lineStartY = result[0][0]; let lineEndX = result[result.length - 1][1]; let lineEndY = result[result.length - 1][0]; - let angelIndicater = (lineEndY - lineStartY) / (lineEndX - lineStartX); - let lineLength = winLineSize * cellWidth; + let lineLength = result.length * cellWidth; winLineStyle.height = `${lineLength}px`; if (lineStartY == lineEndY) { - winLineStyle.left = `0px`; + winLineStyle.left = `${cellWidth * (lineStartX) - 2}px`; //minus 2 is half of winLine width winLineStyle.top = `${cellWidth * (lineStartY + 0.5)}px`; winLineStyle.transform = "rotate(-90deg)"; } else if (lineStartX == lineEndX) { - winLineStyle.left = `${cellWidth * (lineStartX + 0.5)}px`; - winLineStyle.top = `0px`; + winLineStyle.left = `${cellWidth * (lineStartX + 0.5) - 2}px`; //minus 2 is half of winLine width + winLineStyle.top = `${cellWidth * (lineStartY)}px`; winLineStyle.transform = "rotate(0deg)"; } else { - lineLength = Math.sqrt(2) * cellWidth * winLineSize; + lineLength = Math.sqrt(2) * cellWidth * result.length; winLineStyle.height = `${lineLength}px` winLineStyle.transform = `rotate(${angelIndicater > 0 ? -45 : 45}deg)`; winLineStyle.left = `${(lineStartX + (angelIndicater > 0 ? 0 : 1)) * cellWidth}px`; diff --git a/React/src/main.scss b/React/src/main.scss index 3b4b600..c157bdb 100644 --- a/React/src/main.scss +++ b/React/src/main.scss @@ -9,6 +9,7 @@ body, padding: 0; width: 100%; height: 100%; + font-family: 'Joti One', serif; } input, @@ -17,4 +18,4 @@ button, select, a { -webkit-tap-highlight-color: transparent; -} +} \ No newline at end of file diff --git a/React/src/main.tsx b/React/src/main.tsx index f90dad9..05b9ca0 100644 --- a/React/src/main.tsx +++ b/React/src/main.tsx @@ -4,7 +4,7 @@ import App from './app/App' import './main.scss' ReactDOM.createRoot(document.getElementById('root')!).render( - + // - + // ) diff --git a/React/static/images/arrow-down.png b/React/static/images/arrow-down.png new file mode 100644 index 0000000..43906c7 Binary files /dev/null and b/React/static/images/arrow-down.png differ diff --git a/React/static/images/arrow-up.png b/React/static/images/arrow-up.png new file mode 100644 index 0000000..2ec9c05 Binary files /dev/null and b/React/static/images/arrow-up.png differ diff --git a/React/static/images/board_size_3.svg b/React/static/images/board_size_3.svg new file mode 100644 index 0000000..491443f --- /dev/null +++ b/React/static/images/board_size_3.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/React/static/images/board_size_6.svg b/React/static/images/board_size_6.svg new file mode 100644 index 0000000..d5bd74f --- /dev/null +++ b/React/static/images/board_size_6.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/React/static/images/board_size_9.svg b/React/static/images/board_size_9.svg new file mode 100644 index 0000000..df2af5f --- /dev/null +++ b/React/static/images/board_size_9.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/React/static/images/vector-left.png b/React/static/images/vector-left.png new file mode 100644 index 0000000..8ad2cf7 Binary files /dev/null and b/React/static/images/vector-left.png differ diff --git a/React/static/images/vector-right.png b/React/static/images/vector-right.png new file mode 100644 index 0000000..1d9c813 Binary files /dev/null and b/React/static/images/vector-right.png differ