Skip to content

Commit 321a54d

Browse files
committed
Refactored sudoku benchmark to use traits and added some tests
1 parent 5a77a10 commit 321a54d

File tree

1 file changed

+201
-106
lines changed

1 file changed

+201
-106
lines changed

src/test/bench/sudoku.rs

+201-106
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313
extern mod std;
1414

15-
use std::bitv;
1615
use core::io::{ReaderUtil, WriterUtil};
1716
use core::io;
17+
use core::unstable::intrinsics::cttz16;
1818

1919
// Computes a single solution to a given 9x9 sudoku
2020
//
@@ -35,146 +35,241 @@ use core::io;
3535
// internal type of sudoku grids
3636
type grid = ~[~[u8]];
3737

38-
// exported type of sudoku grids
39-
pub enum grid_t { grid_ctor(grid), }
40-
41-
// read a sudoku problem from file f
42-
pub fn read_grid(f: @io::Reader) -> grid_t {
43-
fail_unless!(f.read_line() == ~"9,9"); /* assert first line is exactly "9,9" */
44-
45-
let mut g = vec::from_fn(10u, {|_i|
46-
vec::from_elem(10u, 0 as u8)
47-
});
48-
while !f.eof() {
49-
let comps = str::split_char(str::trim(f.read_line()), ',');
50-
if vec::len(comps) >= 3u {
51-
let row = uint::from_str(comps[0]).get() as u8;
52-
let col = uint::from_str(comps[1]).get() as u8;
53-
g[row][col] = uint::from_str(comps[2]).get() as u8;
38+
struct Sudoku {
39+
grid: grid
40+
}
41+
42+
pub impl Sudoku {
43+
static pub fn new(g: grid) -> Sudoku {
44+
return Sudoku { grid: g }
45+
}
46+
47+
static pub fn from_vec(vec: &[[u8 * 9] * 9]) -> Sudoku {
48+
let mut g = do vec::from_fn(9u) |i| {
49+
do vec::from_fn(9u) |j| { vec[i][j] }
50+
};
51+
return Sudoku::new(g)
52+
}
53+
54+
pub fn equal(&self, other: &Sudoku) -> bool {
55+
for u8::range(0u8, 9u8) |row| {
56+
for u8::range(0u8, 9u8) |col| {
57+
if self.grid[row][col] != other.grid[row][col] {
58+
return false;
59+
}
60+
}
61+
}
62+
return true;
63+
}
64+
65+
static pub fn read(reader: @io::Reader) -> Sudoku {
66+
fail_unless!(reader.read_line() == ~"9,9"); /* assert first line is exactly "9,9" */
67+
68+
let mut g = vec::from_fn(10u, { |_i| ~[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] });
69+
while !reader.eof() {
70+
let comps = str::split_char(str::trim(reader.read_line()), ',');
71+
if vec::len(comps) == 3u {
72+
let row = uint::from_str(comps[0]).get() as u8;
73+
let col = uint::from_str(comps[1]).get() as u8;
74+
g[row][col] = uint::from_str(comps[2]).get() as u8;
75+
}
76+
else {
77+
fail!(~"Invalid sudoku file");
78+
}
79+
}
80+
return Sudoku::new(g)
81+
}
82+
83+
pub fn write(&self, writer: @io::Writer) {
84+
for u8::range(0u8, 9u8) |row| {
85+
writer.write_str(fmt!("%u", self.grid[row][0] as uint));
86+
for u8::range(1u8, 9u8) |col| {
87+
writer.write_str(fmt!(" %u", self.grid[row][col] as uint));
88+
}
89+
writer.write_char('\n');
90+
}
91+
}
92+
93+
// solve sudoku grid
94+
pub fn solve(&mut self) {
95+
let mut work: ~[(u8, u8)] = ~[]; /* queue of uncolored fields */
96+
for u8::range(0u8, 9u8) |row| {
97+
for u8::range(0u8, 9u8) |col| {
98+
let color = self.grid[row][col];
99+
if color == 0u8 { work += ~[(row, col)]; }
100+
}
101+
}
102+
103+
let mut ptr = 0u;
104+
let end = vec::len(work);
105+
while (ptr < end) {
106+
let (row, col) = work[ptr];
107+
// is there another color to try?
108+
if self.next_color(row, col, self.grid[row][col] + (1 as u8)) {
109+
// yes: advance work list
110+
ptr = ptr + 1u;
111+
} else {
112+
// no: redo this field aft recoloring pred; unless there is none
113+
if ptr == 0u { fail!(~"No solution found for this sudoku"); }
114+
ptr = ptr - 1u;
115+
}
54116
}
55117
}
56-
return grid_ctor(g);
57-
}
58118

59-
// solve sudoku grid
60-
pub fn solve_grid(g: grid_t) {
61-
fn next_color(mut g: grid, row: u8, col: u8, start_color: u8) -> bool {
119+
fn next_color(&mut self, row: u8, col: u8, start_color: u8) -> bool {
62120
if start_color < 10u8 {
63121
// colors not yet used
64-
let mut avail = bitv::Bitv::new(10u, false);
65-
for u8::range(start_color, 10u8) |color| {
66-
avail.set(color as uint, true);
67-
}
122+
let mut avail = ~Colors::new(start_color);
68123

69124
// drop colors already in use in neighbourhood
70-
drop_colors(copy g, copy avail, row, col);
125+
self.drop_colors(avail, row, col);
71126

72127
// find first remaining color that is available
73-
for uint::range(1u, 10u) |i| {
74-
if avail.get(i) {
75-
g[row][col] = i as u8;
76-
return true;
77-
}
78-
};
128+
let next = avail.next();
129+
self.grid[row][col] = next;
130+
return 0u8 != next;
79131
}
80-
g[row][col] = 0u8;
132+
self.grid[row][col] = 0u8;
81133
return false;
82134
}
83135

84136
// find colors available in neighbourhood of (row, col)
85-
fn drop_colors(g: grid, avail: bitv::Bitv, row: u8, col: u8) {
86-
fn drop_color(g: grid, mut colors: bitv::Bitv, row: u8, col: u8) {
87-
let color = g[row][col];
88-
if color != 0u8 { colors.set(color as uint, false); }
89-
}
90-
91-
let it = |a,b| drop_color(copy g, copy avail, a, b);
92-
137+
fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) {
93138
for u8::range(0u8, 9u8) |idx| {
94-
it(idx, col); /* check same column fields */
95-
it(row, idx); /* check same row fields */
139+
avail.remove(self.grid[idx][col]); /* check same column fields */
140+
avail.remove(self.grid[row][idx]); /* check same row fields */
96141
}
97142

98143
// check same block fields
99144
let row0 = (row / 3u8) * 3u8;
100145
let col0 = (col / 3u8) * 3u8;
101146
for u8::range(row0, row0 + 3u8) |alt_row| {
102-
for u8::range(col0, col0 + 3u8) |alt_col| { it(alt_row, alt_col); }
147+
for u8::range(col0, col0 + 3u8) |alt_col| { avail.remove(self.grid[alt_row][alt_col]); }
103148
}
104149
}
150+
}
151+
152+
// Stores available colors as simple bitfield, bit 0 is always unset
153+
struct Colors(u16);
154+
155+
const heads: u16 = (1u16 << 10) - 1; /* bits 9..0 */
156+
157+
impl Colors {
158+
static fn new(start_color: u8) -> Colors {
159+
// Sets bits 9..start_color
160+
let tails = !0u16 << start_color;
161+
return Colors(heads & tails);
162+
}
105163

106-
let mut work: ~[(u8, u8)] = ~[]; /* queue of uncolored fields */
107-
for u8::range(0u8, 9u8) |row| {
108-
for u8::range(0u8, 9u8) |col| {
109-
let color = (*g)[row][col];
110-
if color == 0u8 { work += ~[(row, col)]; }
164+
fn next(&self) -> u8 {
165+
let val = **self & heads;
166+
if (0u16 == val) {
167+
return 0u8;
168+
}
169+
else
170+
{
171+
return cttz16(val as i16) as u8;
111172
}
112173
}
113174

114-
let mut ptr = 0u;
115-
let end = vec::len(work);
116-
while (ptr < end) {
117-
let (row, col) = work[ptr];
118-
// is there another color to try?
119-
if next_color(copy *g, row, col, (*g)[row][col] + (1 as u8)) {
120-
// yes: advance work list
121-
ptr = ptr + 1u;
122-
} else {
123-
// no: redo this field aft recoloring pred; unless there is none
124-
if ptr == 0u { fail!(~"No solution found for this sudoku"); }
125-
ptr = ptr - 1u;
175+
fn remove(&mut self, color: u8) {
176+
if color != 0u8 {
177+
let val = **self;
178+
let mask = !(1u16 << color);
179+
*self = Colors(val & mask);
126180
}
127181
}
128182
}
129183

130-
pub fn write_grid(f: @io::Writer, g: grid_t) {
131-
for u8::range(0u8, 9u8) |row| {
132-
f.write_str(fmt!("%u", (*g)[row][0] as uint));
133-
for u8::range(1u8, 9u8) |col| {
134-
f.write_str(fmt!(" %u", (*g)[row][col] as uint));
135-
}
136-
f.write_char('\n');
137-
}
184+
const default_sudoku: [[u8 * 9] * 9] = [
185+
/* 0 1 2 3 4 5 6 7 8 */
186+
/* 0 */ [0u8, 4u8, 0u8, 6u8, 0u8, 0u8, 0u8, 3u8, 2u8],
187+
/* 1 */ [0u8, 0u8, 8u8, 0u8, 2u8, 0u8, 0u8, 0u8, 0u8],
188+
/* 2 */ [7u8, 0u8, 0u8, 8u8, 0u8, 0u8, 0u8, 0u8, 0u8],
189+
/* 3 */ [0u8, 0u8, 0u8, 5u8, 0u8, 0u8, 0u8, 0u8, 0u8],
190+
/* 4 */ [0u8, 5u8, 0u8, 0u8, 0u8, 3u8, 6u8, 0u8, 0u8],
191+
/* 5 */ [6u8, 8u8, 0u8, 0u8, 0u8, 0u8, 0u8, 9u8, 0u8],
192+
/* 6 */ [0u8, 9u8, 5u8, 0u8, 0u8, 6u8, 0u8, 7u8, 0u8],
193+
/* 7 */ [0u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 6u8, 0u8],
194+
/* 8 */ [4u8, 0u8, 0u8, 0u8, 0u8, 7u8, 2u8, 0u8, 3u8]
195+
];
196+
197+
#[cfg(test)]
198+
const default_solution: [[u8 * 9] * 9] = [
199+
/* 0 1 2 3 4 5 6 7 8 */
200+
/* 0 */ [1u8, 4u8, 9u8, 6u8, 7u8, 5u8, 8u8, 3u8, 2u8],
201+
/* 1 */ [5u8, 3u8, 8u8, 1u8, 2u8, 9u8, 7u8, 4u8, 6u8],
202+
/* 2 */ [7u8, 2u8, 6u8, 8u8, 3u8, 4u8, 1u8, 5u8, 9u8],
203+
/* 3 */ [9u8, 1u8, 4u8, 5u8, 6u8, 8u8, 3u8, 2u8, 7u8],
204+
/* 4 */ [2u8, 5u8, 7u8, 4u8, 9u8, 3u8, 6u8, 1u8, 8u8],
205+
/* 5 */ [6u8, 8u8, 3u8, 7u8, 1u8, 2u8, 5u8, 9u8, 4u8],
206+
/* 6 */ [3u8, 9u8, 5u8, 2u8, 8u8, 6u8, 4u8, 7u8, 1u8],
207+
/* 7 */ [8u8, 7u8, 2u8, 3u8, 4u8, 1u8, 9u8, 6u8, 5u8],
208+
/* 8 */ [4u8, 6u8, 1u8, 9u8, 5u8, 7u8, 2u8, 8u8, 3u8]
209+
];
210+
211+
#[test]
212+
fn colors_new_works() {
213+
fail_unless!(*Colors::new(1) == 1022u16);
214+
fail_unless!(*Colors::new(2) == 1020u16);
215+
fail_unless!(*Colors::new(3) == 1016u16);
216+
fail_unless!(*Colors::new(4) == 1008u16);
217+
fail_unless!(*Colors::new(5) == 992u16);
218+
fail_unless!(*Colors::new(6) == 960u16);
219+
fail_unless!(*Colors::new(7) == 896u16);
220+
fail_unless!(*Colors::new(8) == 768u16);
221+
fail_unless!(*Colors::new(9) == 512u16);
222+
}
223+
224+
#[test]
225+
fn colors_next_works() {
226+
fail_unless!(Colors(0).next() == 0u8);
227+
fail_unless!(Colors(2).next() == 1u8);
228+
fail_unless!(Colors(4).next() == 2u8);
229+
fail_unless!(Colors(8).next() == 3u8);
230+
fail_unless!(Colors(16).next() == 4u8);
231+
fail_unless!(Colors(32).next() == 5u8);
232+
fail_unless!(Colors(64).next() == 6u8);
233+
fail_unless!(Colors(128).next() == 7u8);
234+
fail_unless!(Colors(256).next() == 8u8);
235+
fail_unless!(Colors(512).next() == 9u8);
236+
fail_unless!(Colors(1024).next() == 0u8);
237+
}
238+
239+
#[test]
240+
fn colors_remove_works() {
241+
// GIVEN
242+
let mut colors = Colors::new(1);
243+
244+
// WHEN
245+
colors.remove(1);
246+
247+
// THEN
248+
fail_unless!(colors.next() == 2u8);
249+
}
250+
251+
#[test]
252+
fn check_default_sudoku_solution() {
253+
// GIVEN
254+
let mut sudoku = Sudoku::from_vec(&default_sudoku);
255+
let solution = Sudoku::from_vec(&default_solution);
256+
257+
// WHEN
258+
sudoku.solve();
259+
260+
// THEN
261+
fail_unless!(sudoku.equal(&solution));
138262
}
139263

140264
fn main() {
141-
let args = os::args();
142-
let grid = if vec::len(args) == 1u {
143-
// FIXME create sudoku inline since nested vec consts dont work yet
144-
// (#3733)
145-
let mut g = vec::from_fn(10u, |_i| {
146-
vec::from_elem(10u, 0 as u8)
147-
});
148-
g[0][1] = 4u8;
149-
g[0][3] = 6u8;
150-
g[0][7] = 3u8;
151-
g[0][8] = 2u8;
152-
g[1][2] = 8u8;
153-
g[1][4] = 2u8;
154-
g[2][0] = 7u8;
155-
g[2][3] = 8u8;
156-
g[3][3] = 5u8;
157-
g[4][1] = 5u8;
158-
g[4][5] = 3u8;
159-
g[4][6] = 6u8;
160-
g[5][0] = 6u8;
161-
g[5][1] = 8u8;
162-
g[5][7] = 9u8;
163-
g[6][1] = 9u8;
164-
g[6][2] = 5u8;
165-
g[6][5] = 6u8;
166-
g[6][7] = 7u8;
167-
g[7][4] = 4u8;
168-
g[7][7] = 6u8;
169-
g[8][0] = 4u8;
170-
g[8][5] = 7u8;
171-
g[8][6] = 2u8;
172-
g[8][8] = 3u8;
173-
grid_ctor(g)
265+
let args = os::args();
266+
let use_default = vec::len(args) == 1u;
267+
let mut sudoku = if use_default {
268+
Sudoku::from_vec(&default_sudoku)
174269
} else {
175-
read_grid(io::stdin())
270+
Sudoku::read(io::stdin())
176271
};
177-
solve_grid(copy grid);
178-
write_grid(io::stdout(), grid);
272+
sudoku.solve();
273+
sudoku.write(io::stdout());
179274
}
180275

0 commit comments

Comments
 (0)