Compare commits

..

No commits in common. "master" and "generateAll_Fix" have entirely different histories.

18 changed files with 639 additions and 676 deletions

View File

@ -10,8 +10,8 @@ declaration: >-
contributions: contributions:
- uid: u7156831 - uid: u7156831
contribution: 33 contribution: 33
- uid: u7492895 - uid:
contribution: 33 contribution:
- uid: u7280427 - uid: u7280427
contribution: 33 contribution: 33
@ -20,7 +20,7 @@ contributions:
signatures: signatures:
- name: Nathan Woodburn - name: Nathan Woodburn
uid: u7156831 uid: u7156831
- name: Justin Ryu - name:
uid: u7492895 uid:
- name: Immanuel Alvaro Bhirawa - name: Immanuel Alvaro Bhirawa
uid: u7280427 uid: u7280427

View File

@ -52,7 +52,7 @@ declaration: >-
signatures: signatures:
- name: Nathan Woodburn - name: Nathan Woodburn
uid: u7156831 uid: u7156831
- name: Immanuel Alvaro Bhirawa - name:
uid: u7280427 uid:
- name: Justin Ryu - name:
uid: u7492895 uid:

View File

@ -4,7 +4,7 @@ Reviewed by: Nathan Woodburn, u7156831
Reviewing code written by: Immanuel Alvaro Bhirawa, u7280427 Reviewing code written by: Immanuel Alvaro Bhirawa, u7280427
Component: `isMoveValid` from [BlueLagoon.java L145-L311](https://gitlab.cecs.anu.edu.au/u7156831/comp1110-ass2/-/blob/b8487c3c0826bef4e676a13f8ea05c578c73d2de/src/comp1110/ass2/BlueLagoon.java#L145-L311) Component: `isMoveValid` from [BlueLagoon.java L145-L311](https://gitlab.cecs.anu.edu.au/u7156831/comp1110-ass2/-/blob/master/src/comp1110/ass2/BlueLagoon.java#L145-L311)
### Comments ### Comments
@ -17,10 +17,10 @@ Component: `isMoveValid` from [BlueLagoon.java L145-L311](https://gitlab.cecs.an
- Places to improve: - Places to improve:
- Some comments are not needed, such as commenting about well named variables. - Some comments are not needed, such as commenting about well named variables.
```java ```java
int numberOfPlayer = 0; // Number of player int numberOfPlayer = 0; // Number of player
String playerId = ""; // Player ID String playerId = ""; // Player ID
``` ```
- Duplicate code could be avoided. For example [this switch](https://gitlab.cecs.anu.edu.au/u7156831/comp1110-ass2/-/blob/b8487c3c0826bef4e676a13f8ea05c578c73d2de/src/comp1110/ass2/BlueLagoon.java#L231-L256) could be shortened to the below to avoid duplicate code. - Duplicate code could be avoided. For example [this switch](https://gitlab.cecs.anu.edu.au/u7156831/comp1110-ass2/-/blob/b8487c3c0826bef4e676a13f8ea05c578c73d2de/src/comp1110/ass2/BlueLagoon.java#L231-L256) could be shortened to the below to avoid duplicate code.
```java ```java
switch (numberOfPlayer) { switch (numberOfPlayer) {

View File

@ -1,53 +0,0 @@
# IMPORTANT: It is very important that you correctly complete this originality
# statement.
#
# This is your statement of your submitted work being your own.
# Incorrectly filling out this statement could lead to charges
# of academic misconduct.
#
# For information on how to fill this out correctly, see
# https://cs.anu.edu.au/courses/comp1110/help/faq/09-originality/
#
declaration: >-
I submit the work below for assessment as my best work. I declare that this
is entirely my own work, with the following documented exceptions:
# Use this to list names of people who you collaborated with, and a
# comment about what you collaborated on.
#
# Add as many "name+comment" entries as necessary
# (or remove it altogether if you haven't collaborated with anyone)
# collaboration:
# - name:
# comment: >-
# Use this to list any code that you used that you did not write,
# aside from code provided by the lecturer. Provide a comment
# explaining your use and the URL to that code and the licence for
# that code
#
# Add as many "url+licence+comment" entries as necessary
# (or remove it altogether if you haven't used any external code)
# code:
# - comment:
# url:
# licence:
# I wish to submit the following classes as entirely my own (remove this if
# you want to just submit methods):
class:
- Island
- Coord
- Player
- Resource
# I wish to submit the following methods as entirely my own (remove this if
# you want to just submit classes):
method:
- Everything in class state except for scoreLinks(), findLongestLinkScore(),
DFSRecursionLink(), findScoreForLink()
# sign *your* name and uid here
name: Nathan Woodburn
uid: u7156831

View File

@ -9,5 +9,5 @@ of our project implements the following features:
- Playable against a computer opponent / AI (Task 16) - Playable against a computer opponent / AI (Task 16)
- Generalised GUI to more than two players (Task 17) - Generalised GUI to more than two players (Task 17)
- Generalised GUI to different sized boards (Task 17) - Generalised GUI to different sized boards (Task 17)
- Allow more than one AI player (up to 4)
- Toggleable Dark mode additional features...

View File

@ -19,9 +19,9 @@ declaration: >-
# #
# Add as many "name+comment" entries as necessary # Add as many "name+comment" entries as necessary
# (or remove it altogether if you haven't collaborated with anyone) # (or remove it altogether if you haven't collaborated with anyone)
# collaboration: collaboration:
# - name: - name:
# comment: >- comment: >-
# Use this to list any code that you used that you did not write, # Use this to list any code that you used that you did not write,
# aside from code provided by the lecturer. Provide a comment # aside from code provided by the lecturer. Provide a comment
@ -30,10 +30,10 @@ declaration: >-
# #
# Add as many "url+licence+comment" entries as necessary # Add as many "url+licence+comment" entries as necessary
# (or remove it altogether if you haven't used any external code) # (or remove it altogether if you haven't used any external code)
# code: code:
# - comment: CSS styling for buttons + comboboxes - comment:
# url: http://fxexperience.com/2011/12/styling-fx-buttons-with-css/ url:
# licence: Public Domain licence:
# Use this to list any assets (artwork, sound, etc) that you used. # Use this to list any assets (artwork, sound, etc) that you used.
# Provide a comment explaining your use of that asset and the URL # Provide a comment explaining your use of that asset and the URL
@ -41,18 +41,18 @@ declaration: >-
# #
# Add as many "url+licence+comment" entries as necessary # Add as many "url+licence+comment" entries as necessary
# (or remove it altogether if you haven't used any external assets) # (or remove it altogether if you haven't used any external assets)
# assets: assets:
# - comment: - comment:
# url: url:
# licence: licence:
# Sign *your* name and uids here. (Remove entries if you have fewer # Sign *your* name and uids here. (Remove entries if you have fewer
# than three members.) # than three members.)
signatures: signatures:
- name: Nathan Woodburn
uid: u7156831
- name: - name:
uid: uid:
- name: Justin Ryu - name:
uid: u7492895 uid:
- name:
uid:

View File

@ -1,15 +1,21 @@
package comp1110.ass2; package comp1110.ass2;
import java.util.ArrayList; import java.lang.reflect.Array;
import java.util.Arrays; import java.util.*;
import java.util.HashSet;
import java.util.Set;
public class BlueLagoon { public class BlueLagoon {
// The Game Strings for five maps have been created for you. // The Game Strings for five maps have been created for you.
// They have only been encoded for two players. However, they are // They have only been encoded for two players. However, they are
// easily extendable to more by adding additional player statements. // easily extendable to more by adding additional player statements.
// region Game Strings
public static final String DEFAULT_GAME = "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;";
public static final String WHEELS_GAME = "a 13 2; c 0 E; i 5 0,1 0,2 0,3 0,4 1,1 1,5 2,0 2,5 3,0 3,6 4,0 4,5 5,1 5,5 6,1 6,2 6,3 6,4; i 5 0,8 0,9 0,10 1,8 1,11 2,7 2,11 3,8 3,11 4,8 4,9 4,10; i 7 8,8 8,9 8,10 9,8 9,11 10,7 10,11 11,8 11,11 12,8 12,9 12,10; i 7 10,0 10,1 10,4 10,5 11,0 11,2 11,3 11,4 11,6 12,0 12,1 12,4 12,5; i 9 2,2 2,3 3,2 3,4 4,2 4,3; i 9 2,9; i 9 6,6 6,7 6,8 6,9 6,10 6,11 7,6 8,0 8,1 8,2 8,3 8,4 8,5; i 9 10,9; s 0,1 0,4 0,10 2,2 2,3 2,9 2,11 3,0 3,2 3,4 3,6 4,2 4,3 4,10 6,1 6,4 6,6 6,11 8,0 8,5 8,8 8,10 10,0 10,5 10,7 10,9 10,11 11,3 12,1 12,4 12,8 12,10; 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;";
public static final String FACE_GAME = "a 13 2; c 0 E; i 6 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 0,10 0,11 1,0 1,12 2,0 2,11 3,0 3,12 4,0 4,11 5,0 5,12 6,0 6,11 7,0 7,12 8,0 8,11 9,0 9,12 10,0 10,11 11,0 11,12 12,0 12,1 12,2 12,3 12,4 12,5 12,6 12,7 12,8 12,9 12,10 12,11; i 6 2,4 2,5 2,6 2,7; i 9 4,4 4,5 4,6 4,7; i 9 6,5 6,6 7,5 7,7 8,5 8,6; i 12 2,2 3,2 3,3 4,2 5,2 5,3 6,2 7,2 7,3; i 12 2,9 3,9 3,10 4,9 5,9 5,10 6,9 7,9 7,10; i 12 9,2 9,10 10,2 10,3 10,4 10,5 10,6 10,7 10,8 10,9; s 0,3 0,8 1,0 1,12 2,2 2,4 2,7 2,9 4,2 4,5 4,6 4,9 5,0 5,12 6,2 6,5 6,6 6,9 8,0 8,5 8,6 8,11 9,2 9,10 10,3 10,5 10,6 10,8 11,0 11,12 12,4 12,7; 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;";
public static final String SIDES_GAME = "a 7 2; c 0 E; i 4 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 2,0 2,1 2,2 2,3 3,0 3,1 3,2 3,3 4,0 4,1 4,2 4,3 5,0 5,1 5,2 5,3 6,0 6,1 6,2 6,3; i 20 0,5 1,5 1,6 2,5 3,5 3,6 4,5 5,5 5,6 6,5; s 0,0 0,1 0,2 0,3 1,1 1,2 1,3 1,5 1,6 2,0 2,1 2,2 2,3 3,0 3,1 3,2 3,3 3,5 3,6 4,0 4,1 4,2 4,3 5,1 5,2 5,3 5,5 5,6 6,0 6,1 6,2 6,3; 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;";
public static final String SPACE_INVADERS_GAME = "a 23 2; c 0 E; i 6 0,2 0,7 1,3 1,7 2,2 2,3 2,4 2,5 2,6 2,7 3,2 3,4 3,5 3,6 3,8 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9 5,0 5,1 5,3 5,4 5,5 5,6 5,7 5,9 5,10 6,0 6,2 6,7 6,9 7,3 7,4 7,6 7,7; i 6 0,14 0,19 1,15 1,19 2,14 2,15 2,16 2,17 2,18 2,19 3,14 3,16 3,17 3,18 3,20 4,12 4,13 4,14 4,15 4,16 4,17 4,18 4,19 4,20 4,21 5,12 5,13 5,15 5,16 5,17 5,18 5,19 5,21 5,22 6,12 6,14 6,19 6,21 7,15 7,16 7,18 7,19; i 6 17,9 18,8 18,9 19,6 19,7 19,8 19,9 19,10 19,11 19,12 20,5 20,6 20,7 20,8 20,9 20,10 20,11 20,12 21,5 21,6 21,7 21,8 21,9 21,10 21,11 21,12 21,13 22,5 22,6 22,7 22,8 22,9 22,10 22,11 22,12; i 8 12,3 12,5 13,3 13,4 13,5 13,6 14,1 14,2 14,3 14,4 14,5 15,1 15,2 15,3 16,1 16,2; i 8 12,17 12,18 12,19 13,17 13,18 13,19 13,20 14,17 14,18 14,19 14,20 15,19 15,20 15,21 16,19 16,20; i 8 13,14 14,13 14,14 15,13 15,14 15,15 16,13 16,14; i 8 14,7 15,7 15,8 16,7; i 10 8,9 9,9 10,9 11,9; i 10 8,12 9,13 10,12 11,13; i 10 9,1 10,1 11,1 12,1; i 10 9,22 10,21 11,22 12,21; i 10 13,10 14,10 15,10; i 10 17,0 18,0 19,0 20,0; i 10 17,16 18,16 19,16 20,16; s 0,2 0,7 0,14 0,19 3,5 3,17 6,0 6,9 6,12 6,21 7,4 7,6 7,16 7,18 11,9 11,13 12,1 12,19 12,21 13,10 15,2 15,8 15,14 15,20 17,9 18,8 18,9 20,0 20,16 21,6 21,9 21,12; 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;";
// endregion
// region Checks on strings // region Checks on strings
/** /**
* Check if the string encoding of the game state is well-formed. * Check if the string encoding of the game state is well-formed.
@ -38,27 +44,26 @@ public class BlueLagoon {
// For the stonesStatement use the following regex string // For the stonesStatement use the following regex string
matchArray[3] = "(s (\\d{1,2},\\d{1,2} )+\\d{1,2},\\d{1,2}; )"; matchArray[3] = "(s (\\d{1,2},\\d{1,2} )+\\d{1,2},\\d{1,2}; )";
// For the resources and statuettes use the following regex string // For the resources and statuettes use the following regex string
matchArray[4] = "r C (\\d{1,2},\\d{1,2} )*B (\\d{1,2},\\d{1,2} )*W (\\d{1,2},\\d{1,2} )*P (\\d{1,2},\\d{1,2} " + matchArray[4] = "r C (\\d{1,2},\\d{1,2} )*B (\\d{1,2},\\d{1,2} )*W (\\d{1,2},\\d{1,2} )*P (\\d{1,2},\\d{1,2} )*S( \\d{1,2},\\d{1,2})*;";
")*S( \\d{1,2},\\d{1,2})*;";
// For the playersStatement use the following regex string // For the playersStatement use the following regex string
matchArray[5] = "( p \\d \\d{1,3} \\d{1,2} \\d{1,2} \\d{1,2} \\d{1,2} \\d{1,2} S (\\d{1,2},\\d{1,2} )*T( " + matchArray[5] = "( p \\d \\d{1,3} \\d{1,2} \\d{1,2} \\d{1,2} \\d{1,2} \\d{1,2} S (\\d{1,2},\\d{1,2} )*T( (\\d{1,2},\\d{1,2} ?)*)?;)*";
"(\\d{1,2},\\d{1,2} ?)*)?;)*";
// Combine the regex strings into one string to match the state string // Combine the regex strings into one string to match the state string
StringBuilder matchString = new StringBuilder(); String matchString = "";
for (String match:matchArray) { for (String match:matchArray) {
matchString.append(match); matchString += match;
} }
// Check if the state string matches the regex string // Check if the state string matches the regex string
if (!stateString.matches(matchString.toString())) return false; if (!stateString.matches(matchString)) return false;
// Check that there is one and only one of each player id // Check that there is one and only one of each player id
// This fixed test 2-3 of D2DTests.testIsStateStringWellFormed // This fixed test 2-3 of D2DTests.testIsStateStringWellFormed
int numPlayers = Character.getNumericValue(stateString.charAt(stateString.indexOf(";")-1)); int numPlayers = Integer.parseInt(stateString.substring(stateString.indexOf(";") - 1, stateString.indexOf(";")));
for (int i = 0; i < numPlayers; i++) { for (int i = 0; i < numPlayers; i++) {
if (stateString.length() - stateString.replaceAll("p "+i,"").length() != 3) return false; if (stateString.length() - stateString.replaceAll("p "+i,"").length() != 3) return false;
} }
return true; return true;
} }
@ -150,9 +155,9 @@ public class BlueLagoon {
// Coords of the island tiles // Coords of the island tiles
ArrayList<String> coordsContainer = new ArrayList<>(); ArrayList<String> coordsContainer = new ArrayList<>();
int numberOfPlayer; // Number of player int numberOfPlayer = 0; // Number of player
String playerId = ""; // Player ID String playerId = ""; // Player ID
String pStatePlayerId; // the current Player's move ID String pStatePlayerId = ""; // the current Player's move ID
ArrayList<String> settlerCoords = new ArrayList<>(); // Placed Settler Coordinates ArrayList<String> settlerCoords = new ArrayList<>(); // Placed Settler Coordinates
ArrayList<String> villageCoords = new ArrayList<>(); // Placed villages coordinates ArrayList<String> villageCoords = new ArrayList<>(); // Placed villages coordinates
ArrayList<String> playerSettlerCoords = new ArrayList<>(); // The current Player's settler coords ArrayList<String> playerSettlerCoords = new ArrayList<>(); // The current Player's settler coords
@ -329,12 +334,18 @@ public class BlueLagoon {
Set<String> allMoves = new HashSet<>(); Set<String> allMoves = new HashSet<>();
// Calculate number of pieces each player starts with // Calculate number of pieces each player starts with
int startNumSettlers = switch (numPlayers) { int startNumSettlers = 0;
case 2 -> 30; switch (numPlayers) {
case 3 -> 25; case 2:
case 4 -> 20; startNumSettlers = 30;
default -> 0; break;
}; case 3:
startNumSettlers = 25;
break;
case 4:
startNumSettlers = 20;
break;
}
// Check if the player has placed all their settlers or villages // Check if the player has placed all their settlers or villages
@ -413,7 +424,7 @@ public class BlueLagoon {
// if the settler is not adjacent with any of the pieces return false // if the settler is not adjacent with any of the pieces return false
if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) { if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) {
// Add the move to the set // Add the move to the set
allMoves.add("S " + cord); if (hasSettler) allMoves.add("S " + cord);
} }
} }
} }
@ -453,9 +464,9 @@ public class BlueLagoon {
State state = new State(stateString); State state = new State(stateString);
char pieceType = moveString.charAt(0); char pieceType = moveString.charAt(0);
String coordStr = moveString.substring(2); String coordStr = moveString.substring(2);
int y = Integer.parseInt(coordStr.split(",")[0]); int x = Integer.parseInt(coordStr.split(",")[0]);
int x = Integer.parseInt(coordStr.split(",")[1]); int y = Integer.parseInt(coordStr.split(",")[1]);
Coord coord = new Coord(y, x); Coord coord = new Coord(x, y);
state.placePiece(coord, pieceType); state.placePiece(coord, pieceType);
return state.toString(); return state.toString();
} }
@ -477,6 +488,10 @@ public class BlueLagoon {
* the score for each player * the score for each player
*/ */
//"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;"
// "p 1 42 1 2 3 4 5 S 5,6 8,7 T 1,2;"
public static int[] calculateTotalIslandsScore(String stateString) { public static int[] calculateTotalIslandsScore(String stateString) {
State state = new State(stateString); State state = new State(stateString);
int[] scores = new int[state.getNumPlayers()]; int[] scores = new int[state.getNumPlayers()];
@ -508,11 +523,9 @@ public class BlueLagoon {
public static int[] calculateIslandLinksScore(String stateString){ public static int[] calculateIslandLinksScore(String stateString){
State state = new State(stateString); State state = new State(stateString);
int[] scores = new int[state.getNumPlayers()]; int[] scores = new int[state.getNumPlayers()];
for (int i = 0; i < state.getNumPlayers(); i++) { for (int i = 0; i < state.getNumPlayers(); i++) {
scores[i] = state.scoreLinks(i); scores[i] = state.scoreLinks(i);
} }
return scores; return scores;
} }
@ -625,7 +638,6 @@ public class BlueLagoon {
return state.toString(); return state.toString();
} }
// 2 phases, exploration and settlement
/** /**
* Given a state string and a move string, apply the move to the board. * Given a state string and a move string, apply the move to the board.
* <p> * <p>
@ -639,48 +651,24 @@ public class BlueLagoon {
* @return a string representing the new state after the move is applied to the board * @return a string representing the new state after the move is applied to the board
*/ */
public static String applyMove(String stateString, String moveString){ public static String applyMove(String stateString, String moveString){
State state = new State(stateString); State state = new State(stateString);
char pieceType = moveString.charAt(0); char pieceType = moveString.charAt(0);
String coordStr = moveString.substring(2); String coordStr = moveString.substring(2);
int y = Integer.parseInt(coordStr.split(",")[0]); int x = Integer.parseInt(coordStr.split(",")[0]);
int x = Integer.parseInt(coordStr.split(",")[1]); int y = Integer.parseInt(coordStr.split(",")[1]);
Coord coord = new Coord(y, x); Coord coord = new Coord(x, y);
state.placePiece(coord, pieceType);
// if the move is valid, place it
if ( isMoveValid(stateString, moveString)) state.placePiece(coord, pieceType);
// if the move ends the phase
if (state.isPhaseOver()){ if (state.isPhaseOver()){
state.scorePhase();
// Applying end of Phase rules
// For Exploration Phase
// Tally up the score, clean the board, distribute resources, change to next Phase
if (state.getCurrentPhase() == 'E') { if (state.getCurrentPhase() == 'E') {
state.scorePhase();
state.cleanBoard(); state.cleanBoard();
state.distributeResources(); state.distributeResources();
state.nextPhase();
}
// For Settlement Phase
// Tally up the score
else if (state.getCurrentPhase() == 'S') {
state.scorePhase();
} }
} }
// After the endPhase is over, move to the next player
state.nextPlayer(); state.nextPlayer();
// if the current player cannot play, go to the next player
int players = state.getNumPlayers();
// the case where there are multiple players, while the current player cannot play the move,
// move to the next player and skip each player once if that player cannot play a move.
while (!state.getCurrentPlayer().canPlay(state)) { while (!state.getCurrentPlayer().canPlay(state)) {
if (players == 1) break;
state.nextPlayer(); state.nextPlayer();
players--;
} }
return state.toString(); return state.toString();

View File

@ -1,46 +1,62 @@
package comp1110.ass2; package comp1110.ass2;
import java.util.ArrayList;
/** /**
* Object to store coordinates * Object to store coordinates
* This stores the x and y coordinates of a point * This stores the x and y coordinates of a point
*/ */
public record Coord(int y, int x) { public class Coord {
private int x;
private int y;
/** /**
* Constructor for the Coord object * Constructor for the Coord object
*
* @param x x coordinate * @param x x coordinate
* @param y y coordinate * @param y y coordinate
*/ */
public Coord { public Coord(int y, int x) {
this.x = x;
this.y = y;
} }
// region Getters and Setters // region Getters and Setters
/** /**
* Get the x coordinate * Get the x coordinate
*
* @return int x coordinate * @return int x coordinate
*/ */
@Override public int getX() {
public int x() {
return x; return x;
} }
/** /**
* Get the y coordinate * Get the y coordinate
*
* @return int y coordinate * @return int y coordinate
*/ */
@Override public int getY() {
public int y() {
return y; return y;
} }
/**
* Set the x coordinate
* @param x int x coordinate
*/
public void setX(int x) {
this.x = x;
}
/**
* Set the y coordinate
* @param y int y coordinate
*/
public void setY(int y) {
this.y = y;
}
// endregion // endregion
/** /**
* Check if two coordinates are equal * Check if two coordinates are equal
*
* @param coord Coord object to compare to * @param coord Coord object to compare to
* @return boolean true if equal, false otherwise * @return boolean true if equal, false otherwise
*/ */
@ -50,7 +66,6 @@ public record Coord(int y, int x) {
/** /**
* Check if two coordinates are adjacent (does not include diagonals) * Check if two coordinates are adjacent (does not include diagonals)
*
* @param coord Coord object to compare to * @param coord Coord object to compare to
*/ */
public boolean isAdjacent(Coord coord) { public boolean isAdjacent(Coord coord) {
@ -65,64 +80,18 @@ public record Coord(int y, int x) {
/** /**
* Check if two coordinates are adjacent (includes diagonals) * Check if two coordinates are adjacent (includes diagonals)
*
* @param coord Coord object to compare to * @param coord Coord object to compare to
*/ */
public boolean isAdjacentDiagonal(Coord coord) { public boolean isAdjacentDiagonal(Coord coord){
if (isAdjacent(coord)) return true; if (isAdjacent(coord)) return true;
if (this.x == coord.x - 1 && this.y == coord.y - 1) return true;
// Consider hex offsets if (this.x == coord.x - 1 && this.y == coord.y + 1) return true;
if (y%2 == 0){ if (this.x == coord.x + 1 && this.y == coord.y - 1) return true;
if (this.x == coord.x && this.y == coord.y + 1) return true; return (this.x == coord.x + 1 && this.y == coord.y + 1);
if (this.x == coord.x && this.y == coord.y - 1) return true;
if (this.x == coord.x - 1 && this.y == coord.y + 1) return true;
return (this.x == coord.x - 1 && this.y == coord.y - 1);
}
else {
if (this.x == coord.x && this.y == coord.y + 1) return true;
if (this.x == coord.x && this.y == coord.y - 1) return true;
if (this.x == coord.x + 1 && this.y == coord.y + 1) return true;
return (this.x == coord.x + 1 && this.y == coord.y - 1);
}
} }
/**
* @param player Player to check owned by
* @return ArrayList of adjacent coordinates
*/
public Coord[] getClaimedAdjacent(Player player) {
Coord[] adjacent = new Coord[8];
adjacent[0] = new Coord(this.y, this.x + 1);
adjacent[1] = new Coord(this.y, this.x - 1);
adjacent[2] = new Coord(this.y + 1, this.x);
adjacent[3] = new Coord(this.y - 1, this.x);
adjacent[4] = new Coord(this.y + 1, this.x + 1);
adjacent[5] = new Coord(this.y + 1, this.x - 1);
adjacent[6] = new Coord(this.y - 1, this.x + 1);
adjacent[7] = new Coord(this.y - 1, this.x - 1);
Coord[] adjacentOwned = new Coord[8];
int ownedCount = 0;
for (Coord c : player.getPieces()) {
for (int i = 0; i < adjacent.length; i++) {
if (c.equals(adjacent[i])) {
adjacentOwned[i] = c;
ownedCount++;
}
}
}
Coord[] owned = new Coord[ownedCount];
for (int i = 0; i < ownedCount; i++) {
owned[i] = adjacentOwned[i];
}
return adjacentOwned;
}
/** /**
* Get a string representation of the coordinate (y,x) * Get a string representation of the coordinate (y,x)
*
* @return String representation of the coordinate * @return String representation of the coordinate
*/ */
@Override @Override

View File

@ -1,45 +0,0 @@
package comp1110.ass2;
public class GameData {
public static final String DEFAULT_GAME = "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;";
public static final String WHEELS_GAME = "a 13 2; c 0 E; i 5 0,1 0,2 0,3 0,4 1,1 1,5 2,0 2,5 3,0 3,6 4,0 4,5 5,1 " +
"5,5 6,1 6,2 6,3 6,4; i 5 0,8 0,9 0,10 1,8 1,11 2,7 2,11 3,8 3,11 4,8 4,9 4,10; i 7 8,8 8,9 8,10 9,8 " +
"9,11 10,7 10,11 11,8 11,11 12,8 12,9 12,10; i 7 10,0 10,1 10,4 10,5 11,0 11,2 11,3 11,4 11,6 12,0 12,1 " +
"12,4 12,5; i 9 2,2 2,3 3,2 3,4 4,2 4,3; i 9 2,9; i 9 6,6 6,7 6,8 6,9 6,10 6,11 7,6 8,0 8,1 8,2 8,3 8,4 " +
"8,5; i 9 10,9; s 0,1 0,4 0,10 2,2 2,3 2,9 2,11 3,0 3,2 3,4 3,6 4,2 4,3 4,10 6,1 6,4 6,6 6,11 8,0 8,5 8,8 " +
"8,10 10,0 10,5 10,7 10,9 10,11 11,3 12,1 12,4 12,8 12,10; 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;";
public static final String FACE_GAME = "a 13 2; c 0 E; i 6 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 0,10 0,11 " +
"1,0 1,12 2,0 2,11 3,0 3,12 4,0 4,11 5,0 5,12 6,0 6,11 7,0 7,12 8,0 8,11 9,0 9,12 10,0 10,11 11,0 11,12 " +
"12,0 12,1 12,2 12,3 12,4 12,5 12,6 12,7 12,8 12,9 12,10 12,11; i 6 2,4 2,5 2,6 2,7; i 9 4,4 4,5 4,6 4,7; " +
"i 9 6,5 6,6 7,5 7,7 8,5 8,6; i 12 2,2 3,2 3,3 4,2 5,2 5,3 6,2 7,2 7,3; i 12 2,9 3,9 3,10 4,9 5,9 5,10 " +
"6,9 7,9 7,10; i 12 9,2 9,10 10,2 10,3 10,4 10,5 10,6 10,7 10,8 10,9; s 0,3 0,8 1,0 1,12 2,2 2,4 2,7 " +
"2,9 4,2 4,5 4,6 4,9 5,0 5,12 6,2 6,5 6,6 6,9 8,0 8,5 8,6 8,11 9,2 9,10 10,3 10,5 10,6 10,8 11,0 11,12 " +
"12,4 12,7; 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;";
public static final String SIDES_GAME = "a 7 2; c 0 E; i 4 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 2,0 2,1 2,2 2,3 " +
"3,0 3,1 3,2 3,3 4,0 4,1 4,2 4,3 5,0 5,1 5,2 5,3 6,0 6,1 6,2 6,3; i 20 0,5 1,5 1,6 2,5 3,5 3,6 4,5 " +
"5,5 5,6 6,5; s 0,0 0,1 0,2 0,3 1,1 1,2 1,3 1,5 1,6 2,0 2,1 2,2 2,3 3,0 3,1 3,2 3,3 3,5 3,6 4,0 4,1 " +
"4,2 4,3 5,1 5,2 5,3 5,5 5,6 6,0 6,1 6,2 6,3; 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;";
public static final String SPACE_INVADERS_GAME = "a 23 2; c 0 E; i 6 0,2 0,7 1,3 1,7 2,2 2,3 2,4 2,5 2,6 2,7 " +
"3,2 3,4 3,5 3,6 3,8 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9 5,0 5,1 5,3 5,4 5,5 5,6 5,7 5,9 5,10 6,0 " +
"6,2 6,7 6,9 7,3 7,4 7,6 7,7; i 6 0,14 0,19 1,15 1,19 2,14 2,15 2,16 2,17 2,18 2,19 3,14 3,16 3,17 3,18 " +
"3,20 4,12 4,13 4,14 4,15 4,16 4,17 4,18 4,19 4,20 4,21 5,12 5,13 5,15 5,16 5,17 5,18 5,19 5,21 5,22 " +
"6,12 6,14 6,19 6,21 7,15 7,16 7,18 7,19; i 6 17,9 18,8 18,9 19,6 19,7 19,8 19,9 19,10 19,11 19,12 20,5 " +
"20,6 20,7 20,8 20,9 20,10 20,11 20,12 21,5 21,6 21,7 21,8 21,9 21,10 21,11 21,12 21,13 22,5 22,6 22,7 " +
"22,8 22,9 22,10 22,11 22,12; i 8 12,3 12,5 13,3 13,4 13,5 13,6 14,1 14,2 14,3 14,4 14,5 15,1 15,2 15,3 " +
"16,1 16,2; i 8 12,17 12,18 12,19 13,17 13,18 13,19 13,20 14,17 14,18 14,19 14,20 15,19 15,20 15,21 16,19 " +
"16,20; i 8 13,14 14,13 14,14 15,13 15,14 15,15 16,13 16,14; i 8 14,7 15,7 15,8 16,7; i 10 8,9 9,9 10,9 " +
"11,9; i 10 8,12 9,13 10,12 11,13; i 10 9,1 10,1 11,1 12,1; i 10 9,22 10,21 11,22 12,21; i 10 13,10 14,10 " +
"15,10; i 10 17,0 18,0 19,0 20,0; i 10 17,16 18,16 19,16 20,16; s 0,2 0,7 0,14 0,19 3,5 3,17 6,0 6,9 6,12 " +
"6,21 7,4 7,6 7,16 7,18 11,9 11,13 12,1 12,19 12,21 13,10 15,2 15,8 15,14 15,20 17,9 18,8 18,9 20,0 20,16 " +
"21,6 21,9 21,12; 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;";
}

View File

@ -24,7 +24,6 @@ public class Player {
private Coord[] settlers; private Coord[] settlers;
private Coord[] villages; private Coord[] villages;
private Coord lastMove; private Coord lastMove;
private Boolean isAI;
/** /**
* Constructor for Player class * Constructor for Player class
@ -41,7 +40,6 @@ public class Player {
this.numStatuette = 0; this.numStatuette = 0;
this.settlers = new Coord[0]; this.settlers = new Coord[0];
this.villages = new Coord[0]; this.villages = new Coord[0];
this.isAI = false;
lastMove = new Coord(-1, -1); lastMove = new Coord(-1, -1);
} }
// endregion // endregion
@ -77,14 +75,20 @@ public class Player {
* @return int number of the resource the player has * @return int number of the resource the player has
*/ */
public int getNumResource(char resourceType) { public int getNumResource(char resourceType) {
return switch (resourceType) { switch (resourceType) {
case 'C' -> numCoconuts; case 'C':
case 'B' -> numBamboo; return numCoconuts;
case 'W' -> numWater; case 'B':
case 'P' -> numPreciousStones; return numBamboo;
case 'S' -> numStatuette; case 'W':
default -> 0; return numWater;
}; case 'P':
return numPreciousStones;
case 'S':
return numStatuette;
default:
return 0;
}
} }
/** /**
@ -94,11 +98,21 @@ public class Player {
*/ */
public void addResource(int numResource, char resourceType) { public void addResource(int numResource, char resourceType) {
switch (resourceType) { switch (resourceType) {
case 'C' -> numCoconuts += numResource; case 'C':
case 'B' -> numBamboo += numResource; numCoconuts += numResource;
case 'W' -> numWater += numResource; break;
case 'P' -> numPreciousStones += numResource; case 'B':
case 'S' -> numStatuette += numResource; numBamboo += numResource;
break;
case 'W':
numWater += numResource;
break;
case 'P':
numPreciousStones += numResource;
break;
case 'S':
numStatuette += numResource;
break;
} }
} }
@ -137,7 +151,9 @@ public class Player {
*/ */
public void addSettler(Coord coord) { public void addSettler(Coord coord) {
Coord[] newSettlers = new Coord[settlers.length + 1]; Coord[] newSettlers = new Coord[settlers.length + 1];
System.arraycopy(settlers, 0, newSettlers, 0, settlers.length); for (int i = 0; i < settlers.length; i++) {
newSettlers[i] = settlers[i];
}
newSettlers[settlers.length] = coord; newSettlers[settlers.length] = coord;
settlers = newSettlers; settlers = newSettlers;
lastMove = coord; lastMove = coord;
@ -154,7 +170,9 @@ public class Player {
} }
Coord[] newVillages = new Coord[villages.length + 1]; Coord[] newVillages = new Coord[villages.length + 1];
System.arraycopy(villages, 0, newVillages, 0, villages.length); for (int i = 0; i < villages.length; i++) {
newVillages[i] = villages[i];
}
newVillages[villages.length] = coord; newVillages[villages.length] = coord;
villages = newVillages; villages = newVillages;
lastMove = coord; lastMove = coord;
@ -173,9 +191,9 @@ public class Player {
public void removeVillage(Coord coord) { public void removeVillage(Coord coord) {
Coord[] newVillages = new Coord[villages.length - 1]; Coord[] newVillages = new Coord[villages.length - 1];
int j = 0; int j = 0;
for (Coord village : villages) { for (int i = 0; i < villages.length; i++) {
if (village != coord) { if (villages[i] != coord) {
newVillages[j] = village; newVillages[j] = villages[i];
j++; j++;
} }
} }
@ -189,14 +207,18 @@ public class Player {
*/ */
public Coord[] getPieces() { public Coord[] getPieces() {
Coord[] pieces = new Coord[settlers.length + villages.length]; Coord[] pieces = new Coord[settlers.length + villages.length];
System.arraycopy(settlers, 0, pieces, 0, settlers.length); for (int i = 0; i < settlers.length; i++) {
System.arraycopy(villages, 0, pieces, settlers.length, villages.length); pieces[i] = settlers[i];
}
for (int i = 0; i < villages.length; i++) {
pieces[settlers.length + i] = villages[i];
}
return pieces; return pieces;
} }
/** /**
* Get number of pieces on island * Get number of pieces on island
* @param island Island to check * @param island Island island to check
* @return int number of pieces on island * @return int number of pieces on island
*/ */
public int getNumPiecesOnIsland(Island island) { public int getNumPiecesOnIsland(Island island) {
@ -209,31 +231,10 @@ public class Player {
return numPieces; return numPieces;
} }
/**
* Get the player's last move coord
* @return Coord last move coord
*/
public Coord getLastMove() { public Coord getLastMove() {
return lastMove; return lastMove;
} }
/**
* Set if the player is an AI
* @param ai boolean true if player is an AI, false otherwise
*/
public void setAI(boolean ai) {
this.isAI = ai;
}
/**
* Check if the player is an AI
* @return boolean true if player is an AI, false otherwise
*/
public boolean isAI() {
return isAI;
}
/** /**
* Check if player is able to do any moves * Check if player is able to do any moves
* @return true if player can do any moves, false otherwise * @return true if player can do any moves, false otherwise
@ -241,14 +242,15 @@ public class Player {
public boolean canPlay(State state) { public boolean canPlay(State state) {
// Check if the player has placed all their settlers or villages // Check if the player has placed all their settlers or villages
int numSettlers = 30 - ((state.getNumPlayers() - 2) * 5); int startNumSettlers = switch (state.getNumPlayers()) {
boolean hasSettler = (settlers.length < numSettlers); case 2 -> 30;
boolean hasVillage = (villages.length < 5); case 3 -> 25;
if (!hasSettler && !hasVillage) return false; case 4 -> 20;
// if (state.getCurrentPhase() != 'E'){ default -> 0;
// if (!hasSettler && !hasVillage) return false; };
// } boolean hasSettler = (settlers.length < startNumSettlers);
boolean hasVillage = (settlers.length < 5);
if (!hasSettler && !(hasVillage && state.getCurrentPhase() == 'E')) return false;
// Add used coords // Add used coords
ArrayList<String> settlerCoords = new ArrayList<>(); // Placed Settler Coordinates ArrayList<String> settlerCoords = new ArrayList<>(); // Placed Settler Coordinates
@ -264,6 +266,7 @@ public class Player {
villageCoords.add(c.toString()); villageCoords.add(c.toString());
} }
} }
for (Coord c: settlers){ for (Coord c: settlers){
playerSettlerCoords.add(c.toString()); playerSettlerCoords.add(c.toString());
} }
@ -303,13 +306,24 @@ public class Player {
else if(y > state.boardHeight - 1) continue; else if(y > state.boardHeight - 1) continue;
switch (state.getCurrentPhase()) { switch (state.getCurrentPhase()) {
case 'E' -> { case 'E' -> {
if (!islandCoords.contains(cord) && hasSettler) return true; if (!islandCoords.contains(cord)) {
if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) return true; 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 // Settlement Phase
case 'S' -> { case 'S' -> {
// if the settler is adjacent with any of the pieces return true // if the settler is not adjacent with any of the pieces return false
if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords)) && hasSettler) return true; if ((isAdjacent(cord, playerVillageCoords) || isAdjacent(cord, playerSettlerCoords))) {
// Add the move to the set
if (hasSettler) return true;
}
} }
} }
} }
@ -339,9 +353,9 @@ public class Player {
if (i == randomMove) { if (i == randomMove) {
char pieceType = move.charAt(0); char pieceType = move.charAt(0);
String coordStr = move.substring(2); String coordStr = move.substring(2);
int y = Integer.parseInt(coordStr.split(",")[0]); int x = Integer.parseInt(coordStr.split(",")[0]);
int x = Integer.parseInt(coordStr.split(",")[1]); int y = Integer.parseInt(coordStr.split(",")[1]);
Coord coord = new Coord(y, x); Coord coord = new Coord(x, y);
lastMove = coord; lastMove = coord;
state.placePiece(coord, pieceType); state.placePiece(coord, pieceType);
state.nextPlayer(); state.nextPlayer();
@ -385,22 +399,17 @@ public class Player {
/** /**
* Do a calculated move * Do a calculated move
*/ */
public Boolean doAIMove(State state){ public void doAIMove(State state){
String bestMove = createAIMove(state); String bestMove = createAIMove(state);
if (bestMove.equals("")){
System.out.println("No AI moves");
return false;
}
char pieceType = bestMove.charAt(0); char pieceType = bestMove.charAt(0);
String coordStr = bestMove.substring(2); String coordStr = bestMove.substring(2);
int y = Integer.parseInt(coordStr.split(",")[0]); int x = Integer.parseInt(coordStr.split(",")[0]);
int x = Integer.parseInt(coordStr.split(",")[1]); int y = Integer.parseInt(coordStr.split(",")[1]);
Coord coord = new Coord(y, x); Coord coord = new Coord(x, y);
lastMove = coord; lastMove = coord;
state.placePiece(coord, pieceType); state.placePiece(coord, pieceType);
state.nextPlayer(); state.nextPlayer();
return true;
} }
/** /**
@ -438,6 +447,7 @@ public class Player {
} }
// Check if adding this piece will make player have the most pieces on the island // Check if adding this piece will make player have the most pieces on the island
int ties = 0; int ties = 0;
int wins = 0;
int loses = 0; int loses = 0;
int myPieces = this.getNumPiecesOnIsland(island); int myPieces = this.getNumPiecesOnIsland(island);
for (int i = 0; i < state.getNumPlayers(); i++) { for (int i = 0; i < state.getNumPlayers(); i++) {
@ -447,6 +457,9 @@ public class Player {
int otherPlayerPieces = state.getPlayer(i).getNumPiecesOnIsland(island); int otherPlayerPieces = state.getPlayer(i).getNumPiecesOnIsland(island);
if (otherPlayerPieces > myPieces+1) { if (otherPlayerPieces > myPieces+1) {
loses++; loses++;
} else
if (otherPlayerPieces == myPieces) {
wins++;
} else if (otherPlayerPieces == myPieces + 1) { } else if (otherPlayerPieces == myPieces + 1) {
ties++; ties++;
} }
@ -467,10 +480,10 @@ public class Player {
if (currentIslandCount < 8 ){ if (currentIslandCount < 8 ){
// Check if there is an island adjacent to this move // Check if there is an island adjacent to this move
for (Island island : state.getIslands()) { for (Island island : state.getIslands()) {
if (island.containsCoord(new Coord(coord.x() + 1, coord.y())) if (island.containsCoord(new Coord(coord.getX() + 1, coord.getY()))
|| island.containsCoord(new Coord(coord.x() - 1, coord.y())) || island.containsCoord(new Coord(coord.getX() - 1, coord.getY()))
|| island.containsCoord(new Coord(coord.x(), coord.y() + 1)) || island.containsCoord(new Coord(coord.getX(), coord.getY() + 1))
|| island.containsCoord(new Coord(coord.x(), coord.y() - 1))) { || island.containsCoord(new Coord(coord.getX(), coord.getY() - 1))) {
score += 1; score += 1;
if (state.getCurrentPhase() == 'E'){ if (state.getCurrentPhase() == 'E'){
score += 4; score += 4;
@ -493,8 +506,8 @@ public class Player {
private int maxCol(Coord[] coords){ private int maxCol(Coord[] coords){
int maxCol = 0; int maxCol = 0;
for (Coord coord : coords) { for (Coord coord : coords) {
if (coord.x() > maxCol) { if (coord.getX() > maxCol) {
maxCol = coord.x(); maxCol = coord.getX();
} }
} }
return maxCol; return maxCol;
@ -532,7 +545,7 @@ public class Player {
*/ */
@Override @Override
public String toString() { public String toString() {
StringBuilder str = new StringBuilder("p " + playerID + " " + score + " " + numCoconuts + " " + numBamboo + " " + numWater + " " + numPreciousStones + " " + numStatuette + " S"); String str = "p " + playerID + " " + score + " " + numCoconuts + " " + numBamboo + " " + numWater + " " + numPreciousStones + " " + numStatuette + " S";
// Get the coords of the player's pieces in row major order // Get the coords of the player's pieces in row major order
@ -545,7 +558,7 @@ public class Player {
while (settlersCoords[settlers.length - 1] == null) { while (settlersCoords[settlers.length - 1] == null) {
for (Coord coord : settlers) { for (Coord coord : settlers) {
if (coord.x() == col && coord.y() == row) { if (coord.getX() == col && coord.getY() == row) {
settlersCoords[i] = coord; settlersCoords[i] = coord;
i++; i++;
} }
@ -567,7 +580,7 @@ public class Player {
while (villagesCoords[villages.length-1] == null){ while (villagesCoords[villages.length-1] == null){
for (Coord coord : villages) { for (Coord coord : villages) {
if (coord.x() == col && coord.y() == row) { if (coord.getX() == col && coord.getY() == row) {
villagesCoords[i] = coord; villagesCoords[i] = coord;
i++; i++;
} }
@ -584,13 +597,13 @@ public class Player {
for (Coord coord : settlersCoords) { for (Coord coord : settlersCoords) {
str.append(" ").append(coord.toString()); str += " " + coord.toString();
} }
str.append(" T"); str += " T";
for (Coord coord : villagesCoords) { for (Coord coord : villagesCoords) {
str.append(" ").append(coord.toString()); str += " " + coord.toString();
} }
return str.toString(); return str;
} }
} }

View File

@ -7,7 +7,7 @@ package comp1110.ass2;
*/ */
public class Resource { public class Resource {
private char type; private char type;
private final Coord coord; private Coord coord;
private boolean claimed; private boolean claimed;
/** /**
@ -35,14 +35,20 @@ public class Resource {
* @return String type of the resource * @return String type of the resource
*/ */
public String getTypeString() { public String getTypeString() {
return switch (type) { switch (type){
case 'C' -> "Coconut"; case 'C':
case 'B' -> "Bamboo"; return "Coconut";
case 'W' -> "Water"; case 'B':
case 'P' -> "Precious Stone"; return "Bamboo";
case 'S' -> "Statuette"; case 'W':
default -> "Invalid"; return "Water";
}; case 'P':
return "Precious Stone";
case 'S':
return "Statuette";
default:
return "Invalid";
}
} }
/** /**
@ -61,9 +67,17 @@ public class Resource {
this.type = type; this.type = type;
} }
/**
* Set the coordinate of the resource
* @param coord Coord coordinate of the resource
*/
public void setCoord(Coord coord) {
this.coord = coord;
}
/** /**
* Check if the resource is equal to another resource * Check if the resource is equal to another resource
* @param resource resource to be compared * @param resource Resource resource to be compared
* @return boolean true if the resources are equal * @return boolean true if the resources are equal
*/ */
public boolean equals(Resource resource) { public boolean equals(Resource resource) {
@ -82,8 +96,8 @@ public class Resource {
* Check if the resource has been claimed * Check if the resource has been claimed
* @return boolean true if the resource has been claimed * @return boolean true if the resource has been claimed
*/ */
public boolean isAvailable() { public boolean isClaimed() {
return !this.claimed; return this.claimed;
} }
@Override @Override

View File

@ -1,6 +1,8 @@
package comp1110.ass2; package comp1110.ass2;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/** /**
* Object to store the game state * Object to store the game state
@ -75,8 +77,9 @@ public class State {
islandcount++; islandcount++;
// Add the island to the array // Add the island to the array
Island[] tmpislands = new Island[islandcount]; Island[] tmpislands = new Island[islandcount];
if (islands != null) { for (int j=0; j<tmpislands.length-1; j++)
System.arraycopy(islands, 0, tmpislands, 0, tmpislands.length - 1); {
tmpislands[j] = islands[j];
} }
tmpislands[islandcount-1] = tmpIsland; tmpislands[islandcount-1] = tmpIsland;
islands = tmpislands; islands = tmpislands;
@ -149,7 +152,10 @@ public class State {
if (numPlayers >= maxPlayers) return; // There are already the maximum number of players if (numPlayers >= maxPlayers) return; // There are already the maximum number of players
Player[] oldPlayers = players; Player[] oldPlayers = players;
players = new Player[numPlayers+1]; players = new Player[numPlayers+1];
if (numPlayers >= 0) System.arraycopy(oldPlayers, 0, players, 0, numPlayers); for (int i=0; i<numPlayers; i++)
{
players[i] = oldPlayers[i];
}
players[numPlayers] = new Player(numPlayers); players[numPlayers] = new Player(numPlayers);
numPlayers++; numPlayers++;
} }
@ -175,7 +181,7 @@ public class State {
// Create a temporary array to store the shuffled stone circles // Create a temporary array to store the shuffled stone circles
Coord[] tempStoneCircleRandom = new Coord[32]; Coord[] tempStoneCircleRandom = new Coord[32];
// Create a list to store the used cords (to avoid duplicates) // Create a list to store the used cords (to avoid duplicates)
List<Coord> usedCords = new ArrayList<>(); List<Coord> usedCords = new ArrayList<Coord>();
// Shuffle the array // Shuffle the array
for (int j = 0; j < 32; j++) { for (int j = 0; j < 32; j++) {
@ -333,7 +339,9 @@ public class State {
} }
} }
Resource[] resources = new Resource[i]; Resource[] resources = new Resource[i];
System.arraycopy(tmpResources, 0, resources, 0, i); for (int j=0; j<i; j++) {
resources[j] = tmpResources[j];
}
return resources; return resources;
} }
@ -384,7 +392,7 @@ public class State {
// Claim resource if it is a stone circle // Claim resource if it is a stone circle
if (isStone(coord)) { if (isStone(coord)) {
for (Resource resource : resources) { for (Resource resource : resources) {
if (resource.getCoord().equals(coord) && resource.isAvailable()) { if (resource.getCoord().equals(coord) && !resource.isClaimed()) {
players[currentPlayer].addResource(1, resource.getType()); players[currentPlayer].addResource(1, resource.getType());
resource.setClaimed(); resource.setClaimed();
} }
@ -406,21 +414,31 @@ public class State {
/** /**
* is the phase over? * is the phase over?
* Defaults to simple mode
*/ */
public boolean isPhaseOver() { public boolean isPhaseOver() {
return isPhaseOver(true);
}
/**
* is the phase over?
* @param simple boolean don't check all player moves
*/
public boolean isPhaseOver(boolean simple){
boolean resourcesLeft = false; boolean resourcesLeft = false;
for (Resource r : resources) { for (Resource r : resources) {
if (r.isAvailable() && r.getType() != 'S') resourcesLeft = true; if (!r.isClaimed() && r.getType() != 'S') resourcesLeft = true;
} }
boolean moveLeft = false;
int numSettlers = 30 - ((numPlayers - 2) * 5);
for (Player p: players) {
boolean canPlay = p.getVillages().length < 5;
if (p.getSettlers().length < numSettlers) canPlay = true;
if (canPlay) {
if (p.canPlay(this)) moveLeft = true;
}
boolean moveLeft = false;
if (simple) {
if (getCurrentPlayer().canPlay(this)) moveLeft = true;
}
else
{
for (Player player : players) {
if (player.canPlay(this)) moveLeft = true;
}
} }
return !resourcesLeft || !moveLeft; return !resourcesLeft || !moveLeft;
} }
@ -550,100 +568,215 @@ public class State {
* @return int score * @return int score
*/ */
// public boolean isAdjacent(Coord coord) {
// if (this.y == coord.y) {
// return (this.x == coord.x - 1 || this.x == coord.x + 1);
// }
// if (this.x == coord.x) {
// return (this.y == coord.y - 1 || this.y == coord.y + 1);
// }
// return false;
// }
//
// /**
// * Check if two coordinates are adjacent (includes diagonals)
// * @param coord Coord object to compare to
// */
// public boolean isAdjacentDiagonal(Coord coord){
// if (isAdjacent(coord)) return true;
// if (this.x == coord.x - 1 && this.y == coord.y - 1) return true;
// if (this.x == coord.x - 1 && this.y == coord.y + 1) return true;
// if (this.x == coord.x + 1 && this.y == coord.y - 1) return true;
// return (this.x == coord.x + 1 && this.y == coord.y + 1);
// }
// public Player(int playerID) {
// this.playerID = playerID;
// this.score = 0;
// this.numCoconuts = 0;
// this.numBamboo = 0;
// this.numWater = 0;
// this.numPreciousStones = 0;
// this.numStatuette = 0;
// this.settlers = new Coord[0];
// this.villages = new Coord[0];
// }
// // endregion
// // region Getters and Setters
//
// /**
// * Get the player's ID
// * @return int player ID
// */
// public int getPlayerID() {
// return playerID;
// }
// /**
// * Get the player's settlers
// * @return Coord[] list of the player's settlers coords
// */
// public Coord[] getSettlers() {
// return settlers;
// }
//
// /**
// * Get the player's villages
// * @return Coord[] list of the player's villages coords
// */
// public Coord[] getVillages() {
// return villages;
// }
// public Coord[] getPieces() {
// Coord[] pieces = new Coord[settlers.length + villages.length];
// for (int i = 0; i < settlers.length; i++) {
// pieces[i] = settlers[i];
// }
// for (int i = 0; i < villages.length; i++) {
// pieces[settlers.length + i] = villages[i];
// }
// return pieces;
// }
//
// /**
// * Get number of pieces on island
// * @param island Island island to check
// * @return int number of pieces on island
// */
// public int getNumPiecesOnIsland(Island island) {
// int numPieces = 0;
// for (Coord piece : getPieces()) {
// if (island.containsCoord(piece)) {
// numPieces++;
// }
// }
// return numPieces;
// }
// /**
// * Get the coordinates of the island
// * @return Coord[] coordinates of the island
// */
// public Coord[] getCoords() {
// return coords;
// }
// /**
// * Check if the island contains a coordinate
// * @param coord the coordinate to be checked
// * @return boolean true if the island contains the coordinate
// */
// public boolean containsCoord(Coord coord) {
// for (Coord c : this.coords) {
// if (c.equals(coord)) {
// return true;
// }
// }
// return false;
// }
public int scoreLinks(int playerID) { public int scoreLinks(int playerID) {
Coord[] playerCoords = players[playerID].getPieces(); int maxIslands = 0;
Set<Coord> playerCoordsSet = new HashSet<>(Arrays.asList(playerCoords)); int distinctIslandsCounter = 0;
return findLongestLinkScore(playerCoordsSet, islands); int score = 0;
}
/** Coord[] playerCoords = players[playerID].getPieces(); // playerCoords
* Combines both the helper methods `DFSRecurssionLink` and `findScoreForLink` to find the link set that has the
* highest score from a set all Player's pieces coordinates
* @param allCoords all coordinates of a Player's pieces
* @param islands the set of islands from the generated world
* @return the highest score from a player's linked pieces
*/
public static int findLongestLinkScore ( Set<Coord> allCoords, Island[] islands) {
Set<Coord> longestPath = new HashSet<>(); // Container for Longest Path
Set<Coord> currentPath = new HashSet<>(); // Container for Current Path
Coord startingPoint; // Starting point of the DFS Algo for(Island island : islands) {
int maxScore = findScoreForLink(longestPath,islands); Coord[] islandCoords = island.getCoords();
for(Island i : islands) { for ( Coord playerCoord : playerCoords ) {
for ( Coord c : allCoords) { if (island.containsCoord(playerCoord)) {
if(i.containsCoord(c)) {
startingPoint = c; // set starting Point with the current Coords in the Player's all pieces coords
// DFS Algo starts
currentPath.add(startingPoint);
DFSRecursionLink(allCoords, currentPath, longestPath, startingPoint);
// if the score is bigger, update the Set
if(findScoreForLink(currentPath, islands) > maxScore) {
longestPath.clear();
longestPath.addAll(currentPath);
maxScore = findScoreForLink(longestPath, islands);
}
// clear the currentPath as well
currentPath = new HashSet<>();
} }
} }
} }
return maxScore; // for(Island island : islands) {
// Coord[] islandCoords = island.getCoords(); // Island Coords
//
// for ( Coord playerCoord : playerCoords) {
// for( Coord islandCoord : islandCoords) {
// if (playerCoord.areTwoCoordsLink(islandCoord) && )
// }
// }
//
// }
return maxIslands * 5; //! TODO
} }
/** // public boolean areTwoCoordsLink ( Coord coord) {
* a DFS (Depth-First-Search) Recursion algorithm that traverses all the coordinates of a player's pieces, with // if(isAdjacent(coord) || isAdjacentDiagonal(coord)) return true;
* each step of the DFS algorithm determined if the current coordinate is adjacent with other coordinates // else return false;
* within the allCoords set and wether or not the currentPath is contained inside the allCoords. // }
* @param allCoords all coordinates of a Player's pieces //
* @param currentPath a set to store the path that the algorithm has gone through // public Coord[] longestLink ( Coord coord) {
* @param longestPath a set to store the longest path // ArrayList<Coord> linkContainer = new ArrayList<>();
* @param startingPoint the starting point of each step of DFS // if (areTwoCoordsLink(coord)) {
*/ // linkContainer.add(coord);
public static void DFSRecursionLink(Set<Coord> allCoords, Set<Coord> currentPath, Set<Coord> longestPath, Coord startingPoint) { // }
//
// Coord[] endLinkContainer = new Coord[linkContainer.size()];
// return endLinkContainer;
// }
// Add the staring point to the `history` of the path that has been taken by the algorithm // Score Links
currentPath.add(startingPoint); // * A (potentially) branching path of neighbouring settlers and villages
// * belonging to a player forms a chain. Players earn points from the chain
// * of their pieces which links the most islands. Players earn 5 points
// * per linked island in this chain.
for(Coord c : allCoords) { // public int scoreTotalIslands(int playerID) {
// int score = 0;
// int islandCount = 0;
// for (Island island : islands) {
// // Get island coords
// Coord[] islandCoords = island.getCoords();
// // Get player's coords
// Coord[] playerCoords = players[playerID].getPieces();
// // Check if player has a piece on the island
// boolean hasPiece = false;
// for (Coord playerCoord : playerCoords) {
// for (Coord islandCoord : islandCoords) {
// if (playerCoord.equals(islandCoord)) {
// hasPiece = true;
// break;
// }
// }
// if (hasPiece) break;
// }
// if (hasPiece) islandCount++;
// }
//
// if (islandCount >= 8) score = 20;
// else if (islandCount == 7) score = 10;
//
// return score;
// }
// if the startingPoint of the step is adjacent with the coords from allCoords and currentPath // public boolean isAdjacent(Coord coord) {
// i.e. the `history tracker` of the path so far does not have `c` coords from allCoords // if (this.y == coord.y) {
if( startingPoint.isAdjacentDiagonal(c) && !currentPath.contains(c) ) { // return (this.x == coord.x - 1 || this.x == coord.x + 1);
// }
// if the currentPath is bigger than the longestPath, update the longest Path // if (this.x == coord.x) {
if (currentPath.size() > longestPath.size() ) longestPath = currentPath; // return (this.y == coord.y - 1 || this.y == coord.y + 1);
// }
DFSRecursionLink(allCoords, currentPath, longestPath, c); // Repeat the step for all coords // return false;
} // }
} //
} // /**
// * Check if two coordinates are adjacent (includes diagonals)
/** // * @param coord Coord object to compare to
* Helper method for finding the scores for a link set. This helper method is purely to help the // */
* `findLongestLink` method. // public boolean isAdjacentDiagonal(Coord coord){
* @param longestLink the link that wants to be searched the score for // if (isAdjacent(coord)) return true;
* @param islands the islands within the world // if (this.x == coord.x - 1 && this.y == coord.y - 1) return true;
* @return the score of the link set // if (this.x == coord.x - 1 && this.y == coord.y + 1) return true;
*/ // if (this.x == coord.x + 1 && this.y == coord.y - 1) return true;
public static int findScoreForLink(Set<Coord> longestLink, Island[] islands) { // return (this.x == coord.x + 1 && this.y == coord.y + 1);
Set<Island> connectedIslands = new HashSet<>(); // Container for connected Islands within the longest Link set // }
for (Coord c : longestLink) {
for (Island i : islands) {
if (i.containsCoord(c)) {
connectedIslands.add(i);
break;
}
}
}
return connectedIslands.size() * 5;
}
/** /**
* Score resources * Score resources
@ -692,29 +825,42 @@ public class State {
*/ */
@Override @Override
public String toString() { public String toString() {
StringBuilder str = new StringBuilder("a " + boardHeight + " " + getNumPlayers() + "; c " + getCurrentPlayerID() + " " + getCurrentPhase() + "; "); String str = "a " + boardHeight + " " + getNumPlayers() + "; c " + getCurrentPlayerID() + " " + getCurrentPhase() + "; ";
for (Island island : islands) { for (Island island : islands) {
str.append(island.toString()).append(" "); str += island.toString() + " ";
} }
str.append("s"); str += "s";
for (Coord s: stonesCoords) { for (Coord s: stonesCoords) {
str.append(" ").append(s.toString()); str += " " + s.toString();
} }
str.append("; r"); str += "; r";
char[] types = {'C', 'B', 'W', 'P', 'S'}; char[] types = {'C', 'B', 'W', 'P', 'S'};
for (char type : types) { for (char type : types) {
str.append(" ").append(type); str += " " + type;
for (Resource resource : resources) { for (Resource resource : resources) {
if (resource.getType() == type && resource.isAvailable()) str.append(" ").append(resource.getCoord().toString()); if (resource.getType() == type && !resource.isClaimed()) str += " " + resource.getCoord().toString();
} }
} }
str.append(";"); str += ";";
for (Player player : players) { for (Player player : players) {
str.append(" ").append(player.toString()).append(";"); str += " " + player.toString() + ";";
} }
return str.toString(); return str;
} }
/**
* Get a string representation of the score of each player
* @return String scoreString
*/
public String scoreString() {
String str = "";
for (Player player : players) {
str += "Player " + player.getPlayerID() + "'s score is " + player.getScore() + "\n";
}
return str.substring(0, str.length() - 1);
}
// endregion // endregion
} }

View File

@ -10,9 +10,6 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@ -24,8 +21,6 @@ import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.Objects;
public class Game extends Application { public class Game extends Application {
// region Variables // region Variables
@ -37,10 +32,12 @@ public class Game extends Application {
String message; String message;
Boolean messageError; Boolean messageError;
Coord selectedTile; Coord selectedTile;
int AI; Boolean AI;
private final String DEFAULT_GAME = "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;";
Boolean darkMode = false; private final String WHEELS_GAME = "a 13 2; c 0 E; i 5 0,1 0,2 0,3 0,4 1,1 1,5 2,0 2,5 3,0 3,6 4,0 4,5 5,1 5,5 6,1 6,2 6,3 6,4; i 5 0,8 0,9 0,10 1,8 1,11 2,7 2,11 3,8 3,11 4,8 4,9 4,10; i 7 8,8 8,9 8,10 9,8 9,11 10,7 10,11 11,8 11,11 12,8 12,9 12,10; i 7 10,0 10,1 10,4 10,5 11,0 11,2 11,3 11,4 11,6 12,0 12,1 12,4 12,5; i 9 2,2 2,3 3,2 3,4 4,2 4,3; i 9 2,9; i 9 6,6 6,7 6,8 6,9 6,10 6,11 7,6 8,0 8,1 8,2 8,3 8,4 8,5; i 9 10,9; s 0,1 0,4 0,10 2,2 2,3 2,9 2,11 3,0 3,2 3,4 3,6 4,2 4,3 4,10 6,1 6,4 6,6 6,11 8,0 8,5 8,8 8,10 10,0 10,5 10,7 10,9 10,11 11,3 12,1 12,4 12,8 12,10; 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;";
private final String FACE_GAME = "a 13 2; c 0 E; i 6 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 0,10 0,11 1,0 1,12 2,0 2,11 3,0 3,12 4,0 4,11 5,0 5,12 6,0 6,11 7,0 7,12 8,0 8,11 9,0 9,12 10,0 10,11 11,0 11,12 12,0 12,1 12,2 12,3 12,4 12,5 12,6 12,7 12,8 12,9 12,10 12,11; i 6 2,4 2,5 2,6 2,7; i 9 4,4 4,5 4,6 4,7; i 9 6,5 6,6 7,5 7,7 8,5 8,6; i 12 2,2 3,2 3,3 4,2 5,2 5,3 6,2 7,2 7,3; i 12 2,9 3,9 3,10 4,9 5,9 5,10 6,9 7,9 7,10; i 12 9,2 9,10 10,2 10,3 10,4 10,5 10,6 10,7 10,8 10,9; s 0,3 0,8 1,0 1,12 2,2 2,4 2,7 2,9 4,2 4,5 4,6 4,9 5,0 5,12 6,2 6,5 6,6 6,9 8,0 8,5 8,6 8,11 9,2 9,10 10,3 10,5 10,6 10,8 11,0 11,12 12,4 12,7; 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;";
private final String SIDES_GAME = "a 7 2; c 0 E; i 4 0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 2,0 2,1 2,2 2,3 3,0 3,1 3,2 3,3 4,0 4,1 4,2 4,3 5,0 5,1 5,2 5,3 6,0 6,1 6,2 6,3; i 20 0,5 1,5 1,6 2,5 3,5 3,6 4,5 5,5 5,6 6,5; s 0,0 0,1 0,2 0,3 1,1 1,2 1,3 1,5 1,6 2,0 2,1 2,2 2,3 3,0 3,1 3,2 3,3 3,5 3,6 4,0 4,1 4,2 4,3 5,1 5,2 5,3 5,5 5,6 6,0 6,1 6,2 6,3; 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;";
private final String SPACE_INVADERS_GAME = "a 23 2; c 0 E; i 6 0,2 0,7 1,3 1,7 2,2 2,3 2,4 2,5 2,6 2,7 3,2 3,4 3,5 3,6 3,8 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9 5,0 5,1 5,3 5,4 5,5 5,6 5,7 5,9 5,10 6,0 6,2 6,7 6,9 7,3 7,4 7,6 7,7; i 6 0,14 0,19 1,15 1,19 2,14 2,15 2,16 2,17 2,18 2,19 3,14 3,16 3,17 3,18 3,20 4,12 4,13 4,14 4,15 4,16 4,17 4,18 4,19 4,20 4,21 5,12 5,13 5,15 5,16 5,17 5,18 5,19 5,21 5,22 6,12 6,14 6,19 6,21 7,15 7,16 7,18 7,19; i 6 17,9 18,8 18,9 19,6 19,7 19,8 19,9 19,10 19,11 19,12 20,5 20,6 20,7 20,8 20,9 20,10 20,11 20,12 21,5 21,6 21,7 21,8 21,9 21,10 21,11 21,12 21,13 22,5 22,6 22,7 22,8 22,9 22,10 22,11 22,12; i 8 12,3 12,5 13,3 13,4 13,5 13,6 14,1 14,2 14,3 14,4 14,5 15,1 15,2 15,3 16,1 16,2; i 8 12,17 12,18 12,19 13,17 13,18 13,19 13,20 14,17 14,18 14,19 14,20 15,19 15,20 15,21 16,19 16,20; i 8 13,14 14,13 14,14 15,13 15,14 15,15 16,13 16,14; i 8 14,7 15,7 15,8 16,7; i 10 8,9 9,9 10,9 11,9; i 10 8,12 9,13 10,12 11,13; i 10 9,1 10,1 11,1 12,1; i 10 9,22 10,21 11,22 12,21; i 10 13,10 14,10 15,10; i 10 17,0 18,0 19,0 20,0; i 10 17,16 18,16 19,16 20,16; s 0,2 0,7 0,14 0,19 3,5 3,17 6,0 6,9 6,12 6,21 7,4 7,6 7,16 7,18 11,9 11,13 12,1 12,19 12,21 13,10 15,2 15,8 15,14 15,20 17,9 18,8 18,9 20,0 20,16 21,6 21,9 21,12; 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;";
// Store the selected map 0 = default, 1 = wheels, 2 = face, 3 = sides, 4 = space invaders // Store the selected map 0 = default, 1 = wheels, 2 = face, 3 = sides, 4 = space invaders
private int game_selected = 0; private int game_selected = 0;
Boolean game_over; Boolean game_over;
@ -53,12 +50,12 @@ public class Game extends Application {
* *
* @param stage the primary stage for this application, onto which * @param stage the primary stage for this application, onto which
* the application scene can be set. * the application scene can be set.
* @throws Exception if there is an error * @throws Exception
*/ */
@Override @Override
public void start(Stage stage) throws Exception { public void start(Stage stage) throws Exception {
// Set some variables and create the scene // Set some variables and create the scene
AI = 0; AI = false;
selectedTile = new Coord(-1,-1); selectedTile = new Coord(-1,-1);
message = ""; message = "";
messageError = false; messageError = false;
@ -71,14 +68,12 @@ public class Game extends Application {
// Set some app properties // Set some app properties
stage.setScene(scene); stage.setScene(scene);
stage.setTitle("Blue Lagoon"); stage.setTitle("Blue Lagoon");
stage.getIcons().add(new javafx.scene.image.Image(Objects.requireNonNull( stage.getIcons().add(new javafx.scene.image.Image(Game.class.getResourceAsStream("favicon.png")));
Game.class.getResourceAsStream("favicon.png"))));
stage.setResizable(false); stage.setResizable(false);
stage.show(); stage.show();
// Create a new game // Create a new game
newGame(2); newGame(2);
scene.setFill(Color.valueOf("#38AEF2"));
} }
@ -90,34 +85,35 @@ public class Game extends Application {
game_over = false; game_over = false;
// Get selected map // Get selected map
String DEFAULT_GAME = GameData.DEFAULT_GAME; switch (game_selected){
switch (game_selected) { case 1:
case 1 -> currentGame = new State(GameData.WHEELS_GAME); currentGame = new State(WHEELS_GAME);
case 2 -> currentGame = new State(GameData.FACE_GAME); break;
case 3 -> currentGame = new State(GameData.SIDES_GAME); case 2:
case 4 -> currentGame = new State(GameData.SPACE_INVADERS_GAME); currentGame = new State(FACE_GAME);
default -> currentGame = new State(DEFAULT_GAME); break;
case 3:
currentGame = new State(SIDES_GAME);
break;
case 4:
currentGame = new State(SPACE_INVADERS_GAME);
break;
default:
currentGame = new State(DEFAULT_GAME);
break;
} }
// Add additional players as needed // Add additional players as needed
switch (numPlayers) { switch (numPlayers){
case 3 -> currentGame.addPlayer(); case 3:
case 4 -> { currentGame.addPlayer();
break;
case 4:
currentGame.addPlayer(); currentGame.addPlayer();
currentGame.addPlayer(); currentGame.addPlayer();
} break;
default -> { default:
} break;
}
if (AI >= numPlayers){
for (int i = 0; i < numPlayers; i++){
currentGame.getPlayer(i).setAI(true);
}
} else if (AI > 0) {
for (int i = 0; i < AI; i++){
currentGame.getPlayer(i+1).setAI(true);
}
} }
// Distribute resources // Distribute resources
@ -125,23 +121,11 @@ public class Game extends Application {
// Send intro message // Send intro message
message = "Welcome to Blue Lagoon\nYou have started a new game for " + numPlayers + " players."; message = "Welcome to Blue Lagoon\nYou have started a new game for " + numPlayers + " players.";
switch (AI){ if (AI) message += "\nAI is playing";
case 0 -> message += "\nAll players are human";
case 1 -> message += "\nThere is 1 AI";
default -> message += "\nThere are " + AI + " AIs";
}
message += """
To place a settler, left click on a tile
To place a village, right click on a tile""";
sendMessage(message); sendMessage(message);
// Refresh the GUI (render the game) // Refresh the GUI (render the game)
refresh(); refresh();
// Play the game if all players are AI
if (AI >= numPlayers){
AIGame();
}
} }
// endregion // endregion
// region Game Play // region Game Play
@ -169,9 +153,6 @@ public class Game extends Application {
sendMessage("You have placed all your villages"); sendMessage("You have placed all your villages");
return; return;
} }
} else if (currentGame.getCurrentPlayer().getSettlers().length >= (40-(currentGame.getNumPlayers()*5))){
sendMessage("You have placed all your settlers");
return;
} }
// If the move is valid, do it // If the move is valid, do it
@ -182,11 +163,11 @@ public class Game extends Application {
// If the move was a stone, send a message about it // If the move was a stone, send a message about it
Coord lastMove = selectedTile; Coord lastMove = selectedTile;
StringBuilder message = new StringBuilder(); String message = "";
if (currentGame.isStone(lastMove)){ if (currentGame.isStone(lastMove)){
for (Resource resource : currentGame.getResources()) { for (Resource resource : currentGame.getResources()) {
if (resource.getCoord().equals(lastMove) ) { if (resource.getCoord().equals(lastMove) ) {
message = new StringBuilder("Player " + currentGame.getCurrentPlayerID() + " picked up a " + resource.getTypeString().toLowerCase()); message = "Player " + currentGame.getCurrentPlayerID() +" picked up a " + resource.getTypeString().toLowerCase();
} }
} }
} }
@ -194,12 +175,12 @@ public class Game extends Application {
// Go to the next player and if it is an AI, do a move for it // Go to the next player and if it is an AI, do a move for it
currentGame.nextPlayer(); currentGame.nextPlayer();
selectedTile = new Coord(-1,-1); selectedTile = new Coord(-1,-1);
while (currentGame.getCurrentPlayer().isAI()) { if (AI && currentGame.getCurrentPlayerID() == 1) {
message.append("\n").append(doAIMove()); message += "\n"+ doAIMove();
} }
// Send the message to the user // Send the message to the user
sendMessage(message.toString()); sendMessage(message);
} }
else { else {
sendMessage("Invalid move",true); sendMessage("Invalid move",true);
@ -212,11 +193,10 @@ public class Game extends Application {
if (currentGame.getCurrentPhase() == 'E') { if (currentGame.getCurrentPhase() == 'E') {
currentGame.cleanBoard(); currentGame.cleanBoard();
currentGame.distributeResources(); currentGame.distributeResources();
StringBuilder AI = new StringBuilder(); if (AI && currentGame.getCurrentPlayerID() == 1){
while (currentGame.getCurrentPlayer().isAI()) { String AI = doAIMove();
AI.append("\n").append(doAIMove()); sendMessage("Next phase!\n" + AI);
} }
sendMessage("Next phase!\n" + AI);
} }
else { else {
sendMessage("Game over!",true); sendMessage("Game over!",true);
@ -235,26 +215,23 @@ public class Game extends Application {
String message = ""; String message = "";
if (!currentGame.isPhaseOver()){ if (!currentGame.isPhaseOver()){
Player player = currentGame.getCurrentPlayer(); Player player = currentGame.getCurrentPlayer();
if (player.doAIMove(currentGame)) { player.doAIMove(currentGame);
if (currentGame.isPhaseOver()) { if (currentGame.isPhaseOver()){
message = "Starting next phase"; message = "Starting next phase";
} }
if (!player.getLastMove().equals(new Coord(-1, -1))) { if (!player.getLastMove().equals(new Coord(-1,-1))){
Coord lastMove = player.getLastMove(); Coord lastMove = player.getLastMove();
if (currentGame.isStone(lastMove)) { if (currentGame.isStone(lastMove)){
for (Resource resource : currentGame.getResources()) { for (Resource resource : currentGame.getResources()) {
if (resource.getCoord().equals(lastMove)) { if (resource.getCoord().equals(lastMove) ) {
message = "AI " + player.getPlayerID() + " picked up " + resource.getTypeString().toLowerCase(); message = "AI picked up a " + resource.getTypeString().toLowerCase();
}
} }
} else {
message = "AI " + player.getPlayerID() + " placed at " + lastMove.toString();
} }
} }
} else { else {
message += "AI " + player.getPlayerID() + " passed"; message = "AI placed at " + lastMove.toString();
currentGame.nextPlayer(); }
} }
} }
if (currentGame.isPhaseOver()){ if (currentGame.isPhaseOver()){
@ -263,11 +240,10 @@ public class Game extends Application {
currentGame.cleanBoard(); currentGame.cleanBoard();
currentGame.distributeResources(); currentGame.distributeResources();
currentGame.nextPhase(); currentGame.nextPhase();
StringBuilder AI = new StringBuilder("Next phase!\n"); if (AI && currentGame.getCurrentPlayerID() == 1){
while (currentGame.getCurrentPlayer().isAI()) { String AI = doAIMove();
AI.append("\n").append(doAIMove()); sendMessage("Next phase!\n" + AI);
} }
message = AI.toString();
} }
else { else {
message = "Game over!"; message = "Game over!";
@ -282,23 +258,19 @@ public class Game extends Application {
* Do a full AI game. This is good to visualize the AI * Do a full AI game. This is good to visualize the AI
*/ */
void AIGame(){ void AIGame(){
while (!game_over){ while (!currentGame.isPhaseOver()){
if (!currentGame.getCurrentPlayer().doAIMove(currentGame)){ currentGame.getCurrentPlayer().doAIMove(currentGame);
currentGame.nextPlayer();
}
if (currentGame.isPhaseOver()){
if (currentGame.getCurrentPhase() == 'E') {
currentGame.scorePhase();
currentGame.cleanBoard();
currentGame.distributeResources();
currentGame.nextPhase();
}
else {
game_over = true;
}
}
} }
currentGame.scorePhase();
currentGame.cleanBoard();
currentGame.distributeResources();
currentGame.nextPhase();
while (!currentGame.isPhaseOver()){
currentGame.getCurrentPlayer().doAIMove(currentGame);
}
currentGame.scorePhase();
sendMessage("Game over!",true); sendMessage("Game over!",true);
game_over = true;
refresh(); refresh();
} }
@ -329,19 +301,15 @@ public class Game extends Application {
/** /**
* When a tile is clicked, it will be selected * When a tile is clicked, it will be selected
* @param coordString the coordinate of the tile * @param coordString the coordinate of the tile
* @param button the button that was clicked
*/ */
private void tileClick(String coordString, MouseButton button){ private void tileClick(String coordString){
int y = Integer.parseInt(coordString.split(",")[0]); int y = Integer.parseInt(coordString.split(",")[0]);
int x = Integer.parseInt(coordString.split(",")[1]); int x = Integer.parseInt(coordString.split(",")[1]);
selectedTile = new Coord(y,x); selectedTile = new Coord(y,x);
sendMessage("Tile " + selectedTile.toString() + " selected");
if (button == MouseButton.PRIMARY) doMove(0);
else if (button == MouseButton.SECONDARY) doMove(1);
selectedTile = new Coord(-1,-1);
refresh(); refresh();
} }
@ -350,32 +318,20 @@ public class Game extends Application {
* It will clear the whole thing and then render it again * It will clear the whole thing and then render it again
*/ */
private void refresh() { private void refresh() {
if (darkMode){
// Make the background black of the scene
Scene scene = root.getScene();
scene.setFill(Color.BLACK);
} else {
Scene scene = root.getScene();
scene.setFill(Color.valueOf("#38AEF2"));
}
// When refreshing, it clears the whole thing and update it // When refreshing, it clears the whole thing and update it
root.getChildren().clear(); root.getChildren().clear();
root.getChildren().add(controls); root.getChildren().add(controls);
// Add the message // Add the message
Label messageLabel = new Label(message); Label messageLabel = new Label(message);
messageLabel.setLayoutX(35); messageLabel.setLayoutX(0);
messageLabel.setLayoutY(250); messageLabel.setLayoutY(250);
messageLabel.setFont(Font.font("Sans Serif",FontWeight.BOLD, 20)); messageLabel.setFont(Font.font("Sans Serif",FontWeight.BOLD, 20));
if (messageError){ if (messageError){
messageLabel.setTextFill(Color.RED); messageLabel.setTextFill(Color.RED);
} }
else { else {
if (darkMode) messageLabel.setTextFill(Color.WHITE); messageLabel.setTextFill(Color.BLACK);
else messageLabel.setTextFill(Color.BLACK);
} }
root.getChildren().add(messageLabel); root.getChildren().add(messageLabel);
@ -397,7 +353,7 @@ public class Game extends Application {
for(int i = 0; i < boardHeight; i++){ for(int i = 0; i < boardHeight; i++){
for(int j = 0; j < boardHeight - (-1 * i % 2 + 1); j++){ for(int j = 0; j < boardHeight - (-1 * i % 2 + 1); j++){
addBoardTile(viewerGrid, boardHeightPx/boardHeight, addBoardTile(viewerGrid, boardHeightPx/boardHeight,
String.format("%s,%s", i, j), Color.valueOf("#387CFF")); String.format("%s,%s", i, j), Color.DARKBLUE);
} }
} }
@ -407,19 +363,23 @@ public class Game extends Application {
// Getting the two mode of current state either Exploration // Getting the two mode of current state either Exploration
// or settler // or settler
char currentPhaseChar = currentGame.getCurrentPhase(); char currentPhaseChar = currentGame.getCurrentPhase();
String currentPhase = switch (currentPhaseChar) { String currentPhase = "";
case 'E' -> "Exploration"; switch (currentPhaseChar) {
case 'S' -> "Settlement"; case 'E':
default -> ""; currentPhase = "Exploration";
}; break;
case 'S':
currentPhase = "Settlement";
break;
}
// Making the Current State Statement text on the window // Making the Current State Statement text on the window
Text currentStateText = new Text(); Text currentStateText = new Text();
currentStateText.setText("The current player to move is player " + currentStateText.setText("The current player to move is player " +
playerId + "\nCurrent Phase: " + currentPhase); playerId + "\nCurrent Phase: " + currentPhase);
currentStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 20)); currentStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 20));
currentStateText.setX((double) WINDOW_WIDTH / 2 + ((double) WINDOW_WIDTH /5) - 175); currentStateText.setX(WINDOW_WIDTH / 2 + (WINDOW_WIDTH/5) - 175);
currentStateText.setY(30); currentStateText.setY(25);
currentStateText.setTextAlignment(TextAlignment.CENTER); currentStateText.setTextAlignment(TextAlignment.CENTER);
root.getChildren().add(currentStateText); root.getChildren().add(currentStateText);
currentStateText.setFill(Color.GREEN); currentStateText.setFill(Color.GREEN);
@ -427,7 +387,7 @@ public class Game extends Application {
// For each island render the island // For each island render the island
for (Island island: currentGame.getIslands()){ for (Island island: currentGame.getIslands()){
for (Coord c: island.getCoords()){ for (Coord c: island.getCoords()){
addBoardTile(viewerGrid, tileSize, c.toString(), Color.valueOf("#64CA00")); addBoardTile(viewerGrid, tileSize, c.toString(), Color.GREEN);
} }
} }
@ -435,13 +395,14 @@ public class Game extends Application {
for (Coord stoneCircle: currentGame.getStones()){ for (Coord stoneCircle: currentGame.getStones()){
addStoneTileToBoard(viewerGrid, tileSize, stoneCircle.toString(), Color.GRAY); addStoneTileToBoard(viewerGrid, tileSize, stoneCircle.toString(), Color.GRAY);
} }
StringBuilder playerData = new StringBuilder("Scores:");
String playerData = "Scores:";
// For each player add their settlements and roads // For each player add their settlements and roads
for (int i = 0; i < currentGame.getNumPlayers(); i++){ for (int i = 0; i < currentGame.getNumPlayers(); i++){
Player currentPlayer = currentGame.getPlayer(i); Player currentPlayer = currentGame.getPlayer(i);
// Add the player's score to the playerData string // Add the player's score to the playerData string
if (currentPlayer.isAI()) playerData.append("\nAI ").append(i).append(": ").append(currentPlayer.getScore()); if (AI && i == 1) playerData += "\nAI: " + currentPlayer.getScore();
else playerData.append("\nPlayer ").append(i).append(": ").append(currentPlayer.getScore()); else playerData += "\nPlayer " + i + ": " + currentPlayer.getScore();
// Settler tile generator // Settler tile generator
for (Coord c: currentPlayer.getSettlers()){ for (Coord c: currentPlayer.getSettlers()){
@ -449,10 +410,7 @@ public class Game extends Application {
addStoneTileToBoard(viewerGrid, tileSize, c.toString(), Color.PINK); addStoneTileToBoard(viewerGrid, tileSize, c.toString(), Color.PINK);
// Label generator // Label generator
if (currentPlayer.isAI()) addLabelToTile(viewerGrid, tileSize, c.toString(), Color.BLACK, "P"+i);
addLabelToTile(viewerGrid, tileSize, c.toString(), Color.GREEN, "AI "+i);
else
addLabelToTile(viewerGrid, tileSize, c.toString(), Color.BLACK, "P "+i);
} }
// Village tile generator // Village tile generator
for (Coord c: currentPlayer.getVillages()){ for (Coord c: currentPlayer.getVillages()){
@ -460,26 +418,21 @@ public class Game extends Application {
addStoneTileToBoard(viewerGrid, tileSize, c.toString(), Color. LIGHTGOLDENRODYELLOW); addStoneTileToBoard(viewerGrid, tileSize, c.toString(), Color. LIGHTGOLDENRODYELLOW);
// Label generator // Label generator
if (currentPlayer.isAI()) addLabelToTile(viewerGrid, tileSize, c.toString(), Color.BLACK, "P"+i);
addLabelToTile(viewerGrid, tileSize, c.toString(), Color.BLACK, "AI "+i);
else
addLabelToTile(viewerGrid, tileSize, c.toString(), Color.BLACK, "P "+i);
} }
} }
// Adding the player Statement Text to the window // Adding the player Statement Text to the window
Text playerStateText = new Text(); Text playerStateText = new Text();
playerStateText.setText(playerData.toString()); playerStateText.setText(playerData);
playerStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 25)); playerStateText.setFont(Font.font("Sans Serif", FontWeight.BOLD, 25));
playerStateText.setX(35); playerStateText.setX(0);
playerStateText.setY(100); playerStateText.setY(100);
playerStateText.setFill(Color.BLACK);
if (darkMode) playerStateText.setFill(Color.WHITE);
else playerStateText.setFill(Color.BLACK);
root.getChildren().add(playerStateText); root.getChildren().add(playerStateText);
// Add the grid to the root // Add the grid to the root
viewerGrid.relocate(((double) WINDOW_WIDTH /2-viewerGrid.getPrefWidth()/2) + ((double) WINDOW_WIDTH /5), viewerGrid.relocate((WINDOW_WIDTH/2-viewerGrid.getPrefWidth()/2) + (WINDOW_WIDTH/5),
((double) (WINDOW_HEIGHT + 100) /2-viewerGrid.getPrefHeight()/2)); ((WINDOW_HEIGHT+100)/2-viewerGrid.getPrefHeight()/2));
root.getChildren().add(viewerGrid); root.getChildren().add(viewerGrid);
// Add selected tile // Add selected tile
@ -496,52 +449,15 @@ public class Game extends Application {
* It will create the controls variable * It will create the controls variable
*/ */
private void makeControls() { private void makeControls() {
Label newLabel = new Label("Start New Game:"); Label newLabel = new Label("New Game:");
Button twoPlayer = new Button("2 Player"); Button twoPlayer = new Button("2 Player");
Button threePlayer = new Button("3 Player"); Button threePlayer = new Button("3 Player");
Button fourPlayer = new Button("4 Player"); Button fourPlayer = new Button("4 Player");
Label mapLabel = new Label("Select Map:"); Label mapLabel = new Label("Select Map:");
Label aiLabel = new Label("How many AI players:"); Label playLabel = new Label("Place piece:");
Label stuckLabel = new Label("Stuck?"); Button placeVillage = new Button("Village");
Button stuckButton = new Button("Skip my turn"); Button placeSettler = new Button("Settler");
CheckBox isAI = new CheckBox("AI");
// Button styling
twoPlayer.setStyle("-fx-padding: 3 10 10 10; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #69CC00 0%, #83CA00 100%), #69CC00, #83CA00, radial-gradient(center 50% 50%, radius 100%, #83CA00, #A1DF00); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
threePlayer.setStyle("-fx-padding: 3 10 10 10; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #69CC00 0%, #83CA00 100%), #69CC00, #83CA00, radial-gradient(center 50% 50%, radius 100%, #83CA00, #A1DF00); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
fourPlayer.setStyle("-fx-padding: 3 10 10 10; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #69CC00 0%, #83CA00 100%), #69CC00, #83CA00, radial-gradient(center 50% 50%, radius 100%, #83CA00, #A1DF00); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
stuckButton.setStyle("-fx-padding: 3 10 10 10; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%), #9d4024, #d86e3a, radial-gradient(center 50% 50%, radius 100%, #d86e3a, #c54e2c); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
CheckBox darkToggle = new CheckBox("Dark Mode");
darkToggle.setSelected(darkMode);
darkToggle.setOnAction(e -> {
darkMode = !darkMode;
makeControls();
refresh();
});
// Make everything white if dark mode is on
if (darkMode) {
newLabel.setTextFill(Color.WHITE);
mapLabel.setTextFill(Color.WHITE);
aiLabel.setTextFill(Color.WHITE);
stuckLabel.setTextFill(Color.WHITE);
stuckButton.setTextFill(Color.BLACK);
twoPlayer.setTextFill(Color.BLACK);
threePlayer.setTextFill(Color.BLACK);
fourPlayer.setTextFill(Color.BLACK);
darkToggle.setTextFill(Color.WHITE);
}
// Numeric select for AI
ComboBox aiSelector = new ComboBox();
aiSelector.getItems().add("0");
aiSelector.getItems().add("1");
aiSelector.getItems().add("2");
aiSelector.getItems().add("3");
aiSelector.getItems().add("4");
aiSelector.setValue("0");
// Store the selected map 0 = default, 1 = wheels, 2 = face, 3 = sides, 4 = space invaders // Store the selected map 0 = default, 1 = wheels, 2 = face, 3 = sides, 4 = space invaders
ComboBox mapSelector = new ComboBox(); ComboBox mapSelector = new ComboBox();
@ -552,21 +468,29 @@ public class Game extends Application {
mapSelector.getItems().add("Space Invaders"); mapSelector.getItems().add("Space Invaders");
mapSelector.setPromptText("Default"); mapSelector.setPromptText("Default");
// Combobox styling
aiSelector.setStyle("-fx-padding: -1 5 5 5; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #69CC00 0%, #83CA00 100%), #69CC00, #83CA00, radial-gradient(center 50% 50%, radius 100%, #83CA00, #A1DF00); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
mapSelector.setStyle("-fx-padding: -1 5 5 5; -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0; -fx-background-radius: 8; -fx-background-color: linear-gradient(from 0% 93% to 0% 100%, #69CC00 0%, #83CA00 100%), #69CC00, #83CA00, radial-gradient(center 50% 50%, radius 100%, #83CA00, #A1DF00); -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 ); -fx-font-weight: bold; -fx-font-size: 1.0em;");
// Set the map when the map is selected // Set the map when the map is selected
mapSelector.setOnAction(new EventHandler<ActionEvent>() { mapSelector.setOnAction(new EventHandler<ActionEvent>() {
@Override @Override
public void handle(ActionEvent event) { public void handle(ActionEvent event) {
switch (mapSelector.getValue().toString()) { switch (mapSelector.getValue().toString()){
case "Default" -> game_selected = 0; case "Default":
case "Wheels" -> game_selected = 1; game_selected = 0;
case "Face" -> game_selected = 2; break;
case "Sides" -> game_selected = 3; case "Wheels":
case "Space Invaders" -> game_selected = 4; game_selected = 1;
default -> game_selected = 0; break;
case "Face":
game_selected = 2;
break;
case "Sides":
game_selected = 3;
break;
case "Space Invaders":
game_selected = 4;
break;
default:
game_selected = 0;
break;
} }
} }
}); });
@ -574,7 +498,7 @@ public class Game extends Application {
twoPlayer.setOnAction(new EventHandler<ActionEvent>() { twoPlayer.setOnAction(new EventHandler<ActionEvent>() {
@Override @Override
public void handle(ActionEvent e) { public void handle(ActionEvent e) {
AI = Integer.parseInt(aiSelector.getValue().toString()); AI = isAI.isSelected();
newGame(2); newGame(2);
} }
}); });
@ -582,38 +506,46 @@ public class Game extends Application {
threePlayer.setOnAction(new EventHandler<ActionEvent>() { threePlayer.setOnAction(new EventHandler<ActionEvent>() {
@Override @Override
public void handle(ActionEvent e) { public void handle(ActionEvent e) {
AI = Integer.parseInt(aiSelector.getValue().toString()); AI = isAI.isSelected();
newGame(3); newGame(3);
} }
}); });
fourPlayer.setOnAction(new EventHandler<ActionEvent>() { fourPlayer.setOnAction(new EventHandler<ActionEvent>() {
@Override @Override
public void handle(ActionEvent e) { public void handle(ActionEvent e) {
AI = Integer.parseInt(aiSelector.getValue().toString()); AI = isAI.isSelected();
newGame(4); newGame(4);
} }
}); });
stuckButton.setOnAction(new EventHandler<ActionEvent>() { placeVillage.setOnAction(new EventHandler<ActionEvent>() {
@Override @Override
public void handle(ActionEvent e) { public void handle(ActionEvent e) {
currentGame.nextPlayer(); doMove(1);
StringBuilder message = new StringBuilder("You skipped your turn");
while (currentGame.getCurrentPlayer().isAI() && !game_over) {
message.append("\n").append(doAIMove());
}
sendMessage(message.toString());
refresh();
} }
}); });
placeSettler.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
doMove(0);
}
});
// Run AI Game if keypress is a
isAI.setOnKeyPressed(event -> {
if (event.getCode().toString() == "A"){
newGame(currentGame.getNumPlayers());
AIGame();
}
});
HBox hb = new HBox(); HBox hb = new HBox();
hb.getChildren().addAll(mapLabel,mapSelector,aiLabel,aiSelector,newLabel, twoPlayer,threePlayer,fourPlayer, hb.getChildren().addAll(newLabel, twoPlayer,threePlayer,fourPlayer, isAI,mapLabel,mapSelector,playLabel,placeVillage,placeSettler);
stuckLabel,stuckButton,darkToggle);
hb.setSpacing(10); hb.setSpacing(10);
hb.setLayoutX(50); hb.setLayoutX(50);
hb.setLayoutY(WINDOW_HEIGHT - 50); hb.setLayoutY(WINDOW_HEIGHT - 50);
controls.getChildren().clear();
controls.getChildren().add(hb); controls.getChildren().add(hb);
} }
@ -632,7 +564,7 @@ public class Game extends Application {
Hexagon hex = new Hexagon(tileSize, color); Hexagon hex = new Hexagon(tileSize, color);
// if the row is even, translate the tile to the right by tileSize/2 // if the row is even, translate the tile to the right by tileSize/2
if (Integer.parseInt(coords[0]) % 2 == 0) hex.setTranslateX((double) 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 // 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); hex.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize);
@ -640,7 +572,7 @@ public class Game extends Application {
// Add a mouse click event to the tile // Add a mouse click event to the tile
hex.setOnMouseClicked(event -> { hex.setOnMouseClicked(event -> {
tileClick(coordString,event.getButton()); tileClick(coordString);
}); });
} }
@ -662,7 +594,7 @@ public class Game extends Application {
Hexagon hex = new Hexagon(tileSize2, color); Hexagon hex = new Hexagon(tileSize2, color);
// if the row is even, translate the tile to the right by tileSize/2 // if the row is even, translate the tile to the right by tileSize/2
if (Integer.parseInt(coords[0]) % 2 == 0) hex.setTranslateX((double) 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 // 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); hex.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize);
@ -672,7 +604,7 @@ public class Game extends Application {
// Add a mouse click event to the tile // Add a mouse click event to the tile
hex.setOnMouseClicked(event -> { hex.setOnMouseClicked(event -> {
tileClick(coordString,event.getButton()); tileClick(coordString);
}); });
} }
@ -693,16 +625,16 @@ public class Game extends Application {
newLabel.setFont(Font.font("Sans Serif", 12)); newLabel.setFont(Font.font("Sans Serif", 12));
// Following the tile's pos format // Following the tile's pos format
if (Integer.parseInt(coords[0]) % 2 == 0) newLabel.setTranslateX((double) tileSize /2); if (Integer.parseInt(coords[0]) % 2 == 0) newLabel.setTranslateX(tileSize/2);
newLabel.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize); newLabel.setTranslateY(Integer.parseInt(coords[0]) * -0.25 * tileSize);
// Making the label center // Making the label center
newLabel.setTranslateX(18.5 + newLabel.getTranslateX()); newLabel.setTranslateX(19.5 + newLabel.getTranslateX());
board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0]));
// Add a mouse click event to the tile // Add a mouse click event to the tile
newLabel.setOnMouseClicked(event -> { newLabel.setOnMouseClicked(event -> {
tileClick(coordString,event.getButton()); tileClick(coordString);
}); });
} }
@ -733,7 +665,7 @@ public class Game extends Application {
// Adjust the label's position // Adjust the label's position
if (Integer.parseInt(coords[0]) % 2 == 0){ if (Integer.parseInt(coords[0]) % 2 == 0){
newLabel.setTranslateX((double) tileSize /2 + width); newLabel.setTranslateX(tileSize/2 + width);
} else } else
{ {
newLabel.setTranslateX(width); newLabel.setTranslateX(width);
@ -744,7 +676,7 @@ public class Game extends Application {
board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0])); board.add(newLabel, Integer.parseInt(coords[1]), Integer.parseInt(coords[0]));
// Add a mouse click event to the tile // Add a mouse click event to the tile
newLabel.setOnMouseClicked(event -> { newLabel.setOnMouseClicked(event -> {
tileClick(coordString,event.getButton()); tileClick(coordString);
}); });
} }
@ -752,7 +684,7 @@ public class Game extends Application {
* A hexagon shape with a given side length and fill. * A hexagon shape with a given side length and fill.
* Used to create the tiles on the board. * Used to create the tiles on the board.
*/ */
static class Hexagon extends Polygon { class Hexagon extends Polygon {
/** /**
* Create a hexagon with a given side length and fill. * Create a hexagon with a given side length and fill.
* @param side double The length of a side of the hexagon. * @param side double The length of a side of the hexagon.

View File

@ -30,7 +30,7 @@ public class ApplyMoveTest implements TestMapNamePlayerCount {
String result = BlueLagoon.applyMove(game.get(i-2), game.get(i-1)); String result = BlueLagoon.applyMove(game.get(i-2), game.get(i-1));
List<String> errors = sc.compare(game.get(i), result); List<String> errors = sc.compare(game.get(i), result);
if(errors.size() > 0){ if(errors.size() > 0){
Assertions.fail("\nError on input game: " + game.get(i-2) + "\nMove: " + game.get(i-1) + "\nexpected: " + game.get(i) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors)); Assertions.fail("\n"+"expected: " + game.get(i) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors));
} }
} }
} }

View File

@ -28,7 +28,7 @@ public class EndPhaseTest implements TestMapNamePlayerCount {
String result = BlueLagoon.endPhase(pre.get(i)); String result = BlueLagoon.endPhase(pre.get(i));
List<String> errors = sc.compare(post.get(i), result); List<String> errors = sc.compare(post.get(i), result);
if(errors.size() > 0){ if(errors.size() > 0){
Assertions.fail("\nError ending phase on input: " + pre.get(i) + "\nexpected: " + post.get(i) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors)); Assertions.fail("\n"+"expected: " + post.get(i) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors));
} }
} }
} }

View File

@ -77,6 +77,5 @@ public class IsPhaseOverTest implements TestMapNamePlayerCount {
for(int game = 0; game < games.size(); game++){ for(int game = 0; game < games.size(); game++){
testGame(games.get(game), preEndPhase.get(game), solutions.get(game)); testGame(games.get(game), preEndPhase.get(game), solutions.get(game));
} }
testFalse("a 13 2; c 1 S; 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 0,5 1,12 10,6 B 0,0 1,4 2,1 6,6 11,5 W 3,7 3,10 4,2 7,12 8,5 P 1,8 3,12 5,9 7,8 9,9 10,10 S 0,9 3,5 4,0 5,11 6,3 7,0 12,8 12,11; p 0 38 2 0 1 0 0 S 7,3 7,4 8,2 8,4 9,0 9,3 9,4 9,5 10,3 T 9,1 9,2 11,4 12,4; p 1 79 1 1 0 0 0 S 10,0 10,1 10,2 11,0 11,1 11,2 11,3 12,2 12,3 T 12,0 12,1;");
} }
} }

View File

@ -29,7 +29,7 @@ public class PlacePieceTest implements TestMapNamePlayerCount {
String result = BlueLagoon.placePiece(game.get(i-2), game.get(i-1)); String result = BlueLagoon.placePiece(game.get(i-2), game.get(i-1));
List<String> errors = sc.compare(solutions.get(i / 2 - 1), result); List<String> errors = sc.compare(solutions.get(i / 2 - 1), result);
if(errors.size() > 0){ if(errors.size() > 0){
Assertions.fail("\nError on input game: " + game.get(i-2) + "\nMove: " + game.get(i-1) + "\nexpected: " + solutions.get(i / 2 - 1) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors)); Assertions.fail("\n"+"expected: " + solutions.get(i / 2 - 1) + "\nactual: " + result + "\nerrors:\n" + String.join("\n", errors));
} }
} }
} }