From b0c6641ea29ece8a4db25d9af5ef413d4f5df88d Mon Sep 17 00:00:00 2001 From: martin Date: Sat, 28 Oct 2023 15:12:50 +0200 Subject: [PATCH] Added prettier --- .../.idea/codeStyles/codeStyleConfig.xml | 2 +- pac-man-board-game/ClientApp/.prettierrc.json | 9 + pac-man-board-game/ClientApp/package.json | 4 +- pac-man-board-game/ClientApp/pnpm-lock.yaml | 10 + pac-man-board-game/ClientApp/src/App.tsx | 32 +-- .../ClientApp/src/AppRoutes.tsx | 34 +-- .../ClientApp/src/components/button.tsx | 25 +- .../ClientApp/src/components/debugMenu.tsx | 34 ++- .../ClientApp/src/components/dice.tsx | 65 +++--- .../ClientApp/src/components/dropdown.tsx | 29 +-- .../ClientApp/src/components/gameBoard.tsx | 200 ++++++++-------- .../ClientApp/src/components/gameButton.tsx | 51 +++-- .../src/components/gameComponent.tsx | 115 +++++----- .../ClientApp/src/components/gameTile.tsx | 214 ++++++++---------- .../ClientApp/src/components/input.tsx | 37 ++- .../ClientApp/src/components/layout.tsx | 16 +- .../ClientApp/src/components/navMenu.tsx | 95 ++++---- .../ClientApp/src/components/playerStats.tsx | 36 ++- pac-man-board-game/ClientApp/src/game/box.ts | 31 ++- .../ClientApp/src/game/character.ts | 105 +++++---- .../ClientApp/src/game/colour.ts | 2 +- .../ClientApp/src/game/direction.ts | 5 +- pac-man-board-game/ClientApp/src/game/map.ts | 25 +- .../ClientApp/src/game/player.ts | 59 ++--- .../src/game/possibleMovesAlgorithm.ts | 176 ++++++++------ .../ClientApp/src/game/rules.ts | 2 +- .../ClientApp/src/game/tileType.ts | 2 +- .../ClientApp/src/hooks/useToggle.ts | 10 +- pac-man-board-game/ClientApp/src/index.css | 24 +- pac-man-board-game/ClientApp/src/index.tsx | 33 +-- .../ClientApp/src/pages/counter.tsx | 38 ++-- .../ClientApp/src/pages/game.tsx | 21 +- .../ClientApp/src/pages/home.tsx | 38 ++-- .../ClientApp/src/pages/lobby.tsx | 114 +++++----- .../ClientApp/src/pages/login.tsx | 64 +++--- .../ClientApp/src/reportWebVitals.js | 20 +- .../ClientApp/src/types/props.d.ts | 50 ++-- .../ClientApp/src/types/types.d.ts | 52 ++--- .../ClientApp/src/utils/actions.ts | 127 ++++++----- pac-man-board-game/ClientApp/src/utils/api.ts | 10 +- .../ClientApp/src/utils/state.ts | 69 +++--- .../ClientApp/src/utils/utils.ts | 12 +- .../ClientApp/src/vite-env.d.ts | 10 +- .../src/websockets/WebSocketService.ts | 84 +++---- .../Controllers/GameController.cs | 2 +- 45 files changed, 1121 insertions(+), 1072 deletions(-) create mode 100644 pac-man-board-game/ClientApp/.prettierrc.json diff --git a/.idea/.idea.pac-man-board-game/.idea/codeStyles/codeStyleConfig.xml b/.idea/.idea.pac-man-board-game/.idea/codeStyles/codeStyleConfig.xml index a55e7a1..79ee123 100644 --- a/.idea/.idea.pac-man-board-game/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/.idea.pac-man-board-game/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/pac-man-board-game/ClientApp/.prettierrc.json b/pac-man-board-game/ClientApp/.prettierrc.json new file mode 100644 index 0000000..66517d1 --- /dev/null +++ b/pac-man-board-game/ClientApp/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "semi": false, + "singleQuote": false, + "arrowParens": "avoid", + "bracketSpacing": true, + "bracketSameLine": true, + "printWidth": 120 +} \ No newline at end of file diff --git a/pac-man-board-game/ClientApp/package.json b/pac-man-board-game/ClientApp/package.json index 14c120e..55e979b 100644 --- a/pac-man-board-game/ClientApp/package.json +++ b/pac-man-board-game/ClientApp/package.json @@ -23,6 +23,7 @@ "cross-env": "^7.0.3", "happy-dom": "^12.10.3", "postcss": "^8.4.31", + "prettier": "^3.0.3", "tailwindcss": "^3.3.5", "typescript": "^5.2.2", "vite": "^4.5.0", @@ -41,7 +42,8 @@ "build": "vite build", "serve": "vite preview", "test": "cross-env CI=true vitest", - "coverage": "vitest run --coverage" + "coverage": "vitest run --coverage", + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"" }, "browserslist": { "production": [ diff --git a/pac-man-board-game/ClientApp/pnpm-lock.yaml b/pac-man-board-game/ClientApp/pnpm-lock.yaml index 03fd142..6778874 100644 --- a/pac-man-board-game/ClientApp/pnpm-lock.yaml +++ b/pac-man-board-game/ClientApp/pnpm-lock.yaml @@ -67,6 +67,9 @@ importers: postcss: specifier: ^8.4.31 version: 8.4.31 + prettier: + specifier: ^3.0.3 + version: 3.0.3 tailwindcss: specifier: ^3.3.5 version: 3.3.5 @@ -2442,6 +2445,7 @@ packages: chalk: 3.0.0 diff-match-patch: 1.0.5 dev: false + bundledDependencies: [] /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -2890,6 +2894,12 @@ packages: source-map-js: 1.0.2 dev: true + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + /pretty-format@29.6.2: resolution: {integrity: sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} diff --git a/pac-man-board-game/ClientApp/src/App.tsx b/pac-man-board-game/ClientApp/src/App.tsx index 6086812..669efd8 100644 --- a/pac-man-board-game/ClientApp/src/App.tsx +++ b/pac-man-board-game/ClientApp/src/App.tsx @@ -1,21 +1,21 @@ -import React, {FC} from "react"; -import {Navigate, Route, Routes} from "react-router-dom"; -import Layout from "./components/layout"; -import AppRoutes from "./AppRoutes"; -import "./index.css"; -import {useAtomValue} from "jotai"; -import {thisPlayerAtom} from "./utils/state"; +import React, { FC } from "react" +import { Navigate, Route, Routes } from "react-router-dom" +import Layout from "./components/layout" +import AppRoutes from "./AppRoutes" +import "./index.css" +import { useAtomValue } from "jotai" +import { thisPlayerAtom } from "./utils/state" export const App: FC = () => ( {AppRoutes.map((route, index) => { - const {element, secured = false, ...rest} = route; - return {element}}/>; + const { element, secured = false, ...rest } = route + return {element}} /> })} -); +) /** * This component is used to redirect the user to the login page if they are not logged in and the page is secured. @@ -23,13 +23,15 @@ export const App: FC = () => ( * @param secured Whether or not the page is secured. * @constructor The Secured component. */ -const Secured: FC<{ - secured: boolean -} & ChildProps> = ({children, secured}) => { - const player = useAtomValue(thisPlayerAtom); +const Secured: FC< + { + secured: boolean + } & ChildProps +> = ({ children, secured }) => { + const player = useAtomValue(thisPlayerAtom) if (secured && player === undefined) { - return + return } return <>{children} diff --git a/pac-man-board-game/ClientApp/src/AppRoutes.tsx b/pac-man-board-game/ClientApp/src/AppRoutes.tsx index 8fa3cc4..24afae7 100644 --- a/pac-man-board-game/ClientApp/src/AppRoutes.tsx +++ b/pac-man-board-game/ClientApp/src/AppRoutes.tsx @@ -1,37 +1,37 @@ -import React from "react"; -import {Counter} from "./pages/counter"; -import GamePage from "./pages/game"; -import LobbyPage from "./pages/lobby"; -import LoginPage from "./pages/login"; -import HomePage from "./pages/home"; +import React from "react" +import { Counter } from "./pages/counter" +import GamePage from "./pages/game" +import LobbyPage from "./pages/lobby" +import LoginPage from "./pages/login" +import HomePage from "./pages/home" const AppRoutes = [ { index: true, - element: + element: , }, { path: "/counter", - element: + element: , }, { path: "/game/:id", - element: , - secured: true + element: , + secured: true, }, { path: "/lobby", - element: , - secured: true + element: , + secured: true, }, { path: "/login", - element: + element: , }, { path: "*", - element:

Page not found

- } -]; + element:

Page not found

, + }, +] -export default AppRoutes; +export default AppRoutes diff --git a/pac-man-board-game/ClientApp/src/components/button.tsx b/pac-man-board-game/ClientApp/src/components/button.tsx index 7bcf752..127e202 100644 --- a/pac-man-board-game/ClientApp/src/components/button.tsx +++ b/pac-man-board-game/ClientApp/src/components/button.tsx @@ -1,16 +1,15 @@ -import React, {FC} from "react"; +import React, { FC } from "react" -export const Button: FC = ( - { - className, - onClick, - style, - title, - id, - disabled = false, - children, - type = "button", - }) => { +export const Button: FC = ({ + className, + onClick, + style, + title, + id, + disabled = false, + children, + type = "button", +}) => { return ( ) -} \ No newline at end of file +} diff --git a/pac-man-board-game/ClientApp/src/components/debugMenu.tsx b/pac-man-board-game/ClientApp/src/components/debugMenu.tsx index 35d932e..2e7d602 100644 --- a/pac-man-board-game/ClientApp/src/components/debugMenu.tsx +++ b/pac-man-board-game/ClientApp/src/components/debugMenu.tsx @@ -1,36 +1,34 @@ -import React, {FC} from "react"; -import useToggle from "../hooks/useToggle"; -import {BugAntIcon} from "@heroicons/react/20/solid"; -import {selectedMapAtom} from "../utils/state"; -import {useAtom} from "jotai"; +import React, { FC } from "react" +import useToggle from "../hooks/useToggle" +import { BugAntIcon } from "@heroicons/react/20/solid" +import { selectedMapAtom } from "../utils/state" +import { useAtom } from "jotai" const DebugMenu: FC = () => { - - const [open, toggleOpen] = useToggle(); + const [open, toggleOpen] = useToggle() if (import.meta.env.DEV) { return (
- {open && } -
- - ); + ) } } -export default DebugMenu; +export default DebugMenu const DebugOptions: FC = () => { - - const [map, setMap] = useAtom(selectedMapAtom); + const [map, setMap] = useAtom(selectedMapAtom) function clearSessionStorage(): void { - sessionStorage.clear(); + sessionStorage.clear() } function restartGame(): void { diff --git a/pac-man-board-game/ClientApp/src/components/dice.tsx b/pac-man-board-game/ClientApp/src/components/dice.tsx index 6e2e530..4485856 100644 --- a/pac-man-board-game/ClientApp/src/components/dice.tsx +++ b/pac-man-board-game/ClientApp/src/components/dice.tsx @@ -1,56 +1,49 @@ -import React, {FC} from "react"; -import {useAtom, useAtomValue} from "jotai"; -import {selectedDiceAtom, thisPlayerAtom,} from "../utils/state"; -import {Button} from "./button"; +import React, { FC } from "react" +import { useAtom, useAtomValue } from "jotai" +import { selectedDiceAtom, thisPlayerAtom } from "../utils/state" +import { Button } from "./button" -export const AllDice: FC<{ values?: number[] } & ComponentProps> = ( - { - className, - values, - }) => { - - const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom); +export const AllDice: FC<{ values?: number[] } & ComponentProps> = ({ className, values }) => { + const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom) function handleClick(dice: SelectedDice): void { - setSelectedDice(dice); + setSelectedDice(dice) } return (
- {values?.map((value, index) => - handleClick({index, value})}/>)} + {values?.map((value, index) => ( + handleClick({ index, value })} + /> + ))}
- ); -}; - -interface DiceProps extends ComponentProps { - value?: number, - onClick?: (value: number) => void, + ) } -export const Dice: FC = ( - { - className, - value, - onClick, - }) => { +interface DiceProps extends ComponentProps { + value?: number + onClick?: (value: number) => void +} - const thisPlayer = useAtomValue(thisPlayerAtom); +export const Dice: FC = ({ className, value, onClick }) => { + const thisPlayer = useAtomValue(thisPlayerAtom) function handleClick() { if (onClick && value) { - onClick(value); + onClick(value) } } return ( - - ); -}; + ) +} diff --git a/pac-man-board-game/ClientApp/src/components/dropdown.tsx b/pac-man-board-game/ClientApp/src/components/dropdown.tsx index 26fb237..3bf1c1a 100644 --- a/pac-man-board-game/ClientApp/src/components/dropdown.tsx +++ b/pac-man-board-game/ClientApp/src/components/dropdown.tsx @@ -1,24 +1,17 @@ -import React, {forwardRef, ReactEventHandler} from "react"; +import React, { forwardRef, ReactEventHandler } from "react" export interface DropdownProps extends ComponentProps { - options?: string[], - onSelect?: ReactEventHandler, + options?: string[] + onSelect?: ReactEventHandler } -const Dropdown: FRComponent = forwardRef(( - { - className, - options, - onSelect - }, ref) => ( - + {options?.map((option, index) => )} -)); +)) -export default Dropdown; +export default Dropdown diff --git a/pac-man-board-game/ClientApp/src/components/gameBoard.tsx b/pac-man-board-game/ClientApp/src/components/gameBoard.tsx index 1832975..085a529 100644 --- a/pac-man-board-game/ClientApp/src/components/gameBoard.tsx +++ b/pac-man-board-game/ClientApp/src/components/gameBoard.tsx @@ -1,147 +1,138 @@ -import React, {FC, Fragment, useEffect, useState} from "react"; -import {Character} from "../game/character"; -import findPossiblePositions from "../game/possibleMovesAlgorithm"; -import {GameTile} from "./gameTile"; -import {TileType} from "../game/tileType"; -import {atom, useAtom, useAtomValue, useSetAtom} from "jotai"; -import {allCharactersAtom, currentPlayerAtom, playersAtom, selectedDiceAtom} from "../utils/state"; -import {Dialog, Transition} from "@headlessui/react"; +import React, { FC, Fragment, useEffect, useState } from "react" +import { Character } from "../game/character" +import findPossiblePositions from "../game/possibleMovesAlgorithm" +import { GameTile } from "./gameTile" +import { TileType } from "../game/tileType" +import { atom, useAtom, useAtomValue, useSetAtom } from "jotai" +import { allCharactersAtom, currentPlayerAtom, playersAtom, selectedDiceAtom } from "../utils/state" +import { Dialog, Transition } from "@headlessui/react" interface BoardProps extends ComponentProps { - onMove?: Action, + onMove?: Action map: GameMap } -const modalOpenAtom = atom(false); +const modalOpenAtom = atom(false) -const Board: FC = ( - { - className, - onMove, - map - }) => { - - const currentPlayer = useAtomValue(currentPlayerAtom); - const characters = useAtomValue(allCharactersAtom); - const selectedDice = useAtomValue(selectedDiceAtom); - const [selectedCharacter, setSelectedCharacter] = useState(); - const [possiblePositions, setPossiblePositions] = useState([]); - const [hoveredPosition, setHoveredPosition] = useState(); - const setModalOpen = useSetAtom(modalOpenAtom); +const Board: FC = ({ className, onMove, map }) => { + const currentPlayer = useAtomValue(currentPlayerAtom) + const characters = useAtomValue(allCharactersAtom) + const selectedDice = useAtomValue(selectedDiceAtom) + const [selectedCharacter, setSelectedCharacter] = useState() + const [possiblePositions, setPossiblePositions] = useState([]) + const [hoveredPosition, setHoveredPosition] = useState() + const setModalOpen = useSetAtom(modalOpenAtom) function handleSelectCharacter(character: Character): void { if (character.isPacMan() && currentPlayer?.pacMan.colour !== character.colour) { - return; + return } - setSelectedCharacter(character); + setSelectedCharacter(character) } function handleShowPath(path: Path): void { - setHoveredPosition(path); + setHoveredPosition(path) } function handleMoveCharacter(destination: Path): void { if (selectedCharacter) { - setHoveredPosition(undefined); + setHoveredPosition(undefined) if (selectedCharacter.isGhost()) { - tryMovePacManToSpawn(destination); + tryMovePacManToSpawn(destination) } - selectedCharacter.follow(destination); + selectedCharacter.follow(destination) - const positions = pickUpPellets(destination); - onMove?.(positions); - setSelectedCharacter(undefined); + const positions = pickUpPellets(destination) + onMove?.(positions) + setSelectedCharacter(undefined) } } function tryMovePacManToSpawn(destination: Path): void { - const takenChar = characters.find(c => c.isPacMan() && c.isAt(destination.end)); + const takenChar = characters.find(c => c.isPacMan() && c.isAt(destination.end)) if (takenChar) { - takenChar.moveToSpawn(); - stealFromPlayer(); + takenChar.moveToSpawn() + stealFromPlayer() } } function stealFromPlayer(): void { - setModalOpen(true); + setModalOpen(true) } function pickUpPellets(destination: Path): Position[] { - const positions: Position[] = []; + const positions: Position[] = [] if (selectedCharacter?.isPacMan()) { - - for (const tile of [...destination.path ?? [], destination.end]) { - const currentTile = map[tile.y][tile.x]; + for (const tile of [...(destination.path ?? []), destination.end]) { + const currentTile = map[tile.y][tile.x] function updateTileAndPlayerBox(isPowerPellet = false): void { if (isPowerPellet) { - currentPlayer?.addPowerPellet(); + currentPlayer?.addPowerPellet() } else { - currentPlayer?.addPellet(); + currentPlayer?.addPellet() } - map[tile.y][tile.x] = TileType.empty; - positions.push(tile); + map[tile.y][tile.x] = TileType.empty + positions.push(tile) } if (currentTile === TileType.pellet) { - updateTileAndPlayerBox(); + updateTileAndPlayerBox() } else if (currentTile === TileType.powerPellet) { - updateTileAndPlayerBox(true); + updateTileAndPlayerBox(true) } } } - return positions; + return positions } useEffect(() => { if (selectedCharacter && selectedDice) { - const possiblePaths = findPossiblePositions(map, selectedCharacter, selectedDice.value, characters); - setPossiblePositions(possiblePaths); + const possiblePaths = findPossiblePositions(map, selectedCharacter, selectedDice.value, characters) + setPossiblePositions(possiblePaths) } else { - setPossiblePositions([]); + setPossiblePositions([]) } - }, [selectedCharacter, selectedDice]); + }, [selectedCharacter, selectedDice]) return (
- - { - map.map((row, rowIndex) => -
- { - row.map((tile, colIndex) => - p.end.x === colIndex && p.end.y === rowIndex)} - character={characters.find(c => c.isAt({x: colIndex, y: rowIndex}))} - isSelected={selectedCharacter?.isAt({x: colIndex, y: rowIndex})} - showPath={hoveredPosition?.path?.find(pos => pos.x === colIndex && pos.y === rowIndex) !== undefined} - handleMoveCharacter={handleMoveCharacter} - handleSelectCharacter={handleSelectCharacter} - handleStartShowPath={handleShowPath} - handleStopShowPath={() => setHoveredPosition(undefined)}/> - ) - } -
) - } + + {map.map((row, rowIndex) => ( +
+ {row.map((tile, colIndex) => ( + p.end.x === colIndex && p.end.y === rowIndex)} + character={characters.find(c => c.isAt({ x: colIndex, y: rowIndex }))} + isSelected={selectedCharacter?.isAt({ x: colIndex, y: rowIndex })} + showPath={hoveredPosition?.path?.find(pos => pos.x === colIndex && pos.y === rowIndex) !== undefined} + handleMoveCharacter={handleMoveCharacter} + handleSelectCharacter={handleSelectCharacter} + handleStartShowPath={handleShowPath} + handleStopShowPath={() => setHoveredPosition(undefined)} + /> + ))} +
+ ))}
- ); -}; + ) +} -export default Board; +export default Board const SelectPlayerModal: FC = () => { - const [isOpen, setIsOpen] = useAtom(modalOpenAtom); - const currentPlayer = useAtomValue(currentPlayerAtom); - const allPlayers = useAtomValue(playersAtom).filter(p => p !== currentPlayer); + const [isOpen, setIsOpen] = useAtom(modalOpenAtom) + const currentPlayer = useAtomValue(currentPlayerAtom) + const allPlayers = useAtomValue(playersAtom).filter(p => p !== currentPlayer) - if (currentPlayer === undefined) return null; + if (currentPlayer === undefined) return null function close(): void { - setIsOpen(false); + setIsOpen(false) } return ( @@ -155,9 +146,8 @@ const SelectPlayerModal: FC = () => { enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" - leaveTo="opacity-0" - > -
+ leaveTo="opacity-0"> +
@@ -169,10 +159,8 @@ const SelectPlayerModal: FC = () => { enterTo="opacity-100 scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 scale-100" - leaveTo="opacity-0 scale-95" - > - + leaveTo="opacity-0 scale-95"> + Steal from player @@ -182,29 +170,29 @@ const SelectPlayerModal: FC = () => {
- { - allPlayers.map(player => -
- {player.username} has {player.box.pellets} pellets - -
- ) - } + {allPlayers.map(player => ( +
+ + {player.username} has {player.box.pellets} pellets + + +
+ ))}
diff --git a/pac-man-board-game/ClientApp/src/components/gameButton.tsx b/pac-man-board-game/ClientApp/src/components/gameButton.tsx index dc78636..3d7747f 100644 --- a/pac-man-board-game/ClientApp/src/components/gameButton.tsx +++ b/pac-man-board-game/ClientApp/src/components/gameButton.tsx @@ -1,33 +1,36 @@ -import React, {FC, MouseEventHandler} from "react"; -import {State} from "../game/player"; -import {currentPlayerAtom, playersAtom, rollDiceButtonAtom, thisPlayerAtom} from "../utils/state"; -import {useAtomValue} from "jotai"; -import {Button} from "./button"; -import rules from "../game/rules"; +import React, { FC, MouseEventHandler } from "react" +import { State } from "../game/player" +import { currentPlayerAtom, playersAtom, rollDiceButtonAtom, thisPlayerAtom } from "../utils/state" +import { useAtomValue } from "jotai" +import { Button } from "./button" +import rules from "../game/rules" interface GameButtonProps extends ComponentProps { - onReadyClick?: MouseEventHandler, + onReadyClick?: MouseEventHandler onRollDiceClick?: MouseEventHandler } -const GameButton: FC = ( - { - onReadyClick, - onRollDiceClick, - }) => { +const GameButton: FC = ({ onReadyClick, onRollDiceClick }) => { + const currentPlayer = useAtomValue(currentPlayerAtom) + const thisPlayer = useAtomValue(thisPlayerAtom) + const players = useAtomValue(playersAtom) + const activeRollDiceButton = useAtomValue(rollDiceButtonAtom) - const currentPlayer = useAtomValue(currentPlayerAtom); - const thisPlayer = useAtomValue(thisPlayerAtom); - const players = useAtomValue(playersAtom); - const activeRollDiceButton = useAtomValue(rollDiceButtonAtom); - - if (players.length >= rules.minPlayers && (currentPlayer === undefined || currentPlayer.state === State.waitingForPlayers)) { - return ; + if ( + players.length >= rules.minPlayers && + (currentPlayer === undefined || currentPlayer.state === State.waitingForPlayers) + ) { + return } - if (!thisPlayer?.isTurn()) { // TODO also show when waiting for other players - return ; + if (!thisPlayer?.isTurn()) { + // TODO also show when waiting for other players + return } - return ; -}; + return ( + + ) +} -export default GameButton; +export default GameButton diff --git a/pac-man-board-game/ClientApp/src/components/gameComponent.tsx b/pac-man-board-game/ClientApp/src/components/gameComponent.tsx index f87333a..37258d0 100644 --- a/pac-man-board-game/ClientApp/src/components/gameComponent.tsx +++ b/pac-man-board-game/ClientApp/src/components/gameComponent.tsx @@ -1,39 +1,38 @@ -import React, {FC, useEffect} from "react"; -import {AllDice} from "./dice"; -import {doAction, GameAction} from "../utils/actions"; -import GameBoard from "./gameBoard"; -import WebSocketService from "../websockets/WebSocketService"; -import Player from "../game/player"; -import PlayerStats from "../components/playerStats"; -import {useAtom, useAtomValue, useSetAtom} from "jotai"; -import {diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom, selectedDiceAtom} from "../utils/state"; -import GameButton from "./gameButton"; -import {Button} from "./button"; -import {useNavigate, useParams} from "react-router-dom"; -import {getData} from "../utils/api"; +import React, { FC, useEffect } from "react" +import { AllDice } from "./dice" +import { doAction, GameAction } from "../utils/actions" +import GameBoard from "./gameBoard" +import WebSocketService from "../websockets/WebSocketService" +import Player from "../game/player" +import PlayerStats from "../components/playerStats" +import { useAtom, useAtomValue, useSetAtom } from "jotai" +import { diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom, selectedDiceAtom } from "../utils/state" +import GameButton from "./gameButton" +import { Button } from "./button" +import { useNavigate, useParams } from "react-router-dom" +import { getData } from "../utils/api" -const wsService = new WebSocketService(import.meta.env.VITE_API_WS); +const wsService = new WebSocketService(import.meta.env.VITE_API_WS) -export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map}) => { +export const GameComponent: FC<{ player: Player; map: GameMap }> = ({ player, map }) => { + const players = useAtomValue(playersAtom) + const dice = useAtomValue(diceAtom) + const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom) + const setActiveRollDiceButton = useSetAtom(rollDiceButtonAtom) + const ghosts = useAtomValue(ghostsAtom) - const players = useAtomValue(playersAtom); - const dice = useAtomValue(diceAtom); - const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom); - const setActiveRollDiceButton = useSetAtom(rollDiceButtonAtom); - const ghosts = useAtomValue(ghostsAtom); - - const navigate = useNavigate(); - const {id} = useParams(); + const navigate = useNavigate() + const { id } = useParams() /** * Rolls the dice for the current player's turn. */ function rollDice(): void { - if (!player.isTurn()) return; + if (!player.isTurn()) return - setSelectedDice(undefined); - wsService.send({action: GameAction.rollDice}); - setActiveRollDiceButton(false); + setSelectedDice(undefined) + wsService.send({ action: GameAction.rollDice }) + setActiveRollDiceButton(false) } /** @@ -42,22 +41,22 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map */ function onCharacterMove(eatenPellets: Position[]): void { if (dice && selectedDice) { - dice.splice(selectedDice.index, 1); + dice.splice(selectedDice.index, 1) } - setSelectedDice(undefined); + setSelectedDice(undefined) const data: ActionMessage = { action: GameAction.moveCharacter, data: { dice: dice?.length ?? 0 > 0 ? dice : null, players: players, ghosts: ghosts, - eatenPellets: eatenPellets - } - }; - wsService.send(data); + eatenPellets: eatenPellets, + }, + } + wsService.send(data) if (dice?.length === 0) { - endTurn(); + endTurn() } } @@ -70,15 +69,15 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map data: { username: player.username, gameId: id, - } as JoinGameData - }); + } as JoinGameData, + }) } /** * Sends a ready action to the WebSocket service. */ function sendReady(): void { - wsService.send({action: GameAction.ready}); + wsService.send({ action: GameAction.ready }) } /** @@ -86,44 +85,40 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map * to advance to the next player in the game. */ function endTurn(): void { - wsService.send({action: GameAction.nextPlayer}); + wsService.send({ action: GameAction.nextPlayer }) } /** * Leaves the current game and navigates to the lobby. */ function leaveGame(): void { - wsService.send({action: GameAction.disconnect}); - navigate("/lobby"); + wsService.send({ action: GameAction.disconnect }) + navigate("/lobby") } useEffect(() => { + getData(`/game/exists/${id}`).then(res => { + if (!res.ok) { + return navigate("/lobby") + } + wsService.onReceive = doAction + wsService.open() - getData(`/game/exists/${id}`) - .then(res => { - if (!res.ok) { - return navigate("/lobby"); - } - wsService.onReceive = doAction; - wsService.open(); + wsService.waitForOpen().then(() => joinGame()) + }) - wsService.waitForOpen().then(() => joinGame()); - }) - - return () => wsService.close(); - }, []); + return () => wsService.close() + }, []) return ( <> -
- {players?.map(p => )} -
+
{players?.map(p => )}
- +
- - + + - ); -}; + ) +} diff --git a/pac-man-board-game/ClientApp/src/components/gameTile.tsx b/pac-man-board-game/ClientApp/src/components/gameTile.tsx index 5323d6e..8e6a2c1 100644 --- a/pac-man-board-game/ClientApp/src/components/gameTile.tsx +++ b/pac-man-board-game/ClientApp/src/components/gameTile.tsx @@ -1,165 +1,151 @@ -import React, {FC, useEffect, useState} from "react"; -import {TileType} from "../game/tileType"; -import {Character, Dummy} from "../game/character"; -import {Direction} from "../game/direction"; -import {Colour} from "../game/colour"; +import React, { FC, useEffect, useState } from "react" +import { TileType } from "../game/tileType" +import { Character, Dummy } from "../game/character" +import { Direction } from "../game/direction" +import { Colour } from "../game/colour" interface TileWithCharacterProps extends ComponentProps { - possiblePath?: Path, - character?: Character, - type?: TileType, - handleMoveCharacter?: Action, - handleSelectCharacter?: Action, - handleStartShowPath?: Action, - handleStopShowPath?: VoidFunction, - isSelected?: boolean, + possiblePath?: Path + character?: Character + type?: TileType + handleMoveCharacter?: Action + handleSelectCharacter?: Action + handleStartShowPath?: Action + handleStopShowPath?: VoidFunction + isSelected?: boolean showPath?: boolean } -export const GameTile: FC = ( - { - possiblePath, - character, - type, - handleMoveCharacter, - handleSelectCharacter, - handleStartShowPath, - handleStopShowPath, - isSelected = false, - showPath = false - }) => ( - handleMoveCharacter?.(possiblePath) : undefined} - onMouseEnter={possiblePath ? () => handleStartShowPath?.(possiblePath) : undefined} - onMouseLeave={handleStopShowPath}> +export const GameTile: FC = ({ + possiblePath, + character, + type, + handleMoveCharacter, + handleSelectCharacter, + handleStartShowPath, + handleStopShowPath, + isSelected = false, + showPath = false, +}) => ( + handleMoveCharacter?.(possiblePath) : undefined} + onMouseEnter={possiblePath ? () => handleStartShowPath?.(possiblePath) : undefined} + onMouseLeave={handleStopShowPath}> <> - {character && -
- -
- } - {showPath && } - + {character && ( +
+ +
+ )} + {showPath && } +
-); +) -const Circle: FC<{ colour?: Colour } & ComponentProps> = ({colour = Colour.white, className}) => ( +const Circle: FC<{ colour?: Colour } & ComponentProps> = ({ colour = Colour.white, className }) => (
-
+
-); +) interface TileProps extends ChildProps { - type?: TileType, - onClick?: VoidFunction, - onMouseEnter?: VoidFunction, - onMouseLeave?: VoidFunction, - character?: Character, - onCharacterClick?: Action, - characterClass?: string, + type?: TileType + onClick?: VoidFunction + onMouseEnter?: VoidFunction + onMouseLeave?: VoidFunction + character?: Character + onCharacterClick?: Action + characterClass?: string } -const Tile: FC = ( - { - type = TileType.empty, - onClick, - onMouseEnter, - onMouseLeave, - className, - children - }) => { - - const [tileSize, setTileSize] = useState(2); +const Tile: FC = ({ type = TileType.empty, onClick, onMouseEnter, onMouseLeave, className, children }) => { + const [tileSize, setTileSize] = useState(2) function setColor(): string { switch (type) { case TileType.wall: - return "bg-blue-500"; + return "bg-blue-500" case TileType.ghostSpawn: - return "bg-red-500"; + return "bg-red-500" case TileType.pacmanSpawn: - return "bg-green-500"; // TODO should be the colour of the player + return "bg-green-500" // TODO should be the colour of the player default: - return "bg-black"; + return "bg-black" } } useEffect(() => { - function handleResize(): void { - const newSize = Math.floor(window.innerWidth / 16); - setTileSize(newSize); + const newSize = Math.floor(window.innerWidth / 16) + setTileSize(newSize) } - handleResize(); + handleResize() - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); + window.addEventListener("resize", handleResize) + return () => window.removeEventListener("resize", handleResize) + }, []) return ( -
+
{children} - {type === TileType.pellet && } - {type === TileType.powerPellet && } + {type === TileType.pellet && } + {type === TileType.powerPellet && }
- ); -}; - -const AddDummy: FC<{ path?: Path } & ComponentProps> = ({path}) => ( - <> - {path && -
- -
- } - -); - -interface CharacterComponentProps extends ComponentProps { - character?: Character, - onClick?: Action, + ) } -const CharacterComponent: FC = ( - { - character, - onClick, - className - }) => { +const AddDummy: FC<{ path?: Path } & ComponentProps> = ({ path }) => ( + <> + {path && ( +
+ +
+ )} + +) +interface CharacterComponentProps extends ComponentProps { + character?: Character + onClick?: Action +} + +const CharacterComponent: FC = ({ character, onClick, className }) => { function getSide() { switch (character?.position?.direction) { case Direction.up: - return "right-1/4 top-0"; + return "right-1/4 top-0" case Direction.down: - return "right-1/4 bottom-0"; + return "right-1/4 bottom-0" case Direction.left: - return "left-0 top-1/4"; + return "left-0 top-1/4" case Direction.right: - return "right-0 top-1/4"; + return "right-0 top-1/4" } } - if (character === undefined) return null; + if (character === undefined) return null return ( -
onClick?.(character)}> +
onClick?.(character)}>
-
+
- ); -}; + ) +} diff --git a/pac-man-board-game/ClientApp/src/components/input.tsx b/pac-man-board-game/ClientApp/src/components/input.tsx index 551298e..586cde1 100644 --- a/pac-man-board-game/ClientApp/src/components/input.tsx +++ b/pac-man-board-game/ClientApp/src/components/input.tsx @@ -1,23 +1,18 @@ -import React, {forwardRef} from "react"; +import React, { forwardRef } from "react" -const Input: FRComponent = forwardRef(( - { - type = "text", - className, - id, - placeholder, - required = false, - name, - autoComplete = "off", - }, ref) => ( - -)); +const Input: FRComponent = forwardRef( + ({ type = "text", className, id, placeholder, required = false, name, autoComplete = "off" }, ref) => ( + + ), +) -export default Input; +export default Input diff --git a/pac-man-board-game/ClientApp/src/components/layout.tsx b/pac-man-board-game/ClientApp/src/components/layout.tsx index fedac80..db69e25 100644 --- a/pac-man-board-game/ClientApp/src/components/layout.tsx +++ b/pac-man-board-game/ClientApp/src/components/layout.tsx @@ -1,13 +1,11 @@ -import React, {FC} from "react"; -import NavMenu from "./navMenu"; +import React, { FC } from "react" +import NavMenu from "./navMenu" -const Layout: FC = ({children}) => ( +const Layout: FC = ({ children }) => (
- -
- {children} -
+ +
{children}
-); +) -export default Layout; +export default Layout diff --git a/pac-man-board-game/ClientApp/src/components/navMenu.tsx b/pac-man-board-game/ClientApp/src/components/navMenu.tsx index 86e50a8..8b7c66e 100644 --- a/pac-man-board-game/ClientApp/src/components/navMenu.tsx +++ b/pac-man-board-game/ClientApp/src/components/navMenu.tsx @@ -1,84 +1,89 @@ -import React, {FC, useEffect} from "react"; -import {Link, useNavigate} from "react-router-dom"; -import {useAtom, useAtomValue} from "jotai"; -import {thisPlayerAtom} from "../utils/state"; -import {UserCircleIcon} from "@heroicons/react/24/outline"; -import useToggle from "../hooks/useToggle"; +import React, { FC, useEffect } from "react" +import { Link, useNavigate } from "react-router-dom" +import { useAtom, useAtomValue } from "jotai" +import { thisPlayerAtom } from "../utils/state" +import { UserCircleIcon } from "@heroicons/react/24/outline" +import useToggle from "../hooks/useToggle" const NavMenu: FC = () => { - const player = useAtomValue(thisPlayerAtom); + const player = useAtomValue(thisPlayerAtom) return (
- ); -}; + ) +} -export default NavMenu; +export default NavMenu -const NavItem: FC = ({to, children, className}) => ( +const NavItem: FC = ({ to, children, className }) => (
  • - {children} + + {children} +
  • -); +) -const ProfileDropdown: FC = ({className}) => { - const [player, setPlayer] = useAtom(thisPlayerAtom); - const [isOpened, toggleOpen] = useToggle(); - const navigate = useNavigate(); +const ProfileDropdown: FC = ({ className }) => { + const [player, setPlayer] = useAtom(thisPlayerAtom) + const [isOpened, toggleOpen] = useToggle() + const navigate = useNavigate() async function logout(): Promise { - setPlayer(undefined); - navigate("/login"); + setPlayer(undefined) + navigate("/login") } useEffect(() => { - if (isOpened) { function closeIfOutsideButton(e: MouseEvent): void { if (isOpened && e.target instanceof HTMLElement) { if (e.target.closest("#profile-dropdown") === null) { - toggleOpen(false); + toggleOpen(false) } } } - document.addEventListener("click", closeIfOutsideButton); - return () => document.removeEventListener("click", closeIfOutsideButton); + document.addEventListener("click", closeIfOutsideButton) + return () => document.removeEventListener("click", closeIfOutsideButton) } - - }, [isOpened]); + }, [isOpened]) return ( <> -
  • toggleOpen()}> - +
  • toggleOpen()}> + {player?.username}
  • - { - isOpened && -
    - -
    - } + {isOpened && ( +
    + +
    + )} ) } diff --git a/pac-man-board-game/ClientApp/src/components/playerStats.tsx b/pac-man-board-game/ClientApp/src/components/playerStats.tsx index 42bdabd..2fd7f30 100644 --- a/pac-man-board-game/ClientApp/src/components/playerStats.tsx +++ b/pac-man-board-game/ClientApp/src/components/playerStats.tsx @@ -1,29 +1,27 @@ -import React, {FC} from "react"; -import Player, {State} from "../game/player"; -import {useAtomValue} from "jotai"; -import {currentPlayerNameAtom} from "../utils/state"; +import React, { FC } from "react" +import Player, { State } from "../game/player" +import { useAtomValue } from "jotai" +import { currentPlayerNameAtom } from "../utils/state" -const PlayerStats: FC<{ player: Player } & ComponentProps> = ( - { - player, - className, - id - }) => { - const currentPlayerName = useAtomValue(currentPlayerNameAtom); +const PlayerStats: FC<{ player: Player } & ComponentProps> = ({ player, className, id }) => { + const currentPlayerName = useAtomValue(currentPlayerNameAtom) return ( -
    +

    Player: {player.username}

    Colour: {player.colour}

    - {player.state === State.inGame || player.state === State.disconnected ? + {player.state === State.inGame || player.state === State.disconnected ? ( <>

    Pellets: {player.box.pellets}

    PowerPellets: {player.box.powerPellets}

    - : -

    {player.state === State.waitingForPlayers ? "Waiting" : "Ready"}

    } + ) : ( +

    {player.state === State.waitingForPlayers ? "Waiting" : "Ready"}

    + )}
    - ); -}; + ) +} -export default PlayerStats; +export default PlayerStats diff --git a/pac-man-board-game/ClientApp/src/game/box.ts b/pac-man-board-game/ClientApp/src/game/box.ts index 0504976..81ba555 100644 --- a/pac-man-board-game/ClientApp/src/game/box.ts +++ b/pac-man-board-game/ClientApp/src/game/box.ts @@ -1,32 +1,31 @@ export default class Box implements BoxProps { - public readonly colour; - public pellets; - public powerPellets; + public readonly colour + public pellets + public powerPellets - public constructor({colour, pellets = 0, powerPellets = 0}: BoxProps) { - this.colour = colour; - this.pellets = pellets; - this.powerPellets = powerPellets; + public constructor({ colour, pellets = 0, powerPellets = 0 }: BoxProps) { + this.colour = colour + this.pellets = pellets + this.powerPellets = powerPellets } public addPellet(): void { - this.pellets++; + this.pellets++ } public removePellet(): boolean { - if (this.pellets <= 0) return false; - this.pellets--; - return true; + if (this.pellets <= 0) return false + this.pellets-- + return true } public addPowerPellet(): void { - this.powerPellets++; + this.powerPellets++ } public removePowerPellet(): boolean { - if (this.powerPellets <= 0) return false; - this.powerPellets--; - return true; + if (this.powerPellets <= 0) return false + this.powerPellets-- + return true } - } diff --git a/pac-man-board-game/ClientApp/src/game/character.ts b/pac-man-board-game/ClientApp/src/game/character.ts index 61ec36b..f2bdd93 100644 --- a/pac-man-board-game/ClientApp/src/game/character.ts +++ b/pac-man-board-game/ClientApp/src/game/character.ts @@ -1,5 +1,5 @@ -import {Direction} from "./direction"; -import {Colour} from "./colour"; +import { Direction } from "./direction" +import { Colour } from "./colour" export enum CharacterType { pacMan, @@ -8,89 +8,106 @@ export enum CharacterType { } export class Character implements CharacterProps { - public readonly colour; - public position; - public isEatable; - public readonly spawnPosition; - public readonly type; + public readonly colour + public position + public isEatable + public readonly spawnPosition + public readonly type - public constructor( - { - colour, - position = null, - type = CharacterType.dummy, - isEatable = type === CharacterType.pacMan, - spawnPosition = null - }: CharacterProps) { - this.colour = colour; - this.isEatable = isEatable; - this.spawnPosition = spawnPosition; + public constructor({ + colour, + position = null, + type = CharacterType.dummy, + isEatable = type === CharacterType.pacMan, + spawnPosition = null, + }: CharacterProps) { + this.colour = colour + this.isEatable = isEatable + this.spawnPosition = spawnPosition if (position) { - this.position = position; + this.position = position } else { - this.position = spawnPosition ? { - end: spawnPosition!.at, - direction: spawnPosition!.direction - } : null; + this.position = spawnPosition + ? { + end: spawnPosition!.at, + direction: spawnPosition!.direction, + } + : null } - this.type = type; + this.type = type } public follow(path: Path): void { if (!this.position) { - this.position = path; + this.position = path } else { - this.position.end = path.end; - this.position.direction = path.direction; - this.position.path = undefined; + this.position.end = path.end + this.position.direction = path.direction + this.position.path = undefined } } public isPacMan(): boolean { - return this.type === CharacterType.pacMan; + return this.type === CharacterType.pacMan } public isGhost(): boolean { - return this.type === CharacterType.ghost; + return this.type === CharacterType.ghost } public moveToSpawn(): void { - if (!this.spawnPosition) return; - this.follow({end: this.spawnPosition.at, direction: this.spawnPosition.direction}); + if (!this.spawnPosition) return + this.follow({ + end: this.spawnPosition.at, + direction: this.spawnPosition.direction, + }) } public isAt(position: Position): boolean { - return this.position !== null && this.position.end.x === position.x && this.position.end.y === position.y; + return this.position !== null && this.position.end.x === position.x && this.position.end.y === position.y } } export class PacMan extends Character implements CharacterProps { - - public constructor({colour, position, isEatable = true, spawnPosition, type = CharacterType.pacMan}: CharacterProps) { - super({colour: colour, position: position, isEatable: isEatable, spawnPosition: spawnPosition, type: type}); + public constructor({ + colour, + position, + isEatable = true, + spawnPosition, + type = CharacterType.pacMan, + }: CharacterProps) { + super({ + colour: colour, + position: position, + isEatable: isEatable, + spawnPosition: spawnPosition, + type: type, + }) } - } export class Ghost extends Character implements CharacterProps { - - public constructor({colour, position, isEatable, spawnPosition, type = CharacterType.ghost}: CharacterProps) { - super({colour: colour, position: position, isEatable: isEatable, spawnPosition: spawnPosition, type: type}); + public constructor({ colour, position, isEatable, spawnPosition, type = CharacterType.ghost }: CharacterProps) { + super({ + colour: colour, + position: position, + isEatable: isEatable, + spawnPosition: spawnPosition, + type: type, + }) } } export class Dummy extends Character implements CharacterProps { - public constructor(position: Path) { super({ colour: Colour.grey, position: position, isEatable: false, - spawnPosition: {at: {x: 0, y: 0}, direction: Direction.up}, + spawnPosition: { at: { x: 0, y: 0 }, direction: Direction.up }, type: CharacterType.dummy, - }); + }) } - } diff --git a/pac-man-board-game/ClientApp/src/game/colour.ts b/pac-man-board-game/ClientApp/src/game/colour.ts index fa6fa22..1b35109 100644 --- a/pac-man-board-game/ClientApp/src/game/colour.ts +++ b/pac-man-board-game/ClientApp/src/game/colour.ts @@ -8,4 +8,4 @@ export enum Colour { grey = "grey", } -export const getColours = (): Colour[] => Object.values(Colour); +export const getColours = (): Colour[] => Object.values(Colour) diff --git a/pac-man-board-game/ClientApp/src/game/direction.ts b/pac-man-board-game/ClientApp/src/game/direction.ts index a8580bd..f6f5ef2 100644 --- a/pac-man-board-game/ClientApp/src/game/direction.ts +++ b/pac-man-board-game/ClientApp/src/game/direction.ts @@ -2,8 +2,7 @@ export enum Direction { left, up, right, - down + down, } -export const getDirections = () => Object.values(Direction) - .filter(d => !isNaN(Number(d))) as Direction[]; +export const getDirections = () => Object.values(Direction).filter(d => !isNaN(Number(d))) as Direction[] diff --git a/pac-man-board-game/ClientApp/src/game/map.ts b/pac-man-board-game/ClientApp/src/game/map.ts index a264d43..12879fb 100644 --- a/pac-man-board-game/ClientApp/src/game/map.ts +++ b/pac-man-board-game/ClientApp/src/game/map.ts @@ -1,5 +1,5 @@ -import {CharacterType} from "./character"; -import {Direction} from "./direction"; +import { CharacterType } from "./character" +import { Direction } from "./direction" /** * 0 = empty @@ -25,29 +25,32 @@ export const customMap: GameMap = [ [1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1], -]; +] -export function getCharacterSpawns(map: GameMap): { type: CharacterType, position: DirectionalPosition }[] { - - const result: { type: CharacterType, position: DirectionalPosition }[] = []; +export function getCharacterSpawns(map: GameMap): { type: CharacterType; position: DirectionalPosition }[] { + const result: { type: CharacterType; position: DirectionalPosition }[] = [] for (let row = 0; row < map.length; row++) { for (let col = 0; col < map.length; col++) { // TODO find direction if (map[row][col] === 4) { - result.push({type: CharacterType.ghost, position: {at: {x: col, y: row}, direction: Direction.up}}); + result.push({ + type: CharacterType.ghost, + position: { at: { x: col, y: row }, direction: Direction.up }, + }) } else if (map[row][col] === 5) { result.push({ - type: CharacterType.pacMan, position: {at: {x: col, y: row}, direction: Direction.up} - }); + type: CharacterType.pacMan, + position: { at: { x: col, y: row }, direction: Direction.up }, + }) } } } - return result; + return result } export function getPacManSpawns(map: GameMap): DirectionalPosition[] { return getCharacterSpawns(map) .filter(s => s.type === CharacterType.pacMan) .map(s => s.position) -} \ No newline at end of file +} diff --git a/pac-man-board-game/ClientApp/src/game/player.ts b/pac-man-board-game/ClientApp/src/game/player.ts index ecec3b2..0c37c09 100644 --- a/pac-man-board-game/ClientApp/src/game/player.ts +++ b/pac-man-board-game/ClientApp/src/game/player.ts @@ -1,54 +1,57 @@ -import {Character, CharacterType} from "./character"; -import Box from "./box"; -import {getDefaultStore} from "jotai"; -import {currentPlayerNameAtom, playersAtom} from "../utils/state"; -import rules from "./rules"; +import { Character, CharacterType } from "./character" +import Box from "./box" +import { getDefaultStore } from "jotai" +import { currentPlayerNameAtom, playersAtom } from "../utils/state" +import rules from "./rules" export enum State { waitingForPlayers, ready, inGame, - disconnected + disconnected, } export default class Player implements PlayerProps { - private static store = getDefaultStore(); - public readonly username; - public readonly pacMan; - public readonly colour; - public readonly box; - public state; + private static store = getDefaultStore() + public readonly username + public readonly pacMan + public readonly colour + public readonly box + public state constructor(props: PlayerProps) { - this.username = props.username; - this.colour = props.colour; - this.box = new Box(props.box ?? {colour: props.colour}); - this.pacMan = new Character(props.pacMan ?? { - colour: props.colour, - type: CharacterType.pacMan - }); - this.state = props.state ?? State.waitingForPlayers; + this.username = props.username + this.colour = props.colour + this.box = new Box(props.box ?? { colour: props.colour }) + this.pacMan = new Character( + props.pacMan ?? { + colour: props.colour, + type: CharacterType.pacMan, + }, + ) + this.state = props.state ?? State.waitingForPlayers } public isTurn(): boolean { - return Player.store.get(currentPlayerNameAtom) === this.username; + return Player.store.get(currentPlayerNameAtom) === this.username } public addPellet(): void { - this.box.addPellet(); + this.box.addPellet() } public addPowerPellet(): void { - this.box.addPowerPellet(); + this.box.addPowerPellet() } public stealFrom(other: Player): void { for (let i = 0; i < rules.maxStealPellets; i++) { - const removed = other.box.removePellet(); - if (removed) - this.box.addPellet(); + const removed = other.box.removePellet() + if (removed) this.box.addPellet() } - Player.store.set(playersAtom, Player.store.get(playersAtom).map(player => player)); + Player.store.set( + playersAtom, + Player.store.get(playersAtom).map(player => player), + ) } - } diff --git a/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts b/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts index 61238f5..fb597bb 100644 --- a/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts +++ b/pac-man-board-game/ClientApp/src/game/possibleMovesAlgorithm.ts @@ -1,6 +1,6 @@ -import {TileType} from "./tileType"; -import {Character} from "./character"; -import {Direction, getDirections} from "./direction"; +import { TileType } from "./tileType" +import { Character } from "./character" +import { Direction, getDirections } from "./direction" /** * Finds all the possible positions for the character to move to @@ -10,10 +10,15 @@ import {Direction, getDirections} from "./direction"; * @param characters All the characters on the map * @returns An array of paths the character can move to */ -export default function findPossiblePositions(board: GameMap, character: Character, steps: number, characters: Character[]): Path[] { - if (!character.position || !character.spawnPosition) throw new Error("Character has no position or spawn position"); - return findPossibleRecursive(board, character.position, steps, character, characters); -}; +export default function findPossiblePositions( + board: GameMap, + character: Character, + steps: number, + characters: Character[], +): Path[] { + if (!character.position || !character.spawnPosition) throw new Error("Character has no position or spawn position") + return findPossibleRecursive(board, character.position, steps, character, characters) +} /** * Uses recursion to move through the map and find all the possible positions @@ -24,38 +29,40 @@ export default function findPossiblePositions(board: GameMap, character: Charact * @param characters All the characters on the map * @returns {Path[]} An array of paths the character can move to */ -function findPossibleRecursive(map: GameMap, currentPath: Path, steps: number, character: Character, characters: Character[]): Path[] { - - const paths: Path[] = []; +function findPossibleRecursive( + map: GameMap, + currentPath: Path, + steps: number, + character: Character, + characters: Character[], +): Path[] { + const paths: Path[] = [] if (isOutsideBoard(currentPath, map.length)) { if (character.isPacMan()) { - return addTeleportationTiles(map, currentPath, steps, character, characters); + return addTeleportationTiles(map, currentPath, steps, character, characters) } } else if (!isWall(map, currentPath)) { - if (!characterHitsAnotherCharacter(character, currentPath, characters)) { if (steps <= 0) { if (!(isSpawn(map, currentPath) && !isOwnSpawn(currentPath, character))) { - paths.push(currentPath); + paths.push(currentPath) } - } else { + tryAddToPath(currentPath) - tryAddToPath(currentPath); - - steps--; + steps-- for (const direction of getDirections()) { - paths.push(...tryMove(map, currentPath, direction, steps, character, characters)); + paths.push(...tryMove(map, currentPath, direction, steps, character, characters)) } } } else { - const pacMan = ghostHitsPacMan(character, currentPath, characters); + const pacMan = ghostHitsPacMan(character, currentPath, characters) if (pacMan instanceof Character && !isCharactersSpawn(currentPath, pacMan)) { - paths.push(currentPath); + paths.push(currentPath) } } } - return paths; + return paths } /** @@ -65,7 +72,7 @@ function findPossibleRecursive(map: GameMap, currentPath: Path, steps: number, c * @returns {boolean} True if the character is on its spawn, otherwise false */ function isCharactersSpawn(currentPath: Path, character: Character): boolean { - return character.spawnPosition?.at.x === currentPath.end.x && character.spawnPosition.at.y === currentPath.end.y; + return character.spawnPosition?.at.x === currentPath.end.x && character.spawnPosition.at.y === currentPath.end.y } /** @@ -75,8 +82,12 @@ function isCharactersSpawn(currentPath: Path, character: Character): boolean { * @param characters All the characters on the board * @returns {boolean} True if the character is a ghost and hits Pac-Man, otherwise false */ -function ghostHitsPacMan(character: Character, currentPath: Path, characters: Character[]): Character | undefined | false { - return character.isGhost() && characters.find(c => c.isPacMan() && c.isAt(currentPath.end)); +function ghostHitsPacMan( + character: Character, + currentPath: Path, + characters: Character[], +): Character | undefined | false { + return character.isGhost() && characters.find(c => c.isPacMan() && c.isAt(currentPath.end)) } /** @@ -87,7 +98,7 @@ function ghostHitsPacMan(character: Character, currentPath: Path, characters: Ch * @returns {boolean} True if the character hits another character, otherwise false */ function characterHitsAnotherCharacter(character: Character, currentPath: Path, characters: Character[]): boolean { - return characters.find(c => c !== character && c.isAt(currentPath.end)) !== undefined; + return characters.find(c => c !== character && c.isAt(currentPath.end)) !== undefined } /** @@ -96,9 +107,9 @@ function characterHitsAnotherCharacter(character: Character, currentPath: Path, */ function tryAddToPath(currentPos: Path): void { if (!currentPos.path) { - currentPos.path = []; + currentPos.path = [] } else if (!currentPos.path.includes(currentPos.end)) { - currentPos.path = [...currentPos.path, currentPos.end]; + currentPos.path = [...currentPos.path, currentPos.end] } } @@ -113,39 +124,53 @@ function tryAddToPath(currentPos: Path): void { * @param characters All the characters on the board * @returns An array of paths the character can move to */ -function tryMove(board: GameMap, path: Path, direction: Direction, steps: number, character: Character, characters: Character[]): Path[] { - +function tryMove( + board: GameMap, + path: Path, + direction: Direction, + steps: number, + character: Character, + characters: Character[], +): Path[] { function getNewPosition(): Position { switch (direction) { case Direction.left: return { x: path.end.x - 1, - y: path.end.y - }; + y: path.end.y, + } case Direction.up: return { x: path.end.x, - y: path.end.y - 1 - }; + y: path.end.y - 1, + } case Direction.right: return { x: path.end.x + 1, - y: path.end.y - }; + y: path.end.y, + } case Direction.down: return { x: path.end.x, - y: path.end.y + 1 - }; + y: path.end.y + 1, + } } } if (path.direction !== (direction + 2) % 4) { - return findPossibleRecursive(board, { - end: getNewPosition(), direction: direction, path: path.path - }, steps, character, characters); + return findPossibleRecursive( + board, + { + end: getNewPosition(), + direction: direction, + path: path.path, + }, + steps, + character, + characters, + ) } - return []; + return [] } /** @@ -157,22 +182,26 @@ function tryMove(board: GameMap, path: Path, direction: Direction, steps: number * @param characters All the characters on the map * @returns {Path[]} An array of paths the character can move to */ -function addTeleportationTiles(board: GameMap, currentPath: Path, steps: number, character: Character, characters: Character[]): Path[] { - const possiblePositions = findTeleportationTiles(board); - const paths: Path[] = []; +function addTeleportationTiles( + board: GameMap, + currentPath: Path, + steps: number, + character: Character, + characters: Character[], +): Path[] { + const possiblePositions = findTeleportationTiles(board) + const paths: Path[] = [] for (const pos of possiblePositions) { - function inInterval(coordinate: "x" | "y"): boolean { return pos.end[coordinate] !== interval(0, board.length - 1, currentPath.end[coordinate]) } if (inInterval("x") || inInterval("y")) { - - pos.path = currentPath.path; - paths.push(...findPossibleRecursive(board, pos, steps, character, characters)); + pos.path = currentPath.path + paths.push(...findPossibleRecursive(board, pos, steps, character, characters)) } } - return paths; + return paths } /** @@ -183,7 +212,7 @@ function addTeleportationTiles(board: GameMap, currentPath: Path, steps: number, * @returns {number} The value if it's between the lower and upper bounds, otherwise it returns the lower or upper bound */ function interval(lower: number, upper: number, value: number): number { - return Math.max(Math.min(value, upper), lower); + return Math.max(Math.min(value, upper), lower) } /** @@ -192,18 +221,17 @@ function interval(lower: number, upper: number, value: number): number { * @returns An array of paths containing the teleportation tiles */ function findTeleportationTiles(map: GameMap): Path[] { - const possiblePositions: Path[] = []; - const edge = [0, map.length - 1]; + const possiblePositions: Path[] = [] + const edge = [0, map.length - 1] for (const e of edge) { for (let i = 0; i < map[e].length; i++) { - - pushPath(map, possiblePositions, i, e); - pushPath(map, possiblePositions, e, i); + pushPath(map, possiblePositions, i, e) + pushPath(map, possiblePositions, e, i) } } - return possiblePositions; + return possiblePositions } /** @@ -215,7 +243,10 @@ function findTeleportationTiles(map: GameMap): Path[] { */ function pushPath(board: GameMap, possiblePositions: Path[], x: number, y: number): void { if (board[y] && board[y][x] !== TileType.wall) { - possiblePositions.push({end: {x: x, y: y}, direction: findDirection(x, y, board.length)}); + possiblePositions.push({ + end: { x: x, y: y }, + direction: findDirection(x, y, board.length), + }) } } @@ -226,17 +257,17 @@ function pushPath(board: GameMap, possiblePositions: Path[], x: number, y: numbe * @param boardSize The length of the board */ function findDirection(x: number, y: number, boardSize: number): Direction { - let direction: Direction; + let direction: Direction if (x === 0) { - direction = Direction.right; + direction = Direction.right } else if (y === 0) { - direction = Direction.down; + direction = Direction.down } else if (x === boardSize - 1) { - direction = Direction.left; + direction = Direction.left } else { - direction = Direction.up; + direction = Direction.up } - return direction; + return direction } /** @@ -245,8 +276,8 @@ function findDirection(x: number, y: number, boardSize: number): Direction { * @param boardSize The size of the board */ function isOutsideBoard(currentPos: Path, boardSize: number): boolean { - const pos = currentPos.end; - return pos.x < 0 || pos.x >= boardSize || pos.y < 0 || pos.y >= boardSize; + const pos = currentPos.end + return pos.x < 0 || pos.x >= boardSize || pos.y < 0 || pos.y >= boardSize } /** @@ -255,8 +286,8 @@ function isOutsideBoard(currentPos: Path, boardSize: number): boolean { * @param currentPos The current position of the character */ function isWall(board: GameMap, currentPos: Path): boolean { - const pos = currentPos.end; - return board[pos.y][pos.x] === TileType.wall; // Shouldn't work, but it does + const pos = currentPos.end + return board[pos.y][pos.x] === TileType.wall // Shouldn't work, but it does } /** @@ -265,8 +296,8 @@ function isWall(board: GameMap, currentPos: Path): boolean { * @param currentPos The current position of the character */ function isSpawn(board: GameMap, currentPos: Path) { - const pos = currentPos.end; - return board[pos.y][pos.x] === TileType.pacmanSpawn || board[pos.y][pos.x] === TileType.ghostSpawn; + const pos = currentPos.end + return board[pos.y][pos.x] === TileType.pacmanSpawn || board[pos.y][pos.x] === TileType.ghostSpawn } /** @@ -275,8 +306,7 @@ function isSpawn(board: GameMap, currentPos: Path) { * @param character The current character */ function isOwnSpawn(currentPos: Path, character: Character): boolean { - const pos = currentPos.end; - const charPos = character.spawnPosition!.at; - return charPos.x === pos.x && charPos.y === pos.y; + const pos = currentPos.end + const charPos = character.spawnPosition!.at + return charPos.x === pos.x && charPos.y === pos.y } - diff --git a/pac-man-board-game/ClientApp/src/game/rules.ts b/pac-man-board-game/ClientApp/src/game/rules.ts index c692819..b1668b7 100644 --- a/pac-man-board-game/ClientApp/src/game/rules.ts +++ b/pac-man-board-game/ClientApp/src/game/rules.ts @@ -4,4 +4,4 @@ const rules = { maxStealPellets: 2, } -export default rules; +export default rules diff --git a/pac-man-board-game/ClientApp/src/game/tileType.ts b/pac-man-board-game/ClientApp/src/game/tileType.ts index 2ea8ed0..e2fcefb 100644 --- a/pac-man-board-game/ClientApp/src/game/tileType.ts +++ b/pac-man-board-game/ClientApp/src/game/tileType.ts @@ -5,4 +5,4 @@ export enum TileType { powerPellet, ghostSpawn, pacmanSpawn, -} \ No newline at end of file +} diff --git a/pac-man-board-game/ClientApp/src/hooks/useToggle.ts b/pac-man-board-game/ClientApp/src/hooks/useToggle.ts index ef437ab..5e1cb91 100644 --- a/pac-man-board-game/ClientApp/src/hooks/useToggle.ts +++ b/pac-man-board-game/ClientApp/src/hooks/useToggle.ts @@ -1,4 +1,4 @@ -import {useState} from "react"; +import { useState } from "react" /** * A hook that returns a boolean value and a function to toggle it. The function can optionally be passed a boolean @@ -6,7 +6,7 @@ import {useState} from "react"; * @returns A tuple containing the boolean value and a function to toggle it. */ export default function useToggle(defaultValue = false): [boolean, (value?: boolean) => void] { - const [value, setValue] = useState(defaultValue); - const toggleValue = (newValue?: boolean) => newValue ? setValue(newValue) : setValue(!value); - return [value, toggleValue]; -} \ No newline at end of file + const [value, setValue] = useState(defaultValue) + const toggleValue = (newValue?: boolean) => (newValue ? setValue(newValue) : setValue(!value)) + return [value, toggleValue] +} diff --git a/pac-man-board-game/ClientApp/src/index.css b/pac-man-board-game/ClientApp/src/index.css index 7d18b5f..8a6cc40 100644 --- a/pac-man-board-game/ClientApp/src/index.css +++ b/pac-man-board-game/ClientApp/src/index.css @@ -3,35 +3,37 @@ @tailwind utilities; .debug { - @apply border border-red-500; - @apply after:content-['debug'] after:absolute; + @apply border border-red-500; + @apply after:content-['debug'] after:absolute; } .flex-center { - @apply flex justify-center items-center; + @apply flex justify-center items-center; } .inline-flex-center { - @apply inline-flex justify-center items-center; + @apply inline-flex justify-center items-center; } .wh-full { - @apply w-full h-full; + @apply w-full h-full; } h1 { - @apply text-4xl; + @apply text-4xl; } h2 { - @apply text-3xl; + @apply text-3xl; } br { - @apply my-2; + @apply my-2; } -.button-default, button[type=submit], input[type=submit] { - @apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded; - @apply disabled:bg-gray-500; +.button-default, +button[type="submit"], +input[type="submit"] { + @apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded; + @apply disabled:bg-gray-500; } diff --git a/pac-man-board-game/ClientApp/src/index.tsx b/pac-man-board-game/ClientApp/src/index.tsx index 215e774..e25335f 100644 --- a/pac-man-board-game/ClientApp/src/index.tsx +++ b/pac-man-board-game/ClientApp/src/index.tsx @@ -1,25 +1,26 @@ -import React from 'react'; -import {createRoot} from 'react-dom/client'; -import {BrowserRouter} from 'react-router-dom'; -import {App} from './App'; +import React from "react" +import { createRoot } from "react-dom/client" +import { BrowserRouter } from "react-router-dom" +import { App } from "./App" // @ts-ignore -import reportWebVitals from './reportWebVitals'; -import {DevTools} from "jotai-devtools"; -import DebugMenu from "./components/debugMenu"; +import reportWebVitals from "./reportWebVitals" +import { DevTools } from "jotai-devtools" +import DebugMenu from "./components/debugMenu" -const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); -const rootElement = document.getElementById('root'); -if (rootElement === null) throw new Error("Root element is null"); -const root = createRoot(rootElement); +const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href") +const rootElement = document.getElementById("root") +if (rootElement === null) throw new Error("Root element is null") +const root = createRoot(rootElement) root.render( - - - - ); + + + + , +) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals() diff --git a/pac-man-board-game/ClientApp/src/pages/counter.tsx b/pac-man-board-game/ClientApp/src/pages/counter.tsx index 91f5603..373176b 100644 --- a/pac-man-board-game/ClientApp/src/pages/counter.tsx +++ b/pac-man-board-game/ClientApp/src/pages/counter.tsx @@ -1,29 +1,27 @@ -import React, {FC} from "react"; -import WebSocketService from "../websockets/WebSocketService"; +import React, { FC } from "react" +import WebSocketService from "../websockets/WebSocketService" -const ws = new WebSocketService("wss://localhost:3000/api/ws"); +const ws = new WebSocketService("wss://localhost:3000/api/ws") export const Counter: FC = () => { + const [currentCount, setCurrentCount] = React.useState(0) - const [currentCount, setCurrentCount] = React.useState(0); - - async function incrementCounterAndSend() { + function incrementCounterAndSend() { if (ws.isOpen()) { - await ws.send((currentCount + 1).toString()); + ws.send((currentCount + 1).toString()) } } function receiveMessage(data: MessageEvent) { - const count = parseInt(data.data); - if (!isNaN(count)) - setCurrentCount(count); + const count = parseInt(data.data) + if (!isNaN(count)) setCurrentCount(count) } React.useEffect(() => { - ws.onReceive = receiveMessage; - ws.open(); - return () => ws.close(); - }, []); + ws.onReceive = receiveMessage + ws.open() + return () => ws.close() + }, []) return (
    @@ -31,9 +29,13 @@ export const Counter: FC = () => {

    This is a simple example of a React component.

    -

    Current count: {currentCount}

    +

    + Current count: {currentCount} +

    - +
    - ); -}; + ) +} diff --git a/pac-man-board-game/ClientApp/src/pages/game.tsx b/pac-man-board-game/ClientApp/src/pages/game.tsx index 752d4a6..5ea664f 100644 --- a/pac-man-board-game/ClientApp/src/pages/game.tsx +++ b/pac-man-board-game/ClientApp/src/pages/game.tsx @@ -1,17 +1,16 @@ -import React, {FC} from "react"; -import {GameComponent} from "../components/gameComponent"; -import {useAtomValue} from "jotai"; -import {selectedMapAtom, thisPlayerAtom} from "../utils/state"; +import React, { FC } from "react" +import { GameComponent } from "../components/gameComponent" +import { useAtomValue } from "jotai" +import { selectedMapAtom, thisPlayerAtom } from "../utils/state" const GamePage: FC = () => { - const player = useAtomValue(thisPlayerAtom); - const map = useAtomValue(selectedMapAtom); + const player = useAtomValue(thisPlayerAtom) + const map = useAtomValue(selectedMapAtom) if (player && map) { - return ; - } else { - return null; + return } -}; + return null +} -export default GamePage; +export default GamePage diff --git a/pac-man-board-game/ClientApp/src/pages/home.tsx b/pac-man-board-game/ClientApp/src/pages/home.tsx index 0916e8d..0d4a3c6 100644 --- a/pac-man-board-game/ClientApp/src/pages/home.tsx +++ b/pac-man-board-game/ClientApp/src/pages/home.tsx @@ -1,27 +1,35 @@ -import React, {FC} from "react"; -import {Link} from "react-router-dom"; -import {useAtomValue} from "jotai"; -import {thisPlayerAtom} from "../utils/state"; +import React, { FC } from "react" +import { Link } from "react-router-dom" +import { useAtomValue } from "jotai" +import { thisPlayerAtom } from "../utils/state" const HomePage: FC = () => { - const player = useAtomValue(thisPlayerAtom); + const player = useAtomValue(thisPlayerAtom) return (

    Hello {player?.username ?? "Player"}. Do you want to play a game?

    - {!player ? - <>Start by {" "} - logging in. + {!player ? ( + <> + Start by{" "} + + logging in + + . - : - <>Go to the {" "} - lobby to select a game. + ) : ( + <> + Go to the{" "} + + lobby + {" "} + to select a game. - } + )}

    - ); -}; + ) +} -export default HomePage; \ No newline at end of file +export default HomePage diff --git a/pac-man-board-game/ClientApp/src/pages/lobby.tsx b/pac-man-board-game/ClientApp/src/pages/lobby.tsx index 22bbd9a..a6084d5 100644 --- a/pac-man-board-game/ClientApp/src/pages/lobby.tsx +++ b/pac-man-board-game/ClientApp/src/pages/lobby.tsx @@ -1,66 +1,65 @@ -import React, {FC, Suspense} from "react"; -import {atom, useAtomValue} from "jotai"; -import {Button} from "../components/button"; -import {selectedMapAtom, thisPlayerAtom} from "../utils/state"; -import {getData, postData} from "../utils/api"; -import {getPacManSpawns} from "../game/map"; -import {useNavigate} from "react-router-dom"; +import React, { FC, Suspense } from "react" +import { atom, useAtomValue } from "jotai" +import { Button } from "../components/button" +import { selectedMapAtom, thisPlayerAtom } from "../utils/state" +import { getData, postData } from "../utils/api" +import { getPacManSpawns } from "../game/map" +import { useNavigate } from "react-router-dom" const fetchAtom = atom(async () => { - const response = await getData("/game/all"); - return await response.json() as Game[]; -}); + const response = await getData("/game/all") + return (await response.json()) as Game[] +}) const LobbyPage: FC = () => { - - const thisPlayer = useAtomValue(thisPlayerAtom); - const navigate = useNavigate(); - const map = useAtomValue(selectedMapAtom); + const thisPlayer = useAtomValue(thisPlayerAtom) + const navigate = useNavigate() + const map = useAtomValue(selectedMapAtom) async function createGame(): Promise { - const response = await postData("/game/create", { - body: {player: thisPlayer, spawns: getPacManSpawns(map)} as CreateGameData - }); + body: { + player: thisPlayer, + spawns: getPacManSpawns(map), + } as CreateGameData, + }) if (response.ok) { - const data = await response.json(); + const data = await response.json() navigate("/game/" + data.id) } else { - const data = await response.text(); - console.error("Error: ", data); + const data = await response.text() + console.error("Error: ", data) // TODO display error } - } return ( <> - + - ); + ) } -export default LobbyPage; +export default LobbyPage -const GameTable: FC = ({className}) => { - - const data = useAtomValue(fetchAtom); - const thisPlayer = useAtomValue(thisPlayerAtom); - const navigate = useNavigate(); +const GameTable: FC = ({ className }) => { + const data = useAtomValue(fetchAtom) + const thisPlayer = useAtomValue(thisPlayerAtom) + const navigate = useNavigate() async function joinGame(gameId: string): Promise { - if (thisPlayer === undefined) throw new Error("Player is undefined"); + if (thisPlayer === undefined) throw new Error("Player is undefined") - const result = await postData("/game/join/" + gameId, {body: thisPlayer}); + const result = await postData("/game/join/" + gameId, { body: thisPlayer }) if (result.ok) { - navigate("/game/" + gameId); + navigate("/game/" + gameId) } else { - console.error("Failed to join game " + gameId, await result.text()); + console.error("Failed to join game " + gameId, await result.text()) // TODO show error message } } @@ -68,33 +67,34 @@ const GameTable: FC = ({className}) => { return ( - - - - - - + + + + + + - {data?.map(game => - - - - - - - )} - { - data?.length === 0 && - - + {data?.map(game => ( + + + + + - } + ))} + {data?.length === 0 && ( + + + + )}
    IdCountStateJoin
    IdCountStateJoin
    {game.id}{game.count}{game.isGameStarted ? "Closed" : "Open"} - -
    No games found
    {game.id}{game.count}{game.isGameStarted ? "Closed" : "Open"} + +
    + No games found +
    - ); + ) } diff --git a/pac-man-board-game/ClientApp/src/pages/login.tsx b/pac-man-board-game/ClientApp/src/pages/login.tsx index 2e799aa..707980c 100644 --- a/pac-man-board-game/ClientApp/src/pages/login.tsx +++ b/pac-man-board-game/ClientApp/src/pages/login.tsx @@ -1,23 +1,22 @@ -import React, {FC, FormEvent, useState} from "react"; -import {Button} from "../components/button"; -import Input from "../components/input"; -import {useSetAtom} from "jotai"; -import {thisPlayerAtom} from "../utils/state"; -import Player from "../game/player"; -import {useNavigate} from "react-router-dom"; -import {postData} from "../utils/api"; +import React, { FC, FormEvent, useState } from "react" +import { Button } from "../components/button" +import Input from "../components/input" +import { useSetAtom } from "jotai" +import { thisPlayerAtom } from "../utils/state" +import Player from "../game/player" +import { useNavigate } from "react-router-dom" +import { postData } from "../utils/api" const LoginPage: FC = () => { - - const setThisPlayer = useSetAtom(thisPlayerAtom); - const navigate = useNavigate(); - const [error, setError] = useState(); + const setThisPlayer = useSetAtom(thisPlayerAtom) + const navigate = useNavigate() + const [error, setError] = useState() async function handleLogin(e: FormEvent): Promise { - e.preventDefault(); - const fields = e.currentTarget.querySelectorAll("input"); + e.preventDefault() + const fields = e.currentTarget.querySelectorAll("input") - let user: User = {username: "", password: ""}; + let user: User = { username: "", password: "" } for (const field of fields) { user = { ...user, @@ -26,36 +25,41 @@ const LoginPage: FC = () => { } const response = await postData("/player/login", { - body: {username: user.username, password: user.password} as User + body: { username: user.username, password: user.password } as User, }) - if (response.ok) { - const data = await response.json() as PlayerProps; - setThisPlayer(new Player(data)); - navigate("/lobby"); + const data = (await response.json()) as PlayerProps + setThisPlayer(new Player(data)) + navigate("/lobby") } else { - const data = await response.text(); - console.error(data); - setError(data); + const data = await response.text() + console.error(data) + setError(data) } - } - const username = "username", password = "password"; + const username = "username", + password = "password" return (

    Login

    {error &&

    {error}

    } - + - +
    - ); + ) } -export default LoginPage; +export default LoginPage diff --git a/pac-man-board-game/ClientApp/src/reportWebVitals.js b/pac-man-board-game/ClientApp/src/reportWebVitals.js index 532f29b..eee308d 100644 --- a/pac-man-board-game/ClientApp/src/reportWebVitals.js +++ b/pac-man-board-game/ClientApp/src/reportWebVitals.js @@ -1,13 +1,13 @@ -const reportWebVitals = (onPerfEntry) => { +const reportWebVitals = onPerfEntry => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry) + getFID(onPerfEntry) + getFCP(onPerfEntry) + getLCP(onPerfEntry) + getTTFB(onPerfEntry) + }) } -}; +} -export default reportWebVitals; +export default reportWebVitals diff --git a/pac-man-board-game/ClientApp/src/types/props.d.ts b/pac-man-board-game/ClientApp/src/types/props.d.ts index a903022..0f5d7da 100644 --- a/pac-man-board-game/ClientApp/src/types/props.d.ts +++ b/pac-man-board-game/ClientApp/src/types/props.d.ts @@ -1,25 +1,27 @@ -type FRComponent = React.ForwardRefExoticComponent & React.RefAttributes>; +type FRComponent = React.ForwardRefExoticComponent< + React.PropsWithoutRef & React.RefAttributes +> interface ComponentProps { - className?: string, - style?: React.CSSProperties, - id?: string, - title?: string, + className?: string + style?: React.CSSProperties + id?: string + title?: string } interface ChildProps extends ComponentProps { - children?: React.JSX.Element | string, + children?: React.JSX.Element | string } interface LinkProps extends ChildProps { - to: string, - newTab?: boolean, + to: string + newTab?: boolean } interface ButtonProps extends ChildProps { - onClick?: (event: React.MouseEvent) => void, - disabled?: boolean, - type?: "button" | "submit" | "reset", + onClick?: (event: React.MouseEvent) => void + disabled?: boolean + type?: "button" | "submit" | "reset" } interface InputProps extends ComponentProps { @@ -31,23 +33,23 @@ interface InputProps extends ComponentProps { } interface CharacterProps { - colour: import("../game/colour").Colour, - position?: Path | null, - isEatable?: boolean, - spawnPosition?: DirectionalPosition | null, - type?: import("../game/character").CharacterType, + colour: import("../game/colour").Colour + position?: Path | null + isEatable?: boolean + spawnPosition?: DirectionalPosition | null + type?: import("../game/character").CharacterType } interface BoxProps { - pellets?: number, - powerPellets?: number, - readonly colour: import("../game/colour").Colour, + pellets?: number + powerPellets?: number + readonly colour: import("../game/colour").Colour } interface PlayerProps { - readonly username: string, - readonly pacMan?: CharacterProps, - readonly colour: import("../game/colour").Colour, - readonly box?: BoxProps, - state?: import("../game/player").State, + readonly username: string + readonly pacMan?: CharacterProps + readonly colour: import("../game/colour").Colour + readonly box?: BoxProps + state?: import("../game/player").State } diff --git a/pac-man-board-game/ClientApp/src/types/types.d.ts b/pac-man-board-game/ClientApp/src/types/types.d.ts index 00ea66d..1c961bb 100644 --- a/pac-man-board-game/ClientApp/src/types/types.d.ts +++ b/pac-man-board-game/ClientApp/src/types/types.d.ts @@ -1,68 +1,68 @@ -type MessageEventFunction = (data: MessageEvent) => void; +type MessageEventFunction = (data: MessageEvent) => void -type Setter = React.Dispatch>; +type Setter = React.Dispatch> -type GUID = `${string}-${string}-${string}-${string}-${string}`; +type GUID = `${string}-${string}-${string}-${string}-${string}` -type WebSocketData = string | ArrayBufferLike | Blob | ArrayBufferView; +type WebSocketData = string | ArrayBufferLike | Blob | ArrayBufferView type ActionMessage = { - readonly action: import("../utils/actions").GameAction, + readonly action: import("../utils/actions").GameAction readonly data?: T } -type Action = (obj: T) => void; +type Action = (obj: T) => void -type BiAction = (obj1: T1, obj2: T2) => void; +type BiAction = (obj1: T1, obj2: T2) => void -type Predicate = (obj: T) => boolean; +type Predicate = (obj: T) => boolean type SelectedDice = { - value: number, + value: number index: number -}; +} -type Position = { x: number, y: number }; +type Position = { x: number; y: number } -type GameMap = number[][]; +type GameMap = number[][] type DirectionalPosition = { - at: Position, + at: Position direction: import("../game/direction").Direction } type Path = { - path?: Position[] | null, + path?: Position[] | null // TODO replace with DirectionalPosition - end: Position, + end: Position direction: import("../game/direction").Direction } type Game = { - readonly id: string, - readonly count: number, - readonly isGameStarted: boolean, + readonly id: string + readonly count: number + readonly isGameStarted: boolean } type User = { - readonly username: string, - readonly password: string, + readonly username: string + readonly password: string readonly colour?: import("../game/colour").Colour } -type Api = (path: string, data?: ApiRequest & T) => Promise; +type Api = (path: string, data?: ApiRequest & T) => Promise type ApiRequest = { - headers?: HeadersInit, + headers?: HeadersInit body?: any } type JoinGameData = { - readonly username: string, - readonly gameId: GUID, + readonly username: string + readonly gameId: GUID } type CreateGameData = { - readonly player: PlayerProps, - readonly spawns: DirectionalPosition[], + readonly player: PlayerProps + readonly spawns: DirectionalPosition[] } diff --git a/pac-man-board-game/ClientApp/src/utils/actions.ts b/pac-man-board-game/ClientApp/src/utils/actions.ts index 7e2f610..9792c70 100644 --- a/pac-man-board-game/ClientApp/src/utils/actions.ts +++ b/pac-man-board-game/ClientApp/src/utils/actions.ts @@ -1,10 +1,10 @@ -import Player from "../game/player"; -import {CharacterType, Ghost} from "../game/character"; -import {getCharacterSpawns} from "../game/map"; -import {TileType} from "../game/tileType"; -import {getDefaultStore} from "jotai"; -import {currentPlayerNameAtom, diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom, selectedMapAtom} from "./state"; -import {Colour} from "../game/colour"; +import Player from "../game/player" +import { CharacterType, Ghost } from "../game/character" +import { getCharacterSpawns } from "../game/map" +import { TileType } from "../game/tileType" +import { getDefaultStore } from "jotai" +import { currentPlayerNameAtom, diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom, selectedMapAtom } from "./state" +import { Colour } from "../game/colour" export enum GameAction { error, @@ -17,107 +17,112 @@ export enum GameAction { // TODO add updatePellets } -const store = getDefaultStore(); -const map = store.get(selectedMapAtom); +const store = getDefaultStore() +const map = store.get(selectedMapAtom) -const ghostsProps: CharacterProps[] = [ - {colour: Colour.purple}, - {colour: Colour.purple}, -]; -let spawns = getCharacterSpawns(map).filter(spawn => spawn.type === CharacterType.ghost); +const ghostsProps: CharacterProps[] = [{ colour: Colour.purple }, { colour: Colour.purple }] +let spawns = getCharacterSpawns(map).filter(spawn => spawn.type === CharacterType.ghost) ghostsProps.forEach(ghost => { - ghost.spawnPosition = spawns.pop()?.position; + ghost.spawnPosition = spawns.pop()?.position +}) +const ghosts = ghostsProps.map(props => new Ghost(props)) -}); -const ghosts = ghostsProps.map(props => new Ghost(props)); - -store.set(ghostsAtom, ghosts); +store.set(ghostsAtom, ghosts) export const doAction: MessageEventFunction = (event): void => { - const message: ActionMessage = JSON.parse(event.data); - console.debug("Received message:", message); + const message: ActionMessage = JSON.parse(event.data) + console.debug("Received message:", message) switch (message.action as GameAction) { case GameAction.error: - console.error("Error:", message.data); - break; + console.error("Error:", message.data) + break case GameAction.rollDice: - setDice(message.data); - break; + setDice(message.data) + break case GameAction.moveCharacter: - moveCharacter(message.data); - break; + moveCharacter(message.data) + break case GameAction.joinGame: - joinGame(message.data); - break; + joinGame(message.data) + break case GameAction.ready: - ready(message.data); - break; + ready(message.data) + break case GameAction.nextPlayer: - nextPlayer(message.data); - break; + nextPlayer(message.data) + break case GameAction.disconnect: - updatePlayers(message.data); - break; + updatePlayers(message.data) + break } -}; - -function setDice(data?: number[]): void { - store.set(diceAtom, data); } -type MoveCharacterData = { dice: number[], players: PlayerProps[], ghosts: CharacterProps[], eatenPellets: Position[] }; +function setDice(data?: number[]): void { + store.set(diceAtom, data) +} + +type MoveCharacterData = { + dice: number[] + players: PlayerProps[] + ghosts: CharacterProps[] + eatenPellets: Position[] +} function moveCharacter(data?: MoveCharacterData): void { - store.set(diceAtom, data?.dice); - updatePlayers(data?.players); - updateGhosts(data); - removeEatenPellets(data); + store.set(diceAtom, data?.dice) + updatePlayers(data?.players) + updateGhosts(data) + removeEatenPellets(data) } function updatePlayers(updatedPlayers?: PlayerProps[]): void { if (updatedPlayers) { - const newList: Player[] = updatedPlayers.map(p => new Player(p)); - store.set(playersAtom, newList); + const newList: Player[] = updatedPlayers.map(p => new Player(p)) + store.set(playersAtom, newList) } } function updateGhosts(data?: MoveCharacterData): void { - const updatedGhosts = data?.ghosts; + const updatedGhosts = data?.ghosts if (updatedGhosts) { - const newList: Ghost[] = updatedGhosts.map(g => new Ghost(g)); - store.set(ghostsAtom, newList); + const newList: Ghost[] = updatedGhosts.map(g => new Ghost(g)) + store.set(ghostsAtom, newList) } } function removeEatenPellets(data?: MoveCharacterData): void { - const pellets = data?.eatenPellets; + const pellets = data?.eatenPellets for (const pellet of pellets ?? []) { - map[pellet.y][pellet.x] = TileType.empty; + map[pellet.y][pellet.x] = TileType.empty } } -function joinGame(data?: PlayerProps[]): void { // TODO missing data when refreshing page - const playerProps = data ?? []; - spawns = getCharacterSpawns(map).filter(spawn => spawn.type === CharacterType.pacMan); - store.set(playersAtom, playerProps.map(p => new Player(p))); +function joinGame(data?: PlayerProps[]): void { + // TODO missing data when refreshing page + const playerProps = data ?? [] + spawns = getCharacterSpawns(map).filter(spawn => spawn.type === CharacterType.pacMan) + store.set( + playersAtom, + playerProps.map(p => new Player(p)), + ) } -type ReadyData = { allReady: boolean, players: PlayerProps[] }; +type ReadyData = { allReady: boolean; players: PlayerProps[] } function ready(data?: ReadyData): void { if (data) { - const players = data.players.map(p => new Player(p)); - store.set(playersAtom, players); + const players = data.players.map(p => new Player(p)) + store.set(playersAtom, players) if (data.allReady) { - store.set(currentPlayerNameAtom, data.players[0].username); + store.set(currentPlayerNameAtom, data.players[0].username) } } } function nextPlayer(currentPlayerName?: string): void { - store.set(currentPlayerNameAtom, currentPlayerName); - store.set(rollDiceButtonAtom, true); + store.set(currentPlayerNameAtom, currentPlayerName) + store.set(rollDiceButtonAtom, true) } diff --git a/pac-man-board-game/ClientApp/src/utils/api.ts b/pac-man-board-game/ClientApp/src/utils/api.ts index b9ab872..25eaa4d 100644 --- a/pac-man-board-game/ClientApp/src/utils/api.ts +++ b/pac-man-board-game/ClientApp/src/utils/api.ts @@ -1,12 +1,12 @@ -export const getData: Api = async (path, {headers} = {}) => { - if (import.meta.env.MODE === "test") return Promise.resolve(new Response(JSON.stringify([]))); +export const getData: Api = async (path, { headers } = {}) => { + if (import.meta.env.MODE === "test") return Promise.resolve(new Response(JSON.stringify([]))) return await fetch(import.meta.env.VITE_API_HTTP + path, { method: "GET", - headers: headers - }); + headers: headers, + }) } -export const postData: Api = async (path, {body, headers} = {}) => { +export const postData: Api = async (path, { body, headers } = {}) => { return await fetch(import.meta.env.VITE_API_HTTP + path, { method: "POST", headers: { diff --git a/pac-man-board-game/ClientApp/src/utils/state.ts b/pac-man-board-game/ClientApp/src/utils/state.ts index e1134b3..5715b46 100644 --- a/pac-man-board-game/ClientApp/src/utils/state.ts +++ b/pac-man-board-game/ClientApp/src/utils/state.ts @@ -1,74 +1,75 @@ -import Player from "../game/player"; -import {atom} from "jotai"; -import {Ghost} from "../game/character"; -import {customMap} from "../game/map"; +import Player from "../game/player" +import { atom } from "jotai" +import { Ghost } from "../game/character" +import { customMap } from "../game/map" -const playerStorage = "player"; +const playerStorage = "player" /** * All players in the game. */ -export const playersAtom = atom([]); +export const playersAtom = atom([]) /** * All player characters (Pac-Man) in the game. */ -export const playerCharactersAtom = atom(get => get(playersAtom).map(player => player.pacMan)); +export const playerCharactersAtom = atom(get => get(playersAtom).map(player => player.pacMan)) /** * All ghosts in the game. */ -export const ghostsAtom = atom([]); +export const ghostsAtom = atom([]) /** * All characters in the game. */ -export const allCharactersAtom = atom(get => [...get(playerCharactersAtom), ...get(ghostsAtom)]); +export const allCharactersAtom = atom(get => [...get(playerCharactersAtom), ...get(ghostsAtom)]) /** * The player that is currently logged in. */ -const playerAtom = atom(undefined); +const playerAtom = atom(undefined) /** * Gets a getter and setter to get or set the player that is currently logged in. * Returns A tuple containing a getter and setter to get or set the player that is currently logged in. */ -export const thisPlayerAtom = atom(get => { - const atomValue = get(playerAtom); - if (!atomValue) { - const item = sessionStorage.getItem(playerStorage); - if (item) { - const playerProps = JSON.parse(item) as PlayerProps; - return new Player(playerProps); +export const thisPlayerAtom = atom( + get => { + const atomValue = get(playerAtom) + if (!atomValue) { + const item = sessionStorage.getItem(playerStorage) + if (item) { + const playerProps = JSON.parse(item) as PlayerProps + return new Player(playerProps) + } } - } - return atomValue; -}, (_get, set, player: Player | undefined) => { - if (player) - sessionStorage.setItem(playerStorage, JSON.stringify(player)); - else - sessionStorage.removeItem(playerStorage); - set(playerAtom, player); -}); + return atomValue + }, + (_get, set, player: Player | undefined) => { + if (player) sessionStorage.setItem(playerStorage, JSON.stringify(player)) + else sessionStorage.removeItem(playerStorage) + set(playerAtom, player) + }, +) /** * All dice that have been rolled. */ -export const diceAtom = atom(undefined); +export const diceAtom = atom(undefined) /** * The dice that have been selected by the player. */ -export const selectedDiceAtom = atom(undefined); +export const selectedDiceAtom = atom(undefined) /** * The name of the player whose turn it is. */ -export const currentPlayerNameAtom = atom(undefined); +export const currentPlayerNameAtom = atom(undefined) /** * The player whose turn it is. */ export const currentPlayerAtom = atom(get => { - const currentPlayerName = get(currentPlayerNameAtom); - return get(playersAtom).find(player => player.username === currentPlayerName); -}); + const currentPlayerName = get(currentPlayerNameAtom) + return get(playersAtom).find(player => player.username === currentPlayerName) +}) /** * Whether the roll dice button should be enabled. */ -export const rollDiceButtonAtom = atom(true); +export const rollDiceButtonAtom = atom(true) /** * The map that is currently selected. */ -export const selectedMapAtom = atom(customMap); +export const selectedMapAtom = atom(customMap) diff --git a/pac-man-board-game/ClientApp/src/utils/utils.ts b/pac-man-board-game/ClientApp/src/utils/utils.ts index 0c03df9..35ffaef 100644 --- a/pac-man-board-game/ClientApp/src/utils/utils.ts +++ b/pac-man-board-game/ClientApp/src/utils/utils.ts @@ -5,14 +5,14 @@ * @returns A promise that resolves when the predicate is true. */ export function wait(predicate: Predicate, timeout: number = 50): Promise { - return new Promise((resolve) => { + return new Promise(resolve => { const f = () => { if (predicate()) { - return resolve(); + return resolve() } - setTimeout(f, timeout); - }; + setTimeout(f, timeout) + } - f(); - }); + f() + }) } diff --git a/pac-man-board-game/ClientApp/src/vite-env.d.ts b/pac-man-board-game/ClientApp/src/vite-env.d.ts index 68c1e06..5bfcd16 100644 --- a/pac-man-board-game/ClientApp/src/vite-env.d.ts +++ b/pac-man-board-game/ClientApp/src/vite-env.d.ts @@ -1,11 +1,11 @@ /// interface ImportMetaEnv { - readonly VITE_API_URI: string, - readonly VITE_API_HTTP: string, - readonly VITE_API_WS: string, + readonly VITE_API_URI: string + readonly VITE_API_HTTP: string + readonly VITE_API_WS: string } interface ImportMeta { - readonly env: ImportMetaEnv; -} \ No newline at end of file + readonly env: ImportMetaEnv +} diff --git a/pac-man-board-game/ClientApp/src/websockets/WebSocketService.ts b/pac-man-board-game/ClientApp/src/websockets/WebSocketService.ts index 42cd93e..b78226d 100644 --- a/pac-man-board-game/ClientApp/src/websockets/WebSocketService.ts +++ b/pac-man-board-game/ClientApp/src/websockets/WebSocketService.ts @@ -1,9 +1,9 @@ -import {wait} from "../utils/utils"; +import { wait } from "../utils/utils" interface IWebSocket { - onOpen?: VoidFunction, - onReceive?: MessageEventFunction, - onClose?: VoidFunction, + onOpen?: VoidFunction + onReceive?: MessageEventFunction + onClose?: VoidFunction onError?: VoidFunction } @@ -11,59 +11,59 @@ interface IWebSocket { * WebSocketService class provides a WebSocket client interface for easy communication with a WebSocket server. */ export default class WebSocketService { - private ws?: WebSocket; - private readonly _url: string; + private ws?: WebSocket + private readonly _url: string - constructor(url: string, {onOpen, onReceive, onClose, onError}: IWebSocket = {}) { - this._url = url; - this._onOpen = onOpen; - this._onReceive = onReceive; - this._onClose = onClose; - this._onError = onError; + constructor(url: string, { onOpen, onReceive, onClose, onError }: IWebSocket = {}) { + this._url = url + this._onOpen = onOpen + this._onReceive = onReceive + this._onClose = onClose + this._onError = onError } - private _onOpen?: VoidFunction; + private _onOpen?: VoidFunction set onOpen(onOpen: VoidFunction) { - this._onOpen = onOpen; - if (!this.ws) return; - this.ws.onopen = onOpen; + this._onOpen = onOpen + if (!this.ws) return + this.ws.onopen = onOpen } - private _onReceive?: MessageEventFunction; + private _onReceive?: MessageEventFunction set onReceive(onReceive: MessageEventFunction) { - this._onReceive = onReceive; - if (!this.ws) return; - this.ws.onmessage = onReceive; + this._onReceive = onReceive + if (!this.ws) return + this.ws.onmessage = onReceive } - private _onClose?: VoidFunction; + private _onClose?: VoidFunction set onClose(onClose: VoidFunction) { - this._onClose = onClose; - if (!this.ws) return; - this.ws.onclose = onClose; + this._onClose = onClose + if (!this.ws) return + this.ws.onclose = onClose } - private _onError?: VoidFunction; + private _onError?: VoidFunction set onError(onError: VoidFunction) { - this._onError = onError; - if (!this.ws) return; - this.ws.onerror = onError; + this._onError = onError + if (!this.ws) return + this.ws.onerror = onError } /** * Opens a WebSocket connection with the specified URL and sets the event callbacks. */ public open(): void { - if (typeof WebSocket === "undefined" || this.isConnecting()) return; - this.ws = new WebSocket(this._url); - if (this._onOpen) this.ws.onopen = this._onOpen; - if (this._onReceive) this.ws.onmessage = this._onReceive; - if (this._onClose) this.ws.onclose = this._onClose; - if (this._onError) this.ws.onerror = this._onError; + if (typeof WebSocket === "undefined" || this.isConnecting()) return + this.ws = new WebSocket(this._url) + if (this._onOpen) this.ws.onopen = this._onOpen + if (this._onReceive) this.ws.onmessage = this._onReceive + if (this._onClose) this.ws.onclose = this._onClose + if (this._onError) this.ws.onerror = this._onError } /** @@ -72,8 +72,8 @@ export default class WebSocketService { * @returns {Promise} - A promise that resolves when the "isOpen" condition is met. */ public async waitForOpen(): Promise { - await wait(() => this.isOpen()); - if (this._onOpen) this.onOpen = this._onOpen; + await wait(() => this.isOpen()) + if (this._onOpen) this.onOpen = this._onOpen } /** @@ -83,16 +83,16 @@ export default class WebSocketService { */ public send(data: ActionMessage | string): void { if (typeof data !== "string") { - data = JSON.stringify(data); + data = JSON.stringify(data) } - this.ws?.send(data); + this.ws?.send(data) } /** * Closes the WebSocket connection. */ public close(): void { - this.ws?.close(); + this.ws?.close() } /** @@ -100,7 +100,7 @@ export default class WebSocketService { * @returns {boolean} Returns true if the WebSocket is open, otherwise false. */ public isOpen(): boolean { - return this.ws?.readyState === WebSocket?.OPEN; + return this.ws?.readyState === WebSocket?.OPEN } /** @@ -109,7 +109,7 @@ export default class WebSocketService { * @returns {boolean} - Returns 'true' if the WebSocket is connecting, otherwise 'false'. */ public isConnecting(): boolean { - return this.ws?.readyState === WebSocket?.CONNECTING; + return this.ws?.readyState === WebSocket?.CONNECTING } /** @@ -118,6 +118,6 @@ export default class WebSocketService { * @returns {boolean} Returns true if the WebSocket connection is closed, false otherwise. */ public isClosed(): boolean { - return this.ws?.readyState === WebSocket?.CLOSED; + return this.ws?.readyState === WebSocket?.CLOSED } } diff --git a/pac-man-board-game/Controllers/GameController.cs b/pac-man-board-game/Controllers/GameController.cs index c905ce4..4a289db 100644 --- a/pac-man-board-game/Controllers/GameController.cs +++ b/pac-man-board-game/Controllers/GameController.cs @@ -69,7 +69,7 @@ public class GameController : GenericController } catch (Exception e) { - return BadRequest(e.Message); // TODO not necessary? + return BadRequest(e.Message); } }