use super::*;
use crate::app::match_setup::TermsDials;
use crate::app::net::order::{Arranged, seat_order};
use crate::app::teams::TeamMode;
use crate::bots::listener::BotId;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Seated {
pub order: [u8; MAX_PLAYERS],
pub ai: u8,
pub teams: TeamMode,
}
impl Default for Seated {
fn default() -> Self {
Seated {
order: crate::transport::IN_SEAT_ORDER,
ai: 0,
teams: TeamMode::Solo,
}
}
}
impl Seated {
pub fn from_terms(terms: &MatchTerms, places: u8) -> Seated {
let seats = places.saturating_add(terms.bots).min(MAX_PLAYERS as u8);
Seated {
order: terms.order,
ai: seats - places.min(seats),
teams: terms.teams().played_by(seats),
}
}
pub fn seat_of(&self, place: usize) -> u8 {
self.order.get(place).copied().unwrap_or(place as u8)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SeatShown {
pub seat: u8,
pub name: String,
pub kind: crate::sim::SeatKind,
}
pub fn seats_shown(state: &LobbyState, tr: &crate::app::i18n::Tr) -> Vec<SeatShown> {
let seated = state.seated;
let places = state.table.len().min(MAX_PLAYERS);
let seats = (places as u8 + seated.ai).min(MAX_PLAYERS as u8);
let at = |seat: u8| -> SeatShown {
let place = (0..usize::from(seats)).find(|&place| seated.seat_of(place) == seat);
match place.filter(|&place| place < places) {
Some(place) => SeatShown {
seat,
name: state.table[place].clone(),
kind: state
.table_kinds
.get(place)
.copied()
.unwrap_or(crate::sim::SeatKind::Human),
},
None => SeatShown {
seat,
name: format!("{}{}", crate::app::seat_label(tr, seat), tr.tag_ai),
kind: crate::sim::SeatKind::Ai,
},
}
};
match seated.teams {
TeamMode::Solo => (0..places).map(|place| at(seated.seat_of(place))).collect(),
TeamMode::Pairs | TeamMode::Trios => crate::app::lineup::seat_order(seated.teams, seats)
.into_iter()
.map(|(seat, _)| at(seat))
.collect(),
}
}
fn places(hosted: &Hosted, door: &[(BotId, String)]) -> (Vec<usize>, Vec<BotId>) {
let peers: Vec<usize> = hosted
.peers
.at_the_table()
.map(|(peer, _)| peer)
.take(MAX_PLAYERS - 1)
.collect();
let room = MAX_PLAYERS - 1 - peers.len();
let bots = door.iter().take(room).map(|(id, _)| *id).collect();
(peers, bots)
}
pub(super) fn host_seated(
state: &LobbyState,
config: &MatchConfig,
teams: TeamMode,
door: &[(BotId, String)],
) -> Seated {
let (Some(hosted), Some(dealt)) = (state.hosted(), hosts_dealt(state, config)) else {
return Seated::default();
};
let humans = dealt.seats - dealt.bots;
let teams = teams.played_by(dealt.seats);
let held = crate::app::net::order::picks_hold(teams);
let order = match hosted.arranged.as_ref().filter(|_| held) {
None => crate::transport::IN_SEAT_ORDER,
Some(arranged) => {
let (peers, bots) = places(hosted, door);
let wants: Vec<Option<u8>> = std::iter::once(None)
.chain(
peers
.iter()
.map(|&peer| hosted.peers.get(peer).and_then(|row| row.seat_wish)),
)
.chain(bots.iter().map(|id| {
arranged
.bots
.iter()
.find(|(bot, _)| bot == id)
.map(|&(_, seat)| seat)
}))
.collect();
seat_order(&wants, &arranged.ai, humans, dealt.seats)
}
};
Seated {
order,
ai: dealt.bots,
teams,
}
}
pub fn partners_open(state: &LobbyState) -> bool {
state.hosting() && state.seated.teams != TeamMode::Solo
}
pub(super) fn pick_partners(
state: &mut LobbyState,
door: &[(BotId, String)],
seat: u8,
) -> Option<u8> {
let seated = state.seated;
let places_here = state.table.len().min(MAX_PLAYERS) as u8;
let seats = places_here.saturating_add(seated.ai).min(MAX_PLAYERS as u8);
if seat >= seats || seated.teams == TeamMode::Solo {
return None;
}
let mut who = [0u8; MAX_PLAYERS];
for place in 0..seats {
who[usize::from(seated.seat_of(usize::from(place)))] = place;
}
let pick = crate::app::partners::next_pick(seated.teams, seats, seat, (&[0], &[]), &who)?;
let mut order = seated.order;
for slot in order.iter_mut().take(usize::from(seats)) {
*slot = pick.moved(*slot);
}
let hosted = state.hosted_mut()?;
let (peers, bots) = places(hosted, door);
for (at, &peer) in peers.iter().enumerate() {
hosted.peers.row(peer).seat_wish = Some(order[at + 1]);
}
let first_bot = 1 + peers.len();
hosted.arranged = Some(Arranged {
bots: bots
.iter()
.enumerate()
.map(|(at, id)| (*id, order[first_bot + at]))
.collect(),
ai: (places_here..seats)
.map(|place| order[usize::from(place)])
.collect(),
});
state.seated.order = order;
Some(pick.moved(seat))
}
pub fn partner_rows(state: &LobbyState, tr: &crate::app::i18n::Tr) -> Vec<(u8, String)> {
seats_shown(state, tr)
.into_iter()
.map(|shown| (shown.seat + 1, shown.name))
.collect()
}