├── .gitignore ├── src └── main │ └── java │ ├── module-info.java │ └── org │ └── peut │ ├── Main.java │ ├── App.java │ ├── Hokje.java │ ├── Ship.java │ └── Board.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /target -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.peut { 2 | requires javafx.controls; 3 | exports org.peut; 4 | } -------------------------------------------------------------------------------- /src/main/java/org/peut/Main.java: -------------------------------------------------------------------------------- 1 | package org.peut; 2 | 3 | // Glue function for the fat jar file 4 | public class Main { 5 | public static void main(String[] args) { 6 | App.main(args); 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/java/org/peut/App.java: -------------------------------------------------------------------------------- 1 | package org.peut; 2 | 3 | import javafx.application.Application; 4 | import javafx.stage.Stage; 5 | 6 | 7 | /** 8 | * JavaFX App 9 | */ 10 | public class App extends Application { 11 | 12 | @Override 13 | public void start(Stage primaryStage){ 14 | 15 | primaryStage.setTitle("Zee"); 16 | primaryStage.setResizable(false); 17 | 18 | Board board = new Board(primaryStage); 19 | 20 | } 21 | 22 | public static void main(String[] args) { 23 | launch(); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.peut 5 | zee 6 | 1.0-SNAPSHOT 7 | 8 | UTF-8 9 | 11 10 | 11 11 | 12 | 13 | 14 | org.openjfx 15 | javafx-controls 16 | 15.0.1 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.8.0 25 | 26 | 11 27 | 28 | 29 | 30 | org.openjfx 31 | javafx-maven-plugin 32 | 0.0.4 33 | 34 | org.peut.App 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-assembly-plugin 41 | 3.2.0 42 | 43 | 44 | 45 | 46 | true 47 | org.peut.Main 48 | 49 | 50 | 51 | jar-with-dependencies 52 | 53 | 54 | 55 | 56 | 57 | make-assembly 58 | package 59 | 60 | single 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/org/peut/Hokje.java: -------------------------------------------------------------------------------- 1 | package org.peut; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.event.EventHandler; 5 | import javafx.scene.Group; 6 | import javafx.scene.control.Button; 7 | 8 | 9 | public class Hokje extends Button { 10 | //length and width of each square 11 | public static final int SIDE = 60; 12 | // class wide counts to determine position of a new square 13 | private static int xcount = 0; 14 | private static int ycount = 0; 15 | 16 | public static void resetXYcount() { 17 | xcount = 0; 18 | ycount = 0; 19 | } 20 | 21 | // is there a ship in this square? 22 | 23 | private boolean occupied = false; 24 | // reference to the ship if so 25 | private Ship ship; 26 | //back reference to the board 27 | private Board board; 28 | private int index; 29 | 30 | 31 | Hokje(Board board) { 32 | this.board = board; 33 | 34 | //lamba function as per the discussion on 35 | // https://stackoverflow.com/questions/25409044/javafx-multiple-buttons-to-same-handler 36 | 37 | this.setOnAction(e -> { 38 | Hokje h = (Hokje) e.getSource(); 39 | h.checkSquare(); 40 | }); 41 | 42 | 43 | this.place(); 44 | } 45 | 46 | private void place() { 47 | 48 | this.setLayoutX(xcount * SIDE); 49 | this.setLayoutY(ycount * SIDE); 50 | 51 | this.setMinSize(SIDE, SIDE); 52 | this.setMaxSize(SIDE, SIDE); 53 | this.setPrefSize(SIDE, SIDE); 54 | 55 | if (((xcount + (ycount + 1) & 1) == 1)) { 56 | this.setStyle("-fx-background-color: lightblue"); 57 | } else { 58 | this.setStyle("-fx-background-color: skyblue"); 59 | } 60 | 61 | 62 | this.index = xcount + (ycount * Board.BOARDSIDE); 63 | 64 | ++xcount; 65 | if (xcount >= Board.BOARDSIDE) { 66 | ++ycount; 67 | xcount = 0; 68 | } 69 | 70 | } 71 | 72 | 73 | public boolean isOccupied() { 74 | return occupied; 75 | } 76 | 77 | public void setOccupied(boolean occupied) { 78 | this.occupied = occupied; 79 | } 80 | 81 | public Ship getShip() { 82 | return ship; 83 | } 84 | 85 | public void setShip(Ship ship) { 86 | this.ship = ship; 87 | } 88 | 89 | public int getIndex() { 90 | return index; 91 | } 92 | 93 | public void setIndex(int index) { 94 | this.index = index; 95 | } 96 | 97 | public void show() { 98 | System.out.println("- Hokje " + index); 99 | } 100 | 101 | private void checkSquare() { 102 | 103 | board.setGrenades(board.getGrenades() - 1); 104 | if (board.getGrenades() <= 0) { 105 | int shipsLeft = board.getShipsLeft(); 106 | board.setStageTitle("No more grenades. You lost."); 107 | board.gameOver(); 108 | return; 109 | } 110 | 111 | if (isOccupied()) { 112 | 113 | if (ship.isDead()) { 114 | board.setStageTitle("Shooting wreckage is counter productive"); 115 | } else { 116 | if (ship.isDamaged(this)) { 117 | board.setStageTitle("Ship #" + ship.getNumber() + " already damaged here"); 118 | } else { 119 | board.setStageTitle("Hit!"); 120 | 121 | this.setStyle("-fx-background-color: gray"); 122 | 123 | if (ship.hit(this)) { 124 | for (Hokje d : ship.getDamagedParts()) { 125 | d.setStyle("-fx-background-color: darkblue"); 126 | } 127 | 128 | board.setShipsLeft(board.getShipsLeft() - 1); 129 | if (board.getShipsLeft() <= 0) return; 130 | 131 | board.setGrenades(board.getGrenades() + (Board.BOARDSIDE) / 2); 132 | board.setStageTitle(ship.getName() + " destroyed. " + board.getShipsLeft() + " more ships remain."); 133 | return; 134 | } else { 135 | board.setStageTitle("Ship #" + ship.getNumber() + " is damaged"); 136 | } 137 | } 138 | } 139 | } else { 140 | board.setStageTitle("Missed, nothing at " + index); 141 | } 142 | } 143 | 144 | 145 | } 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/main/java/org/peut/Ship.java: -------------------------------------------------------------------------------- 1 | package org.peut; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Random; 5 | 6 | public class Ship { 7 | 8 | private int length; 9 | 10 | private ArrayList healthyParts; 11 | private ArrayList damagedParts; 12 | 13 | private String name; 14 | private int number; 15 | private boolean dead; 16 | 17 | 18 | Ship(String name, Board board) { 19 | this.name = name; 20 | this.dead = false; 21 | healthyParts = new ArrayList<>(); 22 | damagedParts = new ArrayList<>(); 23 | 24 | placeShip(board); 25 | } 26 | 27 | private void placeShip(Board board) { 28 | boolean vertical = false; 29 | if ((Math.random() * 10) < 5) vertical = true; 30 | 31 | 32 | length = (int) Math.floor((Math.random() * 3.2) + 1); 33 | System.out.println("length of" + this.name + " is " + length); 34 | 35 | boolean ready = false; 36 | int maxcount = 100; 37 | 38 | while (!ready && maxcount >= 0) { 39 | int endrow = Board.BOARDSIDE; 40 | int endcol = Board.BOARDSIDE; 41 | 42 | if (vertical) { 43 | endrow -= (length - 1); 44 | } else { 45 | endcol -= (length - 1); 46 | } 47 | 48 | Random r = new Random(); 49 | int startRow = r.nextInt(endrow); 50 | int startCol = r.nextInt(endcol); 51 | 52 | ready = true; 53 | if (vertical) { 54 | for (int i = 0; i < length; ++i) { 55 | if (board.getHokje(startRow + i, startCol).isOccupied()) { 56 | ready = false; 57 | break; 58 | } 59 | } 60 | } else { 61 | for (int i = 0; i < length; ++i) { 62 | if (board.getHokje(startRow, startCol + i).isOccupied()) { 63 | ready = false; 64 | break; 65 | } 66 | } 67 | } 68 | 69 | if (ready) { 70 | if (vertical) { 71 | for (int i = 0; i < length; ++i) { 72 | 73 | Hokje square = board.getHokje(startRow + i, startCol); 74 | square.setOccupied(true); 75 | square.setShip(this); 76 | healthyParts.add(square); 77 | } 78 | } else { 79 | for (int i = 0; i < length; ++i) { 80 | 81 | Hokje square = board.getHokje(startRow, startCol + i); 82 | square.setOccupied(true); 83 | square.setShip(this); 84 | healthyParts.add(square); 85 | } 86 | } 87 | } else { 88 | System.out.println("Ship replace " + name); 89 | maxcount--; 90 | } 91 | 92 | } 93 | 94 | if (maxcount >= 0) { 95 | board.setShipsLeft((board.getShipsLeft() + 1)); 96 | this.number = board.getShipsLeft(); 97 | 98 | this.show(); 99 | } else { 100 | throw new IllegalArgumentException("No space found for " + name + ". Reduce number of ships."); 101 | } 102 | 103 | } 104 | 105 | public String getName() { 106 | return name; 107 | } 108 | 109 | public void setName(String name) { 110 | this.name = name; 111 | } 112 | 113 | public int getNumber() { 114 | return number; 115 | } 116 | 117 | public void setNumber(int number) { 118 | this.number = number; 119 | } 120 | 121 | public boolean isDead() { 122 | return dead; 123 | } 124 | 125 | public int getLength() { 126 | return length; 127 | } 128 | 129 | public void setLength(int length) { 130 | this.length = length; 131 | } 132 | 133 | public ArrayList getDamagedParts() { 134 | return damagedParts; 135 | } 136 | 137 | public void setDamagedParts(ArrayList damagedParts) { 138 | this.damagedParts = damagedParts; 139 | } 140 | 141 | 142 | public boolean isDamaged(Hokje square) { 143 | if (!healthyParts.contains(square)) { 144 | return (true); 145 | } 146 | return (false); 147 | } 148 | 149 | public boolean hit(Hokje square) { 150 | healthyParts.remove(square); 151 | damagedParts.add(square); 152 | if (healthyParts.isEmpty()) { 153 | dead = true; 154 | } 155 | return dead; 156 | } 157 | 158 | public void show() { 159 | System.out.println("Ship name: " + name); 160 | if (!dead) { 161 | System.out.println("Hokjes occupied by #" + number + " - " + name); 162 | for (Hokje a : healthyParts) { 163 | a.show(); 164 | } 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /src/main/java/org/peut/Board.java: -------------------------------------------------------------------------------- 1 | package org.peut; 2 | 3 | import javafx.geometry.Pos; 4 | import javafx.scene.Group; 5 | import javafx.scene.Scene; 6 | import javafx.scene.control.Button; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.effect.DropShadow; 9 | import javafx.scene.layout.*; 10 | import javafx.scene.paint.Color; 11 | import javafx.scene.text.Font; 12 | import javafx.scene.text.TextAlignment; 13 | import javafx.stage.Stage; 14 | 15 | 16 | import java.util.Arrays; 17 | 18 | public class Board extends Group { 19 | public static final int BOARDSIDE = 10; 20 | public static final int SHIPCOUNT = 6; 21 | private Hokje[] board; 22 | private Stage stage; 23 | Ship[] ships; 24 | private int shipsLeft; 25 | private int grenades; 26 | 27 | Board(Stage stage) { 28 | 29 | // set the stage 30 | this.stage = stage; 31 | 32 | init(); 33 | show(); 34 | } 35 | 36 | 37 | private void init() { 38 | 39 | // removing all children of the board, avoid previously used objects staying around. 40 | while( ! this.getChildren().isEmpty() ){ 41 | this.getChildren().remove(0); 42 | } 43 | 44 | // remove old board by assigning a new array 45 | board = new Hokje[(BOARDSIDE * BOARDSIDE)]; 46 | 47 | // make sure the first allocated Hokje is at zero,zero 48 | 49 | Hokje.resetXYcount(); 50 | 51 | // add BOARDSIDE x BOARDSIDE Hokjes to the Board 52 | 53 | for (int i = 0; i < (BOARDSIDE * BOARDSIDE); i++) { 54 | board[i] = new Hokje(this); 55 | this.getChildren().add(board[i]); 56 | //System.out.println( "Hokje "+ i + " style:" + h.getStyle() ); 57 | } 58 | // make sure hokje 0,0 gets focus. Handy for keyboard operation 59 | board[0].requestFocus(); 60 | 61 | // add SHIPCOUNT ships 62 | ships = new Ship[SHIPCOUNT]; 63 | int shipSurface = 0; 64 | for (int i = 0; i < SHIPCOUNT; i++) { 65 | ships[i] = new Ship("BMS Baddy" + (i + 1), this); 66 | shipSurface += ships[i].getLength(); 67 | } 68 | 69 | grenades = (BOARDSIDE * BOARDSIDE) / 2 + shipSurface; 70 | shipsLeft = SHIPCOUNT; 71 | 72 | setStageTitle(SHIPCOUNT + " ship" + (( SHIPCOUNT == 1)?"":"s") + " of unknown size must be destroyed"); 73 | 74 | } 75 | 76 | public int getShipsLeft() { 77 | return shipsLeft; 78 | } 79 | 80 | public void gameOver() { 81 | Label label = new Label("GAME OVER"); 82 | 83 | label.setFont(new Font("Arial", 32)); 84 | label.setMinSize(200, 100); 85 | label.setMaxSize(200, 100); 86 | label.setPrefSize(200, 100); 87 | label.setStyle("-fx-text-fill: white"); 88 | label.setTextAlignment(TextAlignment.CENTER); 89 | 90 | Button quitButton = new Button(); 91 | 92 | quitButton.setMinSize(100, 100); 93 | quitButton.setMaxSize(100, 100); 94 | quitButton.setPrefSize(100, 100); 95 | quitButton.setFont(new Font("Arial", 24)); 96 | quitButton.setStyle("-fx-text-fill: black"); 97 | quitButton.setText("Quit"); 98 | quitButton.setCancelButton(true); 99 | 100 | quitButton.setOnAction(e -> stage.close()); 101 | 102 | Button againButton = new Button(); 103 | 104 | againButton.setMinSize(100, 100); 105 | againButton.setMaxSize(100, 100); 106 | againButton.setPrefSize(100, 100); 107 | againButton.setFont(new Font("Arial", 24)); 108 | againButton.setStyle("-fx-text-fill: black"); 109 | againButton.setText("Again"); 110 | againButton.setDefaultButton(true); 111 | 112 | againButton.setOnAction(e -> init()); 113 | 114 | FlowPane box = new FlowPane(); 115 | box.setStyle("-fx-background-color: skyblue"); 116 | box.setPrefSize(210, 210); 117 | box.setAlignment(Pos.CENTER); 118 | 119 | box.getChildren().add(label); 120 | box.getChildren().add(quitButton); 121 | box.getChildren().add(againButton); 122 | 123 | box.setHgap(7); 124 | box.setVgap(7); 125 | box.setLayoutX(((BOARDSIDE * Hokje.SIDE) - 210) / 2); 126 | box.setLayoutY(((BOARDSIDE * Hokje.SIDE) - 210) / 2); 127 | 128 | DropShadow dropShadow1 = new DropShadow(); 129 | dropShadow1.setRadius(7); 130 | dropShadow1.setOffsetX(3); 131 | dropShadow1.setOffsetY(3); 132 | dropShadow1.setColor(Color.DEEPSKYBLUE); 133 | 134 | box.setEffect(dropShadow1); 135 | 136 | 137 | this.getChildren().add(box); 138 | 139 | // to make sure enter will reach the againButton (which has been set as 140 | // as defaultButton. If not the last boardButton will be selected. 141 | 142 | box.requestFocus(); 143 | 144 | } 145 | 146 | public void setShipsLeft(int shipsLeft) { 147 | this.shipsLeft = shipsLeft; 148 | 149 | if (this.shipsLeft == 0) { 150 | setStageTitle(" You WON! "); 151 | gameOver(); 152 | } 153 | } 154 | 155 | public int getGrenades() { 156 | return grenades; 157 | } 158 | 159 | public void setGrenades(int grenades) { 160 | this.grenades = grenades; 161 | } 162 | 163 | public void setStageTitle(String title) { 164 | stage.setTitle("Grenades left: [" + grenades + "] Ships left: [" + shipsLeft + "] " + title); 165 | } 166 | 167 | public int rowcolToNumber(int row, int column) { 168 | return row + (column * BOARDSIDE); 169 | } 170 | 171 | public int[] numberToRowcol(int number) { 172 | int[] rowcol = new int[2]; 173 | rowcol[0] = number / Board.BOARDSIDE; 174 | rowcol[1] = number % Board.BOARDSIDE; 175 | 176 | return (rowcol); 177 | } 178 | 179 | public Hokje getHokje(int row, int column) { 180 | return board[rowcolToNumber(row, column)]; 181 | } 182 | 183 | public void show() { 184 | stage.setScene(new Scene(this, BOARDSIDE * Hokje.SIDE + 1, BOARDSIDE * Hokje.SIDE + 1)); 185 | stage.show(); 186 | } 187 | 188 | 189 | } 190 | --------------------------------------------------------------------------------