Tris MiniMax
This commit is contained in:
30
TrisMiniMax/.gitignore
vendored
Normal file
30
TrisMiniMax/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
### IntelliJ IDEA ###
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
8
TrisMiniMax/.idea/.gitignore
generated
vendored
Normal file
8
TrisMiniMax/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
6
TrisMiniMax/.idea/misc.xml
generated
Normal file
6
TrisMiniMax/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
TrisMiniMax/.idea/modules.xml
generated
Normal file
8
TrisMiniMax/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/TrisMiniMax.iml" filepath="$PROJECT_DIR$/TrisMiniMax.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
TrisMiniMax/.idea/vcs.xml
generated
Normal file
6
TrisMiniMax/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
TrisMiniMax/TrisMiniMax.iml
Normal file
11
TrisMiniMax/TrisMiniMax.iml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
221
TrisMiniMax/src/TrisGame.java
Normal file
221
TrisMiniMax/src/TrisGame.java
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
|
||||||
|
public class TrisGame extends JFrame {
|
||||||
|
private final JButton[][] buttons = new JButton[3][3];
|
||||||
|
private final char[][] board = new char[3][3];
|
||||||
|
private final char humanPlayer = 'X';
|
||||||
|
private final char aiPlayer = 'O';
|
||||||
|
private final JLabel statusLabel;
|
||||||
|
private boolean gameOver = false;
|
||||||
|
|
||||||
|
public TrisGame() {
|
||||||
|
setTitle("Tris - Minimax AI");
|
||||||
|
setSize(400, 450);
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
// Pannello di gioco
|
||||||
|
JPanel gamePanel = new JPanel(new GridLayout(3, 3, 5, 5));
|
||||||
|
gamePanel.setBackground(Color.BLACK);
|
||||||
|
|
||||||
|
// Inizializza la board e i bottoni
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
board[i][j] = ' ';
|
||||||
|
buttons[i][j] = new JButton("");
|
||||||
|
buttons[i][j].setFont(new Font("Arial", Font.BOLD, 60));
|
||||||
|
buttons[i][j].setFocusPainted(false);
|
||||||
|
buttons[i][j].setBackground(Color.WHITE);
|
||||||
|
|
||||||
|
final int row = i;
|
||||||
|
final int col = j;
|
||||||
|
|
||||||
|
buttons[i][j].addActionListener(e -> playerMove(row, col));
|
||||||
|
gamePanel.add(buttons[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pannello di stato
|
||||||
|
JPanel statusPanel = new JPanel();
|
||||||
|
statusLabel = new JLabel("Il tuo turno! (X)");
|
||||||
|
statusLabel.setFont(new Font("Arial", Font.BOLD, 16));
|
||||||
|
statusPanel.add(statusLabel);
|
||||||
|
|
||||||
|
// Bottone reset
|
||||||
|
JButton resetButton = new JButton("Nuova Partita");
|
||||||
|
resetButton.addActionListener(e -> resetGame());
|
||||||
|
statusPanel.add(resetButton);
|
||||||
|
|
||||||
|
add(gamePanel, BorderLayout.CENTER);
|
||||||
|
add(statusPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playerMove(int row, int col) {
|
||||||
|
if (gameOver || board[row][col] != ' ') return;
|
||||||
|
|
||||||
|
// Mossa del giocatore
|
||||||
|
board[row][col] = humanPlayer;
|
||||||
|
buttons[row][col].setText("X");
|
||||||
|
buttons[row][col].setForeground(Color.BLUE);
|
||||||
|
|
||||||
|
if (checkWinner(humanPlayer)) {
|
||||||
|
statusLabel.setText("Hai vinto! 🎉");
|
||||||
|
gameOver = true;
|
||||||
|
highlightWinner(humanPlayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoardFull()) {
|
||||||
|
statusLabel.setText("Pareggio!");
|
||||||
|
gameOver = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mossa dell'AI
|
||||||
|
statusLabel.setText("L'AI sta pensando...");
|
||||||
|
Timer timer = new Timer(500, e -> {
|
||||||
|
aiMove();
|
||||||
|
|
||||||
|
if (checkWinner(aiPlayer)) {
|
||||||
|
statusLabel.setText("L'AI ha vinto!");
|
||||||
|
gameOver = true;
|
||||||
|
highlightWinner(aiPlayer);
|
||||||
|
} else if (isBoardFull()) {
|
||||||
|
statusLabel.setText("Pareggio!");
|
||||||
|
gameOver = true;
|
||||||
|
} else {
|
||||||
|
statusLabel.setText("Il tuo turno! (X)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timer.setRepeats(false);
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void aiMove() {
|
||||||
|
int[] bestMove = findBestMove();
|
||||||
|
board[bestMove[0]][bestMove[1]] = aiPlayer;
|
||||||
|
buttons[bestMove[0]][bestMove[1]].setText("O");
|
||||||
|
buttons[bestMove[0]][bestMove[1]].setForeground(Color.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] findBestMove() {
|
||||||
|
int bestScore = Integer.MIN_VALUE;
|
||||||
|
int[] bestMove = new int[2];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
if (board[i][j] == ' ') {
|
||||||
|
board[i][j] = aiPlayer;
|
||||||
|
int score = minimax(0, false);
|
||||||
|
board[i][j] = ' ';
|
||||||
|
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestMove[0] = i;
|
||||||
|
bestMove[1] = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int minimax(int depth, boolean isMaximizing) {
|
||||||
|
if (checkWinner(aiPlayer)) return 10 - depth;
|
||||||
|
if (checkWinner(humanPlayer)) return depth - 10;
|
||||||
|
if (isBoardFull()) return 0;
|
||||||
|
|
||||||
|
int bestScore;
|
||||||
|
if (isMaximizing) {
|
||||||
|
bestScore = Integer.MIN_VALUE;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
if (board[i][j] == ' ') {
|
||||||
|
board[i][j] = aiPlayer;
|
||||||
|
int score = minimax(depth + 1, false);
|
||||||
|
board[i][j] = ' ';
|
||||||
|
bestScore = Math.max(score, bestScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bestScore = Integer.MAX_VALUE;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
if (board[i][j] == ' ') {
|
||||||
|
board[i][j] = humanPlayer;
|
||||||
|
int score = minimax(depth + 1, true);
|
||||||
|
board[i][j] = ' ';
|
||||||
|
bestScore = Math.min(score, bestScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkWinner(char player) {
|
||||||
|
// Righe e colonne
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (board[i][0] == player && board[i][1] == player && board[i][2] == player) return true;
|
||||||
|
if (board[0][i] == player && board[1][i] == player && board[2][i] == player) return true;
|
||||||
|
}
|
||||||
|
// Diagonali
|
||||||
|
if (board[0][0] == player && board[1][1] == player && board[2][2] == player) return true;
|
||||||
|
return board[0][2] == player && board[1][1] == player && board[2][0] == player;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void highlightWinner(char player) {
|
||||||
|
Color winColor = new Color(144, 238, 144);
|
||||||
|
|
||||||
|
// Righe
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (board[i][0] == player && board[i][1] == player && board[i][2] == player) {
|
||||||
|
for (int j = 0; j < 3; j++) buttons[i][j].setBackground(winColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Colonne
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (board[0][i] == player && board[1][i] == player && board[2][i] == player) {
|
||||||
|
for (int j = 0; j < 3; j++) buttons[j][i].setBackground(winColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Diagonali
|
||||||
|
if (board[0][0] == player && board[1][1] == player && board[2][2] == player) {
|
||||||
|
for (int i = 0; i < 3; i++) buttons[i][i].setBackground(winColor);
|
||||||
|
}
|
||||||
|
if (board[0][2] == player && board[1][1] == player && board[2][0] == player) {
|
||||||
|
for (int i = 0; i < 3; i++) buttons[i][2-i].setBackground(winColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBoardFull() {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
if (board[i][j] == ' ') return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetGame() {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
board[i][j] = ' ';
|
||||||
|
buttons[i][j].setText("");
|
||||||
|
buttons[i][j].setBackground(Color.WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gameOver = false;
|
||||||
|
statusLabel.setText("Il tuo turno! (X)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SwingUtilities.invokeLater(TrisGame::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user