blob: 331d83b535a0cfae57b51d03fea9e8bac61462c7 [file] [log] [blame]
// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers
// Copyright (c) 2013-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
// the name of "The Computer Language Shootout Benchmarks" nor the
// names of its contributors may be used to endorse or promote
// products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
// no-pretty-expanded FIXME #15189
#![feature(iter_cmp)]
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::thread;
//
// Utilities.
//
// returns an infinite iterator of repeated applications of f to x,
// i.e. [x, f(x), f(f(x)), ...], as haskell iterate function.
fn iterate<T, F>(x: T, f: F) -> Iterate<T, F> where F: FnMut(&T) -> T {
Iterate {f: f, next: x}
}
struct Iterate<T, F> where F: FnMut(&T) -> T {
f: F,
next: T
}
impl<T, F> Iterator for Iterate<T, F> where F: FnMut(&T) -> T {
type Item = T;
fn next(&mut self) -> Option<T> {
let mut res = (self.f)(&self.next);
std::mem::swap(&mut res, &mut self.next);
Some(res)
}
}
// a linked list using borrowed next.
enum List<'a, T:'a> {
Nil,
Cons(T, &'a List<'a, T>)
}
struct ListIterator<'a, T:'a> {
cur: &'a List<'a, T>
}
impl<'a, T> List<'a, T> {
fn iter(&'a self) -> ListIterator<'a, T> {
ListIterator{cur: self}
}
}
impl<'a, T> Iterator for ListIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
match *self.cur {
List::Nil => None,
List::Cons(ref elt, next) => {
self.cur = next;
Some(elt)
}
}
}
}
//
// preprocess
//
// Takes a pieces p on the form [(y1, x1), (y2, x2), ...] and returns
// every possible transformations (the 6 rotations with their
// corresponding mirrored piece), with, as minimum coordinates, (0,
// 0). If all is false, only generate half of the possibilities (used
// to break the symmetry of the board).
fn transform(piece: Vec<(i32, i32)> , all: bool) -> Vec<Vec<(i32, i32)>> {
let mut res: Vec<Vec<(i32, i32)>> =
// rotations
iterate(piece, |rot| rot.iter().map(|&(y, x)| (x + y, -y)).collect())
.take(if all {6} else {3})
// mirror
.flat_map(|cur_piece| {
iterate(cur_piece, |mir| mir.iter().map(|&(y, x)| (x, y)).collect())
.take(2)
}).collect();
// translating to (0, 0) as minimum coordinates.
for cur_piece in &mut res {
let (dy, dx) = *cur_piece.iter().min_by(|e| *e).unwrap();
for &mut (ref mut y, ref mut x) in cur_piece {
*y -= dy; *x -= dx;
}
}
res
}
// A mask is a piece somewhere on the board. It is represented as a
// u64: for i in the first 50 bits, m[i] = 1 if the cell at (i/5, i%5)
// is occupied. m[50 + id] = 1 if the identifier of the piece is id.
// Takes a piece with minimum coordinate (0, 0) (as generated by
// transform). Returns the corresponding mask if p translated by (dy,
// dx) is on the board.
fn mask(dy: i32, dx: i32, id: usize, p: &Vec<(i32, i32)>) -> Option<u64> {
let mut m = 1 << (50 + id);
for &(y, x) in p {
let x = x + dx + (y + (dy % 2)) / 2;
if x < 0 || x > 4 {return None;}
let y = y + dy;
if y < 0 || y > 9 {return None;}
m |= 1 << (y * 5 + x) as usize;
}
Some(m)
}
// Makes every possible masks. masks[i][id] correspond to every
// possible masks for piece with identifier id with minimum coordinate
// (i/5, i%5).
fn make_masks() -> Vec<Vec<Vec<u64> > > {
let pieces = vec!(
vec!((0,0),(0,1),(0,2),(0,3),(1,3)),
vec!((0,0),(0,2),(0,3),(1,0),(1,1)),
vec!((0,0),(0,1),(0,2),(1,2),(2,1)),
vec!((0,0),(0,1),(0,2),(1,1),(2,1)),
vec!((0,0),(0,2),(1,0),(1,1),(2,1)),
vec!((0,0),(0,1),(0,2),(1,1),(1,2)),
vec!((0,0),(0,1),(1,1),(1,2),(2,1)),
vec!((0,0),(0,1),(0,2),(1,0),(1,2)),
vec!((0,0),(0,1),(0,2),(1,2),(1,3)),
vec!((0,0),(0,1),(0,2),(0,3),(1,2)));
// To break the central symmetry of the problem, every
// transformation must be taken except for one piece (piece 3
// here).
let transforms: Vec<Vec<Vec<(i32, i32)>>> =
pieces.into_iter().enumerate()
.map(|(id, p)| transform(p, id != 3))
.collect();
(0..50).map(|yx| {
transforms.iter().enumerate().map(|(id, t)| {
t.iter().filter_map(|p| mask(yx / 5, yx % 5, id, p)).collect()
}).collect()
}).collect()
}
// Check if all coordinates can be covered by an unused piece and that
// all unused piece can be placed on the board.
fn is_board_unfeasible(board: u64, masks: &Vec<Vec<Vec<u64>>>) -> bool {
let mut coverable = board;
for (i, masks_at) in masks.iter().enumerate() {
if board & 1 << i != 0 { continue; }
for (cur_id, pos_masks) in masks_at.iter().enumerate() {
if board & 1 << (50 + cur_id) != 0 { continue; }
for &cur_m in pos_masks {
if cur_m & board != 0 { continue; }
coverable |= cur_m;
// if every coordinates can be covered and every
// piece can be used.
if coverable == (1 << 60) - 1 { return false; }
}
}
if coverable & 1 << i == 0 { return true; }
}
true
}
// Filter the masks that we can prove to result to unfeasible board.
fn filter_masks(masks: &mut Vec<Vec<Vec<u64>>>) {
for i in 0..masks.len() {
for j in 0..(*masks)[i].len() {
masks[i][j] =
(*masks)[i][j].iter().cloned()
.filter(|&m| !is_board_unfeasible(m, masks))
.collect();
}
}
}
// Gets the identifier of a mask.
fn get_id(m: u64) -> u8 {
for id in 0..10 {
if m & (1 << (id + 50) as usize) != 0 {return id;}
}
panic!("{:016x} does not have a valid identifier", m);
}
// Converts a list of mask to a Vec<u8>.
fn to_vec(raw_sol: &List<u64>) -> Vec<u8> {
let mut sol = vec![b'.'; 50];
for &m in raw_sol.iter() {
let id = '0' as u8 + get_id(m);
for i in 0..50 {
if m & 1 << i != 0 {
sol[i] = id;
}
}
}
sol
}
// Prints a solution in Vec<u8> form.
fn print_sol(sol: &Vec<u8>) {
for (i, c) in sol.iter().enumerate() {
if (i) % 5 == 0 { println!(""); }
if (i + 5) % 10 == 0 { print!(" "); }
print!("{} ", *c as char);
}
println!("");
}
// The data managed during the search
struct Data {
// Number of solution found.
nb: isize,
// Lexicographically minimal solution found.
min: Vec<u8>,
// Lexicographically maximal solution found.
max: Vec<u8>
}
impl Data {
fn new() -> Data {
Data {nb: 0, min: vec!(), max: vec!()}
}
fn reduce_from(&mut self, other: Data) {
self.nb += other.nb;
let Data { min: min, max: max, ..} = other;
if min < self.min { self.min = min; }
if max > self.max { self.max = max; }
}
}
// Records a new found solution. Returns false if the search must be
// stopped.
fn handle_sol(raw_sol: &List<u64>, data: &mut Data) {
// because we break the symmetry, 2 solutions correspond to a call
// to this method: the normal solution, and the same solution in
// reverse order, i.e. the board rotated by half a turn.
data.nb += 2;
let sol1 = to_vec(raw_sol);
let sol2: Vec<u8> = sol1.iter().rev().cloned().collect();
if data.nb == 2 {
data.min = sol1.clone();
data.max = sol1.clone();
}
if sol1 < data.min {data.min = sol1;}
else if sol1 > data.max {data.max = sol1;}
if sol2 < data.min {data.min = sol2;}
else if sol2 > data.max {data.max = sol2;}
}
fn search(
masks: &Vec<Vec<Vec<u64>>>,
board: u64,
mut i: usize,
cur: List<u64>,
data: &mut Data)
{
// Search for the lesser empty coordinate.
while board & (1 << i) != 0 && i < 50 {i += 1;}
// the board is full: a solution is found.
if i >= 50 {return handle_sol(&cur, data);}
let masks_at = &masks[i];
// for every unused piece
for id in (0..10).filter(|&id| board & (1 << (id + 50)) == 0) {
// for each mask that fits on the board
for m in masks_at[id].iter().filter(|&m| board & *m == 0) {
// This check is too costly.
//if is_board_unfeasible(board | m, masks) {continue;}
search(masks, board | *m, i + 1, List::Cons(*m, &cur), data);
}
}
}
fn par_search(masks: Vec<Vec<Vec<u64>>>) -> Data {
let masks = Arc::new(masks);
let (tx, rx) = channel();
// launching the search in parallel on every masks at minimum
// coordinate (0,0)
for m in (*masks)[0].iter().flat_map(|masks_pos| masks_pos) {
let masks = masks.clone();
let tx = tx.clone();
let m = *m;
thread::spawn(move|| {
let mut data = Data::new();
search(&*masks, m, 1, List::Cons(m, &List::Nil), &mut data);
tx.send(data).unwrap();
});
}
// collecting the results
drop(tx);
let mut data = rx.recv().unwrap();
for d in rx.iter() { data.reduce_from(d); }
data
}
fn main () {
let mut masks = make_masks();
filter_masks(&mut masks);
let data = par_search(masks);
println!("{} solutions found", data.nb);
print_sol(&data.min);
print_sol(&data.max);
println!("");
}