Add files via upload
This commit is contained in:
parent
e9a9181cd0
commit
f2fe06f720
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 369 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,254 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class specifies the most important and basic parameters of the game
|
||||||
|
* Catan.
|
||||||
|
* <p>
|
||||||
|
* The class provides definitions such as for the type and number of resource
|
||||||
|
* cards or the number of available road elements per player. Furthermore, it
|
||||||
|
* provides a dice number to field and a field to land type mapping for the
|
||||||
|
* standard setup detailed <a href=
|
||||||
|
* "https://www.catan.de/files/downloads/4002051693602_catan_-_das_spiel_0.pdf">here</a>
|
||||||
|
* </p>
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Config {
|
||||||
|
// Minimum number of players
|
||||||
|
// Note: The max. number is equal to the number of factions (see Faction enum)
|
||||||
|
public static final int MIN_NUMBER_OF_PLAYERS = 2;
|
||||||
|
|
||||||
|
// Initial thief position (on the desert field)
|
||||||
|
public static final Point INITIAL_THIEF_POSITION = new Point(7, 11);
|
||||||
|
|
||||||
|
// Available factions
|
||||||
|
public enum Faction {
|
||||||
|
RED("rr"), BLUE("bb"), GREEN("gg"), YELLOW("yy");
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Faction(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESOURCE CARD DECK
|
||||||
|
public static final Map<Resource, Integer> INITIAL_RESOURCE_CARDS_BANK = Map.of(Resource.LUMBER, 19,
|
||||||
|
Resource.BRICK, 19, Resource.WOOL, 19, Resource.GRAIN, 19, Resource.ORE, 19);
|
||||||
|
|
||||||
|
// SPECIFICATION OF AVAILABLE RESOURCE TYPES
|
||||||
|
/**
|
||||||
|
* This {@link Enum} specifies the available resource types in the game.
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public enum Resource {
|
||||||
|
GRAIN("GR"), WOOL("WL"), LUMBER("LU"), ORE("OR"), BRICK("BR");
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Resource(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPECIFICATION OF AVAILABLE LAND TYPES
|
||||||
|
/**
|
||||||
|
* This {@link Enum} specifies the available lands in the game. Some land types
|
||||||
|
* produce resources (e.g., {@link Land#FOREST}, others do not (e.g.,
|
||||||
|
* {@link Land#WATER}.
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public enum Land {
|
||||||
|
FOREST(Resource.LUMBER), PASTURE(Resource.WOOL), FIELDS(Resource.GRAIN),
|
||||||
|
MOUNTAIN(Resource.ORE), HILLS(Resource.BRICK), WATER("~~"), DESERT("--");
|
||||||
|
|
||||||
|
private Resource resource = null;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Land(Resource resource) {
|
||||||
|
this(resource.toString());
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Land(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Resource} that this land provides or null,
|
||||||
|
* if it does not provide any.
|
||||||
|
*
|
||||||
|
* @return the {@link Resource} or null
|
||||||
|
*/
|
||||||
|
public Resource getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STRUCTURES (with costs)
|
||||||
|
private static final int NUMBER_OF_ROADS_PER_PLAYER = 15;
|
||||||
|
private static final int NUMBER_OF_SETTLEMENTS_PER_PLAYER = 5;
|
||||||
|
private static final int NUMBER_OF_CITIES_PER_PLAYER = 4;
|
||||||
|
public static final int MAX_CARDS_IN_HAND_NO_DROP = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum models the different structures that can be built.
|
||||||
|
* <p>
|
||||||
|
* The enum provides information about the cost of a structure and how many of
|
||||||
|
* these structures are available per player.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public enum Structure {
|
||||||
|
SETTLEMENT(List.of(Resource.LUMBER, Resource.BRICK, Resource.WOOL, Resource.GRAIN),
|
||||||
|
NUMBER_OF_SETTLEMENTS_PER_PLAYER),
|
||||||
|
CITY(List.of(Resource.ORE, Resource.ORE, Resource.ORE, Resource.GRAIN, Resource.GRAIN),
|
||||||
|
NUMBER_OF_CITIES_PER_PLAYER),
|
||||||
|
ROAD(List.of(Resource.LUMBER, Resource.BRICK), NUMBER_OF_ROADS_PER_PLAYER);
|
||||||
|
|
||||||
|
private List<Resource> costs;
|
||||||
|
private int stockPerPlayer;
|
||||||
|
|
||||||
|
private Structure(List<Resource> costs, int stockPerPlayer) {
|
||||||
|
this.costs = costs;
|
||||||
|
this.stockPerPlayer = stockPerPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the build costs of this structure.
|
||||||
|
* <p>
|
||||||
|
* Each list entry represents a resource card. The value of an entry (e.g., {@link Resource#LUMBER})
|
||||||
|
* identifies the resource type of the card.
|
||||||
|
* </p>
|
||||||
|
* @return the build costs
|
||||||
|
*/
|
||||||
|
public List<Resource> getCosts() {
|
||||||
|
return costs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the build costs of this structure.
|
||||||
|
*
|
||||||
|
* @return the build costs in terms of the number of resource cards per resource type
|
||||||
|
*/
|
||||||
|
public Map<Resource, Long> getCostsAsMap() {
|
||||||
|
return costs.stream()
|
||||||
|
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of pieces that are available of a certain structure (per
|
||||||
|
* player). For example, there are {@link Config#NUMBER_OF_ROADS_PER_PLAYER}
|
||||||
|
* pieces of the structure {@link Structure#ROAD} per player.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return the stock per player
|
||||||
|
*/
|
||||||
|
public int getStockPerPlayer() {
|
||||||
|
return stockPerPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STANDARD FIXED DICE NUMBER TO FIELD SETUP
|
||||||
|
/**
|
||||||
|
* Returns a mapping of the dice values per field.
|
||||||
|
*
|
||||||
|
* @return the dice values per field
|
||||||
|
*/
|
||||||
|
public static final Map<Point, Integer> getStandardDiceNumberPlacement() {
|
||||||
|
Map<Point, Integer> assignment = new HashMap<>();
|
||||||
|
assignment.put(new Point(4, 8), 2);
|
||||||
|
assignment.put(new Point(7, 5), 3);
|
||||||
|
assignment.put(new Point(8, 14), 3);
|
||||||
|
assignment.put(new Point(6, 8), 4);
|
||||||
|
assignment.put(new Point(7, 17), 4);
|
||||||
|
|
||||||
|
assignment.put(new Point(3, 11), 5);
|
||||||
|
assignment.put(new Point(8, 8), 5);
|
||||||
|
assignment.put(new Point(5, 5), 6);
|
||||||
|
assignment.put(new Point(9, 11), 6);
|
||||||
|
|
||||||
|
assignment.put(new Point(7, 11), 7);
|
||||||
|
assignment.put(new Point(9, 5), 8);
|
||||||
|
assignment.put(new Point(5, 17), 8);
|
||||||
|
assignment.put(new Point(5, 11), 9);
|
||||||
|
assignment.put(new Point(11, 11), 9);
|
||||||
|
assignment.put(new Point(4, 14), 10);
|
||||||
|
assignment.put(new Point(10, 8), 10);
|
||||||
|
assignment.put(new Point(6, 14), 11);
|
||||||
|
assignment.put(new Point(9, 17), 11);
|
||||||
|
assignment.put(new Point(10, 14), 12);
|
||||||
|
return Collections.unmodifiableMap(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STANDARD FIXED LAND SETUP
|
||||||
|
/**
|
||||||
|
* Returns the field (coordinate) to {@link Land} mapping for the <a href=
|
||||||
|
* "https://www.catan.de/files/downloads/4002051693602_catan_-_das_spiel_0.pdf">standard
|
||||||
|
* setup</a> of the game Catan..
|
||||||
|
*
|
||||||
|
* @return the field to {@link Land} mapping for the standard setup
|
||||||
|
*/
|
||||||
|
public static final Map<Point, Land> getStandardLandPlacement() {
|
||||||
|
Map<Point, Land> assignment = new HashMap<>();
|
||||||
|
Point[] water = { new Point(4, 2), new Point(6, 2), new Point(8, 2), new Point(10, 2),
|
||||||
|
new Point(3, 5), new Point(11, 5), new Point(2, 8), new Point(12, 8), new Point(1, 11),
|
||||||
|
new Point(13, 11), new Point(2, 14), new Point(12, 14), new Point(3, 17), new Point(11, 17),
|
||||||
|
new Point(4, 20), new Point(6, 20), new Point(8, 20), new Point(10, 20) };
|
||||||
|
|
||||||
|
for (Point p : water) {
|
||||||
|
assignment.put(p, Land.WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
assignment.put(new Point(5, 5), Land.FOREST);
|
||||||
|
assignment.put(new Point(7, 5), Land.PASTURE);
|
||||||
|
assignment.put(new Point(9, 5), Land.PASTURE);
|
||||||
|
|
||||||
|
assignment.put(new Point(4, 8), Land.FIELDS);
|
||||||
|
assignment.put(new Point(6, 8), Land.MOUNTAIN);
|
||||||
|
assignment.put(new Point(8, 8), Land.FIELDS);
|
||||||
|
assignment.put(new Point(10, 8), Land.FOREST);
|
||||||
|
|
||||||
|
assignment.put(new Point(3, 11), Land.FOREST);
|
||||||
|
assignment.put(new Point(5, 11), Land.HILLS);
|
||||||
|
assignment.put(new Point(7, 11), Land.DESERT);
|
||||||
|
assignment.put(new Point(9, 11), Land.MOUNTAIN);
|
||||||
|
assignment.put(new Point(11, 11), Land.FIELDS);
|
||||||
|
|
||||||
|
assignment.put(new Point(4, 14), Land.FIELDS);
|
||||||
|
assignment.put(new Point(6, 14), Land.MOUNTAIN);
|
||||||
|
assignment.put(new Point(8, 14), Land.FOREST);
|
||||||
|
assignment.put(new Point(10, 14), Land.PASTURE);
|
||||||
|
|
||||||
|
assignment.put(new Point(5, 17), Land.PASTURE);
|
||||||
|
assignment.put(new Point(7, 17), Land.HILLS);
|
||||||
|
assignment.put(new Point(9, 17), Land.HILLS);
|
||||||
|
|
||||||
|
return Collections.unmodifiableMap(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.Config.Land;
|
||||||
|
import ch.zhaw.hexboard.Label;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.beryx.textio.TextIO;
|
||||||
|
import org.beryx.textio.TextIoFactory;
|
||||||
|
import org.beryx.textio.TextTerminal;
|
||||||
|
|
||||||
|
public class Dummy {
|
||||||
|
|
||||||
|
public enum Actions {
|
||||||
|
SHOW, QUIT
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run() {
|
||||||
|
TextIO textIO = TextIoFactory.getTextIO();
|
||||||
|
TextTerminal<?> textTerminal = textIO.getTextTerminal();
|
||||||
|
|
||||||
|
SiedlerBoard board = new SiedlerBoard();
|
||||||
|
board.addField(new Point(2, 2), Land.FOREST);
|
||||||
|
board.setCorner(new Point(3, 3), "RR");
|
||||||
|
board.setEdge(new Point(2, 0), new Point(3, 1), "r");
|
||||||
|
board.addFieldAnnotation(new Point(2, 2), new Point(3, 1), "AA");
|
||||||
|
|
||||||
|
Map<Point, Label> lowerFieldLabel = new HashMap<>();
|
||||||
|
lowerFieldLabel.put(new Point(2, 2), new Label('0', '9'));
|
||||||
|
SiedlerBoardTextView view = new SiedlerBoardTextView(board);
|
||||||
|
|
||||||
|
for (Map.Entry<Point, Label> e : lowerFieldLabel.entrySet()) {
|
||||||
|
view.setLowerFieldLabel(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean running = true;
|
||||||
|
while (running) {
|
||||||
|
switch (getEnumValue(textIO, Actions.class)) {
|
||||||
|
case SHOW:
|
||||||
|
textTerminal.println(view.toString());
|
||||||
|
break;
|
||||||
|
case QUIT:
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Internal error found - Command not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textIO.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> T getEnumValue(TextIO textIO, Class<T> commands) {
|
||||||
|
return textIO.newEnumInputReader(commands).read("What would you like to do?");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Dummy().run();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.Config.Land;
|
||||||
|
import ch.zhaw.hexboard.HexBoard;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SiedlerBoard extends HexBoard<Land, String, String, String> {
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: Add fields, constructors and methods as you see fit. Do NOT change the signature
|
||||||
|
// of the methods below.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fields associated with the specified dice value.
|
||||||
|
*
|
||||||
|
* @param dice the dice value
|
||||||
|
* @return the fields associated with the dice value
|
||||||
|
*/
|
||||||
|
public List<Point> getFieldsForDiceValue(int dice) {
|
||||||
|
//TODO: Implement.
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Land}s adjacent to the specified corner.
|
||||||
|
*
|
||||||
|
* @param corner the corner
|
||||||
|
* @return the list with the adjacent {@link Land}s
|
||||||
|
*/
|
||||||
|
public List<Land> getLandsForCorner(Point corner) {
|
||||||
|
//TODO: Implement.
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.Config.Land;
|
||||||
|
import ch.zhaw.hexboard.HexBoardTextView;
|
||||||
|
|
||||||
|
public class SiedlerBoardTextView extends HexBoardTextView<Land, String, String, String> {
|
||||||
|
|
||||||
|
public SiedlerBoardTextView(SiedlerBoard board) {
|
||||||
|
super(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.Config.Faction;
|
||||||
|
import ch.zhaw.catan.Config.Resource;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class performs all actions related to modifying the game state.
|
||||||
|
*
|
||||||
|
* TODO: (your documentation)
|
||||||
|
*
|
||||||
|
* @author TODO
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SiedlerGame {
|
||||||
|
static final int FOUR_TO_ONE_TRADE_OFFER = 4;
|
||||||
|
static final int FOUR_TO_ONE_TRADE_WANT = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a SiedlerGame game state object.
|
||||||
|
*
|
||||||
|
* @param winPoints the number of points required to win the game
|
||||||
|
* @param numberOfPlayers the number of players
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if winPoints is lower than
|
||||||
|
* three or players is not between two and four
|
||||||
|
*/
|
||||||
|
public SiedlerGame(int winPoints, int numberOfPlayers) {
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to the next player in the defined sequence of players.
|
||||||
|
*/
|
||||||
|
public void switchToNextPlayer() {
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to the previous player in the defined sequence of players.
|
||||||
|
*/
|
||||||
|
public void switchToPreviousPlayer() {
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Faction}s of the active players.
|
||||||
|
*
|
||||||
|
* <p>The order of the player's factions in the list must
|
||||||
|
* correspond to the oder in which they play.
|
||||||
|
* Hence, the player that sets the first settlement must be
|
||||||
|
* at position 0 in the list etc.
|
||||||
|
*
|
||||||
|
* <strong>Important note:</strong> The list must contain the
|
||||||
|
* factions of active players only.</p>
|
||||||
|
*
|
||||||
|
* @return the list with player's factions
|
||||||
|
*/
|
||||||
|
public List<Faction> getPlayerFactions() {
|
||||||
|
// TODO: Implement
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the game board.
|
||||||
|
*
|
||||||
|
* @return the game board
|
||||||
|
*/
|
||||||
|
public SiedlerBoard getBoard() {
|
||||||
|
// TODO: Implement
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Faction} of the current player.
|
||||||
|
*
|
||||||
|
* @return the faction of the current player
|
||||||
|
*/
|
||||||
|
public Faction getCurrentPlayerFaction() {
|
||||||
|
// TODO: Implement
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns how many resource cards of the specified type
|
||||||
|
* the current player owns.
|
||||||
|
*
|
||||||
|
* @param resource the resource type
|
||||||
|
* @return the number of resource cards of this type
|
||||||
|
*/
|
||||||
|
public int getCurrentPlayerResourceStock(Resource resource) {
|
||||||
|
// TODO: Implement
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a settlement in the founder's phase (phase II) of the game.
|
||||||
|
*
|
||||||
|
* <p>The placement does not cost any resource cards. If payout is
|
||||||
|
* set to true, for each adjacent resource-producing field, a resource card of the
|
||||||
|
* type of the resource produced by the field is taken from the bank (if available) and added to
|
||||||
|
* the players' stock of resource cards.</p>
|
||||||
|
*
|
||||||
|
* @param position the position of the settlement
|
||||||
|
* @param payout if true, the player gets one resource card per adjacent resource-producing field
|
||||||
|
* @return true, if the placement was successful
|
||||||
|
*/
|
||||||
|
public boolean placeInitialSettlement(Point position, boolean payout) {
|
||||||
|
// TODO: Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a road in the founder's phase (phase II) of the game.
|
||||||
|
* The placement does not cost any resource cards.
|
||||||
|
*
|
||||||
|
* @param roadStart position of the start of the road
|
||||||
|
* @param roadEnd position of the end of the road
|
||||||
|
* @return true, if the placement was successful
|
||||||
|
*/
|
||||||
|
public boolean placeInitialRoad(Point roadStart, Point roadEnd) {
|
||||||
|
// TODO: Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method takes care of actions depending on the dice throw result.
|
||||||
|
*
|
||||||
|
* A key action is the payout of the resource cards to the players
|
||||||
|
* according to the payout rules of the game. This includes the
|
||||||
|
* "negative payout" in case a 7 is thrown and a player has more than
|
||||||
|
* {@link Config#MAX_CARDS_IN_HAND_NO_DROP} resource cards.
|
||||||
|
*
|
||||||
|
* If a player does not get resource cards, the list for this players'
|
||||||
|
* {@link Faction} is <b>an empty list (not null)</b>!.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The payout rules of the game take into account factors such as, the number
|
||||||
|
* of resource cards currently available in the bank, settlement types
|
||||||
|
* (settlement or city), and the number of players that should get resource
|
||||||
|
* cards of a certain type (relevant if there are not enough left in the bank).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param dicethrow the resource cards that have been distributed to the players
|
||||||
|
* @return the resource cards added to the stock of the different players
|
||||||
|
*/
|
||||||
|
public Map<Faction, List<Resource>> throwDice(int dicethrow) {
|
||||||
|
// TODO: Implement
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a settlement at the specified position on the board.
|
||||||
|
*
|
||||||
|
* <p>The settlement can be built if:
|
||||||
|
* <ul>
|
||||||
|
* <li> the player possesses the required resource cards</li>
|
||||||
|
* <li> a settlement to place on the board</li>
|
||||||
|
* <li> the specified position meets the build rules for settlements</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param position the position of the settlement
|
||||||
|
* @return true, if the placement was successful
|
||||||
|
*/
|
||||||
|
public boolean buildSettlement(Point position) {
|
||||||
|
// TODO: Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a city at the specified position on the board.
|
||||||
|
*
|
||||||
|
* <p>The city can be built if:
|
||||||
|
* <ul>
|
||||||
|
* <li> the player possesses the required resource cards</li>
|
||||||
|
* <li> a city to place on the board</li>
|
||||||
|
* <li> the specified position meets the build rules for cities</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param position the position of the city
|
||||||
|
* @return true, if the placement was successful
|
||||||
|
*/
|
||||||
|
public boolean buildCity(Point position) {
|
||||||
|
// TODO: OPTIONAL task - Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a road at the specified position on the board.
|
||||||
|
*
|
||||||
|
* <p>The road can be built if:
|
||||||
|
* <ul>
|
||||||
|
* <li> the player possesses the required resource cards</li>
|
||||||
|
* <li> a road to place on the board</li>
|
||||||
|
* <li> the specified position meets the build rules for roads</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param roadStart the position of the start of the road
|
||||||
|
* @param roadEnd the position of the end of the road
|
||||||
|
* @return true, if the placement was successful
|
||||||
|
*/
|
||||||
|
public boolean buildRoad(Point roadStart, Point roadEnd) {
|
||||||
|
// TODO: Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trades in {@link #FOUR_TO_ONE_TRADE_OFFER} resource cards of the
|
||||||
|
* offered type for {@link #FOUR_TO_ONE_TRADE_WANT} resource cards of the wanted type.
|
||||||
|
*
|
||||||
|
* The trade only works when bank and player possess the resource cards
|
||||||
|
* for the trade before the trade is executed.
|
||||||
|
*
|
||||||
|
* @param offer offered type
|
||||||
|
* @param want wanted type
|
||||||
|
* @return true, if the trade was successful
|
||||||
|
*/
|
||||||
|
public boolean tradeWithBankFourToOne(Resource offer, Resource want) {
|
||||||
|
// TODO: Implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the winner of the game, if any.
|
||||||
|
*
|
||||||
|
* @return the winner of the game or null, if there is no winner (yet)
|
||||||
|
*/
|
||||||
|
public Faction getWinner() {
|
||||||
|
// TODO: Implement
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places the thief on the specified field and steals a random resource card (if
|
||||||
|
* the player has such cards) from a random player with a settlement at that
|
||||||
|
* field (if there is a settlement) and adds it to the resource cards of the
|
||||||
|
* current player.
|
||||||
|
*
|
||||||
|
* @param field the field on which to place the thief
|
||||||
|
* @return false, if the specified field is not a field or the thief cannot be
|
||||||
|
* placed there (e.g., on water)
|
||||||
|
*/
|
||||||
|
public boolean placeThiefAndStealCard(Point field) {
|
||||||
|
//TODO: Implement (or longest road functionality)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class models an edge on @see ch.zhaw.hexboard.HexBoard.
|
||||||
|
* <p>
|
||||||
|
* Edges are non-directional and can be created by providing the two points that
|
||||||
|
* span an edge on the hex-grid defined by @see ch.zhaw.hexboard.HexBoard
|
||||||
|
* </p>
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Edge {
|
||||||
|
private Point start;
|
||||||
|
private Point end;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an edge between the two points.
|
||||||
|
*
|
||||||
|
* @param p1 first point
|
||||||
|
* @param p2 second point
|
||||||
|
* @throws IllegalArgumentException if the points are not non-null or not a
|
||||||
|
* valid point for an edge on the grid defined
|
||||||
|
* by @see ch.zhaw.hexboard.HexBoard
|
||||||
|
*/
|
||||||
|
public Edge(Point p1, Point p2) {
|
||||||
|
if (Edge.isEdge(p1, p2)) {
|
||||||
|
if (p1.x > p2.x || (p1.x == p2.x && p1.y > p2.y)) {
|
||||||
|
this.start = new Point(p2);
|
||||||
|
this.end = new Point(p1);
|
||||||
|
} else {
|
||||||
|
this.start = new Point(p1);
|
||||||
|
this.end = new Point(p2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Coordinates " + p1 + " and " + p2 + " are not coordinates of an edge.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isEdge(Point p1, Point p2) {
|
||||||
|
boolean isEdge = false;
|
||||||
|
if (p1 != null && p2 != null && HexBoard.isCornerCoordinate(p1)
|
||||||
|
&& HexBoard.isCornerCoordinate(p2)) {
|
||||||
|
int xdistance = Math.abs(p1.x - p2.x);
|
||||||
|
int ydistance = Math.abs(p1.y - p2.y);
|
||||||
|
boolean isVerticalEdge = xdistance == 0 && ydistance == 2;
|
||||||
|
boolean isDiagonalEdge = xdistance == 1 && ydistance == 1;
|
||||||
|
isEdge = isVerticalEdge || isDiagonalEdge;
|
||||||
|
}
|
||||||
|
return isEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEdgePoint(Point p1) {
|
||||||
|
return start.equals(p1) || end.equals(p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#hashCode()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((end == null) ? 0 : end.hashCode());
|
||||||
|
result = prime * result + ((start == null) ? 0 : start.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
Edge other = (Edge) obj;
|
||||||
|
if (end == null) {
|
||||||
|
if (other.end != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!end.equals(other.end)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (start == null) {
|
||||||
|
if (other.start != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!start.equals(other.start)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Edge [start=" + start + ", end=" + end + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class models an annotation for the hex-fields of the hex-grid defined
|
||||||
|
* by @see ch.zhaw.hexboard.HexBoard
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class FieldAnnotationPosition {
|
||||||
|
private Point field;
|
||||||
|
private Point corner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a field annotation for the specified field.
|
||||||
|
*
|
||||||
|
* @param field the field to be annotated
|
||||||
|
* @param corner the location of the annotation
|
||||||
|
* @throws IllegalArgumentException if arguments are null or not valid
|
||||||
|
* field/corner coordinates (@see
|
||||||
|
* ch.zhaw.hexboard.HexBoard).
|
||||||
|
*/
|
||||||
|
public FieldAnnotationPosition(Point field, Point corner) {
|
||||||
|
if (HexBoard.isCorner(field, corner)) {
|
||||||
|
this.field = field;
|
||||||
|
this.corner = corner;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("" + field + " is not a field coordinate or " + corner
|
||||||
|
+ " is not a corner of the field.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the provided coordinate matches the position of the annotation
|
||||||
|
* within the field.
|
||||||
|
*
|
||||||
|
* @param p the corner coordinate
|
||||||
|
* @return true, if they match
|
||||||
|
*/
|
||||||
|
public boolean isCorner(Point p) {
|
||||||
|
return corner.equals(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the provided coordinate matches the field coordinate of this
|
||||||
|
* annotation.
|
||||||
|
*
|
||||||
|
* @param p a field coordinate
|
||||||
|
* @return true, if they match
|
||||||
|
*/
|
||||||
|
public boolean isField(Point p) {
|
||||||
|
return field.equals(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#hashCode()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((field == null) ? 0 : field.hashCode());
|
||||||
|
result = prime * result + ((corner == null) ? 0 : corner.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldAnnotationPosition other = (FieldAnnotationPosition) obj;
|
||||||
|
if (field == null) {
|
||||||
|
if (other.field != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!field.equals(other.field)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (corner == null) {
|
||||||
|
if (other.corner != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!corner.equals(other.corner)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FieldAnnotationPosition [field=" + field + ", corner=" + corner + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,541 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* <p>
|
||||||
|
* This class represents a simple generic hexagonal game board.
|
||||||
|
* </p>
|
||||||
|
* <p>The game board uses a fixed coordinate system which is structured as follows:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 0 1 2 3 4 5 6 7 8
|
||||||
|
* | | | | | | | | | ...
|
||||||
|
*
|
||||||
|
* 0---- C C C C C
|
||||||
|
* \ / \ / \ / \ / \
|
||||||
|
* 1---- C C C C C
|
||||||
|
*
|
||||||
|
* 2---- F | F | F | F | F | ...
|
||||||
|
*
|
||||||
|
* 3---- C C C C C
|
||||||
|
* / \ / \ / \ / \ /
|
||||||
|
* 4---- C C C C C
|
||||||
|
*
|
||||||
|
* 5---- | F | F | F | F | F ...
|
||||||
|
*
|
||||||
|
* 6---- C C C C C
|
||||||
|
* \ / \ / \ / \ / \
|
||||||
|
* 7---- C C C C C
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Fields <strong>F</strong> and corners <strong>C</strong> can be retrieved
|
||||||
|
* using their coordinates ({@link java.awt.Point}) on the board. Edges can be
|
||||||
|
* retrieved using the coordinates of the two corners they connect.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When created, the board is empty (no fields added). To add fields, the
|
||||||
|
* #{@link #addField(Point, Object)} function can be used. Edges and corners are
|
||||||
|
* automatically created when adding a field. They cannot be created/removed
|
||||||
|
* individually. When adding a field, edges and corners that were already
|
||||||
|
* created, e.g., because adding an adjacent field already created them, are
|
||||||
|
* left untouched.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Fields, edges and corners can store an object of the type of the
|
||||||
|
* corresponding type parameter each.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Furthermore, the hexagonal game board can store six additional objects, so
|
||||||
|
* called annotations, for each field. These objects are identified by the
|
||||||
|
* coordinates of the field and the corner. Hence, they can be thought of being
|
||||||
|
* located between the center and the respective corner. Or in other words,
|
||||||
|
* their positions correspond to the positions N, NW, SW, NE, NW, SE and NE in
|
||||||
|
* the below visualization of a field.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* SW (C) SE
|
||||||
|
* / N \
|
||||||
|
* (C) NW NE (C)
|
||||||
|
* | F |
|
||||||
|
* | |
|
||||||
|
* (C) SW SE (C)
|
||||||
|
* \ S /
|
||||||
|
* NW (C) NE
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param <F> Data type for the field data objects
|
||||||
|
* @param <C> Data type for the corner data objects
|
||||||
|
* @param <E> Data type for the edge data objects
|
||||||
|
* @param <A> Data type for the annotation data objects
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HexBoard<F, C, E, A> {
|
||||||
|
private int maxCoordinateX = 0;
|
||||||
|
private int maxCoordinateY = 0;
|
||||||
|
private final Map<Point, F> field;
|
||||||
|
private final Map<Point, C> corner;
|
||||||
|
private final Map<Edge, E> edge;
|
||||||
|
private final Map<FieldAnnotationPosition, A> annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty hexagonal board.
|
||||||
|
*/
|
||||||
|
public HexBoard() {
|
||||||
|
field = new HashMap<>();
|
||||||
|
corner = new HashMap<>();
|
||||||
|
edge = new HashMap<>();
|
||||||
|
annotation = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a field to the board and creates the surrounding (empty) corners and
|
||||||
|
* edges if they do not yet exist Note: Corners and edges of a field might
|
||||||
|
* already have been created while creating adjacent fields.
|
||||||
|
*
|
||||||
|
* @param center Coordinate of the center of a field on the unit grid
|
||||||
|
* @param element Data element to be stored for this field
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if center is not the center of a field, the
|
||||||
|
* field already exists or data is null
|
||||||
|
*/
|
||||||
|
public void addField(Point center, F element) {
|
||||||
|
if (isFieldCoordinate(center) && !field.containsKey(center)) {
|
||||||
|
field.put(center, element);
|
||||||
|
maxCoordinateX = Math.max(center.x + 1, maxCoordinateX);
|
||||||
|
maxCoordinateY = Math.max(center.y + 2, maxCoordinateY);
|
||||||
|
// add (empty) edge, if they do not yet exist
|
||||||
|
for (Edge e : constructEdgesOfField(center)) {
|
||||||
|
if (!edge.containsKey(e)) {
|
||||||
|
edge.put(e, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add (empty) corners, if they do not yet exist
|
||||||
|
for (Point p : getCornerCoordinatesOfField(center)) {
|
||||||
|
if (!corner.containsKey(p)) {
|
||||||
|
corner.put(p, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Coordinates are not the center of a field, the field already exists or data is null - ("
|
||||||
|
+ center.x + ", " + center.y + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an annotation for the specified field and corner.
|
||||||
|
*
|
||||||
|
* @param center the center of the field
|
||||||
|
* @param corner the corner of the field
|
||||||
|
* @param data the annotation
|
||||||
|
* @throws IllegalArgumentException if the field does not exists or when the
|
||||||
|
* annotation already exists
|
||||||
|
*/
|
||||||
|
public void addFieldAnnotation(Point center, Point corner, A data) {
|
||||||
|
FieldAnnotationPosition annotationPosition = new FieldAnnotationPosition(center, corner);
|
||||||
|
if (!annotation.containsKey(annotationPosition)) {
|
||||||
|
annotation.put(annotationPosition, data);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Annotation: " + annotation + " already exists for field "
|
||||||
|
+ center + " and position " + corner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an annotation for the specified field and corner.
|
||||||
|
*
|
||||||
|
* @param center the center of the field
|
||||||
|
* @param corner the corner of the field
|
||||||
|
* @return the annotation
|
||||||
|
* @throws IllegalArgumentException if coordinates are not a field and
|
||||||
|
* corresponding corner coordinate
|
||||||
|
*/
|
||||||
|
public A getFieldAnnotation(Point center, Point corner) {
|
||||||
|
return annotation.get(new FieldAnnotationPosition(center, corner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get field annotation whose position information includes the specified corner.
|
||||||
|
*
|
||||||
|
* @param corner the corner
|
||||||
|
* @return a list with the annotations that are not null
|
||||||
|
* @throws IllegalArgumentException if corner is not a corner
|
||||||
|
*/
|
||||||
|
public List<A> getFieldAnnotationsForCorner(Point corner) {
|
||||||
|
List<A> list = new LinkedList<>();
|
||||||
|
for (Entry<FieldAnnotationPosition, A> entry : annotation.entrySet()) {
|
||||||
|
if (entry.getKey().isCorner(corner) && entry.getValue() != null) {
|
||||||
|
list.add(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all field annotation of the specified field.
|
||||||
|
*
|
||||||
|
* @param center the field
|
||||||
|
* @return a list with the annotations that are not null
|
||||||
|
* @throws IllegalArgumentException if center is not a field
|
||||||
|
*/
|
||||||
|
public List<A> getFieldAnnotationsForField(Point center) {
|
||||||
|
List<A> list = new LinkedList<>();
|
||||||
|
for (Entry<FieldAnnotationPosition, A> entry : annotation.entrySet()) {
|
||||||
|
if (entry.getKey().isField(center) && entry.getValue() != null) {
|
||||||
|
list.add(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the field at the specified position exists.
|
||||||
|
*
|
||||||
|
* @param center the field
|
||||||
|
* @return false, if the field does not exist or the position is not a field
|
||||||
|
*/
|
||||||
|
public boolean hasField(Point center) {
|
||||||
|
if (!HexBoard.isFieldCoordinate(center)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return field.containsKey(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isFieldCoordinate(Point position) {
|
||||||
|
boolean isYFieldCoordinateEven = (position.y - 2) % 6 == 0;
|
||||||
|
boolean isYFieldCoordinateOdd = (position.y - 5) % 6 == 0;
|
||||||
|
boolean isXFieldCoordinateEven = position.x % 2 == 0;
|
||||||
|
boolean isXFieldCoordinateOdd = (position.x - 1) % 2 == 0;
|
||||||
|
|
||||||
|
return (position.y >= 2 && position.x >= 1)
|
||||||
|
&& (isYFieldCoordinateEven && isXFieldCoordinateEven)
|
||||||
|
|| (isYFieldCoordinateOdd && isXFieldCoordinateOdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isCornerCoordinate(Point p) {
|
||||||
|
// On the horizontal center lines, no edge points exist
|
||||||
|
boolean isOnFieldCenterLineHorizontal = (p.y - 2) % 3 == 0;
|
||||||
|
|
||||||
|
// On the vertical center lines, edge points exist
|
||||||
|
boolean isOnFieldCenterLineVerticalOdd = (p.x - 1) % 3 == 0 && p.x % 2 == 0;
|
||||||
|
boolean isOnFieldCenterLineVerticalEven = (p.x - 1) % 3 == 0 && (p.x - 1) % 2 == 0;
|
||||||
|
boolean isNotAnEdgePointOnFieldCentralVerticalLine = isOnFieldCenterLineVerticalOdd
|
||||||
|
&& !(p.y % 6 == 0 || (p.y + 2) % 6 == 0)
|
||||||
|
|| isOnFieldCenterLineVerticalEven && !((p.y + 5) % 6 == 0 || (p.y + 3) % 6 == 0);
|
||||||
|
|
||||||
|
return !(isOnFieldCenterLineHorizontal || isNotAnEdgePointOnFieldCentralVerticalLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Edge> constructEdgesOfField(Point position) {
|
||||||
|
Edge[] e = new Edge[6];
|
||||||
|
e[0] = new Edge(new Point(position.x, position.y - 2),
|
||||||
|
new Point(position.x + 1, position.y - 1));
|
||||||
|
e[1] = new Edge(new Point(position.x + 1, position.y - 1),
|
||||||
|
new Point(position.x + 1, position.y + 1));
|
||||||
|
e[2] = new Edge(new Point(position.x + 1, position.y + 1),
|
||||||
|
new Point(position.x, position.y + 2));
|
||||||
|
e[3] = new Edge(new Point(position.x, position.y + 2),
|
||||||
|
new Point(position.x - 1, position.y + 1));
|
||||||
|
e[4] = new Edge(new Point(position.x - 1, position.y + 1),
|
||||||
|
new Point(position.x - 1, position.y - 1));
|
||||||
|
e[5] = new Edge(new Point(position.x - 1, position.y - 1),
|
||||||
|
new Point(position.x, position.y - 2));
|
||||||
|
return Arrays.asList(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Point> getCornerCoordinatesOfField(Point position) {
|
||||||
|
Point[] corner = new Point[6];
|
||||||
|
corner[0] = new Point(position.x, position.y - 2);
|
||||||
|
corner[1] = new Point(position.x + 1, position.y - 1);
|
||||||
|
corner[2] = new Point(position.x + 1, position.y + 1);
|
||||||
|
corner[3] = new Point(position.x, position.y + 2);
|
||||||
|
corner[4] = new Point(position.x - 1, position.y - 1);
|
||||||
|
corner[5] = new Point(position.x - 1, position.y + 1);
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(corner));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static List<Point> getAdjacentCorners(Point position) {
|
||||||
|
Point[] corner = new Point[3];
|
||||||
|
if (position.y % 3 == 0) {
|
||||||
|
corner[0] = new Point(position.x, position.y - 2);
|
||||||
|
corner[1] = new Point(position.x + 1, position.y + 1);
|
||||||
|
corner[2] = new Point(position.x - 1, position.y + 1);
|
||||||
|
} else {
|
||||||
|
corner[0] = new Point(position.x, position.y + 2);
|
||||||
|
corner[1] = new Point(position.x + 1, position.y - 1);
|
||||||
|
corner[2] = new Point(position.x - 1, position.y - 1);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(corner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all non-null corner data elements.
|
||||||
|
*
|
||||||
|
* @return the non-null corner data elements
|
||||||
|
*/
|
||||||
|
public List<C> getCorners() {
|
||||||
|
List<C> result = new LinkedList<>();
|
||||||
|
for (C c : this.corner.values()) {
|
||||||
|
if (c != null) {
|
||||||
|
result.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Set<Point> getCornerCoordinates() {
|
||||||
|
return Collections.unmodifiableSet(this.corner.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Point> getAdjacentFields(Point corner) {
|
||||||
|
Point[] field = new Point[3];
|
||||||
|
if (corner.y % 3 == 0) {
|
||||||
|
field[0] = new Point(corner.x, corner.y + 2);
|
||||||
|
field[1] = new Point(corner.x + 1, corner.y - 1);
|
||||||
|
field[2] = new Point(corner.x - 1, corner.y - 1);
|
||||||
|
} else {
|
||||||
|
field[0] = new Point(corner.x, corner.y - 2);
|
||||||
|
field[1] = new Point(corner.x + 1, corner.y + 1);
|
||||||
|
field[2] = new Point(corner.x - 1, corner.y + 1);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data for the field denoted by the point.
|
||||||
|
*
|
||||||
|
* @param center the location of the field
|
||||||
|
* @return the stored data (or null)
|
||||||
|
* @throws IllegalArgumentException if the requested field does not exist
|
||||||
|
*/
|
||||||
|
public F getField(Point center) {
|
||||||
|
if (field.containsKey(center)) {
|
||||||
|
return field.get(center);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("No field exists at these coordinates: " + center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fields with non-null data elements.
|
||||||
|
*
|
||||||
|
* @return the list with the (non-null) field data
|
||||||
|
*/
|
||||||
|
public List<Point> getFields() {
|
||||||
|
List<Point> result = new LinkedList<>();
|
||||||
|
for (Entry<Point, F> e : field.entrySet()) {
|
||||||
|
if (e.getValue() != null) {
|
||||||
|
result.add(e.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the field data of the fields that touch this corner.
|
||||||
|
* <p>
|
||||||
|
* If the specified corner is not a corner or none of the fields that touch this
|
||||||
|
* corner have a non-null data element, an empty list is returned.
|
||||||
|
* </p>
|
||||||
|
* @param corner the location of the corner
|
||||||
|
* @return the list with the (non-null) field data
|
||||||
|
*/
|
||||||
|
public List<F> getFields(Point corner) {
|
||||||
|
List<F> result = new LinkedList<>();
|
||||||
|
if (isCornerCoordinate(corner)) {
|
||||||
|
for (Point f : getAdjacentFields(corner)) {
|
||||||
|
if (field.get(f) != null) {
|
||||||
|
result.add(field.get(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data for the edge denoted by the two points.
|
||||||
|
*
|
||||||
|
* @param p1 first point
|
||||||
|
* @param p2 second point
|
||||||
|
* @return the stored data (or null)
|
||||||
|
*/
|
||||||
|
public E getEdge(Point p1, Point p2) {
|
||||||
|
Edge e = new Edge(p1, p2);
|
||||||
|
if (edge.containsKey(e)) {
|
||||||
|
return edge.get(e);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the data for the edge denoted by the two points.
|
||||||
|
*
|
||||||
|
* @param p1 first point
|
||||||
|
* @param p2 second point
|
||||||
|
* @param data the data to be stored
|
||||||
|
* @throws IllegalArgumentException if the two points do not identify an
|
||||||
|
* EXISTING edge of the field
|
||||||
|
*/
|
||||||
|
public void setEdge(Point p1, Point p2, E data) {
|
||||||
|
Edge e = new Edge(p1, p2);
|
||||||
|
if (edge.containsKey(e)) {
|
||||||
|
edge.put(e, data);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Edge does not exist => no data can be stored: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data for the corner denoted by the point.
|
||||||
|
*
|
||||||
|
* @param location the location of the corner
|
||||||
|
* @return the data stored for this node (or null)
|
||||||
|
* @throws IllegalArgumentException if the requested corner does not exist
|
||||||
|
*/
|
||||||
|
public C getCorner(Point location) {
|
||||||
|
if (corner.containsKey(location)) {
|
||||||
|
return corner.get(location);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("No corner exists at the coordinates: " + location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the data for the edge denoted by the two points.
|
||||||
|
*
|
||||||
|
* @param location the location of the corner
|
||||||
|
* @param data the data to be stored
|
||||||
|
* @return the old data entry (or null)
|
||||||
|
* @throws IllegalArgumentException if there is no corner at this location
|
||||||
|
*/
|
||||||
|
public C setCorner(Point location, C data) {
|
||||||
|
C old = corner.get(location);
|
||||||
|
if (corner.containsKey(location)) {
|
||||||
|
corner.put(location, data);
|
||||||
|
return old;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Corner does not exist => no data can be stored: " + location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (non-null) corner data elements of the corners that are direct
|
||||||
|
* neighbors of the specified corner.
|
||||||
|
* <p>
|
||||||
|
* Each corner has three direct neighbors, except corners that are located at
|
||||||
|
* the border of the game board.
|
||||||
|
* </p>
|
||||||
|
* @param center the location of the corner for which to return the direct
|
||||||
|
* neighbors
|
||||||
|
* @return list with non-null corner data elements
|
||||||
|
*/
|
||||||
|
public List<C> getNeighboursOfCorner(Point center) {
|
||||||
|
List<C> result = new LinkedList<>();
|
||||||
|
for (Point c : HexBoard.getAdjacentCorners(center)) {
|
||||||
|
C temp = corner.get(c);
|
||||||
|
if (temp != null) {
|
||||||
|
result.add(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (non-null) edge data elements of the edges that directly connect
|
||||||
|
* to that corner.
|
||||||
|
* <p>
|
||||||
|
* Each corner has three edges connecting to it, except edges that are located
|
||||||
|
* at the border of the game board.
|
||||||
|
* </p>
|
||||||
|
* @param corner corner for which to get the edges
|
||||||
|
* @return list with non-null edge data elements of edges connecting to the
|
||||||
|
* specified edge
|
||||||
|
*/
|
||||||
|
public List<E> getAdjacentEdges(Point corner) {
|
||||||
|
List<E> result = new LinkedList<>();
|
||||||
|
for (Entry<Edge, E> e : this.edge.entrySet()) {
|
||||||
|
if (e.getKey().isEdgePoint(corner)
|
||||||
|
&& e.getValue() != null) {
|
||||||
|
result.add(e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (non-null) data elements of the corners of the specified field.
|
||||||
|
*
|
||||||
|
* @param center the location of the field
|
||||||
|
* @return list with non-null corner data elements
|
||||||
|
*/
|
||||||
|
public List<C> getCornersOfField(Point center) {
|
||||||
|
List<C> result = new LinkedList<>();
|
||||||
|
for (Point c : getCornerCoordinatesOfField(center)) {
|
||||||
|
C temp = getCorner(c);
|
||||||
|
if (temp != null) {
|
||||||
|
result.add(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMaxCoordinateX() {
|
||||||
|
return maxCoordinateX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMaxCoordinateY() {
|
||||||
|
return maxCoordinateY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether there is a corner at that specified location.
|
||||||
|
* @param location the location to check
|
||||||
|
* @return true, if there is a corner at this location
|
||||||
|
*/
|
||||||
|
public boolean hasCorner(Point location) {
|
||||||
|
if (!HexBoard.isCornerCoordinate(location)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return corner.containsKey(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether there is an edge between the two points.
|
||||||
|
* @param p1 first point
|
||||||
|
* @param p2 second point
|
||||||
|
* @return true, if there is an edge between the two points
|
||||||
|
*/
|
||||||
|
public boolean hasEdge(Point p1, Point p2) {
|
||||||
|
if (Edge.isEdge(p1, p2)) {
|
||||||
|
return edge.containsKey(new Edge(p1, p2));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isCorner(Point field, Point corner) {
|
||||||
|
return HexBoard.isFieldCoordinate(field)
|
||||||
|
&& HexBoard.getCornerCoordinatesOfField(field).contains(corner);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be used to get a textual representation of a hex-grid modeled
|
||||||
|
* by {@link ch.zhaw.hexboard.HexBoard}.
|
||||||
|
* <p>
|
||||||
|
* It creates a textual representation of the {@link ch.zhaw.hexboard.HexBoard}
|
||||||
|
* that includes all defined fields, edges, corners and annotations.
|
||||||
|
* </p>
|
||||||
|
* The generation of the textual representation is basically working on a line
|
||||||
|
* by line basis. Thereby, the two text lines needed for the diagonal edges are
|
||||||
|
* treated like "one line" in that they are created in one step together.
|
||||||
|
* <p>
|
||||||
|
* The textual representation does not contain the hex-grid as such but only the
|
||||||
|
* fields that actually exist on the hex-board. Note that if a field exists,
|
||||||
|
* also its corners and edges exist and are therefore shown in the textual
|
||||||
|
* representation.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This class defines how edges, corners and fields look like (their "label").
|
||||||
|
* This is done as follows:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>If there is no data object associated with an edge, corner or field,
|
||||||
|
* their default representation is used. Note that the default representation of
|
||||||
|
* an edge depends on its direction (see below).</li>
|
||||||
|
* <li>If there is a data object associated with an edge, corner or field, the
|
||||||
|
* {@link ch.zhaw.hexboard.Label} is determined by calling:
|
||||||
|
* <ul>
|
||||||
|
* <li>EL = {@link #getEdgeLabel(Object)}</li>
|
||||||
|
* <li>CL = {@link #getCornerLabel(Object)}</li>
|
||||||
|
* <li>UL = {@link #getFieldLabelUpper(Object)}</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* <p>In addition to edges, corners and field labels, the hex-board's field
|
||||||
|
* annotations are included too. If an annotation exists for one of the corners
|
||||||
|
* (N, NW, SW, S, SE, NE), which means that an associated data object exists, it
|
||||||
|
* is turned into a {@link ch.zhaw.hexboard.Label} with
|
||||||
|
* {@link #getAnnotationLabel(Object)}.</p>
|
||||||
|
* <br>
|
||||||
|
* <p>Two examples of how that looks like are shown below. The first example shows
|
||||||
|
* a case with all edges, corners and the field with no data associated with
|
||||||
|
* them. The second one has all edges corner and the upper field label defined
|
||||||
|
* by calling the corresponding method for creating the Label for the associated
|
||||||
|
* data object.</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* DEFAULT LABELS FROM DATA
|
||||||
|
*
|
||||||
|
* ( ) (CL)
|
||||||
|
* // \\ EL EL
|
||||||
|
* // \\ EL N EL
|
||||||
|
* ( ) ( ) (CL) NW NE (CL)
|
||||||
|
* || || EL UL EL
|
||||||
|
* || || EL EL
|
||||||
|
* ( ) ( ) (CL) SW SE (CL)
|
||||||
|
* \\ // EL S EL
|
||||||
|
* \\ // EL EL
|
||||||
|
* ( ) (CL)
|
||||||
|
* </pre>
|
||||||
|
* <br>
|
||||||
|
* <p>To override the default behavior, which creates a Label using the two first
|
||||||
|
* characters of the string returned by the toString() method of the
|
||||||
|
* edge/corner/field data object, you might override the respective methods.
|
||||||
|
* </p>
|
||||||
|
* <br>
|
||||||
|
* <p>
|
||||||
|
* Finally, a field can be labeled with a lower label (LL) by providing a map of
|
||||||
|
* field coordinates and associated labels. An example of a representation with
|
||||||
|
* all field annotations, corner labels and field labels defined but default
|
||||||
|
* edges is the following:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* (CL)
|
||||||
|
* // N \\
|
||||||
|
* // \\
|
||||||
|
* (CL) NW NE (CL)
|
||||||
|
* || UL ||
|
||||||
|
* || LL ||
|
||||||
|
* (CL) SW SE (CL)
|
||||||
|
* \\ S //
|
||||||
|
* \\ //
|
||||||
|
* (CL)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param <F> See {@link ch.zhaw.hexboard.HexBoard}
|
||||||
|
* @param <C> See {@link ch.zhaw.hexboard.HexBoard}
|
||||||
|
* @param <E> See {@link ch.zhaw.hexboard.HexBoard}
|
||||||
|
* @param <A> See {@link ch.zhaw.hexboard.HexBoard}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public class HexBoardTextView<F, C, E, A> {
|
||||||
|
|
||||||
|
private static final String ONE_SPACE = " ";
|
||||||
|
private static final String TWO_SPACES = " ";
|
||||||
|
private static final String FOUR_SPACES = " ";
|
||||||
|
private static final String FIVE_SPACES = " ";
|
||||||
|
private static final String SIX_SPACES = " ";
|
||||||
|
private static final String SEVEN_SPACES = " ";
|
||||||
|
private static final String NINE_SPACES = " ";
|
||||||
|
private final HexBoard<F, C, E, A> board;
|
||||||
|
private final Label emptyLabel = new Label(' ', ' ');
|
||||||
|
private final Label defaultDiagonalEdgeDownLabel = new Label('\\', '\\');
|
||||||
|
private final Label defaultDiagonalEdgeUpLabel = new Label('/', '/');
|
||||||
|
private final Label defaultVerticalEdgeLabel = new Label('|', '|');
|
||||||
|
private Map<Point, Label> fixedLowerFieldLabels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a view for the specified board.
|
||||||
|
*
|
||||||
|
* @param board the board
|
||||||
|
*/
|
||||||
|
public HexBoardTextView(HexBoard<F, C, E, A> board) {
|
||||||
|
this.fixedLowerFieldLabels = new HashMap<>();
|
||||||
|
this.board = board;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the lower field label for the specified field.
|
||||||
|
*
|
||||||
|
* @param field the field
|
||||||
|
* @param label the label
|
||||||
|
* @throws IllegalArgumentException if arguments are null or if the field does
|
||||||
|
* not exist
|
||||||
|
*/
|
||||||
|
public void setLowerFieldLabel(Point field, Label label) {
|
||||||
|
if (field == null || label == null || !board.hasField(field)) {
|
||||||
|
throw new IllegalArgumentException("Argument(s) must not be null and field must exist.");
|
||||||
|
}
|
||||||
|
fixedLowerFieldLabels.put(field, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a label to be used as label for the edge. This method is called to
|
||||||
|
* determine the label for this edge.
|
||||||
|
*
|
||||||
|
* @param e edge data object
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
protected Label getEdgeLabel(E e) {
|
||||||
|
return deriveLabelFromToStringRepresentation(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a label to be used as label for the corner. This method is called to
|
||||||
|
* determine the label for this corner.
|
||||||
|
*
|
||||||
|
* @param c corner data object
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
protected Label getCornerLabel(C c) {
|
||||||
|
return deriveLabelFromToStringRepresentation(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a label to be used as upper label for the field. This method is
|
||||||
|
* called to determine the upper label for this field.
|
||||||
|
*
|
||||||
|
* @param f field data object
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
protected Label getFieldLabelUpper(F f) {
|
||||||
|
return deriveLabelFromToStringRepresentation(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a label to be used as lower label for the field at this position.
|
||||||
|
* This method is called to determine the lower label for this field.
|
||||||
|
*
|
||||||
|
* @param p location of the field
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
private Label getFieldLabelLower(Point p) {
|
||||||
|
Label l = this.fixedLowerFieldLabels.get(p);
|
||||||
|
l = l == null ? emptyLabel : l;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label deriveLabelFromToStringRepresentation(Object o) {
|
||||||
|
Label label = emptyLabel;
|
||||||
|
if (o.toString().length() > 0) {
|
||||||
|
String s = o.toString();
|
||||||
|
if (s.length() > 1) {
|
||||||
|
return new Label(s.charAt(0), s.charAt(1));
|
||||||
|
} else {
|
||||||
|
return new Label(s.charAt(0), ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This method returns a single-line string with all corners and field
|
||||||
|
* annotations for a given y-coordinate. It produces the string by iterating
|
||||||
|
* over corner positions and appending per corner:
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* "(CL) NE NW " for y%3==1 "(CL) SE SW " for y%3==0
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Corners/labels that do not exist are replaced by spaces.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private String printCornerLine(int y) {
|
||||||
|
StringBuilder cornerLine = new StringBuilder("");
|
||||||
|
int offset = 0;
|
||||||
|
if (y % 2 != 0) {
|
||||||
|
cornerLine.append(NINE_SPACES);
|
||||||
|
offset = 1;
|
||||||
|
}
|
||||||
|
for (int x = offset; x <= board.getMaxCoordinateX(); x = x + 2) {
|
||||||
|
Point p = new Point(x, y);
|
||||||
|
Label cornerLabel;
|
||||||
|
|
||||||
|
// handle corner labels for corners other than north and south corners
|
||||||
|
Point center;
|
||||||
|
Label first = null;
|
||||||
|
Label second = null;
|
||||||
|
switch (y % 3) {
|
||||||
|
case 0:
|
||||||
|
center = new Point(x + 1, y - 1);
|
||||||
|
first = this.getAnnotationLabel(
|
||||||
|
board.getFieldAnnotation(center, new Point(center.x - 1, center.y + 1)));
|
||||||
|
second = this.getAnnotationLabel(
|
||||||
|
board.getFieldAnnotation(center, new Point(center.x + 1, center.y + 1)));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
center = new Point(x + 1, y + 1);
|
||||||
|
first = this.getAnnotationLabel(
|
||||||
|
board.getFieldAnnotation(center, new Point(center.x - 1, center.y - 1)));
|
||||||
|
second = this.getAnnotationLabel(
|
||||||
|
board.getFieldAnnotation(center, new Point(center.x + 1, center.y - 1)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Not a corner line");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (board.hasCorner(p)) {
|
||||||
|
cornerLabel = board.getCorner(p) != null ? getCornerLabel(board.getCorner(p)) : emptyLabel;
|
||||||
|
cornerLine.append("(").append(cornerLabel.getFirst()).append(cornerLabel.getSecond()).append(")");
|
||||||
|
} else {
|
||||||
|
cornerLine.append(FOUR_SPACES);
|
||||||
|
}
|
||||||
|
cornerLine.append(ONE_SPACE).append(first.getFirst()).append(first.getSecond());
|
||||||
|
cornerLine.append(FIVE_SPACES).append(second.getFirst()).append(second.getSecond()).append(TWO_SPACES);
|
||||||
|
}
|
||||||
|
return cornerLine.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label getAnnotationLabel(A annotation) {
|
||||||
|
if (annotation == null) {
|
||||||
|
return emptyLabel;
|
||||||
|
} else {
|
||||||
|
return deriveLabelFromToStringRepresentation(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String printMiddlePartOfField(int y) {
|
||||||
|
boolean isOffsetRow = (y - 2) % 6 == 0;
|
||||||
|
StringBuilder lower = new StringBuilder(isOffsetRow ? NINE_SPACES : "");
|
||||||
|
StringBuilder upper = new StringBuilder(isOffsetRow ? NINE_SPACES : "");
|
||||||
|
int xstart = isOffsetRow ? 2 : 1;
|
||||||
|
|
||||||
|
for (int x = xstart; x <= board.getMaxCoordinateX() + 1; x = x + 2) {
|
||||||
|
Point edgeStart = new Point(x - 1, y - 1);
|
||||||
|
Point edgeEnd = new Point(x - 1, y + 1);
|
||||||
|
Label l = this.emptyLabel;
|
||||||
|
if (board.hasEdge(edgeStart, edgeEnd)) {
|
||||||
|
E edge = board.getEdge(edgeStart, edgeEnd);
|
||||||
|
if (edge != null) {
|
||||||
|
l = this.getEdgeLabel(edge);
|
||||||
|
} else {
|
||||||
|
l = this.defaultVerticalEdgeLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Point center = new Point(x, y);
|
||||||
|
boolean hasFieldWithData = board.hasField(center) && board.getField(center) != null;
|
||||||
|
Label lowerFieldLabel = hasFieldWithData ? getFieldLabelLower(center) : emptyLabel;
|
||||||
|
Label upperFieldLabel = hasFieldWithData ? getFieldLabelUpper(board.getField(center))
|
||||||
|
: emptyLabel;
|
||||||
|
lower.append(ONE_SPACE).append(l.getFirst()).append(l.getSecond()).append(SEVEN_SPACES);
|
||||||
|
lower.append(lowerFieldLabel.getFirst()).append(lowerFieldLabel.getSecond()).append(SIX_SPACES);
|
||||||
|
|
||||||
|
upper.append(ONE_SPACE).append(l.getFirst()).append(l.getSecond()).append(SEVEN_SPACES);
|
||||||
|
upper.append(upperFieldLabel.getFirst()).append(upperFieldLabel.getSecond()).append(SIX_SPACES);
|
||||||
|
}
|
||||||
|
return upper + System.lineSeparator() + lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String printDiagonalEdges(int y) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Point edgeStart;
|
||||||
|
Point edgeEnd;
|
||||||
|
Label annotation = null;
|
||||||
|
Label l;
|
||||||
|
boolean isDown = y % 6 == 0;
|
||||||
|
|
||||||
|
builder.append(" ");
|
||||||
|
for (int x = 0; x <= board.getMaxCoordinateX(); x = x + 1) {
|
||||||
|
if (isDown) {
|
||||||
|
edgeStart = new Point(x, y);
|
||||||
|
edgeEnd = new Point(x + 1, y + 1);
|
||||||
|
annotation = getAnnotationLabel(board.getFieldAnnotation(new Point(x + 1, y - 1), new Point(x + 1, y + 1)));
|
||||||
|
} else {
|
||||||
|
edgeStart = new Point(x, y + 1);
|
||||||
|
edgeEnd = new Point(x + 1, y);
|
||||||
|
annotation = getAnnotationLabel(board.getFieldAnnotation(new Point(x + 1, y + 2), new Point(x + 1, y)));
|
||||||
|
}
|
||||||
|
l = determineEdgeLabel(isDown, edgeStart, edgeEnd);
|
||||||
|
|
||||||
|
if (!isDown) {
|
||||||
|
builder.append(TWO_SPACES + l.getFirst() + l.getSecond() + TWO_SPACES + annotation.getFirst() + annotation.getSecond());
|
||||||
|
} else {
|
||||||
|
builder.append(TWO_SPACES + l.getFirst() + l.getSecond() + TWO_SPACES + annotation.getFirst() + annotation.getSecond());
|
||||||
|
}
|
||||||
|
isDown = !isDown;
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label determineEdgeLabel(boolean isDown, Point edgeStart, Point edgeEnd) {
|
||||||
|
Label l;
|
||||||
|
if (board.hasEdge(edgeStart, edgeEnd)) {
|
||||||
|
// does it have data associated with it?
|
||||||
|
if (board.getEdge(edgeStart, edgeEnd) != null) {
|
||||||
|
l = this.getEdgeLabel(board.getEdge(edgeStart, edgeEnd));
|
||||||
|
} else {
|
||||||
|
// default visualization
|
||||||
|
l = isDown ? this.defaultDiagonalEdgeDownLabel : this.defaultDiagonalEdgeUpLabel;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l = this.emptyLabel;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int y = 0; y <= board.getMaxCoordinateY(); y = y + 3) {
|
||||||
|
sb.append(printCornerLine(y));
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
sb.append(printDiagonalEdges(y));
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
sb.append(printCornerLine(y + 1));
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
sb.append(printMiddlePartOfField(y + 2));
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class defines a label composed of two characters.
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Label {
|
||||||
|
public static final char DEFAULT_CHARACTER = ' ';
|
||||||
|
private final char first;
|
||||||
|
private final char second;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a label from two characters.
|
||||||
|
*
|
||||||
|
* @param firstChar first character
|
||||||
|
* @param secondChar second character
|
||||||
|
*/
|
||||||
|
public Label(char firstChar, char secondChar) {
|
||||||
|
first = firstChar;
|
||||||
|
second = secondChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a label using the default character {@link #DEFAULT_CHARACTER}.
|
||||||
|
*/
|
||||||
|
public Label() {
|
||||||
|
first = ' ';
|
||||||
|
second = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getFirst() {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getSecond() {
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "" + first + second;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* TODO Write your own tests in this class.
|
||||||
|
*
|
||||||
|
* Note: Have a look at {@link ch.zhaw.catan.games.ThreePlayerStandard}. It can be used
|
||||||
|
* to get several different game states.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SiedlerGameTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dummyTestMethod() {
|
||||||
|
assertTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.games.ThreePlayerStandard;
|
||||||
|
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains some basic tests for the {@link SiedlerGame} class
|
||||||
|
* <p></p>
|
||||||
|
* <p>DO NOT MODIFY THIS CLASS</p>
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public class SiedlerGameTestBasic {
|
||||||
|
private final static int DEFAULT_WINPOINTS = 5;
|
||||||
|
private final static int DEFAULT_NUMBER_OF_PLAYERS = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the functionality for switching to the next/previous player
|
||||||
|
* works as expected for different numbers of players.
|
||||||
|
*
|
||||||
|
* @param numberOfPlayers the number of players
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {2, 3, 4})
|
||||||
|
public void requirementPlayerSwitching(int numberOfPlayers) {
|
||||||
|
SiedlerGame model = new SiedlerGame(DEFAULT_WINPOINTS, numberOfPlayers);
|
||||||
|
assertTrue(numberOfPlayers == model.getPlayerFactions().size(),
|
||||||
|
"Wrong number of players returned by getPlayers()");
|
||||||
|
//Switching forward
|
||||||
|
for (int i = 0; i < numberOfPlayers; i++) {
|
||||||
|
assertEquals(Config.Faction.values()[i], model.getCurrentPlayerFaction(),
|
||||||
|
"Player order does not match order of Faction.values()");
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
}
|
||||||
|
assertEquals(Config.Faction.values()[0], model.getCurrentPlayerFaction(),
|
||||||
|
"Player wrap-around from last player to first player did not work.");
|
||||||
|
//Switching backward
|
||||||
|
for (int i = numberOfPlayers - 1; i >= 0; i--) {
|
||||||
|
model.switchToPreviousPlayer();
|
||||||
|
assertEquals(Config.Faction.values()[i], model.getCurrentPlayerFaction(),
|
||||||
|
"Switching players in reverse order does not work as expected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the game board meets the required layout/land placement.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementLandPlacementTest() {
|
||||||
|
SiedlerGame model = new SiedlerGame(DEFAULT_WINPOINTS, DEFAULT_NUMBER_OF_PLAYERS);
|
||||||
|
assertTrue(Config.getStandardLandPlacement().size() == model.getBoard().getFields().size(),
|
||||||
|
"Check if explicit init must be done (violates spec): "
|
||||||
|
+ "modify initializeSiedlerGame accordingly.");
|
||||||
|
for (Map.Entry<Point, Config.Land> e : Config.getStandardLandPlacement().entrySet()) {
|
||||||
|
assertEquals(e.getValue(), model.getBoard().getField(e.getKey()),
|
||||||
|
"Land placement does not match default placement.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the {@link ThreePlayerStandard#getAfterSetupPhase(int)}} game board is not empty (returns
|
||||||
|
* an object) at positions where settlements and roads have been placed.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementSettlementAndRoadPositionsOccupiedThreePlayerStandard() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
assertEquals(DEFAULT_NUMBER_OF_PLAYERS, model.getPlayerFactions().size());
|
||||||
|
for (Config.Faction f : model.getPlayerFactions()) {
|
||||||
|
assertTrue(model.getBoard().getCorner(ThreePlayerStandard.INITIAL_SETTLEMENT_POSITIONS.get(f).first) != null);
|
||||||
|
assertTrue(model.getBoard().getCorner(ThreePlayerStandard.INITIAL_SETTLEMENT_POSITIONS.get(f).second) != null);
|
||||||
|
assertTrue(model.getBoard().getEdge(ThreePlayerStandard.INITIAL_SETTLEMENT_POSITIONS.get(f).first, ThreePlayerStandard.INITIAL_ROAD_ENDPOINTS.get(f).first) != null);
|
||||||
|
assertTrue(model.getBoard().getEdge(ThreePlayerStandard.INITIAL_SETTLEMENT_POSITIONS.get(f).second, ThreePlayerStandard.INITIAL_ROAD_ENDPOINTS.get(f).second) != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the resource card payout for different dice values matches
|
||||||
|
* the expected payout for the game state {@link ThreePlayerStandard#getAfterSetupPhase(int)}}.
|
||||||
|
*
|
||||||
|
* Note, that for the test to work, the {@link Map} returned by {@link SiedlerGame#throwDice(int)}
|
||||||
|
* must contain a {@link List} with resource cards (empty {@link List}, if the player gets none)
|
||||||
|
* for each of the players.
|
||||||
|
*
|
||||||
|
* @param diceValue the dice value
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {2, 3, 4, 5, 6, 8, 9, 10, 11, 12})
|
||||||
|
public void requirementDiceThrowResourcePayoutThreePlayerStandardTest(int diceValue) {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
Map<Config.Faction, List<Config.Resource>> expectd = ThreePlayerStandard.INITIAL_DICE_THROW_PAYOUT.get(diceValue);
|
||||||
|
Map<Config.Faction, List<Config.Resource>> actual = model.throwDice(diceValue);
|
||||||
|
assertEquals(ThreePlayerStandard.INITIAL_DICE_THROW_PAYOUT.get(diceValue), model.throwDice(diceValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the resource card stock of the players matches the expected stock
|
||||||
|
* for the game state {@link ThreePlayerStandard#getAfterSetupPhase(int)}}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementPlayerResourceCardStockAfterSetupPhase() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
assertPlayerResourceCardStockEquals(model, ThreePlayerStandard.INITIAL_PLAYER_CARD_STOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the resource card stock of the players matches the expected stock
|
||||||
|
* for the game state {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementPlayerResourceCardStockAfterSetupPhaseAlmostEmptyBank() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhaseAlmostEmptyBank(DEFAULT_WINPOINTS);
|
||||||
|
assertPlayerResourceCardStockEquals(model, ThreePlayerStandard.BANK_ALMOST_EMPTY_RESOURCE_CARD_STOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the resource card stock of the players matches the expected stock
|
||||||
|
* for the game state {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementPlayerResourceCardStockPlayerOneReadyToBuildFifthSettlement() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getPlayerOneReadyToBuildFifthSettlement(DEFAULT_WINPOINTS);
|
||||||
|
assertPlayerResourceCardStockEquals(model, ThreePlayerStandard.PLAYER_ONE_READY_TO_BUILD_FIFTH_SETTLEMENT_RESOURCE_CARD_STOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws each dice value except 7 once and tests whether the resource
|
||||||
|
* card stock of the players matches the expected stock.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementDiceThrowPlayerResourceCardStockUpdateTest() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
for(int i : List.of(2, 3, 4, 5, 6, 8, 9, 10, 11, 12)) {
|
||||||
|
model.throwDice(i);
|
||||||
|
}
|
||||||
|
Map<Config.Faction, Map<Config.Resource, Integer>> expected = Map.of(
|
||||||
|
Config.Faction.values()[0], Map.of(Config.Resource.GRAIN, 1, Config.Resource.WOOL, 2,
|
||||||
|
Config.Resource.BRICK, 2, Config.Resource.ORE, 1, Config.Resource.LUMBER, 1),
|
||||||
|
Config.Faction.values()[1],
|
||||||
|
Map.of(Config.Resource.GRAIN, 1, Config.Resource.WOOL, 5, Config.Resource.BRICK, 0,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0),
|
||||||
|
Config.Faction.values()[2],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 0, Config.Resource.BRICK, 2,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 1));
|
||||||
|
|
||||||
|
assertPlayerResourceCardStockEquals(model, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPlayerResourceCardStockEquals(SiedlerGame model, Map<Config.Faction, Map<Config.Resource, Integer>> expected) {
|
||||||
|
for (int i = 0; i < expected.keySet().size(); i++) {
|
||||||
|
Config.Faction f = model.getCurrentPlayerFaction();
|
||||||
|
for (Config.Resource r : Config.Resource.values()) {
|
||||||
|
assertEquals(expected.get(f).get(r), model.getCurrentPlayerResourceStock(r),
|
||||||
|
"Resource card stock of player " + i + " [faction " + f + "] for resource type " + r + " does not match.");
|
||||||
|
}
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether player one can build two roads starting in game state
|
||||||
|
* {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementBuildRoad() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhaseAlmostEmptyBank(DEFAULT_WINPOINTS);
|
||||||
|
assertTrue(model.buildRoad(new Point(6, 6), new Point(6, 4)));
|
||||||
|
assertTrue(model.buildRoad(new Point(6, 4), new Point(7, 3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether player one can build a road and a settlement starting in game state
|
||||||
|
* {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementBuildSettlement() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhaseAlmostEmptyBank(DEFAULT_WINPOINTS);
|
||||||
|
assertTrue(model.buildRoad(new Point(9, 15), new Point(9, 13)));
|
||||||
|
assertTrue(model.buildSettlement(new Point(9, 13)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether payout with multiple settlements of the same player at one field works
|
||||||
|
* {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementTwoSettlementsSamePlayerSameFieldResourceCardPayout() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
for(int diceValue : List.of(2, 6, 6, 11)){
|
||||||
|
model.throwDice(diceValue);
|
||||||
|
}
|
||||||
|
assertTrue(model.buildRoad(new Point(6, 6), new Point(7, 7)));
|
||||||
|
assertTrue(model.buildSettlement(new Point(7, 7)));
|
||||||
|
assertEquals(List.of(Config.Resource.ORE, Config.Resource.ORE), model.throwDice(4).get(model.getCurrentPlayerFaction()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether player one can build a city starting in game state
|
||||||
|
* {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementBuildCity() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhaseAlmostEmptyBank(DEFAULT_WINPOINTS);
|
||||||
|
assertTrue(model.buildCity(new Point(10, 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether player two can trade in resources with the bank and has the
|
||||||
|
* correct number of resource cards afterwards. The test starts from game state
|
||||||
|
* {@link ThreePlayerStandard#getAfterSetupPhaseAlmostEmptyBank(int)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void requirementCanTradeFourToOneWithBank() {
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhaseAlmostEmptyBank(DEFAULT_WINPOINTS);
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
|
||||||
|
Map<Config.Resource, Integer> expectedResourceCards = ThreePlayerStandard.BANK_ALMOST_EMPTY_RESOURCE_CARD_STOCK.get(model.getCurrentPlayerFaction());
|
||||||
|
assertEquals(expectedResourceCards.get(Config.Resource.WOOL), model.getCurrentPlayerResourceStock(Config.Resource.WOOL));
|
||||||
|
assertEquals(expectedResourceCards.get(Config.Resource.LUMBER), model.getCurrentPlayerResourceStock(Config.Resource.LUMBER));
|
||||||
|
|
||||||
|
model.tradeWithBankFourToOne(Config.Resource.WOOL, Config.Resource.LUMBER);
|
||||||
|
|
||||||
|
int cardsOffered = 4;
|
||||||
|
int cardsReceived = 1;
|
||||||
|
assertEquals(expectedResourceCards.get(Config.Resource.WOOL)-cardsOffered, model.getCurrentPlayerResourceStock(Config.Resource.WOOL));
|
||||||
|
assertEquals(expectedResourceCards.get(Config.Resource.LUMBER)+cardsReceived, model.getCurrentPlayerResourceStock(Config.Resource.LUMBER));
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* This test is not actually a test and should be removed. However,
|
||||||
|
* we leave it in for you to have a quick and easy way to look at the
|
||||||
|
* game board produced by {@link ThreePlayerStandard#getAfterSetupPhase(int)},
|
||||||
|
* augmented by annotations, which you won't need since we do not ask for
|
||||||
|
* more advanced trading functionality using harbours.
|
||||||
|
*/
|
||||||
|
@Disabled
|
||||||
|
@Test
|
||||||
|
public void print(){
|
||||||
|
SiedlerGame model = ThreePlayerStandard.getAfterSetupPhase(DEFAULT_WINPOINTS);
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(6, 6), "N ");
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(5, 7), "NE");
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(5, 9), "SE");
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(6, 10), "S ");
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(7, 7), "NW");
|
||||||
|
model.getBoard().addFieldAnnotation(new Point(6, 8),new Point(7, 9), "SW");
|
||||||
|
System.out.println(new SiedlerBoardTextView(model.getBoard()).toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package ch.zhaw.catan;
|
||||||
|
|
||||||
|
public class Tuple<X, Y> {
|
||||||
|
public final X first;
|
||||||
|
public final Y second;
|
||||||
|
|
||||||
|
public Tuple(X x, Y y) {
|
||||||
|
this.first = x;
|
||||||
|
this.second = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + first + "," + second + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(other instanceof Tuple)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tuple<X, Y> otherCasted = (Tuple<X, Y>) other;
|
||||||
|
|
||||||
|
// null is not a valid value for first and second tuple element
|
||||||
|
return otherCasted.first.equals(this.first) && otherCasted.second.equals(this.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((first == null) ? 0 : first.hashCode());
|
||||||
|
result = prime * result + ((second == null) ? 0 : second.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,480 @@
|
||||||
|
package ch.zhaw.catan.games;
|
||||||
|
|
||||||
|
import ch.zhaw.catan.Config;
|
||||||
|
import ch.zhaw.catan.Config.Resource;
|
||||||
|
import ch.zhaw.catan.Tuple;
|
||||||
|
import ch.zhaw.catan.SiedlerGame;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be used to prepare some predefined siedler game situations and, for some
|
||||||
|
* of the situations, it provides information about the expected game state,
|
||||||
|
* for example the number of resource cards in each player's stock or the expected resource
|
||||||
|
* card payout when the dices are thrown (for each dice value).
|
||||||
|
* <br>
|
||||||
|
* The basic game situations upon which all other situations that can be retrieved are based is
|
||||||
|
* the following:
|
||||||
|
* <pre>
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) (bb) ( ) ( )
|
||||||
|
* || ~~ || LU || WL bb WL || ~~ ||
|
||||||
|
* || || 06 || 03 bb 08 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ rr \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) (rr) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ || GR || OR || GR || LU || ~~ ||
|
||||||
|
* || || 02 || 04 || 05 || 10 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ gg LU || BR || -- || OR || GR || ~~ ||
|
||||||
|
* || gg 05 || 09 || 07 || 06 || 09 || ||
|
||||||
|
* ( ) (gg) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ // \\ // \\ bb \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) (bb) ( )
|
||||||
|
* || ~~ || GR || OR || LU || WL || ~~ ||
|
||||||
|
* || || 10 || 11 || 03 || 12 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ // rr // \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) (rr) ( )
|
||||||
|
* || ~~ || WL || BR || BR || ~~ ||
|
||||||
|
* || || 08 || 04 || 11 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ gg \\ // \\ //
|
||||||
|
* ( ) ( ) (gg) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ //
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
* </pre>
|
||||||
|
* Resource cards after the setup phase:
|
||||||
|
* <ul>
|
||||||
|
* <li>Player 1: WOOL BRICK</li>
|
||||||
|
* <li>Player 2: WOOL WOOL</li>
|
||||||
|
* <li>Player 3: BRICK</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>The main ideas for this setup were the following:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Player one has access to all resource types from the start so that any resource card can be acquired by
|
||||||
|
* throwing the corresponding dice value.</li>
|
||||||
|
* <li>The settlements are positioned in a way that for each dice value, there is only one resource card paid
|
||||||
|
* to one player, except for the dice values 4 and 12.</li>
|
||||||
|
* <li>There is a settlement next to water and the owner has access to resource types required to build roads</li>
|
||||||
|
* <li>The initial resource card stock of each player does not allow to build anything without getting
|
||||||
|
* additional resources first</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public class ThreePlayerStandard {
|
||||||
|
public final static int NUMBER_OF_PLAYERS = 3;
|
||||||
|
|
||||||
|
public static final Map<Config.Faction, Tuple<Point, Point>> INITIAL_SETTLEMENT_POSITIONS =
|
||||||
|
Map.of( Config.Faction.values()[0], new Tuple<>(new Point(5, 7), new Point(10, 16)),
|
||||||
|
Config.Faction.values()[1], new Tuple<>(new Point(11, 13), new Point(8, 4)),
|
||||||
|
Config.Faction.values()[2], new Tuple<>(new Point(2, 12), new Point(7, 19)));
|
||||||
|
|
||||||
|
public static final Map<Config.Faction, Tuple<Point, Point>> INITIAL_ROAD_ENDPOINTS = Map.of(Config.Faction.values()[0],
|
||||||
|
new Tuple<>(new Point(6, 6), new Point(9, 15)), Config.Faction.values()[1],
|
||||||
|
new Tuple<>(new Point(12, 12), new Point(8, 6)), Config.Faction.values()[2],
|
||||||
|
new Tuple<>(new Point(2, 10), new Point(8, 18)));
|
||||||
|
|
||||||
|
public static final Map<Config.Faction, Map<Config.Resource, Integer>> INITIAL_PLAYER_CARD_STOCK = Map.of(
|
||||||
|
Config.Faction.values()[0], Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 1,
|
||||||
|
Config.Resource.BRICK, 1, Config.Resource.ORE, 0, Config.Resource.LUMBER, 0),
|
||||||
|
Config.Faction.values()[1],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 2, Config.Resource.BRICK, 0,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0),
|
||||||
|
Config.Faction.values()[2],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 0, Config.Resource.BRICK, 1,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0));
|
||||||
|
|
||||||
|
public static final Map<Config.Faction, Map<Config.Resource, Integer>> BANK_ALMOST_EMPTY_RESOURCE_CARD_STOCK = Map.of(
|
||||||
|
Config.Faction.values()[0], Map.of(Config.Resource.GRAIN, 8, Config.Resource.WOOL, 9,
|
||||||
|
Config.Resource.BRICK, 9, Config.Resource.ORE, 7, Config.Resource.LUMBER, 9),
|
||||||
|
Config.Faction.values()[1],
|
||||||
|
Map.of(Config.Resource.GRAIN, 8, Config.Resource.WOOL, 10, Config.Resource.BRICK, 0,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0),
|
||||||
|
Config.Faction.values()[2],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 0, Config.Resource.BRICK, 8,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 9));
|
||||||
|
|
||||||
|
public static final Map<Config.Faction, Map<Config.Resource, Integer>> PLAYER_ONE_READY_TO_BUILD_FIFTH_SETTLEMENT_RESOURCE_CARD_STOCK = Map.of(
|
||||||
|
Config.Faction.values()[0], Map.of(Config.Resource.GRAIN, 2, Config.Resource.WOOL, 2,
|
||||||
|
Config.Resource.BRICK, 3, Config.Resource.ORE, 0, Config.Resource.LUMBER, 3),
|
||||||
|
Config.Faction.values()[1],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 5, Config.Resource.BRICK, 0,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0),
|
||||||
|
Config.Faction.values()[2],
|
||||||
|
Map.of(Config.Resource.GRAIN, 0, Config.Resource.WOOL, 0, Config.Resource.BRICK, 1,
|
||||||
|
Config.Resource.ORE, 0, Config.Resource.LUMBER, 0));
|
||||||
|
|
||||||
|
public static final Map<Integer, Map<Config.Faction, List<Resource>>> INITIAL_DICE_THROW_PAYOUT = Map.of(
|
||||||
|
2, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(Resource.GRAIN),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
3, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(),
|
||||||
|
Config.Faction.values()[1], List.of(Resource.WOOL),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
4, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(Resource.ORE),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of(Resource.BRICK)),
|
||||||
|
5, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of(Resource.LUMBER)),
|
||||||
|
6, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(Resource.LUMBER),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
8, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(),
|
||||||
|
Config.Faction.values()[1], List.of(Resource.WOOL),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
9, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(),
|
||||||
|
Config.Faction.values()[1], List.of(Resource.GRAIN),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
10, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
11, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(Resource.BRICK),
|
||||||
|
Config.Faction.values()[1], List.of(),
|
||||||
|
Config.Faction.values()[2], List.of()),
|
||||||
|
12, Map.of(
|
||||||
|
Config.Faction.values()[0], List.of(Resource.WOOL),
|
||||||
|
Config.Faction.values()[1], List.of(Resource.WOOL),
|
||||||
|
Config.Faction.values()[2], List.of()));
|
||||||
|
|
||||||
|
public static final Map<Resource, Integer> RESOURCE_CARDS_IN_BANK_AFTER_STARTUP_PHASE = Map.of(Resource.LUMBER, 19,
|
||||||
|
Resource.BRICK, 17, Resource.WOOL, 16, Resource.GRAIN, 19, Resource.ORE, 19);
|
||||||
|
|
||||||
|
public static final Point PLAYER_ONE_READY_TO_BUILD_FIFTH_SETTLEMENT_FIFTH_SETTLEMENT_POSITION = new Point(9, 13);
|
||||||
|
public static final List<Point> playerOneReadyToBuildFifthSettlementAllSettlementPositions =
|
||||||
|
List.of(INITIAL_SETTLEMENT_POSITIONS.get(Config.Faction.values()[0]).first,
|
||||||
|
INITIAL_SETTLEMENT_POSITIONS.get(Config.Faction.values()[0]).second,
|
||||||
|
new Point(7, 7),new Point(6, 4), PLAYER_ONE_READY_TO_BUILD_FIFTH_SETTLEMENT_FIFTH_SETTLEMENT_POSITION);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a siedler game after the setup phase in the setup
|
||||||
|
* and with the initial resource card setup as described
|
||||||
|
* in {@link ThreePlayerStandard}.
|
||||||
|
*
|
||||||
|
* @param winpoints the number of points required to win the game
|
||||||
|
* @return the siedler game
|
||||||
|
*/
|
||||||
|
public static SiedlerGame getAfterSetupPhase(int winpoints) {
|
||||||
|
SiedlerGame model = new SiedlerGame(winpoints, NUMBER_OF_PLAYERS);
|
||||||
|
for (int i = 0; i < model.getPlayerFactions().size(); i++) {
|
||||||
|
Config.Faction f = model.getCurrentPlayerFaction();
|
||||||
|
assertTrue(model.placeInitialSettlement(INITIAL_SETTLEMENT_POSITIONS.get(f).first, false));
|
||||||
|
assertTrue(model.placeInitialRoad(INITIAL_SETTLEMENT_POSITIONS.get(f).first, INITIAL_ROAD_ENDPOINTS.get(f).first));
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < model.getPlayerFactions().size(); i++) {
|
||||||
|
model.switchToPreviousPlayer();
|
||||||
|
Config.Faction f = model.getCurrentPlayerFaction();
|
||||||
|
assertTrue(model.placeInitialSettlement(INITIAL_SETTLEMENT_POSITIONS.get(f).second, true));
|
||||||
|
assertTrue(model.placeInitialRoad(INITIAL_SETTLEMENT_POSITIONS.get(f).second, INITIAL_ROAD_ENDPOINTS.get(f).second));
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a siedler game after the setup phase in the setup
|
||||||
|
* described in {@link ThreePlayerStandard} and with the bank almost empty.
|
||||||
|
*
|
||||||
|
* The following resource cards should be in the stock of the bank:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 1</li>
|
||||||
|
* <li>BRICK: 2</li>
|
||||||
|
* <li>GRAIN: 3</li>
|
||||||
|
* <li>ORE: 13</li>
|
||||||
|
* <li>WOOL: 0</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* The stocks of the players should contain:
|
||||||
|
* <br>
|
||||||
|
* Player 1:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 9</li>
|
||||||
|
* <li>BRICK: 9</li>
|
||||||
|
* <li>GRAIN: 8</li>
|
||||||
|
* <li>ORE: 7</li>
|
||||||
|
* <li>WOOL: 9</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 2:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 0</li>
|
||||||
|
* <li>BRICK: 0</li>
|
||||||
|
* <li>GRAIN: 8</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 10</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 3:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 9</li>
|
||||||
|
* <li>BRICK: 8</li>
|
||||||
|
* <li>GRAIN: 0</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 0</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param winpoints the number of points required to win the game
|
||||||
|
* @return the siedler game
|
||||||
|
*/
|
||||||
|
public static SiedlerGame getAfterSetupPhaseAlmostEmptyBank(int winpoints) {
|
||||||
|
SiedlerGame model = getAfterSetupPhase(winpoints);
|
||||||
|
throwDiceMultipleTimes(model, 6, 9);
|
||||||
|
throwDiceMultipleTimes(model, 11, 8);
|
||||||
|
throwDiceMultipleTimes(model, 2, 8);
|
||||||
|
throwDiceMultipleTimes(model, 4, 7);
|
||||||
|
throwDiceMultipleTimes(model, 12, 8);
|
||||||
|
throwDiceMultipleTimes(model, 5, 9);
|
||||||
|
throwDiceMultipleTimes(model, 9, 8);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link SiedlerGame} with several roads added but none longer than
|
||||||
|
* 4 elements. Hence, no player meets the longest road criteria yet. Furthermore,
|
||||||
|
* players one and three have enough resource cards to build additional roads and settlements.
|
||||||
|
*
|
||||||
|
* <p></p>
|
||||||
|
* <p>The game board should look as follows:
|
||||||
|
* <pre>
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) (bb) ( ) ( )
|
||||||
|
* || ~~ || LU || WL bb WL || ~~ ||
|
||||||
|
* || || 06 || 03 bb 08 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ rr \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) (rr) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ gg GR rr OR || GR || LU || ~~ ||
|
||||||
|
* || gg 02 rr 04 || 05 || 10 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ gg rr rr \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ gg LU || BR || -- || OR || GR || ~~ ||
|
||||||
|
* || gg 05 || 09 || 07 || 06 || 09 || ||
|
||||||
|
* ( ) (gg) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // gg // \\ // \\ // \\ rr \\ bb \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) (bb) ( )
|
||||||
|
* || ~~ || GR || OR || LU rr WL || ~~ ||
|
||||||
|
* || || 10 || 11 || 03 rr 12 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ // rr rr \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) (rr) ( )
|
||||||
|
* || ~~ || WL gg BR gg BR rr ~~ ||
|
||||||
|
* || || 08 gg 04 gg 11 rr ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // gg gg \\ // \\ //
|
||||||
|
* ( ) ( ) (gg) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ //
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* And the player resource card stocks:
|
||||||
|
* <br>
|
||||||
|
* Player 1:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 6</li>
|
||||||
|
* <li>BRICK: 6</li>
|
||||||
|
* <li>GRAIN: 1</li>
|
||||||
|
* <li>ORE: 11</li>
|
||||||
|
* <li>WOOL: 1</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 2:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 0</li>
|
||||||
|
* <li>BRICK: 0</li>
|
||||||
|
* <li>GRAIN: 0</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 2</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 3:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 6</li>
|
||||||
|
* <li>BRICK: 6</li>
|
||||||
|
* <li>GRAIN: 1</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 1</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param winpoints the number of points required to win the game
|
||||||
|
* @return the siedler game
|
||||||
|
*/
|
||||||
|
public static SiedlerGame getAfterSetupPhaseSomeRoads(int winpoints) {
|
||||||
|
SiedlerGame model = getAfterSetupPhase(winpoints);
|
||||||
|
throwDiceMultipleTimes(model, 6, 7);
|
||||||
|
throwDiceMultipleTimes(model, 11, 6);
|
||||||
|
throwDiceMultipleTimes(model, 4, 5);
|
||||||
|
throwDiceMultipleTimes(model, 5, 6);
|
||||||
|
throwDiceMultipleTimes(model, 2, 1);
|
||||||
|
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
model.buildRoad(new Point(2,12), new Point(3,13));
|
||||||
|
buildRoad(model, List.of(new Point(2,10), new Point(3,9), new Point(3,7)));
|
||||||
|
model.buildRoad(new Point(8,18), new Point(8,16));
|
||||||
|
buildRoad(model, List.of(new Point(7,19), new Point(6,18), new Point(6,16)));
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
model.buildRoad(new Point(10,16), new Point(11,15));
|
||||||
|
model.buildRoad(new Point(10,16), new Point(10,18));
|
||||||
|
buildRoad(model, List.of(new Point(9,15), new Point(9,13), new Point(10,12)));
|
||||||
|
buildRoad(model, List.of(new Point(5,7), new Point(5,9), new Point(4,10), new Point(3, 9)));
|
||||||
|
|
||||||
|
throwDiceMultipleTimes(model, 6, 6);
|
||||||
|
throwDiceMultipleTimes(model, 11, 6);
|
||||||
|
throwDiceMultipleTimes(model, 4, 6);
|
||||||
|
throwDiceMultipleTimes(model, 5, 6);
|
||||||
|
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
throwDiceMultipleTimes(model, 5, 4);
|
||||||
|
model.tradeWithBankFourToOne(Resource.LUMBER, Resource.GRAIN);
|
||||||
|
throwDiceMultipleTimes(model, 5, 4);
|
||||||
|
model.tradeWithBankFourToOne(Resource.LUMBER, Resource.WOOL);
|
||||||
|
model.switchToNextPlayer();
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static SiedlerGame throwDiceMultipleTimes(SiedlerGame model, int diceValue, int numberOfTimes) {
|
||||||
|
for(int i=0; i<numberOfTimes; i++) {
|
||||||
|
model.throwDice(diceValue);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a siedler game after building four additional roads and two
|
||||||
|
* settlements after the setup phase with the resource cards and roads
|
||||||
|
* for player one ready to build a fifth settlement at {@link #PLAYER_ONE_READY_TO_BUILD_FIFTH_SETTLEMENT_FIFTH_SETTLEMENT_POSITION}
|
||||||
|
*<p></p>
|
||||||
|
*<p>The game board should look as follows:
|
||||||
|
* <pre>
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) (rr) (bb) ( ) ( )
|
||||||
|
* || ~~ || LU rr WL bb WL || ~~ ||
|
||||||
|
* || || 06 rr 03 bb 08 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ rr rr // \\ // \\ // \\
|
||||||
|
* ( ) ( ) (rr) (rr) ( ) ( ) ( )
|
||||||
|
* || ~~ || GR || OR || GR || LU || ~~ ||
|
||||||
|
* || || 02 || 04 || 05 || 10 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* // \\ // \\ // \\ // \\ // \\ // \\ // \\
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* || ~~ gg LU || BR || -- || OR || GR || ~~ ||
|
||||||
|
* || gg 05 || 09 || 07 || 06 || 09 || ||
|
||||||
|
* ( ) (gg) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ // \\ // \\ bb \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) (bb) ( )
|
||||||
|
* || ~~ || GR || OR || LU rr WL || ~~ ||
|
||||||
|
* || || 10 || 11 || 03 rr 12 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ // rr // \\ //
|
||||||
|
* ( ) ( ) ( ) ( ) (rr) ( )
|
||||||
|
* || ~~ || WL || BR || BR || ~~ ||
|
||||||
|
* || || 08 || 04 || 11 || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ gg \\ // \\ //
|
||||||
|
* ( ) ( ) (gg) ( ) ( )
|
||||||
|
* || ~~ || ~~ || ~~ || ~~ ||
|
||||||
|
* || || || || ||
|
||||||
|
* ( ) ( ) ( ) ( ) ( )
|
||||||
|
* \\ // \\ // \\ // \\ //
|
||||||
|
* ( ) ( ) ( ) ( )
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
* <br>
|
||||||
|
* <p>And the player resource card stocks:</p>
|
||||||
|
* <br>
|
||||||
|
* Player 1:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 3</li>
|
||||||
|
* <li>BRICK: 3</li>
|
||||||
|
* <li>GRAIN: 2</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 2</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 2:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 0</li>
|
||||||
|
* <li>BRICK: 0</li>
|
||||||
|
* <li>GRAIN: 0</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 5</li>
|
||||||
|
* </ul>
|
||||||
|
* Player 3:
|
||||||
|
* <ul>
|
||||||
|
* <li>LUMBER: 0</li>
|
||||||
|
* <li>BRICK: 1</li>
|
||||||
|
* <li>GRAIN: 0</li>
|
||||||
|
* <li>ORE: 0</li>
|
||||||
|
* <li>WOOL: 0</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param winpoints the number of points required to win the game
|
||||||
|
* @return the siedler game
|
||||||
|
*/
|
||||||
|
public static SiedlerGame getPlayerOneReadyToBuildFifthSettlement(int winpoints) {
|
||||||
|
SiedlerGame model = getAfterSetupPhase(winpoints);
|
||||||
|
//generate resources to build four roads and four settlements.
|
||||||
|
throwDiceMultipleTimes(model, 6, 8);
|
||||||
|
throwDiceMultipleTimes(model, 11, 7);
|
||||||
|
throwDiceMultipleTimes(model, 2, 4);
|
||||||
|
throwDiceMultipleTimes(model, 12, 3);
|
||||||
|
model.buildRoad(new Point(6,6), new Point(7,7));
|
||||||
|
model.buildRoad(new Point(6,6), new Point(6,4));
|
||||||
|
model.buildRoad(new Point(9,15), new Point(9,13));
|
||||||
|
model.buildSettlement(playerOneReadyToBuildFifthSettlementAllSettlementPositions.get(2));
|
||||||
|
model.buildSettlement(playerOneReadyToBuildFifthSettlementAllSettlementPositions.get(3));
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
private static void buildSettlement(SiedlerGame model, Point position, List<Point> roads) {
|
||||||
|
buildRoad(model, roads);
|
||||||
|
assertTrue(model.buildSettlement(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void buildRoad(SiedlerGame model, List<Point> roads) {
|
||||||
|
for (int i = 0; i < roads.size()-1; i++) {
|
||||||
|
assertTrue(model.buildRoad(roads.get(i), roads.get(i + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* <p>
|
||||||
|
* This class performs tests for the class {@link Edge}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author tebe
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
public class EdgeTest {
|
||||||
|
|
||||||
|
private Point[] hexagon22 = { new Point(2, 0), new Point(3, 1), new Point(3, 3), new Point(2, 4),
|
||||||
|
new Point(1, 3), new Point(1, 1) };
|
||||||
|
private Point[] hexagon75 = { new Point(7, 3), new Point(8, 4), new Point(8, 6), new Point(7, 7),
|
||||||
|
new Point(6, 6), new Point(6, 4) };
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createValidEdge() {
|
||||||
|
new Edge(new Point(0, 0), new Point(1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void edgeEqualityStartEndPointReversed() {
|
||||||
|
for (int i = 0; i < hexagon22.length - 1; i++) {
|
||||||
|
assertEquals(new Edge(hexagon22[i], hexagon22[i + 1]),
|
||||||
|
new Edge(hexagon22[i + 1], hexagon22[i]));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hexagon75.length - 1; i++) {
|
||||||
|
assertEquals(new Edge(hexagon75[i], hexagon75[i + 1]),
|
||||||
|
new Edge(hexagon75[i + 1], hexagon75[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notEquals() {
|
||||||
|
assertNotEquals(new Edge(hexagon22[0], hexagon22[1]),
|
||||||
|
new Edge(hexagon22[1], hexagon22[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWithBothArgumentsNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWithFirstArgumentNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(null, new Point(1, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWithSecondArgumentNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(1, 0), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWithStartAndEndpointIdentical() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(hexagon22[0], hexagon22[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalOddTop() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(5, 7), new Point(7, 7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalOddMiddle() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(3, 2), new Point(5, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalOddBottom() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(5, 3), new Point(7, 3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalEvenTop() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(4, 4), new Point(6, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalEvenMiddle() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(2, 5), new Point(4, 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeHorizontalEvenBottom() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(4, 6), new Point(6, 6)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeVerticalEven() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(7, 7), new Point(7, 3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notAnEdgeVerticalOdd() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new Edge(new Point(6, 4), new Point(6, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package ch.zhaw.hexboard;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* <p>
|
||||||
|
* Tests for the class {@link HexBoard}.
|
||||||
|
* </p>
|
||||||
|
* @author tebe
|
||||||
|
*/
|
||||||
|
public class HexBoardTest {
|
||||||
|
private HexBoard<String, String, String, String> board;
|
||||||
|
private Point[] corner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup for a test - Instantiates a board and adds one field at (7,5).
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 0 1 2 3 4 5 6 7 8
|
||||||
|
* | | | | | | | | | ...
|
||||||
|
*
|
||||||
|
* 0----
|
||||||
|
*
|
||||||
|
* 1----
|
||||||
|
*
|
||||||
|
* 2----
|
||||||
|
*
|
||||||
|
* 3---- C
|
||||||
|
* / \
|
||||||
|
* 4---- C C
|
||||||
|
*
|
||||||
|
* 5---- | F | ...
|
||||||
|
*
|
||||||
|
* 6---- C C
|
||||||
|
* \ /
|
||||||
|
* 7---- C
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
board = new HexBoard<>();
|
||||||
|
board.addField(new Point(7, 5), "00");
|
||||||
|
Point[] singleField = { new Point(7, 3), new Point(8, 4), new Point(8, 6), new Point(7, 7),
|
||||||
|
new Point(6, 6), new Point(6, 4) };
|
||||||
|
this.corner = singleField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge retrieval
|
||||||
|
@Test
|
||||||
|
public void edgeTest() {
|
||||||
|
for (int i = 0; i < corner.length - 1; i++) {
|
||||||
|
assertNull(board.getEdge(corner[i], corner[i + 1]));
|
||||||
|
board.setEdge(corner[i], corner[i + 1], Integer.toString(i));
|
||||||
|
assertEquals(board.getEdge(corner[i], corner[i + 1]), Integer.toString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noEdgeCoordinatesTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> board.getEdge(new Point(2, 2), new Point(0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void edgeDoesNotExistTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> board.getEdge(new Point(0, 2), new Point(3, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corner retrieval
|
||||||
|
@Test
|
||||||
|
public void cornerTest() {
|
||||||
|
for (Point p : corner) {
|
||||||
|
assertNull(board.getCorner(p));
|
||||||
|
board.setCorner(p, p.toString());
|
||||||
|
assertEquals(board.getCorner(p), p.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noCornerCoordinateTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> board.getCorner(new Point(2, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cornerDoesNotExistTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> board.getCorner(new Point(2, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field addition/retrieval
|
||||||
|
@Test
|
||||||
|
public void fieldAreadyExistsErrorTest() {
|
||||||
|
board.addField(new Point(2, 2), "22");
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> board.addField(new Point(2, 2), "22"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fieldRetrievalTest() {
|
||||||
|
Point field = new Point(2, 2);
|
||||||
|
board.addField(field, new String("22"));
|
||||||
|
assertTrue(board.hasField(field));
|
||||||
|
assertEquals(board.getField(field), "22");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fieldRetrievalWrongCoordinatesOutsideTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> board.getField(new Point(10, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fieldRetrievalWrongCoordinatesInsideTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> board.getField(new Point(2, 2)));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue