diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..0374b42b750b4860743fa919bca141b95d8e8810 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,76 @@ +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust_hepia_lib" +version = "0.1.0" +dependencies = [ + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tp5_connect4" +version = "0.1.0" +dependencies = [ + "rust_hepia_lib 0.1.0", +] + +[[package]] +name = "winapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b313a412738989d7254628a563ec40e01712672d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "tp5_connect4" +version = "0.1.0" +authors = ["sven.wikberg"] + +[dependencies] +rust_hepia_lib = { path = "./src/rust_hepia_lib" } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..b2ca025603c7405dcdddc5e30fa1d924229170b4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,247 @@ +extern crate rust_hepia_lib; + +const WIDTH: usize = 7; +const HEIGHT: usize = 6; + +#[derive(Copy, Clone, PartialEq)] +enum Connect4Case { + Empty, + Red, + Yellow, +} + +struct Connect4 { + board: [[Connect4Case; WIDTH]; HEIGHT], + game_mode: u8, + turn_count: u32, + width: usize, + height: usize, +} + +impl Connect4 { + fn new() -> Connect4 { + Connect4 { + board: [[Connect4Case::Empty; WIDTH]; HEIGHT], + game_mode: 0, + turn_count: 0, + width: WIDTH, + height: HEIGHT, + } + } + + fn get_max_turn(&self) -> u32 { + (self.width as u32 * self.height as u32) + } + + fn is_red_turn(&self) -> bool { + if self.turn_count % 2 == 1 { + return true; + } else { + return false; + } + } + + fn choose_game_mode() -> u8 { + let mut x: i32; + loop { + println!("Choose your game mode : (1) Player vs Player. (2) Player vs IA, easy. (3) Player vs IA, hard."); + x = rust_hepia_lib::read_int(); + match x { + 1 | 2 | 3 => return x as u8, + _ => println!("This is not a valid number"), + } + } + } + + fn place_piece(&mut self, x: usize) -> bool { + if x >= self.width { + // si on veut placer une piece en dehors du tableau + return false; + } + if self.board[0 as usize][x] != Connect4Case::Empty { + // si la colonne est deja pleine + return false; + } + for i in 1..self.height { + if self.board[i][x] != Connect4Case::Empty { + // on place la nouelle piece au dessus de la plus haute piece de la colonne + self.board[i - 1][x] = if self.is_red_turn() { + Connect4Case::Red + } else { + Connect4Case::Yellow + }; + return true; + } + } + self.board[self.height - 1][x] = if self.is_red_turn() { + // si la colonne est vide + Connect4Case::Red + } else { + Connect4Case::Yellow + }; + return true; + } + + fn choose_col(&self) -> usize { + let mut col: i32; + + loop { + println!("Choose a column (1-{})", self.width); + col = rust_hepia_lib::read_int(); + if col < 1 || col > self.width as i32 { + println!("This is not a valid column"); + } else { + return (col - 1) as usize; + } + } + } + + fn play_turn(&mut self) { + self.turn_count += 1; + println!( + "This is {}'s turn !!", + if self.is_red_turn() { 'O' } else { 'X' } + ); + if self.is_red_turn() || self.game_mode == 1 { + let mut col: usize; + loop { + col = self.choose_col(); + if !self.place_piece(col) { + println!("This is not a valid column"); + } else { + break; + } + } + } else if self.game_mode == 2 { + let mut col: usize; + + loop { + col = rust_hepia_lib::gen(0, self.width as i32) as usize; + if self.place_piece(col) { + break; + } + } + } + } + + fn display(&self) { + for y in 0..self.height { + if y == 0 { + print!("┌─"); + for _x in 1..self.width { + print!("┬─"); + } + println!("┐"); + } else { + print!("├─"); + for _x in 1..self.width { + print!("┼─"); + } + println!("┤"); + } + for x in 0..self.width { + print!( + "|{}", + match self.board[y][x] { + Connect4Case::Empty => ' ', + Connect4Case::Red => 'O', + Connect4Case::Yellow => 'X', + } + ); + if x + 1 == self.width { + print!("|"); + } + } + println!(); + } + print!("└─"); + for _x in 1..self.width { + print!("┴─"); + } + println!("┘"); + for x in 1..self.width + 1 { + print!(" {0}", x); + } + println!(); + } + + fn is_won(&self) -> bool { + for y in 0..self.height { + for x in 0..self.width { + if self.board[y][x] != Connect4Case::Empty { + if x + 3 < WIDTH { + // verif horizontale + if self.board[y][x + 1] == self.board[y][x] + && self.board[y][x + 2] == self.board[y][x] + && self.board[y][x + 3] == self.board[y][x] + { + return true; + } + } + if y + 3 < HEIGHT { + // verif verticale + if self.board[y + 1][x] == self.board[y][x] + && self.board[y + 2][x] == self.board[y][x] + && self.board[y + 3][x] == self.board[y][x] + { + return true; + } + } + if x + 3 < WIDTH && y + 3 < HEIGHT { + // verif diagonale 1 : \ + if self.board[y + 1][x + 1] == self.board[y][x] + && self.board[y + 2][x + 2] == self.board[y][x] + && self.board[y + 3][x + 3] == self.board[y][x] + { + return true; + } + } + if x >= 3 && y + 3 < HEIGHT { + // verif diagonale 2 : / + if self.board[y + 1][x - 1] == self.board[y][x] + && self.board[y + 2][x - 2] == self.board[y][x] + && self.board[y + 3][x - 3] == self.board[y][x] + { + return true; + } + } + } + } + } + return false; + } + + fn is_full(&self) -> bool { + if self.turn_count >= self.get_max_turn() { + true + } else { + false + } + } + + fn play_game(&mut self) { + self.game_mode = Connect4::choose_game_mode(); + self.display(); + loop { + self.play_turn(); + self.display(); + if self.is_won() { + if self.is_red_turn() { + println!("O won GG !"); + } else { + println!("X won, well played !"); + } + break; + } + if self.is_full() { + println!("Draw, nobody won !"); + break; + } + } + } +} + +fn main() { + let mut c: Connect4 = Connect4::new(); + c.play_game(); +} diff --git a/src/rust_hepia_lib/Cargo.toml b/src/rust_hepia_lib/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e2c5c02cebc6869b8e09a67f35cf06c69904ed0a --- /dev/null +++ b/src/rust_hepia_lib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust_hepia_lib" +version = "0.1.0" +authors = ["Orestis Malaspinas <orestis.malaspinas@hesge.ch>", + "Orphée Antoniadis <orphee.antoniadis@hesge.ch>"] + +[dependencies] +rand = "0.4" \ No newline at end of file diff --git a/src/rust_hepia_lib/README.md b/src/rust_hepia_lib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..83d20c75a4f66d1bd4e4af064b6670d060dcd4c4 --- /dev/null +++ b/src/rust_hepia_lib/README.md @@ -0,0 +1,49 @@ +# Librairie de Rust pour le cours de programmation séquentielle + +Plusieurs fonctions sont définies dans cette librairie pour simplifier la vie aux étudiants +de première année de programmation séquentielle. + +## Prérequis + +Pour pouvoir utiliser cette librairie, il faut posséder la chaîne de compilation Rust (et une connexion à internet), qui s'installe à l'aide de la commande +sur les systèmes Linux +``` +curl https://sh.rustup.rs -sSf | sh +``` + +Pour d'autres systèmes ou plus d'informations veuillez vous référer au [guide d'installation](https://www.rust-lang.org/en-US/install.html). + +Vous pouvez aussi utiliser le programme de gestion de version `git` (pas obligatoire). + +## Installation + +Pour installer cette librairie vous avez deux choix: + +1. Télécharger le fichier `.zip` en cliquant sur l'icône "download" sur la page <https://githepia.hesge.ch/orestis.malaspin/rust_hepia_lib> (le petit nuage à droite sur la page) +et décompresser l'archive dans le répertoire de votre choix. + +2. *Cloner* un dépôt *git* dans le répertoire de votre choix à l'aide de la commande + +``` +git clone https://githepia.hesge.ch/orestis.malaspin/rust_hepia_lib.git +``` + +Pour plus d'informations sur `git`, il existe une introduction disponible sur [githepia](https://githepia.hesge.ch/orestis.malaspin/git_tutorial). + +## Test + +Une fois la librairie installée vous pouvez tester si elle compile avec la commande + +``` +cargo build +``` + +Si tout se passe bien vous devriez obtenir un message du genre + +``` + Compiling libc v0.2.43 + Compiling rand v0.4.3 + Compiling rust_hepia_lib v0.1.0 (file:///home/orestis.malaspin/svn/projects/rust_hepia_lib) + Finished dev [unoptimized + debuginfo] target(s) in 2.75s +``` + diff --git a/src/rust_hepia_lib/_gitignore b/src/rust_hepia_lib/_gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f38514536f32d535891ef2847d0f07c8cfa77da9 --- /dev/null +++ b/src/rust_hepia_lib/_gitignore @@ -0,0 +1,7 @@ +# Mac +.DS_Store + +# Rust +target +**/*.rs.bk +Cargo.lock diff --git a/src/rust_hepia_lib/_gitlab-ci.yml b/src/rust_hepia_lib/_gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..82dfeb214e928f320bd9bf6ca63a4e3c2bfd2151 --- /dev/null +++ b/src/rust_hepia_lib/_gitlab-ci.yml @@ -0,0 +1,8 @@ +image: omalaspinas/archlinux-pandoc:latest + +build_only: + script: + - cd .. + - git clone https://github.com/milliams/plotlib.git plotlib 2> /dev/null || (cd plotlib; git pull) + - cd rust_hepia_lib + - cargo build \ No newline at end of file diff --git a/src/rust_hepia_lib/src/lib.rs b/src/rust_hepia_lib/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..816b7009eb01fe08c8eb3a014c1bae9866a5a040 --- /dev/null +++ b/src/rust_hepia_lib/src/lib.rs @@ -0,0 +1,87 @@ +extern crate rand; + +use rand::distributions::range::SampleRange; +use rand::Rng; +use std::cmp::PartialOrd; +use std::io; +use std::iter::Rev; +use std::{f32, i32}; + +pub use std::f32::consts::PI; + +pub fn gen<T: SampleRange + PartialOrd + Copy>(min: T, max: T) -> T { + rand::thread_rng().gen_range(min, max) +} + +fn read_line() -> String { + let mut guess = String::new(); + + io::stdin() + .read_line(&mut guess) + .expect("Failed to read line."); + + guess +} + +fn str_to_int<T: std::str::FromStr>(line: &str) -> Option<T> { + match line.trim().parse() { + Ok(num) => Some(num), + Err(_) => None, + } +} + +fn str_to_char(line: &str) -> char { + match line.trim().parse() { + Ok(c) => c, + Err(_) => panic!("This is not a char."), + } +} + +pub fn read_int() -> i32 { + loop { + match str_to_int(&read_line()) { + Some(num) => return num, + None => println!("This is not a number, please enter a number."), + } + } +} + +pub fn read_char() -> char { + str_to_char(&read_line()) +} + +pub fn get_bytes(name: &String) -> &[u8] { + name.as_bytes() +} + +pub fn pow(x: i32, y: i32) -> i32 { + x.pow(y as u32) +} + +pub fn powf(x: f32, y: f32) -> f32 { + x.powf(y) +} + +pub fn abs(num: i32) -> i32 { + num.abs() +} + +pub fn absf(num: f32) -> f32 { + num.abs() +} + +pub fn reverse<T: Sized + DoubleEndedIterator>(it: T) -> Rev<T> { + it.rev() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn str_to_int_test() { + assert_eq!(str_to_int::<i32>("4"), 4); + assert_eq!(str_to_int::<i32>("101"), 101); + assert_eq!(str_to_int::<i32>("-4"), -4); + } +}