rustbatch 0.4.0

purely game dewelopment crate that offers simple but powerfull 2D rendering and some fast solutions for game world bottle necks
Documentation
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, channel, Receiver, TryRecvError};
use std::thread;
use std::clone::Clone;

pub const INFINITY: i32 = -1;
pub const D4: [(i32, i32); 4] = [(0, 1), (1, 0), (-1, 0), (0, -1),];
pub const D8: [(i32, i32); 8] = [(0, 1), (1, 0), (-1, 0), (0, -1), (1, 1), (1, -1), (-1, -1), (-1, 1)];

pub struct PathFinder {
    terminator: Sender<()>,
    data: Arc<Data>,
}

impl PathFinder {
    pub fn new(costs: &Vec<Vec<i32>>) -> Self {
        let (terminator, t) = channel();
        PathFinder{
            terminator,
            data: Arc::new(Data::new(costs[0].len(), costs.len(), t, costs)),
        }
    }

    pub fn update(&mut self, frontier: &Vec<(usize, usize)>, changes: &Vec<(usize, usize, i32)>) {
        let mut processor = match self.data.processor.try_lock(){
            Ok(guard) => guard,
            Err(_) => {
                self.terminator.send(()).unwrap();
                self.data.processor.lock().unwrap()
            }
        };
        processor.frontier.extend(frontier);

        for change in changes.iter() {
            processor.costs[change.1][change.0] = change.2;
        }

        drop(processor);

        let data = Arc::clone(&self.data);
        thread::spawn(move || data.processor.lock().unwrap().update(&data.map, data.size.0, data.size.1));
    }

    pub fn get_step(&self, current: (usize, usize)) -> (usize, usize) {
        let mut best = current;
        let mut lowest = i32::MAX;
        let map = self.data.map.lock().unwrap();
        for d in D8.iter() {
            let pos = (current.0 as i32 + d.0, current.1 as i32 + d.1);
            if pos.0 < 0 || pos.1 < 0 || pos.0 == self.data.size.0 || pos.1 == self.data.size.1 {
                continue
            }

            let pos = (pos.0 as usize, pos.1 as usize);

            let val = map[pos.1][pos.0];
            if val != INFINITY && (d.1 == 0 || d.0 == 0 || map[pos.1][current.0] != INFINITY ||
                map[current.1][pos.0] != INFINITY) && val < lowest {
                lowest = val;
                best = pos;
            }
        }
        best
    }
}



pub struct Data {
    pub map: Mutex<Vec<Vec<i32>>>,
    pub processor: Mutex<Processor>,
    size: (i32, i32),
}

impl Data {
    pub fn new(w: usize, h: usize, terminator: Receiver<()>, costs: &Vec<Vec<i32>>) -> Self {
        let map = vec![vec![INFINITY; w]; h];
        Self {
            map: Mutex::new(map.clone()),
            processor: Mutex::new(Processor {
                terminator,
                workspace: map,
                frontier: Vec::with_capacity(w + h),
                collector: Vec::with_capacity(w + h),
                costs: costs.clone(),
            }),
            size: (w as i32, h as i32),
        }
    }
}

pub struct Processor {
    terminator: Receiver<()>,
    workspace: Vec<Vec<i32>>,
    frontier: Vec<(usize, usize)>,
    collector: Vec<(usize, usize)>,
    costs: Vec<Vec<i32>>,
}

impl Processor {

    pub fn update(&mut self, map: &Mutex<Vec<Vec<i32>>>, w: i32, h: i32) {

        for row in self.workspace.iter_mut() {
            for tile in row.iter_mut() {
                *tile = INFINITY;
            }
        }

        for pos in self.frontier.iter() {
            self.workspace[pos.1][pos.0] = 0;
        }

        let mut current_cost;
        let mut pos;
        let mut con;
        let mut value;
        let mut previous;
        let mut supposed;

        while self.frontier.len() != 0 {
            match self.terminator.try_recv() {
                Ok(_) => {
                    self.frontier.clear();
                    return
                },
                Err(err) => match err {
                    TryRecvError::Disconnected => {
                        self.frontier.clear();
                        return
                    },
                    _ => {}
                }
            }



            for current in self.frontier.drain(..) {
                current_cost = self.workspace[current.1][current.0];
                for d in D4.iter() {
                    pos = (current.0 as i32 + d.0, current.1 as i32 + d.1);

                    if  pos.0 < 0 || pos.1 < 0 || pos.0 == w || pos.1 == h {
                        continue
                    }

                    con = (pos.0 as usize, pos.1 as usize);

                    value = self.costs[con.1][con.0];
                    if value == -1 {
                        continue
                    }

                    previous = self.workspace[con.1][con.0];
                    supposed = current_cost + value;
                    if previous == -1 || previous > supposed {
                        self.workspace[con.1][con.0] = supposed;
                        self.collector.push(con);
                    }
                }
            }
            self.frontier.append(&mut self.collector);
        }

        map.lock().unwrap().clone_from(&self.workspace);
    }
}

#[cfg(test)]
mod tests {
    use crate::entities::pathfinder::Data;
    use std::sync::mpsc::channel;

    #[test]
    fn update_test() {
        let costs = vec![vec![1; 10]; 10];
        let chan = channel();
        let pro = Data::new(10, 10, chan.1, &costs);
        {
            pro.processor.lock().unwrap().frontier.push((0, 0));
        }
        pro.processor.lock().unwrap().update(&pro.map, 10, 10);
        for i in pro.map.lock().unwrap().iter() {
            println!("{:?}", i);
        }
    }
}