Compare commits
2 Commits
00617afe7b
...
a424211ab4
| Author | SHA1 | Date |
|---|---|---|
|
|
a424211ab4 | |
|
|
5c8e33dab1 |
|
|
@ -255,7 +255,27 @@ impl Night {
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// let current_prompt = action_queue.pop_front().ok_or(GameError::NoNightActions)?;
|
if let Some(prompt) = village.wolf_revert_prompt() {
|
||||||
|
match &prompt {
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
character_id,
|
||||||
|
new_role,
|
||||||
|
} => {
|
||||||
|
action_queue = Self::remove_reverted_prompts(
|
||||||
|
action_queue,
|
||||||
|
character_id.character_id,
|
||||||
|
*new_role,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
prompt => {
|
||||||
|
log::error!(
|
||||||
|
"wolf_revert_prompt should have returned a role change, got {prompt:?}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action_queue.push_front(prompt);
|
||||||
|
}
|
||||||
let night_state = NightState::Active {
|
let night_state = NightState::Active {
|
||||||
current_prompt: ActionPrompt::CoverOfDarkness,
|
current_prompt: ActionPrompt::CoverOfDarkness,
|
||||||
current_changes: Vec::new(),
|
current_changes: Vec::new(),
|
||||||
|
|
@ -272,6 +292,84 @@ impl Night {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_reverted_prompts(
|
||||||
|
mut action_queue: VecDeque<ActionPrompt>,
|
||||||
|
reverting: CharacterId,
|
||||||
|
reverting_into: RoleTitle,
|
||||||
|
) -> VecDeque<ActionPrompt> {
|
||||||
|
let mut new_queue = VecDeque::new();
|
||||||
|
if let Some(wolves) = action_queue.iter_mut().find_map(|q| match q {
|
||||||
|
ActionPrompt::WolvesIntro { wolves } => Some(wolves),
|
||||||
|
_ => None,
|
||||||
|
}) && let Some(w) = wolves.iter_mut().find(|w| w.0.character_id == reverting)
|
||||||
|
{
|
||||||
|
w.1 = reverting_into;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove prompts by the reverting wolf that are in the queue
|
||||||
|
for prompt in action_queue {
|
||||||
|
let (wolf_id, prompt) = match prompt {
|
||||||
|
ActionPrompt::WolvesIntro { mut wolves } => {
|
||||||
|
if let Some(w) = wolves.iter_mut().find(|w| w.0.character_id == reverting) {
|
||||||
|
w.1 = reverting_into;
|
||||||
|
}
|
||||||
|
new_queue.push_front(ActionPrompt::WolvesIntro { wolves });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionPrompt::Shapeshifter { character_id } => (
|
||||||
|
character_id.character_id,
|
||||||
|
ActionPrompt::Shapeshifter { character_id },
|
||||||
|
),
|
||||||
|
ActionPrompt::AlphaWolf {
|
||||||
|
character_id,
|
||||||
|
living_villagers,
|
||||||
|
marked,
|
||||||
|
} => (
|
||||||
|
character_id.character_id,
|
||||||
|
ActionPrompt::AlphaWolf {
|
||||||
|
character_id,
|
||||||
|
living_villagers,
|
||||||
|
marked,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ActionPrompt::DireWolf {
|
||||||
|
character_id,
|
||||||
|
living_players,
|
||||||
|
marked,
|
||||||
|
} => (
|
||||||
|
character_id.character_id,
|
||||||
|
ActionPrompt::DireWolf {
|
||||||
|
character_id,
|
||||||
|
living_players,
|
||||||
|
marked,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ActionPrompt::LoneWolfKill {
|
||||||
|
character_id,
|
||||||
|
living_players,
|
||||||
|
marked,
|
||||||
|
} => (
|
||||||
|
character_id.character_id,
|
||||||
|
ActionPrompt::LoneWolfKill {
|
||||||
|
character_id,
|
||||||
|
living_players,
|
||||||
|
marked,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
other => {
|
||||||
|
new_queue.push_front(other);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if wolf_id != reverting {
|
||||||
|
new_queue.push_front(prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_queue
|
||||||
|
}
|
||||||
|
|
||||||
/// changes that require no input (such as hunter firing)
|
/// changes that require no input (such as hunter firing)
|
||||||
fn automatic_changes(&self) -> Vec<NightChange> {
|
fn automatic_changes(&self) -> Vec<NightChange> {
|
||||||
let mut changes = Vec::new();
|
let mut changes = Vec::new();
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,21 @@ impl Village {
|
||||||
let mut wolves = self
|
let mut wolves = self
|
||||||
.characters
|
.characters
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| c.is_wolf())
|
.filter(|c| c.alive() && c.is_wolf())
|
||||||
.collect::<Box<[_]>>();
|
.collect::<Box<[_]>>();
|
||||||
wolves.sort_by_key(|w| w.killing_wolf_order());
|
wolves.sort_by_key(|w| w.killing_wolf_order());
|
||||||
wolves.first().copied()
|
wolves.first().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wolf_revert_prompt(&self) -> Option<ActionPrompt> {
|
||||||
|
self.killing_wolf()
|
||||||
|
.filter(|killing_wolf| RoleTitle::Werewolf != killing_wolf.role_title())
|
||||||
|
.map(|killing_wolf| ActionPrompt::RoleChange {
|
||||||
|
character_id: killing_wolf.identity(),
|
||||||
|
new_role: RoleTitle::Werewolf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wolf_pack_kill(&self) -> Option<ActionPrompt> {
|
pub fn wolf_pack_kill(&self) -> Option<ActionPrompt> {
|
||||||
let night = match self.time {
|
let night = match self.time {
|
||||||
GameTime::Day { .. } => return None,
|
GameTime::Day { .. } => return None,
|
||||||
|
|
@ -162,23 +172,6 @@ impl Village {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn killing_wolf_id(&self) -> CharacterId {
|
|
||||||
let wolves = self.living_wolf_pack_players();
|
|
||||||
if let Some(ww) = wolves
|
|
||||||
.iter()
|
|
||||||
.find(|w| matches!(w.role_title(), RoleTitle::Werewolf))
|
|
||||||
{
|
|
||||||
ww.character_id()
|
|
||||||
} else if let Some(non_ss_wolf) = wolves
|
|
||||||
.iter()
|
|
||||||
.find(|w| w.is_wolf() && !matches!(w.role_title(), RoleTitle::Shapeshifter))
|
|
||||||
{
|
|
||||||
non_ss_wolf.character_id()
|
|
||||||
} else {
|
|
||||||
wolves.into_iter().next().unwrap().character_id()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn living_players(&self) -> Box<[CharacterIdentity]> {
|
pub fn living_players(&self) -> Box<[CharacterIdentity]> {
|
||||||
self.characters
|
self.characters
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod night_order;
|
mod night_order;
|
||||||
mod previous;
|
mod previous;
|
||||||
|
mod revert;
|
||||||
mod role;
|
mod role;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -788,19 +789,9 @@ fn wolfpack_kill_all_targets_valid() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn varied_test() {
|
fn big_game_test_based_on_story_test() {
|
||||||
init_log();
|
init_log();
|
||||||
let players = (1..32u8)
|
let players = gen_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::<Box<[_]>>();
|
|
||||||
let mut players_iter = players.iter().map(|p| p.player_id);
|
let mut players_iter = players.iter().map(|p| p.player_id);
|
||||||
let (
|
let (
|
||||||
werewolf,
|
werewolf,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
use crate::{
|
||||||
|
game::{Game, GameSettings, SetupRole},
|
||||||
|
game_test::{
|
||||||
|
ActionPromptTitleExt, ActionResultExt, GameExt, SettingsExt, gen_players, init_log,
|
||||||
|
},
|
||||||
|
message::{CharacterIdentity, night::ActionPrompt},
|
||||||
|
role::{Role, RoleTitle},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sole_non_werewolf_wolves_revert() {
|
||||||
|
const REVERTING_WOLVES: &[SetupRole] = &[
|
||||||
|
SetupRole::DireWolf,
|
||||||
|
SetupRole::LoneWolf,
|
||||||
|
SetupRole::AlphaWolf,
|
||||||
|
SetupRole::Shapeshifter,
|
||||||
|
];
|
||||||
|
init_log();
|
||||||
|
for wolf_role in REVERTING_WOLVES {
|
||||||
|
let role_title = Into::<RoleTitle>::into(wolf_role.clone());
|
||||||
|
log::info!("testing initial reverting for [{role_title}]");
|
||||||
|
|
||||||
|
let players = gen_players(1..32u8);
|
||||||
|
let reverting_wolf = players[0].player_id;
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(wolf_role.clone(), reverting_wolf);
|
||||||
|
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(),
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
character_id: game.character_by_player_id(reverting_wolf).identity(),
|
||||||
|
new_role: RoleTitle::Werewolf,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
game.next(),
|
||||||
|
ActionPrompt::WolvesIntro {
|
||||||
|
wolves: Box::new([(
|
||||||
|
game.character_by_player_id(reverting_wolf).identity(),
|
||||||
|
RoleTitle::Werewolf
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
assert_eq!(
|
||||||
|
*game.character_by_player_id(reverting_wolf).role(),
|
||||||
|
Role::Werewolf
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wolves_revert_on_werewolf_death() {
|
||||||
|
const REVERTING_WOLVES: &[SetupRole] = &[
|
||||||
|
SetupRole::DireWolf,
|
||||||
|
SetupRole::LoneWolf,
|
||||||
|
SetupRole::AlphaWolf,
|
||||||
|
SetupRole::Shapeshifter,
|
||||||
|
];
|
||||||
|
init_log();
|
||||||
|
for wolf_role in REVERTING_WOLVES {
|
||||||
|
let role_title = Into::<RoleTitle>::into(wolf_role.clone());
|
||||||
|
log::info!("testing initial reverting for [{role_title}]");
|
||||||
|
|
||||||
|
let players = gen_players(1..32u8);
|
||||||
|
let wolf = players[0].player_id;
|
||||||
|
let reverting_wolf = players[1].player_id;
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, wolf);
|
||||||
|
settings.add_and_assign(wolf_role.clone(), reverting_wolf);
|
||||||
|
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(),
|
||||||
|
ActionPrompt::WolvesIntro {
|
||||||
|
wolves: Box::new([
|
||||||
|
(
|
||||||
|
game.character_by_player_id(wolf).identity(),
|
||||||
|
RoleTitle::Werewolf
|
||||||
|
),
|
||||||
|
(
|
||||||
|
game.character_by_player_id(reverting_wolf).identity(),
|
||||||
|
role_title
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if RoleTitle::DireWolf == role_title {
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
game.next().title().direwolf();
|
||||||
|
game.mark(game.living_villager().character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
} else {
|
||||||
|
game.r#continue().sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
game.mark_for_execution(game.character_by_player_id(wolf).character_id());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
game.execute(),
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
character_id: game.character_by_player_id(reverting_wolf).identity(),
|
||||||
|
new_role: RoleTitle::Werewolf,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
|
||||||
|
game.next().title().wolf_pack_kill();
|
||||||
|
game.mark(game.living_villager().character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
use core::{
|
use core::{fmt::Display, num::NonZeroU8, ops::Not};
|
||||||
fmt::Display,
|
|
||||||
num::NonZeroU8,
|
|
||||||
ops::{Deref, Not},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use werewolves_macros::{ChecksAs, Titles};
|
use werewolves_macros::{ChecksAs, Titles};
|
||||||
|
|
@ -307,15 +303,15 @@ impl Role {
|
||||||
|
|
||||||
pub const fn wakes_night_zero(&self) -> bool {
|
pub const fn wakes_night_zero(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Role::Insomniac
|
Role::PowerSeer
|
||||||
| Role::PowerSeer
|
|
||||||
| Role::Beholder
|
|
||||||
| Role::Adjudicator
|
| Role::Adjudicator
|
||||||
| Role::DireWolf { .. }
|
| Role::DireWolf { .. }
|
||||||
| Role::Arcanist
|
| Role::Arcanist
|
||||||
| Role::Seer => true,
|
| Role::Seer => true,
|
||||||
|
|
||||||
Role::LoneWolf
|
Role::Insomniac // has to at least get one good night of sleep, right?
|
||||||
|
| Role::Beholder
|
||||||
|
| Role::LoneWolf
|
||||||
| Role::Shapeshifter { .. }
|
| Role::Shapeshifter { .. }
|
||||||
| Role::Werewolf
|
| Role::Werewolf
|
||||||
| Role::AlphaWolf { .. }
|
| Role::AlphaWolf { .. }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue