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);
+	}
+}