// Copyright (C) 2025 Emilis Bliūdžius
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use core::num::NonZeroU8;
#[allow(unused)]
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
use crate::{
diedto::DiedToTitle,
game::{Game, GameSettings, GameState, OrRandom, SetupRole},
game_test::{
ActionPromptTitleExt, ActionResultExt, GameExt, SettingsExt, gen_players, init_log,
},
message::{
Identification, PublicIdentity,
host::ServerToHostMessage,
night::{ActionPrompt, ActionPromptTitle, ActionResponse},
},
player::PlayerId,
role::RoleTitle,
};
#[test]
fn previous_shapeshifter_undone_redone() {
let players = gen_players(1..21);
let shapeshifter_player_id = players[0].player_id;
let wolf_player_id = players[1].player_id;
let mut settings = GameSettings::empty();
settings.add_and_assign(SetupRole::Shapeshifter, shapeshifter_player_id);
settings.add_and_assign(SetupRole::Werewolf, wolf_player_id);
settings.fill_remaining_slots_with_villagers(20);
let mut game = Game::new(&players, settings).unwrap();
game.r#continue().r#continue();
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
game.r#continue().sleep();
game.next_expect_day();
game.execute().title().wolf_pack_kill();
let ss_target = game.living_villager();
game.mark(ss_target.character_id());
game.r#continue().r#continue();
game.next().title().shapeshifter();
game.response(ActionResponse::Shapeshift).r#continue();
assert_eq!(
game.next(),
ActionPrompt::RoleChange {
character_id: ss_target.identity(),
new_role: RoleTitle::Werewolf
}
);
match game.game_state_mut() {
GameState::Night { night } => night.previous_state().unwrap(),
GameState::Day { .. } => unreachable!(),
}
assert_eq!(
game.get_state(),
ServerToHostMessage::ActionPrompt(
ActionPrompt::Shapeshifter {
character_id: game
.character_by_player_id(shapeshifter_player_id)
.identity()
},
0
)
);
game.response(ActionResponse::Shapeshift).r#continue();
assert_eq!(
game.next(),
ActionPrompt::RoleChange {
character_id: ss_target.identity(),
new_role: RoleTitle::Werewolf
}
);
game.r#continue().sleep();
game.next_expect_day();
}
#[test]
fn previous_shapeshifter_undone_and_changed_to_no() {
init_log();
let players = gen_players(1..21);
let shapeshifter_player_id = players[0].player_id;
let wolf_player_id = players[1].player_id;
let mut settings = GameSettings::empty();
settings.add_and_assign(SetupRole::Shapeshifter, shapeshifter_player_id);
settings.add_and_assign(SetupRole::Werewolf, wolf_player_id);
settings.fill_remaining_slots_with_villagers(20);
let mut game = Game::new(&players, settings).unwrap();
game.r#continue().r#continue();
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
game.r#continue().sleep();
game.next_expect_day();
game.execute().title().wolf_pack_kill();
let ss_target = game.living_villager();
game.mark(ss_target.character_id());
game.r#continue().r#continue();
game.next().title().shapeshifter();
game.response(ActionResponse::Shapeshift).r#continue();
assert_eq!(
game.next(),
ActionPrompt::RoleChange {
character_id: ss_target.identity(),
new_role: RoleTitle::Werewolf
}
);
match game.game_state_mut() {
GameState::Night { night } => night.previous_state().unwrap(),
GameState::Day { .. } => unreachable!(),
}
assert_eq!(
game.get_state(),
ServerToHostMessage::ActionPrompt(
ActionPrompt::Shapeshifter {
character_id: game
.character_by_player_id(shapeshifter_player_id)
.identity()
},
0
)
);
game.r#continue().sleep();
game.next_expect_day();
assert_eq!(
game.character_by_player_id(ss_target.player_id())
.role_changes(),
&[]
);
}
#[test]
fn previous_prompt() {
init_log();
let players = (1..32u8)
.filter_map(NonZeroU8::new)
.map(|n| Identification {
player_id: PlayerId::from_u128(n.get() as _),
public: PublicIdentity {
name: format!("Player {n}"),
pronouns: Some("he/him".into()),
number: Some(n),
},
})
.collect::>();
let mut players_iter = players.iter().map(|p| p.player_id);
let (
werewolf,
dire_wolf,
shapeshifter,
alpha_wolf,
seer,
arcanist,
maple_wolf,
guardian,
vindicator,
adjudicator,
power_seer,
beholder,
gravedigger,
mortician,
insomniac,
empath,
scapegoat,
hunter,
) = (
(SetupRole::Werewolf, players_iter.next().unwrap()),
(SetupRole::DireWolf, players_iter.next().unwrap()),
(SetupRole::Shapeshifter, players_iter.next().unwrap()),
(SetupRole::AlphaWolf, players_iter.next().unwrap()),
(SetupRole::Seer, players_iter.next().unwrap()),
(SetupRole::Arcanist, players_iter.next().unwrap()),
(SetupRole::MapleWolf, players_iter.next().unwrap()),
(SetupRole::Guardian, players_iter.next().unwrap()),
(SetupRole::Vindicator, players_iter.next().unwrap()),
(SetupRole::Adjudicator, players_iter.next().unwrap()),
(SetupRole::PowerSeer, players_iter.next().unwrap()),
(SetupRole::Beholder, players_iter.next().unwrap()),
(SetupRole::Gravedigger, players_iter.next().unwrap()),
(SetupRole::Mortician, players_iter.next().unwrap()),
(SetupRole::Insomniac, players_iter.next().unwrap()),
(SetupRole::Empath, players_iter.next().unwrap()),
(
SetupRole::Scapegoat {
redeemed: OrRandom::Determined(false),
},
players_iter.next().unwrap(),
),
(SetupRole::Hunter, players_iter.next().unwrap()),
);
let mut settings = GameSettings::empty();
settings.add_and_assign(werewolf.0, werewolf.1);
settings.add_and_assign(dire_wolf.0, dire_wolf.1);
settings.add_and_assign(shapeshifter.0, shapeshifter.1);
settings.add_and_assign(alpha_wolf.0, alpha_wolf.1);
settings.add_and_assign(seer.0, seer.1);
settings.add_and_assign(arcanist.0, arcanist.1);
settings.add_and_assign(maple_wolf.0, maple_wolf.1);
settings.add_and_assign(guardian.0, guardian.1);
settings.add_and_assign(vindicator.0, vindicator.1);
settings.add_and_assign(adjudicator.0, adjudicator.1);
settings.add_and_assign(power_seer.0, power_seer.1);
settings.add_and_assign(beholder.0, beholder.1);
settings.add_and_assign(gravedigger.0, gravedigger.1);
settings.add_and_assign(mortician.0, mortician.1);
settings.add_and_assign(insomniac.0, insomniac.1);
settings.add_and_assign(empath.0, empath.1);
settings.add_and_assign(scapegoat.0, scapegoat.1);
settings.add_and_assign(hunter.0, hunter.1);
settings.fill_remaining_slots_with_villagers(players.len());
let (
werewolf,
dire_wolf,
shapeshifter,
alpha_wolf,
seer,
arcanist,
maple_wolf,
guardian,
vindicator,
adjudicator,
power_seer,
beholder,
gravedigger,
mortician,
insomniac,
empath,
scapegoat,
hunter,
) = (
werewolf.1,
dire_wolf.1,
shapeshifter.1,
alpha_wolf.1,
seer.1,
arcanist.1,
maple_wolf.1,
guardian.1,
vindicator.1,
adjudicator.1,
power_seer.1,
beholder.1,
gravedigger.1,
mortician.1,
insomniac.1,
empath.1,
scapegoat.1,
hunter.1,
);
let mut game = Game::new(&players, settings).unwrap();
game.r#continue().r#continue();
game.next().title().wolves_intro();
game.r#continue().r#continue();
game.next().title().direwolf();
game.mark(game.character_by_player_id(seer).character_id());
game.r#continue().sleep();
game.next().title().seer();
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().seer();
game.r#continue().sleep();
game.next().title().arcanist();
game.mark(game.character_by_player_id(seer).character_id());
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().role_blocked();
game.r#continue().sleep();
game.next().title().adjudicator();
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().adjudicator();
game.r#continue().sleep();
game.next().title().power_seer();
match game.prev() {
ServerToHostMessage::ActionPrompt(
ActionPrompt::Adjudicator {
character_id,
marked: Some(mark),
..
},
_,
) => {
assert_eq!(
character_id,
game.character_by_player_id(adjudicator).identity()
);
assert_eq!(mark, game.character_by_player_id(werewolf).character_id());
}
resp => panic!("expected adjudicator prompt, got {resp:?}"),
}
match game.prev() {
ServerToHostMessage::ActionPrompt(
ActionPrompt::Arcanist {
character_id,
marked: (Some(mark1), Some(mark2)),
..
},
_,
) => {
assert_eq!(
character_id,
game.character_by_player_id(arcanist).identity()
);
assert_eq!(mark1, game.character_by_player_id(seer).character_id());
assert_eq!(mark2, game.character_by_player_id(werewolf).character_id());
}
resp => panic!("expected arcanist prompt, got {resp:?}"),
}
game.r#continue().role_blocked();
game.r#continue().sleep();
game.next().title().adjudicator();
game.r#continue().adjudicator();
game.r#continue().sleep();
game.next().title().power_seer();
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().power_seer();
game.r#continue().sleep();
game.next_expect_day();
game.mark_for_execution(game.character_by_player_id(dire_wolf).character_id());
game.mark_for_execution(game.character_by_player_id(alpha_wolf).character_id());
game.execute().title().guardian();
let protect = game.living_villager();
game.mark(protect.character_id());
game.r#continue().sleep();
game.next().title().wolf_pack_kill();
game.mark(protect.character_id());
game.r#continue().r#continue();
game.next().title().shapeshifter();
game.response(ActionResponse::Shapeshift).sleep();
game.next().title().seer();
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().seer();
game.r#continue().sleep();
game.next().title().arcanist();
game.mark(game.character_by_player_id(seer).character_id());
game.mark(game.character_by_player_id(werewolf).character_id());
game.r#continue().arcanist();
game.r#continue().sleep();
game.next().title().adjudicator();
game.mark(game.character_by_player_id(seer).character_id());
game.r#continue().adjudicator();
game.r#continue().sleep();
game.next().title().power_seer();
game.mark(game.living_villager().character_id());
game.r#continue().power_seer();
game.r#continue().sleep();
game.next().title().gravedigger();
game.mark(game.character_by_player_id(dire_wolf).character_id());
assert_eq!(game.r#continue().gravedigger(), Some(RoleTitle::DireWolf));
game.r#continue().sleep();
game.next().title().mortician();
game.mark(game.character_by_player_id(dire_wolf).character_id());
assert_eq!(game.r#continue().mortician(), DiedToTitle::Execution);
game.r#continue().sleep();
game.next().title().empath();
game.mark(game.living_villager().character_id());
assert!(!game.r#continue().empath());
game.r#continue().sleep();
game.next().title().maple_wolf();
game.mark(
game.living_villager_excl(protect.player_id())
.character_id(),
);
game.r#continue().sleep();
game.next().title().hunter();
game.mark(game.character_by_player_id(insomniac).character_id());
game.r#continue().sleep();
game.next().title().insomniac();
game.r#continue().insomniac();
game.r#continue().sleep();
game.next().title().beholder();
game.mark(game.character_by_player_id(power_seer).character_id());
game.r#continue().sleep();
game.next_expect_day();
}