Jeu de console OOP Battleship en Java

Ma deuxième version de ce jeu peut être trouvée ici

Je voulais faire un jeu de console simple pour pratiquer la POO. Japprécierais vraiment une critique sur la lisibilité, la maintenance et les meilleures pratiques.

Ce qui me dérange un peu avec ce code, cest que je nutilise pas dinterfaces, de classes abstraites ou dhéritage, mais je nai pas pu « Je ne trouve pas ici un bon cas dutilisation.

Board.java

package com.tn.board; import com.tn.constants.Constants; import com.tn.ship.Ship; import com.tn.utils.Position; import com.tn.utils.Utils; import java.awt.Point; import java.util.Scanner; public class Board { private static final Ship[] ships; private char[][] board; /** * Initialize ships (once). * */ static { ships = new Ship[]{ new Ship("Carrier", Constants.CARRIER_SIZE), new Ship("Battleship", Constants.BATTLESHIP_SIZE), new Ship("Cruiser", Constants.CRUISER_SIZE), new Ship("Submarine", Constants.SUBMARINE_SIZE), new Ship("Destroyer", Constants.DESTROYER_SIZE) }; } /** * Constructor */ public Board() { board = new char[Constants.BOARD_SIZE][Constants.BOARD_SIZE]; for(int i = 0; i < Constants.BOARD_SIZE; i++) { for(int j = 0; j < Constants.BOARD_SIZE; j++) { board[i][j] = Constants.BOARD_ICON; } } placeShipsOnBoard(); } /** * Target ship ship. * * @param point the point * @return ship */ public Ship targetShip(Point point) { boolean isHit = false; Ship hitShip = null; for(int i = 0; i < ships.length; i++) { Ship ship = ships[i]; if(ship.getPosition() != null) { if(Utils.isPointBetween(point, ship.getPosition())) { isHit = true; hitShip = ship; break; } } } final char result = isHit ? Constants.SHIP_IS_HIT_ICON : Constants.SHOT_MISSED_ICON; updateShipOnBoard(point, result); printBoard(); return (isHit) ? hitShip : null; } /** * Place ships on board. */ private void placeShipsOnBoard() { System.out.printf("%nAlright - Time to place out your ships%n%n"); Scanner s = new Scanner(System.in); for(int i = 0; i < ships.length; i++) { Ship ship = ships[i]; boolean isShipPlacementLegal = false; System.out.printf("%nEnter position of %s (length %d): ", ship.getName(), ship.getSize()); while(!isShipPlacementLegal) { try { Point from = new Point(s.nextInt(), s.nextInt()); Point to = new Point(s.nextInt(), s.nextInt()); while(ship.getSize() != Utils.distanceBetweenPoints(from, to)) { System.out.printf("The ship currently being placed on the board is of length: %d. Change your coordinates and try again", ship.getSize()); from = new Point(s.nextInt(), s.nextInt()); to = new Point(s.nextInt(), s.nextInt()); } Position position = new Position(from, to); if(!isPositionOccupied(position)) { drawShipOnBoard(position); ship.setPosition(position); isShipPlacementLegal = true; } else { System.out.println("A ship in that position already exists - try again"); } } catch(IndexOutOfBoundsException e) { System.out.println("Invalid coordinates - Outside board"); } } } } private void updateShipOnBoard(Point point, final char result) { int x = (int) point.getX() - 1; int y = (int) point.getY() - 1; board[y][x] = result; } /** * * @param position * @return */ private boolean isPositionOccupied(Position position) { boolean isOccupied = false; Point from = position.getFrom(); Point to = position.getTo(); outer: for(int i = (int) from.getY() - 1; i < to.getY(); i++) { for(int j = (int) from.getX() - 1; j < to.getX(); j++) { if(board[i][j] == Constants.SHIP_ICON) { isOccupied = true; break outer; } } } return isOccupied; } /** * * @param position */ private void drawShipOnBoard(Position position) { Point from = position.getFrom(); Point to = position.getTo(); for(int i = (int) from.getY() - 1; i < to.getY(); i++) { for(int j = (int) from.getX() - 1; j < to.getX(); j++) { board[i][j] = Constants.SHIP_ICON; } } printBoard(); } /** * Print board. */ private void printBoard() { System.out.print("\t"); for(int i = 0; i < Constants.BOARD_SIZE; i++) { System.out.print(Constants.BOARD_LETTERS[i] + "\t"); } System.out.println(); for(int i = 0; i < Constants.BOARD_SIZE; i++) { System.out.print((i+1) + "\t"); for(int j = 0; j < Constants.BOARD_SIZE; j++) { System.out.print(board[i][j] + "\t"); } System.out.println(); } } } 

Constants.java

package com.tn.constants; public class Constants { private Constants() {} public static final int PLAYER_LIVES = 17; //sum of all the ships public static final int CARRIER_SIZE = 5; public static final int BATTLESHIP_SIZE = 4; public static final int CRUISER_SIZE = 3; public static final int SUBMARINE_SIZE = 3; public static final int DESTROYER_SIZE = 2; public static final char SHIP_ICON = "X"; public static final char BOARD_ICON = "-"; public static final char SHIP_IS_HIT_ICON = "O"; public static final char SHOT_MISSED_ICON = "M"; public static final char[] BOARD_LETTERS = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"}; public static final int BOARD_SIZE = 10; } 

Player.java

package com.tn.player; import com.tn.board.Board; import com.tn.constants.Constants; import com.tn.ship.Ship; import java.awt.Point; import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Player { private int id; private int lives; private Board board; private Map<Point, Boolean> targetHistory; private Scanner scanner; /** * Instantiates a new Player. * * @param id the id */ public Player(int id) { System.out.printf("%n=== Setting up everything for Player %s ====", id); this.id = id; this.lives = Constants.PLAYER_LIVES; this.board = new Board(); this.targetHistory = new HashMap<>(); this.scanner = new Scanner(System.in); } /** * Gets id. * * @return the id */ public int getId() { return id; } /** * Gets lives. * * @return the lives */ public int getLives() { return lives; } /** * Decrement live by one. */ public void decrementLiveByOne() { lives--; } /** * Turn to play. * * @param opponent the opponent */ public void turnToPlay(Player opponent) { System.out.printf("%n%nPlayer %d, Choose coordinates you want to hit (x y) ", id); Point point = new Point(scanner.nextInt(), scanner.nextInt()); while(targetHistory.get(point) != null) { System.out.print("This position has already been tried"); point = new Point(scanner.nextInt(), scanner.nextInt()); } attack(point, opponent); } /** * Attack * * @param point * @param opponent */ private void attack(Point point, Player opponent) { Ship ship = opponent.board.targetShip(point); boolean isShipHit = (ship != null) ? true : false; if(isShipHit) { ship.shipWasHit(); opponent.decrementLiveByOne(); } targetHistory.put(point, isShipHit); System.out.printf("Player %d, targets (%d, %d)", id, (int)point.getX(), (int)point.getY()); System.out.println("...and " + ((isShipHit) ? "HITS!" : "misses...")); } } 

Ship.java

package com.tn.ship; import com.tn.utils.Position; public class Ship { private String name; private int size; private int livesLeft; private boolean isSunk; private Position position; public Ship(String name, int size) { this.name = name; this.size = size; this.livesLeft = size; this.isSunk = false; } public String getName() { return name; } public int getSize() { return size; } public int getLivesLeft() { return livesLeft; } public boolean isSunk() { return isSunk; } public void setSunk(boolean sunk) { isSunk = sunk; } public Position getPosition() { return position; } public void setPosition(Position position) { this.position = position; } public void shipWasHit() { if(livesLeft == 0) { isSunk = true; System.out.println("You sunk the " + name); return; } livesLeft--; } } 

Position.java

package com.tn.utils; import com.tn.constants.Constants; import java.awt.Point; public class Position { private Point from; private Point to; /** * Instantiates a new Position. * * @param from the from * @param to the to */ public Position(Point from, Point to) { if(from.getX() > Constants.BOARD_SIZE || from.getX() < 0 || from.getY() > Constants.BOARD_SIZE || from.getY() < 0 || to.getX() > Constants.BOARD_SIZE || to.getX() < 0 || to.getY() > Constants.BOARD_SIZE || to.getY() < 0) { throw new ArrayIndexOutOfBoundsException(); } this.from = from; this.to = to; } /** * Gets from. * * @return the from */ public Point getFrom() { return from; } /** * Gets to. * * @return the to */ public Point getTo() { return to; } } 

Utils.java

package com.tn.utils; import java.awt.Point; public class Utils { private Utils() { } /** * Distance between points double. * * @param from the from * @param to the to * @return the double */ public static double distanceBetweenPoints(Point from, Point to) { double x1 = from.getX(); double y1 = from.getY(); double x2 = to.getX(); double y2 = to.getY(); return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2)) + 1; } /** * Is point between boolean. * * @param point the point * @param position the position * @return the boolean */ public static boolean isPointBetween(Point point, Position position) { Point from = position.getFrom(); Point to = position.getTo(); return from.getY() <= point.getY() && to.getY() >= point.getY() && from.getX() <= point.getX() && to.getX() >= point.getX(); } } 

Game.java

package com.tn.game; import com.tn.player.Player; public class Game { private Player[] players; /** * Instantiates a new Game. */ public Game() { players = new Player[]{ new Player(1), new Player(2) }; } /** * Start. */ public void start() { int i = 0; int j = 1; int size = players.length; Player player = null; while(players[0].getLives() > 0 && players[1].getLives() > 0) { players[i++ % size].turnToPlay(players[j++ % size]); player = (players[0].getLives() < players[1].getLives()) ? players[1] : players[0]; } System.out.printf("Congrats Player %d, you won!",player.getId()); } } 

Mai n.java

package com.tn; import com.tn.game.Game; public class Main { public static void main(String[] args) { Game game = new Game(); game.start(); } } 

Commentaires

  • Je ‘ ne vais pas écrire une critique complète ici (je ‘ ne suis pas du tout un expert Java). Mais peut-être serez-vous intéressé par mon dernier point pour répondre à cette question .
  • @ πάνταῥεῖ Que ‘ est quelques bons points, merci. Je ‘ ne pense pas que jétais très loin de vos suggestions.
  • Je ne pense pas non plus, que ‘ s pourquoi je lai indiqué ici.
  • Votre code est assez bon, les réponses soulignent les choses que je ‘ d souligner, aussi, sauf une chose: Il ny a ‘ aucun cas de test.

Réponse

Merci pour partager votre code.

Ce qui me dérange un peu avec ce code, cest que je nutilise pas dinterfaces, de classes abstraites ou dhéritage,

Faire de la POO signifie que vous suivez certains principes qui sont (entre autres):

  • masquage / encapsulation dinformations
  • responsabilité unique
  • séparation des préoccupations
  • KISS (Restez simple (et) stupide.)
  • DRY (Ne vous répétez pas.)
  • « Dites! Ne demandez pas. »
  • Loi de déméter (« Ne parlez pas à des inconnus! »)

Interfaces, abs les classes de tractus ou les principes du chapeau de support dhéritage et doivent être utilisés si nécessaire. Ils ne « définissent » pas la POO.

À mon humble avis, la principale raison pour laquelle votre approche échoue OOP est que votre « modèle » est un tableau de type primitif char. Cela conduit finalement à une approche procédurale pour la logique du jeu.

Je penserais à une interface comme celle-ci:

interface GameField{ char getIcon(); Result shootAt(); } 

Result serait une énumération:

 enum Result{ NO_HIT, PARTIAL_HIT, DESTROYED } 

Et jaurais différentes implémentations de linterface:

public class BorderField implements GameField{ private final char borderName; public BorderField(char borderName){ this.borderName = borderName; } @Override public char getIcon(){ return borderName; } @Override public Result shootAt(){ return Result.NO_HIT; } } 

public class WaterField implements GameField{ private boolean isThisFieldHit = false; @Override public char getIcon(){ return isThisFieldHit?"M": " "; } @Override public Result shootAt(){ return Result.NO_HIT; } } 

public class ShipField implements GameField{ private final Ship ship; private boolean isThisFieldHit = false; public ShipField(Ship ship){ this.ship = ship; } @Override public char getIcon(){ Result shipState = ship.getState(); switch(shipState){ case NO_HIT: return " "; case PARTIAL_HIT: return isThisFieldHit?"O":" "; case DESTROYED: return "#"; } @Override public Result shootAt(){ ship.hit(); return ship.getState(); } } 

Cela devrait suffire, jespère que vous Obtenez lidée …


Problèmes formels

Dénomination

Trouver de bons noms est la partie la plus difficile de la programmation. Prenez donc toujours votre temps pour réfléchir à vos noms didentifiant.

Du bon côté, vous suivez les conventions de dénomination Java.

Mais vous devriez faire commencer les noms de méthodes par un verbe dans son le présent.Eg: shipWasHit() doit être nommé hit().
Ou distanceBetweenPoints() doit être calculateDistanceBetween(). Ici, les paramètres révèlent que la distance est entre les points, donc pas besoin de mettre cela dans le nom de la méthode.

Soyez verbeux dans vos noms de variables. au lieu de

 double x1 = from.getX(); double y1 = from.getY(); double x2 = to.getX(); double y2 = to.getY(); 

ces variables devraient plutôt être nommé comme ceci:

 double startPointX = from.getX(); double startPointY = from.getY(); double endPointX = to.getX(); double endPointY = to.getY(); 

Prenez vos noms du domaine du problème, pas de la solution technique. par exemple: SHIP_ICON doit être SHIP uniquement sauf si vous avez une autre constante dans la classe Ship .

Commentaires

Les commentaires doivent expliquer pourquoi le code est comme il est . Supprimez tous les autres commentaires.

Les commentaires ne doivent être utilisés que sur les méthodes dinterface ou abstraites où ils contiennent le contrat que limplémenteur doit remplir.

Classe de constantes

Rassemblez les choses qui vont ensemble. Définissez les constantes dans la classe qui les utilise.

Commentaires

  • Tous les points valides, et vraiment utiles. Merci!

Réponse

Il y a déjà de bonnes réponses mais je pensais « ajouter quelques-unes des choses qui se tenaient pour moi quand jai parcouru le code.

Pour le moment, la seule source dentrée est lentrée utilisateur dun scanner. Cela rendrait la tâche assez difficile si vous vouliez ajouter une sorte dadversaires informatiques à jouer contre.

Il semble quil y ait du code dans la classe Board qui pourrait être mieux adapté dans la classe Player.

Plus précisément le contenu de la méthode placeShipsOnBoard ().

Cette méthode prend les entrées de lutilisateur et crée une position. Essayons de la restructurer afin quun joueur (humain ou ordinateur) puisse créer une position.

Faisons un interface

public interface Player { Position placeNextShip(); void fireAt(Position target); } 

Nous avons déjà des implémentations dun humain,

public class HumanPlayer implements Player { // variables @Override public Position placeNextShip(){ // uses Scanner instance to create a Position } @Override public void fireAt(Position target){ // code from your attack method } } 

Et quen est-il un lecteur informatique de base

public class DumbComputer implements Player { @Override public Position placeNextShip(){ // keep choosing random locations } @Override public void fireAt(Position target){ // keep firing at random positions } } 

Ensuite, dans la classe principale Board, nous programmeons sur linterface Player

while(!isShipPlacementLegal){ for(Player player : players){ // some list of players in the game // either scanner input or randomly generated Position Position shipPlacement = player.placeNextShip(); boolean validPosition = validatePos(shipPlacement); if(validPostion){ // good to go! Place ship and continue to next player } else { // prompt again, whatever else you need to do here. } } } 

Si tout le code se réfère à un Player (maintenant une interface) plutôt quà une implémentation concrète, nous pouvons facilement ajouter de nouveaux types de lecteurs informatiques . par exemple. nouveau CheatingComputer (), nouveau HardComputer (), nouveau MediumComputer (). Chacun déterminerait simplement où tirer ensuite et où placer le prochain vaisseau dune manière différente.

Et, si nous avions cela, nous pourrions créer un jeu avec 2 joueurs sur ordinateur et le laisser jouer tout seul! Excitant droit: D

Une autre chose connexe que nous pourrions changer est que dans votre constructeur pour Game, vous aurez toujours 2 joueurs humains. Nous pourrions faire en sorte que cela prenne une liste de joueurs, afin que vous puissiez avoir nimporte quel nombre de joueurs dans votre partie. Ce nest pas parce que le vrai jeu Battleship est limité à 2 joueurs que le vôtre doit lêtre.

Nous pourrions permettre une taille de grille ajustable et un nombre de joueurs arbitraire. Soudain, nous avons une grille de 100×100 avec un 8 joueur gratuit pour tous!

(dont nimporte quelle quantité peut être contrôlée par ordinateur).

Vos vaisseaux sont également initialisés dans un bloc statique de votre classe de plateau. Vous avez tous les vrais vaisseaux Mais encore une fois, pourquoi ne pas permettre plus de flexibilité ici. Et si votre vaisseau se composait dune liste de points? Vous pourriez avoir des navires en forme de S, ils nauraient pas besoin dêtre limités à lalignement horizontal ou vertical. (Cest peut-être exagéré, mais je pense que cest « une chose sympa à penser!)

Je terminerai avec quelques petites choses qui me paraissaient amusantes

throw new ArrayIndexOutOfBoundsException(); 

de la classe Position. Cela semble être une exception inappropriée à lancer ici. Il ny a pas de tableau dans la classe Position, donc cela doit faire référence au tableau de la classe Board. Je pense quun type Exception plus approprié serait une IllegalArgumentException, ArrayIndexOutofBoundsException est un détail dimplémentation (dune classe différente!). Vous devriez assurez-vous également de fournir un message derreur approprié avec le lancement de lexception. Par exemple, « la valeur doit être comprise entre x et y »

La ligne

boolean isShipHit = (ship != null) ? true : false; 

pourrait simplement être remplacé par

boolean isShipHit = ship != null; 

Il ny a pas besoin de lopérateur ternaire ici.

Lutilisation de targetHistory dans la classe Player while (targetHistory.get (point)! = null)

Ici, vous utilisez une carte dans le seul but de voir si un élément sy trouve. Cest exactement ce quest un Set pour!

targetHistory = new HashSet<>(); while(targetHistory.contains(point)){ // re-prompt } 

Commentaires

  • Merci beaucoup pour la perspicacité! Toutes ces réponses complètent chacune autre très bien! Je ‘ Je vais commencer à travailler sur la version 2.0 avec tout cela à lesprit.
  • Pas de problème Je ‘ je suis content que vous layez trouvé utile! Bonne chance avec la version 2.0!

Réponse

Quest-ce qui agace moi un peu avec ce code est que je nutilise pas dinterfaces, de classes abstraites ou dhéritage, mais je nai pas pu trouver un bon cas dutilisation pour eux ici.

Il ny a rien de mal à cela dans votre jeu. Le concept du jeu est si simple que vous nen avez pas besoin. Le problème avec les petits programmes de jouets est que vous navez généralement pas besoin dappliquer de grandes solutions de conception. Il vous suffit de vous assurer de suivre les principes de conception SOLID habituels.


Maintenant que nous avons clarifié cela, laissez « s regardez quelques détails de votre code qui devraient être améliorés.

Le premier est assez évident. Nécrivez pas de commentaires pour le plaisir décrire des commentaires. Certains professeurs aiment vous forcer à écrire un commentaire javadoc sur tout. Je suis en fait complètement contre cela, sauf lorsque jécris une sorte de package utilitaire que tout le monde doit utiliser. Normalement, le code devrait être auto-documenté. Et votre code fait un très bon travail à cela. Donc supprimez simplement le commentaire qui « s répétant essentiellement le nom bien choisi de la variable / fonction / …

Par exemple:

/** * Is point between boolean. * * @param point the point * @param position the position * @return the boolean */ public static boolean isPointBetween(Point point, Position position) { 

Quelle valeur ce commentaire ajoute à la fonction?Je pense que cest aussi la seule méthode que je changerais pour la rendre plus lisible. Car il « nest pas évident que la position soit constituée dun point from et to pour lequel nous vérifions si notre point se situe entre eux.

Et si la méthode avait cette signature:

public static boolean containsPoint(Position position, Point point) { 

Ne serait-ce pas Cela a un peu plus de sens?

Je devrais ajouter ici que je ne suis pas contre les commentaires en général. Mais un commentaire devrait expliquer POURQUOI quelque chose est fait de cette façon. Pas comment cela est implémenté. Si je le voulais sachez comment il est implémenté, je viens de regarder le code.


Le point suivant est davoir cette classe Util … Contrairement à certains puristes, je nai rien contre le concept des classes Util . Les classes Util peuvent être utiles pour rassembler des fonctions similaires. Par exemple java.lang.Math qui regroupe toutes les opérations arithmétiques habituelles dans une seule classe.

Ce qui me dérange avec votre classe Util, cest quil ny a pas vraiment une bonne raison pour quelle existe. Les 2 fonctions que vous avez là ne sont utilisées que dans la classe Board. Ils pourraient donc « avoir tout aussi bien été private static des fonctions daide à lintérieur de cette classe.

En fait, nous pouvons même faire un peu mieux après avoir changé la signature en quoi Jai suggéré plus tôt. Et si nous mettions containsPoint(Position position, Point point) { dans la classe Position au lieu davoir la Position comme paramètre de la méthode? Ensuite, nous pouvons lutiliser comme ceci:

Position shipPosition = //some ship"s position if(shipPosition.contains(targetPoint)) { //handle ship hit } 

Il sintègre vraiment bien là, nest-ce pas?


En parlant de la classe Positions. Jai eu un sentiment étrange à ce sujet en regardant votre code. Au début, je pensais que vous naviez pas de something[][] pour représenter le tableau. Je pensais que vous aviez tout représenté sous forme de points dans toute la base de code. Cela pourrait fonctionner mais rend limpression du tableau difficile. Et puis jai remarqué quil y avait un char[][] dans votre classe Board. Mais alors cela naurait pas plus de sens de placer immédiatement les vaisseaux à lintérieur cette grille sans avoir de classe Position intermédiaire?

Jai également remarqué une autre chose dangereuse à propos du placeShipsOnBoard(). Devriez-vous vraiment faire confiance à votre utilisateur pour entrer 2 coordonnées valides? Que faire si lutilisateur essaie dêtre drôle et saisit de = (1,1) à = (2,2)? Devrions-nous permettre cela? Ou que se passe-t-il si vous voulez quil entre un navire de longueur 5 et quil entre = (1,1) à = (1,1) réduisant essentiellement le navire à une seule case (que vous devez frapper 5 fois! Depuis le navire a 5 vies). Ne devrions-nous pas lempêcher de tricher comme ça?

Regardons une autre façon de gérer le placement des vaisseaux. Tout dabord, laissez lutilisateur choisir sil souhaite placer le navire horizontalement ou verticalement. Puis demandez-lui de saisir les coordonnées haut / gauche du navire. Nous calculerons nous-mêmes les points restants.

Voici à quoi pourrait ressembler limplémentation réelle de la méthode:

private Scanner scanner = new Scanner(System.in); private void placeShipsOnBoard() { System.out.printf("%nAlright - Time to place out your ships%n%n"); for(Ship ship : ships) { //awesome for-each loop is better here boolean horizontal = askValidShipDirection(); Point startingPoint = askValidStartingPoint(ship, horizontal); placeValidShip(ship, startingPoint, boolean horizontal); } } private boolean askValidShipDirection() { do { System.out.println("Do you want to place the ship horizontally (H) or vertically (V)?"); String direction = Scanner.nextLine().trim(); while ( !"H".equals(direction) && !"V".equals(direction); return "H".equals(direction); //note here: use "constant".equals(variable) so nullpointer is impossible. //probably not needed, but it"s best practice in general. } private Point askValidStartingPoint(Ship ship, boolean horizontal) { do { //note: do-while more useful here System.out.printf("%nEnter position of %s (length %d): ", ship.getName(), ship.getSize()); Point from = new Point(scanner.nextInt(), scanner.nextInt()); while(!isValidStartingPoint(from, ship.getLength(), horizontal)); return from; } private boolean isValidStartingPoint(Point from, int length, boolean horizontal) { int xDiff = 0; int yDiff = 0; if(horizontal) { xDiff = 1; } else { yDiff = 1; } for(int i = 0; i < lenth; i++) { if(!isInsideBoard(i,from.getY()) { return false; } if(!Constants.BOARD_ICON.equals(board[from.getY()+i*yDiff][from.getX()+i*xDiff])){ return false; } } return true; } private boolean isInsideBoard(int x, int y){ return x <= Constants.BOARD_SIZE && x >= 0 && y <= Constants.BOARD_SIZE && y >= 0 && x <= Constants.BOARD_SIZE && x >= 0 && y <= Constants.BOARD_SIZE && y >= 0; } private void placeValidShip(Ship ship, Point startingPoint, boolean horizontal) { int xDiff = 0; int yDiff = 0; if(horizontal) { xDiff = 1; } else { yDiff = 1; } for(int i = 0; i < ship.getLenth() ; i++) { board[ship.getY() + i*yDiff][ship.getX()+i*xDiff] = Constants.SHIP_ICON; } } 

Maintenant que nous venons de placez les vaisseaux directement sur le plateau, nous pouvons supprimer la classe Position et toutes ses références. Cela signifie que nous ne savons plus si un navire a coulé ou non.

En yping, jai remarqué que @Timothy Truckle avait déjà publié une réponse. Jadore sa solution consistant à utiliser des Field s au lieu de char « s pour représenter le tableau.

donc notre méthode de vaisseau de lieu change en:

private void placeValidShip(Ship ship, Point startingPoint, boolean horizontal) { int xDiff = 0; int yDiff = 0; if(horizontal) { xDiff = 1; } else { yDiff = 1; } for(int i = 0; i < ship.getLenth() ; i++) { board[ship.getY() + i*yDiff][ship.getX()+i*xDiff] = new ShipField(ship); } } 

De cette façon, nous pouvons vérifier si un navire est complètement détruit ou sil est simplement touché partiellement lors de lattaque dun Field.

Maintenant, au lieu de continuer sur cette réponse, je vous suggère de lire également les @Timothy » s, et de regarder les bons points dans nos deux réponses. À première vue, nous sommes soit daccord, soit nous complétons simplement la réponse de lautre. Vous devriez donc avoir des conseils décents sur la façon daméliorer votre code 🙂

Commentaires

  • Merci beaucoup pour cet examen détaillé! Vous avez tous fait valoir des points solides. Comme toutes les réponses étaient très bonnes, je ‘ coche la personne qui a répondu dabord.
  • ahem  » Il ny a rien de mal à cela dans votre jeu. Le concept du jeu est si simple que vous ne ‘ Jai besoin de lun de ces éléments.  » – Je ne suis pas daccord: il vaut mieux ‘ sentraîner tout en mode facile d’abord. Si vous pouvez ‘ t appliquer des concepts et des modèles à quelque chose de simple, vous pouvez certainement ‘ t les appliquer sur quelque chose de plus complexe.
  • La partie sur les conments est particulièrement importante. Les commentaires redondants sont une perte totale de temps et dénergie à écrire ite, entretenez et vérifiez. Ne commentez que si vous ne pouvez pas obtenir le code et les fonctions pour sexpliquer.

Réponse

Dautres réponses ont presque tout souligné, alors jajouterai juste une chose. pour moi que vous utilisez des exceptions pour effectuer en quelque sorte le contrôle de flux. Les exceptions ne sont pas des mécanismes de flux de contrôle .

public Position(Point from, Point to) { if (from.getX() > Constants.BOARD_SIZE || from.getX() < 0 || from.getY() > Constants.BOARD_SIZE || from.getY() < 0 || to.getX() > Constants.BOARD_SIZE || to.getX() < 0 || to.getY() > Constants.BOARD_SIZE || to.getY() < 0) { throw new ArrayIndexOutOfBoundsException(); } this.from = from; this.to = to; } 

Et puis

} catch (IndexOutOfBoundsException e) { System.out.println("Invalid coordinates - Outside board"); } 

Je pense que vous devriez valider que les coordonnées données sont à lintérieur du tableau avant dessayer de créer le Position. Cest une entrée de lutilisateur et il est parfaitement raisonnable de le faire. Vous « êtes déjà en train de valider cet ship.getSize() != Utils.distanceBetweenPoints(from, to). Vous le feriez même dans lobjet Board lui-même, au lieu de vérifier la position pour Constants.BOARD_SIZE.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *