Json is being handled with camelCase
This commit is contained in:
parent
0c9ba333ea
commit
1db515d796
@ -10,7 +10,7 @@ namespace BackendTests.Services;
|
||||
|
||||
public class ActionServiceTests
|
||||
{
|
||||
private readonly Player _blackPlayer = (Player)Players.Create("black");
|
||||
private readonly Player _blackPlayer = Players.Create("black");
|
||||
private readonly Player _redPlayer = (Player)Players.Create("red");
|
||||
|
||||
private readonly Player _whitePlayer = (Player)Players.Create("white");
|
||||
@ -109,7 +109,7 @@ public class ActionServiceTests
|
||||
var pos = _spawns.Dequeue();
|
||||
_whitePlayer.PacMan.Position = pos;
|
||||
_whitePlayer.PacMan.SpawnPosition = pos;
|
||||
Assert.That(new List<IPlayer> { _whitePlayer }, Is.EqualTo(players));
|
||||
Assert.That(new List<Player> { _whitePlayer }, Is.EqualTo(players));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -173,7 +173,7 @@ public class ActionServiceTests
|
||||
var result = _service.Ready();
|
||||
// If selected the state is changed to InGame
|
||||
_whitePlayer.State = State.InGame;
|
||||
var players = result.GetType().GetProperty("Players")?.GetValue(result) as IEnumerable<IPlayer>;
|
||||
var players = result.GetType().GetProperty("Players")?.GetValue(result) as IEnumerable<Player>;
|
||||
Assert.That(players?.First().Username, Is.EqualTo(_whitePlayer.Username));
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ public class ActionServiceTests
|
||||
|
||||
result = _service.Ready();
|
||||
|
||||
var players = result.GetType().GetProperty("Players")?.GetValue(result) as IEnumerable<IPlayer>;
|
||||
var players = result.GetType().GetProperty("Players")?.GetValue(result) as IEnumerable<Player>;
|
||||
Assert.That(players?.First().Username, Is.EqualTo(_blackPlayer.Username).Or.EqualTo(_whitePlayer.Username));
|
||||
}
|
||||
|
||||
|
@ -20,14 +20,14 @@ public class GameTests
|
||||
private readonly DirectionalPosition _spawn7By7Right = new()
|
||||
{ At = new Position { X = 7, Y = 7 }, Direction = Direction.Right };
|
||||
|
||||
private IPlayer _bluePlayer = null!;
|
||||
private Player _bluePlayer = null!;
|
||||
private pacMan.Services.Game _game = null!;
|
||||
private IPlayer _greenPlayer = null!;
|
||||
private IPlayer _purplePlayer = null!;
|
||||
private IPlayer _redPlayer = null!;
|
||||
private Player _greenPlayer = null!;
|
||||
private Player _purplePlayer = null!;
|
||||
private Player _redPlayer = null!;
|
||||
|
||||
private Queue<DirectionalPosition> _spawns = null!;
|
||||
private IPlayer _yellowPlayer = null!;
|
||||
private Player _yellowPlayer = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@ -61,7 +61,7 @@ public class GameTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddPlayer(IPlayer player)
|
||||
#region AddPlayer(Player player)
|
||||
|
||||
[Test]
|
||||
public void AddPlayer_WhenEmpty()
|
||||
@ -82,7 +82,7 @@ public class GameTests
|
||||
[Test]
|
||||
public void AddPlayer_WhenNameExists()
|
||||
{
|
||||
var redClone = Players.Clone(_redPlayer);
|
||||
var redClone = _redPlayer.Clone();
|
||||
_game.AddPlayer(_redPlayer);
|
||||
var added = _game.AddPlayer(redClone);
|
||||
Assert.That(added, Is.True);
|
||||
@ -146,7 +146,7 @@ public class GameTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetReady(IPlayer player)
|
||||
#region SetReady(Player player)
|
||||
|
||||
[Test]
|
||||
public void SetReady_ReturnsAllPlayers()
|
||||
@ -191,14 +191,14 @@ public class GameTests
|
||||
{
|
||||
AddFullParty();
|
||||
_game.Players.ForEach(player => player.State = State.Ready);
|
||||
Assert.That(_game.Players, Has.All.Property(nameof(IPlayer.State)).EqualTo(State.Ready));
|
||||
Assert.That(_game.Players, Has.All.Property(nameof(Player.State)).EqualTo(State.Ready));
|
||||
|
||||
var allInGame = _game.SetAllInGame();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(allInGame, Is.True);
|
||||
Assert.That(_game.Players, Has.All.Property(nameof(IPlayer.State)).EqualTo(State.InGame));
|
||||
Assert.That(_game.Players, Has.All.Property(nameof(Player.State)).EqualTo(State.InGame));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ namespace BackendTests.TestUtils;
|
||||
|
||||
internal static class Players
|
||||
{
|
||||
internal static IPlayer Create(string colour) =>
|
||||
new Player
|
||||
internal static Player Create(string colour) =>
|
||||
new()
|
||||
{
|
||||
Username = colour,
|
||||
Colour = colour,
|
||||
@ -28,8 +28,8 @@ internal static class Players
|
||||
Pellets = new List<Pellet>()
|
||||
};
|
||||
|
||||
internal static IPlayer Clone(IPlayer player) =>
|
||||
new Player
|
||||
internal static Player Clone(this Player player) =>
|
||||
new()
|
||||
{
|
||||
Box = player.Box,
|
||||
Colour = player.Colour,
|
||||
|
@ -31,7 +31,7 @@ const Board: FC<BoardProps> = (
|
||||
const setModalOpen = useSetAtom(modalOpenAtom);
|
||||
|
||||
function handleSelectCharacter(character: Character): void {
|
||||
if (character.isPacMan() && currentPlayer?.PacMan.Colour !== character.Colour) {
|
||||
if (character.isPacMan() && currentPlayer?.pacMan.colour !== character.colour) {
|
||||
return;
|
||||
}
|
||||
setSelectedCharacter(character);
|
||||
@ -58,7 +58,7 @@ const Board: FC<BoardProps> = (
|
||||
}
|
||||
|
||||
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();
|
||||
@ -73,12 +73,12 @@ const Board: FC<BoardProps> = (
|
||||
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 {
|
||||
currentPlayer?.addPellet(new Pellet(isPowerPellet));
|
||||
map[tile.Y][tile.X] = TileType.empty;
|
||||
map[tile.y][tile.x] = TileType.empty;
|
||||
positions.push(tile);
|
||||
}
|
||||
|
||||
@ -112,10 +112,10 @@ const Board: FC<BoardProps> = (
|
||||
<GameTile
|
||||
key={colIndex + rowIndex * colIndex}
|
||||
type={tile}
|
||||
possiblePath={possiblePositions.find(p => 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}
|
||||
possiblePath={possiblePositions.find(p => 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}
|
||||
@ -181,11 +181,11 @@ const SelectPlayerModal: FC = () => {
|
||||
|
||||
{
|
||||
allPlayers.map(player =>
|
||||
<div key={player.Username} className={"border-b pb-1"}>
|
||||
<span className={"mx-2"}>{player.Username} has {player.Box.count} pellets</span>
|
||||
<div key={player.username} className={"border-b pb-1"}>
|
||||
<span className={"mx-2"}>{player.username} has {player.box.count} pellets</span>
|
||||
<button className={"text-blue-500 enabled:cursor-pointer disabled:text-gray-500"}
|
||||
style={{background: "none"}}
|
||||
disabled={player.Box.count === 0}
|
||||
disabled={player.box.count === 0}
|
||||
onClick={() => {
|
||||
currentPlayer?.stealFrom(player);
|
||||
close();
|
||||
|
@ -21,7 +21,7 @@ const GameButton: FC<GameButtonProps> = (
|
||||
const players = useAtomValue(playersAtom);
|
||||
const activeRollDiceButton = useAtomValue(rollDiceButtonAtom);
|
||||
|
||||
if (players.length >= rules.minPlayers && (currentPlayer === undefined || currentPlayer.State === State.waitingForPlayers)) {
|
||||
if (players.length >= rules.minPlayers && (currentPlayer === undefined || currentPlayer.state === State.waitingForPlayers)) {
|
||||
return <Button onClick={onReadyClick}>Ready</Button>;
|
||||
}
|
||||
if (!thisPlayer?.isTurn()) { // TODO also show when waiting for other players
|
||||
|
@ -36,7 +36,7 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map
|
||||
if (!player.isTurn()) return;
|
||||
|
||||
setSelectedDice(undefined);
|
||||
wsService.send({Action: GameAction.rollDice});
|
||||
wsService.send({action: GameAction.rollDice});
|
||||
setActiveRollDiceButton(false);
|
||||
}
|
||||
|
||||
@ -46,12 +46,12 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map
|
||||
}
|
||||
setSelectedDice(undefined);
|
||||
const data: ActionMessage = {
|
||||
Action: GameAction.moveCharacter,
|
||||
Data: {
|
||||
Dice: dice?.length ?? 0 > 0 ? dice : null,
|
||||
Players: players,
|
||||
Ghosts: ghosts,
|
||||
EatenPellets: eatenPellets
|
||||
action: GameAction.moveCharacter,
|
||||
data: {
|
||||
dice: dice?.length ?? 0 > 0 ? dice : null,
|
||||
players: players,
|
||||
ghosts: ghosts,
|
||||
eatenPellets: eatenPellets
|
||||
}
|
||||
};
|
||||
wsService.send(data);
|
||||
@ -63,19 +63,19 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map
|
||||
|
||||
function sendPlayer(): void {
|
||||
wsService.send({
|
||||
Action: GameAction.playerInfo,
|
||||
Data: {
|
||||
Player: player, Spawns: getPacManSpawns(map)
|
||||
action: GameAction.playerInfo,
|
||||
data: {
|
||||
player: player, spawns: getPacManSpawns(map)
|
||||
} as PlayerInfoData
|
||||
});
|
||||
}
|
||||
|
||||
function sendReady(): void {
|
||||
wsService.send({Action: GameAction.ready});
|
||||
wsService.send({action: GameAction.ready});
|
||||
}
|
||||
|
||||
function endTurn(): void {
|
||||
wsService.send({Action: GameAction.nextPlayer});
|
||||
wsService.send({action: GameAction.nextPlayer});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -90,7 +90,7 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map
|
||||
return (
|
||||
<>
|
||||
<div className={"flex justify-center"}>
|
||||
{players?.map(p => <PlayerStats key={p.Username} player={p}/>)}
|
||||
{players?.map(p => <PlayerStats key={p.username} player={p}/>)}
|
||||
</div>
|
||||
<div className={"flex-center"}>
|
||||
<GameButton onReadyClick={sendReady} onRollDiceClick={rollDice}/>
|
||||
|
@ -28,7 +28,7 @@ export const GameTile: FC<TileWithCharacterProps> = (
|
||||
isSelected = false,
|
||||
showPath = false
|
||||
}) => (
|
||||
<Tile className={`${possiblePath?.End ? "border-4 border-white" : ""}`}
|
||||
<Tile className={`${possiblePath?.end ? "border-4 border-white" : ""}`}
|
||||
type={type}
|
||||
onClick={possiblePath ? () => handleMoveCharacter?.(possiblePath) : undefined}
|
||||
onMouseEnter={possiblePath ? () => handleStartShowPath?.(possiblePath) : undefined}
|
||||
@ -48,7 +48,7 @@ export const GameTile: FC<TileWithCharacterProps> = (
|
||||
</Tile>
|
||||
);
|
||||
|
||||
const Circle: FC<{ colour?: Colour } & ComponentProps> = ({colour = Colour.White, className}) => (
|
||||
const Circle: FC<{ colour?: Colour } & ComponentProps> = ({colour = Colour.white, className}) => (
|
||||
<div className={`flex-center w-full h-full ${className}`}>
|
||||
<div className={`w-1/2 h-1/2 rounded-full`}
|
||||
style={{backgroundColor: colour}}/>
|
||||
@ -93,7 +93,7 @@ const Tile: FC<TileProps> = (
|
||||
useEffect(() => {
|
||||
|
||||
function handleResize(): void {
|
||||
const newSize = Math.floor(window.innerWidth / 12);
|
||||
const newSize = Math.floor(window.innerWidth / 16);
|
||||
setTileSize(newSize);
|
||||
}
|
||||
|
||||
@ -110,8 +110,8 @@ const Tile: FC<TileProps> = (
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}>
|
||||
{children}
|
||||
{type === TileType.pellet && <Circle colour={Colour.Yellow}/>}
|
||||
{type === TileType.powerPellet && <Circle colour={Colour.Red}/>}
|
||||
{type === TileType.pellet && <Circle colour={Colour.yellow}/>}
|
||||
{type === TileType.powerPellet && <Circle colour={Colour.red}/>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -139,7 +139,7 @@ const CharacterComponent: FC<CharacterComponentProps> = (
|
||||
}) => {
|
||||
|
||||
function getSide() {
|
||||
switch (character?.Position?.Direction) {
|
||||
switch (character?.position?.direction) {
|
||||
case Direction.up:
|
||||
return "right-1/4 top-0";
|
||||
case Direction.down:
|
||||
@ -155,7 +155,7 @@ const CharacterComponent: FC<CharacterComponentProps> = (
|
||||
|
||||
return (
|
||||
<div className={`rounded-full w-4/5 h-4/5 cursor-pointer hover:border border-black relative ${className}`}
|
||||
style={{backgroundColor: `${character.Colour}`}}
|
||||
style={{backgroundColor: `${character.colour}`}}
|
||||
onClick={() => onClick?.(character)}>
|
||||
<div>
|
||||
<div className={`absolute ${getSide()} w-1/2 h-1/2 rounded-full bg-black`}/>
|
||||
|
@ -11,16 +11,16 @@ const PlayerStats: FC<{ player: Player } & ComponentProps> = (
|
||||
}) => {
|
||||
const currentPlayerName = useAtomValue(currentPlayerNameAtom);
|
||||
return (
|
||||
<div key={player.Colour}
|
||||
className={`w-fit m-2 ${player.State === State.disconnected ? "text-gray-500" : ""} ${className}`} id={id}>
|
||||
<p className={player.Username === currentPlayerName ? "underline" : ""}>Player: {player.Username}</p>
|
||||
<p>Colour: {player.Colour}</p>
|
||||
{player.State === State.inGame || player.State === State.disconnected ?
|
||||
<div key={player.colour}
|
||||
className={`w-fit m-2 ${player.state === State.disconnected ? "text-gray-500" : ""} ${className}`} id={id}>
|
||||
<p className={player.username === currentPlayerName ? "underline" : ""}>Player: {player.username}</p>
|
||||
<p>Colour: {player.colour}</p>
|
||||
{player.state === State.inGame || player.state === State.disconnected ?
|
||||
<>
|
||||
<p>Pellets: {player.Box.count}</p>
|
||||
<p>PowerPellets: {player.Box.countPowerPellets}</p>
|
||||
<p>Pellets: {player.box.count}</p>
|
||||
<p>PowerPellets: {player.box.countPowerPellets}</p>
|
||||
</> :
|
||||
<p>{player.State === State.waitingForPlayers ? "Waiting" : "Ready"}</p>}
|
||||
<p>{player.state === State.waitingForPlayers ? "Waiting" : "Ready"}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -2,28 +2,28 @@ import Pellet from "./pellet";
|
||||
import {Colour} from "./colour";
|
||||
|
||||
export default class Box {
|
||||
public Pellets: Pellet[];
|
||||
public readonly Colour: Colour;
|
||||
public pellets: Pellet[];
|
||||
public readonly colour: Colour;
|
||||
|
||||
public constructor({Colour, Pellets = []}: BoxProps) {
|
||||
this.Colour = Colour;
|
||||
this.Pellets = Pellets;
|
||||
public constructor({colour, pellets = []}: BoxProps) {
|
||||
this.colour = colour;
|
||||
this.pellets = pellets;
|
||||
}
|
||||
|
||||
get powerPellet(): Pellet | undefined {
|
||||
return this.Pellets.find(pellet => pellet.IsPowerPellet);
|
||||
return this.pellets.find(pellet => pellet.isPowerPellet);
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this.Pellets.filter(pellet => !pellet.IsPowerPellet).length;
|
||||
return this.pellets.filter(pellet => !pellet.isPowerPellet).length;
|
||||
}
|
||||
|
||||
get countPowerPellets(): number {
|
||||
return this.Pellets.filter(pellet => pellet.IsPowerPellet).length;
|
||||
return this.pellets.filter(pellet => pellet.isPowerPellet).length;
|
||||
}
|
||||
|
||||
public addPellet(pellet: Pellet): void {
|
||||
this.Pellets.push(pellet);
|
||||
this.pellets.push(pellet);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,76 +8,76 @@ export enum CharacterType {
|
||||
}
|
||||
|
||||
export class Character {
|
||||
public readonly Colour: Colour;
|
||||
public Position: Path | null;
|
||||
public IsEatable: boolean;
|
||||
public readonly SpawnPosition: DirectionalPosition | null;
|
||||
public readonly Type: CharacterType;
|
||||
public readonly colour: Colour;
|
||||
public position: Path | null;
|
||||
public isEatable: boolean;
|
||||
public readonly spawnPosition: DirectionalPosition | null;
|
||||
public readonly type: CharacterType;
|
||||
|
||||
public constructor(
|
||||
{
|
||||
Colour,
|
||||
Position = null,
|
||||
Type = CharacterType.dummy,
|
||||
IsEatable = Type === CharacterType.pacMan,
|
||||
SpawnPosition = null
|
||||
colour,
|
||||
position = null,
|
||||
type = CharacterType.dummy,
|
||||
isEatable = type === CharacterType.pacMan,
|
||||
spawnPosition = null
|
||||
}: CharacterProps) {
|
||||
this.Colour = Colour;
|
||||
this.IsEatable = IsEatable;
|
||||
this.SpawnPosition = SpawnPosition;
|
||||
this.colour = colour;
|
||||
this.isEatable = isEatable;
|
||||
this.spawnPosition = spawnPosition;
|
||||
|
||||
if (Position) {
|
||||
this.Position = Position;
|
||||
if (position) {
|
||||
this.position = position;
|
||||
} else {
|
||||
this.Position = SpawnPosition ? {
|
||||
End: SpawnPosition!.At,
|
||||
Direction: SpawnPosition!.Direction
|
||||
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;
|
||||
if (!this.position) {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,11 +85,11 @@ export class Dummy extends Character {
|
||||
|
||||
public constructor(position: Path) { // TODO see-through
|
||||
super({
|
||||
Colour: Colour.Grey,
|
||||
Position: position,
|
||||
IsEatable: false,
|
||||
SpawnPosition: {At: {X: 0, Y: 0}, Direction: Direction.up},
|
||||
Type: CharacterType.dummy,
|
||||
colour: Colour.grey,
|
||||
position: position,
|
||||
isEatable: false,
|
||||
spawnPosition: {at: {x: 0, y: 0}, direction: Direction.up},
|
||||
type: CharacterType.dummy,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
export enum Colour {
|
||||
White = "white",
|
||||
Red = "red",
|
||||
Blue = "blue",
|
||||
Yellow = "yellow",
|
||||
Green = "green",
|
||||
Purple = "purple",
|
||||
Grey = "grey",
|
||||
white = "white",
|
||||
red = "red",
|
||||
blue = "blue",
|
||||
yellow = "yellow",
|
||||
green = "green",
|
||||
purple = "purple",
|
||||
grey = "grey",
|
||||
}
|
||||
|
||||
export const getColours = (): Colour[] => Object.values(Colour);
|
||||
|
@ -9,18 +9,22 @@ import {Direction} from "./direction";
|
||||
* 4 = ghost spawn
|
||||
* 5 = pacman spawn
|
||||
*/
|
||||
export const testMap: GameMap = [ // TODO create map class object using tile type enum
|
||||
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 0, 1, 5, 1, 0, 1, 4, 1, 0, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[0, 2, 0, 0, 0, 3, 0, 0, 0, 2, 0],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 0, 1, 4, 1, 0, 1, 5, 1, 0, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
|
||||
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||
export const customMap: GameMap = [
|
||||
[1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
|
||||
[1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1],
|
||||
[0, 2, 1, 5, 1, 2, 1, 0, 1, 2, 1, 5, 1, 2, 0],
|
||||
[1, 2, 1, 0, 0, 2, 0, 3, 0, 2, 0, 0, 1, 2, 1],
|
||||
[1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1],
|
||||
[1, 2, 2, 2, 2, 2, 1, 4, 1, 2, 2, 2, 2, 2, 1],
|
||||
[1, 3, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 3, 1],
|
||||
[1, 2, 2, 2, 2, 2, 1, 4, 1, 2, 2, 2, 2, 2, 1],
|
||||
[1, 2, 1, 1, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 1],
|
||||
[1, 2, 1, 0, 0, 2, 0, 3, 0, 2, 0, 0, 1, 2, 1],
|
||||
[0, 2, 1, 5, 1, 2, 1, 0, 1, 2, 1, 5, 1, 2, 0],
|
||||
[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 }[] {
|
||||
@ -30,10 +34,10 @@ export function getCharacterSpawns(map: GameMap): { type: CharacterType, positio
|
||||
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}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
export default class Pellet {
|
||||
public readonly IsPowerPellet: boolean;
|
||||
public readonly isPowerPellet: boolean;
|
||||
|
||||
public constructor(isPowerPellet = false) {
|
||||
this.IsPowerPellet = isPowerPellet;
|
||||
this.isPowerPellet = isPowerPellet;
|
||||
}
|
||||
}
|
||||
|
@ -14,37 +14,37 @@ export enum State {
|
||||
}
|
||||
|
||||
export default class Player {
|
||||
public readonly Username: string;
|
||||
public readonly PacMan: Character;
|
||||
public readonly Colour: Colour;
|
||||
public readonly Box: Box;
|
||||
public State: State;
|
||||
public readonly username: string;
|
||||
public readonly pacMan: Character;
|
||||
public readonly colour: Colour;
|
||||
public readonly box: Box;
|
||||
public state: 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.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.state = props.state ?? State.waitingForPlayers;
|
||||
}
|
||||
|
||||
public isTurn(): boolean {
|
||||
const store = getDefaultStore();
|
||||
return store.get(currentPlayerNameAtom) === this.Username;
|
||||
return store.get(currentPlayerNameAtom) === this.username;
|
||||
}
|
||||
|
||||
public addPellet(pellet: Pellet): void {
|
||||
this.Box.addPellet(pellet);
|
||||
this.box.addPellet(pellet);
|
||||
}
|
||||
|
||||
public stealFrom(other: Player): void {
|
||||
for (let i = 0; i < rules.maxStealPellets; i++) {
|
||||
const pellet = other.Box.Pellets.pop();
|
||||
const pellet = other.box.pellets.pop();
|
||||
if (pellet)
|
||||
this.Box.addPellet(pellet);
|
||||
this.box.addPellet(pellet);
|
||||
}
|
||||
const store = getDefaultStore();
|
||||
store.set(playersAtom, store.get(playersAtom).map(player => player));
|
||||
|
@ -13,8 +13,8 @@ import {Direction, getDirections} from "./direction";
|
||||
* @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);
|
||||
if (!character.position || !character.spawnPosition) throw new Error("Character has no position or spawn position");
|
||||
return findPossibleRecursive(board, character.position, steps, character, characters);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -29,7 +29,7 @@ export default function findPossiblePositions(board: GameMap, character: Charact
|
||||
function findPossibleRecursive(board: GameMap, currentPath: Path, steps: number, character: Character, characters: Character[]): Path[] {
|
||||
|
||||
const paths: Path[] = [];
|
||||
if (isOutsideBoard(currentPath, board.length)) {
|
||||
if (isOutsideBoard(currentPath, board.length)) { // TODO not working on new map
|
||||
if (character.isPacMan()) {
|
||||
return addTeleportationTiles(board, currentPath, steps, character, characters);
|
||||
}
|
||||
@ -61,7 +61,7 @@ function findPossibleRecursive(board: GameMap, currentPath: Path, steps: number,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +72,7 @@ function isCharactersSpawn(currentPath: Path, character: Character): boolean {
|
||||
* @returns True if the character is a ghost and hits Pac-Man
|
||||
*/
|
||||
function ghostHitsPacMan(character: Character, currentPath: Path, characters: Character[]): Character | undefined | false {
|
||||
return character.isGhost() && characters.find(c => c.isPacMan() && c.isAt(currentPath.End));
|
||||
return character.isGhost() && characters.find(c => c.isPacMan() && c.isAt(currentPath.end));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +83,7 @@ function ghostHitsPacMan(character: Character, currentPath: Path, characters: Ch
|
||||
* @returns True if the character hits another character
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,10 +91,10 @@ function characterHitsAnotherCharacter(character: Character, currentPath: Path,
|
||||
* @param currentPos The current path the character is on
|
||||
*/
|
||||
function addToPath(currentPos: Path): void {
|
||||
if (!currentPos.Path) {
|
||||
currentPos.Path = [];
|
||||
} else if (!currentPos.Path.includes(currentPos.End)) {
|
||||
currentPos.Path = [...currentPos.Path, currentPos.End];
|
||||
if (!currentPos.path) {
|
||||
currentPos.path = [];
|
||||
} else if (!currentPos.path.includes(currentPos.end)) {
|
||||
currentPos.path = [...currentPos.path, currentPos.end];
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,31 +115,31 @@ function tryMove(board: GameMap, path: Path, direction: Direction, steps: number
|
||||
switch (direction) {
|
||||
case Direction.left:
|
||||
return {
|
||||
X: path.End.X - 1,
|
||||
Y: path.End.Y
|
||||
x: path.end.x - 1,
|
||||
y: path.end.y
|
||||
};
|
||||
case Direction.up:
|
||||
return {
|
||||
X: path.End.X,
|
||||
Y: path.End.Y - 1
|
||||
x: path.end.x,
|
||||
y: path.end.y - 1
|
||||
};
|
||||
case Direction.right:
|
||||
return {
|
||||
X: path.End.X + 1,
|
||||
Y: path.End.Y
|
||||
x: path.end.x + 1,
|
||||
y: path.end.y
|
||||
};
|
||||
case Direction.down:
|
||||
return {
|
||||
X: path.End.X,
|
||||
Y: path.End.Y + 1
|
||||
x: path.end.x,
|
||||
y: path.end.y + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (path.Direction !== (direction + 2) % 4) {
|
||||
if (path.direction !== (direction + 2) % 4) {
|
||||
// TODO getNewPosition() and check if a character is on the new position
|
||||
return findPossibleRecursive(board, {
|
||||
End: getNewPosition(), Direction: direction, Path: path.Path
|
||||
end: getNewPosition(), direction: direction, path: path.path
|
||||
}, steps, character, characters);
|
||||
}
|
||||
return [];
|
||||
@ -157,10 +157,10 @@ function addTeleportationTiles(board: GameMap, currentPath: Path, steps: number,
|
||||
const possiblePositions = findTeleportationTiles(board);
|
||||
const paths: Path[] = [];
|
||||
for (const pos of possiblePositions) {
|
||||
if (pos.End.X !== interval(0, board.length - 1, currentPath.End.X) ||
|
||||
pos.End.Y !== interval(0, board.length - 1, currentPath.End.Y)) {
|
||||
if (pos.end.x !== interval(0, board.length - 1, currentPath.end.x) ||
|
||||
pos.end.y !== interval(0, board.length - 1, currentPath.end.y)) {
|
||||
|
||||
pos.Path = currentPath.Path;
|
||||
pos.path = currentPath.path;
|
||||
paths.push(...findPossibleRecursive(board, pos, steps, character, characters));
|
||||
}
|
||||
}
|
||||
@ -172,19 +172,19 @@ function interval(lower: number, upper: number, value: number): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the teleportation tiles on the board
|
||||
* @param board The board the character is on
|
||||
* Finds all the teleportation tiles on the map
|
||||
* @param map The map the character is on
|
||||
* @returns An array of paths containing the teleportation tiles
|
||||
*/
|
||||
function findTeleportationTiles(board: GameMap): Path[] {
|
||||
function findTeleportationTiles(map: GameMap): Path[] {
|
||||
const possiblePositions: Path[] = [];
|
||||
const edge = [0, board.length - 1];
|
||||
const edge = [0, map.length - 1];
|
||||
|
||||
for (const e of edge) {
|
||||
for (let i = 0; i < board[e].length; i++) {
|
||||
for (let i = 0; i < map[e].length; i++) {
|
||||
|
||||
pushPath(board, possiblePositions, i, e);
|
||||
pushPath(board, possiblePositions, e, i);
|
||||
pushPath(map, possiblePositions, i, e);
|
||||
pushPath(map, possiblePositions, e, i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,8 +199,8 @@ function findTeleportationTiles(board: GameMap): Path[] {
|
||||
* @param y The y position of the path
|
||||
*/
|
||||
function pushPath(board: GameMap, possiblePositions: Path[], x: number, y: number): void {
|
||||
if (board[x][y] !== TileType.wall) {
|
||||
possiblePositions.push({End: {X: x, Y: y}, Direction: findDirection(x, y, board.length)});
|
||||
if (board[x] && board[x][y] !== TileType.wall) {
|
||||
possiblePositions.push({end: {x: x, y: y}, direction: findDirection(x, y, board.length)});
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,8 +230,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,8 +240,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
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,8 +250,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,8 +260,8 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React, {FC, useEffect} from "react";
|
||||
import {GameComponent} from "../components/gameComponent";
|
||||
import {useAtomValue} from "jotai";
|
||||
import {thisPlayerAtom} from "../utils/state";
|
||||
import {testMap} from "../game/map";
|
||||
import {customMap} from "../game/map";
|
||||
|
||||
const Game: FC = () => { // TODO gameId in path
|
||||
const player = useAtomValue(thisPlayerAtom);
|
||||
@ -16,7 +16,7 @@ const Game: FC = () => { // TODO gameId in path
|
||||
}, [player]);
|
||||
|
||||
if (player) {
|
||||
return <GameComponent player={player} map={testMap}/>;
|
||||
return <GameComponent player={player} map={customMap}/>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ const Home: FC = () => {
|
||||
function formHandler(): void {
|
||||
if (!input.current || !dropdown.current) return;
|
||||
const player = new Player({
|
||||
Username: input.current.value,
|
||||
Colour: dropdown.current.value as Colour,
|
||||
username: input.current.value,
|
||||
colour: dropdown.current.value as Colour,
|
||||
});
|
||||
setPlayer(player);
|
||||
navigate("/game");
|
||||
|
@ -3,7 +3,7 @@ import {atom, useAtomValue} from "jotai";
|
||||
import {Button} from "../components/button";
|
||||
import {thisPlayerAtom} from "../utils/state";
|
||||
import {getData, postData} from "../utils/api";
|
||||
import {getPacManSpawns, testMap} from "../game/map";
|
||||
import {customMap, getPacManSpawns} from "../game/map";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const fetchAtom = atom(async () => {
|
||||
@ -20,15 +20,15 @@ const LobbyPage: FC = () => { // TODO check if player is defined in storage, if
|
||||
async function createGame(): Promise<void> {
|
||||
|
||||
const response = await postData("/game/create", {
|
||||
body: {Player: thisPlayer, Spawns: getPacManSpawns(testMap)} as PlayerInfoData
|
||||
body: {player: thisPlayer, spawns: getPacManSpawns(customMap)} as PlayerInfoData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.debug("Game created: ", data);
|
||||
// TODO redirect to game page
|
||||
} else {
|
||||
const data = await response.text();
|
||||
console.error("Error: ", data);
|
||||
// TODO display error
|
||||
}
|
||||
|
@ -28,13 +28,14 @@ const Login = () => {
|
||||
body: {username: user.username, password: user.password} as User
|
||||
})
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.debug("Login successful: ", data);
|
||||
setThisPlayer(new Player(data as PlayerProps));
|
||||
navigate("/lobby");
|
||||
} else {
|
||||
const data = await response.text();
|
||||
console.error("Error: ", data);
|
||||
// TODO display error
|
||||
}
|
||||
|
@ -25,22 +25,22 @@ 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?: import("../game/pellet").default[],
|
||||
readonly Colour: import("../game/colour").Colour,
|
||||
pellets?: import("../game/pellet").default[],
|
||||
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,
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ type Setter<T> = React.Dispatch<React.SetStateAction<T>>;
|
||||
type WebSocketData = string | ArrayBufferLike | Blob | ArrayBufferView;
|
||||
|
||||
type ActionMessage<T = any> = {
|
||||
readonly Action: import("../utils/actions").GameAction,
|
||||
readonly Data?: T
|
||||
readonly action: import("../utils/actions").GameAction,
|
||||
readonly data?: T
|
||||
}
|
||||
|
||||
type Action<T> = (obj: T) => void;
|
||||
@ -20,19 +20,19 @@ type SelectedDice = {
|
||||
index: number
|
||||
};
|
||||
|
||||
type Position = { X: number, Y: number };
|
||||
type Position = { x: number, y: number };
|
||||
|
||||
type GameMap = number[][];
|
||||
|
||||
type DirectionalPosition = {
|
||||
At: Position,
|
||||
Direction: import("../game/direction").Direction
|
||||
at: Position,
|
||||
direction: import("../game/direction").Direction
|
||||
}
|
||||
|
||||
type Path = {
|
||||
Path?: Position[] | null,
|
||||
End: Position,
|
||||
Direction: import("../game/direction").Direction
|
||||
path?: Position[] | null,
|
||||
end: Position,
|
||||
direction: import("../game/direction").Direction
|
||||
}
|
||||
|
||||
type Game = {
|
||||
@ -55,6 +55,6 @@ type ApiRequest = {
|
||||
}
|
||||
|
||||
type PlayerInfoData = {
|
||||
readonly Player: PlayerProps,
|
||||
readonly Spawns: DirectionalPosition[],
|
||||
readonly player: PlayerProps,
|
||||
readonly spawns: DirectionalPosition[],
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Player from "../game/player";
|
||||
import {CharacterType, Ghost} from "../game/character";
|
||||
import {getCharacterSpawns, testMap} from "../game/map";
|
||||
import {customMap, getCharacterSpawns} from "../game/map";
|
||||
import {TileType} from "../game/tileType";
|
||||
import {getDefaultStore} from "jotai";
|
||||
import {currentPlayerNameAtom, diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom} from "./state";
|
||||
@ -17,12 +17,12 @@ export enum GameAction {
|
||||
const store = getDefaultStore();
|
||||
|
||||
const ghostsProps: CharacterProps[] = [
|
||||
{Colour: Colour.Purple},
|
||||
{Colour: Colour.Purple},
|
||||
{colour: Colour.purple},
|
||||
{colour: Colour.purple},
|
||||
];
|
||||
let spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.ghost);
|
||||
let spawns = getCharacterSpawns(customMap).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));
|
||||
@ -33,21 +33,21 @@ export const doAction: MessageEventFunction<string> = (event): void => { // TODO
|
||||
const message: ActionMessage = JSON.parse(event.data);
|
||||
console.debug("Received message:", message);
|
||||
|
||||
switch (message.Action as GameAction) {
|
||||
switch (message.action as GameAction) {
|
||||
case GameAction.rollDice:
|
||||
setDice(message.Data);
|
||||
setDice(message.data);
|
||||
break;
|
||||
case GameAction.moveCharacter:
|
||||
moveCharacter(message.Data);
|
||||
moveCharacter(message.data);
|
||||
break;
|
||||
case GameAction.playerInfo:
|
||||
playerInfo(message.Data);
|
||||
playerInfo(message.data);
|
||||
break;
|
||||
case GameAction.ready:
|
||||
ready(message.Data);
|
||||
ready(message.data);
|
||||
break;
|
||||
case GameAction.nextPlayer:
|
||||
nextPlayer(message.Data);
|
||||
nextPlayer(message.data);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@ -56,17 +56,17 @@ function setDice(data?: number[]): void {
|
||||
store.set(diceAtom, data);
|
||||
}
|
||||
|
||||
type MoveCharacterData = { Dice: number[], Players: PlayerProps[], Ghosts: CharacterProps[], EatenPellets: Position[] };
|
||||
type MoveCharacterData = { dice: number[], players: PlayerProps[], ghosts: CharacterProps[], eatenPellets: Position[] };
|
||||
|
||||
function moveCharacter(data?: MoveCharacterData): void {
|
||||
store.set(diceAtom, data?.Dice);
|
||||
store.set(diceAtom, data?.dice);
|
||||
updatePlayers(data);
|
||||
updateGhosts(data);
|
||||
removeEatenPellets(data);
|
||||
}
|
||||
|
||||
function updatePlayers(data?: MoveCharacterData): void {
|
||||
const updatedPlayers = data?.Players;
|
||||
const updatedPlayers = data?.players;
|
||||
|
||||
if (updatedPlayers) {
|
||||
const newList: Player[] = updatedPlayers.map(p => new Player(p));
|
||||
@ -75,7 +75,7 @@ function updatePlayers(data?: MoveCharacterData): void {
|
||||
}
|
||||
|
||||
function updateGhosts(data?: MoveCharacterData): void {
|
||||
const updatedGhosts = data?.Ghosts;
|
||||
const updatedGhosts = data?.ghosts;
|
||||
|
||||
if (updatedGhosts) {
|
||||
const newList: Ghost[] = updatedGhosts.map(g => new Ghost(g));
|
||||
@ -84,27 +84,27 @@ function updateGhosts(data?: MoveCharacterData): void {
|
||||
}
|
||||
|
||||
function removeEatenPellets(data?: MoveCharacterData): void {
|
||||
const pellets = data?.EatenPellets;
|
||||
const pellets = data?.eatenPellets;
|
||||
|
||||
for (const pellet of pellets ?? []) {
|
||||
testMap[pellet.Y][pellet.X] = TileType.empty;
|
||||
customMap[pellet.y][pellet.x] = TileType.empty;
|
||||
}
|
||||
}
|
||||
|
||||
function playerInfo(data?: PlayerProps[]): void { // TODO missing data when refreshing page
|
||||
const playerProps = data ?? [];
|
||||
spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.pacMan);
|
||||
spawns = getCharacterSpawns(customMap).filter(spawn => spawn.type === CharacterType.pacMan);
|
||||
store.set(playersAtom, playerProps.map(p => new Player(p)));
|
||||
}
|
||||
|
||||
type ReadyData = { AllReady: boolean, Players: PlayerProps[] } | string;
|
||||
type ReadyData = { allReady: boolean, players: PlayerProps[] } | string;
|
||||
|
||||
function ready(data?: ReadyData): void {
|
||||
if (data && typeof data !== "string") {
|
||||
const players = data.Players.map(p => new Player(p));
|
||||
const players = data.players.map(p => new Player(p));
|
||||
store.set(playersAtom, players);
|
||||
if (data.AllReady) {
|
||||
store.set(currentPlayerNameAtom, data.Players[0].Username);
|
||||
if (data.allReady) {
|
||||
store.set(currentPlayerNameAtom, data.players[0].username);
|
||||
}
|
||||
} else {
|
||||
console.error("Error:", data);
|
||||
|
@ -11,7 +11,7 @@ export const playersAtom = atom<Player[]>([]);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -47,7 +47,7 @@ export const currentPlayerNameAtom = atom<string | undefined>(undefined);
|
||||
*/
|
||||
export const currentPlayerAtom = atom<Player | undefined>(get => {
|
||||
const currentPlayerName = get(currentPlayerNameAtom);
|
||||
return get(playersAtom).find(player => player.Username === currentPlayerName);
|
||||
return get(playersAtom).find(player => player.username === currentPlayerName);
|
||||
});
|
||||
/**
|
||||
* Whether the roll dice button should be enabled.
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {beforeEach, expect, test} from "vitest";
|
||||
import possibleMovesAlgorithm from "../../src/game/possibleMovesAlgorithm";
|
||||
import {testMap} from "../../src/game/map";
|
||||
import {Ghost, PacMan} from "../../src/game/character";
|
||||
import {Direction} from "../../src/game/direction";
|
||||
import {Colour} from "../../src/game/colour";
|
||||
@ -8,47 +7,61 @@ import {Colour} from "../../src/game/colour";
|
||||
let pacMan: PacMan;
|
||||
let ghost: Ghost;
|
||||
|
||||
const testMap: GameMap = [
|
||||
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 0, 1, 5, 1, 0, 1, 4, 1, 0, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[0, 2, 0, 0, 0, 3, 0, 0, 0, 2, 0],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 0, 1, 4, 1, 0, 1, 5, 1, 0, 1],
|
||||
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 1],
|
||||
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
pacMan = new PacMan({
|
||||
Colour: Colour.Yellow, SpawnPosition: {At: {X: 3, Y: 3}, Direction: Direction.up}
|
||||
colour: Colour.yellow, spawnPosition: {at: {x: 3, y: 3}, direction: Direction.up}
|
||||
});
|
||||
ghost = new Ghost({
|
||||
Colour: Colour.Red, SpawnPosition: {At: {X: 3, Y: 3}, Direction: Direction.up}
|
||||
colour: Colour.red, spawnPosition: {at: {x: 3, y: 3}, direction: Direction.up}
|
||||
});
|
||||
});
|
||||
|
||||
test("Pac-Man rolls one from start, should return one position", () => {
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 1, []);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].Path?.length).toBe(0);
|
||||
expect(result).toEqual([{End: {X: 3, Y: 2}, Direction: Direction.up, Path: []}] as Path[]);
|
||||
expect(result[0].path?.length).toBe(0);
|
||||
expect(result).toEqual([{end: {x: 3, y: 2}, direction: Direction.up, path: []}] as Path[]);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls two from start, should return one position", () => {
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 2, []);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].Path?.length).toBe(1);
|
||||
expect(result).toEqual([{End: {X: 3, Y: 1}, Direction: Direction.up, Path: [{X: 3, Y: 2}]}] as Path[]);
|
||||
expect(result[0].path?.length).toBe(1);
|
||||
expect(result).toEqual([{end: {x: 3, y: 1}, direction: Direction.up, path: [{x: 3, y: 2}]}] as Path[]);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls three from start, should return two positions", () => {
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 3, []);
|
||||
expect(result.length).toBe(2);
|
||||
arrayEquals(result, [{End: {X: 2, Y: 1}, Direction: Direction.left, Path: [{X: 3, Y: 2}, {X: 3, Y: 1}]},
|
||||
{End: {X: 4, Y: 1}, Direction: Direction.right, Path: [{X: 3, Y: 2}, {X: 3, Y: 1}]}]);
|
||||
arrayEquals(result, [{end: {x: 2, y: 1}, direction: Direction.left, path: [{x: 3, y: 2}, {x: 3, y: 1}]},
|
||||
{end: {x: 4, y: 1}, direction: Direction.right, path: [{x: 3, y: 2}, {x: 3, y: 1}]}]);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls four from start, should return two positions", () => {
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 4, []);
|
||||
expect(result.length).toBe(2);
|
||||
arrayEquals(result, [{
|
||||
End: {X: 1, Y: 1},
|
||||
Direction: Direction.left,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 2, Y: 1}]
|
||||
end: {x: 1, y: 1},
|
||||
direction: Direction.left,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}]
|
||||
}, {
|
||||
End: {X: 5, Y: 1},
|
||||
Direction: Direction.right,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}]
|
||||
end: {x: 5, y: 1},
|
||||
direction: Direction.right,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}]
|
||||
}]);
|
||||
});
|
||||
|
||||
@ -56,21 +69,21 @@ test("Pac-Man rolls five from start, should return four positions", () => {
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 5, []);
|
||||
expect(result.length).toBe(4);
|
||||
arrayEquals(result, [{
|
||||
End: {X: 5, Y: 0},
|
||||
Direction: Direction.up,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}]
|
||||
end: {x: 5, y: 0},
|
||||
direction: Direction.up,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}]
|
||||
}, {
|
||||
End: {X: 6, Y: 1},
|
||||
Direction: Direction.right,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}]
|
||||
end: {x: 6, y: 1},
|
||||
direction: Direction.right,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}]
|
||||
}, {
|
||||
End: {X: 1, Y: 2},
|
||||
Direction: Direction.down,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 2, Y: 1}, {X: 1, Y: 1}]
|
||||
end: {x: 1, y: 2},
|
||||
direction: Direction.down,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}, {x: 1, y: 1}]
|
||||
}, {
|
||||
End: {X: 5, Y: 2},
|
||||
Direction: Direction.down,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}]
|
||||
end: {x: 5, y: 2},
|
||||
direction: Direction.down,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}]
|
||||
}
|
||||
]);
|
||||
});
|
||||
@ -80,108 +93,108 @@ test("Pac-Man rolls six from start, should return six positions", () => {
|
||||
expect(result.length).toBe(6);
|
||||
arrayEquals(result, [
|
||||
{
|
||||
End: {X: 1, Y: 3},
|
||||
Direction: Direction.down,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 2, Y: 1}, {X: 1, Y: 1}, {X: 1, Y: 2}]
|
||||
end: {x: 1, y: 3},
|
||||
direction: Direction.down,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 2, y: 1}, {x: 1, y: 1}, {x: 1, y: 2}]
|
||||
}, {
|
||||
End: {X: 0, Y: 5},
|
||||
Direction: Direction.right,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}, {X: 5, Y: 0}]
|
||||
end: {x: 0, y: 5},
|
||||
direction: Direction.right,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}]
|
||||
}, {
|
||||
End: {X: 5, Y: 3},
|
||||
Direction: Direction.down,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}, {X: 5, Y: 2}]
|
||||
end: {x: 5, y: 3},
|
||||
direction: Direction.down,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 2}]
|
||||
}, {
|
||||
End: {X: 7, Y: 1},
|
||||
Direction: Direction.right,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}, {X: 6, Y: 1}]
|
||||
end: {x: 7, y: 1},
|
||||
direction: Direction.right,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 6, y: 1}]
|
||||
}, {
|
||||
End: {X: 10, Y: 5},
|
||||
Direction: Direction.left,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}, {X: 5, Y: 0}]
|
||||
end: {x: 10, y: 5},
|
||||
direction: Direction.left,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}]
|
||||
}, {
|
||||
End: {X: 5, Y: 10},
|
||||
Direction: Direction.up,
|
||||
Path: [{X: 3, Y: 2}, {X: 3, Y: 1}, {X: 4, Y: 1}, {X: 5, Y: 1}, {X: 5, Y: 0}]
|
||||
end: {x: 5, y: 10},
|
||||
direction: Direction.up,
|
||||
path: [{x: 3, y: 2}, {x: 3, y: 1}, {x: 4, y: 1}, {x: 5, y: 1}, {x: 5, y: 0}]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls four from position [5,1] (right), should return 11", () => {
|
||||
pacMan.follow({End: {X: 5, Y: 1}, Direction: Direction.right});
|
||||
pacMan.follow({end: {x: 5, y: 1}, direction: Direction.right});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 4, []);
|
||||
expect(result.length).toBe(11);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls four from position [5,1] (left), should return 12", () => {
|
||||
pacMan.follow({End: {X: 5, Y: 1}, Direction: Direction.left});
|
||||
pacMan.follow({end: {x: 5, y: 1}, direction: Direction.left});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 4, []);
|
||||
expect(result.length).toBe(12);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls three from position [1,5] (left), should return 5", () => {
|
||||
pacMan.follow({End: {X: 1, Y: 5}, Direction: Direction.left});
|
||||
pacMan.follow({end: {x: 1, y: 5}, direction: Direction.left});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 3, []);
|
||||
arrayEquals(result, [
|
||||
{End: {X: 1, Y: 2}, Direction: Direction.up, Path: [{X: 1, Y: 4}, {X: 1, Y: 3}]},
|
||||
{End: {X: 1, Y: 8}, Direction: Direction.down, Path: [{X: 1, Y: 6}, {X: 1, Y: 7}]},
|
||||
{End: {X: 5, Y: 1}, Direction: Direction.down, Path: [{X: 0, Y: 5}, {X: 5, Y: 0}]},
|
||||
{End: {X: 9, Y: 5}, Direction: Direction.left, Path: [{X: 0, Y: 5}, {X: 10, Y: 5}]},
|
||||
{End: {X: 5, Y: 9}, Direction: Direction.up, Path: [{X: 0, Y: 5}, {X: 5, Y: 10}]},
|
||||
{end: {x: 1, y: 2}, direction: Direction.up, path: [{x: 1, y: 4}, {x: 1, y: 3}]},
|
||||
{end: {x: 1, y: 8}, direction: Direction.down, path: [{x: 1, y: 6}, {x: 1, y: 7}]},
|
||||
{end: {x: 5, y: 1}, direction: Direction.down, path: [{x: 0, y: 5}, {x: 5, y: 0}]},
|
||||
{end: {x: 9, y: 5}, direction: Direction.left, path: [{x: 0, y: 5}, {x: 10, y: 5}]},
|
||||
{end: {x: 5, y: 9}, direction: Direction.up, path: [{x: 0, y: 5}, {x: 5, y: 10}]},
|
||||
]);
|
||||
expect(result.length).toBe(5);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls six from position [1,5] (down), should return 17", () => {
|
||||
pacMan.follow({End: {X: 1, Y: 5}, Direction: Direction.down});
|
||||
pacMan.follow({end: {x: 1, y: 5}, direction: Direction.down});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 6, []);
|
||||
expect(result.length).toBe(21);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls six from position [7,1] (right), path to [9,5] should be five tiles long", () => {
|
||||
pacMan.follow({End: {X: 7, Y: 1}, Direction: Direction.right});
|
||||
pacMan.follow({end: {x: 7, y: 1}, direction: Direction.right});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 6, []);
|
||||
expect(result[0].Path?.length).toBe(5);
|
||||
expect(result[0].path?.length).toBe(5);
|
||||
});
|
||||
|
||||
test("Pac-Man rolls 5 from position [9,3] (down), should return 7", () => {
|
||||
pacMan.follow({End: {X: 9, Y: 3}, Direction: Direction.down});
|
||||
pacMan.follow({end: {x: 9, y: 3}, direction: Direction.down});
|
||||
const result = possibleMovesAlgorithm(testMap, pacMan, 5, []);
|
||||
expect(result.length).toBe(7);
|
||||
});
|
||||
|
||||
test("Ghost can take Pac-Man, stops exactly on Pac-Man unless Pac-Man is at spawn", () => {
|
||||
ghost.follow({End: {X: 3, Y: 5}, Direction: Direction.up});
|
||||
ghost.follow({end: {x: 3, y: 5}, direction: Direction.up});
|
||||
const result = possibleMovesAlgorithm(testMap, ghost, 2, [ghost, pacMan]);
|
||||
expect(result.length).toBe(2);
|
||||
arrayEquals(result, [
|
||||
{End: {X: 1, Y: 5}, Direction: Direction.left, Path: [{X: 2, Y: 5}]},
|
||||
{End: {X: 5, Y: 5}, Direction: Direction.right, Path: [{X: 4, Y: 5}]},
|
||||
{end: {x: 1, y: 5}, direction: Direction.left, path: [{x: 2, y: 5}]},
|
||||
{end: {x: 5, y: 5}, direction: Direction.right, path: [{x: 4, y: 5}]},
|
||||
])
|
||||
});
|
||||
|
||||
test("Ghost can take Pac-Man, steps reach Pac-Man exactly", () => {
|
||||
ghost.follow({End: {X: 7, Y: 3}, Direction: Direction.up});
|
||||
pacMan.follow({End: {X: 5, Y: 1}, Direction: Direction.right});
|
||||
ghost.follow({end: {x: 7, y: 3}, direction: Direction.up});
|
||||
pacMan.follow({end: {x: 5, y: 1}, direction: Direction.right});
|
||||
const result = possibleMovesAlgorithm(testMap, ghost, 4, [ghost, pacMan]);
|
||||
expect(result.length).toBe(2);
|
||||
arrayEquals(result, [
|
||||
{End: {X: 5, Y: 1}, Direction: Direction.left, Path: [{X: 7, Y: 2}, {X: 7, Y: 1}, {X: 6, Y: 1}]},
|
||||
{End: {X: 9, Y: 1}, Direction: Direction.right, Path: [{X: 7, Y: 2}, {X: 7, Y: 1}, {X: 8, Y: 1}]},
|
||||
{end: {x: 5, y: 1}, direction: Direction.left, path: [{x: 7, y: 2}, {x: 7, y: 1}, {x: 6, y: 1}]},
|
||||
{end: {x: 9, y: 1}, direction: Direction.right, path: [{x: 7, y: 2}, {x: 7, y: 1}, {x: 8, y: 1}]},
|
||||
])
|
||||
});
|
||||
|
||||
test("Ghost can take Pac-Man, steps overshoot Pac-Man", () => {
|
||||
ghost.follow({End: {X: 7, Y: 3}, Direction: Direction.up});
|
||||
pacMan.follow({End: {X: 5, Y: 1}, Direction: Direction.right});
|
||||
ghost.follow({end: {x: 7, y: 3}, direction: Direction.up});
|
||||
pacMan.follow({end: {x: 5, y: 1}, direction: Direction.right});
|
||||
const result = possibleMovesAlgorithm(testMap, ghost, 6, [ghost, pacMan]);
|
||||
expect(result.length).toBe(2);
|
||||
arrayEquals(result, [
|
||||
{End: {X: 5, Y: 1}, Direction: Direction.left, Path: [{X: 7, Y: 2}, {X: 7, Y: 1}, {X: 6, Y: 1}]},
|
||||
{end: {x: 5, y: 1}, direction: Direction.left, path: [{x: 7, y: 2}, {x: 7, y: 1}, {x: 6, y: 1}]},
|
||||
{
|
||||
End: {X: 9, Y: 3},
|
||||
Direction: Direction.down,
|
||||
Path: [{X: 7, Y: 2}, {X: 7, Y: 1}, {X: 8, Y: 1}, {X: 9, Y: 1}, {X: 9, Y: 2}]
|
||||
end: {x: 9, y: 3},
|
||||
direction: Direction.down,
|
||||
path: [{x: 7, y: 2}, {x: 7, y: 1}, {x: 8, y: 1}, {x: 9, y: 1}, {x: 9, y: 2}]
|
||||
},
|
||||
])
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace pacMan.GameStuff;
|
||||
|
||||
@ -13,8 +14,9 @@ public enum GameAction
|
||||
|
||||
public class ActionMessage<T>
|
||||
{
|
||||
public GameAction Action { get; init; }
|
||||
public T? Data { get; set; }
|
||||
[JsonPropertyName("action")] public GameAction Action { get; init; }
|
||||
|
||||
[JsonPropertyName("data")] public T? Data { get; set; }
|
||||
|
||||
public static ActionMessage FromJson(string json) => JsonSerializer.Deserialize<ActionMessage>(json)!;
|
||||
}
|
||||
|
@ -1,12 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace pacMan.GameStuff;
|
||||
|
||||
public class Character : IEquatable<Character>
|
||||
{
|
||||
public required string Colour { get; init; }
|
||||
public MovePath? Position { get; set; }
|
||||
[JsonPropertyName("colour")] public required string Colour { get; init; }
|
||||
|
||||
[JsonPropertyName("position")] public MovePath? Position { get; set; }
|
||||
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("isEatable")]
|
||||
public bool IsEatable { get; set; } = true;
|
||||
public DirectionalPosition? SpawnPosition { get; set; }
|
||||
public required CharacterType? Type { get; init; }
|
||||
|
||||
[JsonPropertyName("spawnPosition")] public DirectionalPosition? SpawnPosition { get; set; }
|
||||
|
||||
[JsonPropertyName("type")] public required CharacterType? Type { get; init; }
|
||||
|
||||
public bool Equals(Character? other)
|
||||
{
|
||||
|
@ -1,9 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace pacMan.GameStuff.Items;
|
||||
|
||||
public class Box : IEquatable<Box>
|
||||
{
|
||||
public List<Pellet>? Pellets { get; init; } = new();
|
||||
public required string Colour { get; init; }
|
||||
[JsonPropertyName("pellets")] public List<Pellet>? Pellets { get; init; } = new();
|
||||
|
||||
[JsonPropertyName("colour")] public required string Colour { get; init; }
|
||||
|
||||
public int CountNormal => Pellets?.Count(pellet => !pellet.IsPowerPellet) ?? 0;
|
||||
|
||||
@ -15,13 +18,6 @@ public class Box : IEquatable<Box>
|
||||
Colour == other.Colour;
|
||||
}
|
||||
|
||||
public IEnumerator<IPellet> GetEnumerator() => Pellets?.GetEnumerator() ?? new List<Pellet>.Enumerator();
|
||||
|
||||
// IEnumerator IEnumerable.GetEnumerator()
|
||||
// {
|
||||
// return GetEnumerator();
|
||||
// }
|
||||
|
||||
public void Add(Pellet pellet)
|
||||
{
|
||||
Pellets?.Add(pellet);
|
||||
|
@ -1,12 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace pacMan.GameStuff.Items;
|
||||
|
||||
public interface IPellet
|
||||
public class Pellet : IEquatable<Pellet>
|
||||
{
|
||||
bool IsPowerPellet { get; init; }
|
||||
}
|
||||
[JsonPropertyName("isPowerPellet")] public bool IsPowerPellet { get; init; }
|
||||
|
||||
public class Pellet : IPellet, IEquatable<Pellet>
|
||||
{
|
||||
public bool Equals(Pellet? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
@ -14,8 +13,6 @@ public class Pellet : IPellet, IEquatable<Pellet>
|
||||
return IsPowerPellet == other.IsPowerPellet;
|
||||
}
|
||||
|
||||
public bool IsPowerPellet { get; init; }
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
|
@ -1,16 +1,8 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using DAL.Database.Models;
|
||||
|
||||
namespace pacMan.GameStuff.Items;
|
||||
|
||||
public interface IPlayer
|
||||
{
|
||||
string Username { get; init; }
|
||||
Character PacMan { get; init; }
|
||||
string Colour { get; init; }
|
||||
Box? Box { get; init; }
|
||||
State State { get; set; }
|
||||
}
|
||||
|
||||
public enum State
|
||||
{
|
||||
WaitingForPlayers,
|
||||
@ -19,8 +11,18 @@ public enum State
|
||||
Disconnected
|
||||
}
|
||||
|
||||
public class Player : IPlayer, IEquatable<Player>
|
||||
public class Player : IEquatable<Player>
|
||||
{
|
||||
[JsonPropertyName("username")] public required string Username { get; init; }
|
||||
|
||||
[JsonPropertyName("pacMan")] public required Character PacMan { get; init; }
|
||||
|
||||
[JsonPropertyName("colour")] public required string Colour { get; init; }
|
||||
|
||||
[JsonPropertyName("box")] public Box? Box { get; init; }
|
||||
|
||||
[JsonPropertyName("state")] public State State { get; set; } = State.WaitingForPlayers;
|
||||
|
||||
public bool Equals(Player? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
@ -28,13 +30,6 @@ public class Player : IPlayer, IEquatable<Player>
|
||||
return Username == other.Username;
|
||||
}
|
||||
|
||||
// [JsonPropertyName("username")]
|
||||
public required string Username { get; init; }
|
||||
public required Character PacMan { get; init; }
|
||||
public required string Colour { get; init; }
|
||||
public Box? Box { get; init; }
|
||||
public State State { get; set; } = State.WaitingForPlayers;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
|
@ -1,10 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace pacMan.GameStuff;
|
||||
|
||||
public class MovePath : IEquatable<MovePath>
|
||||
{
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("path")]
|
||||
public Position[]? Path { get; set; }
|
||||
public required Position End { get; init; }
|
||||
public required Direction Direction { get; init; }
|
||||
|
||||
[JsonPropertyName("end")] public required Position End { get; init; }
|
||||
|
||||
[JsonPropertyName("direction")] public required Direction Direction { get; init; }
|
||||
|
||||
public bool Equals(MovePath? other)
|
||||
{
|
||||
@ -32,8 +38,9 @@ public class MovePath : IEquatable<MovePath>
|
||||
|
||||
public class Position : IEquatable<Position>
|
||||
{
|
||||
public int X { get; init; }
|
||||
public int Y { get; init; }
|
||||
[JsonPropertyName("x")] public int X { get; init; }
|
||||
|
||||
[JsonPropertyName("y")] public int Y { get; init; }
|
||||
|
||||
public bool Equals(Position? other)
|
||||
{
|
||||
@ -62,8 +69,9 @@ public enum Direction
|
||||
|
||||
public class DirectionalPosition : IEquatable<DirectionalPosition>
|
||||
{
|
||||
public required Position At { get; init; }
|
||||
public required Direction Direction { get; init; }
|
||||
[JsonPropertyName("at")] public required Position At { get; init; }
|
||||
|
||||
[JsonPropertyName("direction")] public required Direction Direction { get; init; }
|
||||
|
||||
public bool Equals(DirectionalPosition? other)
|
||||
{
|
||||
|
@ -7,11 +7,11 @@ namespace pacMan.Services;
|
||||
|
||||
public interface IActionService
|
||||
{
|
||||
IPlayer Player { set; }
|
||||
Player Player { set; }
|
||||
Game Game { set; }
|
||||
void DoAction(ActionMessage message);
|
||||
List<int> RollDice();
|
||||
List<IPlayer> SetPlayerInfo(JsonElement? jsonElement);
|
||||
List<Player> SetPlayerInfo(JsonElement? jsonElement);
|
||||
object? HandleMoveCharacter(JsonElement? jsonElement); // TODO test
|
||||
object Ready();
|
||||
string FindNextPlayer();
|
||||
@ -31,7 +31,7 @@ public class ActionService : IActionService
|
||||
|
||||
public Game? Game { get; set; }
|
||||
|
||||
public IPlayer? Player { get; set; }
|
||||
public Player? Player { get; set; }
|
||||
|
||||
public void DoAction(ActionMessage message)
|
||||
{
|
||||
@ -55,13 +55,13 @@ public class ActionService : IActionService
|
||||
return rolls;
|
||||
}
|
||||
|
||||
public List<IPlayer> SetPlayerInfo(JsonElement? jsonElement)
|
||||
public List<Player> SetPlayerInfo(JsonElement? jsonElement)
|
||||
{
|
||||
var data = jsonElement?.Deserialize<PlayerInfoData>() ?? throw new NullReferenceException("Data is null");
|
||||
Player = data.Player;
|
||||
|
||||
Game? group;
|
||||
IPlayer? player;
|
||||
Player? player;
|
||||
if ((group = _gameService.FindGameByUsername(Player.Username)) != null &&
|
||||
(player = group.Players.Find(p => p.Username == Player.Username))?.State == State.Disconnected)
|
||||
{
|
||||
@ -108,7 +108,7 @@ public class ActionService : IActionService
|
||||
public object? HandleMoveCharacter(JsonElement? jsonElement)
|
||||
{
|
||||
if (Game != null && jsonElement.HasValue)
|
||||
Game.Ghosts = jsonElement.Value.GetProperty("Ghosts").Deserialize<List<Character>>() ??
|
||||
Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize<List<Character>>() ??
|
||||
throw new JsonException("Ghosts is null");
|
||||
|
||||
return jsonElement;
|
||||
@ -117,12 +117,22 @@ public class ActionService : IActionService
|
||||
|
||||
public struct PlayerInfoData
|
||||
{
|
||||
[JsonInclude] public required Player Player { get; init; }
|
||||
[JsonInclude] public required Queue<DirectionalPosition> Spawns { get; init; }
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("player")]
|
||||
public required Player Player { get; init; }
|
||||
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("spawns")]
|
||||
public required Queue<DirectionalPosition> Spawns { get; init; }
|
||||
}
|
||||
|
||||
public struct ReadyData
|
||||
{
|
||||
[JsonInclude] public required bool AllReady { get; init; }
|
||||
[JsonInclude] public required IEnumerable<IPlayer> Players { get; set; }
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("allReady")]
|
||||
public required bool AllReady { get; init; }
|
||||
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("players")]
|
||||
public required IEnumerable<Player> Players { get; set; }
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
|
||||
[JsonInclude] public Guid Id { get; } = Guid.NewGuid();
|
||||
|
||||
[JsonIgnore] public List<IPlayer> Players { get; } = new();
|
||||
[JsonIgnore] public List<Player> Players { get; } = new();
|
||||
|
||||
[JsonIgnore] public List<Character> Ghosts { get; set; } = new();
|
||||
|
||||
@ -27,7 +27,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
[JsonInclude]
|
||||
public bool IsGameStarted => Count > 0 && Players.All(player => player.State is State.InGame or State.Disconnected);
|
||||
|
||||
public IPlayer NextPlayer()
|
||||
public Player NextPlayer()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -45,7 +45,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
|
||||
public event Func<ArraySegment<byte>, Task>? Connections;
|
||||
|
||||
public bool AddPlayer(IPlayer player)
|
||||
public bool AddPlayer(Player player)
|
||||
{
|
||||
if (Players.Count >= Rules.MaxPlayers || IsGameStarted) return false;
|
||||
/* TODO remove above and uncomment below
|
||||
@ -62,7 +62,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
return true;
|
||||
}
|
||||
|
||||
public IPlayer? RemovePlayer(string username)
|
||||
public Player? RemovePlayer(string username)
|
||||
{
|
||||
var index = Players.FindIndex(p => p.Username == username);
|
||||
if (index == -1) return null;
|
||||
@ -71,7 +71,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
return removedPlayer;
|
||||
}
|
||||
|
||||
private void SetSpawn(IPlayer player)
|
||||
private void SetSpawn(Player player)
|
||||
{
|
||||
if (player.PacMan.SpawnPosition is not null) return;
|
||||
var spawn = Spawns.Dequeue();
|
||||
@ -81,7 +81,7 @@ public class Game // TODO handle disconnects and reconnects
|
||||
|
||||
public void SendToAll(ArraySegment<byte> segment) => Connections?.Invoke(segment);
|
||||
|
||||
public IEnumerable<IPlayer> SetReady(IPlayer player)
|
||||
public IEnumerable<Player> SetReady(Player player)
|
||||
{
|
||||
if (!Players.Contains(player))
|
||||
throw new PlayerNotFoundException("The player was not found in the game group.");
|
||||
|
@ -26,7 +26,7 @@ public class GameService : WebSocketService
|
||||
Connections?.Invoke(segment);
|
||||
}
|
||||
|
||||
public Game AddPlayer(IPlayer player, Queue<DirectionalPosition> spawns)
|
||||
public Game AddPlayer(Player player, Queue<DirectionalPosition> spawns)
|
||||
{
|
||||
var index = 0;
|
||||
try
|
||||
@ -50,7 +50,7 @@ public class GameService : WebSocketService
|
||||
/// <param name="player">The player instance that wants to join the game</param>
|
||||
/// <returns>Returns the updated Game object after adding the player.</returns>
|
||||
/// <exception cref="GameNotFoundException">Thrown if a game with the specified id cannot be found.</exception>
|
||||
public Game JoinById(Guid id, IPlayer player)
|
||||
public Game JoinById(Guid id, Player player)
|
||||
{
|
||||
var game = Games.FirstOrDefault(g => g.Id == id) ?? throw new GameNotFoundException();
|
||||
game.AddPlayer(player);
|
||||
@ -69,7 +69,7 @@ public class GameService : WebSocketService
|
||||
/// Thrown if the number of spawns is not equal to the maximum number of players set by
|
||||
/// the Rules.
|
||||
/// </exception>
|
||||
public Game CreateAndJoin(IPlayer player, Queue<DirectionalPosition> spawns)
|
||||
public Game CreateAndJoin(Player player, Queue<DirectionalPosition> spawns)
|
||||
{
|
||||
if (spawns.Count != Rules.MaxPlayers)
|
||||
throw new ArgumentException($"The number of spawns must be equal to {Rules.MaxPlayers}.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user