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