diff --git a/src/board/tictactoe.rs b/src/board/tictactoe.rs
index 20957216f28b9f2e76eb3282a2debf8f1f3a11bc..b5a44a390c77b6df58ddc90bea46246baf0a0a6f 100644
--- a/src/board/tictactoe.rs
+++ b/src/board/tictactoe.rs
@@ -6,6 +6,23 @@ pub enum Player {
     Circle,
 }
 
+impl Default for Player {
+    fn default() -> Self {
+        Player::Cross
+    }
+}
+
+impl std::ops::Not for Player {
+    type Output = Player;
+
+    fn not(self) -> Self::Output {
+        match self {
+            Player::Cross => Player::Circle,
+            Player::Circle => Player::Cross,
+        }
+    }
+}
+
 impl std::fmt::Display for Player {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
@@ -50,9 +67,24 @@ pub enum Move {
     C3 = 8,
 }
 
-// impl std::fmt::FromStr for Move {
-
-// }
+impl FromStr for Move {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "A1" => Ok(Move::A1),
+            "B1" => Ok(Move::B1),
+            "C1" => Ok(Move::C1),
+            "A2" => Ok(Move::A2),
+            "B2" => Ok(Move::B2),
+            "C2" => Ok(Move::C2),
+            "A3" => Ok(Move::A3),
+            "B3" => Ok(Move::B3),
+            "C3" => Ok(Move::C3),
+            _ => Err(format!("Illegal move {}", s))
+        }
+    }
+}
 
 impl Board {
     /// Creates a new empty board
diff --git a/src/main.rs b/src/main.rs
index 4fd4a6b492981a6264ec004eac8bd7548dc03148..6c0d36e2c41d289e2828e335b1970151bde36f9c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,51 @@
 mod board;
 
 use board::tictactoe::Board as TicTacToe;
-    
+use board::Board;
+use std::io;
+use std::io::prelude::*;
+
+fn prompt<Player>(turn: Player)
+where
+    Player: std::fmt::Display,
+{
+    print!("{}>", turn);
+    io::stdout().flush().unwrap();
+}
+
+fn play<Game, Move, Player>(mut board: Game)
+where
+    Game: Board<Move, Player> + std::fmt::Display,
+    Player: Copy + std::fmt::Display + Default + std::ops::Not<Output = Player>,
+    Move: std::str::FromStr,
+{
+    let mut stdin = std::io::stdin().lock();
+    let mut player = Player::default();
+
+    println!("Let start");
+    println!("{board}");
+    prompt(player);
+
+    loop {
+        let mut input = String::default();
+        stdin.read_line(&mut input).unwrap();
+        let str_move = input.trim_end();
+        if let Ok(m) = str_move.parse() {
+            board.do_move(player, &m);
+            println!("{board}");
+            if board.has_won(player) {
+                println!("Victory!");
+                return;
+            }
+            player = !player;
+        }
+        prompt(player);
+    }
+}
+
 fn main() {
     let board = TicTacToe::new();
-
-    board.display();
+    play(board);
 }
 
 // Will only be build in 'test' configuration