1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#[macro_use]
mod macros;

mod carrier;
mod dispatcher;
mod payload;
mod position;
mod slot;
mod tools;

use std::hash::Hash;

pub use carrier::*;
use dispatcher::*;
pub use payload::*;
pub use slot::*;

#[macro_use]
extern crate approx;

fn _debug_dump_slots(slots: &[Slot<char>]) {
    for (i, v) in slots.iter().enumerate() {
        print!("Slot [{}]: ", i);

        match slots[i].current_payload {
            Some(p) => print!("{} ", p.cargo),
            None => print!("None "),
        }

        let [_, target] = v.get_payloads();
        match target {
            Some(p) => print!("    {} ", p.cargo),
            None => print!("    None "),
        }

        print!("\tTaken care of={}", v.taken_care_of);

        println!();
    }
}

fn _debug_dump_slot_distances<T: PartialEq + Eq + Hash + Copy>(
    slots: &[Slot<T>],
    dispatcher: &Dispatcher<T>,
) {
    slots.iter().enumerate().for_each(|(i1, _)| {
        slots.iter().enumerate().for_each(|(i2, _)| {
            println!(
                "{}->{} = {}",
                i1,
                i2,
                dispatcher.get_distance_slot_slot(i1, i2)
            );
        })
    });
}

/// Main struct that allow you to talk to the library
#[derive(Default)]
pub struct Swarm<T: PartialEq + Eq + Hash + Copy> {
    carriers: Vec<Carrier<T>>,
    slots: Vec<Slot<T>>,
    first_tick: bool,
    idle_ticks: u8,
    tick_counter: u64,
    dispatcher: Dispatcher<T>,
}

impl<T: PartialEq + Eq + Hash + Copy> Swarm<T> {
    /// Constructs a new `Swarm`.
    ///
    /// `T` - the type of cargo that your carriers will carry around. The `Copy` bound is going to be removed in future versions.
    ///
    /// # Example
    ///
    /// ```
    /// let game = swarm_it::Swarm::<char>::new();
    /// ```
    pub fn new() -> Swarm<T> {
        Swarm {
            carriers: Vec::new(),
            slots: Vec::new(),
            first_tick: true,
            idle_ticks: 0,
            tick_counter: 0,
            dispatcher: Dispatcher::new(),
        }
    }

    /// Adds new carrier.
    /// Returns the index of the new carrier.
    ///
    /// # Example
    ///
    /// ```
    /// use swarm_it::*;
    /// let mut game = Swarm::<char>::new();
    /// game.add_carrier(Carrier::new(100.0, 200.0));
    /// ```
    pub fn add_carrier(&mut self, carrier: Carrier<T>) -> usize {
        Swarm::<T>::add_object(&mut self.carriers, carrier)
    }

    /// Adds new slot.
    /// Returns the index of the new slot.
    ///
    /// # Example
    ///
    /// ```
    /// use swarm_it::*;
    /// let mut game = Swarm::<char>::new();
    /// game.add_slot(Slot::new(100.0, 100.0, None, Some(Payload::new('X')), swarm_it::SlotKind::CLASSIC));
    /// ```
    pub fn add_slot(&mut self, slot: Slot<T>) -> usize {
        Swarm::<T>::add_object(&mut self.slots, slot)
    }

    /// Returns all carriers
    pub fn get_carriers(&self) -> &Vec<Carrier<T>> {
        &self.carriers
    }

    /// Returns all carriers
    pub fn get_carriers_mut(&mut self) -> &mut Vec<Carrier<T>> {
        &mut self.carriers
    }

    /// Returns all slots
    pub fn get_slots(&self) -> &Vec<Slot<T>> {
        &self.slots
    }

    /// Returns all slots
    pub fn get_slots_mut(&mut self) -> &mut Vec<Slot<T>> {
        &mut self.slots
    }

    /// The engine must be regularly ticked by the outside world by invoking this function.
    /// At each tick swarm will perform calculation of the internal state logic, move
    /// carriers around, etc.
    ///
    /// Returns `true` if there were no more action required, meaning that carriers have finished
    /// tranferring the layout to target position.
    ///
    /// # Example
    ///
    /// ```
    /// use swarm_it::*;
    /// let mut game = Swarm::<char>::new();
    /// if game.tick() { println!("Job finished, yay!"); };
    /// ```
    pub fn tick(&mut self) -> bool {
        self.tick_counter += 1;
        let mut slots = &mut self.slots;
        if self.first_tick {
            self.dispatcher.precalc(&slots);
            self.first_tick = false;
            //_debug_dump_slot_distances(&slots, &self.dispatcher);
        }
        self.dispatcher.conduct(&mut self.carriers, &mut slots);
        self.carriers.iter_mut().for_each(|x| x.tick(slots));
        self.job_finished()
    }

    /// Initiates some precalculation in order for the carriers
    /// to be aware of modified slots. Call this function each time you
    /// finished adding new slots or manually manipulating their data
    /// through `get_slots_mut()`.
    ///
    /// In the future I might eventually remove this obligation, but I need
    /// to come up with some clever way of deciding whether a precalc is
    /// needed or not. Invoking precalc after each single change is an overkill, since
    /// one might want to add 100k slots and do the precalc only once at the end.
    ///
    /// # Example
    ///
    /// ```
    /// use swarm_it::*;
    /// let mut game = Swarm::<char>::new();
    /// game.slot_data_changed();
    /// ```
    ///
    /// # TODO
    /// Different kind of that may change. Provide additional
    /// parameter in order NOT to precalculate everything
    /// 1. Slot payload => recalculate cargo balance
    /// 2. Slots added/removed => recalculate slot distances
    pub fn slot_data_changed(&mut self) {
        self.dispatcher.precalc(&self.slots);
    }

    fn add_object<U>(vec: &mut Vec<U>, obj: U) -> usize {
        vec.push(obj);
        vec.len() - 1
    }

    fn all_carriers_idle(&self) -> bool {
        !self.carriers.iter().any(|c| !c.state.is_idle())
    }

    fn job_finished(&mut self) -> bool {
        if self.all_carriers_idle() {
            self.idle_ticks += 1;
            if self.idle_ticks == std::u8::MAX {
                self.idle_ticks = 3;
            }
            if self.idle_ticks >= 2 {
                return true;
            }
        } else {
            self.idle_ticks = 0;
        }
        false
    }
}

#[cfg(test)]
mod tests {}