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 {
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)]
#[serde(crate = "rocket::serde")]
pub struct TeamGameUpdate {
#[serde(flatten)]
inner: FreeForAllGameUpdate,
pub enum TeamGameUpdateInner {
/// Add or replace a team with the given name and array of members
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 {
@ -262,15 +289,8 @@ mod team_game {
type Update = TeamGameUpdate;
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 {
players,
participants: spec.teams.keys().cloned().collect(),
win_rewards: spec.win_rewards,
lose_rewards: spec.lose_rewards,
};
@ -282,7 +302,34 @@ mod team_game {
}
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 {
@ -305,22 +352,33 @@ mod team_game {
}
mod free_for_all_game {
use std::collections::HashSet;
use super::*;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
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.)
Ranking(Vec<String>),
/// Score based ranking of players by user id
/// Score based ranking of participants/teams
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)]
#[serde(crate = "rocket::serde")]
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.)
ranking: Option<FreeForAllGameRanking>,
@ -341,7 +399,7 @@ mod free_for_all_game {
#[serde(crate = "rocket::serde")]
pub struct FreeForAllGameSpec {
/// 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
/// place, etc.)
@ -355,14 +413,49 @@ mod free_for_all_game {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub enum FreeForAllGameUpdate {
pub enum FreeForAllGameUpdateRanking {
/// 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
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 {
type Spec = FreeForAllGameSpec;
type Update = FreeForAllGameUpdate;
@ -376,17 +469,61 @@ mod free_for_all_game {
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
match update {
FreeForAllGameUpdate::Ranking(r) => self.ranking = Some(r),
FreeForAllGameUpdate::ScoreDelta(d) => match &mut self.ranking {
Some(FreeForAllGameRanking::Ranking(_)) | None => {
return Err(PartyError::Unknown("cannot apply score delta".into()))
FreeForAllGameUpdate::Ranking(update) => match update {
FreeForAllGameUpdateRanking::SetRanking(r) => {
if !r.is_valid(&self.spec.participants) {
return Err(PartyError::Unknown("invalid ranking, all participants mentioned in ranking must be participating".into()));
}
self.ranking = Some(r)
}
Some(FreeForAllGameRanking::Scores(s)) => {
for (player, delta) in d.iter() {
if let Some(value) = s.get(player) {
s.insert(player.clone(), value + delta);
FreeForAllGameUpdateRanking::ScoreDelta(d) => match &mut self.ranking {
Some(FreeForAllGameRanking::Ranking(_)) | None => {
return Err(PartyError::Unknown("cannot apply score delta".into()))
}
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();
for (player, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
let score = points.get(player).unwrap_or(&0);
points.insert(player.clone(), score + reward);
for (participant, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
let score = points.get(participant).unwrap_or(&0);
points.insert(participant.clone(), score + reward);
}
for (player, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
let score = points.get(player).unwrap_or(&0);
points.insert(player.clone(), score + reward);
for (participant, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
let score = points.get(participant).unwrap_or(&0);
points.insert(participant.clone(), score + reward);
}
EventOutcome { points }