1use std::collections::HashSet;
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
4pub enum Direction {
5 Up,
6 Down,
7 Left,
8 Right,
9 UpLeft,
10 UpRight,
11 DownRight,
12 DownLeft,
13}
14
15#[derive(Debug, Copy, Clone)]
16pub enum Axis {
17 Vertical,
18 Horizontal,
19}
20
21impl Direction {
22 pub fn opposite(self) -> Self {
30 match self {
31 Direction::Up => Direction::Down,
32 Direction::Right => Direction::Left,
33 Direction::Down => Direction::Up,
34 Direction::Left => Direction::Right,
35 Direction::UpLeft => Direction::DownRight,
36 Direction::DownRight => Direction::UpLeft,
37 Direction::UpRight => Direction::DownLeft,
38 Direction::DownLeft => Direction::UpRight,
39 }
40 }
41
42 pub fn rotate(self) -> Self {
43 match self {
44 Direction::Up => Direction::Right,
45 Direction::Right => Direction::Down,
46 Direction::Down => Direction::Left,
47 Direction::Left => Direction::Up,
48 Direction::UpLeft => Direction::UpRight,
49 Direction::UpRight => Direction::DownRight,
50 Direction::DownRight => Direction::DownLeft,
51 Direction::DownLeft => Direction::UpLeft,
52 }
53 }
54
55 pub fn reflect(self, axis: Axis) -> Self {
56 match axis {
57 Axis::Horizontal => match self {
58 Direction::Up => Direction::Down,
59 Direction::Down => Direction::Up,
60 Direction::UpLeft => Direction::DownLeft,
61 Direction::UpRight => Direction::DownRight,
62 Direction::DownLeft => Direction::UpLeft,
63 Direction::DownRight => Direction::UpRight,
64 x => x,
65 },
66 Axis::Vertical => match self {
67 Direction::Left => Direction::Right,
68 Direction::Right => Direction::Left,
69 Direction::UpLeft => Direction::UpRight,
70 Direction::UpRight => Direction::UpLeft,
71 Direction::DownLeft => Direction::DownRight,
72 Direction::DownRight => Direction::DownLeft,
73 x => x,
74 },
75 }
76 }
77}
78
79#[derive(Clone, Debug, PartialEq, Eq, Hash)]
80pub struct Tile {
81 pub directions: Vec<Direction>,
82}
83
84impl Tile {
85 pub fn new(directions: Vec<Direction>) -> Self {
86 Tile { directions }
87 }
88
89 pub fn l_tile(length: usize) -> Self {
104 assert!(length > 0);
105
106 let mut directions = vec![Direction::Left];
107
108 for _ in 0..(length - 1) {
109 directions.push(Direction::Up);
110 }
111
112 Tile::new(directions)
113 }
114
115 pub fn box_tile() -> Self {
116 Tile::new(Vec::new())
117 }
118
119 pub fn t_tile(length: usize) -> Self {
120 assert!(length > 0);
121
122 let mut directions = Vec::new();
123
124 for _ in 0..length {
125 directions.push(Direction::Right);
126 }
127 directions.push(Direction::Up);
128 directions.push(Direction::DownRight);
129
130 for _ in 0..(length - 1) {
131 directions.push(Direction::Right);
132 }
133
134 Tile::new(directions)
135 }
136
137 pub fn rotate(&self) -> Tile {
149 Tile::new(self.directions.iter().map(|d| d.rotate()).collect())
150 }
151
152 pub fn reflect(&self, axis: Axis) -> Tile {
166 Tile::new(self.directions.iter().map(|d| d.reflect(axis)).collect())
167 }
168}
169
170#[derive(Debug, Clone)]
171pub struct TileCollection {
172 tiles: Vec<Tile>,
173 contains_single_tile: bool,
174}
175
176impl TileCollection {
177 pub fn new(tiles: Vec<Tile>) -> Self {
178 TileCollection {
179 contains_single_tile: tiles.iter().any(|b| b.directions.is_empty()),
180 tiles,
181 }
182 }
183
184 pub fn contains_single_tile(&self) -> bool {
185 self.contains_single_tile
186 }
187
188 pub fn iter<'b>(&'b self) -> Box<dyn Iterator<Item = &Tile> + 'b> {
189 Box::new(self.tiles.iter())
190 }
191}
192
193impl From<Tile> for TileCollection {
194 fn from(tile: Tile) -> Self {
195 fn symmetry_orbit(tile: Tile) -> TileCollection {
197 let mut orbit = HashSet::new();
198
199 orbit.insert(tile);
201
202 loop {
203 let current_size = orbit.len();
206
207 let mut to_insert = Vec::new();
208
209 for directions in &orbit {
210 to_insert.push(directions.rotate());
212 to_insert.push(directions.reflect(Axis::Horizontal));
214 to_insert.push(directions.reflect(Axis::Vertical));
215 }
216
217 orbit.extend(to_insert);
218
219 if orbit.len() == current_size {
220 break;
221 }
222 }
223
224 TileCollection::new(orbit.into_iter().collect())
225 }
226 symmetry_orbit(tile)
227 }
228}