werewolves/werewolves-proto/src/game_test/role/shapeshifter.rs

280 lines
9.0 KiB
Rust

// 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 <https://www.gnu.org/licenses/>.
#[allow(unused)]
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
use crate::{
character::CharacterId,
game::{Game, GameSettings, SetupRole},
game_test::{
ActionPromptTitleExt, ActionResultExt, GameExt, ServerToHostMessageExt, SettingsExt,
gen_players, init_log,
},
message::{
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
night::{ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult},
},
role::RoleTitle,
};
#[test]
fn protect_stops_shapeshift() {
init_log();
let players = gen_players(1..10);
let mut settings = GameSettings::default();
settings.new_slot(RoleTitle::Shapeshifter);
settings.new_slot(RoleTitle::Protector);
settings.fill_remaining_slots_with_villagers(players.len());
let mut game = Game::new(&players, settings).unwrap();
game.r#continue().r#continue();
game.next().title().wolves_intro();
assert_eq!(
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
ActionResponse::Continue
)))
.unwrap(),
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
);
game.next_expect_day();
let execution_target = game
.village()
.characters()
.into_iter()
.find(|v| v.is_village() && !matches!(v.role().title(), RoleTitle::Protector))
.unwrap()
.character_id();
match game
.process(HostGameMessage::Day(HostDayMessage::MarkForExecution(
execution_target,
)))
.unwrap()
{
ServerToHostMessage::Daytime { marked, .. } => {
assert_eq!(marked.to_vec(), vec![execution_target])
}
resp => panic!("unexpected server message: {resp:#?}"),
}
assert_eq!(
game.process(HostGameMessage::Day(HostDayMessage::Execute))
.unwrap()
.prompt()
.title(),
ActionPromptTitle::CoverOfDarkness
);
game.r#continue().r#continue();
let (prot_and_wolf_target, prot_char_id) = match game
.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap()
{
ServerToHostMessage::ActionPrompt(
ActionPrompt::Protector {
character_id: prot_char_id,
targets,
marked: None,
},
0,
) => (
targets
.into_iter()
.map(|c| game.village().character_by_id(c.character_id).unwrap())
.find(|c| c.is_village())
.unwrap()
.character_id(),
prot_char_id,
),
_ => panic!("first n2 prompt isn't protector"),
};
let target = game
.village()
.character_by_id(prot_and_wolf_target)
.unwrap()
.clone();
log::info!("target: {target:#?}");
match game
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
ActionResponse::MarkTarget(prot_and_wolf_target),
)))
.unwrap()
{
ServerToHostMessage::ActionPrompt(
ActionPrompt::Protector {
marked: Some(mark), ..
},
0,
) => assert_eq!(mark, prot_and_wolf_target, "marked target"),
resp => panic!("unexpected response: {resp:?}"),
}
game.r#continue().sleep();
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
game.mark_and_check(prot_and_wolf_target);
game.r#continue().r#continue();
assert_eq!(game.next().title(), ActionPromptTitle::Shapeshifter,);
game.response(ActionResponse::Shapeshift)
.shapeshift_failed();
game.r#continue().sleep();
game.next_expect_day();
let target = game
.village()
.character_by_id(target.character_id())
.unwrap();
assert!(target.is_village());
assert!(target.alive());
let prot = game
.village()
.character_by_id(prot_char_id.character_id)
.unwrap();
assert!(prot.is_village());
assert!(prot.alive());
assert_eq!(prot.role().title(), RoleTitle::Protector);
}
#[test]
fn only_1_shapeshift_prompt_if_first_shifts() {
let players = gen_players(1..10);
let mut settings = GameSettings::empty();
settings.new_slot(RoleTitle::Werewolf);
settings.new_slot(RoleTitle::Shapeshifter);
settings.new_slot(RoleTitle::Shapeshifter);
settings.fill_remaining_slots_with_villagers(players.len());
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();
let target = game
.village()
.characters()
.into_iter()
.find_map(|c| c.is_village().then_some(c.character_id()))
.unwrap();
let (_, marked, _) = game.mark_for_execution(target);
let (marked, target_list): (&[CharacterId], &[CharacterId]) = (&marked, &[target]);
assert_eq!(target_list, marked);
assert_eq!(game.execute().title(), ActionPromptTitle::WolfPackKill);
let target = game
.village()
.characters()
.into_iter()
.find_map(|c| (c.is_village() && c.alive()).then_some(c.character_id()))
.unwrap();
game.mark_and_check(target);
game.r#continue().r#continue();
assert_eq!(game.next().title(), ActionPromptTitle::Shapeshifter);
game.response(ActionResponse::Shapeshift).r#continue();
assert_eq!(game.next().title(), ActionPromptTitle::RoleChange);
game.r#continue().sleep();
game.next_expect_day();
}
#[test]
fn i_would_simply_refuse() {
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.r#continue().sleep();
game.next_expect_day();
}
#[test]
fn shapeshift_fail_can_continue() {
let players = gen_players(1..21);
let mut player_ids = players.iter().map(|p| p.player_id);
let shapeshifter = player_ids.next().unwrap();
let direwolf = player_ids.next().unwrap();
let wolf = player_ids.next().unwrap();
let protector = player_ids.next().unwrap();
let mut settings = GameSettings::empty();
settings.add_and_assign(SetupRole::Shapeshifter, shapeshifter);
settings.add_and_assign(SetupRole::DireWolf, direwolf);
settings.add_and_assign(SetupRole::Werewolf, wolf);
settings.add_and_assign(SetupRole::Protector, protector);
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().r#continue();
game.next().title().direwolf();
let dw_target = game.living_villager();
game.mark(dw_target.character_id());
game.r#continue().sleep();
game.next_expect_day();
game.execute().title().protector();
let ss_target = game.living_villager();
game.mark(ss_target.character_id());
game.r#continue().sleep();
game.next().title().wolf_pack_kill();
game.mark(ss_target.character_id());
game.r#continue().r#continue();
game.next().title().shapeshifter();
match game
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
ActionResponse::Shapeshift,
)))
.unwrap()
{
ServerToHostMessage::ActionResult(_, ActionResult::ShiftFailed) => {}
other => panic!("expected shift fail, got {other:?}"),
};
game.r#continue().r#continue();
game.next().title().direwolf();
game.mark(
game.living_villager_excl(dw_target.player_id())
.character_id(),
);
game.r#continue().sleep();
game.next_expect_day();
}