Animationen für die Pixeldecke
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
9.9 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. use std::env::args;
  2. use std::io::stdout;
  3. use std::io::Write;
  4. use std::thread::sleep;
  5. use std::time::Duration;
  6. use chrono::Local;
  7. use chrono::Timelike;
  8. use rand::thread_rng;
  9. use rand::Rng;
  10. use pixelfoo::color::Color;
  11. use pixelfoo::point2d::p2d;
  12. use pixelfoo::point2d::Point2d;
  13. use pixelfoo::vec2d::v2d;
  14. use pixelfoo::vec2d::Vec2d;
  15. #[derive(Clone, Copy, Debug, PartialEq, Eq)]
  16. enum Square {
  17. Unused,
  18. Unknown { prio: i32 },
  19. Corridor,
  20. Wall,
  21. Start,
  22. Finish,
  23. }
  24. impl Square {
  25. pub fn is_unknown(&self) -> bool {
  26. match self {
  27. Square::Unknown { .. } => true,
  28. _ => false,
  29. }
  30. }
  31. }
  32. struct Board(Vec<Vec<Square>>);
  33. fn send<T: Write>(w: &mut T, board: &Board) -> std::io::Result<()> {
  34. for line in &board.0 {
  35. for square in line {
  36. let c = match square {
  37. Square::Unused => Color::black(),
  38. Square::Unknown { prio } => {
  39. if *prio == 0 {
  40. Color::black()
  41. } else if *prio < 0 {
  42. Color::lightblue()
  43. } else {
  44. Color::darkyellow()
  45. }
  46. }
  47. Square::Corridor => Color::yellow(),
  48. Square::Wall => Color::darkblue(),
  49. Square::Start => Color::red(),
  50. Square::Finish => Color::green(),
  51. };
  52. w.write_all(&c.rgb())?;
  53. }
  54. }
  55. w.flush()
  56. }
  57. #[derive(Clone, Copy, Debug, PartialEq, Eq)]
  58. enum Orientation {
  59. Horizontal,
  60. Vertical,
  61. }
  62. impl Board {
  63. fn new(board_size: Vec2d, maze_size: Vec2d) -> Board {
  64. Board(
  65. (0..board_size.y)
  66. .map(move |y| {
  67. (0..board_size.x)
  68. .map(|x| {
  69. if x < maze_size.x && y < maze_size.y {
  70. if x == 0
  71. || x == maze_size.x - 1
  72. || y == 0
  73. || y == maze_size.y - 1
  74. || (x % 2 == 0 && y % 2 == 0)
  75. {
  76. Square::Wall
  77. } else {
  78. Square::Unknown { prio: 0 }
  79. }
  80. } else {
  81. Square::Unused
  82. }
  83. })
  84. .collect::<Vec<_>>()
  85. })
  86. .collect::<Vec<_>>(),
  87. )
  88. }
  89. fn get(&self, pos: Point2d) -> Square {
  90. self.0[pos.y as usize][pos.x as usize]
  91. }
  92. fn set(&mut self, pos: Point2d, sq: Square) {
  93. self.0[pos.y as usize][pos.x as usize] = sq;
  94. }
  95. fn set_orientation(&mut self, desired: Orientation, pos: Point2d) {
  96. let sq = self.get(pos);
  97. match sq {
  98. Square::Unknown { .. } => {
  99. let prio;
  100. if pos.x % 2 == 0 && pos.y % 2 != 0 {
  101. // horizontal corridor, vertical wall
  102. prio = if desired == Orientation::Horizontal {
  103. 10
  104. } else {
  105. -10
  106. };
  107. } else if pos.x % 2 != 0 && pos.y % 2 == 0 {
  108. // vertical corridor, horizontal wall
  109. prio = if desired == Orientation::Vertical {
  110. 10
  111. } else {
  112. -10
  113. };
  114. } else {
  115. // always corridor at the end
  116. prio = 0;
  117. }
  118. self.set(pos, Square::Unknown { prio });
  119. }
  120. _ => (),
  121. }
  122. }
  123. fn set_horizontal(&mut self, pos: Point2d) {
  124. self.set_orientation(Orientation::Horizontal, pos);
  125. }
  126. fn set_vertical(&mut self, pos: Point2d) {
  127. self.set_orientation(Orientation::Vertical, pos);
  128. }
  129. fn draw_horizontal_segment(&mut self, pos: Point2d, size: Vec2d) {
  130. for x in 0..size.x {
  131. let xn = size.x - 1 - x;
  132. self.set_horizontal(pos + v2d(x, 0));
  133. for y in 1..x.min(xn).min(size.y / 2) + 1 {
  134. self.set_horizontal(pos + v2d(x, y));
  135. self.set_horizontal(pos + v2d(x, -y));
  136. }
  137. }
  138. }
  139. fn draw_vertical_segment(&mut self, pos: Point2d, size: Vec2d) {
  140. for y in 0..size.y {
  141. let yn = size.y - 1 - y;
  142. self.set_vertical(pos + v2d(0, y));
  143. for x in 1..y.min(yn).min(size.x / 2) + 1 {
  144. self.set_vertical(pos + v2d(x, y));
  145. self.set_vertical(pos + v2d(-x, y));
  146. }
  147. }
  148. }
  149. fn draw_7_segments(&mut self, pos: Point2d, size: Vec2d, segments: u8) {
  150. let length = size.x;
  151. let width = size.y;
  152. let delta = length + 1;
  153. let hsize = v2d(length, width);
  154. let vsize = v2d(width, length);
  155. if (segments & (1 << 0)) != 0 {
  156. self.draw_horizontal_segment(pos + v2d(1, 0), hsize);
  157. }
  158. if (segments & (1 << 1)) != 0 {
  159. self.draw_vertical_segment(pos + v2d(delta, 1), vsize);
  160. }
  161. if (segments & (1 << 2)) != 0 {
  162. self.draw_vertical_segment(pos + v2d(delta, delta + 1), vsize);
  163. }
  164. if (segments & (1 << 3)) != 0 {
  165. self.draw_horizontal_segment(pos + v2d(1, 2 * delta), hsize);
  166. }
  167. if (segments & (1 << 4)) != 0 {
  168. self.draw_vertical_segment(pos + v2d(0, delta + 1), vsize);
  169. }
  170. if (segments & (1 << 5)) != 0 {
  171. self.draw_vertical_segment(pos + v2d(0, 1), vsize);
  172. }
  173. if (segments & (1 << 6)) != 0 {
  174. self.draw_horizontal_segment(pos + v2d(1, delta), hsize);
  175. }
  176. }
  177. fn draw_digit(&mut self, pos: Point2d, size: Vec2d, digit: u8) {
  178. let segment_table = vec![0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f];
  179. let segments = segment_table[digit as usize];
  180. self.draw_7_segments(pos, size, segments);
  181. }
  182. }
  183. struct Move {
  184. from: Point2d,
  185. dir: Vec2d,
  186. prio: i32,
  187. }
  188. fn add_move<R>(board: &Board, open: &mut Vec<Move>, rng: &mut R, from: Point2d, dir: Vec2d)
  189. where
  190. R: Rng,
  191. {
  192. if let Square::Unknown { prio } = board.get(from + dir) {
  193. open.push(Move {
  194. from,
  195. dir,
  196. prio: prio * 100 + rng.gen_range(0, 1000),
  197. });
  198. }
  199. }
  200. const DEFAULT_ARG: isize = 16;
  201. fn main() -> std::io::Result<()> {
  202. let args = args().collect::<Vec<_>>();
  203. eprintln!("executing {}", args[0]);
  204. let x_size = args[1].parse::<usize>().unwrap();
  205. let y_size = args[2].parse::<usize>().unwrap();
  206. let arg = args[3].parse::<isize>().unwrap_or(DEFAULT_ARG);
  207. eprintln!("screen size {}x{}, arg {}", x_size, y_size, arg);
  208. let mut rng = thread_rng();
  209. let t_frame = 0.040; // s
  210. let delay = Duration::new(0, (1_000_000_000.0 * t_frame) as u32);
  211. let board_size = v2d(x_size as i32, y_size as i32);
  212. // round down to odd size for maze
  213. let maze_size = v2d(
  214. (board_size.x - 1) / 2 * 2 + 1,
  215. (board_size.y - 1) / 2 * 2 + 1,
  216. );
  217. // pick odd position at or near the middle
  218. let maze_mid = p2d((maze_size.x - 1) / 4 * 2 + 1, (maze_size.y - 1) / 4 * 2 + 1);
  219. let mut board = Board(Vec::new());
  220. let mut open = Vec::new();
  221. let mut last_drawn_minute = 99;
  222. loop {
  223. if open.is_empty() {
  224. // get time
  225. let dt = Local::now();
  226. let h = dt.hour();
  227. let m = dt.minute();
  228. if m != last_drawn_minute {
  229. last_drawn_minute = m;
  230. board = Board::new(board_size, maze_size);
  231. // draw time in prios
  232. let segment_size = v2d(11, 5);
  233. board.draw_digit(p2d(6, 8), segment_size, (h / 10) as u8);
  234. board.draw_digit(p2d(24, 8), segment_size, (h % 10) as u8);
  235. board.draw_digit(p2d(42, 8), segment_size, (m / 10) as u8);
  236. board.draw_digit(p2d(60, 8), segment_size, (m % 10) as u8);
  237. // start in the middle
  238. board.set(maze_mid, Square::Corridor);
  239. add_move(&board, &mut open, &mut rng, maze_mid, v2d(1, 0));
  240. add_move(&board, &mut open, &mut rng, maze_mid, v2d(0, 1));
  241. add_move(&board, &mut open, &mut rng, maze_mid, v2d(-1, 0));
  242. add_move(&board, &mut open, &mut rng, maze_mid, v2d(0, -1));
  243. }
  244. }
  245. // draw maze
  246. let work = arg.max(1);
  247. let mut count = 0;
  248. while !open.is_empty() && count < work {
  249. open.sort_unstable_by(|Move { prio: pa, .. }, Move { prio: pb, .. }| pa.cmp(pb));
  250. let Move {
  251. from: p0,
  252. dir: dir0,
  253. ..
  254. } = open.pop().unwrap();
  255. let p1 = p0 + dir0;
  256. let p2 = p1 + dir0;
  257. if board.get(p1).is_unknown() {
  258. board.set(p1, Square::Corridor);
  259. board.set(p2, Square::Corridor);
  260. for dir1 in Vec2d::directions() {
  261. let p3 = p2 + dir1;
  262. let p4 = p3 + dir1;
  263. if board.get(p3).is_unknown() {
  264. if board.get(p4).is_unknown() {
  265. add_move(&board, &mut open, &mut rng, p2, dir1);
  266. } else {
  267. board.set(p3, Square::Wall);
  268. }
  269. }
  270. }
  271. count += 1;
  272. }
  273. if open.is_empty() {
  274. board.set(p2d(1, 1), Square::Start);
  275. board.set(p2d(maze_size.x - 2, maze_size.y - 2), Square::Finish);
  276. }
  277. }
  278. let mut buf = Vec::with_capacity((board_size.x * board_size.y * 3) as usize);
  279. send(&mut buf, &board)?;
  280. stdout().write_all(&buf)?;
  281. stdout().flush()?;
  282. sleep(delay);
  283. }
  284. }