Refactored box to use number instead of list of objects

This commit is contained in:
Martin Berg Alstad 2023-07-22 21:20:30 +02:00
parent d98293e3e7
commit 12b40702c8
12 changed files with 49 additions and 76 deletions

@ -11,9 +11,9 @@ namespace BackendTests.Services;
public class ActionServiceTests public class ActionServiceTests
{ {
private readonly Player _blackPlayer = Players.Create("black"); private readonly Player _blackPlayer = Players.Create("black");
private readonly Player _redPlayer = (Player)Players.Create("red"); private readonly Player _redPlayer = Players.Create("red");
private readonly Player _whitePlayer = (Player)Players.Create("white"); private readonly Player _whitePlayer = Players.Create("white");
private ActionMessage _blackMessage = null!; private ActionMessage _blackMessage = null!;
private GameService _gameService = null!; private GameService _gameService = null!;
private ActionMessage _redMessage = null!; private ActionMessage _redMessage = null!;
@ -92,7 +92,7 @@ public class ActionServiceTests
public void PlayerInfo_DataIsNotPlayer() public void PlayerInfo_DataIsNotPlayer()
{ {
var serialized = var serialized =
JsonDocument.Parse(JsonSerializer.Serialize(new Box { Colour = "white", Pellets = new List<Pellet>() })); JsonDocument.Parse(JsonSerializer.Serialize(new Box { Colour = "white" }));
var message = new ActionMessage var message = new ActionMessage
{ {
Action = GameAction.PlayerInfo, Action = GameAction.PlayerInfo,

@ -24,8 +24,7 @@ internal static class Players
internal static Box CreateBox(string colour) => internal static Box CreateBox(string colour) =>
new() new()
{ {
Colour = colour, Colour = colour
Pellets = new List<Pellet>()
}; };
internal static Player Clone(this Player player) => internal static Player Clone(this Player player) =>

@ -3,9 +3,8 @@ import {Character} from "../game/character";
import findPossiblePositions from "../game/possibleMovesAlgorithm"; import findPossiblePositions from "../game/possibleMovesAlgorithm";
import {GameTile} from "./gameTile"; import {GameTile} from "./gameTile";
import {TileType} from "../game/tileType"; import {TileType} from "../game/tileType";
import {atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom} from "jotai"; import {atom, useAtom, useAtomValue, useSetAtom} from "jotai";
import {allCharactersAtom, currentPlayerAtom, playersAtom, selectedDiceAtom} from "../utils/state"; import {allCharactersAtom, currentPlayerAtom, playersAtom, selectedDiceAtom} from "../utils/state";
import Pellet from "../game/pellet";
import {Dialog, Transition} from "@headlessui/react"; import {Dialog, Transition} from "@headlessui/react";
interface BoardProps extends ComponentProps { interface BoardProps extends ComponentProps {
@ -13,7 +12,7 @@ interface BoardProps extends ComponentProps {
map: GameMap map: GameMap
} }
const modalOpenAtom: PrimitiveAtom<boolean> = atom(false); const modalOpenAtom = atom(false);
const Board: FC<BoardProps> = ( const Board: FC<BoardProps> = (
{ {
@ -77,7 +76,11 @@ const Board: FC<BoardProps> = (
const currentTile = map[tile.y][tile.x]; const currentTile = map[tile.y][tile.x];
function updateTileAndPlayerBox(isPowerPellet = false): void { function updateTileAndPlayerBox(isPowerPellet = false): void {
currentPlayer?.addPellet(new Pellet(isPowerPellet)); if (isPowerPellet) {
currentPlayer?.addPowerPellet();
} else {
currentPlayer?.addPellet();
}
map[tile.y][tile.x] = TileType.empty; map[tile.y][tile.x] = TileType.empty;
positions.push(tile); positions.push(tile);
} }
@ -182,10 +185,10 @@ const SelectPlayerModal: FC = () => {
{ {
allPlayers.map(player => allPlayers.map(player =>
<div key={player.username} className={"border-b pb-1"}> <div key={player.username} className={"border-b pb-1"}>
<span className={"mx-2"}>{player.username} has {player.box.count} pellets</span> <span className={"mx-2"}>{player.username} has {player.box.pellets} pellets</span>
<button className={"text-blue-500 enabled:cursor-pointer disabled:text-gray-500"} <button className={"text-blue-500 enabled:cursor-pointer disabled:text-gray-500"}
style={{background: "none"}} style={{background: "none"}}
disabled={player.box.count === 0} disabled={player.box.pellets === 0}
onClick={() => { onClick={() => {
currentPlayer?.stealFrom(player); currentPlayer?.stealFrom(player);
close(); close();

@ -20,7 +20,6 @@ const wsService = new WebSocketService(import.meta.env.VITE_API_WS);
// TODO bug, stolen pellets are only updated on the client that stole them // TODO bug, stolen pellets are only updated on the client that stole them
// TODO guest users // TODO guest users
// TODO protected routes? checking if user is logged in
// TODO store map in backend and save it in state on each client // TODO store map in backend and save it in state on each client
// TODO add debug menu on dev, for testing and cheating // TODO add debug menu on dev, for testing and cheating
// TODO sign up player page // TODO sign up player page

@ -17,9 +17,10 @@ const PlayerStats: FC<{ player: Player } & ComponentProps> = (
<p>Colour: {player.colour}</p> <p>Colour: {player.colour}</p>
{player.state === State.inGame || player.state === State.disconnected ? {player.state === State.inGame || player.state === State.disconnected ?
<> <>
<p>Pellets: {player.box.count}</p> <p>Pellets: {player.box.pellets}</p>
<p>PowerPellets: {player.box.countPowerPellets}</p> <p>PowerPellets: {player.box.powerPellets}</p>
</> : </>
:
<p>{player.state === State.waitingForPlayers ? "Waiting" : "Ready"}</p>} <p>{player.state === State.waitingForPlayers ? "Waiting" : "Ready"}</p>}
</div> </div>
); );

@ -1,28 +1,32 @@
import Pellet from "./pellet";
export default class Box implements BoxProps { export default class Box implements BoxProps {
public pellets;
public readonly colour; public readonly colour;
public pellets;
public powerPellets;
public constructor({colour, pellets = []}: BoxProps) { public constructor({colour, pellets = 0, powerPellets = 0}: BoxProps) {
this.colour = colour; this.colour = colour;
this.pellets = pellets; this.pellets = pellets;
this.powerPellets = powerPellets;
} }
get powerPellet(): Pellet | undefined { public addPellet(): void {
return this.pellets.find(pellet => pellet.isPowerPellet); this.pellets++;
} }
get count(): number { public removePellet(): boolean {
return this.pellets.filter(pellet => !pellet.isPowerPellet).length; if (this.pellets <= 0) return false;
this.pellets--;
return true;
} }
get countPowerPellets(): number { public addPowerPellet(): void {
return this.pellets.filter(pellet => pellet.isPowerPellet).length; this.powerPellets++;
} }
public addPellet(pellet: Pellet): void { public removePowerPellet(): boolean {
this.pellets.push(pellet); if (this.powerPellets <= 0) return false;
this.powerPellets--;
return true;
} }
} }

@ -1,7 +0,0 @@
export default class Pellet {
public readonly isPowerPellet: boolean;
public constructor(isPowerPellet = false) {
this.isPowerPellet = isPowerPellet;
}
}

@ -2,7 +2,6 @@ import {Character, CharacterType} from "./character";
import Box from "./box"; import Box from "./box";
import {getDefaultStore} from "jotai"; import {getDefaultStore} from "jotai";
import {currentPlayerNameAtom, playersAtom} from "../utils/state"; import {currentPlayerNameAtom, playersAtom} from "../utils/state";
import Pellet from "./pellet";
import rules from "./rules"; import rules from "./rules";
export enum State { export enum State {
@ -35,15 +34,19 @@ export default class Player implements PlayerProps {
return Player.store.get(currentPlayerNameAtom) === this.username; return Player.store.get(currentPlayerNameAtom) === this.username;
} }
public addPellet(pellet: Pellet): void { public addPellet(): void {
this.box.addPellet(pellet); this.box.addPellet();
}
public addPowerPellet(): void {
this.box.addPowerPellet();
} }
public stealFrom(other: Player): void { public stealFrom(other: Player): void {
for (let i = 0; i < rules.maxStealPellets; i++) { for (let i = 0; i < rules.maxStealPellets; i++) {
const pellet = other.box.pellets.pop(); const removed = other.box.removePellet();
if (pellet) if (removed)
this.box.addPellet(pellet); 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));
} }

@ -38,7 +38,8 @@ interface CharacterProps {
} }
interface BoxProps { interface BoxProps {
pellets?: import("../game/pellet").default[], pellets?: number,
powerPellets?: number,
readonly colour: import("../game/colour").Colour, readonly colour: import("../game/colour").Colour,
} }

@ -4,23 +4,16 @@ namespace pacMan.GameStuff.Items;
public class Box : IEquatable<Box> public class Box : IEquatable<Box>
{ {
[JsonPropertyName("pellets")] public List<Pellet>? Pellets { get; init; } = new(); [JsonPropertyName("pellets")] public int Pellets { get; init; }
[JsonPropertyName("powerPellets")] public int PowerPellet { get; init; }
[JsonPropertyName("colour")] public required string Colour { get; init; } [JsonPropertyName("colour")] public required string Colour { get; init; }
public int CountNormal => Pellets?.Count(pellet => !pellet.IsPowerPellet) ?? 0;
public bool Equals(Box? other) public bool Equals(Box? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
return (Equals(Pellets, other.Pellets) || (Pellets?.Count == 0 && other.Pellets?.Count == 0)) && return Pellets == other.Pellets && PowerPellet == other.PowerPellet && Colour == other.Colour;
Colour == other.Colour;
}
public void Add(Pellet pellet)
{
Pellets?.Add(pellet);
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@ -30,5 +23,5 @@ public class Box : IEquatable<Box>
return obj.GetType() == GetType() && Equals((Box)obj); return obj.GetType() == GetType() && Equals((Box)obj);
} }
public override int GetHashCode() => HashCode.Combine(Pellets, Colour); public override int GetHashCode() => HashCode.Combine(Pellets, PowerPellet, Colour);
} }

@ -1,24 +0,0 @@
using System.Text.Json.Serialization;
namespace pacMan.GameStuff.Items;
public class Pellet : IEquatable<Pellet>
{
[JsonPropertyName("isPowerPellet")] public bool IsPowerPellet { get; init; }
public bool Equals(Pellet? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return IsPowerPellet == other.IsPowerPellet;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Pellet)obj);
}
public override int GetHashCode() => IsPowerPellet.GetHashCode();
}

@ -48,6 +48,7 @@
<TypeScriptCompile Remove="ClientApp\src\websockets\actions.ts" /> <TypeScriptCompile Remove="ClientApp\src\websockets\actions.ts" />
<TypeScriptCompile Remove="ClientApp\src\utils\colours.ts" /> <TypeScriptCompile Remove="ClientApp\src\utils\colours.ts" />
<TypeScriptCompile Remove="ClientApp\src\utils\dom.ts" /> <TypeScriptCompile Remove="ClientApp\src\utils\dom.ts" />
<TypeScriptCompile Remove="ClientApp\src\game\pellet.ts" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>