Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
|
4007951502 | 2 years ago |
|
517c924351 | 2 years ago |
|
a7159cf104 | 2 years ago |
|
bba3d1a72d | 2 years ago |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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,158 @@
|
|||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #1E2125;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: 2px solid #FFFFFF;
|
||||||
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell[data-mark="1"]::before {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 30px;
|
||||||
|
left: 40px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button::before,
|
||||||
|
.close-button::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 30px;
|
||||||
|
width: 2px;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button::before {
|
||||||
|
transform-origin: center center;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button::after {
|
||||||
|
transform-origin: center center;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover::before {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover::after {
|
||||||
|
transform: rotate(-135deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.github-button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #1E2125;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: 2px solid #FFFFFF;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 20px 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-button:hover {
|
||||||
|
background-color: #2C3036;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
@ -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";
|
|
||||||
}
|
|
@ -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…
Reference in new issue