parent
ec8936f72e
commit
cb96c33135
@ -0,0 +1,9 @@ |
|||||||
|
[package] |
||||||
|
name = "adventure" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["brnrs <brnrs@brnrs.pl>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
@ -0,0 +1,64 @@ |
|||||||
|
use std::cell::RefCell; |
||||||
|
use std::num::NonZeroU32; |
||||||
|
use std::rc::Rc; |
||||||
|
|
||||||
|
pub type ActorId = u32; |
||||||
|
pub type TeamId = u32; |
||||||
|
pub type AiId = u32; |
||||||
|
pub type SkillId = u32; |
||||||
|
|
||||||
|
pub struct Actor { |
||||||
|
pub id: ActorId, |
||||||
|
pub team_id: TeamId, |
||||||
|
pub ai_id: Option<AiId>, |
||||||
|
pub name: String, |
||||||
|
pub state: ActorState, |
||||||
|
pub initiative: NonZeroU32, |
||||||
|
pub skills: Vec<SkillId>, |
||||||
|
} |
||||||
|
|
||||||
|
type Hp = std::num::NonZeroU32; |
||||||
|
pub enum ActorState { |
||||||
|
Dead, |
||||||
|
Alive(Hp), |
||||||
|
} |
||||||
|
impl ActorState { |
||||||
|
pub fn is_dead(&self) -> bool { |
||||||
|
match self { |
||||||
|
Self::Dead => true, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn is_alive(&self) -> bool { |
||||||
|
match self { |
||||||
|
Self::Alive(_) => true, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct TeamInfo(Option<AiId>); |
||||||
|
|
||||||
|
type ActorWrp = Rc<RefCell<Actor>>; |
||||||
|
pub struct Team { |
||||||
|
pub id: TeamId, |
||||||
|
pub info: TeamInfo, |
||||||
|
pub members: Box<dyn Iterator<Item = ActorWrp>>, |
||||||
|
} |
||||||
|
|
||||||
|
pub trait ActorDb<'a> { |
||||||
|
fn lookup_actor(&self, id: ActorId) -> Option<ActorWrp>; |
||||||
|
|
||||||
|
fn lookup_team_info(&self, id: TeamId) -> Option<TeamInfo>; |
||||||
|
|
||||||
|
fn lookup_team(&self, id: TeamId) -> Option<Team>; |
||||||
|
|
||||||
|
fn lookup_actor_ids(&self) -> Box<dyn Iterator<Item = ActorId>>; |
||||||
|
|
||||||
|
fn lookup_team_ids(&self) -> Box<dyn Iterator<Item = TeamId>>; |
||||||
|
|
||||||
|
fn lookup_actors(&self) -> Box<dyn Iterator<Item = ActorWrp>>; |
||||||
|
|
||||||
|
fn lookup_teams(&self) -> Box<dyn Iterator<Item = Team>>; |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
#![allow(dead_code,unused_variables,unused_imports,unused_mut)] |
||||||
|
|
||||||
|
mod actor; |
||||||
|
mod queue; |
||||||
|
|
||||||
|
use actor::*; |
||||||
|
use queue::{Queue, QueueElement}; |
||||||
|
|
||||||
|
use std::cell::RefCell; |
||||||
|
use std::rc::Rc; |
||||||
|
|
||||||
|
fn battle( |
||||||
|
actor_db: &dyn ActorDb, |
||||||
|
decider: &dyn Decider, |
||||||
|
effector: &dyn Effector, |
||||||
|
) -> Result<u32, &'static str> { |
||||||
|
if let Err(err_msg) = check(actor_db) { |
||||||
|
return Err(err_msg); |
||||||
|
} |
||||||
|
let mut queue = Queue::new(); |
||||||
|
for actor in actor_db.lookup_actors() { |
||||||
|
let actor = actor.borrow(); |
||||||
|
queue.enqueue(actor.initiative.get(), actor.id); |
||||||
|
} |
||||||
|
|
||||||
|
while let Some(QueueElement(incr, actor_id)) = queue.next() { |
||||||
|
let actor = match actor_db.lookup_actor(actor_id) { |
||||||
|
Some(actor) => actor, |
||||||
|
None => return Err("Couldn't find actor with specified id"), |
||||||
|
}; |
||||||
|
let team_info = match actor_db.lookup_team_info(actor.borrow().team_id) { |
||||||
|
Some(team_info) => team_info, |
||||||
|
None => return Err("Couldn't find team with specified id"), |
||||||
|
}; |
||||||
|
|
||||||
|
let decision = decider.decide(actor_db, actor.clone())?; |
||||||
|
effector.effect(actor_db, actor.clone(), decision)?; |
||||||
|
|
||||||
|
if at_most_one_team_left_alive(actor_db) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Err("not implemented") |
||||||
|
} |
||||||
|
|
||||||
|
fn check(actor_db: &dyn ActorDb) -> Result<(), &'static str> { |
||||||
|
let stats = actor_db |
||||||
|
.lookup_teams() |
||||||
|
.map(|mut team| team.members.next().is_none()) |
||||||
|
.fold((0, 0), |(non_empty, empty), is_empty| { |
||||||
|
if is_empty { |
||||||
|
(non_empty, empty + 1) |
||||||
|
} else { |
||||||
|
(non_empty + 1, empty) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
match stats { |
||||||
|
(0, 0) => Err("No teams provided for battle"), |
||||||
|
(1, 0) => Err("More than one team must be provided for battle"), |
||||||
|
(_, 0) => Ok(()), |
||||||
|
(_, empty) => Err("Empty teams cannot participate in battle"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn at_most_one_team_left_alive(actor_db: &dyn ActorDb) -> bool { |
||||||
|
let (alive, _) = actor_db |
||||||
|
.lookup_teams() |
||||||
|
.map(|mut team| team.members.all(|actor| actor.borrow().state.is_dead())) |
||||||
|
.fold((0, 0), |(alive, dead), all_dead| { |
||||||
|
if all_dead { |
||||||
|
(alive, dead + 1) |
||||||
|
} else { |
||||||
|
(alive + 1, dead) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
alive <= 1 |
||||||
|
} |
||||||
|
|
||||||
|
enum Target { |
||||||
|
Oneself, |
||||||
|
Actor(ActorId), |
||||||
|
Team(TeamId), |
||||||
|
} |
||||||
|
|
||||||
|
struct Decision(SkillId, Target); |
||||||
|
|
||||||
|
trait Decider { |
||||||
|
fn decide( |
||||||
|
&self, |
||||||
|
actor_db: &dyn ActorDb, |
||||||
|
actor: Rc<RefCell<Actor>>, |
||||||
|
) -> Result<Decision, &'static str>; |
||||||
|
} |
||||||
|
|
||||||
|
trait Effector { |
||||||
|
fn effect( |
||||||
|
&self, |
||||||
|
actor_db: &dyn ActorDb, |
||||||
|
actor: Rc<RefCell<Actor>>, |
||||||
|
decision: Decision, |
||||||
|
) -> Result<(), &'static str>; |
||||||
|
} |
@ -0,0 +1,211 @@ |
|||||||
|
type Increment = u32; |
||||||
|
type Sum = u64; |
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
pub struct QueueElement<T>(pub Increment, pub T); |
||||||
|
|
||||||
|
pub struct Queue<T> { |
||||||
|
container: Vec<QueueElement<T>>, |
||||||
|
sum: Sum, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Queue<T> { |
||||||
|
pub fn new() -> Queue<T> { |
||||||
|
Queue { |
||||||
|
container: Vec::new(), |
||||||
|
sum: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn next(&mut self) -> Option<QueueElement<T>> { |
||||||
|
if self.container.is_empty() { |
||||||
|
None |
||||||
|
} else { |
||||||
|
let next = self.container.remove(0); |
||||||
|
if self.sum < next.0.into() { |
||||||
|
panic!("Sum of increments should not be smaller than a single increment"); |
||||||
|
} else { |
||||||
|
self.sum -= next.0 as u64; |
||||||
|
Some(next) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn enqueue(&mut self, mut incr: Increment, t: T) { |
||||||
|
let mut insert_pos = 0; |
||||||
|
for (i, el) in self.container.iter_mut().enumerate() { |
||||||
|
if el.0 > incr { |
||||||
|
el.0 -= incr; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
insert_pos = i + 1; |
||||||
|
if el.0 < incr { |
||||||
|
incr -= el.0; |
||||||
|
} |
||||||
|
} |
||||||
|
if insert_pos == self.container.len() { |
||||||
|
self.sum += incr as u64; |
||||||
|
} |
||||||
|
self.container.insert(insert_pos, QueueElement(incr, t)); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn from<I: IntoIterator<Item = T>, F: Fn(&T) -> Increment>( |
||||||
|
iter: I, |
||||||
|
key_extractor: F, |
||||||
|
) -> Queue<T> { |
||||||
|
let mut queue = Queue::new(); |
||||||
|
iter.into_iter() |
||||||
|
.map(move |t| (key_extractor(&t), t)) |
||||||
|
.for_each(|(incr, t)| queue.enqueue(incr, t)); |
||||||
|
queue |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_container(&self) -> &[QueueElement<T>] { |
||||||
|
&self.container |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a, T: 'a + Clone> Queue<T> { |
||||||
|
pub fn from_clone<I: IntoIterator<Item = &'a T>, F: Fn(&T) -> Increment>( |
||||||
|
iter: I, |
||||||
|
key_extractor: F, |
||||||
|
) -> Queue<T> { |
||||||
|
let mut queue = Queue::new(); |
||||||
|
iter.into_iter() |
||||||
|
.map(|t| (key_extractor(t), t.clone())) |
||||||
|
.for_each(|(incr, t)| queue.enqueue(incr, t)); |
||||||
|
queue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn retrieve_from_empty() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
|
||||||
|
// When
|
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn retrieve_only_one() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(1, "A"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn inserting_two_reversing_order_and_adjusting_increments() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(2, "A"); |
||||||
|
queue.enqueue(1, "B"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let second = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "B"))); |
||||||
|
assert_eq!(second, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn insert_two_maintaining_order_and_adjusting_increments() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(1, "A"); |
||||||
|
queue.enqueue(2, "B"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let second = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(second, Some(QueueElement(1, "B"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn insert_three_maintaining_order_and_adjusting_increments() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(1, "A"); |
||||||
|
queue.enqueue(2, "B"); |
||||||
|
queue.enqueue(3, "C"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let second = queue.next(); |
||||||
|
let third = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(second, Some(QueueElement(1, "B"))); |
||||||
|
assert_eq!(third, Some(QueueElement(1, "C"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn insert_three_with_last_in_the_middle_and_adjusting_increments() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(1, "A"); |
||||||
|
queue.enqueue(3, "B"); |
||||||
|
queue.enqueue(2, "C"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let second = queue.next(); |
||||||
|
let third = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(second, Some(QueueElement(1, "C"))); |
||||||
|
assert_eq!(third, Some(QueueElement(1, "B"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn insert_three_reversing_order_and_adjusting_increments() { |
||||||
|
// Given
|
||||||
|
let mut queue: Queue<&'static str> = Queue::new(); |
||||||
|
queue.enqueue(3, "A"); |
||||||
|
queue.enqueue(2, "B"); |
||||||
|
queue.enqueue(1, "C"); |
||||||
|
|
||||||
|
// When
|
||||||
|
let first = queue.next(); |
||||||
|
let second = queue.next(); |
||||||
|
let third = queue.next(); |
||||||
|
let no_more = queue.next(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(first, Some(QueueElement(1, "C"))); |
||||||
|
assert_eq!(second, Some(QueueElement(1, "B"))); |
||||||
|
assert_eq!(third, Some(QueueElement(1, "A"))); |
||||||
|
assert_eq!(no_more, None); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
mod battle; |
||||||
|
|
||||||
|
fn main() { |
||||||
|
println!("Hello, world!"); |
||||||
|
} |
Loading…
Reference in new issue