diff --git a/src/comp1110/ass2/BlueLagoon.java b/src/comp1110/ass2/BlueLagoon.java index 1c8d5da..792d2d1 100644 --- a/src/comp1110/ass2/BlueLagoon.java +++ b/src/comp1110/ass2/BlueLagoon.java @@ -186,6 +186,11 @@ public class BlueLagoon { boardHeight = Integer.parseInt(parseSplit[1]); String playerAmount = parseSplit[2]; numberOfPlayer = Integer.parseInt(playerAmount); + switch (numberOfPlayer) { + case 4 -> numberOfSettlersPerPlayer = 20; + case 3 -> numberOfSettlersPerPlayer = 25; + case 2 -> numberOfSettlersPerPlayer = 30; + } } // Get the player ID and Current Phase from here @@ -227,33 +232,8 @@ public class BlueLagoon { if (pStatePlayerId.equals(playerId)) playerVillageCoords.add(parseSplit[i]); i++; } - - // Checking the requirement of how many pieces are left // - switch (numberOfPlayer) { - case 4 -> { - numberOfSettlersPerPlayer -= 10; - if (pieceType.equals("S")) { - if (settlerCounter + 1 > numberOfSettlersPerPlayer) return false; - } else if (pieceType.equals("T")) { - if (villageCounter + 1 > numberOfVillagesPerPlayer) return false; - } - } - case 3 -> { - numberOfSettlersPerPlayer -= 5; - if (pieceType.equals("S")) { - if (settlerCounter + 1 > numberOfSettlersPerPlayer) return false; - } else if (pieceType.equals("T")) { - if (villageCounter + 1 > numberOfVillagesPerPlayer) return false; - } - } - case 2 -> { - if (pieceType.equals("S")) { - if (settlerCounter + 1 > numberOfSettlersPerPlayer) return false; - } else if (pieceType.equals("T")) { - if (villageCounter + 1 > numberOfVillagesPerPlayer) return false; - } - } - } + if (pieceType.equals("S") && settlerCounter + 1 > numberOfSettlersPerPlayer) return false; + else if (pieceType.equals("T") && villageCounter + 1 > numberOfVillagesPerPlayer) return false; } } } @@ -263,9 +243,7 @@ public class BlueLagoon { if(yMoveCoords > boardHeight - 1) return false; // if it's even rows, check the number of cols for out of bound (i.e. the width) - if(yMoveCoords % 2 == 0) { - if(xMoveCoords > boardHeight - 2) return false; - } + if(yMoveCoords % 2 == 0 && xMoveCoords > boardHeight - 2) return false; else if(xMoveCoords > boardHeight - 1) return false; // For Exploration Phase and or Settlement Phase @@ -310,7 +288,7 @@ public class BlueLagoon { return true; } - private static boolean isAdjacent(String centerCoords, ArrayList coordsContainer) { + public static boolean isAdjacent(String centerCoords, ArrayList coordsContainer) { String[] coordsSplit = centerCoords.split(","); int mainX = Integer.parseInt(coordsSplit[1]); // xCoord for center Coords int mainY = Integer.parseInt(coordsSplit[0]); // yCoord for center Coords @@ -331,59 +309,7 @@ public class BlueLagoon { return false; } - /** - * This method is to check if the move is valid for the current player - * This is a very trimmed down version of the isMoveValid method - * - * @param pieceType Type of piece being placed (S = Settler, T = Village) - * @param moveCoords The coords of the piece being placed (i.e. 1,2) - * @param currentPhase The current phase of the game (E = Exploration, S = Settlement) - * @param coordsContainer The coords of the land in a ArrayList of Strings - * @param settlerCoords The coords of the Settlers in a ArrayList of Strings - * @param villageCoords The coords of the Villages in a ArrayList of Strings - * @param playerSettlerCoords The coords of the Settlers of the current player in a ArrayList of Strings - * @param playerVillageCoords The coords of the Villages of the current player in a ArrayList of Strings - * @return boolean True if the move is valid, false if the move is invalid - */ - - public static boolean isMoveValidTrim(String pieceType, String moveCoords, - String currentPhase, ArrayList coordsContainer, - ArrayList settlerCoords, - ArrayList villageCoords, ArrayList playerSettlerCoords, - ArrayList playerVillageCoords) { - // For Exploration Phase and or Settlement Phase - switch(currentPhase){ - // Exploration Phase - case "E": - // If the move Coords is an occupied space, return false; - if(settlerCoords.contains(moveCoords) || villageCoords.contains(moveCoords)) return false; - // If the Village is being placed on the sea return false - if(pieceType.equals("T") && !coordsContainer.contains(moveCoords)) return false; - // if the village is placed on Land and it's not adjacent to any - // of the pieces return false - if(pieceType.equals("T") && (!isAdjacent(moveCoords, playerVillageCoords) && - !isAdjacent(moveCoords, playerSettlerCoords))) return false; - // If settler is on land and it's not adjacent to any of the pieces - // return false - if(pieceType.equals("S") && coordsContainer.contains(moveCoords)){ - if(!isAdjacent(moveCoords, playerSettlerCoords) && - !isAdjacent(moveCoords, playerVillageCoords)) return false; - } - break; - // Settlement Phase - case "S": - // If the move coord is an occupied space, return false; - if(settlerCoords.contains(moveCoords)) return false; - if(villageCoords.contains(moveCoords)) return false; - - // if the settler is not adjacent with any of the pieces return false - if(!isAdjacent(moveCoords, playerSettlerCoords) && - !isAdjacent(moveCoords, playerVillageCoords)) return false; - } - return true; - } - - /** + /** * Given a state string, generate a set containing all move strings playable * by the current player. *

@@ -394,77 +320,18 @@ public class BlueLagoon { */ public static Set generateAllValidMoves(String stateString) { - // Get number of players - int numPlayers = Character.getNumericValue(stateString.charAt(stateString.indexOf(";") - 1)); - // Store the current game phase - String gamePhase = "E"; - // If the game is not in the exploration phase use state 1 - if (!stateString.contains("E")) gamePhase = "S"; - // Get the current player - String currentPlayer = stateString.substring(stateString.indexOf("c ") + 2,stateString.indexOf("c ") + 3); + // Create a state object + State state = new State(stateString); + + // Get information from the state string + int numPlayers = state.getNumPlayers(); + char gamePhase = state.getCurrentPhase(); + // Get the board size - int boardHeight = Integer.parseInt(stateString.substring(stateString.indexOf("a ") + 2, stateString.indexOf(";") - 2)); - // Get player data - String allPlayerData = stateString.substring(stateString.indexOf("p " + currentPlayer)); - String playerData = allPlayerData.substring(0, allPlayerData.indexOf(";")); - String[] pStates = stateString.substring(stateString.indexOf("p ")).split("; ?"); + int boardHeight = state.boardHeight; - ArrayList settlerCoords = new ArrayList<>(); // Placed Settler Coordinates - ArrayList villageCoords = new ArrayList<>(); // Placed villages coordinates - ArrayList playerSettlerCoords = new ArrayList<>(); // The current Player's settler coords - ArrayList playerVillageCoords = new ArrayList<>(); // The current Player's Village coords - - for (String pState:pStates) { - - String[] parseSplit = pState.split(" "); - // Check if there's enough pieces left for that player that is moving - String pStatePlayerId = parseSplit[1]; - - // Collecting the settler Coords that has been placed - for (int i = 9; i < parseSplit.length; i++) { - while (!parseSplit[i].equals("T")) { - settlerCoords.add(parseSplit[i]); // Store all the settler coords - - // If the current player ID is the same as the placed settler's player ID - // Store it into array - if (pStatePlayerId.equals(currentPlayer)) playerSettlerCoords.add(parseSplit[i]); - i++; - } - i++; - - // Collecting the village coords that has been placed - while (i < parseSplit.length) { - villageCoords.add(parseSplit[i]); // Store all the village Coords - - // If the current player ID is the same as the placed Village's player ID - // Store it into array - if (pStatePlayerId.equals(currentPlayer)) playerVillageCoords.add(parseSplit[i]); - i++; - } - } - } - - // Get placed pieces - String settlersPlaced = playerData.substring(playerData.indexOf("S") + 2, playerData.indexOf("T")); - int numSettlersPlaced = settlersPlaced.split(" ").length; - if (!settlersPlaced.contains(" ")){ - numSettlersPlaced = 0; - } - String villagesPlaced = playerData.substring(playerData.indexOf("T")+1); - int numVillagesPlaced = villagesPlaced.split(" ").length; - if (!villagesPlaced.contains(" ")){ - numVillagesPlaced = 0; - } - - // Get island data - String[] islands = stateString.substring(stateString.indexOf("i ")).split("; "); - // Get the coordinates of the islands - ArrayList coordsContainer = new ArrayList<>(); - - for (String island : islands) { - if (!island.startsWith("i ")) continue; - coordsContainer.addAll(Arrays.asList(island.substring(4).split(" "))); - } + // Create a set to store all possible moves + Set allMoves = new HashSet<>(); // Calculate number of pieces each player starts with int startNumSettlers = 0; @@ -480,12 +347,43 @@ public class BlueLagoon { break; } - // Check if the player has placed all their settlers or villages - boolean hasSettler = (numSettlersPlaced < startNumSettlers); - boolean hasVillage = (numVillagesPlaced <= 5); - // Create a set to store all possible moves - Set allMoves = new HashSet<>(); + // Check if the player has placed all their settlers or villages + boolean hasSettler = (state.getCurrentPlayer().getSettlers().length < startNumSettlers); + boolean hasVillage = (state.getCurrentPlayer().getVillages().length < 5); + + if (!hasSettler && !(hasVillage && gamePhase == 'E')) return allMoves; + + // Add used coords + ArrayList settlerCoords = new ArrayList<>(); // Placed Settler Coordinates + ArrayList villageCoords = new ArrayList<>(); // Placed villages coordinates + ArrayList playerSettlerCoords = new ArrayList<>(); // The current Player's settler coords + ArrayList playerVillageCoords = new ArrayList<>(); // The current Player's Village coords + + for (int i = 0; i < numPlayers; i++){ + for (Coord c: state.getPlayer(i).getSettlers()){ + settlerCoords.add(c.toString()); + } + for (Coord c: state.getPlayer(i).getVillages()){ + villageCoords.add(c.toString()); + } + } + + for (Coord c: state.getCurrentPlayer().getSettlers()){ + playerSettlerCoords.add(c.toString()); + } + for (Coord c: state.getCurrentPlayer().getVillages()){ + playerVillageCoords.add(c.toString()); + } + + // Get the coordinates of the islands + ArrayList coordsContainer = new ArrayList<>(); + + for (Island island : state.getIslands()) { + for (Coord c:island.getCoords()){ + coordsContainer.add(c.toString()); + } + } // Generate all possible coordinates in an array String[] coordinates = new String[boardHeight * boardHeight]; @@ -496,29 +394,40 @@ public class BlueLagoon { index++; } } + // For each coordinate for (String cord:coordinates) { - + // Make sure the coordinate is not already used + if(settlerCoords.contains(cord)) continue; + if(villageCoords.contains(cord)) continue; + // Make sure the coordinate is in bounds int y = Integer.parseInt(cord.split(",")[1]); if(Integer.parseInt(cord.substring(0,cord.indexOf(','))) % 2 == 0) { if(y > boardHeight - 2) continue; } else if(y > boardHeight - 1) continue; - - // If the player has not placed all their settlers - if (hasSettler){ - if (isMoveValidTrim("S", cord,gamePhase, - coordsContainer,settlerCoords,villageCoords,playerSettlerCoords,playerVillageCoords)) { - allMoves.add("S " + cord); + switch (gamePhase) { + case 'E' -> { + if (!coordsContainer.contains(cord)) { + if (hasSettler) allMoves.add("S " + cord); + break; + } + // If the Village is being placed on the sea return false + if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) { + // Add the move to the set + if (hasVillage) allMoves.add("T " + cord); + if (hasSettler) allMoves.add("S " + cord); + } + } + // Settlement Phase + case 'S' -> { + // if the settler is not adjacent with any of the pieces return false + if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) { + // Add the move to the set + if (hasSettler) allMoves.add("S " + cord); + } } } - if (hasVillage && gamePhase == "E") { - if (isMoveValidTrim("T", cord,gamePhase, - coordsContainer,settlerCoords,villageCoords,playerSettlerCoords,playerVillageCoords)) { - allMoves.add("T " + cord); - } - } - } return allMoves; } @@ -537,7 +446,7 @@ public class BlueLagoon { */ public static boolean isPhaseOver(String stateString){ State state = new State(stateString); - return state.isPhaseOver(true); + return state.isPhaseOver(); } /** diff --git a/src/comp1110/ass2/Player.java b/src/comp1110/ass2/Player.java index c43c1d9..c7bce3d 100644 --- a/src/comp1110/ass2/Player.java +++ b/src/comp1110/ass2/Player.java @@ -1,8 +1,11 @@ package comp1110.ass2; +import java.util.ArrayList; import java.util.Random; import java.util.Set; +import static comp1110.ass2.BlueLagoon.isAdjacent; + /** * Player class * This class is used to store the information of a player @@ -237,8 +240,94 @@ public class Player { * @return true if player can do any moves, false otherwise */ public boolean canPlay(State state) { - Set validMoves = BlueLagoon.generateAllValidMoves(state.toString()); - return validMoves.size() > 0; + + // Check if the player has placed all their settlers or villages + int startNumSettlers = switch (state.getNumPlayers()) { + case 2 -> 30; + case 3 -> 25; + case 4 -> 20; + default -> 0; + }; + boolean hasSettler = (settlers.length < startNumSettlers); + boolean hasVillage = (settlers.length < 5); + if (!hasSettler && !(hasVillage && state.getCurrentPhase() == 'E')) return false; + + // Add used coords + ArrayList settlerCoords = new ArrayList<>(); // Placed Settler Coordinates + ArrayList villageCoords = new ArrayList<>(); // Placed villages coordinates + ArrayList playerSettlerCoords = new ArrayList<>(); // The current Player's settler coords + ArrayList playerVillageCoords = new ArrayList<>(); // The current Player's Village coords + + for (int i = 0; i < state.getNumPlayers(); i++){ + for (Coord c: state.getPlayer(i).getSettlers()){ + settlerCoords.add(c.toString()); + } + for (Coord c: state.getPlayer(i).getVillages()){ + villageCoords.add(c.toString()); + } + } + + for (Coord c: settlers){ + playerSettlerCoords.add(c.toString()); + } + for (Coord c: villages){ + playerVillageCoords.add(c.toString()); + } + + // Get the coordinates of the islands + ArrayList islandCoords = new ArrayList<>(); + + for (Island island : state.getIslands()) { + for (Coord c:island.getCoords()){ + islandCoords.add(c.toString()); + } + } + + // Generate all possible coordinates in an array + String[] coordinates = new String[state.boardHeight * state.boardHeight]; + int index = 0; + for (int i = 0; i < state.boardHeight; i++){ + for (int j = 0; j < state.boardHeight; j++){ + coordinates[index] = j + "," + i; + index++; + } + } + + // For each coordinate + for (String cord:coordinates) { + // Make sure the coordinate is not already used + if(settlerCoords.contains(cord)) continue; + if(villageCoords.contains(cord)) continue; + // Make sure the coordinate is in bounds + int y = Integer.parseInt(cord.split(",")[1]); + if(Integer.parseInt(cord.substring(0,cord.indexOf(','))) % 2 == 0) { + if(y > state.boardHeight - 2) continue; + } + else if(y > state.boardHeight - 1) continue; + switch (state.getCurrentPhase()) { + case 'E' -> { + if (!islandCoords.contains(cord)) { + if (hasSettler) return true; + break; + } + // If the Village is being placed on the sea return false + if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) { + // Add the move to the set + if (hasVillage) return true; + if (hasSettler) return true; + } + } + // Settlement Phase + case 'S' -> { + // if the settler is not adjacent with any of the pieces return false + if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) { + // Add the move to the set + if (hasSettler) return true; + } + } + } + } + return false; } // endregion