// 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(core)] use std::iter::repeat; 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(x: T, f: F) -> Iterate where F: FnMut(&T) -> T { Iterate {f: f, next: x} } struct Iterate where F: FnMut(&T) -> T { f: F, next: T } impl Iterator for Iterate where F: FnMut(&T) -> T { type Item = T; fn next(&mut self) -> Option { 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> { let mut res: Vec> = // 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 { 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 > > { 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>> = 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>>) -> 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>>) { 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. fn to_vec(raw_sol: &List) -> Vec { let mut sol = repeat('.' as u8).take(50).collect::>(); 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 form. fn print_sol(sol: &Vec) { 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, // Lexicographically maximal solution found. max: Vec } 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, 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 = 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>>, board: u64, mut i: usize, cur: List, 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>>) -> 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.iter()) { 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!(""); }