mnml/server/src/vbox.rs
2019-11-19 10:35:45 +10:00

254 lines
7.9 KiB
Rust

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<Vec<Option<Item>>>,
pub bound: Vec<Item>,
}
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::<Vec<Option<Item>>>()
})
.collect::<Vec<Vec<Option<Item>>>>();
self
}
pub fn accept(&mut self, i: usize, j: usize, construct_id: Option<Uuid>) -> 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<usize>, mut vbox_indicies: Vec<Vec<usize>>) -> 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::<Vec<Item>>();
// 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<Instance, Error> {
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<Uuid>) -> Result<Instance, Error> {
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<usize>, vbox_indices: Vec<Vec<usize>>) -> Result<Instance, Error> {
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<Instance, Error> {
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<Instance, Error> {
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<Uuid>) -> Result<Instance, Error> {
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());
// }
}