Add comments

This commit is contained in:
Quentin Legot 2021-05-01 18:45:18 +02:00
parent d6b7f6decd
commit 5d2aa4edd5
15 changed files with 272 additions and 16 deletions

7
overview.html Normal file
View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
BattleShip package
</body>
</html>

View File

@ -6,8 +6,8 @@ import battleship.model.player.Human;
import battleship.model.player.Player; import battleship.model.player.Player;
import battleship.model.player.Random; import battleship.model.player.Random;
import battleship.utils.Pair; import battleship.utils.Pair;
import battleship.view.AbstractView;
import battleship.view.Terminal; import battleship.view.Terminal;
import battleship.view.View;
import battleship.view.Window; import battleship.view.Window;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -15,9 +15,13 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
/**
* Main class
* Given arguments are importants, please give attention to {@link Main#parseArgs(String[])} about this
*/
public class Main { public class Main {
public static AbstractView view; public static View view;
public static Game game; public static Game game;
public static void main(String[] args) { public static void main(String[] args) {
@ -36,6 +40,20 @@ public class Main {
game.Play(view); game.Play(view);
} }
/**
* <p>Read and parse launch arguments.<br>
* launch arguments need to follow this syntax:<br></p>
* <p><strong>{@code <player1_instance> <player2_instance> [nogui]}</strong></p>
* <p><strong>&#60;arg&#62;</strong> -> mandatory parameter<br>
* <strong>[arg]</strong> -> optional parameter<br>
* player_instance values are: <strong>"Human"</strong> or <strong>"Random"</strong><br>
* nogui to launch the game in terminal or nothing to launch in graphical interface</p>
* @param args launch arguments
* @throws NoSuchMethodException reflect exception
* @throws IllegalAccessException reflect exception
* @throws InvocationTargetException reflect exception
* @throws InstantiationException reflect exception
*/
private static void parseArgs(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { private static void parseArgs(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Player[] players = new Player[2]; Player[] players = new Player[2];
ArrayList<Pair<String, Class<? extends AbstractPlayer>>> playerClass = new ArrayList<>(2); ArrayList<Pair<String, Class<? extends AbstractPlayer>>> playerClass = new ArrayList<>(2);

View File

@ -0,0 +1,4 @@
/**
* Contains keyboard and mouse listeners
*/
package battleship.control;

View File

@ -3,8 +3,11 @@ package battleship.model;
import battleship.model.player.Player; import battleship.model.player.Player;
import battleship.utils.Pair; import battleship.utils.Pair;
import battleship.utils.Triplet; import battleship.utils.Triplet;
import battleship.view.AbstractView; import battleship.view.View;
/**
* Main game class
*/
public class Game { public class Game {
public Player[] players; public Player[] players;
@ -17,7 +20,6 @@ public class Game {
players[1].setId(2); players[1].setId(2);
} }
public Player getCurrentPlayer(){ public Player getCurrentPlayer(){
return this.currentPlayer; return this.currentPlayer;
} }
@ -34,6 +36,9 @@ public class Game {
currentPlayer = getOtherPlayer(); currentPlayer = getOtherPlayer();
} }
/**
* Update ship to know if they're drowned
*/
public void checkDrownedShips(){ public void checkDrownedShips(){
Player otherPlayer = getOtherPlayer(); Player otherPlayer = getOtherPlayer();
for(Ship ship : otherPlayer.getShips()){ for(Ship ship : otherPlayer.getShips()){
@ -42,6 +47,9 @@ public class Game {
} }
} }
/**
* @return player 1 if player 2' ships are drowned, or player 2 if player1' ships are drowned, null otherwise
*/
public Player getWinner(){ public Player getWinner(){
Ship remainingShip = players[0].getShips().parallelStream().filter(ship -> !ship.isDrown()).findFirst().orElse(null); Ship remainingShip = players[0].getShips().parallelStream().filter(ship -> !ship.isDrown()).findFirst().orElse(null);
if(remainingShip == null) if(remainingShip == null)
@ -52,6 +60,11 @@ public class Game {
return null; return null;
} }
/**
* Play the selected move from current player in grid
* @param move selected player move
*/
public void move(Pair<Integer,Integer> move){ public void move(Pair<Integer,Integer> move){
boolean bool = false; boolean bool = false;
Player otherPlayer = getOtherPlayer(); Player otherPlayer = getOtherPlayer();
@ -68,7 +81,11 @@ public class Game {
currentPlayer.addMove(new Triplet<>(move, bool)); currentPlayer.addMove(new Triplet<>(move, bool));
} }
public void Play(AbstractView view) { /**
* game loop
* @param view can be {@link battleship.view.Terminal} or {@link battleship.view.Window}
*/
public void Play(View view) {
try { try {
view.setShips(players[0]); view.setShips(players[0]);
view.setShips(players[1]); view.setShips(players[1]);

View File

@ -0,0 +1,4 @@
/**
* Contains all main Game classes
*/
package battleship.model;

View File

@ -0,0 +1,4 @@
/**
* Contains all players classes instances and theirs super classes and interface
*/
package battleship.model.player;

View File

@ -0,0 +1,4 @@
/**
* Contains Main Class
*/
package battleship;

View File

@ -3,7 +3,7 @@ package battleship.utils;
import java.util.Objects; import java.util.Objects;
/** /**
* tuple containing 2 unknown type elements * tuple containing 2 generic type elements
* *
* @param <U> left * @param <U> left
* @param <K> right * @param <K> right

View File

@ -3,10 +3,11 @@ package battleship.utils;
import java.util.Objects; import java.util.Objects;
/** /**
* tuple containing 2 unknown type elements * tuple containing 3 generic type elements
* *
* @param <U> left * @param <U> left
* @param <K> right * @param <K> middle
* @param <V> right
*/ */
public class Triplet<U, K, V> { public class Triplet<U, K, V> {

View File

@ -0,0 +1,4 @@
/**
* Utility classes
*/
package battleship.utils;

View File

@ -9,18 +9,26 @@ import battleship.utils.Triplet;
import java.util.ArrayList; import java.util.ArrayList;
/**
* Abstract view class
* @see View
* @see Window
* @see Terminal
*/
public abstract class AbstractView implements View { public abstract class AbstractView implements View {
protected Game game; protected Game game;
public AbstractView(Game game) { public AbstractView(Game game) {
this.game = game; this.game = game;
} }
/**
* Used during debugging, used in terminal to display grids too
* @return all player grids
*/
@Override @Override
public String toString() { public String toString() {
// String chain = "A vous de joueur "+game.currentPlayer.toString()+ "\n+ - - - - - - - - - - +\n";
String chain = ""; String chain = "";
for(int u = 0; u < 2; ++u) { for(int u = 0; u < 2; ++u) {
Player player = game.players[u]; Player player = game.players[u];
@ -63,6 +71,10 @@ public abstract class AbstractView implements View {
return chain; return chain;
} }
/**
*
* @return {@code true} if {@link Ship#getFullCoords()} contains {@code boardsCoords}, {@code false} otherwise
*/
private boolean isShipPosition(Ship ship, Pair<Integer, Integer> boardsCoords) { private boolean isShipPosition(Ship ship, Pair<Integer, Integer> boardsCoords) {
for(Pair<Integer, Integer> coords : ship.getFullCoords()) { for(Pair<Integer, Integer> coords : ship.getFullCoords()) {
if(boardsCoords.equals(coords)) if(boardsCoords.equals(coords))
@ -71,6 +83,12 @@ public abstract class AbstractView implements View {
return false; return false;
} }
/**
* ask player for keyboard input and parse it into one of {@link Direction} value
* @return Direction depending of player input
* @throws InterruptedException see {@link Window#getDirectionFromChar()}
* @see Window#getDirectionFromChar()
*/
protected Direction getDirectionFromChar() throws InterruptedException { protected Direction getDirectionFromChar() throws InterruptedException {
String dir; String dir;
while (true) { while (true) {
@ -84,21 +102,37 @@ public abstract class AbstractView implements View {
} }
} }
/**
* ask player for keyboard input and return result
* @return String given by player
* @throws InterruptedException see {@link Window#getKeyInput()}
*/
protected abstract String getKeyInput() throws InterruptedException; protected abstract String getKeyInput() throws InterruptedException;
/**
* Display a text above the grid on {@link Window}, simply print text on {@link Terminal}
* @param s text to display
* @see Window#setUpperText(String)
* @see Terminal#setUpperText(String)
*/
protected abstract void setUpperText(String s); protected abstract void setUpperText(String s);
/**
* used if {@code player} instance of {@link battleship.model.player.Computer}
* @param player player we ask to set position of its ships
* @throws InterruptedException see {@link Window#setShips(Player)}
*/
@Override @Override
public void setShips(Player player) throws InterruptedException { public void setShips(Player player) throws InterruptedException {
player.placeShips(); player.placeShips();
} }
/** /**
*
* @param other other than the current player * @param other other than the current player
* @param ship check if this ship at this position is touch * @param ship check if this ship at this position has been hit
* @param pair coords * @param pair coords
* @return 1 if ship fully drowned, 2 if only damaged, 0 if not * @return 1 if ship fully drowned, 2 if only damaged, 0 if not
* @see
*/ */
private int isPositionDrowned(Player other, Ship ship, Pair<Integer, Integer> pair) { private int isPositionDrowned(Player other, Ship ship, Pair<Integer, Integer> pair) {
if(ship.isDrown()) if(ship.isDrown())
@ -106,6 +140,11 @@ public abstract class AbstractView implements View {
return isPositionDrowned(other, pair); return isPositionDrowned(other, pair);
} }
/**
* @param other other than the current player
* @param pair coords to check
* @return 2 if player already played here, 0 otherwise
*/
private int isPositionDrowned(Player other, Pair<Integer, Integer> pair) { private int isPositionDrowned(Player other, Pair<Integer, Integer> pair) {
for(Triplet<Integer, Integer, Boolean> move : other.getMoves()) { for(Triplet<Integer, Integer, Boolean> move : other.getMoves()) {
if(pair.getLeft().equals(move.getLeft()) && pair.getRight().equals(move.getMiddle())) { if(pair.getLeft().equals(move.getLeft()) && pair.getRight().equals(move.getMiddle())) {
@ -115,12 +154,33 @@ public abstract class AbstractView implements View {
return 0; return 0;
} }
/**
* used by {@link battleship.model.player.Computer} player to play a move in the grid depending of its algorithm
* @param player {@link battleship.model.Game#currentPlayer}
* @return a couple ({@link Pair} containing the x and y coordinate (left side store Y and right side X)
* @throws InterruptedException see {@link Window#chooseMove(Player)}
*/
@Override @Override
public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException { public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException {
return player.chooseMove(); return player.chooseMove();
} }
/**
* ask {@code player} for mouse input
* @param player {@link Game#currentPlayer}
* @return coordinate of {@code player} opponent grid
* @see Window#mouseInput(Player)
* @throws InterruptedException see {@link Window#mouseInput(Player)}
*/
protected abstract Pair<Integer, Integer> mouseInput(Player player) throws InterruptedException; protected abstract Pair<Integer, Integer> mouseInput(Player player) throws InterruptedException;
/**
* ask {@link Game#currentPlayer} for keyboard input
* @return String given by player
* @throws InterruptedException see {@link Window#keyboardInput()}
* @see Window#keyboardInput()
* @see Terminal#keyboardInput()
* @see Terminal#keyboardInputInteger()
*/
protected abstract String keyboardInput() throws InterruptedException; protected abstract String keyboardInput() throws InterruptedException;
} }

View File

@ -10,6 +10,11 @@ import battleship.utils.Pair;
import java.util.Scanner; import java.util.Scanner;
/**
* Terminal view, instanced if argument 2 equals to "nogui"
* @see View
* @see AbstractView
*/
public class Terminal extends AbstractView { public class Terminal extends AbstractView {
public static Scanner scanner = null; public static Scanner scanner = null;
@ -22,16 +27,28 @@ public class Terminal extends AbstractView {
keyboardComponent = new TerminalKeyboardListener(scanner); keyboardComponent = new TerminalKeyboardListener(scanner);
} }
/**
* @return given string in terminal
*/
@Override @Override
protected String getKeyInput() { protected String getKeyInput() {
return scanner.next().toUpperCase(); return scanner.next().toUpperCase();
} }
/**
* print string
* @param s text to display
*/
@Override @Override
protected void setUpperText(String s) { protected void setUpperText(String s) {
System.out.println(s); System.out.println(s);
} }
/**
* Ask {@code player} to set position of its ships
* @param player player we ask to set position of its ships
* @throws InterruptedException see {@link AbstractView#getDirectionFromChar()}
*/
@Override @Override
public void setShips(Player player) throws InterruptedException { public void setShips(Player player) throws InterruptedException {
setUpperText("Joueur " + player.getId() + ", placez vos navires"); setUpperText("Joueur " + player.getId() + ", placez vos navires");
@ -69,11 +86,22 @@ public class Terminal extends AbstractView {
} }
} }
/**
* print board in terminal
*/
@Override @Override
public void displayBoard() { public void displayBoard() {
System.out.println(this); System.out.println(this);
} }
/**
* ask player to choose a coords on its opponent grid, call {@link AbstractView#chooseMove(Player)} if instance of
* player is {@link battleship.model.player.Computer}
* if {@code player} isn't {@link Human} instance
* @param player {@link battleship.model.Game#currentPlayer}
* @return a element containing the x and y coordinate (left side store Y and right side X)
* @throws InterruptedException see {@link AbstractView#chooseMove(Player)}
*/
@Override @Override
public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException { public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException {
if(player instanceof Human) { if(player instanceof Human) {
@ -97,20 +125,38 @@ public class Terminal extends AbstractView {
} }
/**
* Never call in Terminal
* @param player {@link Game#currentPlayer}
* @return {@code null}
*/
@Override @Override
protected Pair<Integer, Integer> mouseInput(Player player) { protected Pair<Integer, Integer> mouseInput(Player player) {
return null; return null;
} }
/**
* @see TerminalKeyboardListener#keyboardInput()
* @return given string in terminal
*/
@Override @Override
protected String keyboardInput() { protected String keyboardInput() {
return keyboardComponent.keyboardInput(); return keyboardComponent.keyboardInput();
} }
/**
* @see Terminal#keyboardInput()
* @return convert string from keyboardInput() and convert it into an integer
* @throws NumberFormatException if given string can't be parse into an integer
*/
protected int keyboardInputInteger() throws NumberFormatException { protected int keyboardInputInteger() throws NumberFormatException {
return Integer.parseInt(keyboardComponent.keyboardInput()); return Integer.parseInt(keyboardComponent.keyboardInput());
} }
/**
* print grid, winner player and close scanner, game automatically close after this
* @param winner the winner of the game.
*/
@Override @Override
public void displayWinner(Player winner) { public void displayWinner(Player winner) {
displayBoard(); displayBoard();

View File

@ -3,15 +3,44 @@ package battleship.view;
import battleship.model.player.Player; import battleship.model.player.Player;
import battleship.utils.Pair; import battleship.utils.Pair;
/**
* <p>View interface, used as an API<br>
* This model (interface(s) -> abstract class(es) -> concrete classes) prevent hard code.</p>
* <p>This is the only object which interact with other object<br>
* Allows an outside person easily change the code or add a view easily without modifying existing classes</p>
* @see AbstractView
* @see Terminal
* @see Window
*/
public interface View { public interface View {
int[] shipsSize = { 5, 4, 3, 3, 2}; int[] shipsSize = { 5, 4, 3, 3, 2};
/**
* Ask {@code player} to set position of its ships
* @param player player instance we ask
* @throws InterruptedException see {@link Window#setShips(Player)}
*/
void setShips(Player player) throws InterruptedException; void setShips(Player player) throws InterruptedException;
/**
* Display all grids
*/
void displayBoard(); void displayBoard();
/**
* ask the player the choose a position on its opponent grid
* @param player {@link battleship.model.Game#currentPlayer}
* @return a element containing the x and y coordinate (left side store Y and right side X)
* @throws InterruptedException see {@link Window#chooseMove(Player)}
*/
Pair<Integer,Integer> chooseMove(Player player) throws InterruptedException; Pair<Integer,Integer> chooseMove(Player player) throws InterruptedException;
/**
* Display the winner of the game and then close the game
* @param winner the winner of the game.
* @see Window#displayWinner(Player)
* @see Terminal#displayWinner(Player)
*/
void displayWinner(Player winner); void displayWinner(Player winner);
} }

View File

@ -15,11 +15,22 @@ import java.awt.*;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
/**
* Window view, instanced if argument 2 equals to "nogui"
* @see View
* @see AbstractView
*/
public class Window extends AbstractView { public class Window extends AbstractView {
final JFrame frame; final JFrame frame;
/**
* grids height, do no represent frame size
*/
public final int height = 600; public final int height = 600;
/**
* grids width, do no represent frame size
*/
public final int width = 1200; public final int width = 1200;
private final WindowMouseListener mouseComponent; private final WindowMouseListener mouseComponent;
private final WindowKeyboardListener keyboardComponent; private final WindowKeyboardListener keyboardComponent;
@ -46,11 +57,20 @@ public class Window extends AbstractView {
return keyboardInput(); return keyboardInput();
} }
/**
* Display a text above the grid
* @param s text to display
*/
@Override @Override
protected void setUpperText(String s) { protected void setUpperText(String s) {
upperTitle = s; upperTitle = s;
} }
/**
* Ask {@code player} to set position of its ships
* @param player player we ask
* @throws InterruptedException see {@link Window#mouseInput(Player)} and {@link Window#getDirectionFromChar()}
*/
@Override @Override
public void setShips(Player player) throws InterruptedException { public void setShips(Player player) throws InterruptedException {
if(player instanceof Human) { if(player instanceof Human) {
@ -80,6 +100,11 @@ public class Window extends AbstractView {
} }
} }
/**
* ask {@link Game#currentPlayer} for keyboard input
* @return String given by player
* @throws InterruptedException throw if this Thread is interrupted while {@link Thread#sleep(long) sleeping}
*/
@Override @Override
protected String keyboardInput() throws InterruptedException { protected String keyboardInput() throws InterruptedException {
keyboardComponent.requestInput = true; keyboardComponent.requestInput = true;
@ -94,6 +119,12 @@ public class Window extends AbstractView {
} }
} }
/**
* ask {@code player} for mouse input
* @param player {@link Game#currentPlayer}
* @return coordinate of {@code player} opponent grid
* @throws InterruptedException throw if this Thread is interrupted while {@link Thread#sleep(long) sleeping}
*/
@Override @Override
protected Pair<Integer, Integer> mouseInput(Player player) throws InterruptedException { protected Pair<Integer, Integer> mouseInput(Player player) throws InterruptedException {
mouseComponent.requestInput = true; mouseComponent.requestInput = true;
@ -105,7 +136,6 @@ public class Window extends AbstractView {
mouseComponent.playerIdLastInput = 0; mouseComponent.playerIdLastInput = 0;
Pair<Integer, Integer> value = mouseComponent.lastInput; Pair<Integer, Integer> value = mouseComponent.lastInput;
mouseComponent.lastInput = null; mouseComponent.lastInput = null;
System.out.println(value);
return value; return value;
} else { } else {
openDialog("Vous avez cliquer sur une zone de jeu qui n'est pas la votre"); openDialog("Vous avez cliquer sur une zone de jeu qui n'est pas la votre");
@ -116,21 +146,40 @@ public class Window extends AbstractView {
} }
} }
/**
* Open a window with {@code message} as content and with a "OK" button
* @param message message to display
* @param exitOnClose {@code true} if when user close this window, the program exit, {@code false} otherwise
*/
public void openDialog(String message, boolean exitOnClose) { public void openDialog(String message, boolean exitOnClose) {
JOptionPane.showMessageDialog(frame, message); JOptionPane.showMessageDialog(frame, message);
if(exitOnClose) if(exitOnClose)
System.exit(0); System.exit(0);
} }
/**
* Open a window with {@code message} as content and with a "OK" button
* @param message message to display
* @see Window#openDialog(String, boolean)
*/
public void openDialog(String message) { public void openDialog(String message) {
openDialog(message, false); openDialog(message, false);
} }
/**
* refresh windows to display updated content
*/
@Override @Override
public void displayBoard() { public void displayBoard() {
frame.paintComponents(frame.getGraphics()); frame.paintComponents(frame.getGraphics());
} }
/**
* ask player to choose a position in its opponent grid
* @param player {@link battleship.model.Game#currentPlayer}
* @return a couple ({@link Pair} containing the x and y coordinate (left side store Y and right side X)
* @throws InterruptedException see {@link Window#mouseInput(Player)}
*/
@Override @Override
public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException { public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException {
setUpperText("Joueur " + player.getId() + " cliquer sur l'emplacement ou vous souhaitez tirer"); setUpperText("Joueur " + player.getId() + " cliquer sur l'emplacement ou vous souhaitez tirer");
@ -150,11 +199,19 @@ public class Window extends AbstractView {
} }
/**
* open a dialog to display the winner and exit the program when window is closed
* @param winner the winner of the game.
*/
@Override @Override
public void displayWinner(Player winner) { public void displayWinner(Player winner) {
openDialog("Le joueur " + winner.getId() + " a gagné(e)", true); openDialog("Le joueur " + winner.getId() + " a gagné(e)", true);
} }
/**
* Panel where we paint the board
* @see JPanel
*/
class Draw extends JPanel { class Draw extends JPanel {
private final Window window; private final Window window;
@ -240,11 +297,8 @@ public class Window extends AbstractView {
g2d.rotate(Math.toRadians(45), x1 + initialWidth - 9, y1 - 9); g2d.rotate(Math.toRadians(45), x1 + initialWidth - 9, y1 - 9);
g2d.fill(cross2); g2d.fill(cross2);
g2d.rotate(Math.toRadians(-45), x1 + initialWidth - 9, y1 - 9); g2d.rotate(Math.toRadians(-45), x1 + initialWidth - 9, y1 - 9);
} }
} }
System.out.println(window);
} }
} }
} }

View File

@ -0,0 +1,4 @@
/**
* Contains terminal and windows view classes and theirs super class and interface
*/
package battleship.view;