// 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(); }