1use crate::board::RectangularBoard;
2use rand::Rng;
3use simplesvg::{Attr, Color, Fig, Svg};
4use std::collections::{HashMap, HashSet};
5
6pub fn render_single_tiling_from_vec(boards: Vec<&RectangularBoard>) -> String {
7 let mut tile_hashmap = HashMap::new();
8
9 for i in (1..boards.len()).rev() {
10 tile_hashmap.insert(boards[i].clone(), vec![boards[i - 1].clone()]);
11 }
12
13 render_single_tiling(boards.last().unwrap(), &tile_hashmap)
14}
15
16pub fn render_single_tiling<S: ::std::hash::BuildHasher>(
17 board: &RectangularBoard,
18 tile_hashmap: &HashMap<RectangularBoard, Vec<RectangularBoard>, S>,
19) -> String {
20 let gap_size = 0.0;
22 let box_size = 50.0;
23 let padding = 10.0;
24
25 let colors = vec![
27 Color(30, 56, 136),
28 Color(71, 115, 170),
29 Color(245, 230, 99),
30 Color(255, 173, 105),
31 Color(156, 56, 72),
32 Color(124, 178, 135),
33 Color(251, 219, 136),
34 ];
35
36 let mut boxes = Vec::new();
37
38 let mut color_index = rand::thread_rng().gen_range(0, colors.len());
41 let mut current = board;
42
43 while tile_hashmap.contains_key(current) {
44 let next = rand::thread_rng().gen_range(0, tile_hashmap[current].len());
46 let next_board = tile_hashmap.get(current).unwrap().get(next).unwrap();
47
48 let mut tiled_positions = HashSet::new();
49
50 for y in 0..next_board.height {
52 for x in 0..next_board.width {
53 if next_board.board[y][x] ^ current.board[y][x] {
54 tiled_positions.insert((x, y));
56 }
57 }
58 }
59
60 for (x, y) in tiled_positions.iter() {
61 let rect = Fig::Rect(
63 (*x as f32) * (box_size + gap_size) + padding,
64 (*y as f32) * (box_size + gap_size) + padding,
65 box_size,
66 box_size,
67 )
68 .styled(Attr::default().fill(colors[color_index]));
69
70 boxes.push(rect);
71
72 enum Border {
73 Left,
74 Right,
75 Top,
76 Bottom,
77 };
78
79 let border = |x: usize, y: usize, b: Border, gray: bool| {
81 let xs = match b {
82 Border::Right => (x as f32 + 1.0) * (box_size + gap_size) + padding - gap_size,
83 _ => (x as f32) * (box_size + gap_size) + padding,
84 };
85 let ys = match b {
86 Border::Top => (y as f32 + 1.0) * (box_size + gap_size) + padding - gap_size,
87 _ => (y as f32) * (box_size + gap_size) + padding,
88 };
89 let xe = match b {
90 Border::Left => (x as f32) * (box_size + gap_size) + padding,
91 _ => (x as f32 + 1.0) * (box_size + gap_size) + padding - gap_size,
92 };
93 let ye = match b {
94 Border::Bottom => (y as f32) * (box_size + gap_size) + padding,
95 _ => (y as f32 + 1.0) * (box_size + gap_size) + padding - gap_size,
96 };
97
98 let mut b = Fig::Line(xs, ys, xe, ye);
99 b = b.styled(
100 Attr::default()
101 .stroke(if gray {
102 Color(211, 211, 211)
103 } else {
104 Color(0, 0, 0)
105 })
106 .stroke_width(0.5),
107 );
108
109 b
110 };
111
112 boxes.push(border(
114 *x,
115 *y,
116 Border::Left,
117 tiled_positions.contains(&(*x - 1, *y)),
118 ));
119 boxes.push(border(
121 *x,
122 *y,
123 Border::Right,
124 tiled_positions.contains(&(*x + 1, *y)),
125 ));
126 boxes.push(border(
128 *x,
129 *y,
130 Border::Top,
131 tiled_positions.contains(&(*x, *y + 1)),
132 ));
133 boxes.push(border(
135 *x,
136 *y,
137 Border::Bottom,
138 tiled_positions.contains(&(*x, *y - 1)),
139 ));
140 }
141
142 color_index = (color_index + 1) % colors.len();
144
145 current = next_board;
146 }
147
148 Svg(
149 vec![Fig::Multiple(boxes)],
150 (50 * board.width) as u32 + 2 * (padding as u32),
151 (50 * board.height) as u32 + 2 * (padding as u32),
152 )
153 .to_string()
154}