Add dual maze.
Builds walls instead of corridors.
This commit is contained in:
		
							
								
								
									
										296
									
								
								src/bin/dualmaze/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								src/bin/dualmaze/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
			
		||||
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 pixelfoo::color::Color;
 | 
			
		||||
use pixelfoo::p2;
 | 
			
		||||
use pixelfoo::point2d::Point2d;
 | 
			
		||||
use pixelfoo::v2;
 | 
			
		||||
use pixelfoo::vec2d::Vec2d;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
enum Square {
 | 
			
		||||
    Unused,
 | 
			
		||||
    Unknown { prio: i32 },
 | 
			
		||||
    Corridor,
 | 
			
		||||
    Wall,
 | 
			
		||||
    Start,
 | 
			
		||||
    Finish,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Square {
 | 
			
		||||
    pub fn is_unknown(&self) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            Square::Unknown { .. } => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Board(Vec<Vec<Square>>);
 | 
			
		||||
 | 
			
		||||
fn send<T: Write>(w: &mut T, board: &Board) -> std::io::Result<()> {
 | 
			
		||||
    for line in &board.0 {
 | 
			
		||||
        for square in line {
 | 
			
		||||
            let c = match square {
 | 
			
		||||
                Square::Unused => Color::black(),
 | 
			
		||||
                Square::Unknown { .. } => Color::black(),
 | 
			
		||||
                Square::Corridor => Color::yellow(),
 | 
			
		||||
                Square::Wall => Color::darkblue(),
 | 
			
		||||
                Square::Start => Color::red(),
 | 
			
		||||
                Square::Finish => Color::green(),
 | 
			
		||||
            };
 | 
			
		||||
            w.write_all(&c.rgb())?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    w.flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
enum Orientation {
 | 
			
		||||
    Horizontal,
 | 
			
		||||
    Vertical,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Board {
 | 
			
		||||
    fn new(board_size: Vec2d, maze_size: Vec2d) -> Board {
 | 
			
		||||
        Board(
 | 
			
		||||
            (0..board_size.y)
 | 
			
		||||
                .map(move |y| {
 | 
			
		||||
                    (0..board_size.x)
 | 
			
		||||
                        .map(|x| {
 | 
			
		||||
                            if x < maze_size.x && y < maze_size.y {
 | 
			
		||||
                                if x == 0 || x == maze_size.x - 1 || y == 0 || y == maze_size.y - 1
 | 
			
		||||
                                {
 | 
			
		||||
                                    Square::Wall
 | 
			
		||||
                                } else if x % 2 != 0 && y % 2 != 0 {
 | 
			
		||||
                                    Square::Corridor
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    Square::Unknown { prio: 0 }
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                Square::Unused
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        .collect::<Vec<_>>()
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Vec<_>>(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    fn get(&self, pos: Point2d) -> Square {
 | 
			
		||||
        self.0[pos.y as usize][pos.x as usize]
 | 
			
		||||
    }
 | 
			
		||||
    fn set(&mut self, pos: Point2d, sq: Square) {
 | 
			
		||||
        self.0[pos.y as usize][pos.x as usize] = sq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_orientation(&mut self, desired: Orientation, pos: Point2d) {
 | 
			
		||||
        let sq = self.get(pos);
 | 
			
		||||
        let actual;
 | 
			
		||||
        match sq {
 | 
			
		||||
            Square::Unknown { .. } => {
 | 
			
		||||
                // determine wall orientation
 | 
			
		||||
                if pos.x % 2 == 0 && pos.y % 2 != 0 {
 | 
			
		||||
                    actual = Orientation::Vertical;
 | 
			
		||||
                } else if pos.x % 2 != 0 && pos.y % 2 == 0 {
 | 
			
		||||
                    actual = Orientation::Horizontal;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // not a potential wall, unused
 | 
			
		||||
                    actual = Orientation::Horizontal;
 | 
			
		||||
                }
 | 
			
		||||
                let prio = if actual == desired { -1 } else { 1 };
 | 
			
		||||
                self.set(pos, Square::Unknown { prio });
 | 
			
		||||
            }
 | 
			
		||||
            _ => (),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn set_horizontal(&mut self, pos: Point2d) {
 | 
			
		||||
        self.set_orientation(Orientation::Horizontal, pos);
 | 
			
		||||
    }
 | 
			
		||||
    fn set_vertical(&mut self, pos: Point2d) {
 | 
			
		||||
        self.set_orientation(Orientation::Vertical, pos);
 | 
			
		||||
    }
 | 
			
		||||
    fn draw_horizontal_segment(&mut self, pos: Point2d, size: Vec2d) {
 | 
			
		||||
        for x in 0..size.x {
 | 
			
		||||
            let xn = size.x - 1 - x;
 | 
			
		||||
            self.set_horizontal(pos + v2!(x, 0));
 | 
			
		||||
            for y in 1..x.min(xn).min(size.y / 2) + 1 {
 | 
			
		||||
                self.set_horizontal(pos + v2!(x, y));
 | 
			
		||||
                self.set_horizontal(pos + v2!(x, -y));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn draw_vertical_segment(&mut self, pos: Point2d, size: Vec2d) {
 | 
			
		||||
        for y in 0..size.y {
 | 
			
		||||
            let yn = size.y - 1 - y;
 | 
			
		||||
            self.set_vertical(pos + v2!(0, y));
 | 
			
		||||
            for x in 1..y.min(yn).min(size.x / 2) + 1 {
 | 
			
		||||
                self.set_vertical(pos + v2!(x, y));
 | 
			
		||||
                self.set_vertical(pos + v2!(-x, y));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn draw_7_segments(&mut self, pos: Point2d, size: Vec2d, segments: u8) {
 | 
			
		||||
        let length = size.x;
 | 
			
		||||
        let width = size.y;
 | 
			
		||||
        let delta = length + 1;
 | 
			
		||||
        let hsize = v2!(length, width);
 | 
			
		||||
        let vsize = v2!(width, length);
 | 
			
		||||
        if (segments & (1 << 0)) != 0 {
 | 
			
		||||
            self.draw_horizontal_segment(pos + v2!(1, 0), hsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 1)) != 0 {
 | 
			
		||||
            self.draw_vertical_segment(pos + v2!(delta, 1), vsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 2)) != 0 {
 | 
			
		||||
            self.draw_vertical_segment(pos + v2!(delta, delta + 1), vsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 3)) != 0 {
 | 
			
		||||
            self.draw_horizontal_segment(pos + v2!(1, 2 * delta), hsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 4)) != 0 {
 | 
			
		||||
            self.draw_vertical_segment(pos + v2!(0, 1), vsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 5)) != 0 {
 | 
			
		||||
            self.draw_vertical_segment(pos + v2!(0, delta + 1), vsize);
 | 
			
		||||
        }
 | 
			
		||||
        if (segments & (1 << 6)) != 0 {
 | 
			
		||||
            self.draw_horizontal_segment(pos + v2!(1, delta), hsize);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Move {
 | 
			
		||||
    from: Point2d,
 | 
			
		||||
    dir: Vec2d,
 | 
			
		||||
    prio: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn add_move<R>(board: &Board, open: &mut Vec<Move>, rng: &mut R, from: Point2d, dir: Vec2d)
 | 
			
		||||
where
 | 
			
		||||
    R: Rng,
 | 
			
		||||
{
 | 
			
		||||
    if let Square::Unknown { prio } = board.get(from + dir) {
 | 
			
		||||
        open.push(Move {
 | 
			
		||||
            from,
 | 
			
		||||
            dir,
 | 
			
		||||
            prio: prio * 1000 + rng.gen_range(0, 1000),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEFAULT_ARG: isize = 16;
 | 
			
		||||
 | 
			
		||||
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::<isize>().unwrap_or(DEFAULT_ARG);
 | 
			
		||||
    eprintln!("screen size {}x{}, arg {}", x_size, y_size, arg);
 | 
			
		||||
 | 
			
		||||
    let mut rng = thread_rng();
 | 
			
		||||
 | 
			
		||||
    let t_frame = 0.040; // s
 | 
			
		||||
    let delay = Duration::new(0, (1_000_000_000.0 * t_frame) as u32);
 | 
			
		||||
    let mut t_wait = 0.0; // s
 | 
			
		||||
 | 
			
		||||
    let board_size = v2!(x_size as i32, y_size as i32);
 | 
			
		||||
    // round down to odd size for maze
 | 
			
		||||
    let maze_size = v2!(
 | 
			
		||||
        (board_size.x - 1) / 2 * 2 + 1,
 | 
			
		||||
        (board_size.y - 1) / 2 * 2 + 1
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let mut board = Board(Vec::new());
 | 
			
		||||
    let mut open = Vec::new();
 | 
			
		||||
    loop {
 | 
			
		||||
        if open.is_empty() {
 | 
			
		||||
            if t_wait > 0.0 {
 | 
			
		||||
                t_wait -= t_frame;
 | 
			
		||||
            } else {
 | 
			
		||||
                t_wait = 60.0; // s
 | 
			
		||||
                board = Board::new(board_size, maze_size);
 | 
			
		||||
 | 
			
		||||
                // TODO draw stuff in prios
 | 
			
		||||
                let segment_size = v2!(11, 5);
 | 
			
		||||
                board.draw_7_segments(p2!(6, 8), segment_size, 0x06);
 | 
			
		||||
                board.draw_7_segments(p2!(24, 8), segment_size, 0x4f);
 | 
			
		||||
                board.draw_7_segments(p2!(42, 8), segment_size, 0x4f);
 | 
			
		||||
                board.draw_7_segments(p2!(60, 8), segment_size, 0x07);
 | 
			
		||||
 | 
			
		||||
                // start building walls from the border
 | 
			
		||||
                for x in (2..(maze_size.x - 2)).step_by(2) {
 | 
			
		||||
                    add_move(&board, &mut open, &mut rng, p2!(x, 0), v2!(0, 1));
 | 
			
		||||
                    add_move(
 | 
			
		||||
                        &board,
 | 
			
		||||
                        &mut open,
 | 
			
		||||
                        &mut rng,
 | 
			
		||||
                        p2!(x, maze_size.y - 1),
 | 
			
		||||
                        v2!(0, -1),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                for y in (2..(maze_size.y - 2)).step_by(2) {
 | 
			
		||||
                    add_move(&board, &mut open, &mut rng, p2!(0, y), v2!(1, 0));
 | 
			
		||||
                    add_move(
 | 
			
		||||
                        &board,
 | 
			
		||||
                        &mut open,
 | 
			
		||||
                        &mut rng,
 | 
			
		||||
                        p2!(maze_size.x - 1, y),
 | 
			
		||||
                        v2!(-1, 0),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // draw maze
 | 
			
		||||
        let work = arg.max(1);
 | 
			
		||||
        let mut count = 0;
 | 
			
		||||
        while !open.is_empty() && count < work {
 | 
			
		||||
            open.sort_unstable_by(|Move { prio: pa, .. }, Move { prio: pb, .. }| pb.cmp(pa));
 | 
			
		||||
            let Move {
 | 
			
		||||
                from: p0,
 | 
			
		||||
                dir: dir0,
 | 
			
		||||
                ..
 | 
			
		||||
            } = open.pop().unwrap();
 | 
			
		||||
 | 
			
		||||
            let p1 = p0 + dir0;
 | 
			
		||||
            let p2 = p1 + dir0;
 | 
			
		||||
            if board.get(p1).is_unknown() {
 | 
			
		||||
                board.set(p1, Square::Wall);
 | 
			
		||||
                board.set(p2, Square::Wall);
 | 
			
		||||
 | 
			
		||||
                for dir1 in Vec2d::directions() {
 | 
			
		||||
                    let p3 = p2 + dir1;
 | 
			
		||||
                    let p4 = p3 + dir1;
 | 
			
		||||
                    if board.get(p3).is_unknown() {
 | 
			
		||||
                        if board.get(p4).is_unknown() {
 | 
			
		||||
                            add_move(&board, &mut open, &mut rng, p2, dir1);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            board.set(p3, Square::Corridor);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                count += 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if open.is_empty() {
 | 
			
		||||
                board.set(p2!(1, 1), Square::Start);
 | 
			
		||||
                board.set(p2!(maze_size.x - 2, maze_size.y - 2), Square::Finish);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut buf = Vec::with_capacity((board_size.x * board_size.y * 3) as usize);
 | 
			
		||||
        send(&mut buf, &board)?;
 | 
			
		||||
        stdout().write_all(&buf)?;
 | 
			
		||||
        stdout().flush()?;
 | 
			
		||||
        sleep(delay);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user