WIP: battle

feature/battle
brnrs 5 years ago
parent ec8936f72e
commit cb96c33135
  1. 9
      Cargo.toml
  2. 64
      src/battle/actor.rs
  3. 105
      src/battle/mod.rs
  4. 211
      src/battle/queue.rs
  5. 5
      src/main.rs

@ -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…
Cancel
Save