Merge remote-tracking branch 'origin/master'

# Conflicts:
#	server/src/main/java/fr/lnl/game/server/games/Game.java
This commit is contained in:
Katchan 2021-12-08 21:50:00 +01:00
commit eaec044950
19 changed files with 148 additions and 21 deletions

View File

@ -1,6 +1,39 @@
Projet de Conception Logiciel conçu par : " Projet de Conception Logiciel conçu par :
LUCAS Valentin LUCAS Valentin
LEGOT Quentin LEGOT Quentin
NEVEU Thomas NEVEU Thomas
# Dépendances
Le projet nécessite le ressources suivante pour fonctionner:
- Java 17 (téléchargeable depuis apt sur Debian 11 ou en téléchargeant sur Oracle ou openJDK)
- Gradle 7.3 ou supérieur (téléchargeable depuis https://gradle.org/releases/ puis en suivez les indications données sur cette page: https://docs.gradle.org/7.3.1/userguide/installation.html#installing_manually )
Accédez ensuite à la racine du projet (dossier livraison/ normalement ou du moins le dossier contenant le fichier settings.gradle) pour pouvoir execute le programme
# Les commandes
- `gradle :client:run --args="[ARGUMENTS]"` permet de lancer le jeu avec les arguments suivant la logique suivant
- le premier argument concerne le type de vue, indiquer `terminal` pour jouer en mode terminal ou `window` pour jouer en mode fenêtre avec JavaFX
- les arguments suivants concernent les joueurs, ils suivent une forme "<type du joueur> [classe du joueur]"
- `type du joueur` pour indiquer si le joueur est:
- `human` pour indiquer un joueur humain utilisant le clavier ou la souris
- `computer` indiquant un joueur ordinateur jouant des coups aléatoire
- `computerS` indiquant un joueur ordinateur semi-aléatoire
- `classe du joueur` est un paramètre facultatif correspond à:
- `tank` indiquant que le joueur est de type tank et qu'il subit moins de dégâts des explosions que les autres ses coups en energies pour faire des actions sont plus élevés
- `dps` ou `default` et un joueur standard, infligeant le plus de dégâts d'attaque et ayant un coup de déplacement équilibré, c'est la valeur par défaut si la classe de ce joueur n'est pas donné
- `support` est un joueur basé sur la pose d'explosif, n'allant que peu au combat direct, ses coups de déplacement sont standard mais ses coups en energies de pose de mine ou de bombes sont les plus faibles.
- Un exemple de ce que cela peux donner est: `window computer default human support computerS dps human tank`
- ici On joue en vue fenêtre avec 4 joueurs:
- computer de classe default (ou dps)
- human de classe support
- computerS de classe dps (ou default)
- human de classe tank
- `gradle build` permet de compiler, exécuter les tests et archiver les classes dans un fichier `.jar` pour chaque module (client et server)
- `gradle javadoc` pour générer la javadoc, une javadoc est générer par projet (soit 2 javadocs), nous vous mettons à disposition dans le dossier livraison un dossier javadoc pour faciliter la navigation
- Si vous voulez naviguer dans la nouvelle javadoc que vous aurez générer, vous pouvez y accéder en allant dans livraison/MODULE/build/doc/javadoc
- les modules étant `client` et `server`
- `gradle test` pour exécuter les tests unitaires

View File

@ -51,7 +51,7 @@ public class App extends Application {
public static void startGame(ViewLambda lambda) throws IllegalArgumentException, InvocationTargetException, NoSuchMethodException, public static void startGame(ViewLambda lambda) throws IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
InstantiationException, IllegalAccessException { InstantiationException, IllegalAccessException {
List<Player> players = parsePlayers(); List<Player> players = parsePlayers();
GridFactoryBuilder builder = LockGridFactoryBuilder.create().energyProbability(0.95F).wallProbability(0.80F).playersList(players).gridDimensions(12, 12); GridFactoryBuilder builder = LockGridFactoryBuilder.create().energyProbability(0.95F).wallProbability(0.80F).gridDimensions(12, 12);
game = new Game(builder, players); game = new Game(builder, players);
for (Player player : game.getPlayers()) { for (Player player : game.getPlayers()) {
playerList.put(player, new ClientPlayer(player, lambda.createViewLambda(player))); playerList.put(player, new ClientPlayer(player, lambda.createViewLambda(player)));

View File

@ -16,34 +16,42 @@ import static java.util.function.Predicate.not;
public class Game { public class Game {
private final GridFactoryBuilder buildStrategy;
private final Grid grid; private final Grid grid;
private final List<Player> players; private final List<Player> players;
private Player currentPlayer; private Player currentPlayer;
private Action selectedAction = null; private Action selectedAction = null;
private int nbrTurn; private int nbrTurn;
/**
* @param buildStrategy used to build a grid
* @param players a list players in the game
* @throws IllegalArgumentException if number of players given in parameters is too many or inferior to 2
*/
public Game(GridFactoryBuilder buildStrategy, List<Player> players) throws IllegalArgumentException { public Game(GridFactoryBuilder buildStrategy, List<Player> players) throws IllegalArgumentException {
this.grid = buildStrategy.build(); this.grid = buildStrategy.playersList(players).build();
if(players.size() < 2) if(players.size() < 2)
throw new IllegalArgumentException("The game need 2 or more player to start"); throw new IllegalArgumentException("The game need 2 or more player to start");
if(players.size() > grid.getNumberNeutralBox()){ if(players.size() > grid.getNumberNeutralBox()){
throw new IllegalArgumentException("There are too many players for the number of box available"); throw new IllegalArgumentException("There are too many players for the number of box available");
} }
this.buildStrategy = buildStrategy;
this.players = players; this.players = players;
this.currentPlayer = players.get(0); this.currentPlayer = players.get(0);
this.nbrTurn = 1; this.nbrTurn = 1;
initGame(); initGame(buildStrategy);
} }
public void initGame(){ /**
* Initialize a game by placing players on the grid and by generating current player available actions
* @param buildStrategy builder used to create a grid
*/
public void initGame(GridFactoryBuilder buildStrategy){
buildStrategy.initPlacePlayers(); buildStrategy.initPlacePlayers();
currentPlayer.setActions(generateAndGetPlayerActions(currentPlayer)); currentPlayer.setActions(generateAndGetPlayerActions(currentPlayer));
} }
/** /**
* * Game "main" method, call by a controller after human chose an action or when a computer player play an action
* Method is call everytime an action has been chosen by a human or when aa computer player need to play
* @return true if game is over, false otherwise * @return true if game is over, false otherwise
*/ */
public boolean play() { public boolean play() {
@ -59,17 +67,28 @@ public class Game {
return isOver(); return isOver();
} }
/**
* Remove dead players from the grid
*/
private void gridPlayersUpdate(){ private void gridPlayersUpdate(){
for (Player player: getPlayersNotAlive().toList()) { for (Player player: getPlayersNotAlive().toList()) {
getGrid().getBoard().get(player.getPosition()).setA(null); getGrid().getBoard().get(player.getPosition()).setA(null);
} }
} }
/**
* play grid's elements that use a timer like {@link fr.lnl.game.server.games.grid.elements.Bomb} at each game tick
*/
private void countdownGridElementsUpdate() { private void countdownGridElementsUpdate() {
List<CountdownBox> countdownBoxes = this.getGrid().getAllCountdownElements(); List<CountdownBox> countdownBoxes = this.getGrid().getAllCountdownElements();
countdownBoxes.forEach(CountdownBox::update); countdownBoxes.forEach(CountdownBox::update);
} }
/**
* Used to list all actions a player can execute at current time
* @param player the player to generate actions
* @return a list of available actions
*/
public List<Action> generateAndGetPlayerActions(Player player) { public List<Action> generateAndGetPlayerActions(Player player) {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
for(Direction direction : Direction.values()) { for(Direction direction : Direction.values()) {
@ -90,29 +109,52 @@ public class Game {
return actions; return actions;
} }
/**
*
* @return a list of alive players
*/
public Stream<Player> getPlayersAlive() { public Stream<Player> getPlayersAlive() {
return players.parallelStream().filter(Player::isAlive); return players.parallelStream().filter(Player::isAlive);
} }
/**
* Opposite of {@link Game#getPlayersAlive()}
* @return a list of dead players
*/
public Stream<Player> getPlayersNotAlive() { public Stream<Player> getPlayersNotAlive() {
return players.parallelStream().filter(not(Player::isAlive)); return players.parallelStream().filter(not(Player::isAlive));
} }
/**
* A game is over if the number of alive players is inferior to 2
* @return true if game is over, false otherwise
*/
public boolean isOver() { public boolean isOver() {
return getPlayersAlive().count() <= 1; return getPlayersAlive().count() <= 1;
} }
/**
*
* @return the winner of the game if exists, null otherwise.<br>
* return the only remaining alive player when it exists, or null if everyone is dead, per example when a bomb kill
* the 2 remaining players
*/
public Player getWinner() { public Player getWinner() {
// On part du principe que isOver est forcément appelé avant d'appeler getWinner // On part du principe que isOver est forcément appelé avant d'appeler getWinner
return getPlayersAlive().findFirst().orElse(null); return getPlayersAlive().findFirst().orElse(null);
} }
/**
*
* @return the player who is currently playing
*/
public Player getCurrentPlayer() { public Player getCurrentPlayer() {
return currentPlayer; return currentPlayer;
} }
/** /**
* Change player to the next available in the list * Change player to the next available in the list.<br>
* We set its shield deploy state to false.
*/ */
public void nextCurrentPlayer() { public void nextCurrentPlayer() {
do { do {
@ -124,6 +166,9 @@ public class Game {
currentPlayer.setShieldDeploy(false); // on reset son état currentPlayer.setShieldDeploy(false); // on reset son état
} }
/**
* @param current_player the new current player
*/
public void setCurrentPlayer(Player current_player) { public void setCurrentPlayer(Player current_player) {
this.currentPlayer = current_player; this.currentPlayer = current_player;
} }
@ -136,10 +181,17 @@ public class Game {
return players; return players;
} }
/**
*
* @return action selected by current player if not already executed or last player if already executed
*/
public Action getSelectedAction() { public Action getSelectedAction() {
return selectedAction; return selectedAction;
} }
/**
* @param selectedAction set the action selected by current player before doing it
*/
public void setSelectedAction(Action selectedAction) { public void setSelectedAction(Action selectedAction) {
this.selectedAction = selectedAction; this.selectedAction = selectedAction;
} }

View File

@ -1,8 +0,0 @@
package fr.lnl.game.server.games;
public enum InterfaceAction {
SELECT_ACTION,
DIALOG_TO_CONTINUE_COMPUTER_IA
}

View File

@ -19,7 +19,7 @@ public interface Action {
boolean isPossible(); boolean isPossible();
/** /**
* Used by {@link Move}, {@link Shot} and {@link DropObject} to list all direction when the action is possible * Used by {@link Move}, {@link Shot} and {@link DropObject} to list all direction where the action is possible
* @return a list a point where the action is possible (not block by a wall per example) * @return a list a point where the action is possible (not block by a wall per example)
*/ */
Point getPoint(); Point getPoint();

View File

@ -53,7 +53,8 @@ public interface GridFactoryBuilder {
Grid build(); Grid build();
/** /**
* call when initializing the game, it'll place player depending on the strategy used by its implementation * call when initializing the game, it'll place player depending on the strategy used by its implementation,
* need to be call after build, call an NullPointerException otherwise
*/ */
void initPlacePlayers(); void initPlacePlayers();

View File

@ -0,0 +1,4 @@
/**
* Package containing all about Grid construction
*/
package fr.lnl.game.server.games.grid.build;

View File

@ -0,0 +1,4 @@
/**
* Package containing all the elements that can be arranged in a grid
*/
package fr.lnl.game.server.games.grid.elements;

View File

@ -0,0 +1,4 @@
/**
* Package containing all about Grid components
*/
package fr.lnl.game.server.games.grid;

View File

@ -0,0 +1,4 @@
/**
* Game package, contains all classes and sub-packages mainly related game works
*/
package fr.lnl.game.server.games;

View File

@ -107,7 +107,7 @@ public abstract class AbstractPlayer implements Player {
@Override @Override
public void setPosition(Point position){ public void setPosition(/* NotNull */ Point position){
if(position == null){ if(position == null){
throw new IllegalArgumentException("Position is null"); throw new IllegalArgumentException("Position is null");
} }

View File

@ -3,6 +3,9 @@ package fr.lnl.game.server.games.player;
import fr.lnl.game.server.games.weapon.Firearm; import fr.lnl.game.server.games.weapon.Firearm;
import fr.lnl.game.server.games.weapon.Weapon; import fr.lnl.game.server.games.weapon.Weapon;
/**
* ClassPlayer contains all data about the cost of an action or the cost of a damage
*/
public enum ClassPlayer { public enum ClassPlayer {
DEFAULT(800, 25, 20, 30, 40, 10, 80, 40, 20, 15, new Firearm()), DEFAULT(800, 25, 20, 30, 40, 10, 80, 40, 20, 15, new Firearm()),

View File

@ -5,12 +5,19 @@ import fr.lnl.game.server.games.action.Action;
import fr.lnl.game.server.games.action.Nothing; import fr.lnl.game.server.games.action.Nothing;
import fr.lnl.game.server.utils.Point; import fr.lnl.game.server.utils.Point;
/**
* Super class of all Computer players
*/
public abstract class ComputerPlayer extends AbstractPlayer { public abstract class ComputerPlayer extends AbstractPlayer {
public ComputerPlayer(Integer id, Point point, ClassPlayer classPlayer) { public ComputerPlayer(Integer id, Point point, ClassPlayer classPlayer) {
super(id, point, false, classPlayer); super(id, point, false, classPlayer);
} }
/**
* Call when an AI need to choose an action to execute
* @return the chosen action
*/
public Action choseAction(Game game){ public Action choseAction(Game game){
Action action; Action action;
switch (getActions().size()){ switch (getActions().size()){

View File

@ -2,6 +2,11 @@ package fr.lnl.game.server.games.player;
import fr.lnl.game.server.utils.Point; import fr.lnl.game.server.utils.Point;
/**
* Instance of Human Player.<br>
* A human player choose an action to execute by using mouse or keyboard.<br>
* Human Player don't implement choseAction cause this method is executed on client part
*/
public class HumanPlayer extends AbstractPlayer { public class HumanPlayer extends AbstractPlayer {
public HumanPlayer(Integer id, Point point, ClassPlayer classPlayer) { public HumanPlayer(Integer id, Point point, ClassPlayer classPlayer) {

View File

@ -14,6 +14,10 @@ public class RandomComputerPlayer extends ComputerPlayer {
super(id,point, classPlayer); super(id,point, classPlayer);
} }
/**
* Choose an action fully randomly
* @return an action between all available
*/
@Override @Override
public Action strategy(Game game) { public Action strategy(Game game) {
Action action = null; Action action = null;

View File

@ -0,0 +1,4 @@
/**
* Package storing all players classes and as well AI behavior
*/
package fr.lnl.game.server.games.player;

View File

@ -4,8 +4,14 @@ public interface Weapon {
int getBullet(); int getBullet();
/**
* @return distance a bullet can go horizontally
*/
int getHorizontalDistance(); int getHorizontalDistance();
/**
* @return distance a bullet can go vertically
*/
int getVerticalDistance(); int getVerticalDistance();
} }

View File

@ -0,0 +1,4 @@
/**
* Package containing all bout player's weapons
*/
package fr.lnl.game.server.games.weapon;

View File

@ -14,7 +14,7 @@ public class Mock {
public Grid grid; public Grid grid;
public Mock(List<Player> players) { public Mock(List<Player> players) {
this.buildStrategy = MockGridFactoryBuilder.create().gridDimensions(16, 16).playersList(players).wallProbability(0.80F).energyProbability(0.95F); this.buildStrategy = MockGridFactoryBuilder.create().gridDimensions(16, 16).wallProbability(0.80F).energyProbability(0.95F);
game = new Game(buildStrategy, players); game = new Game(buildStrategy, players);
this.grid = game.getGrid(); this.grid = game.getGrid();
} }