Add game of life
This commit is contained in:
		
							
								
								
									
										127
									
								
								src/bin/life/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/bin/life/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
use std::env::args;
 | 
			
		||||
use std::io::stdout;
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::thread::sleep;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use rand::thread_rng;
 | 
			
		||||
use rand::Rng;
 | 
			
		||||
 | 
			
		||||
use lowdim::bb2d;
 | 
			
		||||
use lowdim::p2d;
 | 
			
		||||
use lowdim::v2d;
 | 
			
		||||
use lowdim::Array2d;
 | 
			
		||||
use lowdim::BBox2d;
 | 
			
		||||
use lowdim::Point2d;
 | 
			
		||||
 | 
			
		||||
use pixelfoo::color::Color;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
enum Square {
 | 
			
		||||
    Empty,
 | 
			
		||||
    Full,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
struct Board {
 | 
			
		||||
    /// The inner bounding box that should be displayed.
 | 
			
		||||
    bbox: BBox2d,
 | 
			
		||||
    map: Array2d<i64, Square>,
 | 
			
		||||
}
 | 
			
		||||
impl Board {
 | 
			
		||||
    pub fn with(bbox: BBox2d, f: impl FnMut(Point2d) -> Square) -> Board {
 | 
			
		||||
        let new_bbox = BBox2d::from_corners(bbox.min() - v2d(1, 1), bbox.max() + v2d(1, 1));
 | 
			
		||||
        // The actual map is enlarged by one square on all sides
 | 
			
		||||
        // to avoid special cases when regarding the neighbors.
 | 
			
		||||
        // The boundary squares can be filled with some data.
 | 
			
		||||
        // For example they can stay empty or be filled randomly.
 | 
			
		||||
        let map = Array2d::with(new_bbox, f);
 | 
			
		||||
        Board { bbox, map }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bbox(&self) -> BBox2d {
 | 
			
		||||
        self.bbox
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get(&self, pos: Point2d) -> Square {
 | 
			
		||||
        self.map[pos]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn send<T: Write>(w: &mut T, board: &Board) -> std::io::Result<()> {
 | 
			
		||||
    for y in board.bbox().y_range() {
 | 
			
		||||
        for x in board.bbox().x_range() {
 | 
			
		||||
            let square = board.map[p2d(x, y)];
 | 
			
		||||
            let c = match square {
 | 
			
		||||
                Square::Empty => Color::black(),
 | 
			
		||||
                Square::Full => Color::green(),
 | 
			
		||||
            };
 | 
			
		||||
            w.write_all(&c.rgb())?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    w.flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEFAULT_ARG: usize = 10;
 | 
			
		||||
 | 
			
		||||
fn main() -> std::io::Result<()> {
 | 
			
		||||
    let args = args().collect::<Vec<_>>();
 | 
			
		||||
    eprintln!("executing {}", args[0]);
 | 
			
		||||
 | 
			
		||||
    let x_size = args[1].parse::<usize>().unwrap();
 | 
			
		||||
    let y_size = args[2].parse::<usize>().unwrap();
 | 
			
		||||
    let arg = args[3].parse::<usize>().unwrap_or(DEFAULT_ARG);
 | 
			
		||||
    eprintln!("screen size {}x{}, arg {}", x_size, y_size, arg);
 | 
			
		||||
 | 
			
		||||
    let mut rng = thread_rng();
 | 
			
		||||
    let bbox = bb2d(0..x_size as i64, 0..y_size as i64);
 | 
			
		||||
 | 
			
		||||
    let p_full = 0.25;
 | 
			
		||||
    let mut board = Board::with(bbox, |_p| {
 | 
			
		||||
        // Initialize board randomly.
 | 
			
		||||
        let r = rng.gen::<f64>();
 | 
			
		||||
        if r < p_full {
 | 
			
		||||
            Square::Full
 | 
			
		||||
        } else {
 | 
			
		||||
            Square::Empty
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let t_frame = 0.5; // s
 | 
			
		||||
    let delay = Duration::from_secs_f64(t_frame);
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let new_board = Board::with(bbox, |p| {
 | 
			
		||||
            if bbox.contains(&p) {
 | 
			
		||||
                // Use the game of life rule in the interior.
 | 
			
		||||
                let nc = p
 | 
			
		||||
                    .neighbors_l_infty()
 | 
			
		||||
                    .filter(|&np| board.get(np) == Square::Full)
 | 
			
		||||
                    .count();
 | 
			
		||||
                if nc == 3 || (nc == 2 && board.get(p) == Square::Full) {
 | 
			
		||||
                    Square::Full
 | 
			
		||||
                } else {
 | 
			
		||||
                    Square::Empty
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if p.y() < bbox.y_min() || p.y() > bbox.y_max() {
 | 
			
		||||
                    // The invisible long edge is filled randomly.
 | 
			
		||||
                    let r = rng.gen::<f64>();
 | 
			
		||||
                    if r < p_full {
 | 
			
		||||
                        Square::Full
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Square::Empty
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // The short edges are empty.
 | 
			
		||||
                    Square::Empty
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        board = new_board;
 | 
			
		||||
 | 
			
		||||
        let mut buf = Vec::with_capacity(x_size * y_size * 3);
 | 
			
		||||
        send(&mut buf, &board)?;
 | 
			
		||||
        stdout().write_all(&buf)?;
 | 
			
		||||
        stdout().flush()?;
 | 
			
		||||
        sleep(delay);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user