Skip to content

Commit

Permalink
feat(game-history-record): interactions in records (#974)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinezanardi authored Apr 11, 2024
1 parent 44be6de commit 57b4813
Show file tree
Hide file tree
Showing 30 changed files with 34,080 additions and 33,559 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,6 @@
"os": [
"darwin",
"linux"
]
}
],
"packageManager": "pnpm@8.15.6+sha256.01c01eeb990e379b31ef19c03e9d06a14afa5250b82e81303f88721c99ff2e6f"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const PLAYER_INTERACTION_TYPES = [
"sentence-to-death",
"steal-role",
"infect",
"bury",
] as const;

export { PLAYER_INTERACTION_TYPES };
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from "@nestjs/common";
import { isDefined } from "class-validator";

import { RoleName } from "@/modules/role/types/role.types";
import { DeadPlayer } from "@/modules/game/schemas/player/dead-player.schema";
import { createGamePlaySourceInteraction } from "@/modules/game/helpers/game-play/game-play-source/game-play-source-interaction/game-play-source-interaction.factory";
import { createGamePlay } from "@/modules/game/helpers/game-play/game-play.factory";
import { getAlivePlayers, getAllowedToVotePlayers, getEligibleCupidTargets, getEligiblePiedPiperTargets, getEligibleWerewolvesTargets, getEligibleWhiteWerewolfTargets, getGroupOfPlayers, getPlayersWithActiveAttributeName, getPlayersWithCurrentRole, getPlayerWithCurrentRole, isGameSourceGroup, isGameSourceRole } from "@/modules/game/helpers/game.helpers";
Expand All @@ -15,6 +15,7 @@ import type { Game } from "@/modules/game/schemas/game.schema";
import type { Player } from "@/modules/game/schemas/player/player.schema";
import { GamePlayAction, GamePlaySourceName } from "@/modules/game/types/game-play/game-play.types";
import { WEREWOLF_ROLES } from "@/modules/role/constants/role-set.constants";
import { RoleName } from "@/modules/role/types/role.types";

import { createCantFindLastDeadPlayersUnexpectedException, createCantFindLastNominatedPlayersUnexpectedException, createCantFindPlayerWithCurrentRoleUnexpectedException, createMalformedCurrentGamePlayUnexpectedException, createNoCurrentGamePlayUnexpectedException } from "@/shared/exception/helpers/unexpected-exception.factory";

Expand Down Expand Up @@ -154,24 +155,39 @@ export class GamePlayAugmenterService {
return [interaction];
}

private async getSurvivorsBuryDeadBodiesGamePlaySourceInteractions(game: Game): Promise<GamePlaySourceInteraction[]> {
private getSurvivorsBuryDeadBodiesGamePlaySourceDevotedServantInteraction(game: Game, previousDeadPlayers: DeadPlayer[]): GamePlaySourceInteraction | undefined {
const devotedServantPlayer = getPlayerWithCurrentRole(game, "devoted-servant");
if (!devotedServantPlayer || !isPlayerAliveAndPowerful(devotedServantPlayer, game) ||
doesPlayerHaveActiveAttributeWithName(devotedServantPlayer, "in-love", game)) {
return [];
}
const previousGameHistoryRecord = await this.gameHistoryRecordService.getPreviousGameHistoryRecord(game._id);
if (previousGameHistoryRecord?.deadPlayers === undefined || previousGameHistoryRecord.deadPlayers.length === 0) {
throw createCantFindLastDeadPlayersUnexpectedException("getSurvivorsBuryDeadBodiesGamePlaySourceInteractions", { gameId: game._id });
return undefined;
}
const eligibleTargets = previousGameHistoryRecord.deadPlayers;
const interaction = createGamePlaySourceInteraction({
return createGamePlaySourceInteraction({
source: "devoted-servant",
type: "steal-role",
eligibleTargets,
eligibleTargets: previousDeadPlayers,
boundaries: { min: 0, max: 1 },
});
return [interaction];
}

private async getSurvivorsBuryDeadBodiesGamePlaySourceInteractions(game: Game): Promise<GamePlaySourceInteraction[]> {
const previousGameHistoryRecord = await this.gameHistoryRecordService.getPreviousGameHistoryRecord(game._id);
if (previousGameHistoryRecord?.deadPlayers === undefined || previousGameHistoryRecord.deadPlayers.length === 0) {
throw createCantFindLastDeadPlayersUnexpectedException("getSurvivorsBuryDeadBodiesGamePlaySourceInteractions", { gameId: game._id });
}
const interactions = [
createGamePlaySourceInteraction({
source: "survivors",
type: "bury",
eligibleTargets: previousGameHistoryRecord.deadPlayers,
boundaries: { min: 0, max: previousGameHistoryRecord.deadPlayers.length },
isInconsequential: true,
}),
];
const devotedServantInteraction = this.getSurvivorsBuryDeadBodiesGamePlaySourceDevotedServantInteraction(game, previousGameHistoryRecord.deadPlayers);
if (devotedServantInteraction) {
interactions.push(devotedServantInteraction);
}
return interactions;
}

private async getSurvivorsGamePlaySourceInteractions(game: Game, gamePlay: GamePlay): Promise<GamePlaySourceInteraction[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ApiPropertyOptions } from "@nestjs/swagger";
import type { ReadonlyDeep } from "type-fest";

import { GAME_PLAY_SOURCE_INTERACTION_SCHEMA } from "@/modules/game/schemas/game-play/game-play-source/game-play-source-interaction/game-play-source-interaction.schema";
import type { Player } from "@/modules/game/schemas/player/player.schema";
import { PLAYER_SCHEMA } from "@/modules/game/schemas/player/player.schema";
import { GAME_PLAY_SOURCE_NAMES } from "@/modules/game/constants/game-play/game-play.constants";
Expand All @@ -20,6 +21,11 @@ const GAME_HISTORY_RECORD_PLAY_SOURCE_FIELDS_SPECS = {
type: [PLAYER_SCHEMA],
validate: [(players: Player[]): boolean => doesArrayRespectBounds(players, { minItems: 1 }), "Path `play.source.players` length is less than minimum allowed value (1)."],
},
interactions: {
required: false,
type: [GAME_PLAY_SOURCE_INTERACTION_SCHEMA],
default: undefined,
},
} as const satisfies Record<keyof GameHistoryRecordPlaySource, MongoosePropOptions>;

const GAME_HISTORY_RECORD_PLAY_SOURCE_API_PROPERTIES: ReadonlyDeep<Record<keyof GameHistoryRecordPlaySource, ApiPropertyOptions>> = {
Expand All @@ -31,6 +37,10 @@ const GAME_HISTORY_RECORD_PLAY_SOURCE_API_PROPERTIES: ReadonlyDeep<Record<keyof
description: "Players that made the play",
...convertMongoosePropOptionsToApiPropertyOptions(GAME_HISTORY_RECORD_PLAY_SOURCE_FIELDS_SPECS.players),
},
interactions: {
description: "Interactions that the source had during the play",
...convertMongoosePropOptionsToApiPropertyOptions(GAME_HISTORY_RECORD_PLAY_SOURCE_FIELDS_SPECS.interactions),
},
};

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ApiPropertyOptions } from "@nestjs/swagger";
import { ApiProperty } from "@nestjs/swagger";
import { Expose, Type } from "class-transformer";

import { GamePlaySourceInteraction } from "@/modules/game/schemas/game-play/game-play-source/game-play-source-interaction/game-play-source-interaction.schema";
import { GamePlaySourceName } from "@/modules/game/types/game-play/game-play.types";
import { GAME_HISTORY_RECORD_PLAY_SOURCE_API_PROPERTIES, GAME_HISTORY_RECORD_PLAY_SOURCE_FIELDS_SPECS } from "@/modules/game/schemas/game-history-record/game-history-record-play/game-history-record-play-source/game-history-record-play-source.schema.constants";
import { Player } from "@/modules/game/schemas/player/player.schema";
Expand All @@ -24,6 +25,12 @@ class GameHistoryRecordPlaySource {
@Type(() => Player)
@Expose()
public players: Player[];

@ApiProperty(GAME_HISTORY_RECORD_PLAY_SOURCE_API_PROPERTIES.interactions as ApiPropertyOptions)
@Prop(GAME_HISTORY_RECORD_PLAY_SOURCE_FIELDS_SPECS.interactions as PropOptions)
@Type(() => GamePlaySourceInteraction)
@Expose()
public interactions?: GamePlaySourceInteraction[];
}

const GAME_HISTORY_RECORD_PLAY_SOURCE_SCHEMA = SchemaFactory.createForClass(GameHistoryRecordPlaySource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const GAME_PLAY_SOURCE_INTERACTION_FIELDS_SPECS = {
required: true,
type: GAME_PLAY_SOURCE_INTERACTION_BOUNDARIES_SCHEMA,
},
isInconsequential: {
required: false,
type: Boolean,
},
} as const satisfies Record<keyof GamePlaySourceInteraction, MongoosePropOptions>;

const GAME_PLAY_SOURCE_INTERACTION_API_PROPERTIES: ReadonlyDeep<Record<keyof GamePlaySourceInteraction, ApiPropertyOptions>> = {
Expand All @@ -46,6 +50,10 @@ const GAME_PLAY_SOURCE_INTERACTION_API_PROPERTIES: ReadonlyDeep<Record<keyof Gam
description: "Boundaries of the interaction",
...GAME_PLAY_SOURCE_INTERACTION_FIELDS_SPECS.boundaries,
},
isInconsequential: {
description: "Whether the interaction is inconsequential (i.e. it doesn't affect the game play nor will be validated, it's just for information purposes). It must not be set in game play targets.",
...GAME_PLAY_SOURCE_INTERACTION_FIELDS_SPECS.isInconsequential,
},
};

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class GamePlaySourceInteraction {
@Type(() => GamePlaySourceInteractionBoundaries)
@Expose()
public boundaries: GamePlaySourceInteractionBoundaries;

@ApiProperty(GAME_PLAY_SOURCE_INTERACTION_API_PROPERTIES.isInconsequential as ApiPropertyOptions)
@Prop(GAME_PLAY_SOURCE_INTERACTION_FIELDS_SPECS.isInconsequential)
@Expose()
public isInconsequential?: true;
}

const GAME_PLAY_SOURCE_INTERACTION_SCHEMA = SchemaFactory.createForClass(GamePlaySourceInteraction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,20 @@ Feature: 📜 Game History
| Babou |
| JB |
| Thomas |
And the play's type from the previous history record should be vote
And the play's source name from the previous history record should be survivors
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| vote | survivors | 1 | 6 |
And the play's source interaction from the previous history with type vote should have the following eligible targets
| name |
| Antoine |
| Juju |
| Doudou |
| Babou |
| JB |
| Thomas |
And the play's source interaction from the previous history with type vote should have consequences
And the play's type from the previous history record should be vote
And the play's cause from the previous history record should be angel-presence
And the game's current play should be survivors to vote because previous-votes-were-in-ties

Expand All @@ -101,6 +113,14 @@ Feature: 📜 Game History
| Thomas |
And the play's type from the previous history record should be vote
And the play's source name from the previous history record should be survivors
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| vote | survivors | 0 | 6 |
And the play's source interaction from the previous history with type vote should have the following eligible targets
| name |
| Juju |
| Doudou |
And the play's source interaction from the previous history with type vote should have consequences
And the play's cause from the previous history record should be previous-votes-were-in-ties
And the game's current play should be survivors to bury-dead-bodies

Expand All @@ -109,6 +129,13 @@ Feature: 📜 Game History
Then the play's action from the previous history record should be bury-dead-bodies
And the play's type from the previous history record should be bury-dead-bodies
And the play's source name from the previous history record should be survivors
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| bury | survivors | 0 | 1 |
And the play's source interaction from the previous history with type bury should have the following eligible targets
| name |
| Doudou |
And the play's source interaction from the previous history with type bury should be inconsequential
And the play's source players from the previous history record should be the following players
| name |
| Antoine |
Expand All @@ -125,6 +152,7 @@ Feature: 📜 Game History
And the play's source players from the previous history record should be the following players
| name |
| Antoine |
And the play's source players from the previous history record should not have interactions
And the play's source name from the previous history record should be stuttering-judge
And the play's cause from the previous history record should be undefined
And the game's current play should be seer to look
Expand All @@ -135,6 +163,16 @@ Feature: 📜 Game History
And the play's source players from the previous history record should be the following players
| name |
| JB |
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| look | seer | 1 | 1 |
And the play's source interaction from the previous history with type look should have the following eligible targets
| name |
| Antoine |
| Juju |
| Babou |
| Thomas |
And the play's source interaction from the previous history with type look should have consequences
And the play's type from the previous history record should be target
And the play's source name from the previous history record should be seer
And the play's cause from the previous history record should be undefined
Expand All @@ -147,6 +185,17 @@ Feature: 📜 Game History
| name |
| Thomas |
And the play's source name from the previous history record should be scandalmonger
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| mark | scandalmonger | 0 | 1 |
And the play's source interaction from the previous history with type mark should have the following eligible targets
| name |
| Antoine |
| Juju |
| Babou |
| JB |
| Thomas |
And the play's source interaction from the previous history with type mark should have consequences
And the play's cause from the previous history record should be undefined
And the game's current play should be werewolves to eat

Expand All @@ -170,6 +219,13 @@ Feature: 📜 Game History
| Babou |
| JB |
And the play's source name from the previous history record should be survivors
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| bury | survivors | 0 | 1 |
And the play's source interaction from the previous history with type bury should have the following eligible targets
| name |
| Thomas |
And the play's source interaction from the previous history with type bury should be inconsequential
And the game's current play should be survivors to vote

When the survivors vote with the following votes
Expand All @@ -184,6 +240,16 @@ Feature: 📜 Game History
| Babou |
| JB |
And the play's source name from the previous history record should be survivors
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| vote | survivors | 0 | 4 |
And the play's source interaction from the previous history with type vote should have the following eligible targets
| name |
| Antoine |
| Juju |
| Babou |
| JB |
And the play's source interaction from the previous history with type vote should have consequences
And the play's cause from the previous history record should be undefined
And the game's current play should be survivors to bury-dead-bodies

Expand All @@ -195,6 +261,13 @@ Feature: 📜 Game History
| Antoine |
| Babou |
| JB |
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| bury | survivors | 0 | 1 |
And the play's source interaction from the previous history with type bury should have the following eligible targets
| name |
| Juju |
And the play's source interaction from the previous history with type bury should be inconsequential
And the play's source name from the previous history record should be survivors
And the game's current play should be stuttering-judge to request-another-vote

Expand All @@ -205,6 +278,7 @@ Feature: 📜 Game History
And the play's source players from the previous history record should be the following players
| name |
| Antoine |
And the play's source players from the previous history record should not have interactions
And the play's source name from the previous history record should be stuttering-judge
And the play's cause from the previous history record should be undefined
And the game's current play should be survivors to vote because stuttering-judge-request
Expand All @@ -219,6 +293,15 @@ Feature: 📜 Game History
| Antoine |
| Babou |
| JB |
And the play's source players from the previous history record should have the following interactions
| type | source | minBoundary | maxBoundary |
| vote | survivors | 0 | 3 |
And the play's source interaction from the previous history with type vote should have the following eligible targets
| name |
| Antoine |
| Babou |
| JB |
And the play's source interaction from the previous history with type vote should have consequences
And the play's source name from the previous history record should be survivors
And the play's cause from the previous history record should be stuttering-judge-request

Expand Down
10 changes: 8 additions & 2 deletions tests/acceptance/features/game/features/game-play/vote.feature
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Feature: 🗳️ Vote Game Play
| Antoine |
| JB |
| Thomas |
And the game's current play source interaction with type vot should have consequences

When the survivors vote with the following votes
| voter | target |
Expand Down Expand Up @@ -82,8 +83,13 @@ Feature: 🗳️ Vote Game Play
When the werewolves eat the player named Olivia
Then the player named Olivia should be murdered by werewolves from eaten
And the game's current play should be survivors to bury-dead-bodies
And the game's current play source should not have interactions
And the game's current play can be skipped
And the game's current play source should have the following interactions
| type | source | minBoundary | maxBoundary |
| bury | survivors | 0 | 1 |
And the game's current play source interaction with type bury should have the following eligible targets
| name |
| Olivia |
And the game's current play source interaction with type bury should be inconsequential

When the survivors bury dead bodies
Then the game's current play should be survivors to vote
Expand Down
Loading

0 comments on commit 57b4813

Please sign in to comment.