use uuid::Uuid; use std::iter; // reclaims use rand::prelude::*; use rand::{thread_rng}; use rand::distributions::{WeightedIndex}; use postgres::transaction::Transaction; use failure::Error; use failure::err_msg; use account::Account; use instance::{Instance, instance_get, instance_update}; use construct::{Colours}; use item::*; #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Vbox { pub bits: usize, pub free: Vec>>, pub bound: Vec, } pub enum ItemType { Colours, Skills, Specs, } impl Vbox { pub fn new() -> Vbox { let starting_items = vec![ Item::Attack, Item::Attack, Item::Attack, ]; Vbox { free: vec![vec![], vec![], vec![]], bound: starting_items, bits: 30, } } pub fn balance_sub(&mut self, amount: usize) -> Result<&mut Vbox, Error> { let new_balance = self.bits .checked_sub(amount) .ok_or(format_err!("insufficient balance: {:?}", self.bits))?; self.bits = new_balance; Ok(self) } pub fn balance_add(&mut self, amount: usize) -> &mut Vbox { self.bits = self.bits.saturating_add(amount); self } pub fn fill(&mut self) -> &mut Vbox { let mut rng = thread_rng(); self.free = [ItemType::Colours, ItemType::Skills, ItemType::Specs].iter() .map(|item_type| { let items = match item_type { ItemType::Colours => vec![ (Some(Item::Red), 1), (Some(Item::Green), 1), (Some(Item::Blue), 1), ], ItemType::Skills => vec![ (Some(Item::Attack), 1), (Some(Item::Block), 1), (Some(Item::Buff), 1), (Some(Item::Debuff), 1), (Some(Item::Stun), 1), ], ItemType::Specs => vec![ (Some(Item::Power), 1), (Some(Item::Life), 1), (Some(Item::Speed), 1), ], }; let dist = WeightedIndex::new(items.iter().map(|item| item.1)).unwrap(); iter::repeat_with(|| { items[dist.sample(&mut rng)].0}).take(match item_type { ItemType::Colours => 6, _ => 3, }).collect::>>() }) .collect::>>>(); self } pub fn accept(&mut self, i: usize, j: usize, construct_id: Option) -> Result<&mut Vbox, Error> { if self.bound.len() >= 9 && !construct_id.is_some() { return Err(err_msg("too many items bound")); } // check item exists self.free .get(i).ok_or(format_err!("no item group at index {:?}", i))? .get(j).ok_or(format_err!("no item at index {:?}", j))?; // check can purchase let cost = match self.free[i][j] { None => 0, _ => self.free[i][j].unwrap().cost() }; self.balance_sub(cost)?; // actually move match self.free[i][j] { None => (), _ => self.bound.push(self.free[i][j].unwrap()) } // self.bound.push(self.free[i][j].unwrap()); self.free[i][j] = None; // self.bound.sort_unstable(); Ok(self) } pub fn bot_accept(&mut self, i: usize) -> Result<&mut Vbox, Error> { let buy_index = self.free[i].iter().position(|item| item.is_some()); self.accept(i, buy_index.expect("no valid buys"), None) } pub fn reclaim(&mut self, i: usize) -> Result<&mut Vbox, Error> { self.bound.get(i).ok_or(format_err!("no item at index {:?}", i))?; let reclaimed = self.bound.remove(i); let refund = reclaimed.cost(); // info!("reclaiming {:?} for {:?}", refund, reclaimed); self.balance_add(refund); Ok(self) } pub fn combine(&mut self, mut inv_indices: Vec, mut vbox_indicies: Vec>) -> Result<&mut Vbox, Error> { if !inv_indices.iter().all(|i| self.bound.get(*i).is_some()) { return Err(err_msg("item missing index")); } // have to sort the indices and keep track of the iteration // because when removing the elements the array shifts inv_indices.sort_unstable(); let mut input = inv_indices .iter() .enumerate() .map(|(i, index)| { self.bound.remove(index.saturating_sub(i)) }) .collect::>(); // sort the input to align with the combinations // combos are sorted when created input.sort_unstable(); let combos = get_combos(); let combo = combos.iter().find(|c| c.components == input).ok_or(err_msg("not a combo"))?; self.bound.push(combo.item); // self.bound.sort_unstable(); Ok(self) } } pub fn vbox_discard(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result { let instance = instance_get(tx, instance_id)? .vbox_discard(account.id)?; return instance_update(tx, instance); } pub fn vbox_accept(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: usize, index: usize, construct_id: Option) -> Result { let instance = instance_get(tx, instance_id)? .vbox_accept(account.id, group, index, construct_id)?; return instance_update(tx, instance); } pub fn vbox_combine(tx: &mut Transaction, account: &Account, instance_id: Uuid, inv_indices: Vec, vbox_indices: Vec>) -> Result { let instance = instance_get(tx, instance_id)? .vbox_combine(account.id, inv_indices, vbox_indices)?; return instance_update(tx, instance); } pub fn vbox_reclaim(tx: &mut Transaction, account: &Account, instance_id: Uuid, index: usize) -> Result { let instance = instance_get(tx, instance_id)? .vbox_reclaim(account.id, index)?; return instance_update(tx, instance); } pub fn vbox_apply(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, index: usize) -> Result { let instance = instance_get(tx, instance_id)? .vbox_apply(account.id, index, construct_id)?; return instance_update(tx, instance); } pub fn vbox_unequip(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, target: Item, target_construct_id: Option) -> Result { let instance = instance_get(tx, instance_id)? .vbox_unequip(account.id, target, construct_id, target_construct_id)?; return instance_update(tx, instance); } #[cfg(test)] mod tests { use super::*; #[test] fn combine_test() { let mut vbox = Vbox::new(); vbox.bound = vec![Item::Attack, Item::Green, Item::Green]; vbox.combine(vec![1,2,0], vec![]).unwrap(); assert_eq!(vbox.bound[0], Item::Heal); } #[test] fn combos_test() { let mut input = vec![Item::Green, Item::Attack, Item::Green]; let combos = get_combos(); // sort input so they align input.sort_unstable(); let combo = combos.iter().find(|c| c.components == input); assert!(combo.is_some()); } #[test] fn reclaim_test() { let mut vbox = Vbox::new(); vbox.bound = vec![Item::Strike]; vbox.reclaim(0).unwrap(); assert_eq!(vbox.bits, 32); } #[test] fn colours_count_test() { let strike = Item::Strike; let mut count = Colours::new(); strike.colours(&mut count); assert_eq!(count.red, 2); } // #[test] // fn item_info_test() { // info!("{:#?}", item_info()); // } }