diff --git a/src/bin/dualmaze/main.rs b/src/bin/dualmaze/main.rs new file mode 100644 index 0000000..5879573 --- /dev/null +++ b/src/bin/dualmaze/main.rs @@ -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>); + +fn send(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::>() + }) + .collect::>(), + ) + } + 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(board: &Board, open: &mut Vec, 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::>(); + eprintln!("executing {}", args[0]); + + let x_size = args[1].parse::().unwrap(); + let y_size = args[2].parse::().unwrap(); + let arg = args[3].parse::().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); + } +}