Read and parse launch arguments.
+ launch arguments need to follow this syntax:
+
<player1_instance> <player2_instance> [nogui]
+
<arg> -> mandatory parameter
+ [arg] -> optional parameter
+ player_instance values are: "Human" or "Random"
+ nogui to launch the game in terminal or nothing to launch in graphical interface
Returns an array containing the constants of this enum type, in
+the order they are declared.
+
+
Returns:
+
an array containing the constants of this enum type, in the order they are declared
+
+
+
+
+
+
valueOf
+
public staticDirectionvalueOf(java.lang.String name)
+
Returns the enum constant of this type with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this type. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
+
Returns:
+
the enum constant with the specified name
+
Throws:
+
java.lang.IllegalArgumentException - if this enum type has no constant with the specified name
+
java.lang.NullPointerException - if the argument is null
reference every shot on the opponent board, left and middle side of the Triplet reference the coordinates and the
+ right side if this move hit or not an opponent ship
check if ship position and direction are valides and does not overlap on other vessels
+ add the ship to player ships list and return true if valid
+ false otherwise
reference every shot on the opponent board, left and middle side of the Triplet reference the coordinates and the
+ right side if this move hit or not an opponent ship
check if ship position and direction are valides and does not overlap on other vessels
+ add the ship to player ships list and return true if valid
+ false otherwise
This model (interface -> abstract class(es) -> concrete classes) prevent hard code.
+
This is the only object which interact with other object
+ Allows an outside person from the project to easily change the code or to add a view easily without modifying
+ existing classes
check if ship position and direction are valides and does not overlap on other vessels
+ add the ship to player AbstractPlayer.ships list and return true if valid
+ false otherwise
check if ship position and direction are valides and does not overlap on other vessels
+ add the ship to player AbstractPlayer.ships list and return true if valid
+ false otherwise
This model (interface -> abstract class(es) -> concrete classes) prevent hard code.
+
This is the only object which interact with other object
+ Allows an outside person easily change the code or add a view easily without modifying existing classes
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
Overview
+
The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain six categories:
+
+
Interfaces
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class or Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class Inheritance Diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class or Interface Declaration
+
Class or Interface Description
+
+
+
+
Nested Class Summary
+
Field Summary
+
Property Summary
+
Constructor Summary
+
Method Summary
+
+
+
+
Field Details
+
Property Details
+
Constructor Details
+
Method Details
+
+
The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type Declaration
+
Annotation Type Description
+
Required Element Summary
+
Optional Element Summary
+
Element Details
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum Declaration
+
Enum Description
+
Enum Constant Summary
+
Enum Constant Details
+
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. Classes are organized by inheritance structure starting with java.lang.Object. Interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking on "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to shortcomings, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic index of all classes, interfaces, constructors, methods, and fields, as well as lists of all packages and all classes.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to those who implement rather than use the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See Also" section of the class description.
You can search for definitions of modules, packages, types, fields, methods, system properties and other terms defined in the API, using some or all of the name, optionally using "camel-case" abbreviations. For example:
+
+
j.l.obj will match "java.lang.Object"
+
InpStr will match "java.io.InputStream"
+
HM.cK will match "java.util.HashMap.containsKey(Object)"
reference every shot on the opponent board, left and middle side of the Triplet reference the coordinates and the
+ right side if this move hit or not an opponent ship
check if ship position and direction are valides and does not overlap on other vessels
+ add the ship to player AbstractPlayer.ships list and return true if valid
+ false otherwise
").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[t](e),void 0):(this.search(null,e),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.children().hide(),t("
Read and parse launch arguments.
+ * launch arguments need to follow this syntax:
+ *
{@code [nogui]}
+ *
<arg> -> mandatory parameter
+ * [arg] -> optional parameter
+ * player_instance values are: "Human" or "Random"
+ * nogui to launch the game in terminal or nothing to launch in graphical interface
+ * @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 {
+ Player[] players = new Player[2];
+ ArrayList>> playerClass = new ArrayList<>(2);
+ playerClass.add(new Pair<>("human", Human.class));
+ playerClass.add(new Pair<>("random", Random.class));
+ if(args.length >= 2) {
+ for(int i = 0; i < 2; ++i) {
+ for (Pair> pair : playerClass) {
+ if(args[i].equalsIgnoreCase(pair.getLeft())) {
+ players[i] = pair.getRight().getDeclaredConstructor().newInstance();
+ }
+ }
+ }
+ if(players[0] != null && players[1] != null) {
+ game = new Game(players);
+ } else
+ throw new IllegalArgumentException("Arguments incorrects: " + Arrays.toString(args));
+ if(args.length >= 3) {
+ // arguments > 3 ignorés
+ if(args[2].equalsIgnoreCase("nogui"))
+ view = new Terminal(game);
+ else
+ view = new Window(game);
+ } else {
+ view = new Window(game);
+ }
+ } else
+ throw new NoSuchElementException("Pas assez d'arguments");
+ }
+}
diff --git a/livraison/src/battleship/control/TerminalKeyboardListener.java b/livraison/src/battleship/control/TerminalKeyboardListener.java
new file mode 100644
index 0000000..00e56fa
--- /dev/null
+++ b/livraison/src/battleship/control/TerminalKeyboardListener.java
@@ -0,0 +1,16 @@
+package battleship.control;
+
+import java.util.Scanner;
+
+public class TerminalKeyboardListener {
+
+ private final Scanner scanner;
+
+ public TerminalKeyboardListener(Scanner scanner) {
+ this.scanner = scanner;
+ }
+
+ public String keyboardInput() {
+ return scanner.next();
+ }
+}
diff --git a/livraison/src/battleship/control/WindowKeyboardListener.java b/livraison/src/battleship/control/WindowKeyboardListener.java
new file mode 100644
index 0000000..04555a9
--- /dev/null
+++ b/livraison/src/battleship/control/WindowKeyboardListener.java
@@ -0,0 +1,34 @@
+package battleship.control;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+public class WindowKeyboardListener implements KeyListener {
+
+ public boolean requestInput = false;
+ public char keyTyped = KeyEvent.CHAR_UNDEFINED;
+ public int keyTypedArrow = KeyEvent.VK_UNDEFINED;
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if(requestInput) {
+ if(e.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
+ keyTyped = e.getKeyChar();
+ if(e.getKeyCode() != KeyEvent.VK_UNDEFINED) {
+ System.out.println(e.getKeyCode());
+ keyTypedArrow = e.getKeyCode();
+ }
+
+ }
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+
+ }
+}
diff --git a/livraison/src/battleship/control/WindowMouseListener.java b/livraison/src/battleship/control/WindowMouseListener.java
new file mode 100644
index 0000000..4b1c140
--- /dev/null
+++ b/livraison/src/battleship/control/WindowMouseListener.java
@@ -0,0 +1,58 @@
+package battleship.control;
+
+import battleship.utils.Pair;
+import battleship.view.Window;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+public class WindowMouseListener implements MouseListener {
+
+ private final Window window;
+ public boolean requestInput = false;
+ public Pair lastInput = null;
+ public int playerIdLastInput = 0;
+
+ public WindowMouseListener(Window view) {
+ this.window = view;
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if(requestInput) {
+ int x = e.getX() - 7;
+ int y = e.getY() - 30;
+ int initialHeight = window.height / 12;
+ int initialWidth = window.width / 23;
+ if(y >= initialHeight * 2 && y <= window.height) {
+ y -= initialHeight * 2;
+ if(x >= initialWidth && x <= initialWidth * 11) {
+ x -= initialWidth;
+ lastInput = new Pair<>((y + 2) / initialHeight, (x + 2) / initialWidth);
+ playerIdLastInput = 1;
+ } else if(x >= initialHeight * 13 && x <= window.width) {
+ x -= initialWidth * 13;
+ lastInput = new Pair<>((y + 2) / initialHeight, (x + 2) / initialWidth);
+ playerIdLastInput = 2;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent ignored) {}
+
+ @Override
+ public void mouseExited(MouseEvent ignored) {}
+
+}
diff --git a/livraison/src/battleship/control/package-info.java b/livraison/src/battleship/control/package-info.java
new file mode 100644
index 0000000..c1cc5f0
--- /dev/null
+++ b/livraison/src/battleship/control/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains keyboard and mouse listeners
+ */
+package battleship.control;
\ No newline at end of file
diff --git a/livraison/src/battleship/model/Direction.java b/livraison/src/battleship/model/Direction.java
new file mode 100644
index 0000000..f9b63fd
--- /dev/null
+++ b/livraison/src/battleship/model/Direction.java
@@ -0,0 +1,36 @@
+package battleship.model;
+
+import battleship.utils.Pair;
+
+import java.awt.event.KeyEvent;
+
+public enum Direction {
+
+ RIGHT(new Pair<>(0, 1), "D", KeyEvent.VK_RIGHT),
+ LEFT(new Pair<>(0,-1), "G", KeyEvent.VK_LEFT),
+ UP(new Pair<>(-1,0), "H", KeyEvent.VK_UP),
+ DOWN(new Pair<>(1,0), "B", KeyEvent.VK_DOWN),
+ DEFAULT(new Pair<>(-1,-1), null, KeyEvent.VK_UNDEFINED);
+
+ private final Pair direction;
+ private final String keyword;
+ private final int arrow;
+
+ Direction(Pair ukPair, String keyword, int arrow) {
+ this.direction = ukPair;
+ this.keyword = keyword;
+ this.arrow = arrow;
+ }
+
+ public Pair getDirection() {
+ return direction;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public int getArrow() {
+ return arrow;
+ }
+}
diff --git a/livraison/src/battleship/model/Game.java b/livraison/src/battleship/model/Game.java
new file mode 100644
index 0000000..a0d52a6
--- /dev/null
+++ b/livraison/src/battleship/model/Game.java
@@ -0,0 +1,111 @@
+package battleship.model;
+
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+import battleship.view.View;
+
+/**
+ * Main game class
+ */
+public class Game {
+
+ public final Player[] players;
+ public Player currentPlayer;
+
+ public Game(Player[] players) {
+ this.players = players;
+ this.currentPlayer = players[0];
+ players[0].setId(1);
+ players[1].setId(2);
+ }
+
+ public Player getCurrentPlayer(){
+ return this.currentPlayer;
+ }
+
+ public Player getOtherPlayer() {
+ return this.currentPlayer == players[0] ? players[1] : players[0];
+ }
+
+ public Player getOtherPlayer(Player player) {
+ return this.currentPlayer == player ? getOtherPlayer() : currentPlayer;
+ }
+
+ public void changeCurrentPlayer(){
+ currentPlayer = getOtherPlayer();
+ }
+
+ /**
+ * Update ship to know if they're drowned
+ */
+ public void checkDrownedShips(){
+ Player otherPlayer = getOtherPlayer();
+ for(Ship ship : otherPlayer.getShips()){
+ if(!ship.isDrown())
+ ship.updateIsDrown(currentPlayer);
+ }
+ }
+
+ /**
+ * @return player 1 if player 2' ships are drowned, or player 2 if player1' ships are drowned, null otherwise
+ */
+ public Player getWinner(){
+ Ship remainingShip = players[0].getShips().parallelStream().filter(ship -> !ship.isDrown()).findFirst().orElse(null);
+ if(remainingShip == null)
+ return players[1];
+ remainingShip = players[1].getShips().parallelStream().filter(ship -> !ship.isDrown()).findFirst().orElse(null);
+ if(remainingShip == null)
+ return players[0];
+ return null;
+
+ }
+
+ /**
+ * Play the selected move from current player in grid
+ * @param move selected player move
+ */
+ public void move(Pair move){
+ boolean bool = false;
+ Player otherPlayer = getOtherPlayer();
+ for (Ship ship : otherPlayer.getShips()) {
+ for(Pair coords : ship.getFullCoords()){
+ if ((coords.getRight().equals(move.getRight())) && (coords.getLeft().equals(move.getLeft()))) {
+ bool = true;
+ break;
+ }
+ }
+ if(bool)
+ break;
+ }
+ currentPlayer.addMove(new Triplet<>(move, bool));
+ }
+
+ /**
+ * game loop
+ * @param view can be {@link battleship.view.Terminal} or {@link battleship.view.Window}
+ */
+ public void Play(View view) {
+ try {
+ view.setShips(players[0]);
+ changeCurrentPlayer();
+ view.setShips(players[1]);
+ changeCurrentPlayer();
+ Player winner = null;
+ while (winner == null) {
+ System.out.println("Au tour du joueur " + currentPlayer.getId());
+ view.displayBoard();
+ move(view.chooseMove(currentPlayer));
+ checkDrownedShips();
+ changeCurrentPlayer();
+ winner = getWinner();
+ }
+ view.displayWinner(winner);
+ } catch (InterruptedException e) {
+ System.out.println("Une erreur est survenue");
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ }
+}
diff --git a/livraison/src/battleship/model/Ship.java b/livraison/src/battleship/model/Ship.java
new file mode 100644
index 0000000..00fcf76
--- /dev/null
+++ b/livraison/src/battleship/model/Ship.java
@@ -0,0 +1,107 @@
+package battleship.model;
+
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+
+/**
+ * player's ship class
+ */
+public class Ship {
+
+ /**
+ * base coordinates of the ship
+ */
+ private Pair coords;
+ /**
+ * ship size
+ */
+ private final int size;
+ /**
+ * ship full coordinates calculate thank to base coordinates, direction and size
+ */
+ final Pair[] fullCoords;
+ private Direction direction;
+ /**
+ * if true the ship is destroyed
+ */
+ private boolean isDrown;
+
+ @SuppressWarnings("unchecked")
+ public Ship(Pair coords, int size, Direction direction) {
+ this.coords = coords;
+ this.size = size;
+ this.fullCoords = new Pair[this.size];
+ this.direction = direction;
+ isDrown = false;
+ recalculateFullCoords();
+ }
+
+ public void setDirection(Direction d){
+ this.direction = d;
+ }
+
+ public void setCoords(Pair c){
+ this.coords = c;
+ }
+
+ /**
+ * set {@link Ship#isDrown} to true
+ */
+ public void setDrown(){
+ isDrown = true;
+ }
+
+ public boolean isDrown(){
+ return isDrown;
+ }
+
+ public int getSize(){
+ return this.size;
+ }
+
+ public Direction getDirection(){
+ return this.direction;
+ }
+
+ public Pair getCoords(){
+ return this.coords;
+ }
+
+ /**
+ * recalculate all coords based on this base coords, direction and size
+ */
+ public void recalculateFullCoords() {
+ for(int i = 0; i < size; ++i){
+ fullCoords[i] = new Pair<>(coords.getLeft() + i * direction.getDirection().getLeft(),coords.getRight() + i * direction.getDirection().getRight());
+ }
+ }
+
+ public Pair[] getFullCoords(){
+ return fullCoords;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + ", coords=" + coords.toString() + ", size=" + size + ", direction=" + direction.toString();
+ }
+
+ /**
+ * update value {@link Ship#isDrown} to true if {@code player} hit all of ship boxes
+ * @param player the opponent of this ship owner
+ */
+ public void updateIsDrown(Player player) {
+ int cpt = 0;
+ for(Pair coords : getFullCoords()) {
+ for(Triplet move : player.getMoves()) {
+ if(move.getRight() && move.getLeft().equals(coords.getLeft()) && move.getMiddle().equals(coords.getRight())){
+ ++cpt;
+ break;
+ }
+ }
+ }
+ if(cpt == getSize()) {
+ setDrown();
+ }
+ }
+}
diff --git a/livraison/src/battleship/model/package-info.java b/livraison/src/battleship/model/package-info.java
new file mode 100644
index 0000000..ddd86bc
--- /dev/null
+++ b/livraison/src/battleship/model/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains all main Game classes
+ */
+package battleship.model;
\ No newline at end of file
diff --git a/livraison/src/battleship/model/player/AbstractPlayer.java b/livraison/src/battleship/model/player/AbstractPlayer.java
new file mode 100644
index 0000000..11ec9c0
--- /dev/null
+++ b/livraison/src/battleship/model/player/AbstractPlayer.java
@@ -0,0 +1,98 @@
+package battleship.model.player;
+
+import battleship.model.Direction;
+import battleship.model.Ship;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+
+import java.util.ArrayList;
+
+/**
+ * Abstract player class see {@link Player} to know more about this code organisation
+ * @see Player
+ * @see Human
+ * @see Computer
+ * @see Random
+ */
+public abstract class AbstractPlayer implements Player {
+
+ final ArrayList ships = new ArrayList<>();
+ /**
+ * reference every shot on the opponent board, left and middle side of the Triplet reference the coordinates and the
+ * right side if this move hit or not an opponent ship
+ */
+ final ArrayList> moves = new ArrayList<>();
+ public int id;
+
+ public boolean setShips(Ship ship) {
+ if(ship.getDirection() == Direction.DEFAULT)
+ return false;
+ for(Pair coords : ship.getFullCoords()) {
+ if(coords.getLeft() > 9 || coords.getLeft() < 0 || coords.getRight() > 9 || coords.getRight() < 0)
+ return false;
+ for(Ship ship1 : this.ships) {
+ for (Pair coords1 : ship1.getFullCoords()) {
+ if (coords1.getLeft().equals(coords.getLeft()) && coords1.getRight().equals(coords.getRight()))
+ return false;
+ }
+ }
+ }
+ this.ships.add(ship);
+ return true;
+ }
+
+ /**
+ * add {@code move} to the {@link AbstractPlayer#moves} list
+ */
+ public void addMove(Triplet move){
+ moves.add(move);
+ }
+
+ public ArrayList> validMoves() {
+ ArrayList> validMovesList = new ArrayList<>();
+ for(int x = 0; x < 10; x++){
+ for(int y = 0; y < 10; y++) {
+ Pair coords = new Pair<>(x,y);
+ if(!(moves.contains(new Triplet<>(coords, true)) || moves.contains(new Triplet<>(coords, false)))){
+ validMovesList.add(new Pair<>(x,y));
+ }
+ }
+ }
+ return validMovesList;
+
+ }
+
+ public boolean areValid(int x, int y){
+ if(x < 0 || x > 10 || y < 0 || y > 10)
+ return false;
+ for(Triplet move : moves){
+ if(move.getLeft() == x && move.getMiddle() == y)
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void setId(int i) {
+ id = i;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " " + id;
+ }
+
+ @Override
+ public ArrayList getShips() {
+ return ships;
+ }
+
+ @Override
+ public ArrayList> getMoves() {
+ return moves;
+ }
+}
diff --git a/livraison/src/battleship/model/player/Computer.java b/livraison/src/battleship/model/player/Computer.java
new file mode 100644
index 0000000..ea9832a
--- /dev/null
+++ b/livraison/src/battleship/model/player/Computer.java
@@ -0,0 +1,28 @@
+package battleship.model.player;
+
+import battleship.model.Direction;
+import battleship.model.Ship;
+import battleship.utils.Pair;
+
+import java.util.Random;
+
+/**
+ *
Computer super class, all player object which use an algorithm to calculate coordinates to use should extend from
+ * this object.
+ *
Random is the only algorithm include here but another algorithm can be easily implemented
+ */
+public abstract class Computer extends AbstractPlayer {
+
+ public void placeShipRandomly() {
+ java.util.Random rand = new Random();
+ for(int i : shipSize) {
+ Ship ship = new Ship(new Pair<>(-1, -1), i, Direction.DEFAULT);
+ while(!setShips(ship)) {
+ ship.setCoords(new Pair<>(rand.nextInt(10), rand.nextInt(10)));
+ ship.setDirection(Direction.values()[rand.nextInt(Direction.values().length)]);
+ ship.recalculateFullCoords();
+ }
+ }
+ }
+
+}
diff --git a/livraison/src/battleship/model/player/Human.java b/livraison/src/battleship/model/player/Human.java
new file mode 100644
index 0000000..0a94e8d
--- /dev/null
+++ b/livraison/src/battleship/model/player/Human.java
@@ -0,0 +1,19 @@
+package battleship.model.player;
+
+import battleship.utils.Pair;
+
+/**
+ * This object do nothing itself, it just an interface to know the type of player (Human or not),
+ * each view interact with the player with its methods
+ */
+public class Human extends AbstractPlayer {
+
+ @Override
+ public Pair chooseMove() {
+ return null;
+ }
+
+ @Override
+ public void placeShips() {}
+
+}
diff --git a/livraison/src/battleship/model/player/Player.java b/livraison/src/battleship/model/player/Player.java
new file mode 100644
index 0000000..f97f2c8
--- /dev/null
+++ b/livraison/src/battleship/model/player/Player.java
@@ -0,0 +1,73 @@
+package battleship.model.player;
+
+import battleship.model.Ship;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+
+import java.util.ArrayList;
+
+/**
+ *
Player interface, used as an API.
+ *
This model (interface -> abstract class(es) -> concrete classes) prevent hard code.
+ *
This is the only object which interact with other object
+ * Allows an outside person from the project to easily change the code or to add a view easily without modifying
+ * existing classes
+ * @see AbstractPlayer
+ * @see Computer
+ * @see Human
+ * @see Random
+ */
+public interface Player {
+
+ int[] shipSize = { 5, 4, 3, 3, 2 };
+
+ /**
+ * Used by computer only
+ * @see Random#chooseMove()
+ * @return coords in its opponent grid to play a move
+ */
+ Pair chooseMove();
+
+ /**
+ * check if ship position and direction are valides and does not overlap on other vessels
+ * add the ship to player {@link AbstractPlayer#ships} list and return {@code true} if valid
+ * {@code false} otherwise
+ * @param ship the ship instance we check
+ * @return {@code true} if ship data are valids, {@code false} otherwise
+ */
+ boolean setShips(Ship ship);
+
+ int getId();
+
+ /**
+ * Adds coordinates of the {@code move} in the {@link AbstractPlayer#moves} list
+ * @param move the move chosen by the player
+ */
+ void addMove(Triplet move);
+
+
+ void setId(int i);
+
+ /**
+ * give a list of the player possible moves, used in {@link Player#chooseMove()}
+ * @return a list of playable move
+ */
+ ArrayList> validMoves();
+
+ /**
+ * Used by {@link Computer} instances to place ships
+ */
+ void placeShips();
+
+ /**
+ * check if coordinates from {@link battleship.view.View#chooseMove(Player)}are in valids position
+ * @param x the x-axis of the coordinates
+ * @param y the y-axis of the coordinates
+ * @return {@code true} if valid, {@code false} otherwise
+ */
+ boolean areValid(int x, int y);
+
+ ArrayList getShips();
+
+ ArrayList> getMoves();
+}
diff --git a/livraison/src/battleship/model/player/Random.java b/livraison/src/battleship/model/player/Random.java
new file mode 100644
index 0000000..a6dfcfb
--- /dev/null
+++ b/livraison/src/battleship/model/player/Random.java
@@ -0,0 +1,21 @@
+package battleship.model.player;
+
+import battleship.utils.Pair;
+
+/**
+ * place its ship and choose moves randomly
+ */
+public class Random extends Computer {
+
+ @Override
+ public Pair chooseMove() {
+ java.util.Random rand = new java.util.Random();
+ return validMoves().get(rand.nextInt(validMoves().size()));
+ }
+
+ @Override
+ public void placeShips() {
+ placeShipRandomly();
+ }
+
+}
diff --git a/livraison/src/battleship/model/player/package-info.java b/livraison/src/battleship/model/player/package-info.java
new file mode 100644
index 0000000..1ff58cc
--- /dev/null
+++ b/livraison/src/battleship/model/player/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains all players classes instances and theirs super classes and interface
+ */
+package battleship.model.player;
\ No newline at end of file
diff --git a/livraison/src/battleship/package-info.java b/livraison/src/battleship/package-info.java
new file mode 100644
index 0000000..e8d3bd2
--- /dev/null
+++ b/livraison/src/battleship/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains Main Class
+ */
+package battleship;
\ No newline at end of file
diff --git a/livraison/src/battleship/utils/Pair.java b/livraison/src/battleship/utils/Pair.java
new file mode 100644
index 0000000..16de05f
--- /dev/null
+++ b/livraison/src/battleship/utils/Pair.java
@@ -0,0 +1,53 @@
+package battleship.utils;
+
+import java.util.Objects;
+
+/**
+ * tuple containing 2 generic type elements
+ *
+ * @param left
+ * @param right
+ */
+public class Pair {
+
+ private final U left;
+ private final K right;
+
+ public Pair(U left, K right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public U getLeft() {
+ return left;
+ }
+
+ public K getRight() {
+ return right;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Pair, ?> other = (Pair, ?>) obj;
+ return this.left.equals(other.getLeft()) && this.right.equals(other.getRight());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(left.hashCode(), right.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return "(" + left + ", " + right + ")";
+ }
+}
diff --git a/livraison/src/battleship/utils/Triplet.java b/livraison/src/battleship/utils/Triplet.java
new file mode 100644
index 0000000..961a335
--- /dev/null
+++ b/livraison/src/battleship/utils/Triplet.java
@@ -0,0 +1,64 @@
+package battleship.utils;
+
+import java.util.Objects;
+
+/**
+ * tuple containing 3 generic type elements
+ *
+ * @param left
+ * @param middle
+ * @param right
+ */
+public class Triplet {
+
+ private final U left;
+ private final K middle;
+ private final V right;
+
+ public Triplet(U left, K middle, V right) {
+ this.left = left;
+ this.middle = middle;
+ this.right = right;
+ }
+
+ public Triplet(Pair pair, V right) {
+ this(pair.getLeft(), pair.getRight(), right);
+ }
+
+ public U getLeft() {
+ return left;
+ }
+
+ public K getMiddle() {
+ return middle;
+ }
+
+ public V getRight() {
+ return right;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Triplet, ?, ?> other = (Triplet, ?, ?>) obj;
+ return this.left.equals(other.getLeft()) && this.middle.equals(other.getMiddle()) && this.right.equals(other.getRight());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(left, middle, right);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + left + ", " + middle + ", " + right + ")";
+ }
+}
diff --git a/livraison/src/battleship/utils/package-info.java b/livraison/src/battleship/utils/package-info.java
new file mode 100644
index 0000000..abd472d
--- /dev/null
+++ b/livraison/src/battleship/utils/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Utility classes
+ */
+package battleship.utils;
\ No newline at end of file
diff --git a/livraison/src/battleship/view/AbstractView.java b/livraison/src/battleship/view/AbstractView.java
new file mode 100644
index 0000000..bcf2a26
--- /dev/null
+++ b/livraison/src/battleship/view/AbstractView.java
@@ -0,0 +1,192 @@
+package battleship.view;
+
+import battleship.model.Direction;
+import battleship.model.Game;
+import battleship.model.Ship;
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+
+import java.util.ArrayList;
+
+/**
+ * Abstract view class
+ * @see View
+ * @see Window
+ * @see Terminal
+ */
+public abstract class AbstractView implements View {
+
+ protected final Game game;
+
+ public AbstractView(Game game) {
+ this.game = game;
+ }
+
+ /**
+ * Used during debugging, used in terminal to display grids too
+ * @return all player grids
+ */
+ @Override
+ public String toString() {
+ return toString(true);
+ }
+
+ public String toString(boolean debug) {
+ StringBuilder chain = new StringBuilder();
+ for(int u = 0; u < 2; ++u) {
+ Player player = game.players[u];
+ ArrayList ships = game.players[u].getShips();
+ chain.append("Joueur ").append(player.getId()).append(" :\n");
+ chain.append("+ 0 1 2 3 4 5 6 7 8 9 +\n");
+ for(int x = 0; x < 10; ++x) {
+ chain.append(x);
+ for(int y = 0; y < 10; ++y) {
+ Pair pair = new Pair<>(x, y);
+ boolean isPosition = false;
+ for(Ship ship : ships) {
+ if(isShipPosition(ship, pair)) {
+ isPosition = true;
+ int result = isPositionDrowned(game.players[u == 0 ? 1 : 0], ship, pair);
+ if(result == 1) {
+ chain.append(" X");
+ } else if (result == 2){
+ chain.append(" !");
+ } else if(debug || game.getCurrentPlayer() == player) {
+ chain.append(" .");
+ } else {
+ chain.append(" _");
+ }
+ break;
+ }
+ }
+ if(!isPosition) {
+ if(isPositionDrowned(game.players[u == 0 ? 1 : 0], pair) == 2) {
+ chain.append(" ?");
+ } else {
+ chain.append(" _");
+ }
+ }
+
+ }
+ chain.append(" |\n");
+ }
+ chain.append("+ - - - - - - - - - - +\n");
+ }
+
+ return chain.toString();
+ }
+
+ /**
+ *
+ * @return {@code true} if {@link Ship#getFullCoords()} contains {@code boardsCoords}, {@code false} otherwise
+ */
+ private boolean isShipPosition(Ship ship, Pair boardsCoords) {
+ for(Pair coords : ship.getFullCoords()) {
+ if(boardsCoords.equals(coords))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * ask player for keyboard input and parse it into one of {@link Direction} value
+ * @return Direction depending of player input
+ * @throws InterruptedException caused by {@link Window#getKeyInput()}
+ * @see Window#getDirectionFromChar()
+ */
+ protected Direction getDirectionFromChar() throws InterruptedException {
+ String dir;
+ while (true) {
+ setUpperText("Veuillez indiquer la direction de placement de votre bateau (d droite, h haut, b bas, g gauche)");
+ dir = getKeyInput();
+ for (Direction direction : Direction.values()) {
+ if (direction.getKeyword() != null && direction.getKeyword().equals(dir)) {
+ return direction;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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;
+
+ /**
+ * 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);
+
+ /**
+ * 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
+ public void setShips(Player player) throws InterruptedException {
+ player.placeShips();
+ }
+
+ /**
+ * @param other other than the current player
+ * @param ship check if this ship at this position has been hit
+ * @param pair coords
+ * @return 1 if ship fully drowned, 2 if only damaged, 0 if not
+ * @see AbstractView#isPositionDrowned(Player, Pair)
+ */
+ private int isPositionDrowned(Player other, Ship ship, Pair pair) {
+ if(ship.isDrown())
+ return 1;
+ 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 pair) {
+ for(Triplet move : other.getMoves()) {
+ if(pair.getLeft().equals(move.getLeft()) && pair.getRight().equals(move.getMiddle())) {
+ return 2;
+ }
+ }
+ 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
+ public Pair chooseMove(Player player) throws InterruptedException {
+ 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 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;
+}
diff --git a/livraison/src/battleship/view/Terminal.java b/livraison/src/battleship/view/Terminal.java
new file mode 100644
index 0000000..0bd6d81
--- /dev/null
+++ b/livraison/src/battleship/view/Terminal.java
@@ -0,0 +1,167 @@
+package battleship.view;
+
+import battleship.control.TerminalKeyboardListener;
+import battleship.model.Direction;
+import battleship.model.Game;
+import battleship.model.Ship;
+import battleship.model.player.Human;
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+
+import java.util.Scanner;
+
+/**
+ * Terminal view, instanced if argument 2 equals to "nogui"
+ * @see View
+ * @see AbstractView
+ */
+public class Terminal extends AbstractView {
+
+ public static Scanner scanner = null;
+ private final TerminalKeyboardListener keyboardComponent;
+
+ public Terminal(Game game) {
+ super(game);
+ if(scanner == null)
+ scanner = new Scanner(System.in);
+ keyboardComponent = new TerminalKeyboardListener(scanner);
+ }
+
+ /**
+ * @return given string in terminal
+ */
+ @Override
+ protected String getKeyInput() {
+ return scanner.next().toUpperCase();
+ }
+
+ /**
+ * print string
+ * @param s text to display
+ */
+ @Override
+ protected void setUpperText(String 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
+ public void setShips(Player player) throws InterruptedException {
+ setUpperText("Joueur " + player.getId() + ", placez vos navires");
+ int x, y;
+ if(player instanceof Human) {
+ for(int i : shipsSize) {
+ boolean valid = false;
+ Pair defaultPos = new Pair<>(-1, -1);
+ Ship ship = new Ship(defaultPos, i, Direction.DEFAULT);
+ while (!player.setShips(ship)) {
+ try {
+ if (valid) {
+ setUpperText("Erreur");
+ }
+ valid = true;
+ setUpperText("Placement du bateau de longueur " + ship.getSize());
+ setUpperText("Veuillez indiquer la coordonée x de votre bateau");
+ x = keyboardInputInteger();
+ setUpperText("Veuillez indiquer la coordonée y de votre bateau");
+ y = keyboardInputInteger();
+ ship.setCoords(new Pair<>(x, y));
+ ship.setDirection(getDirectionFromChar());
+ ship.recalculateFullCoords();
+ } catch(NumberFormatException e) {
+ // Pour être sur qu'il ne passera pas la boucle
+ ship.setCoords(defaultPos);
+ ship.setDirection(Direction.DEFAULT);
+ }
+
+ }
+ }
+ } else {
+ super.setShips(player);
+ // Computer
+ }
+ }
+
+ /**
+ * print board in terminal
+ */
+ @Override
+ public void displayBoard() {
+ System.out.println(this.toString(false));
+ }
+
+ /**
+ * 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
+ public Pair chooseMove(Player player) throws InterruptedException {
+ if(player instanceof Human) {
+ int x = -1, y = -1;
+ while(!player.areValid(x, y)) {
+ try {
+ // y correspond à l'ordonnée mais est stocké comme étant l'abscisse
+ // (erreur de notre part aperçu lors du passage à une fenetre swing)
+ setUpperText("Veuillez indiquer la coordonée x de votre coup");
+ y = keyboardInputInteger();
+ setUpperText("Veuillez indiquer la coordonée y de votre coup");
+ x = keyboardInputInteger();
+ } catch (NumberFormatException ignored) {
+ x = -1;
+ y = -1;
+ }
+ }
+ return new Pair<>(x,y);
+ }
+ return super.chooseMove(player);
+
+ }
+
+ /**
+ * Never call in Terminal
+ * @param player {@link Game#currentPlayer}
+ * @return {@code null}
+ */
+ @Override
+ protected Pair mouseInput(Player player) {
+ return null;
+ }
+
+ /**
+ * @see TerminalKeyboardListener#keyboardInput()
+ * @return given string in terminal
+ */
+ @Override
+ protected String 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 {
+ 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
+ public void displayWinner(Player winner) {
+ displayBoard();
+ setUpperText("Le joueur " + winner.getId() + " a gagné");
+ scanner.close();
+ }
+
+}
diff --git a/livraison/src/battleship/view/View.java b/livraison/src/battleship/view/View.java
new file mode 100644
index 0000000..12e2df2
--- /dev/null
+++ b/livraison/src/battleship/view/View.java
@@ -0,0 +1,46 @@
+package battleship.view;
+
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+
+/**
+ *
View interface, used as an API
+ *
This model (interface -> abstract class(es) -> concrete classes) prevent hard code.
+ *
This is the only object which interact with other object
+ * Allows an outside person easily change the code or add a view easily without modifying existing classes
+ * @see AbstractView
+ * @see Terminal
+ * @see Window
+ */
+public interface View {
+
+ 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;
+
+ /**
+ * Display all grids
+ */
+ 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 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);
+}
diff --git a/livraison/src/battleship/view/Window.java b/livraison/src/battleship/view/Window.java
new file mode 100644
index 0000000..b2cdfde
--- /dev/null
+++ b/livraison/src/battleship/view/Window.java
@@ -0,0 +1,304 @@
+package battleship.view;
+
+import battleship.control.WindowKeyboardListener;
+import battleship.control.WindowMouseListener;
+import battleship.model.Direction;
+import battleship.model.Game;
+import battleship.model.Ship;
+import battleship.model.player.Human;
+import battleship.model.player.Player;
+import battleship.utils.Pair;
+import battleship.utils.Triplet;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.awt.geom.RoundRectangle2D;
+
+/**
+ * Window view, instanced if argument 2 equals to "nogui"
+ * @see View
+ * @see AbstractView
+ */
+public class Window extends AbstractView {
+
+ final JFrame frame;
+
+ /**
+ * grids height, do no represent frame size
+ */
+ public final int height = 600;
+ /**
+ * grids width, do no represent frame size
+ */
+ public final int width = 1200;
+ private final WindowMouseListener mouseComponent;
+ private final WindowKeyboardListener keyboardComponent;
+ String upperTitle = "";
+ String upperSubTitle = "";
+
+ public Window(Game game) {
+ super(game);
+ this.frame = new JFrame("Battleship");
+ frame.setSize(width + width / 13, height + height / 4);
+ frame.setResizable(false);
+ frame.setContentPane(new Draw(this));
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setVisible(true);
+ this.mouseComponent = new WindowMouseListener(this);
+ frame.addMouseListener(mouseComponent);
+ this.keyboardComponent = new WindowKeyboardListener();
+ frame.addKeyListener(keyboardComponent);
+
+ }
+
+ @Override
+ protected String getKeyInput() throws InterruptedException {
+ return keyboardInput();
+ }
+
+ /**
+ * Display a text above the grid
+ * @param s text to display
+ */
+ @Override
+ protected void setUpperText(String 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
+ public void setShips(Player player) throws InterruptedException {
+ if(player instanceof Human) {
+ for(int i : shipsSize) {
+ Ship ship = new Ship(new Pair<>(-1, -1), i, Direction.DEFAULT);
+ boolean valid = false;
+ while(!player.setShips(ship)) {
+ frame.repaint();
+ if(valid)
+ openDialog("Erreur de placement, votre navire se superpose avec un autre, ou la direction donnée n'est pas valide");
+
+ upperTitle = "joueur " + player.getId() + ", Placez votre premier navire de taille " + i + " à l'aide de la souris";
+ ship.setCoords(mouseInput(player));
+ upperTitle = "joueur " + player.getId() + ", Choisissez la direction de votre navire avec le clavier";
+ upperSubTitle = "H, B, G, D pour respectivement Haut, Bas, Gauche, Droite";
+ frame.repaint();
+ ship.setDirection(getDirectionFromChar());
+ ship.recalculateFullCoords();
+ valid = true;
+ }
+
+ }
+ upperTitle = "";
+ upperSubTitle = "";
+ } else {
+ super.setShips(player);
+ }
+ }
+
+ /**
+ * 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
+ protected String keyboardInput() throws InterruptedException {
+ keyboardComponent.requestInput = true;
+ while(true) {
+ Thread.sleep(25);
+ if(keyboardComponent.keyTyped != KeyEvent.CHAR_UNDEFINED) {
+ keyboardComponent.requestInput = false;
+ String value = String.valueOf(keyboardComponent.keyTyped).toUpperCase();
+ keyboardComponent.keyTyped = KeyEvent.CHAR_UNDEFINED;
+ return value;
+ }
+ }
+ }
+
+ /**
+ * 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
+ protected Pair mouseInput(Player player) throws InterruptedException {
+ mouseComponent.requestInput = true;
+ while(true) {
+ Thread.sleep(25);
+ if(mouseComponent.playerIdLastInput != 0) {
+ if(player.getId() == mouseComponent.playerIdLastInput) {
+ mouseComponent.requestInput = false;
+ mouseComponent.playerIdLastInput = 0;
+ Pair value = mouseComponent.lastInput;
+ mouseComponent.lastInput = null;
+ return value;
+ } else {
+ openDialog("Vous avez cliqué sur une zone de jeu qui n'est pas la votre");
+ mouseComponent.playerIdLastInput = 0;
+ }
+ }
+
+ }
+ }
+
+ /**
+ * 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) {
+ JOptionPane.showMessageDialog(frame, message);
+ if(exitOnClose)
+ 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) {
+ openDialog(message, false);
+ }
+
+ /**
+ * refresh windows to display updated content
+ */
+ @Override
+ public void displayBoard() {
+ 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
+ public Pair chooseMove(Player player) throws InterruptedException {
+ setUpperText("Joueur " + player.getId() + " cliquez sur l'emplacement où vous souhaitez tirer");
+ frame.repaint();
+ if(player instanceof Human) {
+ Pair coords = new Pair<>(-1, -1);
+ boolean valid = false;
+ while(!player.areValid(coords.getLeft(), coords.getRight())) {
+ if(valid)
+ openDialog("Erreur de placement, ce coup a déjà été effectué");
+ valid = true;
+ coords = mouseInput(game.getOtherPlayer(player));
+ }
+ return coords;
+ }
+ return super.chooseMove(player);
+
+ }
+
+ /**
+ * open a dialog to display the winner and exit the program when window is closed
+ * @param winner the winner of the game.
+ */
+ @Override
+ public void displayWinner(Player winner) {
+ openDialog("Le joueur " + winner.getId() + " a gagné", true);
+ }
+
+ /**
+ * Panel where we paint the board
+ * @see JPanel
+ */
+ class Draw extends JPanel {
+
+ private final Window window;
+
+ public Draw(Window window) {
+ this.window = window;
+ }
+
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D)g;
+ g2d.drawString(upperTitle, (int) (window.width /2 - (upperTitle.length() * 2.5)), 50);
+ g2d.drawString(upperSubTitle, (int) (window.width / 2 - (upperSubTitle.length() * 2.5)), 65);
+ int width = window.width;
+ int height = window.height;
+ int initialHeight = height / 12;
+ int initialWidth = width / 23;
+ for(int abscisse = initialWidth; abscisse < width; abscisse += initialWidth) {
+ g2d.drawLine(abscisse, initialHeight * 2, abscisse, height);
+ if(abscisse == initialWidth * 11)
+ abscisse += initialWidth;
+ }
+ for(int ordonnee = initialHeight * 2; ordonnee < height + 1; ordonnee += initialHeight) {
+ g2d.drawLine(initialWidth, ordonnee, initialWidth * 11, ordonnee);
+ g2d.drawLine(initialWidth * 13, ordonnee, width - 4, ordonnee);
+ }
+
+ for(int i = 1; i < 3; ++i) {
+ Player player = game.players[i-1];
+ for(Ship ship : player.getShips()) {
+ if(player == game.getCurrentPlayer() ||ship.isDrown()) {
+ int x1 = i == 1 ? initialWidth : initialWidth * 13;
+ int y1 = initialHeight * 2;
+ int shipWidth = initialWidth;
+ int shipHeight = initialHeight;
+ switch(ship.getDirection()) {
+ case DOWN:
+ x1 += initialWidth * ship.getCoords().getRight();
+ y1 += initialHeight * ship.getCoords().getLeft();
+ shipHeight = initialHeight * ship.getSize();
+ g.setColor(new Color(0, 255, 255));
+ break;
+ case UP:
+ x1 += initialWidth * ship.getCoords().getRight();
+ shipHeight = initialHeight * ship.getSize();
+ y1 += initialHeight * ship.getCoords().getLeft() - shipHeight + initialHeight;
+ g.setColor(new Color(255, 255, 0));
+ break;
+ case RIGHT:
+ x1 += initialWidth * ship.getCoords().getRight();
+ y1 += initialHeight * ship.getCoords().getLeft();
+ shipWidth = initialWidth * ship.getSize();
+ g.setColor(new Color(0, 255, 0));
+ break;
+ case LEFT:
+ shipWidth = initialWidth * ship.getSize();
+ x1 += initialWidth * ship.getCoords().getRight() - shipWidth + initialWidth;
+ y1 += initialHeight * ship.getCoords().getLeft();
+ g.setColor(new Color(0, 0, 255));
+ break;
+ }
+ g2d.fillRoundRect(x1 + 1, y1 + 1, shipWidth - 1, shipHeight - 1, 25, 25);
+ }
+ }
+ }
+ for(int i = 1; i < 3; ++i) {
+ Player player = game.players[i-1];
+ float rectangleSize = initialWidth / 4f;
+ int sqrt = (int) Math.sqrt(initialHeight * initialHeight + initialWidth * initialWidth) - 10;
+ for(Triplet move : player.getMoves()) {
+ int x1 = (i == 1 ? initialWidth * 13 : initialWidth) + initialWidth * move.getMiddle();
+ int y1 = initialHeight * 2 + initialHeight * move.getLeft() + 8;
+ RoundRectangle2D cross1 = new RoundRectangle2D.Float(x1, y1, rectangleSize, sqrt, 15, 15);
+ RoundRectangle2D cross2 = new RoundRectangle2D.Float(x1 + initialWidth - 9, y1 - 9, rectangleSize, sqrt, 15, 15);
+ if(move.getRight()) {
+ g.setColor(new Color(255, 0, 0));
+ } else {
+ g.setColor(new Color(0, 123, 255));
+ }
+ g2d.rotate(Math.toRadians(-45), x1, y1);
+ g2d.fill(cross1);
+ g2d.rotate(Math.toRadians(45), x1, y1);
+ g2d.rotate(Math.toRadians(45), x1 + initialWidth - 9, y1 - 9);
+ g2d.fill(cross2);
+ g2d.rotate(Math.toRadians(-45), x1 + initialWidth - 9, y1 - 9);
+ }
+ }
+ }
+ }
+}
diff --git a/livraison/src/battleship/view/WindowListener.java b/livraison/src/battleship/view/WindowListener.java
new file mode 100644
index 0000000..428082f
--- /dev/null
+++ b/livraison/src/battleship/view/WindowListener.java
@@ -0,0 +1,32 @@
+package battleship.view;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+public class WindowListener implements MouseListener {
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+
+ }
+}
diff --git a/livraison/src/battleship/view/package-info.java b/livraison/src/battleship/view/package-info.java
new file mode 100644
index 0000000..b104f2e
--- /dev/null
+++ b/livraison/src/battleship/view/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains terminal and windows view classes and theirs super class and interface
+ */
+package battleship.view;
\ No newline at end of file
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..e0f1fa4
--- /dev/null
+++ b/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: battleship.Main
+