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 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 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;