use crate::app::cycle::Cycle;
use crate::app::net;
use crate::app::settings::GameSettings;
use crate::sim::MAX_PLAYERS;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum TeamMode {
#[default]
Solo,
Pairs,
Trios,
}
impl TeamMode {
pub const ALL: [TeamMode; 3] = [TeamMode::Solo, TeamMode::Pairs, TeamMode::Trios];
pub fn fits(self, seats: u8) -> bool {
match self {
TeamMode::Solo => true,
TeamMode::Pairs => seats >= 4 && seats.is_multiple_of(2),
TeamMode::Trios => seats == 6,
}
}
pub fn team_of(self, seat: u8) -> u8 {
match self {
TeamMode::Solo => seat,
TeamMode::Pairs => seat / 2,
TeamMode::Trios => seat % 2,
}
}
pub fn teams(self, seats: u8) -> u8 {
match self {
TeamMode::Solo => seats,
TeamMode::Pairs => seats / 2,
TeamMode::Trios => 2,
}
}
pub fn seats_of(self, team: u8, seats: u8) -> Vec<u8> {
(0..seats).filter(|&s| self.team_of(s) == team).collect()
}
pub fn index(self) -> usize {
Cycle::index(self)
}
pub fn from_index(index: usize) -> TeamMode {
<TeamMode as Cycle>::from_index(index)
}
pub fn key(self) -> &'static str {
match self {
TeamMode::Solo => "ffa",
TeamMode::Pairs => "pairs",
TeamMode::Trios => "trios",
}
}
pub fn from_key(key: &str) -> TeamMode {
match key {
"pairs" | "2v2" => TeamMode::Pairs,
"trios" => TeamMode::Trios,
_ => TeamMode::Solo,
}
}
}
impl Cycle for TeamMode {
const VARIANTS: &'static [Self] = &TeamMode::ALL;
}
pub(crate) fn in_play(settings: &GameSettings, online: &net::Online, seats: u8) -> TeamMode {
let wanted = match &online.0 {
Some(session) => TeamMode::from_index(usize::from(session.terms.teams)),
None => settings.team_mode,
};
if wanted.fits(seats) {
wanted
} else {
TeamMode::Solo
}
}
pub fn team_scores(scores: &[u32; MAX_PLAYERS], seats: u8, mode: TeamMode) -> Vec<u32> {
(0..mode.teams(seats))
.map(|team| {
mode.seats_of(team, seats)
.iter()
.map(|&seat| scores[usize::from(seat)])
.sum()
})
.collect()
}
pub fn label(
settings: &GameSettings,
names: &crate::app::SeatNames,
mode: TeamMode,
team: u8,
seats: u8,
) -> String {
mode.seats_of(team, seats)
.iter()
.map(|&seat| names.label(settings.tr(), seat))
.collect::<Vec<_>>()
.join("+")
}
pub fn face_of(mode: TeamMode, team: u8, seats: u8) -> u8 {
mode.seats_of(team, seats).first().copied().unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sim::castle_spots;
#[test]
fn modes_only_offer_themselves_where_they_fit() {
assert!(TeamMode::Solo.fits(2) && TeamMode::Solo.fits(5));
assert!(!TeamMode::Pairs.fits(2), "one pair is not a match");
assert!(!TeamMode::Pairs.fits(5), "odd seats cannot pair up");
assert!(TeamMode::Pairs.fits(4) && TeamMode::Pairs.fits(6));
assert!(!TeamMode::Trios.fits(4) && TeamMode::Trios.fits(6));
assert_eq!(TeamMode::Pairs.teams(6), 3, "2v2v2");
assert_eq!(TeamMode::Trios.teams(6), 2, "3v3");
}
#[test]
fn every_split_is_symmetric_on_the_board() {
let pair_of = |seat: u8| seat / 2;
for seats in [4u8, 6] {
for team in 0..TeamMode::Pairs.teams(seats) {
let members = TeamMode::Pairs.seats_of(team, seats);
assert_eq!(members.len(), 2);
assert_eq!(
pair_of(members[0]),
pair_of(members[1]),
"a pair team must hold both ends of one axis"
);
}
}
let a = TeamMode::Trios.seats_of(0, 6);
let b = TeamMode::Trios.seats_of(1, 6);
assert_eq!(a, vec![0, 2, 4]);
assert_eq!(b, vec![1, 3, 5]);
for (x, y) in a.iter().zip(&b) {
assert_eq!(pair_of(*x), pair_of(*y), "{x} and {y} are not opposites");
}
let (w, h) = (20u8, 13);
let spots = castle_spots(w, h);
for pair in 0..3usize {
let (x0, y0) = spots[pair * 2];
let (x1, y1) = spots[pair * 2 + 1];
assert_eq!((w - 1 - x0, h - 1 - y0), (x1, y1), "pair {pair}");
}
}
#[test]
fn team_totals_add_up() {
let scores = [1, 2, 3, 4, 5, 6];
assert_eq!(team_scores(&scores, 6, TeamMode::Pairs), vec![3, 7, 11]);
assert_eq!(team_scores(&scores, 6, TeamMode::Trios), vec![9, 12]);
assert_eq!(team_scores(&scores, 4, TeamMode::Pairs), vec![3, 7]);
assert_eq!(
team_scores(&scores, 4, TeamMode::Solo),
vec![1, 2, 3, 4],
"solo teams are seats"
);
}
#[test]
fn keys_round_trip_and_the_old_name_still_loads() {
for mode in TeamMode::ALL {
assert_eq!(TeamMode::from_key(mode.key()), mode);
assert_eq!(TeamMode::from_index(mode.index()), mode);
}
assert_eq!(TeamMode::from_key("2v2"), TeamMode::Pairs, "old setting");
assert_eq!(TeamMode::from_key("nonsense"), TeamMode::Solo);
}
}