Skip to content

Commit fb08920

Browse files
committed
colormap: more compact representation
1 parent 931837d commit fb08920

File tree

1 file changed

+96
-63
lines changed

1 file changed

+96
-63
lines changed

src/colormap.rs

Lines changed: 96 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use crate::map::RegionId;
22
use crate::Map;
33

4-
#[derive(Debug)]
4+
#[derive(Clone, Debug, PartialEq, Eq)]
55
pub struct ColorMap {
6-
// possible colors by regionid. A color is represented as a 4 bit bitmask.
6+
// possible colors by regionid. A color is represented as a 4 bit bitmask and each element
7+
// contains the possible colors for 2 regions.
78
possible_colors: Vec<u8>,
9+
10+
// number of regions of this colormap.
11+
regions: usize,
812
}
913

1014
#[derive(Debug, Clone, Copy)]
@@ -16,6 +20,12 @@ pub enum Color {
1620
C4 = 1 << 3,
1721
}
1822

23+
enum SolutionState {
24+
CannotSolve,
25+
Solved,
26+
Unknown,
27+
}
28+
1929
impl ColorMap {
2030
pub fn color(m: &Map) -> Option<ColorMap> {
2131
Self::all_possible_colorings(m).next()
@@ -26,7 +36,8 @@ impl ColorMap {
2636
}
2737

2838
pub fn color_of_region(&self, rid: RegionId) -> Color {
29-
let c = self.possible_colors[rid];
39+
let c = self.at(rid);
40+
3041
if c == Color::C1 as u8 {
3142
return Color::C1;
3243
}
@@ -50,29 +61,94 @@ impl ColorMap {
5061
unreachable!()
5162
}
5263
}
64+
65+
fn at(&self, rid: RegionId) -> u8 {
66+
assert!(rid < self.regions);
67+
68+
let pcs = self.possible_colors[rid / 2];
69+
if rid & 1 != 0 {
70+
pcs >> 4
71+
} else {
72+
pcs & 0xf
73+
}
74+
}
75+
76+
fn set(&mut self, rid: RegionId, v: u8) {
77+
assert!(rid < self.regions);
78+
79+
let old = self.possible_colors[rid / 2];
80+
let pcs = if rid & 1 != 0 {
81+
(v << 4) | (old & 0xf)
82+
} else {
83+
(old & 0xf0) | (v & 0xf)
84+
};
85+
86+
self.possible_colors[rid / 2] = pcs;
87+
}
88+
89+
fn remove_conflicts(&mut self, map: &Map) -> SolutionState {
90+
loop {
91+
// first find regions with a single possible colors and remove that color from its
92+
// neighbors until no regions change its respective colors. If any of the regions cannot be
93+
// colored then this map cannot be colored. On the other hand, if all the regions have a
94+
// single possible color then that's the solution.
95+
let mut stalled = true;
96+
let mut solved = true;
97+
98+
for rid in 0..self.regions {
99+
let c = self.at(rid);
100+
if c == 0 {
101+
return SolutionState::CannotSolve;
102+
}
103+
104+
if c.count_ones() != 1 {
105+
solved = false;
106+
continue;
107+
}
108+
109+
for &neigh in &map.regions[rid].neighbors {
110+
let old = self.at(neigh);
111+
let new = old & !c;
112+
113+
self.set(neigh, new);
114+
if old != new {
115+
stalled = false;
116+
solved = false;
117+
}
118+
}
119+
}
120+
121+
if stalled {
122+
break if solved {
123+
SolutionState::Solved
124+
} else {
125+
SolutionState::Unknown
126+
};
127+
}
128+
}
129+
}
53130
}
54131

55132
#[derive(Debug)]
56133
struct SolutionIter<'m> {
57-
stack: Vec<Vec<u8>>,
134+
stack: Vec<ColorMap>,
58135
map: &'m Map,
59136
}
60137

61138
impl<'m> SolutionIter<'m> {
62139
fn new(map: &'m Map) -> Self {
140+
let cm = vec![0xff; map.regions.len() / 2 + (map.regions.len() & 1)];
141+
63142
SolutionIter {
64143
map,
65-
stack: vec![vec![0xf; map.regions.len()]],
144+
stack: vec![ColorMap {
145+
possible_colors: cm,
146+
regions: map.regions.len(),
147+
}],
66148
}
67149
}
68150
}
69151

70-
enum SolutionState {
71-
CannotSolve,
72-
Solved,
73-
Unknown,
74-
}
75-
76152
impl Iterator for SolutionIter<'_> {
77153
type Item = ColorMap;
78154

@@ -81,19 +157,17 @@ impl Iterator for SolutionIter<'_> {
81157
let possible_colors_len =
82158
|pc: u8| ((pc >> 3) & 1) + ((pc >> 2) & 1) + ((pc >> 1) & 1) + (pc & 1);
83159

84-
while let Some(mut possible_colors) = self.stack.pop() {
85-
let state = remove_conflicts(&mut possible_colors, &self.map);
160+
while let Some(mut color_map) = self.stack.pop() {
161+
let state = color_map.remove_conflicts(self.map);
86162

87163
match state {
88-
SolutionState::Solved => return Some(ColorMap { possible_colors }),
164+
SolutionState::Solved => return Some(color_map),
89165
SolutionState::CannotSolve => continue,
90166
SolutionState::Unknown => {
91167
// pick the region with the smallest amount of possible colors to choose from so that we
92168
// have to explore less space
93-
let (candidate_i, _) = possible_colors
94-
.iter()
95-
.enumerate()
96-
.map(|(i, &pc)| (i, possible_colors_len(pc)))
169+
let (candidate_rid, _) = (0..color_map.regions)
170+
.map(|rid| (rid, possible_colors_len(color_map.at(rid))))
97171
.filter(|(_, pcl)| {
98172
// regions that have a single possible color are fixed and cannot be
99173
// changed, aka they're not candidates
@@ -102,16 +176,16 @@ impl Iterator for SolutionIter<'_> {
102176
.min_by_key(|(_, pcl)| *pcl)?;
103177

104178
// try all the possible colors for the candidate and recursively find a solution
105-
let pcs = possible_colors[candidate_i];
179+
let pcs = color_map.at(candidate_rid);
106180
self.stack.extend(
107181
[Color::C1, Color::C2, Color::C3, Color::C4]
108182
.iter()
109183
.rev()
110184
.filter(|&&c| has_color(pcs, c))
111185
.map(|&c| {
112-
let mut new_possible_colors = possible_colors.clone();
113-
new_possible_colors[candidate_i] = c as u8;
114-
new_possible_colors
186+
let mut new_color_map = color_map.clone();
187+
new_color_map.set(candidate_rid, c as u8);
188+
new_color_map
115189
}),
116190
);
117191
}
@@ -121,44 +195,3 @@ impl Iterator for SolutionIter<'_> {
121195
None
122196
}
123197
}
124-
125-
fn remove_conflicts(possible_colors: &mut [u8], map: &Map) -> SolutionState {
126-
loop {
127-
// first find regions with a single possible colors and remove that color from its
128-
// neighbors until no regions change its respective colors. If any of the regions cannot be
129-
// colored then this map cannot be colored. On the other hand, if all the regions have a
130-
// single possible color then that's the solution.
131-
let mut stalled = true;
132-
let mut solved = true;
133-
134-
for rid in 0..possible_colors.len() {
135-
let c = possible_colors[rid];
136-
if c == 0 {
137-
return SolutionState::CannotSolve;
138-
}
139-
140-
if c.count_ones() != 1 {
141-
solved = false;
142-
continue;
143-
}
144-
145-
for &neigh in &map.regions[rid].neighbors {
146-
let old = possible_colors[neigh];
147-
possible_colors[neigh] &= !c;
148-
149-
if old != possible_colors[neigh] {
150-
stalled = false;
151-
solved = false;
152-
}
153-
}
154-
}
155-
156-
if stalled {
157-
break if solved {
158-
SolutionState::Solved
159-
} else {
160-
SolutionState::Unknown
161-
};
162-
}
163-
}
164-
}

0 commit comments

Comments
 (0)