use uuid::Uuid; use serde_cbor::{from_slice, to_vec}; use postgres::transaction::Transaction; use failure::Error; use failure::err_msg; use std::iter; use rpc::{InstanceJoinParams, InstanceReadyParams}; use account::Account; use player::{Player, player_create, player_get, player_update}; use cryp::{Cryp, cryp_get}; use mob::{instance_mobs}; use game::{Game, Team, game_get, game_write, game_instance_new, game_instance_join}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] enum InstancePhase { Open, Vbox, Games, Finished, } #[derive(Debug,Clone,Serialize,Deserialize)] struct Round { player_ids: Vec, game_id: Uuid, outcome: Option<(Uuid, Uuid)>, } #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Instance { id: Uuid, players: Vec, phase: InstancePhase, rounds: Vec>, open: bool, pve: bool, } impl Instance { fn new() -> Instance { Instance { id: Uuid::new_v4(), players: vec![], rounds: vec![], phase: InstancePhase::Open, open: true, pve: false, } } fn add_bots(mut self) -> Instance { self.pve = true; self.players = iter::repeat_with(|| { let bot_id = Uuid::new_v4(); let cryps = instance_mobs(bot_id); Player::new(bot_id, self.id, cryps).set_bot(true) }) .take(15) .collect::>(); self } fn add_player(&mut self, player: Player) -> &mut Instance { self.players.push(player); self } fn player_ready(mut self, mut player: Player) -> Result { if self.phase != InstancePhase::Vbox { panic!("instance not in vbox phase"); } let i = self.players .iter() .position(|p| p.id == player.id) .ok_or(err_msg("player_id not found"))?; player.set_ready(true); self.players[i] = player; Ok(self) } fn bot_vs_player_game(&self, player: &Player) -> Game { let current_round = self.current_round(player); let plr = self.players.clone().into_iter().find(|p| p.id == player.id).unwrap(); let bot = self.players.clone().into_iter().find(|p| p.id != player.id).unwrap(); let mut game = Game::new(); game.id = current_round.game_id; game .set_pve(true) .set_team_num(2) .set_team_size(3); // add the players let mut plr_team = Team::new(plr.account); plr_team.set_cryps(plr.cryps); let mut bot_team = Team::new(bot.account); bot_team.set_cryps(bot.cryps); bot_team.set_bot(); game .team_add(plr_team).unwrap() .team_add(bot_team).unwrap(); game.start(); game } fn can_start(&self) -> bool { match self.pve { true => self.players.len() == 16, false => self.players.len() == 2, } } fn start(&mut self) -> &mut Instance { // self.players.sort_unstable_by_key(|p| p.id); self.generate_rounds(); self.open = false; self.phase = InstancePhase::Vbox; self.vbox_phase_start() } fn vbox_phase_start(&mut self) -> &mut Instance { // match self.rounds.last() { // Some(r) => { // for round in r { // { // let winner = self.players.iter_mut().find(|p| p.account == round.outcome.unwrap().0).unwrap(); // winner.add_win(); // } // { // let loser = self.players.iter_mut().find(|p| p.account == round.outcome.unwrap().1).unwrap(); // loser.add_loss(); // } // } // } // None => (), // } self.bot_vbox_phase(); self } fn vbox_phase_finished(&self) -> bool { self.players.iter().all(|p| p.ready) } // requires no input // just do it fn games_phase_start(&mut self) -> &mut Instance { if self.phase != InstancePhase::Vbox { panic!("instance not in vbox phase"); } assert!(self.vbox_phase_finished()); self.phase = InstancePhase::Games; self.bot_games_phase(); self } fn games_phase_finished(&self) -> bool { match self.rounds.last() { Some(r) => r.iter().all(|g| g.outcome.is_some()), None => true, } } fn bot_vbox_phase(&mut self) -> &mut Instance { for bot in self.players.iter_mut().filter(|p| p.bot) { bot.set_ready(true); } self } fn bot_games_phase(&mut self) -> &mut Instance { if self.phase != InstancePhase::Games { panic!("instance not in games phase"); } if self.pve { let r = self.rounds.len() - 1; for mut round in self.rounds[r].iter_mut() { if self.players .iter() .filter(|p| round.player_ids.contains(&p.id) && p.bot) .count() == 2 { println!("should play a game between {:?}", round.player_ids); let a = self.players.clone().into_iter().find(|p| p.id == round.player_ids[0]).unwrap(); let b = self.players.clone().into_iter().find(|p| p.id == round.player_ids[1]).unwrap(); let mut game = Game::new(); game .set_pve(true) .set_team_num(2) .set_team_size(3); // add the players let mut a_team = Team::new(a.id); a_team.set_cryps(a.cryps); a_team.set_bot(); let mut b_team = Team::new(b.id); b_team.set_cryps(b.cryps); b_team.set_bot(); game .team_add(a_team).unwrap() .team_add(b_team).unwrap(); game.start(); assert!(game.finished()); round.outcome = Some((game.winner().unwrap().id, Uuid::new_v4())); } } } self } fn generate_rounds(&mut self) -> &mut Instance { let round_num = self.rounds.len(); let mut matched_players = self.players .iter() .map(|p| p.id) .collect::>(); if round_num > 0 { matched_players.rotate_right(round_num); matched_players.swap(0,1); } // only set up for even player numbers atm // no byes let np = matched_players.len(); println!("{:?} players in instance", np); let current_round = matched_players[0..(np / 2)] .iter() .enumerate() .map(|(i, id)| Round { player_ids: vec![*id, matched_players[np - (i + 1)]], game_id: Uuid::new_v4(), outcome: None, }) .collect::>(); self.rounds.push(current_round); self } fn current_round(&self, player: &Player) -> &Round { let round_num = self.rounds.len() - 1; let current_round = self.rounds[round_num] .iter() .find(|g| g.player_ids.contains(&player.id)) .unwrap(); current_round } } pub fn instance_create(instance: Instance, tx: &mut Transaction) -> Result { let instance_bytes = to_vec(&instance)?; let query = " INSERT INTO instances (id, data) VALUES ($1, $2) RETURNING id; "; let result = tx .query(query, &[&instance.id, &instance_bytes])?; result.iter().next().ok_or(format_err!("no instances written"))?; return Ok(instance); } pub fn instance_write(instance: Instance, tx: &mut Transaction) -> Result { let instance_bytes = to_vec(&instance)?; let query = " UPDATE instances SET data = $1, open = $2 WHERE id = $3 RETURNING id, data; "; let result = tx .query(query, &[&instance_bytes, &instance.open, &instance.id])?; result.iter().next().ok_or(err_msg("no instance row returned"))?; // println!("{:?} wrote instance", instance.id); return Ok(instance); } pub fn instance_get(tx: &mut Transaction, instance_id: Uuid) -> Result { let query = " SELECT * FROM instances WHERE id = $1; "; let result = tx .query(query, &[&instance_id])?; let returned = match result.iter().next() { Some(row) => row, None => return Err(err_msg("instance not found")), }; let instance_bytes: Vec = returned.get("data"); let instance = from_slice::(&instance_bytes)?; return Ok(instance); } pub fn instance_get_open(tx: &mut Transaction) -> Result { let query = " SELECT * FROM instances WHERE open = true; "; let result = tx .query(query, &[])?; let returned = match result.iter().next() { Some(row) => row, None => return Err(err_msg("instance not found")), }; let instance_bytes: Vec = returned.get("data"); let instance = from_slice::(&instance_bytes)?; return Ok(instance); } pub fn instance_join(params: InstanceJoinParams, tx: &mut Transaction, account: &Account) -> Result { let mut instance = match params.pve { true => instance_create(Instance::new().add_bots(), tx)?, false => match instance_get_open(tx) { Ok(i) => i, Err(_) => instance_create(Instance::new(), tx)?, }, }; let cryps = params.cryp_ids .iter() .map(|id| cryp_get(tx, *id, account.id)) .collect::, Error>>()?; if cryps.len() != 3 { return Err(format_err!("incorrect team size. ({:})", 3)); } let player = Player::new(account.id, instance.id, cryps); player_create(tx, &player, account)?; instance.add_player(player.clone()); if instance.can_start() { instance.start(); } instance_write(instance, tx)?; return Ok(player); } pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result { let player = player_get(tx, account.id, params.instance_id)?; let instance = instance_get(tx, params.instance_id)? .player_ready(player.clone())?; let game_id = instance.current_round(&player).game_id; let game = match instance.pve { true => match game_get(tx, game_id) { Ok(g) => g, Err(_) => { let game = instance.bot_vs_player_game(&player); game_write(&game, tx)?; game }, }, false => match game_get(tx, game_id) { Ok(_g) => game_instance_join(tx, player.clone(), game_id)?, Err(_) => game_instance_new(tx, player.clone(), game_id)?, } }; player_update(tx, player)?; instance_write(instance, tx)?; return Ok(game); } #[cfg(test)] mod tests { use super::*; #[test] fn instance_pve_test() { let mut instance = Instance::new().add_bots(); let player_id = Uuid::new_v4(); let cryps = instance_mobs(player_id); let player = Player::new(player_id, instance.id, cryps).set_bot(true); instance.add_player(player); assert!(instance.can_start()); instance.start(); assert_eq!(instance.rounds[0].len(), 8); let player = instance.players.clone().into_iter().find(|p| p.account == player_id).unwrap(); let mut instance = instance.player_ready(player).unwrap(); assert!(instance.vbox_phase_finished()); instance.games_phase_start(); println!("{:?}", instance.rounds); assert!(instance.games_phase_finished()); } }