package comp1110.ass2.gui; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.TilePane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import javafx.stage.Stage; import javafx.scene.shape.Polygon; import javafx.scene.paint.Paint; import javax.swing.*; import java.awt.*; public class Viewer extends Application { private static final int VIEWER_WIDTH = 1200; private static final int VIEWER_HEIGHT = 700; private final Group root = new Group(); private final Group controls = new Group(); private TextField stateTextField; /** * Given a state string, draw a representation of the state * on the screen. *

* This may prove useful for debugging complex states. * * @param stateString a string representing a game state * The combined game state is made up of the following in order: * * 1 Game Arrangement Statement * 1 Current State Statement * Many Island Statements (as many as there are Islands on the map) - sorted ascending numerically by the island bonus (ties broken by numerically ascending coordinates) * 1 Stones Statement * 1 Unclaimed Resources Statement * Many Player Statements (as many as there are Players) - sorted ascending numerically by player number * Formally this is: * * gameState = gameArrangementStatement, " ", currentStateStatement, {" ", islandStatement}, " ", stonesStatement, " ", unclaimedResourcesAndStatuettesStatement, {" ", playerStatement} * a 13 2; c 0 E; i 6 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 1,4 2,0 2,1; i 6 0,5 0,6 0,7 1,6 1,7 1,8 2,6 2,7 2,8 3,7 3,8; i 6 7,12 8,11 9,11 9,12 10,10 10,11 11,10 11,11 11,12 12,10 12,11; i 8 0,9 0,10 0,11 1,10 1,11 1,12 2,10 2,11 3,10 3,11 3,12 4,10 4,11 5,11 5,12; i 8 4,0 5,0 5,1 6,0 6,1 7,0 7,1 7,2 8,0 8,1 8,2 9,0 9,1 9,2; i 8 10,3 10,4 11,0 11,1 11,2 11,3 11,4 11,5 12,0 12,1 12,2 12,3 12,4 12,5; i 10 3,3 3,4 3,5 4,2 4,3 4,4 4,5 5,3 5,4 5,5 5,6 6,3 6,4 6,5 6,6 7,4 7,5 7,6 8,4 8,5; i 10 5,8 5,9 6,8 6,9 7,8 7,9 7,10 8,7 8,8 8,9 9,7 9,8 9,9 10,6 10,7 10,8 11,7 11,8 12,7 12,8; s 0,0 0,5 0,9 1,4 1,8 1,12 2,1 3,5 3,7 3,10 3,12 4,0 4,2 5,9 5,11 6,3 6,6 7,0 7,8 7,12 8,2 8,5 9,0 9,9 10,3 10,6 10,10 11,0 11,5 12,2 12,8 12,11; r C B W P S; p 0 0 0 0 0 0 0 S T; p 1 0 0 0 0 0 0 S T; * * ^ The initial board. Two players, player 0 to start. * * [Game statement] * gameArrangementStatement = "a ", boardHeight, " ", numPlayers, ";" * * where boardHeight and numPlayers are both positive integers. * * e.g. "a 13 2;" * * ^ The standard map layout - 13 high, 2 players * * [Current State] * currentStateStatement = "c ", playerId, " ", phase, ";" * * phase = "E" | "S" * * and where playerId is a non-negative integer that represents the ID of the current player whose turn it is. * * e.g. "c 0 E;" * * ^ The current player to move is player 0 in the Exploration phase * * [Many Island Statements] * islandStatement = "i ", bonus, {" ", coordinate}, ";" * * where bonus is a non-negative integer. * * e.g. "i 6 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 1,4 2,0 2,1;" * * ^ The first island (top left) of the standard map * * e.g. "i 6 0,5 0,6 0,7 1,6 1,7 1,8 2,6 2,7 2,8 3,7 3,8;" * * ^ The second island (top middle) of the standard map * * e.g. "i 6 7,12 8,11 9,11 9,12 10,10 10,11 11,10 11,11 11,12 12,10 12,11; i 8 0,9 0,10 0,11 1,10 1,11 1,12 2,10 2,11 3,10 3,11 3,12 4,10 4,11 5,11 5,12; i 8 4,0 5,0 5,1 6,0 6,1 7,0 7,1 7,2 8,0 8,1 8,2 9,0 9,1 9,2;" * * ^ A sequence of three island statements appearing in the standard game string * * [Stones Statement] * There will always be exactly 32 stone circles. Coordinates are sorted in numerically ascending order. hint: parse all island statements before the stones statement * * stonesStatement = "s", {" ", coordinate}, ";" * * e.g. "s 0,0 0,5 0,9 1,4 1,8 1,12 2,1 3,5 3,7 3,10 3,12 4,0 4,2 5,9 5,11 6,3 6,6 7,0 7,8 7,12 8,2 8,5 9,0 9,9 10,3 10,6 10,10 11,0 11,5 12,2 12,8 12,11;" * * ^ The stone circles on the base map * * [Unclaimed Resource statement] * unclaimedResourcesAndStatuettesStatement = "r C", {" ", coordinate}, " B", {" ", coordinate}, " W", {" ", coordinate}, " P", {" ", coordinate}, " S", {" ", coordinate}, ";" * * e.g. "r C 1,1 B 1,2 W P 1,4 S;" * * ^ Coconut at 1,1, Bamboo at 1,2, Precious Stone at 1,4. No Water or Statuettes * * [Player Statement] * playerStatement = "p ", playerId, " ", score, " ", coconut, " ", bamboo, " ", water, " ", preciousStone, " ", statuette, " S", {" ", coordinate}, " T", {" ", coordinate}, ";" * * where coconut, bamboo, water, preciousStone, statuette are non-negative integers representing the number of each resource or statuettes the player has collected during this phase. score is the total score of the player. * * e.g. "p 1 42 1 2 3 4 5 S 5,6 8,7 T 1,2;" * * private static final int VIEWER_WIDTH = 1200; * private static final int VIEWER_HEIGHT = 700; * * ^ player 1 with score: 42, coconuts: 1, bamboo: 2, water: 3, precious stone: 4, statuettes: 5, placed settlers at 5,6 and 8,7, placed villages at 1,2 * * a 13 2; c 0 E; i 6 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 1,4 2,0 2,1; i 6 0,5 0,6 0,7 1,6 1,7 */ void displayState(String stateString) { // When refreshing, it clears the whole thing and update it root.getChildren().clear(); root.getChildren().add(controls); // Intizialiation of the grid GridPane viewerGrid = new GridPane(); String[] parts = stateString.split("; ?"); int boardHeight = 0; int boardHeightPx = 0; int tileSize = 0; for ( String part : parts) { String[] parseSplit = part.split(" "); String stateCases = parseSplit[0]; // Splitting of the states of the game as per the encoded string switch (stateCases) { // Game Arrangement Statement case "a": // Settting up the board height value and the tile size boardHeight = Integer.parseInt(parseSplit[1]); boardHeightPx = 650; tileSize = boardHeightPx / boardHeight; viewerGrid.setPrefWidth(boardHeightPx); viewerGrid.setPrefHeight(boardHeightPx); // Generating the water tiles ( the background water map ) for(int i = 0; i < boardHeight; i++){ for(int j = 0; j < boardHeight; j++){ addBoardTile(viewerGrid, boardHeightPx/boardHeight, String.format("%s,%s", i, j), Color.DARKBLUE); } } break; // Current state statement case "c": // Getting the player ID from the string int playerId = Integer.parseInt(parseSplit[1]); // Getting the two mode of current state either Exploration // or settler String currentPhase = parseSplit[2]; switch (currentPhase) { case "E": currentPhase = "Exploration"; break; case "S": currentPhase = "Settler"; break; } // Making the Current State Statement text on the window Text currentStateText = new Text(); currentStateText.setText("The current player to move is player " + playerId + " in the " + currentPhase + " phase"); currentStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 20)); currentStateText.setX(VIEWER_WIDTH / boardHeight * 3.5); currentStateText.setY(50); currentStateText.setTextAlignment(TextAlignment.CENTER); root.getChildren().add(currentStateText); currentStateText.setFill(Color.GREEN); break; // Generating the Island tiles from the Island State Statement case "i": for(int i = 2; i < parseSplit.length; i++){ addBoardTile(viewerGrid, tileSize, parseSplit[i], Color.GREEN); } break; // Generating the Stone Tiles from the Stone Statement case "s": for(int i = 1; i < parseSplit.length; i++){ addStoneTileToBoard(viewerGrid, tileSize, parseSplit[i], Color.GRAY); } break; // Generating the Resources Tiles from the Resource Statement case "r": for(int i = 1; i < parseSplit.length; i++){ switch(parseSplit[i]) { // Generating Resource: Coconut Tiles case "C": i++; // To Skip the "C" itself and go to the numbers in the string while(!parseSplit[i].equals("B")){ // Tile generation addTileToBoard(viewerGrid, tileSize, parseSplit[i], Color.BROWN); // Label for the tiles generation addLabelToTile(viewerGrid,tileSize,parseSplit[i], Color.WHITE, "C"); i++; // To continue the iteration for the while loops } i--; // So that i does not go straight to the coords after the letters instead // i stop at the letter "B" break; // Generating Resource: Bamboo Tiles case "B": i++; // To Skip the "B" itself and go to the numbers in the string while(!parseSplit[i].equals("W")){ // Tile generation addTileToBoard(viewerGrid, tileSize, parseSplit[i], Color.YELLOW); // Label for the tiles generation addLabelToTile(viewerGrid,tileSize,parseSplit[i], Color.BLACK, "B"); i++; // To continue the iteration for the while loops } i--; // So that i does not go straight to the coords after the letters instead // i stop at the letter "W" break; // Generating Resource: Water tiles case "W": i++; // To Skip the "W" itself and go to the numbers in the string while(!parseSplit[i].equals("P")){ // Tile Generation addTileToBoard(viewerGrid,tileSize, parseSplit[i], Color.CYAN); // Label for the tiles generation addLabelToTile(viewerGrid,tileSize,parseSplit[i], Color.BLACK, "W"); i++; // To continue the iteration for the while loops } i--; // So that i does not go straight to the coords after the letters instead // i stop at the letter "P" break; // Generating Resource: Precious Stone tiles case "P": i++; // To Skip the "P" itself and go to the numbers in the string while(!parseSplit[i].equals("S")){ // Tile Generation addTileToBoard(viewerGrid,tileSize,parseSplit[i], Color.GOLD); // Label for the tiles generation addLabelToTile(viewerGrid, tileSize, parseSplit[i], Color.BLACK, "P"); i++; // To continue the iteration for the while loops } i--; // So that i does not go straight to the coords after the letters instead // i stop at the letter "S" break; // Generating Resource: Statuettes tiles case "S": i++; // To Skip the "P" itself and go to the numbers in the string while(i < parseSplit.length){ // Tile Generation addTileToBoard(viewerGrid, tileSize, parseSplit[i], Color.SILVER); // Label for the tiles generation addLabelToTile(viewerGrid, tileSize, parseSplit[i], Color.BLACK, "S"); i++; // To continue the iteration for the while loops } break; } } break; // Player Statement case "p": // A container for the String that is printed at the end String container = ""; container += "player " + parseSplit[1] + " with score: " + parseSplit[2] + ", coconuts: " + parseSplit[3] + ", bambooo: " + parseSplit[4] + ", water: " + parseSplit[5] + ", precious stone: " + parseSplit[6] + ", statuettes: " + parseSplit[7] + ", placed settlers at "; // Setller tile generator int i = 9; int counter = 0; while(!parseSplit[i].equals("T")){ counter++; String[] coords = parseSplit[i].split(","); // Tile generator addStoneTileToBoard(viewerGrid, tileSize, parseSplit[i], Color.PINK); // Label generator addSmallLabelToTile(viewerGrid, tileSize, parseSplit[i], Color.BLACK, "Settlers"); // if there's more than 1 settler placed if(counter > 1) container += " and "; container += Integer.parseInt(coords[0]) + "," + Integer.parseInt(coords[1]); i++; // To iterate the while loop } i++; // To continue past the "T" keyword // Village tile generator container += ", placed villages at "; int counterT = 0; while(i < parseSplit.length){ counterT++; String[] coords = parseSplit[i].split(","); // Tile generator addStoneTileToBoard(viewerGrid, tileSize, parseSplit[i], Color. LIGHTGOLDENRODYELLOW); // Label generator addSmallLabelToTile(viewerGrid, tileSize, parseSplit[i], Color.BLACK, "Village"); // If there's more than 1 village placed if(counterT > 1) container += " and "; container += Integer.parseInt(coords[0]) + "," + Integer.parseInt(coords[1]); i++; } // Adding the player Statement Text to the window Text playerStateText = new Text(); playerStateText.setText(container); playerStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 15)); playerStateText.setX(125); playerStateText.setY(600); playerStateText.setTextAlignment(TextAlignment.CENTER); root.getChildren().add(playerStateText); playerStateText.setFill(Color.BLACK); // } } } // Relocate the grid to be more center viewerGrid.relocate((VIEWER_WIDTH/2-viewerGrid.getPrefWidth()/2), ((VIEWER_HEIGHT+100)/2-viewerGrid.getPrefHeight()/2)); root.getChildren().add(viewerGrid); // FIXME Task 5 } // Generating the big board tiles such as the water tiles and the island tiles void addBoardTile(GridPane board, int tileSize, String coordString, Color color) { // If the string empty, stop the function if (coordString.equals("")) return; String[] coords = coordString.split(","); Hexagon hex = new Hexagon(tileSize, color); // if the row is even, translate the tile to the right by tileSize/2 if (Integer.parseInt(coords[0]) % 2 == 0) hex.setTranslateX(tileSize/2); // Translate the whole tile's Y axis downwards so they connect and there's no gap hex.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); board.add(hex, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); } // Generating the small tiles such as resources, stones, etc void addStoneTileToBoard(GridPane board, int tileSize, String coordString, Color color) { int tileSize2 = tileSize; tileSize2 -= 15; // If the string empty, stop the function if (coordString.equals("")) return; String[] coords = coordString.split(","); Hexagon hex = new Hexagon(tileSize2, color); // if the row is even, translate the tile to the right by tileSize/2 if (Integer.parseInt(coords[0]) % 2 == 0) hex.setTranslateX(tileSize/2); // Translate the whole tile's Y axis downwards so they connect and there's no gap hex.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); // Translate the tile so they look center hex.setTranslateX(7 + hex.getTranslateX()); board.add(hex, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); } void addTileToBoard(GridPane board, int tileSize, String coordString, Color color) { int tileSize2 = tileSize; tileSize2 -= 25; // If the string empty, stop the function if (coordString.equals("")) return; String[] coords = coordString.split(","); Hexagon hex = new Hexagon(tileSize2, color); // if the row is even, translate the tile to the right by tileSize/2 if (Integer.parseInt(coords[0]) % 2 == 0) hex.setTranslateX(tileSize/2); // Translate the whole tile's Y axis downwards so they connect and there's no gap hex.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); // Translate the tile so they look center hex.setTranslateX(12 + hex.getTranslateX()); board.add(hex, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); } // Adding labels to the resources tiles and stones tiles void addLabelToTile(GridPane board, int tileSize, String coordString, Color color, String labelName){ // If the string empty, stop the function if (coordString.equals("")) return; String[] coords = coordString.split(","); Label newLabel = new Label(labelName); newLabel.setTextFill(color); newLabel.setFont(Font.font("Sans Serif", 12)); // Following the tile's pos format if (Integer.parseInt(coords[0]) % 2 == 0) newLabel.setTranslateX(tileSize/2); newLabel.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); // Making the label center newLabel.setTranslateX(19.5 + newLabel.getTranslateX()); board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); } // Adding labels to the villagers and settlers void addSmallLabelToTile(GridPane board, int tileSize, String coordString, Color color, String labelName){ // If the string empty, stop the function if (coordString.equals("")) return; String[] coords = coordString.split(","); Label newLabel = new Label(labelName); newLabel.setTextFill(color); newLabel.setFont(Font.font("Sans Serif", 9)); // Following the tile's pos format if (Integer.parseInt(coords[0]) % 2 == 0) newLabel.setTranslateX(tileSize/2); newLabel.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); // Making the label center newLabel.setTranslateX(11 + newLabel.getTranslateX()); board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); } // A hexagon nested class shape class Hexagon extends Polygon { public Hexagon(double side, Paint fill) { super( 0.0, side / 2.0, side / 2.0, side / 4.0, side / 2.0, -side / 4.0, 0.0, -side / 2.0, -side / 2.0, -side / 4.0, -side / 2.0, side / 4.0 ); this.setFill(fill); } } /** * Create a basic text field for input and a refresh button. */ private void makeControls() { Label playerLabel = new Label("Game State:"); stateTextField = new TextField(); stateTextField.setPrefWidth(200); Button button = new Button("Refresh"); button.setOnAction(new EventHandler() { @Override public void handle(ActionEvent e) { displayState(stateTextField.getText()); } }); HBox hb = new HBox(); hb.getChildren().addAll(playerLabel, stateTextField, button); hb.setSpacing(10); hb.setLayoutX(50); hb.setLayoutY(VIEWER_HEIGHT - 50); controls.getChildren().add(hb); } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Blue Lagoon Viewer"); Scene scene = new Scene(root, VIEWER_WIDTH, VIEWER_HEIGHT); root.getChildren().add(controls); makeControls(); primaryStage.getIcons().add(new javafx.scene.image.Image(Viewer.class.getResourceAsStream("favicon.png"))); primaryStage.setScene(scene); primaryStage.show(); } }