fix tests for new setup + role change fix
This commit is contained in:
parent
d664ff281d
commit
97e1ca8a39
|
|
@ -1,5 +1,5 @@
|
||||||
mod kill;
|
mod kill;
|
||||||
mod night;
|
pub(crate) mod night;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod village;
|
mod village;
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
player::CharacterId,
|
player::CharacterId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
settings::{Category, GameSettings, OrRandom, SetupRole, SetupSlot, SlotId},
|
settings::{Category, GameSettings, OrRandom, SetupRole, SetupSlot, SlotId},
|
||||||
village::Village,
|
village::Village,
|
||||||
|
|
|
||||||
|
|
@ -565,7 +565,22 @@ impl Night {
|
||||||
_ => Err(GameError::InvalidMessageForGameState),
|
_ => Err(GameError::InvalidMessageForGameState),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
ActionResponse::Continue => {}
|
ActionResponse::Continue => {
|
||||||
|
if let ActionPrompt::RoleChange {
|
||||||
|
character_id,
|
||||||
|
new_role,
|
||||||
|
} = current_prompt
|
||||||
|
{
|
||||||
|
return Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
||||||
|
result: ActionResult::GoBackToSleep,
|
||||||
|
change: Some(NightChange::RoleChange(
|
||||||
|
character_id.character_id.clone(),
|
||||||
|
*new_role,
|
||||||
|
)),
|
||||||
|
unless: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match current_prompt {
|
match current_prompt {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
mod night_order;
|
mod night_order;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
diedto::DiedTo,
|
||||||
error::GameError,
|
error::GameError,
|
||||||
game::{Game, GameSettings, SetupRole},
|
game::{Game, GameSettings, OrRandom, SetupRole, night::NightChange},
|
||||||
message::{
|
message::{
|
||||||
CharacterState, Identification, PublicIdentity,
|
CharacterState, Identification, PublicIdentity,
|
||||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||||
night::{ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult},
|
night::{ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult},
|
||||||
},
|
},
|
||||||
player::{CharacterId, PlayerId},
|
player::{CharacterId, PlayerId},
|
||||||
role::RoleTitle,
|
role::{Alignment, Role, RoleTitle},
|
||||||
};
|
};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use core::{num::NonZeroU8, ops::Range};
|
use core::{
|
||||||
|
char,
|
||||||
|
num::{NonZero, NonZeroU8},
|
||||||
|
ops::Range,
|
||||||
|
};
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -20,6 +25,7 @@ use std::io::Write;
|
||||||
trait ActionResultExt {
|
trait ActionResultExt {
|
||||||
fn sleep(&self);
|
fn sleep(&self);
|
||||||
fn r#continue(&self);
|
fn r#continue(&self);
|
||||||
|
fn seer(&self) -> Alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionResultExt for ActionResult {
|
impl ActionResultExt for ActionResult {
|
||||||
|
|
@ -30,6 +36,13 @@ impl ActionResultExt for ActionResult {
|
||||||
fn r#continue(&self) {
|
fn r#continue(&self) {
|
||||||
assert_eq!(*self, ActionResult::Continue)
|
assert_eq!(*self, ActionResult::Continue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn seer(&self) -> Alignment {
|
||||||
|
match self {
|
||||||
|
ActionResult::Seer(a) => a.clone(),
|
||||||
|
_ => panic!("expected a seer result"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ServerToHostMessageExt {
|
trait ServerToHostMessageExt {
|
||||||
|
|
@ -210,7 +223,10 @@ fn gen_players(range: Range<u8>) -> Box<[Identification]> {
|
||||||
#[test]
|
#[test]
|
||||||
fn starts_with_wolf_intro() {
|
fn starts_with_wolf_intro() {
|
||||||
let players = gen_players(1..10);
|
let players = gen_players(1..10);
|
||||||
let settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
|
for _ in 0..8 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
let mut game = Game::new(&players, settings).unwrap();
|
let mut game = Game::new(&players, settings).unwrap();
|
||||||
let resp = game.process(HostGameMessage::GetState).unwrap();
|
let resp = game.process(HostGameMessage::GetState).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -225,6 +241,9 @@ fn no_wolf_kill_n1() {
|
||||||
let mut settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
settings.new_slot(RoleTitle::Shapeshifter);
|
settings.new_slot(RoleTitle::Shapeshifter);
|
||||||
settings.new_slot(RoleTitle::Protector);
|
settings.new_slot(RoleTitle::Protector);
|
||||||
|
for _ in 0..7 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
if let Some(slot) = settings
|
if let Some(slot) = settings
|
||||||
.slots()
|
.slots()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -266,7 +285,10 @@ fn no_wolf_kill_n1() {
|
||||||
#[test]
|
#[test]
|
||||||
fn yes_wolf_kill_n2() {
|
fn yes_wolf_kill_n2() {
|
||||||
let players = gen_players(1..10);
|
let players = gen_players(1..10);
|
||||||
let settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
|
for _ in 0..8 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
let mut game = Game::new(&players, settings).unwrap();
|
let mut game = Game::new(&players, settings).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||||
|
|
@ -350,6 +372,9 @@ fn protect_stops_shapeshift() {
|
||||||
let mut settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
settings.new_slot(RoleTitle::Shapeshifter);
|
settings.new_slot(RoleTitle::Shapeshifter);
|
||||||
settings.new_slot(RoleTitle::Protector);
|
settings.new_slot(RoleTitle::Protector);
|
||||||
|
for _ in 0..7 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
if let Some(slot) = settings
|
if let Some(slot) = settings
|
||||||
.slots()
|
.slots()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -496,6 +521,9 @@ fn wolfpack_kill_all_targets_valid() {
|
||||||
init_log();
|
init_log();
|
||||||
let players = gen_players(1..10);
|
let players = gen_players(1..10);
|
||||||
let mut settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
|
for _ in 0..8 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
settings.new_slot(RoleTitle::Shapeshifter);
|
settings.new_slot(RoleTitle::Shapeshifter);
|
||||||
if let Some(slot) = settings
|
if let Some(slot) = settings
|
||||||
.slots()
|
.slots()
|
||||||
|
|
@ -569,6 +597,9 @@ fn only_1_shapeshift_prompt_if_first_shifts() {
|
||||||
let mut settings = GameSettings::default();
|
let mut settings = GameSettings::default();
|
||||||
settings.new_slot(RoleTitle::Shapeshifter);
|
settings.new_slot(RoleTitle::Shapeshifter);
|
||||||
settings.new_slot(RoleTitle::Shapeshifter);
|
settings.new_slot(RoleTitle::Shapeshifter);
|
||||||
|
for _ in 0..7 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
if let Some(slot) = settings
|
if let Some(slot) = settings
|
||||||
.slots()
|
.slots()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -614,3 +645,173 @@ fn only_1_shapeshift_prompt_if_first_shifts() {
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn redeemed_scapegoat_role_changes() {
|
||||||
|
let players = gen_players(1..10);
|
||||||
|
let scapegoat_player_id = players[0].player_id.clone();
|
||||||
|
let seer_player_id = players[1].player_id.clone();
|
||||||
|
let wolf_player_id = players[2].player_id.clone();
|
||||||
|
let wolf_target_2_player_id = players[3].player_id.clone();
|
||||||
|
let mut settings = GameSettings::default();
|
||||||
|
{
|
||||||
|
let scapegoat_slot = settings.new_slot(RoleTitle::Scapegoat);
|
||||||
|
let mut scapegoat_slot = settings
|
||||||
|
.slots()
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.slot_id == scapegoat_slot)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
scapegoat_slot.role = SetupRole::Scapegoat {
|
||||||
|
redeemed: OrRandom::Determined(true),
|
||||||
|
};
|
||||||
|
scapegoat_slot.assign_to = Some(scapegoat_player_id.clone());
|
||||||
|
settings.update_slot(scapegoat_slot);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut slot = settings
|
||||||
|
.slots()
|
||||||
|
.iter()
|
||||||
|
.find(|s| matches!(s.role, SetupRole::Werewolf))
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
slot.assign_to = Some(wolf_player_id.clone());
|
||||||
|
settings.update_slot(slot);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let slot = settings.new_slot(RoleTitle::Seer);
|
||||||
|
let mut slot = settings
|
||||||
|
.slots()
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.slot_id == slot)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
slot.assign_to = Some(seer_player_id.clone());
|
||||||
|
settings.update_slot(slot);
|
||||||
|
}
|
||||||
|
for _ in 0..6 {
|
||||||
|
settings.new_slot(RoleTitle::Villager);
|
||||||
|
}
|
||||||
|
let mut game = Game::new(&players, settings).unwrap();
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::Seer);
|
||||||
|
let wolf_char_id = game
|
||||||
|
.village()
|
||||||
|
.characters()
|
||||||
|
.into_iter()
|
||||||
|
.find(|c| c.player_id() == &wolf_player_id)
|
||||||
|
.unwrap()
|
||||||
|
.character_id()
|
||||||
|
.clone();
|
||||||
|
game.mark_and_check(&wolf_char_id, |p| match p {
|
||||||
|
ActionPrompt::Seer {
|
||||||
|
marked: Some(marked),
|
||||||
|
..
|
||||||
|
} => marked == &wolf_char_id,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
||||||
|
game.next_expect_day();
|
||||||
|
|
||||||
|
assert_eq!(game.execute().title(), ActionPromptTitle::CoverOfDarkness);
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
|
||||||
|
let seer = game
|
||||||
|
.village()
|
||||||
|
.characters()
|
||||||
|
.into_iter()
|
||||||
|
.find(|c| c.player_id() == &seer_player_id)
|
||||||
|
.unwrap()
|
||||||
|
.character_id()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
game.mark_and_check(&seer, |p| match p {
|
||||||
|
ActionPrompt::WolfPackKill {
|
||||||
|
marked: Some(t), ..
|
||||||
|
} => *t == seer,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::Seer);
|
||||||
|
game.mark_and_check(&wolf_char_id, |p| match p {
|
||||||
|
ActionPrompt::Seer {
|
||||||
|
marked: Some(marked),
|
||||||
|
..
|
||||||
|
} => marked == &wolf_char_id,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
||||||
|
game.next_expect_day();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
*game
|
||||||
|
.village()
|
||||||
|
.character_by_id(&seer)
|
||||||
|
.unwrap()
|
||||||
|
.died_to()
|
||||||
|
.unwrap(),
|
||||||
|
DiedTo::Wolfpack {
|
||||||
|
killing_wolf: wolf_char_id.clone(),
|
||||||
|
night: NonZero::new(1).unwrap()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(game.execute().title(), ActionPromptTitle::CoverOfDarkness);
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
|
||||||
|
let wolf_target_2 = game
|
||||||
|
.village()
|
||||||
|
.characters()
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.player_id() == &wolf_target_2_player_id)
|
||||||
|
.unwrap()
|
||||||
|
.character_id()
|
||||||
|
.clone();
|
||||||
|
game.mark_and_check(&wolf_target_2, |r| match r {
|
||||||
|
ActionPrompt::WolfPackKill {
|
||||||
|
marked: Some(marked),
|
||||||
|
..
|
||||||
|
} => marked == &wolf_target_2,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
game.r#continue().sleep();
|
||||||
|
let scapegoat = game
|
||||||
|
.village()
|
||||||
|
.characters()
|
||||||
|
.into_iter()
|
||||||
|
.find(|c| c.player_id() == &scapegoat_player_id)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
assert_eq!(
|
||||||
|
game.next(),
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
character_id: scapegoat.identity(),
|
||||||
|
new_role: RoleTitle::Seer
|
||||||
|
}
|
||||||
|
);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
match game.game_state() {
|
||||||
|
crate::game::GameState::Night { night } => night
|
||||||
|
.changes()
|
||||||
|
.iter()
|
||||||
|
.find(|c| match c {
|
||||||
|
NightChange::RoleChange(char, role) => {
|
||||||
|
char == scapegoat.character_id() && role == &RoleTitle::Seer
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.expect("no role change"),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
let day_scapegoat = game
|
||||||
|
.village()
|
||||||
|
.character_by_id(scapegoat.character_id())
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(day_scapegoat.role().title(), RoleTitle::Seer);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ pub enum ActionPrompt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionPrompt {
|
impl ActionPrompt {
|
||||||
pub fn with_mark(&self, mark: CharacterId) -> Result<ActionPrompt> {
|
pub(crate) fn with_mark(&self, mark: CharacterId) -> Result<ActionPrompt> {
|
||||||
let mut prompt = self.clone();
|
let mut prompt = self.clone();
|
||||||
match &mut prompt {
|
match &mut prompt {
|
||||||
ActionPrompt::WolvesIntro { .. }
|
ActionPrompt::WolvesIntro { .. }
|
||||||
|
|
|
||||||
|
|
@ -1075,4 +1075,6 @@ input {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
row-gap: 10px;
|
row-gap: 10px;
|
||||||
|
|
||||||
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -473,10 +473,36 @@ impl Component for Host {
|
||||||
HostMessage::InGame(HostGameMessage::PreviousState),
|
HostMessage::InGame(HostGameMessage::PreviousState),
|
||||||
self.send.clone(),
|
self.send.clone(),
|
||||||
);
|
);
|
||||||
|
let client_click = Callback::from(|_| {
|
||||||
|
if let Some(loc) = gloo::utils::document().location() {
|
||||||
|
let _ = loc.replace("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let screen = if self.big_screen {
|
||||||
|
let to_normal = Callback::from(|_| {
|
||||||
|
if let Some(loc) = gloo::utils::document().location() {
|
||||||
|
let _ = loc.replace("/host");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
html! {
|
||||||
|
<Button on_click={to_normal}>{"small screen"}</Button>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let to_big = Callback::from(|_| {
|
||||||
|
if let Some(loc) = gloo::utils::document().location() {
|
||||||
|
let _ = loc.replace("/host/big");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
html! {
|
||||||
|
<Button on_click={to_big}>{"big screen 📺"}</Button>
|
||||||
|
}
|
||||||
|
};
|
||||||
html! {
|
html! {
|
||||||
<nav class="debug-nav" style="z-index: 3;">
|
<nav class="debug-nav" style="z-index: 3;">
|
||||||
<Button on_click={on_error_click}>{"error"}</Button>
|
<Button on_click={on_error_click}>{"error"}</Button>
|
||||||
<Button on_click={on_prev_click}>{"previous"}</Button>
|
<Button on_click={on_prev_click}>{"previous"}</Button>
|
||||||
|
{screen}
|
||||||
|
<Button on_click={client_click}>{"client"}</Button>
|
||||||
</nav>
|
</nav>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -140,19 +140,32 @@ pub fn ClientNav(ClientNavProps { message_callback }: &ClientNavProps) -> Html {
|
||||||
</ClickableTextEdit>
|
</ClickableTextEdit>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cb = message_callback.clone();
|
let debug = option_env!("DEBUG").map(|_| {
|
||||||
let forgor = move |_| {
|
let cb = message_callback.clone();
|
||||||
PlayerId::delete();
|
let forgor = move |_| {
|
||||||
PublicIdentity::delete();
|
PlayerId::delete();
|
||||||
cb.emit(ClientMessage::Goodbye);
|
PublicIdentity::delete();
|
||||||
let _ = gloo::utils::window().location().reload();
|
cb.emit(ClientMessage::Goodbye);
|
||||||
};
|
let _ = gloo::utils::window().location().reload();
|
||||||
|
};
|
||||||
|
let host_click = Callback::from(|_| {
|
||||||
|
if let Some(loc) = gloo::utils::document().location() {
|
||||||
|
let _ = loc.replace("/host");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<Button on_click={host_click}>{"host"}</Button>
|
||||||
|
<Button on_click={forgor}>{"forgor 💀"}</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
});
|
||||||
html! {
|
html! {
|
||||||
<nav class="client-nav">
|
<nav class="client-nav">
|
||||||
{number}
|
{number}
|
||||||
{name}
|
{name}
|
||||||
{pronouns}
|
{pronouns}
|
||||||
<Button on_click={forgor}>{"forgor 💀"}</Button>
|
{debug}
|
||||||
</nav>
|
</nav>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue