tictactoe/
tictactoe.rs

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