ajout de message en cas de victoires / draws...

master
Leo TUAILLON 2 years ago
parent 62863dc4ef
commit bba3d1a72d

@ -59,69 +59,75 @@ class AI {
const random_index = Math.floor(Math.random() * available_moves.length); const random_index = Math.floor(Math.random() * available_moves.length);
return available_moves[random_index]; return available_moves[random_index];
} }
minimax(matrix, maximizing) {
const caseStatus = matrix.winner();
if (caseStatus === 1) return { value: 1, move: null }; minimax(matrix, depth, maximizingPlayer) {
if (caseStatus === 2) return { value: -1, move: null }; const winner = this.check_winner(matrix);
if (matrix.isFull()) return { value: 0, move: null };
let bestValue; if (winner !== 0) {
let bestMove = null; return winner === this.player ? 10 - depth : depth - 10;
const emptyCases = matrix.getEmptyCases(); }
if (maximizing) {
bestValue = -Infinity;
for (const [row, col] of emptyCases) {
const tempMatrix = matrix.clone();
tempMatrix.markCase(row, col, 1);
const { value } = this.minimax(tempMatrix, false);
if (value > bestValue) { if (matrix.is_full()) {
bestValue = value; return 0;
bestMove = [row, col];
} }
if (depth >= this.level) {
return 0;
} }
} else {
bestValue = Infinity;
for (const [row, col] of emptyCases) { const opponent = 3 - this.player;
const tempMatrix = matrix.clone(); const currentPlayer = maximizingPlayer ? this.player : opponent;
tempMatrix.markCase(row, col, this.player);
const { value } = this.minimax(tempMatrix, true); let bestScore = maximizingPlayer ? -Infinity : Infinity;
if (value < bestValue) { for (let row = 0; row < 3; row++) {
bestValue = value; for (let col = 0; col < 3; col++) {
bestMove = [row, col]; if (matrix.cases[row][col] === 0) {
matrix.mark(row, col, currentPlayer);
const score = this.minimax(matrix, depth + 1, !maximizingPlayer);
matrix.cases[row][col] = 0;
matrix.marked_case--;
bestScore = maximizingPlayer ? Math.max(bestScore, score) : Math.min(bestScore, score);
} }
} }
} }
return { value: bestValue, move: bestMove }; return bestScore;
} }
evaluate(mainMatrix) { check_winner(matrix) {
if (this.level === 0) { for (let player = 1; player <= 2; player++) {
const emptyCases = mainMatrix.getEmptyCases(); if (matrix.check_win(player)) {
const randomIndex = Math.floor(Math.random() * emptyCases.length); return player;
return emptyCases[randomIndex];
} else {
const { move } = this.minimax(mainMatrix, false);
return move;
} }
} }
return 0;
}
move(matrix) { move(matrix) {
if (this.level === 0) { let bestScore = -Infinity;
return this.random_move(matrix); let bestMove = null;
} else {
return this.evaluate(matrix); for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
if (matrix.cases[row][col] === 0) {
matrix.mark(row, col, this.player);
const score = this.minimax(matrix, 1, false);
matrix.cases[row][col] = 0;
matrix.marked_case--;
if (score > bestScore) {
bestScore = score;
bestMove = [row, col];
}
}
}
} }
return bestMove;
} }
} }
class Game { class Game {
@ -135,32 +141,42 @@ class Game {
} }
updateBoard() { updateBoard() {
const gameBoard = document.getElementById('game-board'); const cells = document.querySelectorAll('.cell');
gameBoard.innerHTML = '';
this.matrix.cases.forEach((row, rowIndex) => { this.matrix.cases.forEach((row, rowIndex) => {
const rowElement = document.createElement('div');
rowElement.classList.add('row');
row.forEach((cell, cellIndex) => { row.forEach((cell, cellIndex) => {
const cellElement = document.createElement('div'); const cellElement = cells[rowIndex * 3 + cellIndex];
cellElement.classList.add('cell'); cellElement.classList.remove('x', 'o');
cellElement.dataset.mark = cell; if (cell === 1) {
cellElement.addEventListener('click', () => this.handleCellClick(rowIndex, cellIndex)); cellElement.classList.add('x');
rowElement.appendChild(cellElement); } else if (cell === 2) {
cellElement.classList.add('o');
}
});
}); });
}
gameBoard.appendChild(rowElement); displayMessage(messageId) {
const messages = ['p1win', 'p2win', 'aiwin', 'draw'];
messages.forEach((id) => {
const element = document.getElementById(id);
if (id === messageId) {
element.style.display = 'block';
} else {
element.style.display = 'none';
}
}); });
} }
handleCellClick(row, col) { handleCellClick(row, col) {
if (this.running && this.matrix.mark(row, col, this.player)) { if (this.running && this.matrix.mark(row, col, this.player)) {
if (this.matrix.check_win(this.player)) { if (this.matrix.check_win(this.player)) {
alert(`Le joueur ${this.player} a gagné !`); this.displayMessage(this.player === 1 ? 'p1win' : 'p2win');
this.running = false; this.running = false;
} else if (this.matrix.is_full()) { } else if (this.matrix.is_full()) {
alert("Match nul !"); this.displayMessage('draw');
this.running = false; this.running = false;
} else { } else {
this.player = 3 - this.player; this.player = 3 - this.player;
@ -169,10 +185,10 @@ class Game {
if (ai_row !== null && ai_col !== null) { if (ai_row !== null && ai_col !== null) {
this.matrix.mark(ai_row, ai_col, this.ai.player); this.matrix.mark(ai_row, ai_col, this.ai.player);
if (this.matrix.check_win(this.ai.player)) { if (this.matrix.check_win(this.ai.player)) {
alert(`L'IA a gagné !`); this.displayMessage('aiwin');
this.running = false; this.running = false;
} else if (this.matrix.is_full()) { } else if (this.matrix.is_full()) {
alert("Match nul !"); this.displayMessage('draw');
this.running = false; this.running = false;
} }
} }
@ -183,6 +199,17 @@ class Game {
} }
} }
setupEventListeners() {
const cells = document.querySelectorAll('[data-cell]');
cells.forEach((cell, index) => {
const row = Math.floor(index / 3);
const col = index % 3;
cell.addEventListener('click', () => this.handleCellClick(row, col));
});
}
start() { start() {
this.running = true; this.running = true;
this.matrix = new Matrix(); this.matrix = new Matrix();
@ -192,9 +219,32 @@ class Game {
this.player = 3 - this.player; this.player = 3 - this.player;
} }
this.updateBoard(); this.updateBoard();
const messages = ['p1win', 'p2win', 'aiwin', 'draw'];
messages.forEach((id) => {
document.getElementById(id).style.display = 'none';
});
} }
} }
const cellElements = document.querySelectorAll('[data-cell]');
cellElements.forEach((cell, index) => {
cell.addEventListener('click', () => {
const row = Math.floor(index / 3);
const col = index % 3;
game.handleCellClick(row, col);
});
});
const game = new Game(); const game = new Game();
game.start(); game.start();
const resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => game.start());
const aiButton = document.getElementById('ai');
aiButton.addEventListener('click', () => {
game.gamemode = game.gamemode === 'ai' ? 'player' : 'ai';
game.start();
});

@ -1,225 +0,0 @@
class Matrix {
constructor() {
this.cases = Array(3).fill(null).map(() => Array(3).fill(0));
this.marked_case = 0;
}
is_full() {
return this.marked_case === 9;
}
mark(row, col, player) {
if (this.cases[row][col] === 0) {
this.cases[row][col] = player;
this.marked_case++;
return true;
}
return false;
}
check_win(player) {
const win_combinations = [
[[0, 0], [0, 1], [0, 2]],
[[1, 0], [1, 1], [1, 2]],
[[2, 0], [2, 1], [2, 2]],
[[0, 0], [1, 0], [2, 0]],
[[0, 1], [1, 1], [2, 1]],
[[0, 2], [1, 2], [2, 2]],
[[0, 0], [1, 1], [2, 2]],
[[0, 2], [1, 1], [2, 0]],
];
return win_combinations.some(combination =>
combination.every(([row, col]) => this.cases[row][col] === player)
);
}
}
class AI {
constructor(level = 5, player = 2) {
this.level = level;
this.player = player;
}
random_move(matrix) {
const available_moves = [];
matrix.cases.forEach((row, i) => {
row.forEach((cell, j) => {
if (cell === 0) {
available_moves.push([i, j]);
}
});
});
if (available_moves.length === 0) {
return null;
}
const random_index = Math.floor(Math.random() * available_moves.length);
return available_moves[random_index];
}
minimax(matrix, depth, maximizingPlayer) {
const winner = this.check_winner(matrix);
if (winner !== 0) {
return winner === this.player ? 10 - depth : depth - 10;
}
if (matrix.is_full()) {
return 0;
}
if (depth >= this.level) {
return 0;
}
const opponent = 3 - this.player;
const currentPlayer = maximizingPlayer ? this.player : opponent;
let bestScore = maximizingPlayer ? -Infinity : Infinity;
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
if (matrix.cases[row][col] === 0) {
matrix.mark(row, col, currentPlayer);
const score = this.minimax(matrix, depth + 1, !maximizingPlayer);
matrix.cases[row][col] = 0;
matrix.marked_case--;
bestScore = maximizingPlayer ? Math.max(bestScore, score) : Math.min(bestScore, score);
}
}
}
return bestScore;
}
check_winner(matrix) {
for (let player = 1; player <= 2; player++) {
if (matrix.check_win(player)) {
return player;
}
}
return 0;
}
move(matrix) {
let bestScore = -Infinity;
let bestMove = null;
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
if (matrix.cases[row][col] === 0) {
matrix.mark(row, col, this.player);
const score = this.minimax(matrix, 1, false);
matrix.cases[row][col] = 0;
matrix.marked_case--;
if (score > bestScore) {
bestScore = score;
bestMove = [row, col];
}
}
}
}
return bestMove;
}
}
class Game {
constructor() {
this.matrix = new Matrix();
this.ai = new AI();
this.player = 1;
this.gamemode = 'ai';
this.running = true;
this.ai_starts = false;
}
updateBoard() {
const cells = document.querySelectorAll('.cell');
this.matrix.cases.forEach((row, rowIndex) => {
row.forEach((cell, cellIndex) => {
const cellElement = cells[rowIndex * 3 + cellIndex];
cellElement.classList.remove('x', 'o');
if (cell === 1) {
cellElement.classList.add('x');
} else if (cell === 2) {
cellElement.classList.add('o');
}
});
});
}
handleCellClick(row, col) {
if (this.running && this.matrix.mark(row, col, this.player)) {
if (this.matrix.check_win(this.player)) {
alert(`Le joueur ${this.player} a gagné !`);
this.running = false;
} else if (this.matrix.is_full()) {
alert("Match nul !");
this.running = false;
} else {
this.player = 3 - this.player;
if (this.gamemode === 'ai' && this.player === this.ai.player) {
const [ai_row, ai_col] = this.ai.move(this.matrix);
if (ai_row !== null && ai_col !== null) {
this.matrix.mark(ai_row, ai_col, this.ai.player);
if (this.matrix.check_win(this.ai.player)) {
alert(`L'IA a gagné !`);
this.running = false;
} else if (this.matrix.is_full()) {
alert("Match nul !");
this.running = false;
}
}
this.player = 3 - this.player;
}
}
this.updateBoard();
}
}
setupEventListeners() {
const cells = document.querySelectorAll('[data-cell]');
cells.forEach((cell, index) => {
const row = Math.floor(index / 3);
const col = index % 3;
cell.addEventListener('click', () => this.handleCellClick(row, col));
});
}
start() {
this.running = true;
this.matrix = new Matrix();
if (this.gamemode === 'ai' && this.ai_starts) {
const [ai_row, ai_col] = this.ai.move(this.matrix);
this.matrix.mark(ai_row, ai_col, this.ai.player);
this.player = 3 - this.player;
}
this.updateBoard();
this.setupEventListeners();
}
}
const game = new Game();
game.start();
const resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => game.start());
const aiButton = document.getElementById('ai');
aiButton.addEventListener('click', () => {
game.gamemode = game.gamemode === 'ai' ? 'player' : 'ai';
game.start();
});

@ -1,48 +1,94 @@
body { body {
font-family: Arial, sans-serif;
display: flex; display: flex;
flex-direction: column; justify-content: center;
align-items: center; align-items: center;
background-color: #f0f0f0; height: 100vh;
} margin: 0;
background-color: #1E2125;
h1 { font-family: "Arial", sans-serif;
margin-bottom: 1rem;
} }
#game-board { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 300px; align-items: center;
height: 300px;
background-color: white;
border: 1px solid black;
} }
.row { .grid {
display: flex; display: grid;
flex-direction: row; grid-template-columns: repeat(3, 100px);
height: 33.33%; grid-template-rows: repeat(3, 100px);
gap: 15px;
margin-bottom: 25px;
} }
.cell { .cell {
width: 100px;
height: 100px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 33.33%; background-color: #2C3036;
height: 100%; color: #FFFFFF;
border: 1px solid black; font-size: 24px;
font-weight: bold;
cursor: pointer; cursor: pointer;
} }
.cell[data-mark="1"]::before { button {
padding: 10px 20px;
background-color: #1E2125;
color: #FFFFFF;
border: 2px solid #FFFFFF;
font-size: 16px;
cursor: pointer;
margin: 5px;
}
button:hover {
background-color: #2C3036;
}
.cell.x::after {
content: "X"; content: "X";
font-size: 2rem; color: #4fb2d3;
color: #2196F3;
} }
.cell[data-mark="2"]::before { .cell.o::after {
content: "O"; content: "O";
font-size: 2rem; color: #b40e0e;
color: #F44336; }
h1{
color: #FFFFFF;
font-size: 24px;
font-weight: bold;
margin: 0;
margin-bottom: 25px;
}
.winnerMessage {
color: #FFFFFF;
font-size: 18px;
font-weight: bold;
margin: 0;
margin-bottom: 15px;
}
#aiwin {
color: #b40e0e;
display: none;
}
#p1win {
color: #4fb2d3;
display: none;
}
#p2win {
color: #b40e0e;
display: none;
}
#draw {
color: #FFFFFF;
display: none;
} }

@ -1,57 +0,0 @@
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #1E2125;
font-family: "Arial", sans-serif;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
gap: 15px;
margin-bottom: 25px;
}
.cell {
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
background-color: #2C3036;
color: #FFFFFF;
font-size: 24px;
font-weight: bold;
cursor: pointer;
}
button {
padding: 10px 20px;
background-color: #1E2125;
color: #FFFFFF;
border: 2px solid #FFFFFF;
font-size: 16px;
cursor: pointer;
margin: 5px;
}
button:hover {
background-color: #2C3036;
}
.cell.x::after {
content: "X";
}
.cell.o::after {
content: "O";
}

@ -4,10 +4,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic Tac Toe - IA</title> <title>Tic Tac Toe - IA</title>
<link rel="stylesheet" href="style1.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>Tic Tac Toe - AI</h1>
<div class="grid"> <div class="grid">
<div class="cell" data-cell></div> <div class="cell" data-cell></div>
<div class="cell" data-cell></div> <div class="cell" data-cell></div>
@ -19,9 +21,13 @@
<div class="cell" data-cell></div> <div class="cell" data-cell></div>
<div class="cell" data-cell></div> <div class="cell" data-cell></div>
</div> </div>
<button id="reset">Recommencer</button> <div class="winnerMessage" id="p1win">Player 1 won</div>
<button id="ai">Activer/désactiver IA</button> <div class="winnerMessage" id="p2win">Player 2 won</div>
<div class="winnerMessage" id="aiwin">IA won</div>
<div class="winnerMessage" id="draw">There is equality</div>
<button id="reset">Reset</button>
<button id="ai">Enable/Disable IA</button>
</div> </div>
<script src="script1.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jeu de Morpion</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Jeu de Morpion</h1>
<div id="game-board"></div>
<script src="script.js"></script>
</body>
</html>
Loading…
Cancel
Save