Skip to content

Commit

Permalink
feat: declare greatest empire
Browse files Browse the repository at this point in the history
 - function declareGreatestEmpire(player) on usePlayer()
 - test usePlayer.declareGreatestEmpire
 - function: empireSize(board): Record<Player, number>
 - test empire-size
 - refactor: move isConquering({ ... }) from useBoard()
 - check if player is creating greatest empire on build action
 - TODO: check if player is creating greatest empire on conquer!
  • Loading branch information
manuartero committed Jan 9, 2023
1 parent dbc6e59 commit 93ed0c1
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 26 deletions.
14 changes: 3 additions & 11 deletions src/contexts/game-context/use-board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ export function useBoard() {
});
};

const isConquering = ({ piece, to }: { to: TileID; piece: Piece }) => {
if (board[to].building && board[to].building?.owner !== piece.owner) {
console.info(`GameContext.isConquering({ ... }): YES`);
return true;
}
return false;
};

/** direct update on the board; no validity check */
const movePiece = ({ piece, from, to }: MoveAction) => {
console.info(
Expand All @@ -48,7 +40,6 @@ export function useBoard() {
...currentBoard[to],
piece,
};
// is it better to use isConquering() ?
if (targetTile.building) {
targetTile.building = {
...targetTile.building,
Expand Down Expand Up @@ -79,7 +70,7 @@ export function useBoard() {
});
};

return { board, buildOnTile, movePiece, recruitOnTile, isConquering };
return { board, buildOnTile, movePiece, recruitOnTile };
}

/**
Expand All @@ -90,6 +81,7 @@ export const emptyBoard: Board = tiles.reduce((acc, key) => {
acc[key] = {
terrain: "field",
piece: undefined,
building: undefined,
};
return acc;
}, {} as any);
}, {} as Record<TileID, Tile>);
18 changes: 13 additions & 5 deletions src/contexts/game-context/use-game-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { logRender } from "utils/console";
import { useBoard, emptyBoard } from "./use-board";
import usePlayers from "./use-players";
import { emptyTimeline, useTimeline } from "./use-timeline";
import { isConquering, isCreatingGreatesEmpire } from "models/score";
import { empireSize } from "models/empire-size";

const GameContext = createContext<GameContext>({
phase: "setup",
Expand Down Expand Up @@ -46,10 +48,10 @@ export function GameContextProvider({ children }: Props): JSX.Element {
logRender("GameContextProvider");

const [phase, setPhase] = useState<PhaseType>("planification"); // will be setup
const { board, isConquering, buildOnTile, movePiece, recruitOnTile } =
useBoard();
const { board, buildOnTile, movePiece, recruitOnTile } = useBoard();
const { timeline, nextCard, planification, newTurn } = useTimeline();
const { players, firstPlayer, scorePoint } = usePlayers();
const { players, firstPlayer, scorePoint, declareGreatestEmpire } =
usePlayers();

/* derived state */
const activeCard = timeline.current;
Expand Down Expand Up @@ -98,13 +100,19 @@ export function GameContextProvider({ children }: Props): JSX.Element {
players,

build(action: BuildAction) {
if (
isCreatingGreatesEmpire({ ...action, empires: empireSize(board) })
) {
declareGreatestEmpire(action.building.owner);
}
buildOnTile(action);
resolveActionCard();
},

move(action: MoveAction) {
if (isConquering(action)) {
scorePoint(action.piece.owner);
const player = action.piece.owner;
if (isConquering({ player, targetTile: board[action.to] })) {
scorePoint(player);
}
movePiece(action);
resolveActionCard();
Expand Down
67 changes: 58 additions & 9 deletions src/contexts/game-context/use-players.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,46 @@ import { render, screen, fireEvent } from "@testing-library/react";
import usePlayers from "./use-players";

function TestingComponent() {
const { players, firstPlayer, scorePoint } = usePlayers();
const { players, firstPlayer, scorePoint, declareGreatestEmpire } =
usePlayers();
return (
<>
<ul>
{players.map((player, i) => (
<li
key={`player-#${i}`}
>{`${player.player} - ${player.points} points`}</li>
<li key={`player-#${i}`}>{`${player.player} - ${
player.points
} points${player.greatestEmpirePoint ? "*" : ""}`}</li>
))}
</ul>
<button data-testid="enemy1-scores-point" onClick={() => scorePoint("enemy1")} />
<button
data-testid="enemy1-scores-point"
onClick={() => scorePoint("enemy1")}
/>
<button
data-testid="enemy2-first-player"
onClick={() => firstPlayer("enemy2")}
/>
<button
data-testid="player-first-player"
onClick={() => firstPlayer("player")}
/>
<button
data-testid="enemy3-greatest-empire"
onClick={() => declareGreatestEmpire("enemy3")}
/>
<button
data-testid="player-greatest-empire"
onClick={() => declareGreatestEmpire("player")}
/>
</>
);
}

const getPlayerList = () =>
Array.from(screen.getByRole("list").children).map((li) => li.textContent);

describe("game-context", () => {
test("usePlayers() returns players[]", () => {
describe("gameContext.usePlayers()", () => {
it("returns players[]", () => {
render(<TestingComponent />);
expect(getPlayerList()).toEqual([
"player - 0 points",
Expand All @@ -35,7 +51,7 @@ describe("game-context", () => {
]);
});

test("usePlayers() returns scorePoint()", () => {
test("returns scorePoint()", () => {
render(<TestingComponent />);
fireEvent.click(screen.getByTestId("enemy1-scores-point"));
expect(getPlayerList()).toEqual([
Expand All @@ -44,9 +60,17 @@ describe("game-context", () => {
"enemy2 - 0 points",
"enemy3 - 0 points",
]);
fireEvent.click(screen.getByTestId("enemy1-scores-point"));
fireEvent.click(screen.getByTestId("enemy1-scores-point"));
expect(getPlayerList()).toEqual([
"player - 0 points",
"enemy1 - 3 points",
"enemy2 - 0 points",
"enemy3 - 0 points",
]);
});

test("usePlayers() returns firstPlayer()", () => {
test("returns firstPlayer()", () => {
render(<TestingComponent />);
fireEvent.click(screen.getByTestId("enemy2-first-player"));
expect(getPlayerList()).toEqual([
Expand All @@ -55,5 +79,30 @@ describe("game-context", () => {
"enemy1 - 0 points",
"enemy3 - 0 points",
]);
fireEvent.click(screen.getByTestId("player-first-player"));
expect(getPlayerList()).toEqual([
"player - 0 points",
"enemy2 - 0 points",
"enemy1 - 0 points",
"enemy3 - 0 points",
]);
});

test("returns declareGreatesEmpire()", () => {
render(<TestingComponent />);
fireEvent.click(screen.getByTestId("enemy3-greatest-empire"));
expect(getPlayerList()).toEqual([
"player - 0 points",
"enemy1 - 0 points",
"enemy2 - 0 points",
"enemy3 - 0 points*",
]);
fireEvent.click(screen.getByTestId("player-greatest-empire"));
expect(getPlayerList()).toEqual([
"player - 0 points*",
"enemy1 - 0 points",
"enemy2 - 0 points",
"enemy3 - 0 points",
]);
});
});
18 changes: 17 additions & 1 deletion src/contexts/game-context/use-players.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const initialPlayerStatus: PlayerStatus[] = [
{ player: "enemy3", points: 0, greatestEmpirePoint: false },
];

/**
* plain react state + named update methods
*
* **no game logic here**
*/
function usePlayers() {
const [players, setPlayers] = useState(initialPlayerStatus);

Expand All @@ -29,7 +34,18 @@ function usePlayers() {
);
};

return { players, firstPlayer, scorePoint };
const declareGreatestEmpire = (player: Player) => {
console.info(`GameContext.declareGreatestEmpire({ player: ${player} })`);
setPlayers((currentPlayers) =>
currentPlayers.map((p) =>
p.player === player
? { ...p, greatestEmpirePoint: true }
: { ...p, greatestEmpirePoint: false }
)
);
};

return { players, firstPlayer, scorePoint, declareGreatestEmpire };
}

export default usePlayers;
40 changes: 40 additions & 0 deletions src/models/empire-size.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { empireSize } from "./empire-size";

const board = {
"-2,3": {
terrain: "field",
building: { owner: "player", type: "town" },
piece: undefined,
},
"-1,3": {
terrain: "field",
building: undefined,
piece: undefined,
},
"0,3": {
terrain: "field",
building: { owner: "enemy3", type: "village" },
piece: undefined,
},
"1,3": {
terrain: "field",
building: { owner: "enemy3", type: "city" },
piece: undefined,
},
"2,3": {
terrain: "field",
building: { owner: "enemy1", type: "city" },
piece: undefined,
},
} as Board;

describe("empireSize()", () => {
it("returns number of buildings grouped by player", () => {
expect(empireSize(board)).toEqual({
player: 1,
enemy1: 1,
enemy2: 0,
enemy3: 2,
});
});
});
18 changes: 18 additions & 0 deletions src/models/empire-size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export function empireSize(board: Board) {
return Object.entries(board).reduce(
(acc, [_, tile]) => {
if (tile.building) {
const player = tile.building.owner;
acc[player] = acc[player] + 1;
}
return acc;
},
{
player: 0,
enemy1: 0,
enemy2: 0,
enemy3: 0,
}
);
}

35 changes: 35 additions & 0 deletions src/models/score.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export function isConquering({
targetTile,
player,
}: {
targetTile: Tile;
player: Player;
}) {
if (targetTile.building && targetTile.building.owner !== player) {
console.info(`Score: ${player} is conquering a settlement`);
return true;
}
return false;
}

export function isCreatingGreatesEmpire({
building,
empires,
}: {
building: Building;
empires: Record<Player, number>;
}) {
const player = building.owner;
const newEmpireSize = empires[player] + 1;
if (newEmpireSize < 3) {
return false;
}
const isGreatestEmpire = Object.values(empires).every(
(size) => size < newEmpireSize
);
if (isGreatestEmpire) {
console.info(`Score: ${player} is creating the greatest empire`);
return true;
}
return false;
}

0 comments on commit 93ed0c1

Please sign in to comment.