Initial commit — sujet TP1 IIA (Minimax / Alpha-Beta)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kerboul
2026-04-07 14:41:05 +02:00
commit 9700af5c65
22 changed files with 885 additions and 0 deletions

78
.gitignore vendored Normal file
View File

@@ -0,0 +1,78 @@
# Directories #
/build/
/bin/
target/
# OS Files #
.DS_Store
*.class
# Package Files #
*.jar
*.war
*.ear
*.db
######################
# Windows
######################
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
######################
# OSX
######################
.DS_Store
.svn
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
######################
# Eclipse
######################
*.pydevproject
.project
.metadata
bin/**
tmp/**
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
/src/main/resources/rebel.xml
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
######################
# Gradle
######################
.gradle/

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# TP 3-4
Structure de départ pour le TP 3-4.

35
build.gradle Normal file
View File

@@ -0,0 +1,35 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.3/userguide/java_library_plugin.html
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
id 'eclipse'
}
sourceCompatibility = 11.0
targetCompatibility = 11.0
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
//api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
//implementation 'com.google.guava:guava:28.2-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}

10
settings.gradle Normal file
View File

@@ -0,0 +1,10 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html
*/
rootProject.name = 'iialib'

View File

@@ -0,0 +1,186 @@
package games.dominos;
import iialib.games.model.IBoard;
import iialib.games.model.Player;
import iialib.games.model.Score;
import java.util.ArrayList;
public class DominosBoard implements IBoard<DominosMove, DominosRole, DominosBoard> {
private static int DEFAULT_GRID_SIZE = 7;
// --------- Class Attribute ---------
public static int GRID_SIZE = DEFAULT_GRID_SIZE;
private enum SQUARE {
EMPTY, VERTICAL, HORIZONTAL
};
// ---------------------- Attributes ---------------------
private final SQUARE[][] boardGrid;
// ---------------------- Constructors ---------------------
public DominosBoard() {
boardGrid = new SQUARE[GRID_SIZE][GRID_SIZE];
for (int i = 0; i < GRID_SIZE; i++)
for (int j = 0; j < GRID_SIZE; j++)
boardGrid[i][j] = SQUARE.EMPTY;
}
// Constructors
public DominosBoard(DominosBoard other) {
boardGrid = other.copyGrid();
}
private DominosBoard(SQUARE[][] other) {
boardGrid = new SQUARE[GRID_SIZE][GRID_SIZE];
for (int i = 0; i < GRID_SIZE; i++)
System.arraycopy(other[i], 0, boardGrid[i], 0, GRID_SIZE);
}
// ------------------- Getters / Setters -------------------
protected int retGridSize(int n) {
return GRID_SIZE;
}
protected void setGridSize(int n) {
GRID_SIZE = n;
}
// --------------------- IBoard Methods ---------------------
@Override
public DominosBoard play(DominosMove move, DominosRole playerRole) {
SQUARE[][] newGrid = copyGrid();
int x = move.x;
int y = move.y;
if (playerRole == DominosRole.VERTICAL) {
newGrid[x][y] = SQUARE.VERTICAL;
newGrid[x + 1][y] = SQUARE.VERTICAL;
} else {
newGrid[x][y] = SQUARE.HORIZONTAL;
newGrid[x][y + 1] = SQUARE.HORIZONTAL;
}
return new DominosBoard(newGrid);
}
@Override
public ArrayList<DominosMove> possibleMoves(DominosRole playerRole) {
if (playerRole == DominosRole.VERTICAL) {
return freeVerticalMoves();
} else {
return freeHorizontalMoves();
}
}
@Override
public boolean isValidMove(DominosMove move, DominosRole playerRole) {
int x = move.x;
int y = move.y;
return (boardGrid[x][y] == SQUARE.EMPTY)
&& ((playerRole == DominosRole.VERTICAL) ? (boardGrid[x + 1][y] == SQUARE.EMPTY)
: (boardGrid[x][y + 1] == SQUARE.EMPTY));
}
@Override
public boolean isGameOver() {
return (this.nbHorizontalMoves() == 0) || (this.nbVerticalMoves() == 0);
}
// --------------------- Other Methods ---------------------
private ArrayList<DominosMove> freeVerticalMoves() {
ArrayList<DominosMove> allPossibleMoves = new ArrayList<>();
for (int i = 0; i < GRID_SIZE- 1; i++) { // lines
for (int j = 0; j < GRID_SIZE ; j++) { // columns
if ((boardGrid[i][j] == SQUARE.EMPTY) && (boardGrid[i + 1][j] == SQUARE.EMPTY)) // possible move
allPossibleMoves.add(new DominosMove(i, j));
}
}
return allPossibleMoves;
}
private ArrayList<DominosMove> freeHorizontalMoves() {
ArrayList<DominosMove> allPossibleMoves = new ArrayList<>();
for (int i = 0; i < GRID_SIZE; i++) { // lines
for (int j = 0; j < GRID_SIZE - 1; j++) { // columns
if ((boardGrid[i][j] == SQUARE.EMPTY) && (boardGrid[i ][j+ 1] == SQUARE.EMPTY)) // possible move
allPossibleMoves.add(new DominosMove(i, j));
}
}
return allPossibleMoves;
}
private SQUARE[][] copyGrid() {
SQUARE[][] newGrid = new SQUARE[GRID_SIZE][GRID_SIZE];
for (int i = 0; i < GRID_SIZE; i++)
System.arraycopy(boardGrid[i], 0, newGrid[i], 0, GRID_SIZE);
return newGrid;
}
public String toString() {
StringBuilder retstr = new StringBuilder(new String(""));
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++)
if (boardGrid[i][j] == SQUARE.EMPTY)
retstr.append("-");
else if (boardGrid[i][j] == SQUARE.VERTICAL)
retstr.append("V");
else // damier[i][j] == NOIR
retstr.append("H");
retstr.append("\n");
}
return retstr.toString();
}
public int nbHorizontalMoves() {
int nbMoves = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE - 1; j++) {
if (boardGrid[i][j] == SQUARE.EMPTY && boardGrid[i][j + 1] == SQUARE.EMPTY)
nbMoves++;
}
}
return nbMoves;
}
public int nbVerticalMoves() {
int nbMoves = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE - 1; j++) {
if (boardGrid[j][i] == SQUARE.EMPTY && boardGrid[j + 1][i] == SQUARE.EMPTY)
nbMoves++;
}
}
return nbMoves;
}
@Override
public ArrayList<Score<DominosRole>> getScores() {
ArrayList<Score<DominosRole>> scores = new ArrayList<Score<DominosRole>>();
if(this.isGameOver()) {
if (nbHorizontalMoves() == 0) {
scores.add(new Score<DominosRole>(DominosRole.HORIZONTAL,Score.Status.LOOSE,0));
scores.add(new Score<DominosRole>(DominosRole.VERTICAL,Score.Status.WIN,1));
}
else {
scores.add(new Score<DominosRole>(DominosRole.HORIZONTAL,Score.Status.WIN,1));
scores.add(new Score<DominosRole>(DominosRole.VERTICAL,Score.Status.LOOSE,0));
}
}
else {
}
return scores;
}
}

View File

@@ -0,0 +1,45 @@
package games.dominos;
import java.util.ArrayList;
import iialib.games.algs.AIPlayer;
import iialib.games.algs.AbstractGame;
import iialib.games.algs.GameAlgorithm;
import iialib.games.algs.algorithms.MiniMax;
public class DominosGame extends AbstractGame<DominosMove, DominosRole, DominosBoard> {
DominosGame(ArrayList<AIPlayer<DominosMove, DominosRole, DominosBoard>> players, DominosBoard board) {
super(players, board);
}
public static void main(String[] args) {
DominosRole roleV = DominosRole.VERTICAL;
DominosRole roleH = DominosRole.HORIZONTAL;
GameAlgorithm<DominosMove, DominosRole, DominosBoard> algV = new MiniMax<DominosMove, DominosRole, DominosBoard>(
roleV, roleH, DominosHeuristics.hVertical, 4); // Minimax depth 4
GameAlgorithm<DominosMove, DominosRole, DominosBoard> algH = new MiniMax<DominosMove, DominosRole, DominosBoard>(
roleH, roleV, DominosHeuristics.hHorizontal, 2); // Minimax depth 2
AIPlayer<DominosMove, DominosRole, DominosBoard> playerV = new AIPlayer<DominosMove, DominosRole, DominosBoard>(
roleV, algV);
AIPlayer<DominosMove, DominosRole, DominosBoard> playerH = new AIPlayer<DominosMove, DominosRole, DominosBoard>(
roleH, algH);
ArrayList<AIPlayer<DominosMove, DominosRole, DominosBoard>> players = new ArrayList<AIPlayer<DominosMove, DominosRole, DominosBoard>>();
players.add(playerV); // First Player
players.add(playerH); // Second Player
// Setting the initial Board
DominosBoard initialBoard = new DominosBoard();
DominosGame game = new DominosGame(players, initialBoard);
game.runGame();
}
}

View File

@@ -0,0 +1,23 @@
package games.dominos;
import iialib.games.algs.IHeuristic;
public class DominosHeuristics {
public static IHeuristic<DominosBoard,DominosRole> hVertical = (board,role) -> {
/* TODO */
return 0; // TODO
};
public static IHeuristic<DominosBoard,DominosRole> hHorizontal = (board,role) -> {
/* TODO */
return 0; // TODO
};
}

View File

@@ -0,0 +1,19 @@
package games.dominos;
import iialib.games.model.IMove;
public class DominosMove implements IMove {
public final int x;
public final int y;
DominosMove(int x, int y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Move{" + x + "," + y + "}";
}
}

View File

@@ -0,0 +1,8 @@
package games.dominos;
import iialib.games.model.IRole;
public enum DominosRole implements IRole{
HORIZONTAL, // For the player playing its tiles hally
VERTICAL // For the player playing its tiles vertically
}

View File

@@ -0,0 +1,54 @@
package games.otherGame;
import iialib.games.model.IBoard;
import iialib.games.model.Player;
import iialib.games.model.Score;
import java.util.ArrayList;
public class otherGameBoard implements IBoard<otherGameMove, otherGameRole, otherGameBoard> {
// ---------------------- Attributes ---------------------
// Attributes
//TODO
// --------------------- IBoard Methods ---------------------
@Override
public ArrayList<otherGameMove> possibleMoves(otherGameRole playerRole) {
// TODO Auto-generated method stub
return null;
}
@Override
public otherGameBoard play(otherGameMove move, otherGameRole playerRole) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isValidMove(otherGameMove move, otherGameRole playerRole) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isGameOver() {
// TODO Auto-generated method stub
return false;
}
@Override
public ArrayList<Score<otherGameRole>> getScores() {
// TODO Auto-generated method stub
return null;
}
// --------------------- Other Methods ---------------------
}

View File

@@ -0,0 +1,19 @@
package games.otherGame;
import iialib.games.model.IMove;
public class otherGameMove implements IMove {
public final int x;
public final int y;
otherGameMove(int x, int y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Move{" + x + "," + y + "}";
}
}

View File

@@ -0,0 +1,8 @@
package games.otherGame;
import iialib.games.model.IRole;
public enum otherGameRole implements IRole{
HORIZONTAL, // For the player playing its tiles hally
VERTICAL // For the player playing its tiles vertically
}

View File

@@ -0,0 +1,33 @@
package iialib.games.algs;
import iialib.games.model.IBoard;
import iialib.games.model.IMove;
import iialib.games.model.IRole;
import iialib.games.model.Player;
public class AIPlayer<Move extends IMove,Role extends IRole, Board extends IBoard<Move,Role,Board>> extends Player<Role> {
private GameAlgorithm< Move,Role,Board> ai;
public void setAi(GameAlgorithm<Move, Role, Board> ai) {
this.ai = ai;
}
public AIPlayer(Role role) {
super(role);
}
public AIPlayer(Role role,GameAlgorithm<Move,Role,Board> alg) {
super(role);
this.ai = alg;
}
public Move bestMove(Board board) {
return(ai.bestMove(board,this.getRole()));
}
public Board playMove(Board board, Move move) {
return(board.play(move, this.getRole()));
}
}

View File

@@ -0,0 +1,56 @@
package iialib.games.algs;
import java.util.ArrayList;
import iialib.games.model.IBoard;
import iialib.games.model.IMove;
import iialib.games.model.IRole;
import iialib.games.model.Score;
public abstract class AbstractGame<Move extends IMove, Role extends IRole, Board extends IBoard<Move,Role,Board>> {
// Attributes
Board currentBoard;
ArrayList<AIPlayer<Move,Role,Board>> players;
// Constructor
public AbstractGame(ArrayList<AIPlayer<Move,Role,Board>> players,Board initialBoard){
this.currentBoard = initialBoard;
this.players = players;
}
// Methods
public void runGame() {
int index = 0;
AIPlayer<Move,Role,Board> currentPlayer = players.get(index);
System.out.println("Game begining - First player is : " + currentPlayer);
System.out.println("The board is :");
System.out.println(currentBoard);
while(!currentBoard.isGameOver()) {
System.out.println("Next player is :" + currentPlayer);
Move nextMove = currentPlayer.bestMove(currentBoard);
System.out.println("Best Move is :" + nextMove);
currentBoard = currentPlayer.playMove(currentBoard, nextMove);
System.out.println("The board is :");
System.out.println(currentBoard);
index = 1 - index;
currentPlayer = players.get(index);
}
System.out.println("Game over !");
ArrayList<Score<Role>> scores = currentBoard.getScores();
for(AIPlayer<Move,Role,Board> p: players)
for(Score<Role> s : scores)
if(p.getRole() == s.getRole())
System.out.println("" + p + " score is : " + s.getStatus() + " " + s.getScore());
;
}
}

View File

@@ -0,0 +1,12 @@
package iialib.games.algs;
import iialib.games.model.IBoard;
import iialib.games.model.IMove;
import iialib.games.model.IRole;
import iialib.games.model.Player;
public interface GameAlgorithm< Move extends IMove, Role extends IRole, Board extends IBoard<Move,Role,Board>> {
Move bestMove(Board board,Role playerRole);
}

View File

@@ -0,0 +1,17 @@
package iialib.games.algs;
import iialib.games.model.IBoard;
import iialib.games.model.IMove;
import iialib.games.model.IRole;
import iialib.games.model.Player;
@FunctionalInterface
public interface IHeuristic<Board extends IBoard<?,Role, Board>,Role extends IRole> {
public static int MIN_VALUE = java.lang.Integer.MIN_VALUE;
public static int MAX_VALUE = java.lang.Integer.MAX_VALUE;
int eval(Board board,Role role);
}

View File

@@ -0,0 +1,84 @@
package iialib.games.algs.algorithms;
import iialib.games.algs.GameAlgorithm;
import iialib.games.algs.IHeuristic;
import iialib.games.model.IBoard;
import iialib.games.model.IMove;
import iialib.games.model.IRole;
public class MiniMax<Move extends IMove,Role extends IRole,Board extends IBoard<Move,Role,Board>> implements GameAlgorithm<Move,Role,Board> {
// Constants
/** Defaut value for depth limit
*/
private final static int DEPTH_MAX_DEFAUT = 4;
// Attributes
/** Role of the max player
*/
private final Role playerMaxRole;
/** Role of the min player
*/
private final Role playerMinRole;
/** Algorithm max depth
*/
private int depthMax = DEPTH_MAX_DEFAUT;
/** Heuristic used by the max player
*/
private IHeuristic<Board, Role> h;
//
/** number of internal visited (developed) nodes (for stats)
*/
private int nbNodes;
/** number of leaves nodes nodes (for stats)
*/
private int nbLeaves;
// --------- Constructors ---------
public MiniMax(Role playerMaxRole, Role playerMinRole, IHeuristic<Board, Role> h) {
this.playerMaxRole = playerMaxRole;
this.playerMinRole = playerMinRole;
this.h = h;
}
//
public MiniMax(Role playerMaxRole, Role playerMinRole, IHeuristic<Board, Role> h, int depthMax) {
this(playerMaxRole, playerMinRole, h);
this.depthMax = depthMax;
}
/*
* IAlgo METHODS =============
*/
@Override
public Move bestMove(Board board, Role playerRole) {
System.out.println("[MiniMax]");
Move bestMove = null;
// TODO
return bestMove;
}
/*
* PUBLIC METHODS ==============
*/
public String toString() {
return "MiniMax(ProfMax=" + depthMax + ")";
}
/*
* PRIVATE METHODS ===============
*/
//TODO
}

View File

@@ -0,0 +1,53 @@
package iialib.games.model;
import java.util.ArrayList;
/**
* Used to chararacterize the boards.he
* It has to be impemented by some class in a real game.
*
* @param <Move> Class implementing the moves for the game
* @param <Role> Class implementing the roles for the game
* @param <Board> Class implementing the boards for the game
*
*
*/
public interface IBoard<Move extends IMove, Role extends IRole, Board extends IBoard<Move,Role,Board>> {
/**
* returns the possible moves a player having the playerRole
* @param playerRole
* @return a list of all possible moves for the player having the playerRole
*/
ArrayList<Move> possibleMoves(Role playerRole);
/** play move on the board, played by a player having the playerRole
*
* @param move
* @param playerRole
* @return the successor board
*/
Board play(Move move, Role playerRole);
/**
* checks that move is valid for the player having the playerRole
* @param move
* @param playerRole
* @return yes if the move is valid for playerRole
*/
boolean isValidMove(Move move, Role playerRole);
/**
* checks that the board corresponds to an end of game
* @return yes if the game completed
*/
boolean isGameOver();
/**
* returns the scores for each role (when the game is over)
* @return
*/
ArrayList<Score<Role>> getScores();
}

View File

@@ -0,0 +1,11 @@
package iialib.games.model;
/**
* used to characterize game moves
* It has to be implemented by some class in a real game
*/
public interface IMove {
}

View File

@@ -0,0 +1,15 @@
package iialib.games.model;
/**
* Used to represent characterize the role in the game of the different players
*
* It has to be implemented by some class for a real game.
* Generally it is just an enum for representing what distinguishes the players in the game
* e.g. the colors( BLACK, WHITE), the Position (NORTH, SOUTH), ...
*
* Exemple
* enum Colors {BLACK,WHITE} implements IRole
*/
public interface IRole {
}

View File

@@ -0,0 +1,56 @@
package iialib.games.model;
/**
* used to associate an real player identifier to a role
*
* @param <Role>
*/
public class Player<Role extends IRole> {
// ----------- Attributes ------------
/**
* the role of the player in the game
*/
Role role;
/**
* An (optional) identifier characterizing the player having that role in the game
* This is useful for instance in a tournament, for keeping tra
*/
String id;
// ----------- Getters / Setters ------------
public String getId() {
return id;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
// ----------- Constructors ------------
public Player(Role role) {
this.role = role;
this.id = "";
}
public Player(Role role, String id) {
this(role);
this.id = id;
}
// ----------- Other Methods ------------
public String toString() {
return "" + role + ( id.isEmpty() ? "" : " (" + id + ")");
}
}

View File

@@ -0,0 +1,60 @@
package iialib.games.model;
/**
* class used to describe the score corresponding to each player role when the game is over
*/
public class Score<Role extends IRole> {
/**
*
*/
public enum Status {WIN,LOOSE,TIE};
// ----------- Attributes ------------
/**
*
*/
private Role role;
/**
*
*/
private Status status;
/**
* score can be just 1/0 or a real score depending on the game
*/
private int score;
// ----------- Constructors ------------
public Score(Role role, Status status, int score) {
super();
this.role = role;
this.status = status;
this.score = score;
}
// ----------- Getter / Setters ------------
public Role getRole() {
return role;
}
public Status getStatus() {
return status;
}
public int getScore() {
return score;
}
// ----------- Other public methods ------------
public String toString() {
return "Score <" + role + "," + status + "," + score + ">";
}
}