1fn to_str(peg: u8) -> String {
10 match peg {
11 0 => String::from(" "),
12 1 => String::from("X"),
13 2 => String::from("O"),
14 _ => panic!("Illegal peg value!"),
15 }
16}
17
18fn print_intro() {
19 println!("TIC TAC TOE\n");
20 println!("Get 3 in a row horizontally, vertically or diagonally to win.\n");
21 println!("Me first!\n");
22}
23
24fn print_board(board: &[[u8; 3]; 3]) {
25 println!(" A B C");
26 println!(" +---+---+---+");
27 println!(
28 "1 | {} | {} | {} |",
29 to_str(board[0][0]),
30 to_str(board[0][1]),
31 to_str(board[0][2]),
32 );
33 println!(" +---+---+---+");
34 println!(
35 "2 | {} | {} | {} |",
36 to_str(board[1][0]),
37 to_str(board[1][1]),
38 to_str(board[1][2]),
39 );
40 println!(" +---+---+---+");
41 println!(
42 "3 | {} | {} | {} |",
43 to_str(board[2][0]),
44 to_str(board[2][1]),
45 to_str(board[2][2]),
46 );
47 println!(" +---+---+---+");
48}
49
50fn query_move() -> [usize; 2] {
51 let mut posvec = [0usize; 2];
52 let mut tries = 0;
53 while tries < 5 {
54 tries += 1;
55
56 println!("\nWhat is your move? ");
57 let mut move_str = String::new();
58 let len = std::io::stdin()
59 .read_line(&mut move_str)
60 .expect("read_line failed!");
61 if len != 3 {
62 println!("?");
63 continue;
64 }
65 move_str = move_str.to_uppercase();
66
67 let chars: Vec<&str> = move_str.split("").collect::<Vec<&str>>(); match chars[1] {
69 "A" => {
70 posvec[0] = 0;
71 }
72 "B" => {
73 posvec[0] = 1;
74 }
75 "C" => {
76 posvec[0] = 2;
77 }
78 _ => {
79 println!("?");
80 continue;
81 }
82 }
83 match chars[2] {
84 "1" => {
85 posvec[1] = 0;
86 }
87 "2" => {
88 posvec[1] = 1;
89 }
90 "3" => {
91 posvec[1] = 2;
92 }
93 _ => {
94 println!("?");
95 continue;
96 }
97 }
98 break;
99 }
100
101 if tries == 5 {
102 panic!("No answer given.")
103 }
104
105 posvec
106}
107
108fn posvec_to_move(posvec: &[usize; 2]) -> String {
109 let mut posstring = String::new();
110 match posvec[0] {
111 0 => {
112 posstring.push_str("A");
113 }
114 1 => {
115 posstring.push_str("B");
116 }
117 2 => {
118 posstring.push_str("C");
119 }
120 _ => {
121 panic!("Illegal value!");
122 }
123 }
124 match posvec[1] {
125 0 => {
126 posstring.push_str("1");
127 }
128 1 => {
129 posstring.push_str("2");
130 }
131 2 => {
132 posstring.push_str("3");
133 }
134 _ => {
135 panic!("Illegal value!");
136 }
137 }
138 posstring
139}
140
141fn decode_candidate(triplet: &[[usize; 2]; 3], the_board: &[[u8; 3]; 3]) -> [u8; 3] {
144 let mut candidate = [0u8; 3];
148 let mut i = 0;
149 for pos in triplet.iter() {
150 candidate[i] = the_board[pos[1]][pos[0]];
151 i += 1;
152 }
153 return candidate;
154}
155
156fn decode_result(
157 found_match: &[u8; 3],
158 triplet: &[[usize; 2]; 3],
159 the_board: &[[u8; 3]; 3],
160) -> [usize; 2] {
161 for i in 0..3 {
165 let peg = the_board[triplet[i][1]][triplet[i][0]];
166 if peg == found_match[i] {
167 continue;
168 }
169 return triplet[i];
170 }
171 panic!("found_match: not legal values");
172}
173
174fn check_candidate(candidate: [u8; 3]) -> Option<([u8; 3], u8)> {
175 let state = [
182 [[1, 1, 0], [1, 1, 1]],
183 [[1, 0, 1], [1, 1, 1]],
184 [[0, 1, 1], [1, 1, 1]],
185 [[2, 2, 0], [2, 2, 1]],
186 [[2, 0, 2], [2, 1, 2]],
187 [[0, 2, 2], [1, 2, 2]],
188 [[1, 0, 0], [1, 1, 0]],
189 [[0, 1, 0], [0, 1, 1]],
190 [[0, 0, 1], [1, 0, 1]],
191 [[2, 0, 0], [2, 1, 0]],
192 [[0, 2, 0], [1, 2, 0]],
193 [[0, 0, 2], [0, 1, 2]],
194 [[0, 0, 0], [0, 1, 0]],
195 ];
196 let priority = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3];
197
198 let mut i = 0;
199 for [pos, mov] in state {
200 if pos == candidate {
201 return Some((mov, priority[i]));
202 }
203 i += 1;
204 }
205 return None;
206}
207
208fn find_empty_pos(the_board: &[[u8; 3]; 3]) -> [usize; 2] {
209 for j in 0..3 {
210 for i in 0..3 {
211 if the_board[i][j] == 0 {
212 return [j, i];
213 }
214 }
215 }
216 panic!("No empty positions on the board!")
217}
218
219fn make_move(the_board: &[[u8; 3]; 3]) -> [usize; 2] {
220 let triplet_list = [
221 [[0, 0], [1, 1], [2, 2]], [[0, 2], [1, 1], [2, 0]], [[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]],
226 [[2, 0], [2, 1], [2, 2]],
227 [[0, 0], [1, 0], [2, 0]], [[0, 1], [1, 1], [2, 1]],
229 [[0, 2], [1, 2], [2, 2]],
230 ];
231 let mut do_move = [1, 1];
232 let mut do_priority = 4;
233 for triplet in triplet_list.iter() {
234 let candidate = decode_candidate(&triplet, &the_board);
235
236 match check_candidate(candidate) {
237 None => continue,
238 Some((found_match, priority)) => {
239 let candidate_move = decode_result(&found_match, &triplet, &the_board);
240 if priority < do_priority {
241 do_priority = priority;
242 do_move = candidate_move;
243 }
244 }
245 }
246 }
247 if do_priority == 4 {
248 do_move = find_empty_pos(&the_board);
249 }
250 return do_move;
251}
252
253fn is_empty(your_move: &[usize; 2], the_board: &[[u8; 3]; 3]) -> bool {
256 return the_board[your_move[1]][your_move[0]] == 0;
257}
258
259fn game_won(the_board: &[[u8; 3]; 3]) -> bool {
260 let triplet_list = [
261 [[0, 0], [1, 1], [2, 2]], [[0, 2], [1, 1], [2, 0]], [[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]],
266 [[2, 0], [2, 1], [2, 2]],
267 [[0, 0], [1, 0], [2, 0]], [[0, 1], [1, 1], [2, 1]],
269 [[0, 2], [1, 2], [2, 2]],
270 ];
271 'triplet_loop: for triplet in triplet_list.iter() {
272 let peg = the_board[triplet[0][0]][triplet[0][1]];
273 if peg == 0 {
275 continue 'triplet_loop;
276 }
277 for pos in triplet.iter() {
279 if the_board[pos[0]][pos[1]] != peg {
280 continue 'triplet_loop;
281 }
282 }
283 return true;
284 }
285 return false;
286}
287
288fn board_full(the_board: &[[u8; 3]; 3]) -> bool {
289 let mut empty_count = 0;
290
291 for row in 0..3 {
293 for col in 0..3 {
294 if the_board[row][col] == 0 {
295 empty_count += 1;
296 }
297 }
298 }
299
300 return empty_count == 0;
301}
302
303pub fn play() {
312 let mut board = [[0u8; 3]; 3];
313
314 print_intro();
315
316 'game_loop: loop {
317 let my_move = make_move(&board);
318 board[my_move[1]][my_move[0]] = 1;
319 print_board(&board);
320 println!("\nMy move was {}", posvec_to_move(&my_move));
321 if game_won(&board) {
322 print!("\nI won!");
323 break 'game_loop;
324 }
325
326 if board_full(&board) {
327 print!("\nIt's a tie.");
328 break 'game_loop;
329 }
330
331 'query_loop: loop {
332 let your_move = query_move();
333 if !is_empty(&your_move, &board) {
334 println!("{} is occupied!", posvec_to_move(&your_move));
335 continue 'query_loop;
336 }
337
338 board[your_move[1]][your_move[0]] = 2;
339 print_board(&board);
340 if game_won(&board) {
341 println!("\nYou won!");
342 break 'game_loop;
343 }
344 break 'query_loop;
345 }
346 println!("\n-------------------\n")
347 }
348 println!("\nGAME OVER\n");
349}