Skip to content

Commit 89a9ce0

Browse files
committed
Add shootout meteor contest benchmark.
This implementation of the meteor contest implements: - insertion check with bit trick; - pregenetation of every feasible placement of the pieces on the board; - filtering of placement that implies unfeasible board - central symetry breaking
1 parent 314d6f6 commit 89a9ce0

File tree

1 file changed

+285
-0
lines changed

1 file changed

+285
-0
lines changed

src/test/bench/shootout-meteor.rs

+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//
12+
// Utilities.
13+
//
14+
15+
// returns an infinite iterator of repeated applications of f to x,
16+
// i.e. [x, f(x), f(f(x)), ...], as haskell iterate function.
17+
fn iterate<'a, T>(f: &'a fn(&T) -> T, x: T) -> Iterate<'a, T> {
18+
Iterate::new(f, x)
19+
}
20+
struct Iterate<'self, T> {
21+
priv f: &'self fn(&T) -> T,
22+
priv next: T
23+
}
24+
impl<'self, T> Iterate<'self, T> {
25+
fn new<'a>(f: &'a fn(&T) -> T, x: T) -> Iterate<'a, T> {
26+
Iterate {f: f, next: x}
27+
}
28+
}
29+
impl<'self, T> Iterator<T> for Iterate<'self, T> {
30+
fn next(&mut self) -> Option<T> {
31+
let mut res = (self.f)(&self.next);
32+
std::util::swap(&mut res, &mut self.next);
33+
Some(res)
34+
}
35+
}
36+
37+
// a linked list using borrowed next.
38+
enum List<'self, T> {
39+
Nil,
40+
Cons(T, &'self List<'self, T>)
41+
}
42+
struct ListIterator<'self, T> {
43+
priv cur: &'self List<'self, T>
44+
}
45+
impl<'self, T> List<'self, T> {
46+
fn iter(&'self self) -> ListIterator<'self, T> {
47+
ListIterator{cur: self}
48+
}
49+
}
50+
impl<'self, T> Iterator<&'self T> for ListIterator<'self, T> {
51+
fn next(&mut self) -> Option<&'self T> {
52+
match *self.cur {
53+
Nil => None,
54+
Cons(ref elt, next) => {
55+
self.cur = next;
56+
Some(elt)
57+
}
58+
}
59+
}
60+
}
61+
62+
//
63+
// preprocess
64+
//
65+
66+
// Takes a pieces p on the form [(y1, x1), (y2, x2), ...] and returns
67+
// every possible transformations (the 6 rotations with their
68+
// corresponding mirrored piece), with, as minimum coordinates, (0,
69+
// 0). If all is false, only generate half of the possibilities (used
70+
// to break the symetry of the board).
71+
fn transform(p: ~[(int, int)], all: bool) -> ~[~[(int, int)]] {
72+
let mut res =
73+
// rotations
74+
iterate(|p| p.iter().map(|&(y, x)| (x + y, -y)).collect(), p)
75+
.take(if all {6} else {3})
76+
// mirror
77+
.flat_map(|p| {
78+
iterate(|p| p.iter().map(|&(y, x)| (x, y)).collect(), p).take(2)
79+
}).to_owned_vec();
80+
81+
// translating to (0, 0) as minimum coordinates.
82+
for p in res.mut_iter() {
83+
let (dy, dx) = *p.iter().min_by(|e| *e).unwrap();
84+
for &(ref mut y, ref mut x) in p.mut_iter() {
85+
*y -= dy; *x -= dx;
86+
}
87+
}
88+
89+
res
90+
}
91+
92+
// A mask is a piece somewere on the board. It is represented as a
93+
// u64: for i in the first 50 bits, m[i] = 1 if the cell at (i/5, i%5)
94+
// is occuped. m[50 + id] = 1 if the identifier of the piece is id.
95+
96+
// Takes a piece with minimum coordinate (0, 0) (as generated by
97+
// transform). Returns the corresponding mask if p translated by (dy,
98+
// dx) is on the board.
99+
fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
100+
let mut m = 1 << (50 + id);
101+
for &(y, x) in p.iter() {
102+
let x = x + dx + (y + (dy % 2)) / 2;
103+
if x < 0 || x > 4 {return None;}
104+
let y = y + dy;
105+
if y < 0 || y > 9 {return None;}
106+
m |= 1 << (y * 5 + x);
107+
}
108+
Some(m)
109+
}
110+
111+
// Makes every possible masks. masks[id][i] correspond to every
112+
// possible masks for piece with identifier id with minimum coordinate
113+
// (i/5, i%5).
114+
fn make_masks() -> ~[~[~[u64]]] {
115+
let pieces = ~[
116+
~[(0,0),(0,1),(0,2),(0,3),(1,3)],
117+
~[(0,0),(0,2),(0,3),(1,0),(1,1)],
118+
~[(0,0),(0,1),(0,2),(1,2),(2,1)],
119+
~[(0,0),(0,1),(0,2),(1,1),(2,1)],
120+
~[(0,0),(0,2),(1,0),(1,1),(2,1)],
121+
~[(0,0),(0,1),(0,2),(1,1),(1,2)],
122+
~[(0,0),(0,1),(1,1),(1,2),(2,1)],
123+
~[(0,0),(0,1),(0,2),(1,0),(1,2)],
124+
~[(0,0),(0,1),(0,2),(1,2),(1,3)],
125+
~[(0,0),(0,1),(0,2),(0,3),(1,2)]];
126+
let mut res = ~[];
127+
for (id, p) in pieces.move_iter().enumerate() {
128+
// To break the central symetry of the problem, every
129+
// transformation must be taken except for one piece (piece 3
130+
// here).
131+
let trans = transform(p, id != 3);
132+
let mut cur_piece = ~[];
133+
for dy in range(0, 10) {
134+
for dx in range(0, 5) {
135+
let masks =
136+
trans.iter()
137+
.filter_map(|t| mask(dy, dx, id, *t))
138+
.collect();
139+
cur_piece.push(masks);
140+
}
141+
}
142+
res.push(cur_piece);
143+
}
144+
res
145+
}
146+
147+
// Check if all coordinates can be covered by an unused piece and that
148+
// all unused piece can be placed on the board.
149+
fn is_board_unfeasible(board: u64, masks: &[~[~[u64]]]) -> bool {
150+
let mut coverable = board;
151+
for i in range(0, 50).filter(|&i| board & 1 << i == 0) {
152+
for (cur_id, pos_masks) in masks.iter().enumerate() {
153+
if board & 1 << (50 + cur_id) != 0 {continue;}
154+
for &cur_m in pos_masks[i].iter() {
155+
if cur_m & board == 0 {coverable |= cur_m;}
156+
}
157+
}
158+
if coverable & (1 << i) == 0 {return true;}
159+
}
160+
// check if every coordinates can be covered and every piece can
161+
// be used.
162+
coverable != (1 << 60) - 1
163+
}
164+
165+
// Filter the masks that we can prove to result to unfeasible board.
166+
fn filter_masks(masks: &[~[~[u64]]]) -> ~[~[~[u64]]] {
167+
masks.iter().map(
168+
|p| p.iter().map(
169+
|p| p.iter()
170+
.map(|&m| m)
171+
.filter(|&m| !is_board_unfeasible(m, masks))
172+
.collect())
173+
.collect())
174+
.collect()
175+
}
176+
177+
// Gets the identifier of a mask.
178+
fn get_id(m: u64) -> u8 {
179+
for id in range(0, 10) {
180+
if m & (1 << (id + 50)) != 0 {return id as u8;}
181+
}
182+
fail!("{:016x} does not have a valid identifier", m);
183+
}
184+
185+
// Converts a list of mask to a ~str.
186+
fn to_utf8(raw_sol: &List<u64>) -> ~str {
187+
let mut sol: ~[u8] = std::vec::from_elem(50, '.' as u8);
188+
for &m in raw_sol.iter() {
189+
let id = get_id(m);
190+
for i in range(0, 50) {
191+
if m & 1 << i != 0 {sol[i] = '0' as u8 + id;}
192+
}
193+
}
194+
std::str::from_utf8_owned(sol)
195+
}
196+
197+
// Prints a solution in ~str form.
198+
fn print_sol(sol: &str) {
199+
for (i, c) in sol.iter().enumerate() {
200+
if (i) % 5 == 0 {println("");}
201+
if (i + 5) % 10 == 0 {print(" ");}
202+
print!("{} ", c);
203+
}
204+
println("");
205+
}
206+
207+
// The data managed during the search
208+
struct Data {
209+
// If more than stop_after is found, stop the search.
210+
stop_after: int,
211+
// Number of solution found.
212+
nb: int,
213+
// Lexicographically minimal solution found.
214+
min: ~str,
215+
// Lexicographically maximal solution found.
216+
max: ~str
217+
}
218+
219+
// Records a new found solution. Returns false if the search must be
220+
// stopped.
221+
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) -> bool {
222+
// because we break the symetry, 2 solutions correspond to a call
223+
// to this method: the normal solution, and the same solution in
224+
// reverse order, i.e. the board rotated by half a turn.
225+
data.nb += 2;
226+
let sol1 = to_utf8(raw_sol);
227+
let sol2: ~str = sol1.iter().invert().collect();
228+
229+
if data.nb == 2 {
230+
data.min = sol1.clone();
231+
data.max = sol1.clone();
232+
}
233+
234+
if sol1 < data.min {data.min = sol1.clone();}
235+
if sol2 < data.min {data.min = sol2.clone();}
236+
if sol1 > data.max {data.max = sol1;}
237+
if sol2 > data.max {data.max = sol2;}
238+
data.nb < data.stop_after
239+
}
240+
241+
// Search for every solutions. Returns false if the search was
242+
// stopped before the end.
243+
fn search(
244+
masks: &[~[~[u64]]],
245+
board: u64,
246+
mut i: int,
247+
cur: List<u64>,
248+
data: &mut Data)
249+
-> bool
250+
{
251+
// Search for the lesser empty coordinate.
252+
while board & (1 << i) != 0 && i < 50 {i += 1;}
253+
// the board is full: a solution is found.
254+
if i >= 50 {return handle_sol(&cur, data);}
255+
256+
// for every unused piece
257+
for id in range(0, 10).filter(|id| board & (1 << (id + 50)) == 0) {
258+
// for each mask that fits on the board
259+
for &m in masks[id][i].iter().filter(|&m| board & *m == 0) {
260+
// This check is too costy.
261+
//if is_board_unfeasible(board | m, masks) {continue;}
262+
if !search(masks, board | m, i + 1, Cons(m, &cur), data) {
263+
return false;
264+
}
265+
}
266+
}
267+
return true;
268+
}
269+
270+
fn main () {
271+
let args = std::os::args();
272+
let stop_after = if args.len() <= 1 {
273+
2098
274+
} else {
275+
from_str(args[1]).unwrap()
276+
};
277+
let masks = make_masks();
278+
let masks = filter_masks(masks);
279+
let mut data = Data {stop_after: stop_after, nb: 0, min: ~"", max: ~""};
280+
search(masks, 0, 0, Nil, &mut data);
281+
println!("{} solutions found", data.nb);
282+
print_sol(data.min);
283+
print_sol(data.max);
284+
println("");
285+
}

0 commit comments

Comments
 (0)