Refactored

This commit is contained in:
Martin Berg Alstad 2023-05-21 21:42:21 +02:00
parent 44192ea3fe
commit a16de14d54
11 changed files with 54 additions and 140 deletions

View File

@ -41,13 +41,14 @@ const Board: Component<BoardProps> = (
const [tileSize, setTileSize] = useState(2);
const [selectedCharacter, setSelectedCharacter] = useState<Character>();
const [possiblePositions, setPossiblePositions] = useState<CharacterPosition[]>([]);
// TODO show the paths to the positions when hovering over a possible position (type Path = CharacterPosition[])
const [possiblePositions, setPossiblePositions] = useState<Position[]>([]);
function handleSelectCharacter(character: Character): void {
setSelectedCharacter(character);
}
function handleMoveCharacter(position: CharacterPosition): void {
function handleMoveCharacter(position: Position): void {
if (selectedCharacter) {
selectedCharacter.moveTo(position);
onMove?.(selectedCharacter);

View File

@ -1,11 +1,11 @@
import React, {useEffect, useRef, useState} from "react";
import Game from "../game/game";
import {AllDice} from "./dice";
import {Action} from "../websockets/actions";
import GameBoard from "./gameBoard";
import {Character, Ghost, PacMan} from "../game/character";
import WebSocketService from "../websockets/WebSocketService";
let game: Game;
const wsService = new WebSocketService("wss://localhost:3000/api/game");
export const GameComponent: Component = () => {
// Better for testing than outside of the component
@ -16,39 +16,38 @@ export const GameComponent: Component = () => {
function handleDiceClick(selected: SelectedDice): void {
setSelectedDice(selected);
game.selectedDice = selected;
}
function rollDice(): void {
wsService.send({Action: Action.rollDice});
}
function startGameLoop(): void {
setSelectedDice(undefined);
if (!game.isConnected()) {
if (!wsService.isOpen()) {
setTimeout(startGameLoop, 50);
return;
}
void game.gameLoop(setDice);
rollDice();
}
function updateState(): void {
game.wsService.onReceive = (message) => {
const parsed: ActionMessage = JSON.parse(message.data);
function doAction(message: MessageEvent<string>): void {
const parsed: ActionMessage = JSON.parse(message.data);
switch (parsed.Action) {
case Action.rollDice:
setDice(parsed.Data as number[]); // Updates the state of other players
break;
case Action.moveCharacter:
setDice(parsed.Data?.dice as number[]);
const character = parsed.Data?.character as Character;
characters.current.find(c => c.color === character.color)?.moveTo(character.position);
break;
}
};
switch (parsed.Action) {
case Action.rollDice:
setDice(parsed.Data as number[]);
break;
case Action.moveCharacter:
setDice(parsed.Data?.dice as number[]);
const character = parsed.Data?.character as Character;
characters.current.find(c => c.color === character.color)?.moveTo(character.position);
break;
}
}
function onCharacterMove(character: Character): void {
if (dice && selectedDice) {
// Remove the dice that was used from the list of dice
dice.splice(selectedDice.index, 1);
setDice([...dice]);
}
@ -60,16 +59,15 @@ export const GameComponent: Component = () => {
character: character
}
};
game.wsService.send(data);
wsService.send(data);
}
useEffect(() => {
game = new Game();
updateState();
game.connectToServer();
wsService.onReceive = doAction;
wsService.open();
startGameLoop();
return () => game.disconnect();
return () => wsService.close();
}, []);
return (

View File

@ -2,29 +2,29 @@ type CharacterColor = "red" | "blue" | "yellow" | "green" | "purple";
export abstract class Character {
public color: CharacterColor;
public position: CharacterPosition;
public position: Position;
public constructor(color: CharacterColor, startPosition: CharacterPosition = {x: 0, y: 0}) {
public constructor(color: CharacterColor, startPosition: Position = {x: 0, y: 0}) {
this.color = color;
this.position = startPosition;
}
public abstract moveTo(position: CharacterPosition): void;
public abstract moveTo(position: Position): void;
public isAt(position: CharacterPosition): boolean {
public isAt(position: Position): boolean {
return this.position.x === position.x && this.position.y === position.y;
}
}
export class PacMan extends Character {
moveTo(position: CharacterPosition): void {
moveTo(position: Position): void {
this.position = position;
}
}
export class Ghost extends Character {
moveTo(position: CharacterPosition): void {
moveTo(position: Position): void {
this.position = position;
}

View File

@ -1,79 +0,0 @@
import WebSocketService from "../websockets/WebSocketService";
import {Action} from "../websockets/actions";
export default class Game {
private readonly _wsService: WebSocketService;
public selectedDice?: SelectedDice;
constructor() {
this._wsService = new WebSocketService("wss://localhost:3000/api/game");
// Connect to the server
// Create players
// Pick player pieces
// Roll to start
}
public async gameLoop(setDice: Setter<number[] | undefined>): Promise<void> {
// Throw the dice
const result = await this.rollDice();
const dice = result.Data;
setDice(dice); // Updates the state of the current player
// Use the remaining dice to move pac-man if the player moved a ghost or vice versa
// Check if the game is over
// If not, next player
}
public connectToServer(): void {
this._wsService.open();
this._wsService.registerEvents();
}
public isConnected(): boolean {
return this._wsService.isOpen();
}
public disconnect(): void {
this._wsService.close();
}
private createPlayers(): void {
throw new Error("Not implemented");
}
private pickPlayerPieces(): void {
throw new Error("Not implemented");
}
private rollToStart(): void {
throw new Error("Not implemented");
}
private async rollDice(): Promise<ActionMessage<number[]>> {
let result: ActionMessage<number[]>;
result = await this._wsService.sendAndReceive<ActionMessage<number[]>>({Action: Action.rollDice});
return result;
}
private isGameOver(): boolean {
throw new Error("Not implemented");
}
private nextPlayer(): void {
throw new Error("Not implemented");
}
private chooseCharacter(): void {
throw new Error("Method not implemented.");
}
get wsService(): WebSocketService {
return this._wsService;
}
}

View File

@ -6,14 +6,14 @@ import {TileType} from "./tileType";
* @param currentPos The current position of the character
* @param steps The number of steps the character can move
*/
export default function findPossiblePositions(board: number[][], currentPos: CharacterPosition, steps: number): CharacterPosition[] {
const possiblePositions: CharacterPosition[] = [];
export default function findPossiblePositions(board: number[][], currentPos: Position, steps: number): Position[] {
const possiblePositions: Position[] = [];
findPossibleRecursive(board, currentPos, steps, possiblePositions, []);
return possiblePositions;
}
function findPossibleRecursive(board: number[][], currentPos: CharacterPosition, steps: number,
possibleList: CharacterPosition[], visitedTiles: CharacterPosition[]): CharacterPosition | null {
function findPossibleRecursive(board: number[][], currentPos: Position, steps: number,
possibleList: Position[], visitedTiles: Position[]): Position | null {
if (isOutsideBoard(currentPos, board.length)) {
addTeleportationTiles(board, currentPos, steps, possibleList, visitedTiles);
} else if (visitedTiles.find(tile => tile.x === currentPos.x && tile.y === currentPos.y)) {
@ -36,9 +36,9 @@ function findPossibleRecursive(board: number[][], currentPos: CharacterPosition,
return null;
}
function addTeleportationTiles(board: number[][], currentPos: CharacterPosition, steps: number,
possibleList: CharacterPosition[], visitedTiles: CharacterPosition[]): void {
const newPositons: (CharacterPosition | null)[] = [];
function addTeleportationTiles(board: number[][], currentPos: Position, steps: number,
possibleList: Position[], visitedTiles: Position[]): void {
const newPositons: (Position | null)[] = [];
const possiblePositions = findTeleportationTiles(board);
for (const pos of possiblePositions) {
if (pos.x !== Math.max(currentPos.x, 0) || pos.y !== Math.max(currentPos.y, 0)) {
@ -48,7 +48,7 @@ function addTeleportationTiles(board: number[][], currentPos: CharacterPosition,
pushToList(board, possibleList, newPositons);
}
function pushToList(board: number[][], list: CharacterPosition[], newEntries: (CharacterPosition | null)[]): void {
function pushToList(board: number[][], list: Position[], newEntries: (Position | null)[]): void {
for (const entry of newEntries) {
if (entry !== null && !list.find(p => p.x === entry.x && p.y === entry.y) && !isOutsideBoard(entry, board.length) && !isSpawn(board, entry)) {
list.push(entry);
@ -56,8 +56,8 @@ function pushToList(board: number[][], list: CharacterPosition[], newEntries: (C
}
}
function findTeleportationTiles(board: number[][]): CharacterPosition[] {
const possiblePositions: CharacterPosition[] = [];
function findTeleportationTiles(board: number[][]): Position[] {
const possiblePositions: Position[] = [];
const edge = [0, board.length - 1];
for (const e of edge) {
@ -75,15 +75,15 @@ function findTeleportationTiles(board: number[][]): CharacterPosition[] {
return possiblePositions;
}
function isOutsideBoard(currentPos: CharacterPosition, boardSize: number): boolean {
function isOutsideBoard(currentPos: Position, boardSize: number): boolean {
return currentPos.x < 0 || currentPos.x >= boardSize || currentPos.y < 0 || currentPos.y >= boardSize;
}
function isWall(board: number[][], currentPos: CharacterPosition): boolean {
function isWall(board: number[][], currentPos: Position): boolean {
return board[currentPos.y][currentPos.x] === TileType.wall; // TODO shouldn't work, but it does
}
function isSpawn(board: number[][], currentPos: CharacterPosition): boolean {
function isSpawn(board: number[][], currentPos: Position): boolean {
return board[currentPos.x][currentPos.y] === TileType.pacmanSpawn ||
board[currentPos.x][currentPos.y] === TileType.ghostSpawn;
}

View File

@ -22,10 +22,7 @@ export const Counter: Component = () => {
React.useEffect(() => {
ws.onReceive = receiveMessage;
ws.open();
ws.registerEvents();
return () => {
ws.close();
};
return () => ws.close();
}, []);
return (

View File

@ -1,4 +1,4 @@
type MessageEventFunction = (data: MessageEvent<any>) => void;
type MessageEventFunction<T = any> = (data: MessageEvent<T>) => void;
type Setter<T> = React.Dispatch<React.SetStateAction<T>>;
@ -14,4 +14,4 @@ type SelectedDice = {
index: number
};
type CharacterPosition = { x: number, y: number };
type Position = { x: number, y: number };

View File

@ -23,10 +23,6 @@ export default class WebSocketService {
public open(): void {
this.ws = new WebSocket(this._url);
}
public registerEvents(): void {
if (!this.ws) return;
if (this._onOpen) this.ws.onopen = this._onOpen;
if (this._onReceive) this.ws.onmessage = this._onReceive;
if (this._onClose) this.ws.onclose = this._onClose;

View File

@ -44,7 +44,7 @@ public class GameController : GenericController
message.Data = rolls;
break;
default:
Logger.Log(LogLevel.Information, "Sending message to all clients");
Logger.Log(LogLevel.Information, "Forwarding message to all clients");
break;
}
}

View File

@ -36,12 +36,12 @@ public abstract class GenericController : ControllerBase
{
try
{
var buffer = new byte[BufferSize];
WebSocketReceiveResult? result;
do
{
var buffer = new byte[BufferSize];
result = await _wsService.Receive(webSocket, buffer);
if (result.CloseStatus.HasValue) break;
var segment = Run(result, buffer);

View File

@ -38,6 +38,7 @@
<TypeScriptCompile Remove="ClientApp\src\classes\tileMap.ts" />
<TypeScriptCompile Remove="ClientApp\src\game\tileMap.ts" />
<TypeScriptCompile Remove="ClientApp\src\components\gameCanvas.tsx" />
<TypeScriptCompile Remove="ClientApp\src\game\game.ts" />
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">