Skip to content

Instantly share code, notes, and snippets.

@jimblandy
Forked from jorendorff/fifteen.rs
Created April 15, 2015 16:01
Show Gist options
  • Save jimblandy/4f389c7736d7bd52db75 to your computer and use it in GitHub Desktop.
Save jimblandy/4f389c7736d7bd52db75 to your computer and use it in GitHub Desktop.
use std::str::FromStr;
use std::fmt::Debug;
use std::io::stdin;
use std::io::stdout;
use std::io::Write;
trait Game : Clone {
type Move : Copy + FromStr;
fn start() -> Self;
fn moves(&self) -> Vec<Self::Move>;
fn apply_move(&self, Self::Move) -> Self;
fn score_finished_game(&self) -> f64; // returns >0 if last move won the game.
}
// === How to play a game, if you're a computer
fn max<T, I>(mut iter: I) -> T
where I: Iterator<Item=T>, T: PartialOrd
{
let mut champion = iter.next().expect("max: empty iterator");
for challenger in iter {
if challenger > champion {
champion = challenger;
}
}
champion
}
fn max_by<T, I, F, M>(mut iter: I, score: F) -> T
where
I: Iterator<Item=T>,
F: Fn(&T) -> M,
M: PartialOrd
{
let mut hi_value = iter.next().expect("max_by: empty iterator");
let mut hi_score = score(&hi_value);
for v in iter {
let s = score(&v);
if s > hi_score {
hi_score = s;
hi_value = v;
}
}
hi_value
}
fn best_move<G: Game>(game: &G) -> G::Move {
*max_by(game.moves().iter(), |m| score_move(game, **m))
}
fn score_move<G: Game>(game: &G, m: G::Move) -> f64 {
score_game(&game.apply_move(m))
}
fn score_game<G: Game>(game: &G) -> f64 {
let moves = game.moves();
if moves.len() == 0 {
game.score_finished_game()
} else {
-max(moves.iter().map(|m| score_move(game, *m)))
}
}
// === A layer of paint
fn input_one_of<T>(options: &Vec<T>, prompt: &str) -> std::io::Result<Option<T>>
where T: Copy + FromStr + PartialEq + Debug
{
loop {
try!(stdout().write(prompt.as_bytes()));
try!(stdout().flush());
let mut line_buf = String::new();
try!(stdin().read_line(&mut line_buf));
if line_buf.len() == 0 {
return Ok(None);
}
let line_str : &str = line_buf.as_ref();
let line = line_str.trim();
if line == "q" {
return Ok(None);
}
if line == "?" {
println!("options: {:?}", options);
continue;
}
match T::from_str(&line) {
Err(_) => {
println!("i didn't understand that");
continue;
}
Ok(m) => {
if options.contains(&m) {
return Ok(Some(m));
} else {
println!("{:?} is not an option (enter ? to show all options)", m);
}
}
}
}
}
fn play_human_vs_computer<G: Game + Debug, F>(select_move: F, start: G)
where
G::Move: PartialEq + Debug + FromStr,
F: Fn(&G) -> G::Move
{
println!("{:?}", start);
let mut board = start;
loop {
let options = board.moves();
if options.len() == 0 {
println!("game over");
let s = board.score_finished_game();
println!("{}",
if s == 0.0 {
"it's a tie"
} else if s > 0.0 {
"i win"
} else {
"you win"
});
return;
}
let your_move = match input_one_of(&options, "your turn> ").unwrap() {
None => {
return; // user typed "q" to quit
},
Some(m) => m
};
board = board.apply_move(your_move);
println!("{:?}", board);
let move_vec = board.moves();
if move_vec.len() == 0 {
println!("game over");
let s = board.score_finished_game();
println!("{}",
if s == 0.0 {
"it's a tie"
} else if s > 0.0 {
"you win"
} else {
"i win"
});
return;
}
// computer's turn
let my_move = select_move(&board);
println!("my move: {:?}", my_move);
board = board.apply_move(my_move);
println!("{:?}", board);
}
}
// === The game of Pennies
#[derive(Debug, Clone, Copy)]
struct Pennies(i32);
impl Game for Pennies {
type Move = i32;
fn start() -> Pennies {
Pennies(14)
}
fn moves(&self) -> Vec<i32> {
let Pennies(n) = *self;
(1..4).filter(|x| n - x >= 0).collect::<Vec<i32>>()
}
fn apply_move(&self, m: i32) -> Pennies {
let Pennies(n) = *self;
Pennies(n - m)
}
fn score_finished_game(&self) -> f64 {
1.0
}
}
fn main() {
play_human_vs_computer(best_move::<Pennies>, Pennies::start());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment