262 lines
8.2 KiB
Rust
262 lines
8.2 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() >= 6 && !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>, 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"));
|
|
}
|
|
// try to buy up the vbox indicies and add them to the inventory indicies for combining
|
|
for vi in vbox_indicies.iter() {
|
|
inv_indices.push(self.bound.len());
|
|
self.accept(vi[0], vi[1], Some(Uuid::nil()))?;
|
|
}
|
|
|
|
// 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();
|
|
if self.bound.len() > 6 {
|
|
return Err(err_msg("too many items bound"));
|
|
}
|
|
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());
|
|
// }
|
|
} |