tictactoe/
tictactoe.rs

1use prettytable::{cell, table, Table};
2
3use std::io;
4use std::io::Write;
5use std::str::FromStr;
6
7const CROSS: &'static str = "X";
8const EMPTY: &'static str = " ";
9const ROUND: &'static str = "O";
10
11fn main() {
12    let mut table = table![
13        [EMPTY, EMPTY, EMPTY],
14        [EMPTY, EMPTY, EMPTY],
15        [EMPTY, EMPTY, EMPTY]
16    ];
17    let mut height = table.print_tty(false).unwrap();
18    let stdin = io::stdin();
19    let mut stdout = io::stdout();
20    let mut current = CROSS;
21    let mut terminal = term::stdout().unwrap();
22    loop {
23        let mut line = String::new();
24        print!("{} plays > ", current);
25        height += 1;
26        stdout.flush().unwrap();
27        stdin.read_line(&mut line).expect("Cannot read input");
28        let i = match usize::from_str(line.trim()) {
29            Ok(i) => i,
30            _ => {
31                println!("Bad input");
32                height += 1;
33                continue;
34            }
35        };
36        if i < 1 || i > 9 {
37            println!("Bad input, should be between 1 and 9");
38            height += 1;
39            continue;
40        }
41        let x = (i - 1) % 3;
42        let y = (i - 1) / 3;
43        {
44            let row = table.get_mut_row(y).unwrap();
45            if row.get_cell(x).unwrap().to_string() != EMPTY {
46                println!("There's already someone there");
47                height += 1;
48                continue;
49            }
50            row.set_cell(cell!(current), x).unwrap();
51        }
52        for _ in 0..height {
53            terminal.cursor_up().unwrap();
54            terminal.delete_line().unwrap();
55        }
56        height = table.print_tty(false).unwrap();
57        if check(&table) {
58            return;
59        }
60        if current == CROSS {
61            current = ROUND;
62        } else {
63            current = CROSS;
64        }
65    }
66}
67
68fn get(table: &Table, x: usize, y: usize) -> String {
69    match table.get_row(y) {
70        Some(r) => match r.get_cell(x) {
71            Some(c) => c.to_string(),
72            _ => EMPTY.to_string(),
73        },
74        _ => EMPTY.to_string(),
75    }
76}
77
78fn is(table: &Table, s: &str, x: usize, y: usize) -> bool {
79    get(table, x, y).as_str() == s
80}
81
82fn check(table: &Table) -> bool {
83    let mut full = true;
84    for y in 0..3 {
85        for x in 0..3 {
86            if is(table, EMPTY, x, y) {
87                full = false;
88                continue;
89            }
90            let current = get(table, x, y);
91            let c = current.as_str();
92            if is(table, c, x + 1, y) && is(table, c, x + 2, y)
93                || is(table, c, x + 1, y + 1) && is(table, c, x + 2, y + 2)
94                || x >= 2 && is(table, c, x - 1, y + 1) && is(table, c, x - 2, y + 2)
95                || is(table, c, x, y + 1) && is(table, c, x, y + 2)
96            {
97                println!("Game is over. {} is the winner", current);
98                return true;
99            }
100        }
101    }
102    if full {
103        println!("Game is over. It's a draw");
104    }
105    full
106}