werewolves/werewolves-proto/src/game/settings.rs

263 lines
7.7 KiB
Rust
Raw Normal View History

2025-10-04 17:50:29 +01:00
mod settings_role;
use std::collections::HashMap;
2025-10-04 17:50:29 +01:00
use rand::seq::SliceRandom;
pub use settings_role::*;
use super::Result;
use serde::{Deserialize, Serialize};
use crate::{character::Character, error::GameError, message::Identification, role::RoleTitle};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GameSettings {
2025-10-04 17:50:29 +01:00
roles: Vec<SetupSlot>,
next_order: u32,
}
impl Default for GameSettings {
fn default() -> Self {
Self {
2025-10-04 17:50:29 +01:00
roles: vec![SetupSlot::new(RoleTitle::Werewolf, 0)],
next_order: 1,
}
}
}
impl GameSettings {
pub fn empty() -> Self {
Self {
roles: vec![],
next_order: 1,
}
}
pub fn fill_remaining_slots_with_villagers(&mut self, player_count: usize) {
if self.roles.len() >= player_count {
return;
}
for _ in 0..(player_count - self.roles.len()) {
self.new_slot(RoleTitle::Villager);
}
}
pub fn wolves_count(&self) -> usize {
self.roles
.iter()
2025-10-04 17:50:29 +01:00
.filter(|s| s.role.category().is_wolves())
.count()
}
pub fn slots(&self) -> &[SetupSlot] {
&self.roles
}
pub fn get_slot_by_id(&self, slot_id: SlotId) -> Option<&SetupSlot> {
self.roles.iter().find(|s| s.slot_id == slot_id)
}
pub fn village_roles_count(&self) -> usize {
2025-10-04 17:50:29 +01:00
self.roles.len() - self.wolves_count()
}
pub fn remove_assignments_not_in_list(&mut self, list: &[Identification]) {
self.roles.iter_mut().for_each(|r| {
if let Some(pid) = &r.assign_to
&& !list.iter().any(|i| i.player_id == *pid)
{
r.assign_to.take();
}
})
}
pub fn remove_duplicate_assignments(&mut self) {
let assignments = self
.roles
.iter()
2025-10-04 17:50:29 +01:00
.filter_map(|r| r.assign_to.as_ref())
.cloned()
.collect::<Box<[_]>>();
let mut assignment_counter = HashMap::new();
for assign in assignments {
if let Some(counter) = assignment_counter.get_mut(&assign) {
*counter += 1;
} else {
assignment_counter.insert(assign, 1usize);
}
}
let to_remove = assignment_counter
.into_iter()
.filter_map(|(pid, cnt)| (cnt > 1).then_some(pid))
.collect::<Box<[_]>>();
for role in self.roles.iter_mut() {
if let Some(pid) = role.assign_to.as_ref()
&& to_remove.contains(pid)
{
role.assign_to.take();
}
}
}
2025-10-04 17:50:29 +01:00
pub fn assign(&self, players: &[Identification]) -> Result<Box<[Character]>> {
self.check_with_player_list(players)?;
let roles_in_game = self
.roles
.iter()
.map(|r| r.role.clone().into())
.collect::<Box<[RoleTitle]>>();
let with_assigned_roles = self
.roles
.iter()
.filter_map(|s| {
s.assign_to.as_ref().map(|assign_to| {
players
.iter()
.find(|pid| pid.player_id == *assign_to)
2025-10-05 10:54:47 +01:00
.ok_or(GameError::AssignedPlayerMissing(*assign_to))
2025-10-04 17:50:29 +01:00
.map(|id| (id, s))
})
})
.collect::<Result<Box<[_]>>>()?;
let mut random_assign_players = players
.iter()
.filter(|p| {
!with_assigned_roles
.iter()
.any(|(r, _)| r.player_id == p.player_id)
})
.collect::<Box<[_]>>();
random_assign_players.shuffle(&mut rand::rng());
with_assigned_roles
.into_iter()
.chain(
random_assign_players
.into_iter()
.zip(self.roles.iter().filter(|s| s.assign_to.is_none())),
)
.map(|(id, slot)| slot.clone().into_character(id.clone(), &roles_in_game))
.collect::<Result<Box<[_]>>>()
}
2025-10-04 17:50:29 +01:00
pub fn check_with_player_list(&self, players: &[Identification]) -> Result<()> {
self.check()?;
let (p_len, r_len) = (players.len(), self.roles.len());
if p_len > r_len {
return Err(GameError::TooManyPlayers {
got: p_len.min(0xFF) as u8,
need: self.roles.len().min(0xFF) as u8,
});
} else if p_len < r_len {
return Err(GameError::TooManyRoles {
players: p_len.min(0xFF) as u8,
roles: r_len.min(0xFF) as u8,
});
}
2025-10-04 17:50:29 +01:00
for role in self.roles.iter() {
if let Some(assigned) = role.assign_to.as_ref()
&& !players.iter().any(|p| p.player_id == *assigned)
{
2025-10-05 10:54:47 +01:00
return Err(GameError::AssignedPlayerMissing(*assigned));
2025-10-04 17:50:29 +01:00
}
}
let assignments = self
.roles
.iter()
.filter_map(|r| r.assign_to.as_ref())
.cloned()
.collect::<Box<[_]>>();
let mut assignment_counter = HashMap::new();
for assign in assignments {
if let Some(counter) = assignment_counter.get_mut(&assign) {
*counter += 1;
} else {
assignment_counter.insert(assign, 1usize);
}
}
if let Some((assign, cnt)) = assignment_counter.into_iter().find(|(_, cnt)| *cnt > 1) {
let ident = players
.iter()
.find(|i| i.player_id == assign)
.ok_or(GameError::AssignedPlayerMissing(assign))?;
return Err(GameError::AssignedMultipleTimes(ident.public.clone(), cnt));
}
Ok(())
}
pub fn check(&self) -> Result<()> {
if self.wolves_count() == 0 {
return Err(GameError::NoWolves);
}
2025-10-04 17:50:29 +01:00
let mentor_count = self
.roles
.iter()
2025-10-04 17:50:29 +01:00
.filter(|r| Into::<RoleTitle>::into(r.role.clone()).is_mentor())
.count();
self.roles.iter().try_for_each(|s| match &s.role {
2025-10-06 01:03:16 +01:00
SetupRole::Apprentice { to: None } => (mentor_count > 0)
2025-10-04 17:50:29 +01:00
.then_some(())
.ok_or(GameError::NoApprenticeMentor),
SetupRole::Apprentice { to: Some(role) } => role
2025-10-04 17:50:29 +01:00
.is_mentor()
.then_some(())
.ok_or(GameError::NotAMentor(*role)),
_ => Ok(()),
})?;
Ok(())
}
pub fn min_players_needed(&self) -> usize {
let (wolves, villagers) = (self.wolves_count(), self.village_roles_count());
if wolves > villagers {
wolves + 1 + wolves
} else if wolves < villagers {
wolves + villagers
} else {
wolves + villagers + 1
}
}
2025-10-04 17:50:29 +01:00
pub fn new_slot(&mut self, role: RoleTitle) -> SlotId {
let slot = SetupSlot::new(role, self.next_order);
self.next_order += 1;
let slot_id = slot.slot_id;
self.roles.push(slot);
self.sort_roles();
slot_id
}
pub fn update_slot(&mut self, slot: SetupSlot) {
if let Some(old_slot) = self.roles.iter_mut().find(|r| r.slot_id == slot.slot_id) {
*old_slot = slot;
}
}
2025-10-04 17:50:29 +01:00
pub fn remove_slot(&mut self, slot_id: SlotId) {
if let Some(idx) = self
.roles
.iter()
.enumerate()
.find_map(|(idx, slot)| (slot.slot_id == slot_id).then_some(idx))
{
2025-10-04 17:50:29 +01:00
self.roles.swap_remove(idx);
self.sort_roles();
}
}
2025-10-04 17:50:29 +01:00
fn sort_roles(&mut self) {
self.roles
.sort_by(|l, r| l.created_order.cmp(&r.created_order).reverse());
}
}