OOP Battleship-konsolspel i Java

Min andra uppfattning om detta kan hittas här

Jag ville skapa ett enkelt konsolspel för att träna OOP. Jag skulle verkligen uppskatta en recension som tittar på läsbarhet, underhåll och bästa praxis.

Det som irriterar mig lite med den här koden är att jag inte använder gränssnitt, abstrakta klasser eller arv, men jag kunde inte ”t hittar ett bra användningsfall för dem här.

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(); } } } 

Konstanter.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(); } } 

Kommentarer

  • Jag ’ ska inte skriva en fullständig recension här (jag ’ är inte alls en Java-expert). Men kanske är du intresserad av min sista punkt i att svara på den här frågan .
  • @ πάνταῥεῖ Att ’ några bra poäng, tack. Jag tror inte ’ att jag var väldigt långt ifrån dina förslag.
  • Jag tror inte heller att ’ s varför jag pekade där.
  • Din kod är ganska bra, svaren pekar på de saker jag ’ d påpekade också förutom en sak: Det finns ’ inga testfall.

Svar

Tack för att dela din kod.

Det som irriterar mig lite med den här koden är att jag inte använder gränssnitt, abstrakta klasser eller arv,

Att göra OOP innebär att du följer vissa principer som är (bland andra):

  • information döljer / inkapslar
  • enskilt ansvar
  • åtskillnad mellan oro
  • KISS (Håll det enkelt (och) dumt.)
  • TORR (upprepa inte dig själv.) li>
  • ”Berätta! Fråga inte.”
  • Demeterlag (”Prata inte med främlingar!”)

Gränssnitt, abs traktklasser eller principer för arvstöd och bör användas efter behov. De ”definierar” inte OOP.

IMHO den främsta anledningen till att ditt tillvägagångssätt misslyckas OOP är att din ”modell” är en matris av primitiv typ char. Detta leder i slutändan till en procedurell strategi för spellogiken.

Jag skulle tänka på ett sådant gränssnitt:

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

där Result skulle vara ett antal:

 enum Result{ NO_HIT, PARTIAL_HIT, DESTROYED } 

Och jag skulle ha olika implementeringar av gränssnittet:

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(); } } 

Detta borde vara tillräckligt, hoppas du få idén …


Formella frågor

Namngivning

Att hitta bra namn är den svåraste delen i programmeringen. Så ta dig alltid tid att tänka på dina identifieringsnamn.

På den ljusa sidan följer du Java-namngivningskonventionerna.

Men du bör ha dina metodnamn att börja med ett verb i nutid.Eg: shipWasHit() ska heta hit().
Eller distanceBetweenPoints() borde vara calculateDistanceBetween(). Här avslöjar parametrarna att avståndet är mellan punkter, så inget behov av att sätta det i metodnamnet.

Var noggrann i dina variabelnamn. istället för

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

bör dessa variabler snarare vara heter så här:

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

Ta dina namn från problemdomänen, inte från den tekniska lösningen. t.ex.: SHIP_ICON bör endast vara SHIP om du inte har en annan konstant inom klassen Ship .

Kommentarer

Kommentarer ska förklara varför koden är som den är . Ta bort alla andra kommentarer.

kommentarer ska endast användas på gränssnitt eller abstrakta metoder där de innehåller det kontrakt som implementeraren måste uppfylla.

Konstanterklass

Sätt ihop saker som hör ihop. Definiera konstanter i klassen som använder dem.

Kommentarer

  • Alla giltiga punkter och verkligen hjälpsamma. Tack!

Svar

Det finns redan några bra svar men jag trodde att jag skulle lägga till några av de saker som stod när jag tittade igenom koden.

För tillfället är den enda ingångskällan användarinmatning från en skanner. Det skulle göra det ganska svårt om du vill lägga till någon slags datormotståndare till spela mot.

Det ser ut som om det finns någon kod i Board-klassen som kanske passar bättre i Player-klassen.

Specifikt innehållet i metoden placeShipsOnBoard ().

Denna metod tar inmatning från användaren och skapar en position. Låt oss försöka omstrukturera den så att en spelare (mänsklig eller dator) kan skapa en position.

Låt oss göra en gränssnitt

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

Vi har redan implementeringar från en människa,

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 } } 

Och vad sägs om en grundläggande datorspelare

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

Sedan, i huvudkortklassen, programmerar vi till spelargränssnittet

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. } } } 

Om all kod hänvisar till en spelare (nu ett gränssnitt) snarare än en konkret implementering kan vi enkelt lägga till nya typer av datorspelare . t.ex. ny CheatingComputer (), ny HardComputer (), ny MediumComputer (). Var och en skulle bara bestämma var de ska skjuta nästa och var de ska placera nästa skepp på ett annat sätt.

Och om vi hade det här kunde vi skapa ett spel med två datorspelare och låta det spela själv! Spännande rätt: D

En annan relaterad sak som vi kan ändra är att du i din konstruktör för spel alltid kommer att ha två mänskliga spelare. Vi kan få detta att ta en lista över spelare, så att du kan ha valfritt antal spelare i ditt spel. Bara för att det riktiga spelet Battleship är begränsat till två spelare betyder inte det att ditt måste vara.

Vi kan möjliggöra justerbar rutstorlek och ett godtyckligt spelarnummer. Plötsligt har vi ett 100×100 rutnät med ett 8 spelare gratis för alla!

(vilken mängd som helst som kan datorstyras).

Dina fartyg initialiseras också i ett statiskt block i din brädklass. Du har alla riktiga fartyg från Battleship. Men igen, varför inte tillåta mer flexibilitet här. Vad händer om ditt fartyg bestod av en lista med punkter? Du kan ha S-formade fartyg, de behöver inte begränsas till horisontell vertikal inriktning. (Det här kan vara överst, men jag tycker att det är en cool sak att tänka på!)

Jag slutar med några små saker som såg roliga ut för mig

throw new ArrayIndexOutOfBoundsException(); 

från positionsklassen. Detta verkar som ett olämpligt undantag att kasta här. Det finns ingen matris i positionsklassen, så det här måste syfta på matrisen i styrelseklassen. Jag tror att en mer lämplig undantagstyp skulle vara en IllegalArgumentException, ArrayIndexOutofBoundsException är en implementeringsdetalj (av en annan klass!). Se också till att ge ett lämpligt felmeddelande tillsammans med att kasta undantaget. t.ex. ”värde måste vara inom intervallet x och y”

Raden

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

kan helt enkelt ersättas med

boolean isShipHit = ship != null; 

Det finns inget behov av den ternära operatören här.

Användningen av targetHistory i Player-klassen medan (targetHistory.get (point)! = null)

Här använder du en karta för det enda syftet att se om ett element finns i det. Det är precis vad en uppsättning är för!

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

Kommentarer

  • Tack så mycket för insikten! Alla dessa svar kompletterar var och en andra riktigt bra! Jag ’ Jag börjar arbeta med version 2.0 med allt detta i åtanke.
  • Inget problem Jag ’ är glad att du tyckte att det var användbart! Lycka till med 2.0!

Svar

Vad irriterar mig lite med den här koden är att jag inte använder gränssnitt, abstrakta klasser eller arv, men jag kunde inte hitta ett bra användningsfall för dem här.

Det är inget fel med detta i ditt spel. Spelkonceptet är så enkelt att du inte behöver något av det. Problemet med små leksaksprogram är att du vanligtvis inte behöver använda stora designlösningar. Du behöver bara se till att du följer de vanliga SOLID-designprinciperna .


Nu när vi fick det rensat låt oss titta på några detaljer i din kod som bör förbättras.

Den första är ganska uppenbar. Skriv inte kommentarer för att skriva kommentarer. Vissa lärare gillar att tvinga dig att skriva en javadoc-kommentar till allt. Jag är faktiskt helt emot detta, förutom när jag skriver något slags verktygspaket som alla andra måste använda. Normalt sett borde koden vara självdokumenterande. Och din kod gör ett riktigt bra jobb med det. Så ta bara bort kommentaren som ”s i princip upprepa det väl valda namnet på variabeln / funktionen / …

Till exempel:

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

Vilket värde lägger den kommentaren till till funktionen?Jag tror att detta också är den enda metoden som jag skulle ändra för att göra den mer läsbar. Eftersom det inte är uppenbart att positionen består av en from och to punkt för vilken vi kontrollerar om vår point ligger mellan dem.

Vad händer om metoden hade den här signaturen:

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

Skulle inte det göra lite mer meningsfullt?

Jag borde lägga till här att jag inte är emot kommentarer i allmänhet men. Men en kommentar ska förklara varför något görs på det sättet. Inte hur det implementeras. Om jag ville vet hur den har implementerats skulle jag bara ha tittat på koden.


Nästa punkt är att ha den Util-klassen … Till skillnad från vissa purister har jag inget emot begreppet Util-klasser Util-klasser kan vara användbara för att sätta ihop liknande funktioner. Till exempel java.lang.Math som grupperar alla vanliga aritmetiska operationer i en klass.

Det som stör mig med din Util-klass är att det inte finns någon bra anledning för att den ska existera. De två funktionerna som du har där inne används bara någonsin inom styrelseklassen. Så de kunde ”lika gärna ha varit private static hjälparfunktioner i den klassen.

Vi kan till och med göra lite bättre efter att ha ändrat signaturen till vad Jag föreslog tidigare. Vad händer om vi lägger containsPoint(Position position, Point point) { inuti Position -klassen istället för att ha Position som parameter för metoden? Då kan vi använda den så här:

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

Det passar riktigt bra där, eller hur?


På tal om klassen Positions. Jag hade en konstig känsla av detta när jag tittade igenom din kod. Först trodde jag att du inte hade en something[][] för att representera tavlan. Jag trodde att du hade representerat allt som poäng under hela kodbasen. Och sedan märkte jag att jag hade en char[][] inuti din Board -klass. Men skulle det inte vara vettigare att omedelbart sätta fartygen inuti det rutnät utan att ha en mellanliggande positionsklass?

Jag märkte också en annan farlig sak med placeShipsOnBoard(). Ska du verkligen lita på att din användare anger 2 giltiga koordinater? Vad händer om användaren försöker vara rolig och matar in från = (1,1) till = (2,2)? Ska vi tillåta detta? Eller tänk om du vill att han ska mata in ett fartyg med längd 5 och han matar in från = (1,1) till = (1,1), vilket i huvudsak krymper fartyget till en enda kvadrat (som du måste slå 5 gånger! har 5 liv). Ska vi inte hindra honom från att fuska så här?

Låt oss titta på ett alternativt sätt att hantera placeringen av fartygen. Låt först användaren välja om han vill placera fartyget horisontellt eller vertikalt. Låt honom sedan mata in fartygets övre / vänstra koordinat. Vi kommer att beräkna de återstående poängen själva.

Här är hur den faktiska metodimplementeringen kan se ut:

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; } } 

Nu när vi bara placera fartygen direkt på tavlan så kan vi ta bort Position -klassen och alla dess referenser. Det betyder att vi inte längre vet om ett fartyg har sjunkit eller inte.

Medan jag ypade detta märkte jag att @Timothy Truckle redan publicerade ett svar också. Jag älskar verkligen hans lösning att använda dedikerade Field s istället för char ”s för att representera styrelsen.

så vår platsfartygsmetod ändras till:

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); } } 

På det sättet kan vi kontrollera om ett fartyg förstörs helt eller bara träffas delvis när vi attackerar en Field.

Nu istället för att fortsätta med det här svaret föreslår jag att du läser @Timothy också, och tittar på de bra poängen i båda våra svarare. Vid första anblicken är vi antingen överens om eller helt enkelt komplettera varandras svar. Så du borde ha några anständiga tips om hur du kan förbättra din kod 🙂

Kommentarer

  • Tack så mycket för en detaljerad granskning! Alla gör solida poäng. Eftersom alla svaren var så bra,

ger jag bocken till personen som svarade först.

  • ahem ” Det är inget fel med detta i ditt spel. Spelkonceptet är så enkelt att du inte ’ t behöver någon av dem. ” – Jag måste inte hålla med: Det är ’ bättre att träna vad som helst i lättläget först. Om du kan ’ t tillämpa begrepp och mönster på något enkelt, kan du säkert ’ t tillämpa dem på något mer komplicerat.
  • Det är särskilt viktigt med delarna om conment. Redundanta kommentarer är ett helt slöseri med tid och energi för att det, underhålla och verifiera. Kommentera bara om du inte kan få koden och funktionerna för att förklara sig själva.
  • Svar

    Andra svar har påpekat nästan allt, så jag lägger bara till en sak. Det ser ut för mig att du använder undantag för att på något sätt utföra flödeskontroll. Undantag är inte kontrollflödesmekanismer .

    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; } 

    Och sedan

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

    Jag tycker att du bör verifiera att de givna koordinaterna finns i styrelsen innan du försöker skapa Position. Detta är användarinmatning och det är helt rimligt att göra det. Du validerar redan det ship.getSize() != Utils.distanceBetweenPoints(from, to). Du skulle till och med göra det i själva Board istället för att sedan ha positionskontroll för Constants.BOARD_SIZE.

    Lämna ett svar

    Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *