Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Red herrings #770

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5e3a314
Optimized remove
Feb 22, 2025
85f58fe
Merge branch 'main' into vec_optimizations_and_new_funcs
Apersoma Feb 22, 2025
d60aa28
Added red herrings
Apersoma Feb 24, 2025
d4190e3
Merge branch 'main' into red_herrings
Apersoma Feb 24, 2025
e39c5da
Added red herrings
Apersoma Feb 24, 2025
3d3af8c
forgot to add file last commit
Apersoma Feb 24, 2025
828d780
Add files via upload
Apersoma Feb 24, 2025
e21ebf5
Merge branch 'main' into red_herrings
Apersoma Feb 24, 2025
1a8c376
Merge branch 'main' into red_herrings
Apersoma Feb 24, 2025
9369a40
Made confused players think framed players are evil. Added tests.
Apersoma Feb 25, 2025
d7861fb
Merge branch 'red_herrings' into vec_optimizations_and_new_funcs
Apersoma Feb 25, 2025
05f67c4
Merge pull request #772 from mafia-rust/vec_optimizations_and_new_funcs
Apersoma Feb 25, 2025
e9c7d2f
Forgot to remove equals signs when merging
Apersoma Feb 25, 2025
dde37b2
Updated duration.rs
Apersoma Feb 25, 2025
644bf23
Merge branch 'main' into red_herrings
Apersoma Feb 26, 2025
b37908f
Added changes to confusion & drunk aura from Clippy Cleanup II to her…
Apersoma Feb 27, 2025
1029181
Merge branch 'main' into red_herrings
Apersoma Mar 3, 2025
e9a4f3f
Merge branch 'main' into red_herrings
Apersoma Mar 3, 2025
1121e87
Merge branch 'main' into red_herrings
Apersoma Mar 3, 2025
8e13434
renamed duration added red herrings to confused
Apersoma Mar 3, 2025
b2e988c
prev cont.
Apersoma Mar 3, 2025
ed7a42a
changed roles to used the red herrings stored in confusion
Apersoma Mar 3, 2025
55aaf9b
fixed error
Apersoma Mar 3, 2025
f5ecc4a
Merge branch 'main' into red_herrings
Apersoma Mar 5, 2025
fa409f4
Fixed import issue?
Apersoma Mar 5, 2025
c97a124
Merge branch 'main' into red_herrings
Apersoma Mar 5, 2025
01e8c37
Merge branch 'main' into red_herrings
ItsSammyM Mar 6, 2025
2b43b36
removed duration. made it so red herrings stay the same when stuff ab…
Apersoma Mar 6, 2025
c846af9
move the sus checking for confused players to a separate function & m…
Apersoma Mar 6, 2025
4c3f3ef
changed gossip to use to the detective funcs
Apersoma Mar 6, 2025
b2b7f8c
fixed gossip so that its no longer detective
Apersoma Mar 6, 2025
4b74128
fixed bugs
Apersoma Mar 6, 2025
6b47605
forgot that reeducator doesn't work night 1
Apersoma Mar 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"Doesnt",
"dompurify",
"entriy",
"guiltied",
"guilties",
"Informant",
"Informants",
"inno",
Expand Down
4 changes: 2 additions & 2 deletions server/src/game/chat/chat_message_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ pub enum ChatMessageVariant {

Wardblocked,

SheriffResult {suspicious: bool},
DetectiveResult {suspicious: bool},
LookoutResult{players: Vec<PlayerIndex>},
TrackerResult{players: Vec<PlayerIndex>},
SeerResult{enemies: bool},
PhilosopherResult{enemies: bool},
SpyMafiaVisit{players: Vec<PlayerIndex>},
SpyBug{bug: SpyBug},
PsychicGood{player: PlayerReference},
Expand Down
90 changes: 70 additions & 20 deletions server/src/game/components/confused.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,82 @@
use std::collections::HashSet;
use rand::seq::IteratorRandom;

use crate::game::{player::PlayerReference, Game};


#[derive(Default)]
pub struct Confused{
players: HashSet<PlayerReference>
use crate::{game::{player::PlayerReference, Game}, vec_map::VecMap};
/// Its not an enum that is different for every role because if you swap roles but stay confused,
/// the information you get should be consistent with your previous role's info if possible.
/// the reason why it stores whether your confused instead of removing you if your not,
/// is so if you become confused after stop being confused, you have the same confusion data.
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ConfusionData{
pub red_herrings: Vec<PlayerReference>,
pub confused: bool,
}
impl Confused{
fn confused<'a>(game: &'a Game)->&'a Self{
&game.confused
impl ConfusionData {
pub fn new(game: &Game, player: PlayerReference) -> ConfusionData {
ConfusionData {
red_herrings: Self::generate_red_herrings(game,player),
confused: true,
}
}
fn confused_mut<'a>(game: &'a mut Game)->&'a mut Self{
&mut game.confused
pub fn generate_red_herrings(game: &Game, player: PlayerReference) -> Vec<PlayerReference> {
let count = game.assignments.iter()
.filter(|a|a.2.is_evil())
.count();
PlayerReference::all_players(game)
.filter(|p|*p != player)
.choose_multiple(&mut rand::rng(), count)
}
}


pub fn add_player(game: &mut Game, player: PlayerReference){
let confused = Self::confused_mut(game);
confused.players.insert(player);
#[derive(Default, Clone, Debug)]
pub struct Confused(pub VecMap<PlayerReference, ConfusionData>);

impl Game {
fn confused(&self)->&Confused{
&self.confused
}
fn confused_mut(&mut self)->&mut Confused{
&mut self.confused
}
}

impl Confused {
pub fn add_player(game: &mut Game, player: PlayerReference) {
match game.confused_mut().0.get_mut(&player) {
Some(data) => data.confused = true,
_=> {
let data = ConfusionData::new(game, player);
game.confused_mut().0.insert_unsized(player, data);
}
}
}
pub fn remove_player(game: &mut Game, player: PlayerReference){
let confused = Self::confused_mut(game);
confused.players.remove(&player);

pub fn remove_player(game: &mut Game, player: PlayerReference) -> bool {
match game.confused_mut().0.get_mut(&player) {
Some(data) => {
let old = data.confused.clone();
data.confused = false;
old
},
None=>false,
}
}

pub fn is_confused(game: &Game, player: PlayerReference)->bool{
let confused = Self::confused(game);
confused.players.contains(&player)
Self::get_confusion_data(game, player).is_some_and(|data|data.confused)
}

pub fn is_red_herring(game: &Game, confused_player: PlayerReference, target: PlayerReference) -> bool{
match game.confused().0.get(&confused_player) {
Some(data) => data.confused && data.red_herrings.contains(&target),
None => false,
}
}

/// <strong>WARNING: JUST BECAUSE A PLAYER HAS CONFUSION DATA, IT DOESN'T MEAN THEY ARE CONFUSED</strong> <br>
/// to check if player is confused, either check whether the data's confused flag is true
/// or use is_confused function
pub fn get_confusion_data(game: &Game, player: PlayerReference) -> Option<&ConfusionData>{
game.confused().0.get(&player)
}
}
34 changes: 17 additions & 17 deletions server/src/game/components/drunk_aura.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
use std::collections::HashSet;

use crate::game::{player::PlayerReference, Game};
use crate::{game::{player::PlayerReference, Game}, vec_set::VecSet};

use super::confused::Confused;

#[derive(Default, Clone)]
pub struct DrunkAura {
pub players: HashSet<PlayerReference>,
pub players: VecSet<PlayerReference>,
}

impl DrunkAura {
fn drunk_aura(game: &Game) -> &Self {
&game.drunk_aura
impl Game {
fn drunk_aura(&self) -> &DrunkAura {
&self.drunk_aura
}
fn drunk_aura_mut(game: &mut Game) -> &mut Self {
&mut game.drunk_aura
fn drunk_aura_mut(&mut self) -> &mut DrunkAura {
&mut self.drunk_aura
}
}

pub fn add_player(game: &mut Game, player: PlayerReference) {
Self::drunk_aura_mut(game).players.insert(player);
impl DrunkAura {
pub fn add_player(game: &mut Game, player: PlayerReference){
game.drunk_aura_mut().players.insert(player);
}
pub fn remove_player(game: &mut Game, player: PlayerReference) {
Self::drunk_aura_mut(game).players.remove(&player);
/// Returns true if the player was drunk
pub fn remove_player(game: &mut Game, player: PlayerReference) -> bool{
game.drunk_aura_mut().players.remove(&player).is_some()
}

pub fn has_drunk_aura(game: &Game, player: PlayerReference) -> bool {
Self::drunk_aura(game).players.contains(&player)
game.drunk_aura().players.contains(&player)
}

pub fn on_role_switch(game: &mut Game, player: PlayerReference) {
if Self::has_drunk_aura(game, player) {
Self::remove_player(game, player);
if Self::remove_player(game, player) {
Confused::remove_player(game, player);
}
}
Expand Down
4 changes: 1 addition & 3 deletions server/src/game/event/on_phase_start.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::game::{
ability_input::saved_controllers_map::SavedControllersMap, components::{
cult::Cult, detained::Detained,
mafia::Mafia, night_visits::NightVisits,
verdicts_today::VerdictsToday
cult::Cult, detained::Detained, mafia::Mafia, night_visits::NightVisits, verdicts_today::VerdictsToday
}, modifiers::Modifiers, phase::PhaseState, player::PlayerReference, Game
};

Expand Down
28 changes: 16 additions & 12 deletions server/src/game/role/detective.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ impl RoleStateImpl for Detective {
if priority != Priority::Investigative {return;}

let actor_visits = actor_ref.untagged_night_visits_cloned(game);
if let Some(visit) = actor_visits.first(){
let suspicious = if Confused::is_confused(game, actor_ref) {
false
}else{
Detective::player_is_suspicious(game, visit.target)
};
let Some(visit) = actor_visits.first() else {return};
let target_ref = visit.target;
let suspicious = if Confused::is_confused(game, actor_ref) {
Self::player_is_suspicious_confused(game, target_ref, actor_ref)
}else{
Detective::player_is_suspicious(game, target_ref)
};

let message = ChatMessageVariant::SheriffResult {
suspicious
};

actor_ref.push_night_message(game, message);
}
let message = ChatMessageVariant::DetectiveResult {
suspicious
};

actor_ref.push_night_message(game, message);
}
fn controller_parameters_map(self, game: &Game, actor_ref: PlayerReference) -> ControllerParametersMap {
crate::game::role::common_role::controller_parameters_map_player_list_night_typical(
Expand Down Expand Up @@ -68,4 +68,8 @@ impl Detective {
!player_ref.win_condition(game).friends_with_resolution_state(GameConclusion::Town)
}
}
pub fn player_is_suspicious_confused(game: &Game, player_ref: PlayerReference, actor_ref: PlayerReference) -> bool {
player_ref.night_framed(game) ||
Confused::is_red_herring(game, actor_ref, player_ref)
}
}
23 changes: 10 additions & 13 deletions server/src/game/role/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,11 @@ impl RoleStateImpl for Gossip {
let actor_visits = actor_ref.untagged_night_visits_cloned(game);
if let Some(visit) = actor_visits.first(){

let enemies = if Confused::is_confused(game, actor_ref){
false
}else{
Gossip::enemies(game, visit.target)
};

let message = ChatMessageVariant::GossipResult{ enemies };

let enemies = Self::visited_enemies(game, visit.target, actor_ref);
let message: ChatMessageVariant = ChatMessageVariant::GossipResult{ enemies };
actor_ref.push_night_message(game, message);
}

}
fn controller_parameters_map(self, game: &Game, actor_ref: PlayerReference) -> ControllerParametersMap {
crate::game::role::common_role::controller_parameters_map_player_list_night_typical(
Expand All @@ -57,16 +52,18 @@ impl RoleStateImpl for Gossip {
}

impl Gossip {
pub fn enemies(game: &Game, player_ref: PlayerReference) -> bool {

pub fn visited_enemies(game: &Game, player_ref: PlayerReference, actor_ref: PlayerReference) -> bool {
match player_ref.night_appeared_visits(game) {
Some(x) => x.clone(),
None => player_ref.all_night_visits_cloned(game),
}
.iter()
.map(|v|v.target.clone())
.any(|targets_target|
Detective::player_is_suspicious(game, targets_target)
.any(|visit: &Visit|
if Confused::is_confused(game, actor_ref) {
Detective::player_is_suspicious_confused(game, visit.target, actor_ref)
} else {
Detective::player_is_suspicious(game, visit.target)
}
)
}
}
17 changes: 15 additions & 2 deletions server/src/game/role/philosopher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::game::visit::Visit;
use crate::game::Game;
use crate::vec_set;

use super::detective::Detective;
use super::{common_role, AvailableAbilitySelection, ControllerID, ControllerParametersMap, Priority, Role, RoleStateImpl};

#[derive(Clone, Debug, Serialize, Default)]
Expand All @@ -24,19 +25,27 @@ impl RoleStateImpl for Philosopher {
fn do_night_action(self, game: &mut Game, actor_ref: PlayerReference, priority: Priority) {
if priority != Priority::Investigative {return;}

println!("phil do_night_action");

let actor_visits = actor_ref.untagged_night_visits_cloned(game);
let Some(first_visit) = actor_visits.get(0) else {return;};
let Some(second_visit) = actor_visits.get(1) else {return;};

let enemies = if Confused::is_confused(game, actor_ref) {
let enemies =
if first_visit.target == second_visit.target {
false
} else if Confused::is_confused(game, actor_ref) {
(first_visit.target.night_framed(game) || Confused::is_red_herring(game, actor_ref, first_visit.target)) ^
(second_visit.target.night_framed(game) || Confused::is_red_herring(game, actor_ref, second_visit.target))
} else {
Philosopher::players_are_enemies(game, first_visit.target, second_visit.target)
};

let message = ChatMessageVariant::SeerResult{ enemies };
let message = ChatMessageVariant::PhilosopherResult{ enemies };

actor_ref.push_night_message(game, message);

println!("enemies: {}", enemies);
}
fn controller_parameters_map(self, game: &Game, actor_ref: PlayerReference) -> ControllerParametersMap {

Expand Down Expand Up @@ -81,4 +90,8 @@ impl Philosopher{
!WinCondition::are_friends(a.win_condition(game), b.win_condition(game))
}
}
pub fn players_are_enemies_confused(game: &Game, a: PlayerReference, b: PlayerReference, actor_ref: PlayerReference) -> bool {
Detective::player_is_suspicious_confused(game, a, actor_ref) ^
Detective::player_is_suspicious_confused(game, b, actor_ref)
}
}
22 changes: 9 additions & 13 deletions server/src/game/role/tally_clerk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,14 @@ impl RoleStateImpl for TallyClerk {
.filter(|player|player.alive(game))
.filter(|player|VerdictsToday::player_guiltied_today(game, player))
{
if TallyClerk::player_is_suspicious(game, player){
if Confused::is_confused(game, actor_ref) {
if Self::player_is_suspicious_confused(game, player, actor_ref) {
evil_count += 1;
}
} else if Self::player_is_suspicious(game, player) {
evil_count += 1;
}
}

if Confused::is_confused(game, actor_ref){
let total_guilties = VerdictsToday::guilties(game).len();
//add or subtract 1 randomly from the count
if rand::random::<bool>(){
evil_count = (evil_count.saturating_add(1u8)).min(total_guilties as u8);
}else{
evil_count = (evil_count.saturating_sub(1u8)).max(0);
}
}


let message = ChatMessageVariant::TallyClerkResult{ evil_count };
actor_ref.push_night_message(game, message);
Expand All @@ -53,7 +46,6 @@ impl RoleStateImpl for TallyClerk {

impl TallyClerk {
pub fn player_is_suspicious(game: &Game, player_ref: PlayerReference) -> bool {

if player_ref.has_suspicious_aura(game){
true
}else if player_ref.has_innocent_aura(game){
Expand All @@ -62,4 +54,8 @@ impl TallyClerk {
!player_ref.win_condition(game).is_loyalist_for(GameConclusion::Town)
}
}
pub fn player_is_suspicious_confused(game: &Game, player_ref: PlayerReference, actor_ref: PlayerReference) -> bool {
actor_ref.night_framed(game) ||
Confused::is_red_herring(game, actor_ref, player_ref)
}
}
20 changes: 19 additions & 1 deletion server/src/game/role_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,25 @@ pub struct RoleAssignment {
pub win_condition: RoleOutlineOptionWinCondition
}


impl RoleAssignment {
pub fn is_evil(&self)-> bool{
match self.win_condition.clone() {
RoleOutlineOptionWinCondition::GameConclusionReached { win_if_any } =>
win_if_any.into_iter().any(|a|
a == GameConclusion::Politician || a == GameConclusion::Cult ||
a == GameConclusion::Fiends || a == GameConclusion::Mafia ||
a == GameConclusion::NaughtyList
),
RoleOutlineOptionWinCondition::RoleDefault => {
self.role == Role::Politician ||
RoleSet::Mafia.get_roles().contains(&self.role) ||
RoleSet::Minions.get_roles().contains(&self.role) ||
RoleSet::Fiends.get_roles().contains(&self.role) ||
RoleSet::Cult.get_roles().contains(&self.role)
},
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RoleOutline {
Expand Down
Loading