Skip to content
Browse files

Removal of RUSThello from Redox while moving to games-for-redox.

1 parent 213eee1 commit 8ab8e3a2955dd5f42788990981666329d17f9f14 @EGhiorzi EGhiorzi committed
View
7 Makefile
@@ -133,9 +133,6 @@ help:
all: $(BUILD)/harddrive.bin
-filesystem/apps/rusthello/main.bin: filesystem/apps/rusthello/src/main.rs filesystem/apps/rusthello/src/*.rs filesystem/apps/rusthello/src/*/*.rs $(BUILD)/crt0.o $(BUILD)/libstd.rlib
- $(RUSTC) $(RUSTCFLAGS) --crate-type bin -o $@ $<
-
filesystem/apps/sodium/main.bin: filesystem/apps/sodium/src/main.rs filesystem/apps/sodium/src/*.rs $(BUILD)/libstd.rlib $(BUILD)/liborbclient.rlib
$(RUSTC) $(RUSTCFLAGS) --crate-type bin -o $@ $< --cfg 'feature="orbital"'
@@ -153,7 +150,6 @@ apps: filesystem/apps/calculator/main.bin \
filesystem/apps/file_manager/main.bin \
filesystem/apps/orbtk/main.bin \
filesystem/apps/player/main.bin \
- filesystem/apps/rusthello/main.bin \
filesystem/apps/sodium/main.bin \
filesystem/apps/terminal/main.bin \
filesystem/apps/viewer/main.bin
@@ -228,7 +224,8 @@ filesystem/bin/%: crates/games/src/%/main.rs crates/games/src/%/*.rs $(BUILD)/cr
games: \
filesystem/bin/ice \
- filesystem/bin/minesweeper
+ filesystem/bin/minesweeper \
+ filesystem/bin/rusthello \
filesystem/bin/%: crates/%/main.rs crates/%/*.rs $(BUILD)/crt0.o $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
View
4 filesystem/apps/rusthello/_REDOX
@@ -1,4 +0,0 @@
-name=RUSThello
-icon=file:/ui/apps/rusthello.bmp
-author=Enrico Ghiorzi
-description=A simple Reversi game, written in Rust with love
View
299 filesystem/apps/rusthello/interface.rs~
@@ -1,299 +0,0 @@
-// This module provides interface functionalities and manages all the input/output part of the program
-
-use std::cmp::Ordering;
-use std::io::{self, Write};
-use players;
-use reversi;
-
-pub enum UserCommand {
- NewGame,
- NewPlayer(players::Player),
- Move(usize, usize),
- Help,
- Undo,
- Quit,
-}
-
-pub const INTRO: &'static str =
-"\n\n
- RUSThello
- ● ○ ● ○ ●
- a simple Reversi game
- written in Rust with love
- Redox Edition
- v 1.1.0\n\n";
-
-pub const MAIN_MENU: &'static str =
-"\nMain Menu:
- n - New match
- h - Help
- q - Quit RUSThello";
-
- pub const NEW_PLAYER_MENU: &'static str =
- "\nChoose a player:
- hp - Human Player
- ai - Artificial Intelligence
- q - Quit match";
-
-pub const COMMANDS_INFO: &'static str =
-"\nStarting new game…
-Type a cell's coordinates to place your disk there. Exaple: \"c4\"
-Type 'help' or 'h' instead of a move to display help message.
-Type 'undo' or 'u' instead of a move to undo last move.
-Type 'quit' or 'q' instead of a move to abandon the game.";
-
-pub const HELP: &'static str = "\
-\n\n\n\tHOW TO PLAY REVERSI:\n
-Reversi is a board game where two players compete against each other. \
-The game is played on a 8x8 board, just like chess but for the squares’ colour which is always green. \
-There are 64 identical pieces called disks, which are white on one side and black on the other. \
-A player is Light, using disks’ white side, and the other one is Dark, using disks' black side. \
-The game starts with four disks already placed at the centre of the board, two for each side. \
-Dark moves first.\n
-Let’s say it’s Dark’s turn, for simplicity's sake, as for Light the rules are just the same. \
-Dark has to place a disk in a free square of the board, with the black side facing up. \
-Whenever the newly placed black disk and any other previously placed black disk enclose a sequence of white disks (horizontal, vertical or diagonal and of any length), all of those flip and turn black. \
-It is mandatory to place the new disk such that at least a white disk is flipped, otherwise the move is not valid.\n
-Usually players’ turn alternate, passing from one to the other. \
-When a player cannot play any legal move, the turn goes back to the other player, thus allowing the same player to play consecutive turns. \
-When neither player can play a legal move, the game ends. \
-In particular, this is true whenever the board has been completely filled up (for a total of 60 moves), but games happen sometimes to end before that, leaving empty squares on the board.\n
-When the game ends, the player with more disks turned to its side wins. \
-Ties are possible as well, if both player have the same number of disks.\n\n\n
-\tHOW TO USE RUSThello:\n
-To play RUSThello you first have to choose who is playing on each side, Dark and Light. \
-You can choose among human players or AIs of various strength. \
-Choose human for both players and challenge a friend, or test your skills against an AI, or even relax and watch as two AIs compete with each other: all matches are possible!\n
-As a human player, you move by entering the coordinates (a letter and a number) of the square you want to place your disk on, e.g. all of 'c4', 'C4', '4c' and '4C' are valid and equivalent coordinates. \
-For your ease of use, all legal moves are marked on the board with a *.\n
-Furthermore, on your turn you can also input special commands: 'undo' to undo your last move (and yes, you can 'undo' as many times as you like) and 'quit' to quit the game.\n\n\n
-\tCREDITS:\n
-RUSThello v. 1.1.0 Redox Edition
-by Enrico Ghiorzi, with the invaluable help of the Redox community
-Copyright (c) 2015 by Enrico Ghiorzi
-Released under the MIT license\n\n\n";
-
-
-pub fn input_main_menu() -> UserCommand {
-
- loop {
- print!("Insert input: ");
- match get_user_command() {
- Some(UserCommand::NewGame) => return UserCommand::NewGame,
- Some(UserCommand::Help) => return UserCommand::Help,
- Some(UserCommand::Quit) => {
- println!("\nGoodbye!\n\n\n");
- return UserCommand::Quit;
- }
- _ => println!("This is not a valid command!"),
- }
- }
-}
-
-pub fn new_player(side: reversi::Disk) -> Option<players::Player> {
- loop {
- match side {
- reversi::Disk::Light => print!("● Light player: "),
- reversi::Disk::Dark => print!("○ Dark player: "),
- }
- match get_user_command() {
- Some(UserCommand::NewPlayer(player)) => return Some(player),
- Some(UserCommand::Quit) => return None,
- _ => println!("This is not a valid command!"),
- }
- }
-}
-
-/// It gets an input from the user and tries to parse it, then returns a Option<UserCommand>`.
-/// If the input is recognized as a legit command, it returns the relative `Option::Some(UserCommand)`.
-/// If the input is not recognized as a legit command, it returns a `Option::None`.
-pub fn get_user_command() -> Option<UserCommand> {
-
- // Read the input
- let _ = io::stdout().flush();
-
- let mut input = String::new();
-
- io::stdin().read_line(&mut input)
- .ok()
- .expect("failed to read line");
-
- let input = input.trim().to_lowercase();
-
- match &*input {
- "hp" => Some(UserCommand::NewPlayer(players::Player::Human)),
- "ai" => Some(UserCommand::NewPlayer(players::Player::AiMedium)),
- "n" | "new game" => Some(UserCommand::NewGame),
- "h" | "help" => Some(UserCommand::Help),
- "u" | "undo" => Some(UserCommand::Undo),
- "q" | "quit" => Some(UserCommand::Quit),
- _ => {
-
- let mut row: Option<usize> = None;
- let mut col: Option<usize> = None;
-
- for curr_char in input.chars() {
- match curr_char {
- '1'...'8' => {
- if let None = row {
- row = Some(curr_char as usize - '1' as usize);
- } else {
- return None;
- }
- }
- 'a'...'h' => {
- if let None = col {
- col = Some(curr_char as usize - 'a' as usize);
- } else {
- return None;
- }
- }
- _ => return None,
- }
- }
-
- if row.is_none() || col.is_none() {
- None
- } else {
- // The move is not checked!
- Some(UserCommand::Move(row.unwrap(), col.unwrap()))
- }
- }
- }
-}
-
-
-
-/// draw_board draws the board (using text characters) in a pleasant-looking way, converting the board in a string (board_to_string) and then printing this.
-pub fn draw_board(game: &reversi::Game) {
-
- let board = game.get_board();
-
- // Declare board_to_string and add column reference at the top
- let mut board_to_string: String = "\n\n\n\t a b c d e f g h\n".to_string();
-
- // For every row add a row reference to the left
- for (row, row_array) in board.iter().enumerate() {
- board_to_string.push('\t');
- board_to_string.push_str(&(row + 1).to_string());
- board_to_string.push(' ');
-
- // For every column, add the appropriate character depending on the content of the current cell
- for (col, &cell) in row_array.iter().enumerate() {
-
- match cell {
- // Light and Dark cells are represented by white and black bullets
- reversi::Cell::Taken { disk: reversi::Disk::Light } => board_to_string.push_str(" ● "),
- reversi::Cell::Taken { disk: reversi::Disk::Dark } => board_to_string.push_str(" ○ "),
-
- // An empty cell will display a plus or a multiplication sign if the current player can move in that cell
- // or a little central dot otherwise
- reversi::Cell::Empty => {
- if game.check_move((row, col)) {
- if let reversi::Status::Running { .. } = game.get_status() {
- board_to_string.push_str(" * ");
- }
- } else {
- board_to_string.push_str(" ∙ ");
- }
- }
- }
- }
-
- // Add a row reference to the right
- board_to_string.push(' ');
- board_to_string.push_str(&(row + 1).to_string());
- board_to_string.push('\n');
- }
-
- // Add column reference at the bottom
- board_to_string.push_str("\t a b c d e f g h\n");
-
- // Print board
- println!("{}", board_to_string);
-
- // Print current score and game info
- let (score_light, score_dark) = game.get_score();
-
- match game.get_status() {
- reversi::Status::Running { current_turn } => {
- match current_turn {
- reversi::Disk::Light => println!("\t {:>2} ○ >> ● {:<2}\n", score_dark, score_light),
- reversi::Disk::Dark => println!("\t {:>2} ○ << ● {:<2}\n", score_dark, score_light),
- }
- }
- reversi::Status::Ended => {
- println!("\t {:>2} ○ ● {:<2}\n", score_dark, score_light);
- match score_light.cmp(&score_dark) {
- Ordering::Greater => println!("Light wins!"),
- Ordering::Less => println!("Dark wins!"),
- Ordering::Equal => println!("Draw!"),
- }
- }
- }
-}
-
-
-
-/// Prints a message with info on a move.
-pub fn print_move(game: &reversi::Game, (row, col): (usize, usize)) {
-
- let char_col = (('a' as u8) + (col as u8)) as char;
- if let reversi::Status::Running { current_turn } = game.get_status() {
- match current_turn {
- reversi::Disk::Light => println!("● Light moves: {}{}", char_col, row + 1),
- reversi::Disk::Dark => println!("○ Dark moves: {}{}", char_col, row + 1),
- }
- }
-}
-
-
-
-/// It get_status a human player's input and convert it into a move.
-/// If the move if illegal, it ask for another input until the given move is a legal one.
-pub fn human_make_move(game: &reversi::Game) -> UserCommand {
-
- if let reversi::Status::Running { current_turn } = game.get_status() {
- match current_turn {
- reversi::Disk::Light => print!("● Light moves: "),
- reversi::Disk::Dark => print!("○ Dark moves: "),
- }
- }
-
- loop {
- if let Some(user_command) = get_user_command() {
- match user_command {
- UserCommand::Move(row, col) => {
- if game.check_move((row, col)) {
- return UserCommand::Move(row, col);
- } else {
- print!("Illegal move, try again: ");
- continue;
- }
- }
- _ => return user_command,
- }
- } else {
- print!("This doesn't look like a valid command. Try again: ");
- continue;
- }
- }
-}
-
-
-
-// Print a last message before a player quits the game
-pub fn quitting_message(coward: reversi::Disk) {
- match coward {
- reversi::Disk::Light => println!("Light is running away, the coward!"),
- reversi::Disk::Dark => println!("Dark is running away, the coward!"),
- }
-}
-
-// Print a last message when 'undo' is not possible
-pub fn no_undo_message(undecided: reversi::Disk) {
- match undecided {
- reversi::Disk::Light => println!("There is no move Light can undo."),
- reversi::Disk::Dark => println!("There is no move Dark can undo."),
- }
-}
View
118 filesystem/apps/rusthello/main.rs~
@@ -1,118 +0,0 @@
-//! A simple Reversi game written in Rust with love
-//! by Enrico Ghiorzi
-
-extern crate rand;
-
-// Import modules
-mod reversi;
-mod interface;
-mod players;
-
-
-
-pub fn main() {
- // Main intro
- println!("{}", interface::INTRO);
-
- loop {
- println!("{}", interface::MAIN_MENU);
-
- match interface::input_main_menu() {
- // Runs the game
- interface::UserCommand::NewGame => play_game(),
- // Prints help message
- interface::UserCommand::Help => println!("{}", interface::HELP),
- // Quit RUSThello
- interface::UserCommand::Quit => break,
- _ => panic!("Main got a user command it shouldn't have got!"),
- }
- }
-}
-
-
-
-fn play_game() {
-
- // Get the two players
- println!("{}", interface::NEW_PLAYER_MENU);
- let dark = match interface::new_player(reversi::Disk::Dark) {
- None => return,
- Some(player) => player,
- };
- let light = match interface::new_player(reversi::Disk::Light) {
- None => return,
- Some(player) => player,
- };
-
- // Create a new game
- let mut game = reversi::Game::new();
- let mut hystory: Vec<reversi::Game> = Vec::new();
-
- println!("{}", interface::COMMANDS_INFO);
-
- // Draw the current board and game info
- interface::draw_board(&game);
-
- // Proceed with turn after turn till the game ends
- 'turn: while let reversi::Status::Running { current_turn } = game.get_status() {
-
- // If the game is running, get the coordinates of the new move from the right player
- let action = match current_turn {
- reversi::Disk::Light => light.make_move(&game),
- reversi::Disk::Dark => dark.make_move(&game),
- };
-
- match action {
- // If the new move is valid, perform it; otherwise panic
- // Player's make_move method is responsible for returning a legal move
- // so the program should never print this message unless something goes horribly wrong
- interface::UserCommand::Move(row, col) => {
-
- if game.check_move((row, col)) {
- hystory.push(game.clone());
- game.make_move((row, col));
- interface::draw_board(&game);
- } else {
- panic!("Invalid move sent to main::game!");
- }
- }
-
- // Manage hystory
- interface::UserCommand::Undo => {
- let mut recovery: Vec<reversi::Game> = Vec::new();
-
- while let Some(previous_game) = hystory.pop() {
- recovery.push(previous_game.clone());
- if let reversi::Status::Running { current_turn: previous_player } = previous_game.get_status() {
- if previous_player == current_turn {
- game = previous_game;
- interface::draw_board(&game);
- continue 'turn;
- }
- }
- }
-
- while let Some(recovered_game) = recovery.pop() {
- hystory.push(recovered_game.clone());
- }
-
- interface::no_undo_message(current_turn);
- }
-
- interface::UserCommand::Help => {
- println!("{}", interface::HELP);
- interface::draw_board(&game);
- }
-
- // Quit Match
- interface::UserCommand::Quit => {
- interface::quitting_message(current_turn);
- break;
- }
-
- _ => {
- panic!("Something's wrong here!");
- }
- }
- }
-}
View
159 filesystem/apps/rusthello/players/ai_medium.rs
@@ -1,159 +0,0 @@
-//use rand;
-//use rand::Rng;
-
-use reversi;
-use players::Score;
-
-const MOBILITY: u8 = 1;
-//const RANDOMNESS: f32 = 1.0;
-
-
-
-pub fn ai_eval(game: &reversi::Game, depth: u8) -> Score {
-
- match game.get_status() {
- reversi::Status::Running { current_turn } => {
- if depth == 0 {
- Score::Running(heavy_eval(game) as f32)
- } else {
- let mut best_score: Option<Score> = None;
- let mut num_moves: u8 = 0;
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves += 1;
- let new_score = ai_eval(&game_after_move, depth - 1);
- match best_score.clone() {
- Some(old_score) => {
- if Score::is_better_for(new_score.clone(), old_score, current_turn) {
- best_score = Some(new_score);
- }
- }
- None => best_score = Some(new_score),
- }
- game_after_move = game.clone();
-
- }
- }
- }
- if let Some(score) = best_score {
- if let Score::Running(val) = score {
- return match current_turn {
- reversi::Disk::Light => Score::Running(val + ( num_moves * MOBILITY ) as f32 ),
- reversi::Disk::Dark => Score::Running(val - ( num_moves * MOBILITY ) as f32 ),
- }
- } else {
- return score;
- }
- } else {
- panic!("ai_eval produced no best_score!");
- }
- }
- }
- reversi::Status::Ended => {
- Score::EndGame(game.get_score_diff())
- }
- }
-}
-
-
-
-fn heavy_eval(game: &reversi::Game) -> i16 {
- const CORNER_BONUS: i16 = 15;
- const ODD_MALUS: i16 = 3;
- const EVEN_BONUS: i16 = 3;
- const ODD_CORNER_MALUS: i16 = 10;
- const EVEN_CORNER_BONUS: i16 = 5;
- const FIXED_BONUS: i16 = 3;
-
- const SIDES: [( (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize) ); 4] = [
- ( (0,0), (0,1), (1,1), (0,2), (2,2), (1,0), (2,0) ), // NW corner
- ( (0,7), (1,7), (1,6), (2,7), (2,5), (0,6), (0,5) ), // NE corner
- ( (7,0), (6,0), (6,1), (5,0), (5,2), (7,1), (7,2) ), // SW corner
- ( (7,7), (6,7), (6,6), (5,7), (5,5), (7,6), (7,5) ), // SE corner
- ];
-
- let mut score: i16 = 0;
-
- for &(corner, odd, odd_corner, even, even_corner, counter_odd, counter_even) in SIDES.iter() {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(corner) {
- match disk {
- reversi::Disk::Light => {
- score += CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(even) {
- score += FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_even) {
- score += FIXED_BONUS;
- }
- }
- }
- reversi::Disk::Dark => {
- score -= CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(even) {
- score -= FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_even) {
- score -= FIXED_BONUS;
- }
- }
- }
- }
-
- } else {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(counter_odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(counter_even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd_corner) {
- score += match disk {
- reversi::Disk::Light => -ODD_CORNER_MALUS,
- reversi::Disk::Dark => ODD_CORNER_MALUS,
- }
-
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even_corner) {
- score += match disk {
- reversi::Disk::Light => EVEN_CORNER_BONUS,
- reversi::Disk::Dark => -EVEN_CORNER_BONUS,
- }
- }
- }
- }
-
- score
-}
View
159 filesystem/apps/rusthello/players/ai_medium.rs~
@@ -1,159 +0,0 @@
-use rand;
-use rand::Rng;
-
-use reversi;
-use players::Score;
-
-const MOBILITY: u8 = 1;
-const RANDOMNESS: f32 = 1.0;
-
-
-
-pub fn ai_eval(game: &reversi::Game, depth: u8) -> Score {
-
- match game.get_status() {
- reversi::Status::Running { current_turn } => {
- if depth == 0 {
- Score::Running(heavy_eval(game) as f32)
- } else {
- let mut best_score: Option<Score> = None;
- let mut num_moves: u8 = 0;
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves += 1;
- let new_score = ai_eval(&game_after_move, depth - 1);
- match best_score.clone() {
- Some(old_score) => {
- if Score::is_better_for(new_score.clone(), old_score, current_turn) {
- best_score = Some(new_score);
- }
- }
- None => best_score = Some(new_score),
- }
- game_after_move = game.clone();
-
- }
- }
- }
- if let Some(score) = best_score {
- if let Score::Running(val) = score {
- return match current_turn {
- reversi::Disk::Light => Score::Running(val + ( num_moves * MOBILITY ) as f32 + rand::thread_rng().gen_range::<f32>(-RANDOMNESS, RANDOMNESS) ),
- reversi::Disk::Dark => Score::Running(val - ( num_moves * MOBILITY ) as f32 + rand::thread_rng().gen_range::<f32>(-RANDOMNESS, RANDOMNESS) ),
- }
- } else {
- return score;
- }
- } else {
- panic!("ai_eval produced no best_score!");
- }
- }
- }
- reversi::Status::Ended => {
- Score::EndGame(game.get_score_diff())
- }
- }
-}
-
-
-
-fn heavy_eval(game: &reversi::Game) -> i16 {
- const CORNER_BONUS: i16 = 15;
- const ODD_MALUS: i16 = 3;
- const EVEN_BONUS: i16 = 3;
- const ODD_CORNER_MALUS: i16 = 10;
- const EVEN_CORNER_BONUS: i16 = 5;
- const FIXED_BONUS: i16 = 3;
-
- const SIDES: [( (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize) ); 4] = [
- ( (0,0), (0,1), (1,1), (0,2), (2,2), (1,0), (2,0) ), // NW corner
- ( (0,7), (1,7), (1,6), (2,7), (2,5), (0,6), (0,5) ), // NE corner
- ( (7,0), (6,0), (6,1), (5,0), (5,2), (7,1), (7,2) ), // SW corner
- ( (7,7), (6,7), (6,6), (5,7), (5,5), (7,6), (7,5) ), // SE corner
- ];
-
- let mut score: i16 = 0;
-
- for &(corner, odd, odd_corner, even, even_corner, counter_odd, counter_even) in SIDES.iter() {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(corner) {
- match disk {
- reversi::Disk::Light => {
- score += CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(even) {
- score += FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_even) {
- score += FIXED_BONUS;
- }
- }
- }
- reversi::Disk::Dark => {
- score -= CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(even) {
- score -= FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_even) {
- score -= FIXED_BONUS;
- }
- }
- }
- }
-
- } else {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(counter_odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(counter_even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd_corner) {
- score += match disk {
- reversi::Disk::Light => -ODD_CORNER_MALUS,
- reversi::Disk::Dark => ODD_CORNER_MALUS,
- }
-
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even_corner) {
- score += match disk {
- reversi::Disk::Light => EVEN_CORNER_BONUS,
- reversi::Disk::Dark => -EVEN_CORNER_BONUS,
- }
- }
- }
- }
-
- score
-}
View
200 filesystem/apps/rusthello/players/mod.rs
@@ -1,200 +0,0 @@
-use interface;
-use reversi;
-
-use std::thread;
-use std::sync::mpsc;
-use std::sync::mpsc::{Sender, Receiver};
-use std::time;
-
-
-
-mod ai_medium;
-
-const STARTING_DEPTH: u8 = 2;
-const TIME_LIMIT: i64 = 1;
-
-
-
-#[derive(Clone)]
-pub enum Score {
- Running(f32),
- EndGame(i16),
-}
-
-impl Score {
-
- pub fn is_better_for(first: Score, second: Score, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first, second),
- reversi::Disk::Dark => !Score::is_better(first, second),
- }
- }
-
- pub fn is_better(first: Score, second: Score) -> bool {
- match first {
- Score::Running(val1) => {
- match second {
- Score::Running(val2) => val1 > val2,
- Score::EndGame(scr2) => scr2 < 0i16 || ( scr2 == 0i16 && val1 > 0f32 ),
- }
- }
- Score::EndGame(scr1) => {
- match second {
- Score::Running(val2) => scr1 > 0i16 || ( scr1 == 0i16 && val2 < 0f32 ),
- Score::EndGame(scr2) => scr1 > scr2,
- }
- }
- }
- }
-}
-
-
-
-#[derive(Clone)]
-struct MoveScore{
- score: Score,
- coord: (usize, usize),
-}
-
-impl MoveScore {
- pub fn is_better_for(first: MoveScore, second: MoveScore, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first.score, second.score),
- reversi::Disk::Dark => !Score::is_better(first.score, second.score),
- }
- }
-}
-
-
-
-
-/// It represents the different kind of player who can take part to the game.
-#[derive(Clone)]
-pub enum Player {
- Human,
- AiMedium,
-}
-
-
-impl Player {
-
- /// It produces the new move from each kind of Player.
- pub fn make_move(&self, game: &reversi::Game) -> interface::UserCommand {
-
- if let reversi::Status::Ended = game.get_status() {
- panic!("make_move called on ended game!");
- }
-
- if let Player::Human = *self {
- interface::human_make_move(game)
- } else {
- let (row, col) = ai_make_move(game, &self.clone());
-
- interface::print_move(game, (row, col));
-
- interface::UserCommand::Move(row, col)
- }
- }
-}
-
-
-
-pub fn ai_make_move(game: &reversi::Game, player: &Player) -> (usize, usize) {
- let mut num_moves = 0;
- let mut forced_move: (usize, usize) = (reversi::BOARD_SIZE, reversi::BOARD_SIZE);
- let mut game_after_move = game.clone();
-
- // To save computation time, first check whether the move is forced.
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
- num_moves += 1;
- forced_move = (row, col);
- game_after_move = game.clone();
- }
- }
- }
-
- match num_moves {
- 0 => panic!("No valid move is possible!"),
- 1 => forced_move,
- _ => {
- let start_time = time::Instant::now();
- let mut depth = STARTING_DEPTH;
- let mut best_move = (0, 0);
-
- while start_time.elapsed() < time::Duration::new(TIME_LIMIT, 0) {
- if game.get_tempo() + 2 * (depth - 1) >= ( reversi::BOARD_SIZE * reversi::BOARD_SIZE ) as u8 {
- return find_best_move(game, &player, (reversi::BOARD_SIZE * reversi::BOARD_SIZE) as u8 - game.get_tempo());
- } else {
- best_move = find_best_move(game, &player, depth);
- }
- depth += 1;
- }
- best_move
- }
- }
-}
-
-
-
-pub fn find_best_move(game: &reversi::Game, player: &Player, depth: u8) -> (usize, usize) {
-
- if let reversi::Status::Running { current_turn } = game.get_status() {
-
- let ai_eval: fn(&reversi::Game, u8) -> Score = match *player {
- Player::AiMedium => ai_medium::ai_eval,
- Player::Human => panic!("A human is not an AI!")
- };
-
- let mut best_move: Option<MoveScore> = None;
-
- let mut num_moves: u8 = 0;
-
- let (tx, rx): (Sender<MoveScore>, Receiver<MoveScore>) = mpsc::channel();
-
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves +=1;
- let thread_tx = tx.clone();
-
- thread::spawn(move || {
- let new_move = MoveScore {
- score: ai_eval(&game_after_move, depth),
- coord: (row, col),
- };
- thread_tx.send(new_move).unwrap();
- });
-
- game_after_move = game.clone();
-
- }
- }
- }
-
- for _ in 0..num_moves {
- let new_move = rx.recv().ok().expect("Could not receive answer");
-
- if let Some(old_move) = best_move.clone() {
- if MoveScore::is_better_for(new_move.clone(), old_move, current_turn) {
- best_move = Some(new_move);
- }
- } else {
- best_move = Some(new_move);
- }
- }
-
- if let Some(some_move) = best_move {
- some_move.coord
- } else {
- panic!("best_eval is None");
- }
-
- } else {
- panic!{"Game ended, cannot make a move!"};
- }
-}
View
200 filesystem/apps/rusthello/players/mod.rs~
@@ -1,200 +0,0 @@
-use interface;
-use reversi;
-
-use std::thread;
-use std::sync::mpsc;
-use std::sync::mpsc::{Sender, Receiver};
-use std::time;
-
-
-
-mod ai_medium;
-
-const STARTING_DEPTH: u8 = 2;
-const TIME_LIMIT: f64 = 1.0;
-
-
-
-#[derive(Clone)]
-pub enum Score {
- Running(f32),
- EndGame(i16),
-}
-
-impl Score {
-
- pub fn is_better_for(first: Score, second: Score, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first, second),
- reversi::Disk::Dark => !Score::is_better(first, second),
- }
- }
-
- pub fn is_better(first: Score, second: Score) -> bool {
- match first {
- Score::Running(val1) => {
- match second {
- Score::Running(val2) => val1 > val2,
- Score::EndGame(scr2) => scr2 < 0i16 || ( scr2 == 0i16 && val1 > 0f32 ),
- }
- }
- Score::EndGame(scr1) => {
- match second {
- Score::Running(val2) => scr1 > 0i16 || ( scr1 == 0i16 && val2 < 0f32 ),
- Score::EndGame(scr2) => scr1 > scr2,
- }
- }
- }
- }
-}
-
-
-
-#[derive(Clone)]
-struct MoveScore{
- score: Score,
- coord: (usize, usize),
-}
-
-impl MoveScore {
- pub fn is_better_for(first: MoveScore, second: MoveScore, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first.score, second.score),
- reversi::Disk::Dark => !Score::is_better(first.score, second.score),
- }
- }
-}
-
-
-
-
-/// It represents the different kind of player who can take part to the game.
-#[derive(Clone)]
-pub enum Player {
- Human,
- AiMedium,
-}
-
-
-impl Player {
-
- /// It produces the new move from each kind of Player.
- pub fn make_move(&self, game: &reversi::Game) -> interface::UserCommand {
-
- if let reversi::Status::Ended = game.get_status() {
- panic!("make_move called on ended game!");
- }
-
- if let Player::Human = *self {
- interface::human_make_move(game)
- } else {
- let (row, col) = ai_make_move(game, &self.clone());
-
- interface::print_move(game, (row, col));
-
- interface::UserCommand::Move(row, col)
- }
- }
-}
-
-
-
-pub fn ai_make_move(game: &reversi::Game, player: &Player) -> (usize, usize) {
- let mut num_moves = 0;
- let mut forced_move: (usize, usize) = (reversi::BOARD_SIZE, reversi::BOARD_SIZE);
- let mut game_after_move = game.clone();
-
- // To save computation time, first check whether the move is forced.
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
- num_moves += 1;
- forced_move = (row, col);
- game_after_move = game.clone();
- }
- }
- }
-
- match num_moves {
- 0 => panic!("No valid move is possible!"),
- 1 => forced_move,
- _ => {
- let start_time = time::Instant::now();
- let mut depth = STARTING_DEPTH;
- let mut best_move = (0, 0);
-
- while start_time.elapsed() < time::Duration::new(1, 0) {
- if game.get_tempo() + 2 * (depth - 1) >= ( reversi::BOARD_SIZE * reversi::BOARD_SIZE ) as u8 {
- return find_best_move(game, &player, (reversi::BOARD_SIZE * reversi::BOARD_SIZE) as u8 - game.get_tempo());
- } else {
- best_move = find_best_move(game, &player, depth);
- }
- depth += 1;
- }
- best_move
- }
- }
-}
-
-
-
-pub fn find_best_move(game: &reversi::Game, player: &Player, depth: u8) -> (usize, usize) {
-
- if let reversi::Status::Running { current_turn } = game.get_status() {
-
- let ai_eval: fn(&reversi::Game, u8) -> Score = match *player {
- Player::AiMedium => ai_medium::ai_eval,
- Player::Human => panic!("A human is not an AI!")
- };
-
- let mut best_move: Option<MoveScore> = None;
-
- let mut num_moves: u8 = 0;
-
- let (tx, rx): (Sender<MoveScore>, Receiver<MoveScore>) = mpsc::channel();
-
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves +=1;
- let thread_tx = tx.clone();
-
- thread::spawn(move || {
- let new_move = MoveScore {
- score: ai_eval(&game_after_move, depth),
- coord: (row, col),
- };
- thread_tx.send(new_move).unwrap();
- });
-
- game_after_move = game.clone();
-
- }
- }
- }
-
- for _ in 0..num_moves {
- let new_move = rx.recv().ok().expect("Could not receive answer");
-
- if let Some(old_move) = best_move.clone() {
- if MoveScore::is_better_for(new_move.clone(), old_move, current_turn) {
- best_move = Some(new_move);
- }
- } else {
- best_move = Some(new_move);
- }
- }
-
- if let Some(some_move) = best_move {
- some_move.coord
- } else {
- panic!("best_eval is None");
- }
-
- } else {
- panic!{"Game ended, cannot make a move!"};
- }
-}
View
299 filesystem/apps/rusthello/src/interface.rs
@@ -1,299 +0,0 @@
-// This module provides interface functionalities and manages all the input/output part of the program
-
-use std::cmp::Ordering;
-use std::io::{self, Write};
-use players;
-use reversi;
-
-pub enum UserCommand {
- NewGame,
- NewPlayer(players::Player),
- Move(usize, usize),
- Help,
- Undo,
- Quit,
-}
-
-pub const INTRO: &'static str =
-"\n\n
- RUSThello
- ● ○ ● ○ ●
- a simple Reversi game
- written in Rust with love
- Redox Edition
- v 1.1.0\n\n";
-
-pub const MAIN_MENU: &'static str =
-"\nMain Menu:
- n - New match
- h - Help
- q - Quit RUSThello";
-
- pub const NEW_PLAYER_MENU: &'static str =
- "\nChoose a player:
- hp - Human Player
- ai - Artificial Intelligence
- q - Quit match";
-
-pub const COMMANDS_INFO: &'static str =
-"\nStarting new game…
-Type a cell's coordinates to place your disk there. Exaple: \"c4\"
-Type 'help' or 'h' instead of a move to display help message.
-Type 'undo' or 'u' instead of a move to undo last move.
-Type 'quit' or 'q' instead of a move to abandon the game.";
-
-pub const HELP: &'static str = "\
-\n\n\n\tHOW TO PLAY REVERSI:\n
-Reversi is a board game where two players compete against each other. \
-The game is played on a 8x8 board, just like chess but for the squares’ colour which is always green. \
-There are 64 identical pieces called disks, which are white on one side and black on the other. \
-A player is Light, using disks’ white side, and the other one is Dark, using disks' black side. \
-The game starts with four disks already placed at the centre of the board, two for each side. \
-Dark moves first.\n
-Let’s say it’s Dark’s turn, for simplicity's sake, as for Light the rules are just the same. \
-Dark has to place a disk in a free square of the board, with the black side facing up. \
-Whenever the newly placed black disk and any other previously placed black disk enclose a sequence of white disks (horizontal, vertical or diagonal and of any length), all of those flip and turn black. \
-It is mandatory to place the new disk such that at least a white disk is flipped, otherwise the move is not valid.\n
-Usually players’ turn alternate, passing from one to the other. \
-When a player cannot play any legal move, the turn goes back to the other player, thus allowing the same player to play consecutive turns. \
-When neither player can play a legal move, the game ends. \
-In particular, this is true whenever the board has been completely filled up (for a total of 60 moves), but games happen sometimes to end before that, leaving empty squares on the board.\n
-When the game ends, the player with more disks turned to its side wins. \
-Ties are possible as well, if both player have the same number of disks.\n\n\n
-\tHOW TO USE RUSThello:\n
-To play RUSThello you first have to choose who is playing on each side, Dark and Light. \
-You can choose a human players or an AI. \
-Choose human for both players and challenge a friend, or test your skills against an AI, or even relax and watch as two AIs compete with each other: all matches are possible!\n
-As a human player, you move by entering the coordinates (a letter and a number) of the square you want to place your disk on, e.g. all of 'c4', 'C4', '4c' and '4C' are valid and equivalent coordinates. \
-For your ease of use, all legal moves are marked on the board with a *.\n
-Furthermore, on your turn you can also input special commands: 'undo' to undo your last move (and yes, you can 'undo' as many times as you like) and 'quit' to quit the game.\n\n\n
-\tCREDITS:\n
-RUSThello v. 1.1.0 Redox Edition
-by Enrico Ghiorzi, with the invaluable help of the Redox community
-Copyright (c) 2015 by Enrico Ghiorzi
-Released under the MIT license\n\n\n";
-
-
-pub fn input_main_menu() -> UserCommand {
-
- loop {
- print!("Insert input: ");
- match get_user_command() {
- Some(UserCommand::NewGame) => return UserCommand::NewGame,
- Some(UserCommand::Help) => return UserCommand::Help,
- Some(UserCommand::Quit) => {
- println!("\nGoodbye!\n\n\n");
- return UserCommand::Quit;
- }
- _ => println!("This is not a valid command!"),
- }
- }
-}
-
-pub fn new_player(side: reversi::Disk) -> Option<players::Player> {
- loop {
- match side {
- reversi::Disk::Light => print!("● Light player: "),
- reversi::Disk::Dark => print!("○ Dark player: "),
- }
- match get_user_command() {
- Some(UserCommand::NewPlayer(player)) => return Some(player),
- Some(UserCommand::Quit) => return None,
- _ => println!("This is not a valid command!"),
- }
- }
-}
-
-/// It gets an input from the user and tries to parse it, then returns a Option<UserCommand>`.
-/// If the input is recognized as a legit command, it returns the relative `Option::Some(UserCommand)`.
-/// If the input is not recognized as a legit command, it returns a `Option::None`.
-pub fn get_user_command() -> Option<UserCommand> {
-
- // Read the input
- let _ = io::stdout().flush();
-
- let mut input = String::new();
-
- io::stdin().read_line(&mut input)
- .ok()
- .expect("failed to read line");
-
- let input = input.trim().to_lowercase();
-
- match &*input {
- "hp" => Some(UserCommand::NewPlayer(players::Player::Human)),
- "ai" => Some(UserCommand::NewPlayer(players::Player::AiMedium)),
- "n" | "new game" => Some(UserCommand::NewGame),
- "h" | "help" => Some(UserCommand::Help),
- "u" | "undo" => Some(UserCommand::Undo),
- "q" | "quit" => Some(UserCommand::Quit),
- _ => {
-
- let mut row: Option<usize> = None;
- let mut col: Option<usize> = None;
-
- for curr_char in input.chars() {
- match curr_char {
- '1'...'8' => {
- if let None = row {
- row = Some(curr_char as usize - '1' as usize);
- } else {
- return None;
- }
- }
- 'a'...'h' => {
- if let None = col {
- col = Some(curr_char as usize - 'a' as usize);
- } else {
- return None;
- }
- }
- _ => return None,
- }
- }
-
- if row.is_none() || col.is_none() {
- None
- } else {
- // The move is not checked!
- Some(UserCommand::Move(row.unwrap(), col.unwrap()))
- }
- }
- }
-}
-
-
-
-/// draw_board draws the board (using text characters) in a pleasant-looking way, converting the board in a string (board_to_string) and then printing this.
-pub fn draw_board(game: &reversi::Game) {
-
- let board = game.get_board();
-
- // Declare board_to_string and add column reference at the top
- let mut board_to_string: String = "\n\n\n\t a b c d e f g h\n".to_string();
-
- // For every row add a row reference to the left
- for (row, row_array) in board.iter().enumerate() {
- board_to_string.push('\t');
- board_to_string.push_str(&(row + 1).to_string());
- board_to_string.push(' ');
-
- // For every column, add the appropriate character depending on the content of the current cell
- for (col, &cell) in row_array.iter().enumerate() {
-
- match cell {
- // Light and Dark cells are represented by white and black bullets
- reversi::Cell::Taken { disk: reversi::Disk::Light } => board_to_string.push_str(" ● "),
- reversi::Cell::Taken { disk: reversi::Disk::Dark } => board_to_string.push_str(" ○ "),
-
- // An empty cell will display a plus or a multiplication sign if the current player can move in that cell
- // or a little central dot otherwise
- reversi::Cell::Empty => {
- if game.check_move((row, col)) {
- if let reversi::Status::Running { .. } = game.get_status() {
- board_to_string.push_str(" * ");
- }
- } else {
- board_to_string.push_str(" ∙ ");
- }
- }
- }
- }
-
- // Add a row reference to the right
- board_to_string.push(' ');
- board_to_string.push_str(&(row + 1).to_string());
- board_to_string.push('\n');
- }
-
- // Add column reference at the bottom
- board_to_string.push_str("\t a b c d e f g h\n");
-
- // Print board
- println!("{}", board_to_string);
-
- // Print current score and game info
- let (score_light, score_dark) = game.get_score();
-
- match game.get_status() {
- reversi::Status::Running { current_turn } => {
- match current_turn {
- reversi::Disk::Light => println!("\t {:>2} ○ >> ● {:<2}\n", score_dark, score_light),
- reversi::Disk::Dark => println!("\t {:>2} ○ << ● {:<2}\n", score_dark, score_light),
- }
- }
- reversi::Status::Ended => {
- println!("\t {:>2} ○ ● {:<2}\n", score_dark, score_light);
- match score_light.cmp(&score_dark) {
- Ordering::Greater => println!("Light wins!"),
- Ordering::Less => println!("Dark wins!"),
- Ordering::Equal => println!("Draw!"),
- }
- }
- }
-}
-
-
-
-/// Prints a message with info on a move.
-pub fn print_move(game: &reversi::Game, (row, col): (usize, usize)) {
-
- let char_col = (('a' as u8) + (col as u8)) as char;
- if let reversi::Status::Running { current_turn } = game.get_status() {
- match current_turn {
- reversi::Disk::Light => println!("● Light moves: {}{}", char_col, row + 1),
- reversi::Disk::Dark => println!("○ Dark moves: {}{}", char_col, row + 1),
- }
- }
-}
-
-
-
-/// It get_status a human player's input and convert it into a move.
-/// If the move if illegal, it ask for another input until the given move is a legal one.
-pub fn human_make_move(game: &reversi::Game) -> UserCommand {
-
- if let reversi::Status::Running { current_turn } = game.get_status() {
- match current_turn {
- reversi::Disk::Light => print!("● Light moves: "),
- reversi::Disk::Dark => print!("○ Dark moves: "),
- }
- }
-
- loop {
- if let Some(user_command) = get_user_command() {
- match user_command {
- UserCommand::Move(row, col) => {
- if game.check_move((row, col)) {
- return UserCommand::Move(row, col);
- } else {
- print!("Illegal move, try again: ");
- continue;
- }
- }
- _ => return user_command,
- }
- } else {
- print!("This doesn't look like a valid command. Try again: ");
- continue;
- }
- }
-}
-
-
-
-// Print a last message before a player quits the game
-pub fn quitting_message(coward: reversi::Disk) {
- match coward {
- reversi::Disk::Light => println!("Light is running away, the coward!"),
- reversi::Disk::Dark => println!("Dark is running away, the coward!"),
- }
-}
-
-// Print a last message when 'undo' is not possible
-pub fn no_undo_message(undecided: reversi::Disk) {
- match undecided {
- reversi::Disk::Light => println!("There is no move Light can undo."),
- reversi::Disk::Dark => println!("There is no move Dark can undo."),
- }
-}
View
118 filesystem/apps/rusthello/src/main.rs
@@ -1,118 +0,0 @@
-//! A simple Reversi game written in Rust with love
-//! by Enrico Ghiorzi
-
-//extern crate rand;
-
-// Import modules
-mod reversi;
-mod interface;
-mod players;
-
-
-
-pub fn main() {
- // Main intro
- println!("{}", interface::INTRO);
-
- loop {
- println!("{}", interface::MAIN_MENU);
-
- match interface::input_main_menu() {
- // Runs the game
- interface::UserCommand::NewGame => play_game(),
- // Prints help message
- interface::UserCommand::Help => println!("{}", interface::HELP),
- // Quit RUSThello
- interface::UserCommand::Quit => break,
- _ => panic!("Main got a user command it shouldn't have got!"),
- }
- }
-}
-
-
-
-fn play_game() {
-
- // Get the two players
- println!("{}", interface::NEW_PLAYER_MENU);
- let dark = match interface::new_player(reversi::Disk::Dark) {
- None => return,
- Some(player) => player,
- };
- let light = match interface::new_player(reversi::Disk::Light) {
- None => return,
- Some(player) => player,
- };
-
- // Create a new game
- let mut game = reversi::Game::new();
- let mut hystory: Vec<reversi::Game> = Vec::new();
-
- println!("{}", interface::COMMANDS_INFO);
-
- // Draw the current board and game info
- interface::draw_board(&game);
-
- // Proceed with turn after turn till the game ends
- 'turn: while let reversi::Status::Running { current_turn } = game.get_status() {
-
- // If the game is running, get the coordinates of the new move from the right player
- let action = match current_turn {
- reversi::Disk::Light => light.make_move(&game),
- reversi::Disk::Dark => dark.make_move(&game),
- };
-
- match action {
- // If the new move is valid, perform it; otherwise panic
- // Player's make_move method is responsible for returning a legal move
- // so the program should never print this message unless something goes horribly wrong
- interface::UserCommand::Move(row, col) => {
-
- if game.check_move((row, col)) {
- hystory.push(game.clone());
- game.make_move((row, col));
- interface::draw_board(&game);
- } else {
- panic!("Invalid move sent to main::game!");
- }
- }
-
- // Manage hystory
- interface::UserCommand::Undo => {
- let mut recovery: Vec<reversi::Game> = Vec::new();
-
- while let Some(previous_game) = hystory.pop() {
- recovery.push(previous_game.clone());
- if let reversi::Status::Running { current_turn: previous_player } = previous_game.get_status() {
- if previous_player == current_turn {
- game = previous_game;
- interface::draw_board(&game);
- continue 'turn;
- }
- }
- }
-
- while let Some(recovered_game) = recovery.pop() {
- hystory.push(recovered_game.clone());
- }
-
- interface::no_undo_message(current_turn);
- }
-
- interface::UserCommand::Help => {
- println!("{}", interface::HELP);
- interface::draw_board(&game);
- }
-
- // Quit Match
- interface::UserCommand::Quit => {
- interface::quitting_message(current_turn);
- break;
- }
-
- _ => {
- panic!("Something's wrong here!");
- }
- }
- }
-}
View
159 filesystem/apps/rusthello/src/players/ai_medium.rs
@@ -1,159 +0,0 @@
-//use rand;
-//use rand::Rng;
-
-use reversi;
-use players::Score;
-
-const MOBILITY: u8 = 1;
-//const RANDOMNESS: f32 = 1.0;
-
-
-
-pub fn ai_eval(game: &reversi::Game, depth: u8) -> Score {
-
- match game.get_status() {
- reversi::Status::Running { current_turn } => {
- if depth == 0 {
- Score::Running(heavy_eval(game) as f32)
- } else {
- let mut best_score: Option<Score> = None;
- let mut num_moves: u8 = 0;
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves += 1;
- let new_score = ai_eval(&game_after_move, depth - 1);
- match best_score.clone() {
- Some(old_score) => {
- if Score::is_better_for(new_score.clone(), old_score, current_turn) {
- best_score = Some(new_score);
- }
- }
- None => best_score = Some(new_score),
- }
- game_after_move = game.clone();
-
- }
- }
- }
- if let Some(score) = best_score {
- if let Score::Running(val) = score {
- return match current_turn {
- reversi::Disk::Light => Score::Running(val + ( num_moves * MOBILITY ) as f32 ),
- reversi::Disk::Dark => Score::Running(val - ( num_moves * MOBILITY ) as f32 ),
- }
- } else {
- return score;
- }
- } else {
- panic!("ai_eval produced no best_score!");
- }
- }
- }
- reversi::Status::Ended => {
- Score::EndGame(game.get_score_diff())
- }
- }
-}
-
-
-
-fn heavy_eval(game: &reversi::Game) -> i16 {
- const CORNER_BONUS: i16 = 15;
- const ODD_MALUS: i16 = 3;
- const EVEN_BONUS: i16 = 3;
- const ODD_CORNER_MALUS: i16 = 10;
- const EVEN_CORNER_BONUS: i16 = 5;
- const FIXED_BONUS: i16 = 3;
-
- const SIDES: [( (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize) ); 4] = [
- ( (0,0), (0,1), (1,1), (0,2), (2,2), (1,0), (2,0) ), // NW corner
- ( (0,7), (1,7), (1,6), (2,7), (2,5), (0,6), (0,5) ), // NE corner
- ( (7,0), (6,0), (6,1), (5,0), (5,2), (7,1), (7,2) ), // SW corner
- ( (7,7), (6,7), (6,6), (5,7), (5,5), (7,6), (7,5) ), // SE corner
- ];
-
- let mut score: i16 = 0;
-
- for &(corner, odd, odd_corner, even, even_corner, counter_odd, counter_even) in SIDES.iter() {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(corner) {
- match disk {
- reversi::Disk::Light => {
- score += CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(even) {
- score += FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_odd) {
- score += FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Light } = game.get_cell(counter_even) {
- score += FIXED_BONUS;
- }
- }
- }
- reversi::Disk::Dark => {
- score -= CORNER_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(even) {
- score -= FIXED_BONUS;
- }
- }
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_odd) {
- score -= FIXED_BONUS;
- if let reversi::Cell::Taken { disk: reversi::Disk::Dark } = game.get_cell(counter_even) {
- score -= FIXED_BONUS;
- }
- }
- }
- }
-
- } else {
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(counter_odd) {
- score += match disk {
- reversi::Disk::Light => -ODD_MALUS,
- reversi::Disk::Dark => ODD_MALUS,
- }
- } else if let reversi::Cell::Taken { disk } = game.get_cell(counter_even) {
- score += match disk {
- reversi::Disk::Light => EVEN_BONUS,
- reversi::Disk::Dark => -EVEN_BONUS,
- }
- }
-
- if let reversi::Cell::Taken { disk } = game.get_cell(odd_corner) {
- score += match disk {
- reversi::Disk::Light => -ODD_CORNER_MALUS,
- reversi::Disk::Dark => ODD_CORNER_MALUS,
- }
-
- } else if let reversi::Cell::Taken { disk } = game.get_cell(even_corner) {
- score += match disk {
- reversi::Disk::Light => EVEN_CORNER_BONUS,
- reversi::Disk::Dark => -EVEN_CORNER_BONUS,
- }
- }
- }
- }
-
- score
-}
View
201 filesystem/apps/rusthello/src/players/mod.rs
@@ -1,201 +0,0 @@
-use interface;
-use reversi;
-
-use std::thread;
-use std::sync::mpsc;
-use std::sync::mpsc::{Sender, Receiver};
-use std::time;
-
-
-
-mod ai_medium;
-
-const STARTING_DEPTH: u8 = 2;
-const TIME_LIMIT: f64 = 1.0;
-
-
-
-#[derive(Clone)]
-pub enum Score {
- Running(f32),
- EndGame(i16),
-}
-
-impl Score {
-
- pub fn is_better_for(first: Score, second: Score, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first, second),
- reversi::Disk::Dark => !Score::is_better(first, second),
- }
- }
-
- pub fn is_better(first: Score, second: Score) -> bool {
- match first {
- Score::Running(val1) => {
- match second {
- Score::Running(val2) => val1 > val2,
- Score::EndGame(scr2) => scr2 < 0i16 || ( scr2 == 0i16 && val1 > 0f32 ),
- }
- }
- Score::EndGame(scr1) => {
- match second {
- Score::Running(val2) => scr1 > 0i16 || ( scr1 == 0i16 && val2 < 0f32 ),
- Score::EndGame(scr2) => scr1 > scr2,
- }
- }
- }
- }
-}
-
-
-
-#[derive(Clone)]
-struct MoveScore{
- score: Score,
- coord: (usize, usize),
-}
-
-impl MoveScore {
- pub fn is_better_for(first: MoveScore, second: MoveScore, side: reversi::Disk) -> bool {
- match side {
- reversi::Disk::Light => Score::is_better(first.score, second.score),
- reversi::Disk::Dark => !Score::is_better(first.score, second.score),
- }
- }
-}
-
-
-
-
-/// It represents the different kind of player who can take part to the game.
-#[derive(Clone)]
-pub enum Player {
- Human,
- AiMedium,
-}
-
-
-impl Player {
-
- /// It produces the new move from each kind of Player.
- pub fn make_move(&self, game: &reversi::Game) -> interface::UserCommand {
-
- if let reversi::Status::Ended = game.get_status() {
- panic!("make_move called on ended game!");
- }
-
- if let Player::Human = *self {
- interface::human_make_move(game)
- } else {
- let (row, col) = ai_make_move(game, &self.clone());
-
- interface::print_move(game, (row, col));
-
- interface::UserCommand::Move(row, col)
- }
- }
-}
-
-
-
-pub fn ai_make_move(game: &reversi::Game, player: &Player) -> (usize, usize) {
-
- let mut num_moves = 0;
- let mut forced_move: (usize, usize) = (reversi::BOARD_SIZE, reversi::BOARD_SIZE);
- let mut game_after_move = game.clone();
-
- // To save computation time, first check whether the move is forced.
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
- num_moves += 1;
- forced_move = (row, col);
- game_after_move = game.clone();
- }
- }
- }
-
- match num_moves {
- 0 => panic!("No valid move is possible!"),
- 1 => forced_move,
- _ => {
- let start_time = time::Instant::now();
- let mut depth = STARTING_DEPTH;
- let mut best_move = (0, 0);
-
- while start_time.elapsed() < time::Duration::new(1, 0) {
- if game.get_tempo() + 2 * (depth - 1) >= ( reversi::BOARD_SIZE * reversi::BOARD_SIZE ) as u8 {
- return find_best_move(game, &player, (reversi::BOARD_SIZE * reversi::BOARD_SIZE) as u8 - game.get_tempo());
- } else {
- best_move = find_best_move(game, &player, depth);
- }
- depth += 1;
- }
- best_move
- }
- }
-}
-
-
-
-pub fn find_best_move(game: &reversi::Game, player: &Player, depth: u8) -> (usize, usize) {
-
- if let reversi::Status::Running { current_turn } = game.get_status() {
-
- let ai_eval: fn(&reversi::Game, u8) -> Score = match *player {
- Player::AiMedium => ai_medium::ai_eval,
- Player::Human => panic!("A human is not an AI!")
- };
-
- let mut best_move: Option<MoveScore> = None;
-
- let mut num_moves: u8 = 0;
-
- let (tx, rx): (Sender<MoveScore>, Receiver<MoveScore>) = mpsc::channel();
-
- let mut game_after_move = game.clone();
-
- for (row, &rows) in game.get_board().iter().enumerate() {
- for (col, _) in rows.iter().enumerate() {
- if game_after_move.make_move((row, col)) {
-
- num_moves +=1;
- let thread_tx = tx.clone();
-
- thread::spawn(move || {
- let new_move = MoveScore {
- score: ai_eval(&game_after_move, depth),
- coord: (row, col),
- };
- thread_tx.send(new_move).unwrap();
- });
-
- game_after_move = game.clone();
-
- }
- }
- }
-
- for _ in 0..num_moves {
- let new_move = rx.recv().ok().expect("Could not receive answer");
-
- if let Some(old_move) = best_move.clone() {
- if MoveScore::is_better_for(new_move.clone(), old_move, current_turn) {
- best_move = Some(new_move);
- }
- } else {
- best_move = Some(new_move);
- }
- }
-
- if let Some(some_move) = best_move {
- some_move.coord
- } else {
- panic!("best_eval is None");
- }
-
- } else {
- panic!{"Game ended, cannot make a move!"};
- }
-}
View
307 filesystem/apps/rusthello/src/reversi.rs
@@ -1,307 +0,0 @@
-//! It provides the main structures and mechanics for a Reversi game.
-
-/// There are two players playing the match: Light and Dark
-#[derive(Clone, Copy, PartialEq)]
-pub enum Disk {
- Light,
- Dark,
-}
-
-impl Disk {
- /// Get self's opposite side
- pub fn opposite(&self) -> Disk {
- match *self {
- Disk::Light => Disk::Dark,
- Disk::Dark => Disk::Light,
- }
- }
-}
-
-
-
-/// A game can be in two status: either running (with a next player to play) or ended.
-#[derive(Clone)]
-pub enum Status {
- Running { current_turn: Disk },
- Ended,
-}
-
-
-
-/// Each cell in the board can either be empty or taken by one of the players.
-#[derive(Clone, Copy)]
-pub enum Cell {
- Taken { disk: Disk },
- Empty,
-}
-
-
-
-/// An array listing all the cardinal directions, represented by the coordinate delta to move in that direction.
-/// #Examples
-/// If I am in cell (4, 5) and move NE, I go to cell (4, 5) + (1, -1) = (5, 4).
-pub const DIRECTIONS: [(i8, i8); 8] = [
- ( 1, 0), //North
- ( 1, 1), //NE
- ( 0, 1), //East
- (-1, 1), //SE
- (-1, 0), //South
- (-1, -1), //SW
- ( 0, -1), //West
- ( 1, -1), //NW
- ];
-
-/// The size of the board is a constant.
-pub const BOARD_SIZE: usize = 8;
-
-/// Board is the type of boards, that is, bidimensional arrays of Cells of size BOARD_SIZE.
-pub type Board = [[Cell; BOARD_SIZE]; BOARD_SIZE];
-
-
-
-/// The board is given by a matrix of cells of size BOARD_SIZE and by which player has to move next.
-#[derive(Clone)]
-pub struct Game {
- board: Board,
- status: Status,
- score_light: u8,
- score_dark: u8,
-}
-
-impl Game {
- /// Initializing a new game: starting positions on the board and Dark is the first to play
- pub fn new() -> Game {
- let mut board: Board = [[Cell::Empty; BOARD_SIZE]; BOARD_SIZE];
- board[3][3] = Cell::Taken { disk: Disk::Light };
- board[4][4] = Cell::Taken { disk: Disk::Light };
- board[3][4] = Cell::Taken { disk: Disk::Dark };
- board[4][3] = Cell::Taken { disk: Disk::Dark };
-
- Game {
- board: board,
- status: Status::Running { current_turn: Disk::Dark },
- score_light: 2,
- score_dark: 2,
- }
- }
-
-
-
- /// Returns the game's board
- pub fn get_board(&self) -> &Board {
- &self.board
- }
-
- /// Returns the game's status
- pub fn get_status(&self) -> Status {
- self.status.clone()
- }
-
- /// Returns the current score of the match.
- pub fn get_score(&self) -> (u8, u8) {
- (self.score_light, self.score_dark)
- }
-
- /// Returns the difference in score between Light and Dark.
- pub fn get_score_diff(&self) -> i16 {
- self.score_light as i16 - self.score_dark as i16
- }
-
- /// Returns game's tempo (how many disks there are on the board).
- pub fn get_tempo(&self) -> u8 {
- self.score_light + self.score_dark
- }
-
- /// Returns the board's cell corresponding to the given coordinates.
- pub fn get_cell(&self, (row, col): (usize, usize)) -> Cell {
- self.board[row][col]
- }
-
-
-
- /// Check that a given move is legal
- pub fn check_move (&self, (row, col): (usize, usize)) -> bool {
-
- // If the given coordinate falls out of the board or in a taken cell, the move cannot be legal
- if row >= BOARD_SIZE || col >= BOARD_SIZE {
- return false;
- } else if let Cell::Taken { .. } = self.board[row][col] {
- return false;
- }
-
- if let Status::Running { current_turn } = self.status {
- return self.quick_check_move(current_turn, (row, col));
- }
-
- false
- }
-
-
-
- /// Check that a given move is legal, but faster (by skipping unnecessary checks)
- fn quick_check_move (&self, current_turn: Disk, (row, col): (usize, usize)) -> bool {
-
- // If a move leads to eat in at least one direction, then it is legal
- for &dir in DIRECTIONS.iter() {
- if self.quick_check_move_along_direction(current_turn, (row, col), dir) {
- return true;
- }
- }
-
- false
- }
-
-
-
- /// Check whether a move leads to eat in a specified direction, but faster (by skipping unnecessary checks)
- /// Does NOT perform checks already performed by check_move!
- fn quick_check_move_along_direction (&self, current_turn: Disk, (row, col): (usize, usize), (delta_ns, delta_ew): (i8, i8)) -> bool {
-
- // Need at least two cells' space in the given direction
- let mut col_i8: i8 = col as i8 + 2*delta_ew;
- if ( col_i8 < 0 ) || ( col_i8 >= BOARD_SIZE as i8 ) {
- return false;
- }
-
- let mut row_i8: i8 = row as i8 + 2*delta_ns;
- if ( row_i8 < 0 ) || ( row_i8 >= BOARD_SIZE as i8 ) {
- return false;
- }
-
- // Next cell has to be owned by the other player
- if let Cell::Taken { disk } = self.board[ ( row as i8 + delta_ns ) as usize ][ ( col as i8 + delta_ew ) as usize] {
- if disk == current_turn {
- return false;
- }
-
- while let Some(&rows) = self.board.get(row_i8 as usize) {
- if let Some(&cell) = rows.get(col_i8 as usize) {
- if let Cell::Taken { disk } = cell {
- if disk == current_turn {
- return true;
- }
- col_i8 += delta_ew;
- row_i8 += delta_ns;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
- }
-
- false
- }
-
-
-
-
- /// Eats all of the opponent's occupied cells from a specified cell (given by its coordinates) in a specified direction
- /// until it finds a cell of the current player
- /// WARNING: this function do NOT perform any check about whether or not the move is legal
- fn eat_along_direction (&mut self, current_turn: Disk, (row, col): (usize, usize), (delta_ns, delta_ew): (i8, i8)) {
-
- self.board[ ( row as i8 + delta_ns ) as usize ][ ( col as i8 + delta_ew ) as usize] = Cell::Taken { disk: current_turn };
-
- let (mut row_i8, mut col_i8): (i8, i8) = (row as i8 + 2*delta_ns, col as i8 + 2*delta_ew);
-
- let mut eating: u8 = 1;
-
- while let Some(rows) = self.board.get_mut(row_i8 as usize) {
- if let Some(cell) = rows.get_mut(col_i8 as usize) {
- if let Cell::Taken { disk } = *cell {
- if current_turn == disk {
- break;
- } else {
- *cell = Cell::Taken { disk: current_turn };
- eating += 1;
- row_i8 += delta_ns;
- col_i8 += delta_ew;
- }
- }
- }
- }
-
- match current_turn {
- Disk::Light => {
- self.score_light += eating;
- self.score_dark -= eating;
- }
- Disk::Dark => {
- self.score_light -= eating;
- self.score_dark += eating;
- }
- }
- }
-
-
- /// Current player perform a move, after verifying that it is legal.
- /// It returns whether the move is legal or not.
- pub fn make_move (&mut self, (row, col): (usize, usize)) -> bool {
-
- if row >= BOARD_SIZE || col >= BOARD_SIZE {
- return false;
- } else if let Cell::Taken { .. } = self.board[row][col] {
- return false;
- }
-
- let mut legal: bool = false;
-
- if let Status::Running { current_turn } = self.status {
- for &dir in DIRECTIONS.iter() {
- if self.quick_check_move_along_direction(current_turn, (row, col), dir) {
- self.eat_along_direction(current_turn, (row, col), dir);
- legal = true;
- }
- }
- }
-
- // If a move is legal, the next player to play has to be determined
- // If the opposite player can make any move at all, it gets the turn
- // If not, if the previous player can make any move at all, it gets the turn
- // If not (that is, if no player can make any move at all) the game is ended
- if legal {
- if let Status::Running { current_turn } = self.status {
- self.board[row][col] = Cell::Taken { disk: current_turn };
- match current_turn {
- Disk::Light => self.score_light += 1,
- Disk::Dark => self.score_dark += 1,
- }
-
- if self.get_tempo() == BOARD_SIZE as u8 * BOARD_SIZE as u8 {
- self.status = Status::Ended;
- } else {
- self.status = Status::Running { current_turn: current_turn.opposite() };
- //self.flip_game_status();
- if !self.can_move() {
- self.status = Status::Running { current_turn: current_turn };
- //self.flip_game_status();
- if !self.can_move() {
- self.status = Status::Ended;
- }
- }
- }
- }
- }
-
- legal
- }
-
- /// Returns whether or not next_player can make any move at all.
- fn can_move(&self) -> bool {
- if let Status::Running{ current_turn } = self.status {
- for (row_n, row) in self.board.iter().enumerate() {
- for (col_n, &cell) in row.iter().enumerate() {
- if let Cell::Empty = cell {
- if self.quick_check_move(current_turn, (row_n, col_n)) {
- return true;
- }
- }
- }
- }
- }
- false
- }
-
-}
View
BIN filesystem/ui/apps/rusthello.bmp
Binary file not shown.

0 comments on commit 8ab8e3a

Please sign in to comment.
Something went wrong with that request. Please try again.