blueLagoon: Added AI
This commit is contained in:
parent
3e04727e7c
commit
a9a7ddef94
@ -611,6 +611,7 @@ public class BlueLagoon {
|
|||||||
scores[i] = state.scoreTotalIslands(i);
|
scores[i] = state.scoreTotalIslands(i);
|
||||||
}
|
}
|
||||||
return scores;
|
return scores;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*// ArrayList<Integer> islandCoords = islandCoords(stateString);
|
/*// ArrayList<Integer> islandCoords = islandCoords(stateString);
|
||||||
@ -673,7 +674,7 @@ public class BlueLagoon {
|
|||||||
// check the size of the arrayList to set the size for the int[]
|
// check the size of the arrayList to set the size for the int[]
|
||||||
}
|
}
|
||||||
|
|
||||||
return new int[]{0, 0}; // FIXME Task 11*/
|
return new int[]{0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
// for (int i = 9; i < parseSplit.length; i++) {
|
// for (int i = 9; i < parseSplit.length; i++) {
|
||||||
@ -698,6 +699,8 @@ public class BlueLagoon {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a state string, calculate the "Links" portion of the score for
|
* Given a state string, calculate the "Links" portion of the score for
|
||||||
@ -882,6 +885,7 @@ public class BlueLagoon {
|
|||||||
* @return a move string generated by an AI
|
* @return a move string generated by an AI
|
||||||
*/
|
*/
|
||||||
public static String generateAIMove(String stateString){
|
public static String generateAIMove(String stateString){
|
||||||
return ""; // FIXME Task 16
|
State state = new State(stateString);
|
||||||
|
return state.getCurrentPlayer().createAIMove(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import java.util.Set;
|
|||||||
* This includes the player's ID, score, resources, settlers and villages
|
* This includes the player's ID, score, resources, settlers and villages
|
||||||
*/
|
*/
|
||||||
public class Player {
|
public class Player {
|
||||||
|
|
||||||
|
// region Setup
|
||||||
final int playerID;
|
final int playerID;
|
||||||
private int score;
|
private int score;
|
||||||
private int numCoconuts;
|
private int numCoconuts;
|
||||||
@ -35,6 +37,8 @@ public class Player {
|
|||||||
this.settlers = new Coord[0];
|
this.settlers = new Coord[0];
|
||||||
this.villages = new Coord[0];
|
this.villages = new Coord[0];
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
// region Getters and Setters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the player's ID
|
* Get the player's ID
|
||||||
@ -220,6 +224,8 @@ public class Player {
|
|||||||
return numPieces;
|
return numPieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -229,12 +235,14 @@ public class Player {
|
|||||||
return validMoves.size() > 0;
|
return validMoves.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// region Auto Player
|
||||||
/**
|
/**
|
||||||
* Do a Random Move
|
* Do a Random Move
|
||||||
* @param state State to do the move on
|
* @param state State to do the move on
|
||||||
*/
|
*/
|
||||||
public void doRandomMove(State state) {
|
public void doRandomMove(State state) {
|
||||||
if (state.getCurrentPlayerID() != playerID) {
|
if (state.getCurrentPlayer() != this) {
|
||||||
|
System.out.println("Not this player's turn");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<String> validMoves = BlueLagoon.generateAllValidMoves(state.toString());
|
Set<String> validMoves = BlueLagoon.generateAllValidMoves(state.toString());
|
||||||
@ -258,6 +266,144 @@ public class Player {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a calculated move
|
||||||
|
* @param state State to do the move on
|
||||||
|
*/
|
||||||
|
public String createAIMove(State state){
|
||||||
|
if (state.getCurrentPlayer() != this){
|
||||||
|
System.out.println("Not this player's turn");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Set<String> validMoves = BlueLagoon.generateAllValidMoves(state.toString());
|
||||||
|
if (validMoves.size() == 0){
|
||||||
|
System.out.println("No valid moves");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int islandCount = 0;
|
||||||
|
for (Island island : state.getIslands()) {
|
||||||
|
if (this.getNumPiecesOnIsland(island)>0) islandCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bestMove = "";
|
||||||
|
int bestScore = -1;
|
||||||
|
for (String move : validMoves){
|
||||||
|
int score = calculateMoveScore(state, move,islandCount);
|
||||||
|
if (score > bestScore){
|
||||||
|
bestScore = score;
|
||||||
|
bestMove = move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a calculated move
|
||||||
|
*/
|
||||||
|
public void doAIMove(State state){
|
||||||
|
|
||||||
|
String bestMove = createAIMove(state);
|
||||||
|
char pieceType = bestMove.charAt(0);
|
||||||
|
String coordStr = bestMove.substring(2);
|
||||||
|
int x = Integer.parseInt(coordStr.split(",")[0]);
|
||||||
|
int y = Integer.parseInt(coordStr.split(",")[1]);
|
||||||
|
Coord coord = new Coord(x, y);
|
||||||
|
state.placePiece(coord, pieceType);
|
||||||
|
state.nextPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the score of a move
|
||||||
|
* The score is calculated only with public information
|
||||||
|
* The score is calculated by:
|
||||||
|
* 1. If the move gives the player some resources (10 points)
|
||||||
|
* 2. If the move gives the player a new island (1 point) or if below rules are met don't give a point
|
||||||
|
* 3. If this move makes the player have the pieces on 7-8 islands (10-20 points)
|
||||||
|
* 4. If this move makes the player have the most pieces on an island (island bonus points)
|
||||||
|
*
|
||||||
|
* @param state State to do the move on
|
||||||
|
* @param move String move to calculate the score of
|
||||||
|
* @return int score of the move
|
||||||
|
*/
|
||||||
|
public int calculateMoveScore(State state, String move, int currentIslandCount){
|
||||||
|
int score = 0;
|
||||||
|
String coordStr = move.substring(2);
|
||||||
|
int y = Integer.parseInt(coordStr.split(",")[0]);
|
||||||
|
int x = Integer.parseInt(coordStr.split(",")[1]);
|
||||||
|
Coord coord = new Coord(y, x);
|
||||||
|
|
||||||
|
// Check if this will give player some resources
|
||||||
|
if (state.isStone(coord)){
|
||||||
|
score += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a new island or will make player have the most pieces on the island
|
||||||
|
for (Island island:state.getIslands()) {
|
||||||
|
if (!island.containsCoord(coord)) continue;
|
||||||
|
if (this.getNumPiecesOnIsland(island) == 0) {
|
||||||
|
if (currentIslandCount == 7) score += 20;
|
||||||
|
else if (currentIslandCount == 6) score += 10;
|
||||||
|
else score += 1;
|
||||||
|
}
|
||||||
|
// Check if adding this piece will make player have the most pieces on the island
|
||||||
|
int ties = 0;
|
||||||
|
int wins = 0;
|
||||||
|
int loses = 0;
|
||||||
|
int myPieces = this.getNumPiecesOnIsland(island);
|
||||||
|
for (int i = 0; i < state.getNumPlayers(); i++) {
|
||||||
|
if (i == this.getPlayerID()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int otherPlayerPieces = state.getPlayer(i).getNumPiecesOnIsland(island);
|
||||||
|
if (otherPlayerPieces > myPieces+1) {
|
||||||
|
loses++;
|
||||||
|
} else
|
||||||
|
if (otherPlayerPieces == myPieces) {
|
||||||
|
wins++;
|
||||||
|
} else if (otherPlayerPieces == myPieces + 1) {
|
||||||
|
ties++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loses == 0){
|
||||||
|
if (ties > 0){
|
||||||
|
score += island.getBonus()/(ties+1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
score += island.getBonus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if player needs more islands
|
||||||
|
if (score == 0) {
|
||||||
|
if (currentIslandCount < 8 ){
|
||||||
|
// Check if there is an island adjacent to this move
|
||||||
|
for (Island island : state.getIslands()) {
|
||||||
|
if (island.containsCoord(new Coord(coord.getX() + 1, coord.getY()))
|
||||||
|
|| island.containsCoord(new Coord(coord.getX() - 1, coord.getY()))
|
||||||
|
|| island.containsCoord(new Coord(coord.getX(), coord.getY() + 1))
|
||||||
|
|| island.containsCoord(new Coord(coord.getX(), coord.getY() - 1))) {
|
||||||
|
score += 1;
|
||||||
|
if (state.getCurrentPhase() == 'E'){
|
||||||
|
score += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the largest column of the player's pieces
|
||||||
|
* @param coords Coord[] list of the player's pieces
|
||||||
|
* @return int largest column
|
||||||
|
*/
|
||||||
private int maxCol(Coord[] coords){
|
private int maxCol(Coord[] coords){
|
||||||
int maxCol = 0;
|
int maxCol = 0;
|
||||||
for (Coord coord : coords) {
|
for (Coord coord : coords) {
|
||||||
|
@ -352,6 +352,7 @@ public class State {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Place a piece on the board. Uses current turn's player
|
* Place a piece on the board. Uses current turn's player
|
||||||
|
* This does not check if the move is valid (Use isValidMove() first)
|
||||||
* @param coord Coord coordinate to place piece
|
* @param coord Coord coordinate to place piece
|
||||||
* @param type char type of piece
|
* @param type char type of piece
|
||||||
*/
|
*/
|
||||||
|
@ -60,33 +60,58 @@ public class StateTest {
|
|||||||
// Place a settler at (0,0)
|
// Place a settler at (0,0)
|
||||||
state.placePiece(new Coord(0,0), 'S');
|
state.placePiece(new Coord(0,0), 'S');
|
||||||
// Get the island score
|
// Get the island score
|
||||||
Assertions.assertEquals(34,state.scoreMajorities(0), "The test failed because the score of the islands for player 0 is not 34");
|
Assertions.assertEquals(6,state.scoreMajorities(0), "The test failed because the score of the islands for player 0 is not 34");
|
||||||
Assertions.assertEquals(28, state.scoreMajorities(1), "The test failed because the score of the islands for player 1 is not 28");
|
Assertions.assertEquals(0, state.scoreMajorities(1), "The test failed because the score of the islands for player 1 is not 28");
|
||||||
Assertions.assertEquals(0,state.scoreTotalIslands(0), "The test failed because the score of the islands for player 0 is not 0");
|
Assertions.assertEquals(0,state.scoreTotalIslands(0), "The test failed because the score of the islands for player 0 is not 0");
|
||||||
Assertions.assertEquals(1, state.getPlayer(0).getNumResource(toClaim.getType()), "The test failed because the number of resources for player 0 is not 1");
|
Assertions.assertEquals(1, state.getPlayer(0).getNumResource(toClaim.getType()), "The test failed because the number of resources for player 0 is not 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
/**
|
||||||
public void testStateWHEELSGAME(){
|
* Run Sim on AI vs Random
|
||||||
State state = new State(GameDataLoader.WHEELS_GAME);
|
* @return true if AI wins, false if Random wins
|
||||||
Assertions.assertEquals(GameDataLoader.WHEELS_GAME,state.toString(), "The test failed because the state created from the string is not the same as the state created from the string");
|
*/
|
||||||
|
public boolean simulateGame(State state){
|
||||||
state.distributeResources();
|
|
||||||
Assertions.assertFalse(state.isPhaseOver(), "The test failed because the phase is over even though it should not be because the phase is not over");
|
|
||||||
|
|
||||||
// Test endPhase
|
|
||||||
while (!state.isPhaseOver()) {
|
while (!state.isPhaseOver()) {
|
||||||
if (!state.getCurrentPlayer().canPlay(state)) {
|
if (!state.getCurrentPlayer().canPlay(state)) {
|
||||||
System.out.println("Player " + state.getCurrentPlayerID() + " can't play");
|
System.out.println("Player " + state.getCurrentPlayerID() + " can't play");
|
||||||
state.nextPlayer();
|
state.nextPlayer();
|
||||||
}
|
}
|
||||||
state.getCurrentPlayer().doRandomMove(state);
|
if (state.getCurrentPlayerID() == 0) {
|
||||||
|
state.getCurrentPlayer().doAIMove(state);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.getCurrentPlayer().doRandomMove(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.scorePhase();
|
state.scorePhase();
|
||||||
System.out.println(state.getCurrentPhase());
|
int P0 = state.getPlayer(0).getScore();
|
||||||
System.out.println(state.scoreString());
|
int P1 = state.getPlayer(1).getScore();
|
||||||
|
if (P0 > P1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAIDominance() {
|
||||||
|
|
||||||
|
|
||||||
|
State Startstate;
|
||||||
|
|
||||||
|
for (String map:GameDataLoader.MAP_NAMES) {
|
||||||
|
Startstate = new State(GameDataLoader.readMap(map));
|
||||||
|
Startstate.distributeResources();
|
||||||
|
|
||||||
|
int numGames = 20;
|
||||||
|
int numWins = 0;
|
||||||
|
for (int i = 0; i < numGames; i++) {
|
||||||
|
State state = new State(Startstate.toString());
|
||||||
|
if (simulateGame(state)) {
|
||||||
|
numWins++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("AI won " + numWins + " out of " + numGames + " games on the " + map + " map");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
tests/comp1110/ass2/testdata/GameDataLoader.java
vendored
15
tests/comp1110/ass2/testdata/GameDataLoader.java
vendored
@ -7,6 +7,21 @@ public class GameDataLoader extends DataLoader<String>{
|
|||||||
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 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;";
|
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;";
|
||||||
|
|
||||||
|
public static String readMap(String mapName){
|
||||||
|
switch (mapName){
|
||||||
|
case "WHEELS_GAME":
|
||||||
|
return WHEELS_GAME;
|
||||||
|
case "FACE_GAME":
|
||||||
|
return FACE_GAME;
|
||||||
|
case "SIDES_GAME":
|
||||||
|
return SIDES_GAME;
|
||||||
|
case "SPACE_INVADERS_GAME":
|
||||||
|
return SPACE_INVADERS_GAME;
|
||||||
|
default:
|
||||||
|
return DEFAULT_GAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public GameDataLoader(){
|
public GameDataLoader(){
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user