15 puzzle game: Difference between revisions
Content added Content deleted
Drkameleon (talk | contribs) (added Arturo implementation) |
|||
Line 11,364: | Line 11,364: | ||
{{libheader|rand}} |
{{libheader|rand}} |
||
<syntaxhighlight lang="rust">extern crate rand; |
<syntaxhighlight lang="rust">extern crate rand; |
||
use std::collections::HashMap; |
use std::collections::HashMap; |
||
use std::fmt; |
use std::fmt; |
||
⚫ | |||
use rand::seq::SliceRandom; |
use rand::seq::SliceRandom; |
||
⚫ | |||
#[derive(Copy, Clone, PartialEq, Debug)] |
#[derive(Copy, Clone, PartialEq, Debug)] |
||
enum Cell { |
enum Cell { |
||
Line 11,376: | Line 11,376: | ||
Empty, |
Empty, |
||
} |
} |
||
#[derive(Eq, PartialEq, Hash, Debug)] |
#[derive(Eq, PartialEq, Hash, Debug)] |
||
enum Direction { |
enum Direction { |
||
Line 11,384: | Line 11,384: | ||
Right, |
Right, |
||
} |
} |
||
enum Action { |
enum Action { |
||
Move(Direction), |
Move(Direction), |
||
Quit, |
Quit, |
||
} |
} |
||
type Board = [Cell; 16]; |
type Board = [Cell; 16]; |
||
const EMPTY: Board = [Cell::Empty; 16]; |
const EMPTY: Board = [Cell::Empty; 16]; |
||
struct P15 { |
struct P15 { |
||
board: Board, |
board: Board, |
||
} |
} |
||
impl P15 { |
impl P15 { |
||
fn new() -> Self { |
fn new() -> Self { |
||
Line 11,403: | Line 11,403: | ||
*cell = Cell::Card(i); |
*cell = Cell::Card(i); |
||
} |
} |
||
let mut rng = rand::thread_rng(); |
let mut rng = rand::thread_rng(); |
||
board.shuffle(&mut rng); |
board.shuffle(&mut rng); |
||
if !Self::is_valid(board) { |
if !Self::is_valid(board) { |
||
// random swap |
// random swap |
||
let i = rng.gen_range(0 |
let i = rng.gen_range(0..16); |
||
let mut j = rng.gen_range(0 |
let mut j = rng.gen_range(0..16); |
||
while j == i { |
while j == i { |
||
j = rng.gen_range(0 |
j = rng.gen_range(0..16); |
||
} |
} |
||
board.swap(i, j); |
board.swap(i, j); |
||
} |
} |
||
Self { board } |
Self { board } |
||
} |
} |
||
fn is_valid(mut board: Board) -> bool { |
fn is_valid(mut board: Board) -> bool { |
||
// TODO: optimize |
// TODO: optimize |
||
let mut permutations = 0; |
let mut permutations = 0; |
||
let pos = board.iter().position(|&cell| cell == Cell::Empty).unwrap(); |
let pos = board.iter().position(|&cell| cell == Cell::Empty).unwrap(); |
||
if pos != 15 { |
if pos != 15 { |
||
board.swap(pos, 15); |
board.swap(pos, 15); |
||
permutations += 1; |
permutations += 1; |
||
} |
} |
||
for i in 1..16 { |
for i in 1..16 { |
||
let pos = board |
let pos = board |
||
Line 11,439: | Line 11,439: | ||
}) |
}) |
||
.unwrap(); |
.unwrap(); |
||
if pos + 1 != i { |
if pos + 1 != i { |
||
board.swap(pos, i - 1); |
board.swap(pos, i - 1); |
||
Line 11,445: | Line 11,445: | ||
} |
} |
||
} |
} |
||
permutations % 2 == 0 |
permutations % 2 == 0 |
||
} |
} |
||
fn get_empty_position(&self) -> usize { |
fn get_empty_position(&self) -> usize { |
||
self.board.iter().position(|&c| c == Cell::Empty).unwrap() |
self.board.iter().position(|&c| c == Cell::Empty).unwrap() |
||
} |
} |
||
fn get_moves(&self) -> HashMap<Direction, Cell> { |
fn get_moves(&self) -> HashMap<Direction, Cell> { |
||
let mut moves = HashMap::new(); |
let mut moves = HashMap::new(); |
||
let i = self.get_empty_position(); |
let i = self.get_empty_position(); |
||
if i > 3 { |
if i > 3 { |
||
moves.insert(Direction::Up, self.board[i - 4]); |
moves.insert(Direction::Up, self.board[i - 4]); |
||
Line 11,471: | Line 11,471: | ||
moves |
moves |
||
} |
} |
||
fn play(&mut self, direction: &Direction) { |
fn play(&mut self, direction: &Direction) { |
||
let i = self.get_empty_position(); |
let i = self.get_empty_position(); |
||
Line 11,482: | Line 11,482: | ||
}; |
}; |
||
} |
} |
||
fn is_complete(&self) -> bool { |
fn is_complete(&self) -> bool { |
||
self.board.iter().enumerate().all(|(i, &cell)| match cell { |
self.board.iter().enumerate().all(|(i, &cell)| match cell { |
||
Line 11,490: | Line 11,490: | ||
} |
} |
||
} |
} |
||
impl fmt::Display for P15 { |
impl fmt::Display for P15 { |
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||
(writeln!(f, "+----+----+----+----+"))?; |
|||
for (i, &cell) in self.board.iter().enumerate() { |
for (i, &cell) in self.board.iter().enumerate() { |
||
match cell { |
match cell { |
||
Cell::Card(value) => |
Cell::Card(value) => (write!(f, "| {value:2} "))?, |
||
Cell::Empty => |
Cell::Empty => (write!(f, "| "))?, |
||
} |
} |
||
if i % 4 == 3 { |
if i % 4 == 3 { |
||
(writeln!(f, "|"))?; |
|||
(writeln!(f, "+----+----+----+----+"))?; |
|||
} |
} |
||
} |
} |
||
Line 11,508: | Line 11,508: | ||
} |
} |
||
} |
} |
||
fn main() { |
fn main() { |
||
let mut p15 = P15::new(); |
let mut p15 = P15::new(); |
||
for turns in 1.. { |
for turns in 1.. { |
||
println!("{}" |
println!("{p15}"); |
||
match ask_action(&p15.get_moves()) { |
match ask_action(&p15.get_moves()) { |
||
Action::Move(direction) => { |
Action::Move(direction) => { |
||
Line 11,519: | Line 11,519: | ||
} |
} |
||
Action::Quit => { |
Action::Quit => { |
||
println!("Bye |
println!("Bye !"); |
||
break; |
break; |
||
} |
} |
||
} |
} |
||
if p15.is_complete() { |
if p15.is_complete() { |
||
println!("Well done |
println!("Well done ! You won in {turns} turns"); |
||
break; |
break; |
||
} |
} |
||
} |
} |
||
} |
} |
||
fn ask_action(moves: &HashMap<Direction, Cell>) -> Action { |
fn ask_action(moves: &HashMap<Direction, Cell>) -> Action { |
||
use std::io::{self, Write}; |
use std::io::{self, Write}; |
||
use Action::*; |
use Action::*; |
||
use Direction::*; |
use Direction::*; |
||
println!("Possible moves:"); |
println!("Possible moves:"); |
||
if let Some(&Cell::Card(value)) = moves.get(&Up) { |
if let Some(&Cell::Card(value)) = moves.get(&Up) { |
||
println!("\tU) {}" |
println!("\tU) {value}"); |
||
} |
} |
||
if let Some(&Cell::Card(value)) = moves.get(&Left) { |
if let Some(&Cell::Card(value)) = moves.get(&Left) { |
||
println!("\tL) {}" |
println!("\tL) {value}"); |
||
} |
} |
||
if let Some(&Cell::Card(value)) = moves.get(&Right) { |
if let Some(&Cell::Card(value)) = moves.get(&Right) { |
||
println!("\tR) {}" |
println!("\tR) {value}"); |
||
} |
} |
||
if let Some(&Cell::Card(value)) = moves.get(&Down) { |
if let Some(&Cell::Card(value)) = moves.get(&Down) { |
||
println!("\tD) {}" |
println!("\tD) {value}"); |
||
} |
} |
||
println!("\tQ) Quit"); |
println!("\tQ) Quit"); |
||
print!("Choose your move |
print!("Choose your move : "); |
||
io::stdout().flush().unwrap(); |
io::stdout().flush().unwrap(); |
||
let mut action = String::new(); |
let mut action = String::new(); |
||
io::stdin().read_line(&mut action).expect("read error"); |
io::stdin().read_line(&mut action).expect("read error"); |
||
Line 11,563: | Line 11,563: | ||
"Q" => Quit, |
"Q" => Quit, |
||
_ => { |
_ => { |
||
println!("Unknown action: {}" |
println!("Unknown action: {action}"); |
||
ask_action(moves) |
ask_action(moves) |
||
} |
} |