Skip to content

Commit 376b013

Browse files
committedMay 1, 2023
Updated README, added header with group members to bot files
1 parent fed77b9 commit 376b013

File tree

7 files changed

+126
-191
lines changed

7 files changed

+126
-191
lines changed
 

‎README.md

+52-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,61 @@
11
# COMP20050 Group12
2-
Eoin Creavin, Mynah Bhattacharyya, Ben McDowell
2+
[Eoin Creavin](https://github.com/eoin-cr/), [Mynah Bhattacharyya](https://github.com/mynah-bird),
3+
[Ben McDowell](https://github.com/Benmc1/)
34

4-
Use of certain newer Java features means a requirement of at least Java 17 has been
5-
imposed in the gradle build file.
5+
## How to play
6+
Simply either run the jar release with `java -jar [RELEASE].jar` or run the
7+
`Main` java file in your IDE of choice. The game will
8+
then start. It will ask you whether you want bot mode on or off. If you select to
9+
have bot mode on, two bots will play each other in cascadia, selecting where to place
10+
tiles, tokens, how to rotate the tiles, whether to activate a cull or not, etc.
11+
The bots will play their turns in under a five-second time limit.
12+
13+
## Bot strategies
14+
### Tokens
15+
The token class will base the ranking of the deck tokens on how many points each
16+
token would give it if it were placed. It iterates through the possible placements
17+
for each token and sees what the score for each placement would be. Due to the
18+
limited number of tiles, this is not incredibly time intensive, and does not come
19+
close to the 5-second time limit.
20+
21+
The bot has two implementations of this—constructive and destructive. The
22+
constructive method attempts to increase the current players score the most,
23+
whilst the destructive tries to take the token away from the other player which
24+
would have increased the other player's score the most.
25+
26+
### Tiles
27+
The bot first prioritises increasing the size of habitat corridors which are close
28+
in size (-1,0, or 1 away) to the opponents same habitat corridor. This is because
29+
bonus points are allocated to the player with the longest of each type of habitat
30+
corridor, so we don't want to lose our advantage.
31+
32+
For any tiles that do not have habitat corridors that are close in size the bot
33+
finds the smallest habitat corridor and tries to increase its size, so we have
34+
multiple consistently sized habitat corridors to maximise scoring. The bot also
35+
uses a constructive and destructive version of this.
636

7-
Gradle should not be required to run the game, however, the tests use the Mockito
8-
library which requires gradle to include.
37+
The tile bot also selects the tile rotation which will maximise its habitat corridor
38+
length.
939

10-
A screenshot of the test coverage of the project has been included
11-
(see TestCoverage.png).
40+
### Tile token selection
41+
The bot then gets the tile and token preferences. It then iterates through, finding
42+
the best tile token pair for placement. Usually you can only place a tile and token
43+
from the same pair, however, you can spend a nature token to select any tile and any
44+
token and make a pair. If the max possible score is a decent bit larger than the
45+
max pair, if the bot has a nature token it will spend it in order to select the
46+
maximum score.
1247

13-
A modified version of the Google stylesheet has been used for the code
14-
styling (see stylesheet.xml)
48+
# Requirements
49+
50+
Use of certain newer Java features means a requirement of at least Java 17 has been
51+
imposed in the gradle build file. Gradle is not required to run the game, however,
52+
the game tests use the Mockito library which requires gradle to include.
1553

1654
We have used the default gradle package layout (`src/main/java/...` and `src/test/java/...`).
1755
Make sure to change the package source file from `src` to `src/main/java` and set the
1856
main run configuration to `cascadia.Main`.
57+
58+
### Links
59+
**This repo:** https://github.com/eoin-cr/COMP20050_Group12
60+
61+
**Sprint 5 kanban board:** https://github.com/users/mynah-bird/projects/2

‎src/bot/java/cascadia/BotMain.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
COMP20050 Group 12
3+
Eoin Creavin – Student ID: 21390601
4+
eoin.creavin@ucdconnect.ie
5+
GitHub ID: eoin-cr
6+
7+
Mynah Bhattacharyya – Student ID: 21201085
8+
malhar.bhattacharyya@ucdconnect.ie
9+
GitHub ID: mynah-bird
10+
11+
Ben McDowell – Student ID: 21495144
12+
ben.mcdowell@ucdconnect.ie
13+
GitHub ID: Benmc1
14+
*/
15+
116
package cascadia;
217

318
import java.util.Arrays;
@@ -53,13 +68,11 @@ public void makeBestChoiceFromDeck(Player currPlayer) {
5368

5469
BotTimer.startTimer();
5570

56-
5771
int[] tilePreferences = tileBots[turn % 2].chooseStrategy(currPlayer, nextPlayer);
5872
int[] tokenPreferences = tokenBots[turn % 2].chooseStrategy(currPlayer, nextPlayer);
5973
System.out.printf("tile: %s\n", Arrays.toString(tilePreferences));
6074
System.out.printf("token: %s\n", Arrays.toString(tokenPreferences));
6175

62-
//TODO: handle nature token spend here
6376
int maxScore = tilePreferences[0] + tokenPreferences[0];
6477
int maxScoreIdx = 0;
6578
for (int i = 0; i < tilePreferences.length; i++) {

‎src/bot/java/cascadia/BotTimer.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
COMP20050 Group 12
3+
Eoin Creavin – Student ID: 21390601
4+
eoin.creavin@ucdconnect.ie
5+
GitHub ID: eoin-cr
6+
7+
Mynah Bhattacharyya – Student ID: 21201085
8+
malhar.bhattacharyya@ucdconnect.ie
9+
GitHub ID: mynah-bird
10+
11+
Ben McDowell – Student ID: 21495144
12+
ben.mcdowell@ucdconnect.ie
13+
GitHub ID: Benmc1
14+
*/
15+
116
package cascadia;
217

318
public class BotTimer {
@@ -11,10 +26,9 @@ public static void startTimer() {
1126
/**
1227
* True if there is time left.
1328
* Otherwise, its False
14-
* @return Boolean
1529
*/
1630
public static boolean isTimeLeft() {
17-
double milliSeconds = (System.nanoTime() - startTime)*.000001;
31+
double milliSeconds = (System.nanoTime() - startTime) * .000001;
1832
return !(milliSeconds > TIME_ALLOWED);
1933
}
2034
}

‎src/bot/java/cascadia/TileBot.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
COMP20050 Group 12
3+
Eoin Creavin – Student ID: 21390601
4+
eoin.creavin@ucdconnect.ie
5+
GitHub ID: eoin-cr
6+
7+
Mynah Bhattacharyya – Student ID: 21201085
8+
malhar.bhattacharyya@ucdconnect.ie
9+
GitHub ID: mynah-bird
10+
11+
Ben McDowell – Student ID: 21495144
12+
ben.mcdowell@ucdconnect.ie
13+
GitHub ID: Benmc1
14+
*/
15+
116
package cascadia;
217
import java.util.ArrayList;
318
import java.util.Arrays;

‎src/bot/java/cascadia/TokenBot.java

+18-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1+
/*
2+
COMP20050 Group 12
3+
Eoin Creavin – Student ID: 21390601
4+
eoin.creavin@ucdconnect.ie
5+
GitHub ID: eoin-cr
6+
7+
Mynah Bhattacharyya – Student ID: 21201085
8+
malhar.bhattacharyya@ucdconnect.ie
9+
GitHub ID: mynah-bird
10+
11+
Ben McDowell – Student ID: 21495144
12+
ben.mcdowell@ucdconnect.ie
13+
GitHub ID: Benmc1
14+
*/
15+
116
package cascadia;
217

318
import cascadia.scoring.ScoreToken;
4-
import java.util.ArrayList;
519
import java.util.Arrays;
620
import java.util.List;
721
import java.util.Random;
@@ -14,15 +28,10 @@ public class TokenBot {
1428
public static final int NUM_TOKEN_STRATS = 2;
1529
// we don't need the token score map atm, but I'm keeping it in case we need it in the
1630
// future
17-
private final List<ValueSortedMap<Integer, Integer>> deckScoreMap = new ArrayList<>();
1831
private final int[] bestPlacementIds = new int[Constants.MAX_DECK_SIZE];
1932
private final int[] rankedTokens = new int[Constants.MAX_DECK_SIZE];
2033

2134
public TokenBot() {
22-
for (int j = 0; j < Constants.NUM_TOKEN_TYPES; j++) {
23-
deckScoreMap.add(new ValueSortedMap<>());
24-
}
25-
2635
// initialise it to -1, so we know when we aren't able to place a token
2736
Arrays.fill(bestPlacementIds, -1);
2837
}
@@ -45,7 +54,6 @@ public int[] chooseStrategy(Player player, Player nextPlayer) {
4554

4655
if (!BotTimer.isTimeLeft()) {
4756
System.out.println("No time left!");
48-
System.out.println(Arrays.toString(rankedTokens));
4957
return rankedTokens;
5058
}
5159

@@ -77,7 +85,6 @@ private void destructiveTokenStrat(List<WildlifeToken> deckTokens, Player nextPl
7785
private void rankDeckTokens(Player player, boolean isConst, List<WildlifeToken> deckTokens) {
7886
int[] scores = new int[Constants.MAX_DECK_SIZE];
7987
for (int i = 0; i < Constants.MAX_DECK_SIZE; i++) {
80-
deckScoreMap.get(i).clear(); // clear the scores in the map before calculating
8188
// checks whether it is possible to place a certain token on the map. If it
8289
// is not, the score for that token is largely decreased
8390
if (player.getMap().numPossibleTokenPlacements(deckTokens.get(i)) == 0) {
@@ -94,7 +101,7 @@ private void rankDeckTokens(Player player, boolean isConst, List<WildlifeToken>
94101
}
95102

96103
/*
97-
Tries place the token in all possible positions and returns the greatest increase
104+
Tries to place the token in all possible positions and returns the greatest increase
98105
in score
99106
*/
100107
private int calculatePlacementScoresAndReturnMax(WildlifeToken token, Player player,
@@ -109,7 +116,6 @@ private int calculatePlacementScoresAndReturnMax(WildlifeToken token, Player pla
109116

110117
int prevScore = ScoreToken.calculateScore(player.getMap(), token);
111118

112-
// oh boy the time complexity of this is. not good.
113119
for (HabitatTile tile : possibleTiles) {
114120
Player tmp = new Player("temp");
115121
tmp.getMap().setTileBoard(PlayerMap.deepCopy(player.getMap().getTileBoardPosition()));
@@ -120,7 +126,6 @@ private int calculatePlacementScoresAndReturnMax(WildlifeToken token, Player pla
120126
// we get an extra point for getting a nature token from placing on a keystone tile
121127
scoreDiff++;
122128
}
123-
deckScoreMap.get(idx).put(tile.getTileID(), scoreDiff);
124129

125130
// set it to scores that are equal to as well, as for example if the
126131
// diff is 0 when we place a token, it's still better to place a token
@@ -148,24 +153,17 @@ private int calculatePlacementScoresAndReturnMax(WildlifeToken token, Player pla
148153
// converts an arraylist of scores (e.g. [1,10,5,6]) to their relative
149154
// ranking (e.g. [0,3,1,2])
150155
private void convertToRank(int[] scores, List<WildlifeToken> deckTokens) {
151-
System.out.println("-----");
152-
System.out.println(Arrays.toString(scores));
153156

154157
List<Integer> sorted = Arrays.stream(scores).sorted().boxed().toList();
155-
System.out.println("Sorted: " + sorted);
156158

157159
for (int i = 0; i < sorted.size(); i++) {
158160
scores[i] = sorted.indexOf(scores[i]);
159161
}
160-
System.out.println(Arrays.toString(scores));
161162

162163
// for (int i = 0; i < scores.length; i++) {
163164
// rankedTokens[i] = 4 - scores[i];
164165
// }
165166
System.arraycopy(scores, 0, rankedTokens, 0, rankedTokens.length);
166-
167-
System.out.println(Arrays.toString(rankedTokens));
168-
System.out.println("-----");
169167
}
170168

171169
/**
@@ -179,14 +177,14 @@ private void convertToRank(int[] scores, List<WildlifeToken> deckTokens) {
179177
*/
180178
public int getBestPlacement(WildlifeToken token, int deckIdx, Player player) {
181179
int id = bestPlacementIds[deckIdx];
182-
System.out.println(Arrays.toString(bestPlacementIds));
180+
// System.out.println(Arrays.toString(bestPlacementIds));
183181

184182
// if we were doing the destructive method (finding the best token for the opponent
185183
// and taking it) we want to find where we can place that token on our map
186184
if (id == -2) {
187185
calculatePlacementScoresAndReturnMax(token, player, true, deckIdx);
188186
id = bestPlacementIds[deckIdx];
189-
System.out.println(Arrays.toString(bestPlacementIds));
187+
// System.out.println(Arrays.toString(bestPlacementIds));
190188
}
191189
return id;
192190
}

0 commit comments

Comments
 (0)