Skip to content

Commit 93ad581

Browse files
Add Minimax AI mode
Signed-off-by: estelle.tamalet <estelle.tamalet@etu.umontpellier.fr>
1 parent 830be2a commit 93ad581

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed

topics/tic-tac-toe/src/game.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,74 @@ impl TicTacToe {
9898
}
9999
empty_cells
100100
}
101+
102+
pub fn unplace(&mut self, row: usize, col: usize) {
103+
if row < SIZE && col < SIZE {
104+
self.board[row][col] = Cell::Empty;
105+
}
106+
}
107+
108+
pub fn evaluate(&self) -> i32 {
109+
if let Some(winner) = self.check_winner() {
110+
match winner {
111+
Cell::O => 10, // L'ordinateur (O) gagne
112+
Cell::X => -10, // Le joueur humain (X) gagne
113+
Cell::Empty => 0,
114+
}
115+
} else {
116+
0 // Match nul ou jeu non terminé
117+
}
118+
}
119+
120+
pub fn minimax(&mut self, is_maximizing: bool) -> i32 {
121+
// Vérifier si le jeu est terminé
122+
let score = self.evaluate();
123+
if score == 10 || score == -10 {
124+
return score;
125+
}
126+
127+
if self.is_full() {
128+
return 0; // Match nul
129+
}
130+
131+
if is_maximizing {
132+
// Tour de l'ordinateur (O) - maximiser le score
133+
let mut best_score = i32::MIN;
134+
for (row, col) in self.get_empty_cells() {
135+
self.board[row][col] = Cell::O;
136+
let score = self.minimax(false);
137+
self.board[row][col] = Cell::Empty;
138+
best_score = best_score.max(score);
139+
}
140+
best_score
141+
} else {
142+
// Tour du joueur (X) - minimiser le score
143+
let mut best_score = i32::MAX;
144+
for (row, col) in self.get_empty_cells() {
145+
self.board[row][col] = Cell::X;
146+
let score = self.minimax(true);
147+
self.board[row][col] = Cell::Empty;
148+
best_score = best_score.min(score);
149+
}
150+
best_score
151+
}
152+
}
153+
154+
pub fn find_best_move(&mut self) -> Option<(usize, usize)> {
155+
let mut best_score = i32::MIN;
156+
let mut best_move = None;
157+
158+
for (row, col) in self.get_empty_cells() {
159+
self.board[row][col] = Cell::O;
160+
let score = self.minimax(false);
161+
self.board[row][col] = Cell::Empty;
162+
163+
if score > best_score {
164+
best_score = score;
165+
best_move = Some((row, col));
166+
}
167+
}
168+
169+
best_move
170+
}
101171
}

topics/tic-tac-toe/src/main.rs

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ fn main() {
88
println!("=== Tic-Tac-Toe ===");
99
println!("Choisissez le mode de jeu :");
1010
println!("1. Deux joueurs");
11-
println!("2. Contre l'ordinateur");
11+
println!("2. Contre l'ordinateur (aléatoire)");
12+
println!("3. Contre l'ordinateur (Minimax - imbattable)");
1213
print!("> ");
1314
io::stdout().flush().unwrap();
1415

@@ -19,6 +20,7 @@ fn main() {
1920
match mode {
2021
"1" => play_two_players(),
2122
"2" => play_against_computer(),
23+
"3" => play_against_minimax(),
2224
_ => {
2325
println!("Mode invalide ! Relancez le jeu.");
2426
}
@@ -200,3 +202,102 @@ fn play_against_computer() {
200202
game.display();
201203
println!("\nMerci d'avoir joué !");
202204
}
205+
206+
fn play_against_minimax() {
207+
let mut game = TicTacToe::new();
208+
209+
println!("\n=== Mode Contre l'Ordinateur (Minimax) ===");
210+
println!("Vous êtes X, l'ordinateur est O");
211+
println!("L'ordinateur utilise l'algorithme Minimax - il est imbattable !");
212+
println!("Entrez les coordonnées comme : ligne colonne (1-3)");
213+
println!("Exemple : 1 1 pour le coin supérieur gauche\n");
214+
215+
loop {
216+
game.display();
217+
218+
// Vérifier victoire ou match nul
219+
if let Some(winner) = game.check_winner() {
220+
match winner {
221+
Cell::X => println!("Vous avez gagné ! Incroyable !"),
222+
Cell::O => println!("L'ordinateur a gagné !"),
223+
_ => {}
224+
}
225+
break;
226+
}
227+
228+
if game.is_full() {
229+
println!("Match nul ! Bien joué !");
230+
break;
231+
}
232+
233+
// Tour du joueur humain (X)
234+
println!("Votre tour (X), entrez votre coup (ligne colonne) :");
235+
print!("> ");
236+
io::stdout().flush().unwrap();
237+
238+
let mut input = String::new();
239+
io::stdin().read_line(&mut input).unwrap();
240+
241+
let coords: Vec<&str> = input.trim().split_whitespace().collect();
242+
if coords.len() != 2 {
243+
println!("Entrée invalide ! Utilisez le format : ligne colonne (ex: 1 1)");
244+
continue;
245+
}
246+
247+
let row = match coords[0].parse::<usize>() {
248+
Ok(r) if r >= 1 && r <= 3 => r - 1,
249+
Ok(_) => {
250+
println!("Ligne invalide ! Utilisez un nombre entre 1 et 3.");
251+
continue;
252+
}
253+
Err(_) => {
254+
println!("Ligne invalide !");
255+
continue;
256+
}
257+
};
258+
259+
let col = match coords[1].parse::<usize>() {
260+
Ok(c) if c >= 1 && c <= 3 => c - 1,
261+
Ok(_) => {
262+
println!("Colonne invalide ! Utilisez un nombre entre 1 et 3.");
263+
continue;
264+
}
265+
Err(_) => {
266+
println!("Colonne invalide !");
267+
continue;
268+
}
269+
};
270+
271+
if !game.place(row, col, Cell::X) {
272+
println!("Coup invalide ! La case est déjà occupée.");
273+
continue;
274+
}
275+
276+
// Vérifier victoire après coup du joueur
277+
if let Some(winner) = game.check_winner() {
278+
game.display();
279+
if winner == Cell::X {
280+
println!("Vous avez gagné ! Incroyable !");
281+
}
282+
break;
283+
}
284+
285+
if game.is_full() {
286+
game.display();
287+
println!("Match nul ! Bien joué !");
288+
break;
289+
}
290+
291+
// Tour de l'ordinateur (O) avec Minimax
292+
println!("\nL'ordinateur calcule le meilleur coup (Minimax)...");
293+
std::thread::sleep(std::time::Duration::from_millis(500));
294+
295+
if let Some((comp_row, comp_col)) = game.find_best_move() {
296+
game.place(comp_row, comp_col, Cell::O);
297+
println!("L'ordinateur joue en ({}, {})", comp_row + 1, comp_col + 1);
298+
}
299+
}
300+
301+
game.display();
302+
println!("\nMerci d'avoir joué !");
303+
}

0 commit comments

Comments
 (0)