This is getting so stupidly complicated

This commit is contained in:
Daan Vanoverloop 2022-08-30 18:59:42 +02:00
parent 91ec035439
commit bef66328f7
Signed by: Danacus
GPG Key ID: F2272B50E129FC5C
1 changed files with 170 additions and 33 deletions

View File

@ -220,7 +220,11 @@ mod test {
mod team_game { mod team_game {
use super::{ use super::{
free_for_all_game::{FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate}, free_for_all_game::{
FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate,
FreeForAllGameUpdateParticipants, FreeForAllGameUpdateRanking,
FreeForAllGameUpdateRewards,
},
*, *,
}; };
@ -252,9 +256,32 @@ mod team_game {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub struct TeamGameUpdate { pub enum TeamGameUpdateInner {
#[serde(flatten)] /// Add or replace a team with the given name and array of members
inner: FreeForAllGameUpdate, SetTeam { team: String, members: Vec<String> },
/// Remove team with given name
RemoveTeam(String),
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
#[serde(untagged)]
pub enum TeamGameFfaInheritedUpdate {
/// Change the ranking and scores
Ranking(FreeForAllGameUpdateRanking),
/// Update rewards
Rewards(FreeForAllGameUpdateRewards),
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
#[serde(untagged)]
pub enum TeamGameUpdate {
/// Team specific updates
Team(TeamGameUpdateInner),
/// Inherited from FreeForAllGame
Ffa(TeamGameFfaInheritedUpdate),
} }
impl EventTrait for TeamGame { impl EventTrait for TeamGame {
@ -262,15 +289,8 @@ mod team_game {
type Update = TeamGameUpdate; type Update = TeamGameUpdate;
fn from_spec(spec: TeamGameSpec) -> Self { fn from_spec(spec: TeamGameSpec) -> Self {
let players: Vec<String> = spec
.teams
.values()
.map(|v| v.iter().cloned())
.flatten()
.collect();
let ffa_game_spec = FreeForAllGameSpec { let ffa_game_spec = FreeForAllGameSpec {
players, participants: spec.teams.keys().cloned().collect(),
win_rewards: spec.win_rewards, win_rewards: spec.win_rewards,
lose_rewards: spec.lose_rewards, lose_rewards: spec.lose_rewards,
}; };
@ -282,7 +302,34 @@ mod team_game {
} }
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> { fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
self.ffa_game.apply_update(update.inner) match update {
TeamGameUpdate::Ffa(update) => match update {
TeamGameFfaInheritedUpdate::Ranking(u) => {
self.ffa_game.apply_update(FreeForAllGameUpdate::Ranking(u))
}
TeamGameFfaInheritedUpdate::Rewards(u) => {
self.ffa_game.apply_update(FreeForAllGameUpdate::Rewards(u))
}
},
TeamGameUpdate::Team(update) => match update {
TeamGameUpdateInner::SetTeam { team, members } => {
self.ffa_game
.apply_update(FreeForAllGameUpdate::Participants(
FreeForAllGameUpdateParticipants::AddParticipant(team.clone()),
))?;
self.teams.insert(team, members);
Ok(())
}
TeamGameUpdateInner::RemoveTeam(team) => {
self.ffa_game
.apply_update(FreeForAllGameUpdate::Participants(
FreeForAllGameUpdateParticipants::RemoveParticipant(team.clone()),
))?;
self.teams.remove(&team);
Ok(())
}
},
}
} }
fn outcome(&self) -> EventOutcome { fn outcome(&self) -> EventOutcome {
@ -305,22 +352,33 @@ mod team_game {
} }
mod free_for_all_game { mod free_for_all_game {
use std::collections::HashSet;
use super::*; use super::*;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub enum FreeForAllGameRanking { pub enum FreeForAllGameRanking {
/// Ranking of players by user id (first element is first place, second element is second /// Ranking of participants by user id or team name (first element is first place, second element is second
/// place, etc.) /// place, etc.)
Ranking(Vec<String>), Ranking(Vec<String>),
/// Score based ranking of players by user id /// Score based ranking of participants/teams
Scores(HashMap<String, i64>), Scores(HashMap<String, i64>),
} }
impl FreeForAllGameRanking {
pub fn is_valid(&self, participants: &HashSet<String>) -> bool {
match self {
Self::Ranking(v) => v.iter().all(|p| participants.contains(p)),
Self::Scores(m) => m.keys().all(|p| participants.contains(p)),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub struct FreeForAllGame { pub struct FreeForAllGame {
/// Ranking of players by user id (first element is first place, second element is second /// Ranking of participants by user id or team name (first element is first place, second element is second
/// place, etc.) /// place, etc.)
ranking: Option<FreeForAllGameRanking>, ranking: Option<FreeForAllGameRanking>,
@ -341,7 +399,7 @@ mod free_for_all_game {
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub struct FreeForAllGameSpec { pub struct FreeForAllGameSpec {
/// Array of user ids that participate in the game /// Array of user ids that participate in the game
pub(crate) players: Vec<String>, pub(crate) participants: HashSet<String>,
/// Rewards for winning the game (first element for first place, second element for second /// Rewards for winning the game (first element for first place, second element for second
/// place, etc.) /// place, etc.)
@ -355,14 +413,49 @@ mod free_for_all_game {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub enum FreeForAllGameUpdate { pub enum FreeForAllGameUpdateRanking {
/// Replace the current ranking with the given ranking /// Replace the current ranking with the given ranking
Ranking(FreeForAllGameRanking), SetRanking(FreeForAllGameRanking),
/// If the current ranking is of type `Scores`, apply the given score deltas /// If the current ranking is of type `Scores`, apply the given score deltas
ScoreDelta(HashMap<String, i64>), ScoreDelta(HashMap<String, i64>),
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub enum FreeForAllGameUpdateRewards {
/// Set rewards for winning the game
SetWinRewards(Vec<i64>),
/// Set rewards for losing the game
SetLoseRewards(Vec<i64>),
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub enum FreeForAllGameUpdateParticipants {
/// Set list of participants participating in the game
SetParticipants(HashSet<String>),
/// Add participant by id
AddParticipant(String),
/// Remove participant by id
RemoveParticipant(String),
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
#[serde(untagged)]
pub enum FreeForAllGameUpdate {
/// Change the ranking and scores
Ranking(FreeForAllGameUpdateRanking),
/// Update rewards
Rewards(FreeForAllGameUpdateRewards),
/// Update participants
Participants(FreeForAllGameUpdateParticipants),
}
impl EventTrait for FreeForAllGame { impl EventTrait for FreeForAllGame {
type Spec = FreeForAllGameSpec; type Spec = FreeForAllGameSpec;
type Update = FreeForAllGameUpdate; type Update = FreeForAllGameUpdate;
@ -376,17 +469,61 @@ mod free_for_all_game {
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> { fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
match update { match update {
FreeForAllGameUpdate::Ranking(r) => self.ranking = Some(r), FreeForAllGameUpdate::Ranking(update) => match update {
FreeForAllGameUpdate::ScoreDelta(d) => match &mut self.ranking { FreeForAllGameUpdateRanking::SetRanking(r) => {
Some(FreeForAllGameRanking::Ranking(_)) | None => { if !r.is_valid(&self.spec.participants) {
return Err(PartyError::Unknown("cannot apply score delta".into())) return Err(PartyError::Unknown("invalid ranking, all participants mentioned in ranking must be participating".into()));
}
self.ranking = Some(r)
} }
Some(FreeForAllGameRanking::Scores(s)) => { FreeForAllGameUpdateRanking::ScoreDelta(d) => match &mut self.ranking {
for (player, delta) in d.iter() { Some(FreeForAllGameRanking::Ranking(_)) | None => {
if let Some(value) = s.get(player) { return Err(PartyError::Unknown("cannot apply score delta".into()))
s.insert(player.clone(), value + delta); }
Some(FreeForAllGameRanking::Scores(s)) => {
for (participant, delta) in d.iter() {
if let Some(value) = s.get(participant) {
s.insert(participant.clone(), value + delta);
}
} }
} }
},
},
FreeForAllGameUpdate::Participants(update) => match update {
FreeForAllGameUpdateParticipants::AddParticipant(id) => {
self.spec.participants.insert(id);
}
FreeForAllGameUpdateParticipants::RemoveParticipant(id) => {
self.spec.participants.remove(&id);
if !self
.ranking
.as_ref()
.map(|r| r.is_valid(&self.spec.participants))
.unwrap_or(true)
{
self.spec.participants.insert(id);
return Err(PartyError::Unknown("cannot remove participant, all participants mentioned in ranking must be participating".into()));
}
}
FreeForAllGameUpdateParticipants::SetParticipants(participants) => {
if !self
.ranking
.as_ref()
.map(|r| r.is_valid(&participants))
.unwrap_or(true)
{
return Err(PartyError::Unknown("invalid list of participants, all participants mentioned in ranking must be participating".into()));
}
self.spec.participants = participants;
}
},
FreeForAllGameUpdate::Rewards(update) => match update {
FreeForAllGameUpdateRewards::SetWinRewards(rewards) => {
self.spec.win_rewards = rewards;
}
FreeForAllGameUpdateRewards::SetLoseRewards(rewards) => {
self.spec.lose_rewards = rewards;
} }
}, },
} }
@ -406,14 +543,14 @@ mod free_for_all_game {
let mut points = HashMap::new(); let mut points = HashMap::new();
for (player, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) { for (participant, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
let score = points.get(player).unwrap_or(&0); let score = points.get(participant).unwrap_or(&0);
points.insert(player.clone(), score + reward); points.insert(participant.clone(), score + reward);
} }
for (player, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) { for (participant, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
let score = points.get(player).unwrap_or(&0); let score = points.get(participant).unwrap_or(&0);
points.insert(player.clone(), score + reward); points.insert(participant.clone(), score + reward);
} }
EventOutcome { points } EventOutcome { points }