tictactoe_rust/ai/
mod.rs

1extern crate rand;
2
3use super::board::Board;
4use super::game::{Players, SmartLevel};
5use rand::Rng;
6
7pub struct Ai;
8
9impl Ai {
10    pub fn ask_ai_move_input(board: &Board, player: Players, level: SmartLevel) -> (usize, usize) {
11        match level {
12            SmartLevel::Kindergarden => ai_generate_random_valid_tuple(board, player),
13            SmartLevel::Elementary | _ => {
14                let available_to_win_tuples = get_avaiable_to_win_tuples(board, player);
15                let opponent = match player {
16                    Players::Player1 => Players::Player2,
17                    Players::Player2 => Players::Player1,
18                };
19                let opponent_available_to_win_tuples = get_avaiable_to_win_tuples(board, opponent);
20                if !available_to_win_tuples.is_empty() {
21                    available_to_win_tuples[0]
22                } else if !opponent_available_to_win_tuples.is_empty() {
23                    opponent_available_to_win_tuples[0]
24                } else {
25                    ai_generate_random_valid_tuple(board, player)
26                }
27            }
28        }
29    }
30}
31
32fn ai_generate_random_valid_tuple(board: &Board, player: Players) -> (usize, usize) {
33    let v = get_available_tuples(board, player);
34    let random_valid_index = rand::thread_rng().gen_range(0, v.len());
35    v[random_valid_index]
36}
37
38fn get_avaiable_to_win_tuples(board: &Board, player: Players) -> Vec<(usize, usize)> {
39    let v = get_available_tuples(board, player);
40    let mut new_v: Vec<(usize, usize)> = Vec::new();
41    let get_the_other_two_player = |two: [(usize, usize); 2]| {
42        (
43            board.board[tuple_to_index(two[0])],
44            board.board[tuple_to_index(two[1])],
45        )
46    };
47    let if_three_player_matches =
48        |three: [Option<Players>; 3]| three[0] == three[1] && three[0] == three[2];
49    for coor in v.iter() {
50        let other_two_player_row =
51            get_the_other_two_player(get_the_other_two_on(LineType::Row, coor.clone()));
52        let other_two_player_col =
53            get_the_other_two_player(get_the_other_two_on(LineType::Col, coor.clone()));
54        let mut flag_diag0 = false;
55        let mut flag_diag1 = false;
56        if coor.0 == coor.1 {
57            // Diag0
58            let other_two_player_diag0 =
59                get_the_other_two_player(get_the_other_two_on(LineType::Diag0, coor.clone()));
60            flag_diag0 = if_three_player_matches([
61                other_two_player_diag0.0,
62                other_two_player_diag0.1,
63                Some(player),
64            ]);
65        } else if 2 - coor.0 == coor.1 {
66            //Diag1
67            let other_two_player_diag1 =
68                get_the_other_two_player(get_the_other_two_on(LineType::Diag1, coor.clone()));
69            flag_diag1 = if_three_player_matches([
70                other_two_player_diag1.0,
71                other_two_player_diag1.1,
72                Some(player),
73            ]);
74        }
75        if if_three_player_matches([other_two_player_row.0, other_two_player_row.1, Some(player)])
76            || if_three_player_matches([
77                other_two_player_col.0,
78                other_two_player_col.1,
79                Some(player),
80            ])
81            || flag_diag0
82            || flag_diag1
83        {
84            new_v.push(coor.clone());
85        }
86    }
87    new_v
88}
89
90fn get_available_tuples(board: &Board, _player: Players) -> Vec<(usize, usize)> {
91    let mut v: Vec<(usize, usize)> = Vec::with_capacity(9);
92    for (i, cell) in board.board.iter().enumerate() {
93        if cell.is_none() {
94            v.push(index_to_tuple(i));
95        }
96    }
97    v
98}
99
100fn index_to_tuple(index: usize) -> (usize, usize) {
101    (index % 3, index / 3)
102}
103
104fn tuple_to_index(tuple: (usize, usize)) -> usize {
105    tuple.1 * 3 + tuple.0
106}
107
108#[allow(dead_code)]
109fn check_cell_is(board: &Board, cell: (usize, usize), player: Players) -> bool {
110    let option = board.board[tuple_to_index(cell)];
111    option.is_some() && option.unwrap() == player
112}
113
114fn get_the_other_two_on(linetype: LineType, cell: (usize, usize)) -> [(usize, usize); 2] {
115    let get_two_other_num = |num| match num {
116        0 => [1, 2],
117        1 => [0, 2],
118        2 => [0, 1],
119        _ => {
120            panic!("wrong num");
121        }
122    };
123    match linetype {
124        LineType::Row => {
125            let row_line = cell.1;
126            let other_two = get_two_other_num(cell.0);
127            [(other_two[0], row_line), (other_two[1], row_line)]
128        }
129        LineType::Col => {
130            let col_line = cell.0;
131            let other_two = get_two_other_num(cell.1);
132            [(col_line, other_two[0]), (col_line, other_two[1])]
133        }
134        LineType::Diag0 => {
135            assert_eq!(cell.0, cell.1);
136            let other_two = get_two_other_num(cell.1);
137            [(other_two[0], other_two[0]), (other_two[1], other_two[1])]
138        }
139        LineType::Diag1 => {
140            assert_eq!(2 - cell.0, cell.1);
141            let other_two = get_two_other_num(cell.1);
142            [
143                (2 - other_two[0], other_two[0]),
144                (2 - other_two[1], other_two[1]),
145            ]
146        }
147    }
148}
149
150enum LineType {
151    Row,
152    Col,
153    Diag0,
154    Diag1,
155}
156#[cfg(test)]
157mod tests;