// 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 . use core::num::NonZeroU8; #[allow(unused)] use pretty_assertions::{assert_eq, assert_ne, assert_str_eq}; use crate::{ diedto::DiedTo, game::{ Game, GameSettings, SetupRole, night::changes::{ChangesLookup, NightChange}, }, game_test::{GameExt, SettingsExt, gen_players, init_log}, player::Protection, }; #[test] fn shapeshift() { init_log(); let players = gen_players(1..10); let mut player_ids = players.iter().map(|p| p.player_id); let target = player_ids.next().unwrap(); let shapeshifter = player_ids.next().unwrap(); let wolf = player_ids.next().unwrap(); let mut settings = GameSettings::empty(); settings.add_and_assign(SetupRole::Villager, target); settings.add_and_assign(SetupRole::Shapeshifter, shapeshifter); settings.add_and_assign(SetupRole::Werewolf, wolf); settings.fill_remaining_slots_with_villagers(9); let game = Game::new(&players, settings).unwrap(); let all_changes = [ NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Shapeshift { source: game.character_by_player_id(shapeshifter).character_id(), into: game.character_by_player_id(target).character_id(), }, ]; let changes = ChangesLookup::new(&all_changes); assert_eq!( changes.died_to( game.character_by_player_id(target).character_id(), 1, game.village() ), Ok(None) ); assert_eq!( changes.died_to( game.character_by_player_id(shapeshifter).character_id(), 1, game.village() ), Ok(Some(DiedTo::Shapeshift { into: game.character_by_player_id(target).character_id(), night: NonZeroU8::new(1).unwrap() })) ); } #[test] fn guardian_protect() { init_log(); let players = gen_players(1..10); let mut player_ids = players.iter().map(|p| p.player_id); let target = player_ids.next().unwrap(); let guardian = player_ids.next().unwrap(); let wolf = player_ids.next().unwrap(); let mut settings = GameSettings::empty(); settings.add_and_assign(SetupRole::Villager, target); settings.add_and_assign(SetupRole::Guardian, guardian); settings.add_and_assign(SetupRole::Werewolf, wolf); settings.fill_remaining_slots_with_villagers(9); let game = Game::new(&players, settings).unwrap(); let all_changes = [ NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Protection { target: game.character_by_player_id(target).character_id(), protection: Protection::Guardian { source: game.character_by_player_id(guardian).character_id(), guarding: false, }, }, ]; let changes = ChangesLookup::new(&all_changes); assert_eq!( changes.died_to( game.character_by_player_id(target).character_id(), 1, game.village() ), Ok(None) ); assert_eq!( changes.died_to( game.character_by_player_id(guardian).character_id(), 1, game.village() ), Ok(None) ); } #[test] fn guardian_guard() { init_log(); let players = gen_players(1..10); let mut player_ids = players.iter().map(|p| p.player_id); let target = player_ids.next().unwrap(); let guardian = player_ids.next().unwrap(); let wolf = player_ids.next().unwrap(); let mut settings = GameSettings::empty(); settings.add_and_assign(SetupRole::Villager, target); settings.add_and_assign(SetupRole::Guardian, guardian); settings.add_and_assign(SetupRole::Werewolf, wolf); settings.fill_remaining_slots_with_villagers(9); let game = Game::new(&players, settings).unwrap(); let all_changes = [ NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Protection { target: game.character_by_player_id(target).character_id(), protection: Protection::Guardian { source: game.character_by_player_id(guardian).character_id(), guarding: true, }, }, ]; let changes = ChangesLookup::new(&all_changes); assert_eq!( changes.died_to( game.character_by_player_id(target).character_id(), 1, game.village() ), Ok(None) ); assert_eq!( changes.died_to( game.character_by_player_id(guardian).character_id(), 1, game.village() ), Ok(Some(DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap() })) ); assert_eq!( changes.died_to( game.character_by_player_id(wolf).character_id(), 1, game.village() ), Ok(Some(DiedTo::GuardianProtecting { source: game.character_by_player_id(guardian).character_id(), protecting: game.character_by_player_id(target).character_id(), protecting_from: game.character_by_player_id(wolf).character_id(), protecting_from_cause: Box::new(DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }), night: NonZeroU8::new(1).unwrap(), })) ); } #[test] fn guardian_protect_from_multiple_attackers() { init_log(); let players = gen_players(1..10); let mut player_ids = players.iter().map(|p| p.player_id); let target = player_ids.next().unwrap(); let guardian = player_ids.next().unwrap(); let wolf = player_ids.next().unwrap(); let militia = player_ids.next().unwrap(); let mut settings = GameSettings::empty(); settings.add_and_assign(SetupRole::Villager, target); settings.add_and_assign(SetupRole::Villager, militia); settings.add_and_assign(SetupRole::Guardian, guardian); settings.add_and_assign(SetupRole::Werewolf, wolf); settings.fill_remaining_slots_with_villagers(9); let game = Game::new(&players, settings).unwrap(); let all_changes = [ NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Militia { killer: game.character_by_player_id(militia).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Protection { target: game.character_by_player_id(target).character_id(), protection: Protection::Guardian { source: game.character_by_player_id(guardian).character_id(), guarding: false, }, }, ]; let changes = ChangesLookup::new(&all_changes); assert_eq!( changes.died_to( game.character_by_player_id(target).character_id(), 1, game.village() ), Ok(None) ); assert_eq!( changes.died_to( game.character_by_player_id(guardian).character_id(), 1, game.village() ), Ok(None) ); } #[test] fn guardian_protect_someone_else_unprotected_dies() { init_log(); let players = gen_players(1..10); let mut player_ids = players.iter().map(|p| p.player_id); let target = player_ids.next().unwrap(); let guardian = player_ids.next().unwrap(); let wolf = player_ids.next().unwrap(); let mut settings = GameSettings::empty(); settings.add_and_assign(SetupRole::Seer, target); settings.add_and_assign(SetupRole::Guardian, guardian); settings.add_and_assign(SetupRole::Werewolf, wolf); settings.fill_remaining_slots_with_villagers(9); let game = Game::new(&players, settings).unwrap(); let all_changes = [ NightChange::Kill { target: game.character_by_player_id(target).character_id(), died_to: DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap(), }, }, NightChange::Protection { target: game.living_villager().character_id(), protection: Protection::Guardian { source: game.character_by_player_id(guardian).character_id(), guarding: false, }, }, ]; let changes = ChangesLookup::new(&all_changes); assert_eq!( changes.died_to( game.character_by_player_id(target).character_id(), 1, game.village() ), Ok(Some(DiedTo::Wolfpack { killing_wolf: game.character_by_player_id(wolf).character_id(), night: NonZeroU8::new(1).unwrap() })) ); assert_eq!( changes.died_to( game.character_by_player_id(guardian).character_id(), 1, game.village() ), Ok(None) ); }