diff --git a/.gitignore b/.gitignore index 33732f06..14fbc1ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ target .Makefile.swp +.idea/ +.aider.* +.DS_Store diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..3500e153 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..aa00ffab --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..712ab9d9 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..644f2008 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 6 +} + + + + + + + + + + + + + + + + + + + 1730900339117 + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 0a2144e5..341874c3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,45 @@ -# Stone Age +# Assignment - Stone Age game -This is the Java version of the semestral project from Principles of Software Design (1) course on FMFI UK, 2024/25. +Your task is to construct a component implementing the logic of board game Stone Age. These are the [rules](https://images.zmangames.com/filer_public/ee/88/ee888bec-5000-4566-9fb0-8c6475e706a0/zm7260_stone_age_rules.pdf). + +[!Classes Description!](https://github.com/sdeffff/pts24-java/blob/main/src/task/classes.md) + + +## Design + +I prepared a [design](pts24.png) template for your implementation. Besides that I prepared [simple description of the classes](classes.md) with estimations + +Note that the design is by no means complete. Most notably +- You have to add appropriate constructors. Chose your constructors in a way that enables good testing. E.g. card deck and building stacks do not need to be full. +- You have to add interfaces that separate classes that need to be separated. +- You probably want to do something with Bag class to handle the randomness involved better. +Also, it is highly likely, that I overlooked something and there are some issues that need to be resolved. Feel free to do necessary changes and adjustments. + +I recommend to stick to the following timeline +- 11. 11. create teams, understand game rules, understand the design (so we can have a meaningful discussion on the lecture). +- 20. 11. the common part of the project is finished +- 27. 11. you are done + +## Implementation remarks + +You should build your project either in Python or in Java. I started the projects for you, including several tools that might help you. See [pts24-python](https://github.com/relatko/pts1-23-python) and [pts24-java](https://github.com/relatko/pts1-23-java). Note that if you use Python, your code should be type annotated and mypy --strict should show no errors. Do not use any except for when handling json. + +This is a fairly large project and it would take too long to implement. Thus you will collaborate while implementing the project. + +* Create groups of, ideally, six to eight people. and fork the repository you agree to use. Green classes are provided in the project (Java GamePhaseController will be implemented soonish). +* Collaboratively implement the elements marked white (trivial) and blue, including factories and integration tests for two components. For blue elements create a PR (typically one PR per element, but you may merge several very trivial ones into one PR), discuss it and merge it into the master (I recommend main branch protection requiring PR with at least 1 approval). Blue classes are not of particular interest to me. The amount of points given here is quite limited (up to 20 points including non-coding aspects of common work like like making and reviewing PRs), so do not overthink stuff, but make it sound enough so you actually can write working integration tests. This also implies that the test coverage does not have to be great in these classes. While collaborating create new features in own branches and merge it to main with a PR after code review is done. Turn on branch protection. Try to keep CI green (you can enforce this by branch protection rules). You may use github issues to your the progress. You may split large task into smaller tasks. You may want to meet one time before you start your work. Splitting L tasks is a good thing to do. +It is expected that everybody prepares and reviews 3-5 PRs. +* After this, you should add your own implementation of the red classes (including unit tests) and prepare two integration tests. To reduce the amount of work necessary, it is sufficient to create one end to end scenario for each of these integration test. + +Unit tests for Game should be solitary. Do a sociable unit test of CivilisationCardPlace that also uses CivilisationCardDeck. Tests of ResourceSource must be solitary. You can discuss technical aspects of the implementation, but everybody should implement the red classes and the red integration tests on his own. You can either work on local machine or use a private repository for your effort. + +You should use Git and produce a reasonable history of commits. Note that to work on the red classes you do not need the blue ones. You are also free to adjust the implementation of blue and green classes according to the needs of your project (but, the changes, hopefully should not be large, a good example is changing interface name, adding a new interface, ...). + + +## Instructions for submitting your project + +Send your solution to [lukotka.pts@gmail.com](lukotka.pts@gmail.com). The deadline is 30.11.2022 23:59:59. The solutions sent later will be accepted, however the number of points awarded may be reduced. + +Send the solution to me either as compressed folders containing the whole repository (including the hidden .git files) or as a link to private repository with read access granted (GitHub handle `relatko`). Attach a link to the public repository for the team part of the project and link PRs you have made and PRs you have reviewed. + +![UML of the task](https://github.com/sdeffff/pts24-java/blob/main/src/task/pts24.png?raw=true) \ No newline at end of file diff --git a/STONE_AGE.iml b/STONE_AGE.iml new file mode 100644 index 00000000..fbce2312 --- /dev/null +++ b/STONE_AGE.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index d148f55d..eb733590 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ UTF-8 - 21 + 17 diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuilding.java b/src/main/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuilding.java new file mode 100644 index 00000000..4f4aa54b --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuilding.java @@ -0,0 +1,40 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; +import java.util.Collection; +import java.util.OptionalInt; + +public class ArbitraryBuilding implements Building { + private final int maxNumberOfResources; + + public ArbitraryBuilding(int maxNumberOfResources) { + this.maxNumberOfResources = maxNumberOfResources; + } + + @Override + public OptionalInt build(Collection resources) { + if (resources.isEmpty() || resources.size() > maxNumberOfResources) { + return OptionalInt.empty(); + } + + // Verify all resources are valid + for (Effect resource : resources) { + if (!resource.isResource()) { + return OptionalInt.empty(); + } + } + + // Calculate points + int points = 0; + for (Effect resource : resources) { + points += resource.points(); + } + + return OptionalInt.of(points); + } + + @Override + public String state() { + return String.format("ArbitraryBuilding[maxResources=%d]", maxNumberOfResources); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/Building.java b/src/main/java/sk/uniba/fmph/dcs/game_board/Building.java index cf279418..7da07b3a 100644 --- a/src/main/java/sk/uniba/fmph/dcs/game_board/Building.java +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/Building.java @@ -4,6 +4,7 @@ import java.util.OptionalInt; import sk.uniba.fmph.dcs.stone_age.Effect; -interface Building { +public interface Building { OptionalInt build(Collection resources); + String state(); } diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/BuildingTile.java b/src/main/java/sk/uniba/fmph/dcs/game_board/BuildingTile.java new file mode 100644 index 00000000..242835e7 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/BuildingTile.java @@ -0,0 +1,99 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.PlayerOrder; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.Player; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.ActionResult; + +import java.util.*; + +import org.json.JSONObject; + +public class BuildingTile implements InterfaceFigureLocationInternal { + private final Stack buildings; + private final ArrayList figures; + private static final int MAX_FIGURES = 1; + + public BuildingTile(List building) { + this.buildings = new Stack<>(); + this.buildings.addAll(building); + this.figures = new ArrayList<>(); + } + + @Override + public boolean placeFigures(Player player, int figureCount) { + if (figureCount != MAX_FIGURES || !figures.isEmpty()) { + return false; + } + figures.add(player.playerOrder()); + return true; + } + + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + if (count != MAX_FIGURES || !figures.isEmpty()) { + return HasAction.NO_ACTION_POSSIBLE; + } + if (!player.playerBoard().hasFigures(count)) { + return HasAction.NO_ACTION_POSSIBLE; + } + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + @Override + public ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources) { + if (figures.isEmpty() || !figures.get(0).equals(player.playerOrder())) { + return ActionResult.FAILURE; + } + + Collection resources = List.of(inputResources); + + if(buildings.isEmpty()){ + return ActionResult.FAILURE; + } + OptionalInt points = buildings.pop().build(resources); + + // Give points to player + List pointsToGive = new ArrayList<>(); + for(int i=0; i state = Map.of( + "building", buildings, + "figures", figures.stream().map(PlayerOrder::getOrder).toList() + ); + return new JSONObject(state).toString(); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/CivilisationCardDeck.java b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilisationCardDeck.java new file mode 100644 index 00000000..7a48c504 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilisationCardDeck.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.game_board; + +public class CivilisationCardDeck { + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCard.java b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCard.java new file mode 100644 index 00000000..cc7447a1 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCard.java @@ -0,0 +1,25 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.EndOfGameEffect; +import sk.uniba.fmph.dcs.stone_age.ImmediateEffect; + +import java.util.Collections; +import java.util.List; + +public class CivilizationCard { + private List immediateEffectType; + private List EndOfGameEffectType; + + public CivilizationCard(List immediateEffectType, List EndOfGameEffectType){ + this.immediateEffectType = immediateEffectType; + this.EndOfGameEffectType = EndOfGameEffectType; + } + + public List getEndOfGameEffectType() { + return Collections.unmodifiableList(EndOfGameEffectType); + } + + public List getImmediateEffectType() { + return Collections.unmodifiableList(immediateEffectType); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlace.java b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlace.java new file mode 100644 index 00000000..5624e491 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlace.java @@ -0,0 +1,170 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.*; + +/** + * Represents the area on the game board where players interact with Civilization Cards. + * Handles figure placement, actions related to card acquisition, and resets at the end of each turn. + */ +public class CivilizationCardPlace implements InterfaceFigureLocationInternal { + // The number of resources required to acquire a civilization card + private final int requiredResources; + + // Evaluates and applies the immediate effect of a civilization card + private final EvaluateCivilizationCardImmediateEffect evaluateCivilizationCardImmediateEffect; + + // The deck of civilization cards + private final CivilizationCardDeck deck; + + // Stores the player orders (figures) currently placed in this location + private final List figures; + + // The topmost civilization card available for acquisition + private CivilizationCard currentCivilizationCard; + + // Tracks whether an action has already been made this turn + private boolean actionMade; + + /** + * Constructor for CivilizationCardPlace. + * + * @param requiredResources The number of resources needed to acquire a card. + * @param figures A collection to track figures placed in this location. + */ + public CivilizationCardPlace(int requiredResources, List figures, + CivilizationCardDeck deck, + EvaluateCivilizationCardImmediateEffect evaluateEffect) { + this.requiredResources = requiredResources; + this.figures = figures; + this.actionMade = false; + this.deck = deck; + this.evaluateCivilizationCardImmediateEffect = evaluateEffect; + } + + /** + * Places a specified number of figures on this location for a player. + * + * @param player The player attempting to place figures. + * @param figureCount The number of figures to place. + * @return True if figures were successfully placed, otherwise false. + */ + @Override + public boolean placeFigures(Player player, int figureCount) { + if (!canPlaceFigures(player, figureCount)) { + return false; + } + figures.add(player.playerOrder()); + return true; + } + + private boolean canPlaceFigures(Player player, int figureCount) { + return player.playerBoard().hasFigures(figureCount) && figures.isEmpty() && player.playerBoard() + .takeFigures(figureCount); + } + + /** + * Attempts to validate and place figures for a player. + * + * @param player The player attempting to place figures. + * @param count The number of figures to place. + * @return The action status as HasAction. + */ + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + if (isInvalidPlacement(player, count)) { + return HasAction.NO_ACTION_POSSIBLE; + } + figures.add(player.playerOrder()); + return HasAction.AUTOMATIC_ACTION_DONE; + } + + private boolean isInvalidPlacement(Player player, int count) { + return player == null || count < 1 || count > 10 || !canPlaceFigures(player, count); + } + + /** + * Executes the action associated with acquiring a civilization card. + * + * @param player The player performing the action. + * @param inputResources Resources the player is using to acquire the card. + * @param outputResources Effects/resources gained from the card (unused here). + * @return The result of the action as ActionResult. + */ + @Override + public ActionResult makeAction(Player player, Collection inputResources, Collection outputResources) { + if (!canMakeAction(player, inputResources)) { + return ActionResult.FAILURE; + } + applyCardEffects(player, outputResources); + currentCivilizationCard = deck.getTop().orElse(null); + actionMade = true; + return ActionResult.ACTION_DONE; + } + + private boolean canMakeAction(Player player, Collection inputResources) { + return !actionMade && figures.contains(player.playerOrder()) && inputResources.size() >= requiredResources && player.playerBoard().takeResources(inputResources); + } + + private void applyCardEffects(Player player, Collection outputResources) { + player.playerBoard().giveCard(currentCivilizationCard); + if (currentCivilizationCard != null) { + for (ImmediateEffect effect : currentCivilizationCard.getImmediateEffectType()) { + evaluateCivilizationCardImmediateEffect.performEffect(player, effect); + } + } + if (!outputResources.isEmpty()) { + player.playerBoard().giveEffect(outputResources); + } + } + + /** + * Allows a player to skip the action for this location. + * + * @param player The player skipping the action. + * @return True after successfully skipping the action. + */ + @Override + public boolean skipAction(Player player) { + figures.remove(player.playerOrder()); + actionMade = true; + return true; + } + + /** + * Checks if the player can perform the action associated with this location. + * + * @param player The player attempting the action. + * @return The action status as HasAction. + */ + @Override + public HasAction tryToMakeAction(Player player) { + return actionMade || !figures.contains(player.playerOrder()) ? HasAction.NO_ACTION_POSSIBLE : HasAction.AUTOMATIC_ACTION_DONE; + } + + /** + * Resets the location for the next game round. + * + * @return True after successfully resetting. + */ + @Override + public boolean newTurn() { + actionMade = false; + figures.clear(); + currentCivilizationCard = deck.getTop().orElse(null); + return true; + } + + public String state() { + return "CivilizationCardPlace State:\n" + + " Current Card: " + + (currentCivilizationCard != null ? currentCivilizationCard.toString() : "None") + + "\n" + + " Action Made: " + actionMade + "\n" + + " Figures Placed: " + figures.size() + " " + + (figures.isEmpty() ? "(None)" : figures) + "\n" + + " Deck Status: " + deck.state(); + } +} + diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/CurrentThrow.java b/src/main/java/sk/uniba/fmph/dcs/game_board/CurrentThrow.java new file mode 100644 index 00000000..1046e174 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/CurrentThrow.java @@ -0,0 +1,155 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.*; +import java.util.Optional; + +import java.util.*; + +/** + * The {@code CurrentThrow} class represents the process of managing a dice throw for a player in the game. + * It calculates throw results, allows the use of tools to modify the results, and applies the results + * as resources or effects to the player. + * + *

This class implements the {@link InterfaceToolUse} interface to manage tool usage during the throw. + */ +public class CurrentThrow implements InterfaceToolUse { + + private Effect throwsFor; + private int throwResult; + private Throw throw_ = new Throw(); + private Player player; + private int dices; + private int[] dicesResults; + private boolean toolsUsed = false; + private boolean finished = false; + + /** + * Initializes a new dice throw for a player, specifying the target effect and number of dice to roll. + * This method calculates the initial throw result and stores the dice outcomes. + * + * @param player the player performing the throw + * @param effect the effect for which the dice are being thrown (e.g., resource type) + * @param dices the number of dice to roll + */ + public void initiate(Player player, Effect effect, int dices) { + this.throwsFor = effect; + this.player = player; + this.dices = dices; + int[] dicesResults = throw_.throw_(dices); + this.dicesResults = dicesResults; + throwResult = Arrays.stream(dicesResults).reduce(0, Integer::sum); + } + + /** + * Uses a tool to modify the throw result. The tool adds its value to the throw result. + * + * @param idx the index of the tool to use + * @return {@code true} if the tool was successfully used; {@code false} otherwise + */ + @Override + public boolean useTool(int idx) { + if (this.finished) { + return false; + } + + Optional a = player.playerBoard().useTool(idx); + if (a.isPresent()) { + throwResult += a.get(); + toolsUsed = true; + return true; + } + return false; + } + + /** + * Checks if the player can use tools during this throw. + * + * @return {@code true} if the player has at least one tool available; {@code false} otherwise + */ + @Override + public boolean canUseTools() { + return this.player.playerBoard().hasSufficientTools(1); + } + + /** + * Finalizes the tool usage and applies the throw result as resources or effects to the player. + * + * @return {@code true} if the process was successfully finished; {@code false} otherwise + */ + @Override + public boolean finishUsingTools() { + if (this.finished) { + return false; + } + + if (!throwsFor.isResourceOrFood()) { + return false; + } + + List effects = new ArrayList<>(); + + switch (throwsFor) { + case WOOD: + for (int i = 0; i < Math.floorDiv(throwResult, throwsFor.points()); i++) { + effects.add(Effect.WOOD); + } + break; + case CLAY: + for (int i = 0; i < Math.floorDiv(throwResult, throwsFor.points()); i++) { + effects.add(Effect.CLAY); + } + break; + case STONE: + for (int i = 0; i < Math.floorDiv(throwResult, throwsFor.points()); i++) { + effects.add(Effect.STONE); + } + break; + case GOLD: + for (int i = 0; i < Math.floorDiv(throwResult, throwsFor.points()); i++) { + effects.add(Effect.GOLD); + } + break; + case FOOD: + for (int i = 0; i < Math.floorDiv(throwResult, throwsFor.points()); i++) { + effects.add(Effect.FOOD); + } + break; + default: + break; + } + + player.playerBoard().giveEffect(effects); + finished = true; + return true; + } + + /** + * Returns a JSON string representing the current state of the throw. + * + *

The JSON object contains the following fields: + *

    + *
  • {@code "throwsFor"}: the target effect of the throw
  • + *
  • {@code "throwResult"}: the total result of the throw
  • + *
  • {@code "player"}: the player performing the throw
  • + *
  • {@code "dices"}: the number of dice rolled
  • + *
  • {@code "dicesResults"}: an array of individual dice results
  • + *
  • {@code "toolsUsed"}: whether tools have been used in this throw
  • + *
+ * + * @return a JSON string representation of the throw state + */ + public String state() { + Map state = Map.of( + "throwsFor", throwsFor, + "throwResult", throwResult, + "player", player, + "dices", dices, + "dicesResults", dicesResults, + "toolsUsed", toolsUsed + ); + return new JSONObject(state).toString(); + } +} + + diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/EvaluateCivilizationCardImmediateEffect.java b/src/main/java/sk/uniba/fmph/dcs/game_board/EvaluateCivilizationCardImmediateEffect.java new file mode 100644 index 00000000..6778f25f --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/EvaluateCivilizationCardImmediateEffect.java @@ -0,0 +1,8 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.Player; + +public interface EvaluateCivilizationCardImmediateEffect { + boolean performEffect(Player player, Effect choice); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/FigureLocationAdaptor.java b/src/main/java/sk/uniba/fmph/dcs/game_board/FigureLocationAdaptor.java new file mode 100644 index 00000000..f1ebac1a --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/FigureLocationAdaptor.java @@ -0,0 +1,88 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.ActionResult; +import sk.uniba.fmph.dcs.stone_age.InterfaceFigureLocation; +import sk.uniba.fmph.dcs.stone_age.Player; +import sk.uniba.fmph.dcs.stone_age.PlayerOrder; + +import java.util.*; + +public class FigureLocationAdaptor implements InterfaceFigureLocation { + private final InterfaceFigureLocationInternal figureLocation; + private final List players; + + + public FigureLocationAdaptor(final InterfaceFigureLocationInternal figureLocation, List players) { + this.figureLocation = figureLocation; + this.players = players; + } + + // Helper method to find the Player object by PlayerOrder + private Player getPlayerOrder(PlayerOrder player) { + for (Player pl : players) { + if (pl.playerOrder().equals(player)) { + return pl; + } + } + return ActionResult.FAILURE; + } + + // Places the specified number of figures for the given player + @Override + public boolean placeFigures(PlayerOrder player, int figureCount) { + Player p = getPlayerOrder(player); + if (p != null) { + return figureLocation.placeFigures(p, figureCount); + } + return false; + } + + // Checks if figures can be placed for the given player + @Override + public HasAction tryToPlaceFigures(PlayerOrder player, int count) { + Player pl = getPlayerOrder(player); + if (pl != null) { + return figureLocation.tryToPlaceFigures(pl, count); + } + return ActionResult.FAILURE; + } + + // Performs an action for the player using input and output resources + @Override + public ActionResult makeAction(PlayerOrder player, Collection inputResources, Collection outputResources) { + Effect[] input = inputResources.toArray(new Effect[0]); + Effect[] output = outputResources.toArray(new Effect[0]); + Player pl = getPlayerOrder(player); + if (pl != null) { + return figureLocation.makeAction(pl, input, output); + } + return ActionResult.FAILURE; + } + + // Skips the action + @Override + public boolean skipAction(PlayerOrder player) { + Player pl = getPlayerOrder(player); + if (pl != null) { + return figureLocation.skipAction(pl); + } + return false; + } + + // Attempts to perform an action for the given player + @Override + public HasAction tryToMakeAction(PlayerOrder player) { + Player pl = getPlayerOrder(player); + if (pl != null) { + figureLocation.tryToMakeAction(pl); + } + return ActionResult.FAILURE; + } + + @Override + public boolean newTurn() { + return figureLocation.newTurn(); + } +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/GameBoard.java b/src/main/java/sk/uniba/fmph/dcs/game_board/GameBoard.java new file mode 100644 index 00000000..91e4a184 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/GameBoard.java @@ -0,0 +1,81 @@ +package sk.uniba.fmph.dcs.game_board; +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.InterfaceGetState; +import sk.uniba.fmph.dcs.stone_age.Location; +import sk.uniba.fmph.dcs.stone_age.Player; + +import java.util.*; + +/** + * The {@code GameBoard} class represents the central game board in the Stone Age game. + * It manages all locations on the board, including resource sources, tool-making huts, + * fields, buildings, and civilization card slots. + * + *

This class implements the {@link InterfaceGetState} interface to provide a JSON-based + * representation of the game board's state for external use. + */ +public class GameBoard implements InterfaceGetState { + + private final Map locations = new HashMap<>(); + + /** + * Constructs a new {@code GameBoard} instance with the given players, buildings, and civilization cards. + * Initializes all locations on the board, including tool-making, resource sources, building tiles, + * and civilization card slots. + * + * @param players a collection of players participating in the game + * @param buildings an array of {@link Building} objects to be placed on the building tiles + * @param civilizationCards an array of {@link CivilizationCard} objects for the card deck + */ + public GameBoard(Collection players, Building[] buildings, CivilizationCard[] civilizationCards) { + ToolMakerHutFields fields = new ToolMakerHutFields(players.size()); + + // Tool Maker, Hut, and Field locations + locations.put(Location.TOOL_MAKER, new PlaceOnToolMakerAdaptor(fields)); + locations.put(Location.HUT, new PlaceOnHutAdaptor()); + locations.put(Location.FIELD, new PlaceOnFieldsAdaptor(fields)); + + // Resource sources + locations.put(Location.FOREST, new ResourceSource("Wood forest", Effect.WOOD, Integer.MAX_VALUE, 4)); + locations.put(Location.CLAY_MOUND, new ResourceSource("Clay mound", Effect.CLAY, Integer.MAX_VALUE, 4)); + locations.put(Location.QUARY, new ResourceSource("Stone quarry", Effect.STONE, Integer.MAX_VALUE, 4)); + locations.put(Location.RIVER, new ResourceSource("Gold river", Effect.GOLD, Integer.MAX_VALUE, 4)); + + // Building tiles + locations.put(Location.BUILDING_TILE1, new BuildingTile(buildings[0])); + locations.put(Location.BUILDING_TILE2, new BuildingTile(buildings[1])); + locations.put(Location.BUILDING_TILE3, new BuildingTile(buildings[2])); + locations.put(Location.BUILDING_TILE4, new BuildingTile(buildings[3])); + + // Civilization card slots + CivilisationCardDeck deck = new CivilisationCardDeck(civilizationCards); + var ccp1 = new CivilizationCardPlace(null, deck, 1); + var ccp2 = new CivilizationCardPlace(ccp1, deck, 2); + var ccp3 = new CivilizationCardPlace(ccp2, deck, 3); + var ccp4 = new CivilizationCardPlace(ccp3, deck, 4); + + locations.put(Location.CIVILISATION_CARD1, ccp1); + locations.put(Location.CIVILISATION_CARD2, ccp2); + locations.put(Location.CIVILISATION_CARD3, ccp3); + locations.put(Location.CIVILISATION_CARD4, ccp4); + } + + /** + * Returns a JSON string representing the current state of the game board. + * + *

The JSON object contains key-value pairs where: + *

    + *
  • The key is a {@link Location} representing the location type.
  • + *
  • The value is a string representation of the location's state.
  • + *
+ * + * @return a JSON string representation of the game board's state + */ + @Override + public String state() { + Map state = new HashMap<>(); + locations.forEach((key, value) -> state.put(key, value.state())); + return new JSONObject(state).toString(); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/GetCard.java b/src/main/java/sk/uniba/fmph/dcs/game_board/GetCard.java new file mode 100644 index 00000000..7269fb80 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/GetCard.java @@ -0,0 +1,31 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.Player; + +import java.util.Optional; +public class GetCard implements EvaluateCivilizationCardImmediateEffect { + private CivilizationCardDeck deck; + + public GetCard(CivilizationCardDeck deck) { + this.deck = deck; + } + + //Player gets endOfGameEffect from a card on top of the deck + @Override + public boolean performEffect(Player player, Effect effect){ + if (effect.equals(Effect.CARD)) { + Optional card = deck.getTop(); + if(card.isPresent()) { + player.playerBoard().giveCard(card.get()); + return true; + }else{ + return false; + } + }else{ + return false; + } + return false; + + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoice.java b/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoice.java new file mode 100644 index 00000000..488c07cc --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoice.java @@ -0,0 +1,39 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.Player; +import sk.uniba.fmph.dcs.stone_age.PlayerOrder; + +import java.util.List; +import java.util.ArrayList; + +public class GetSomethingChoice implements EvaluateCivilizationCardImmediateEffect{ + private int totalNumber; + private PlayerOrder playerOrder; + + public GetSomethingChoice(int totalNumber){ + this.totalNumber = totalNumber; + } + + //Player gets a resource choice + @Override + public boolean performEffect(Player player,Effect choice) { + if(playerOrder ==null){ + playerOrder = player.playerOrder(); + } + if(!playerOrder.equals(player.playerOrder())){ + return false; + } + if (!choice.isResource() || totalNumber == 0) { + return false; + } + totalNumber--; + + List effectToGive = new ArrayList<>(); + effectToGive.add(choice); + + player.playerBoard().giveEffect(effectToGive); + return true; + } + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingThrow.java b/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingThrow.java new file mode 100644 index 00000000..7bee34fe --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/GetSomethingThrow.java @@ -0,0 +1,12 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; + +public class GetSomethingThrow { + private Effect[] resource; + //private CurrentThrow name + + public GetSomethingThrow(Effect[] resource){ + this.resource = resource; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/InterfaceFigureLocationInternal.java b/src/main/java/sk/uniba/fmph/dcs/game_board/InterfaceFigureLocationInternal.java new file mode 100644 index 00000000..06e8dbb4 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/InterfaceFigureLocationInternal.java @@ -0,0 +1,17 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Player; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.ActionResult; + +public interface InterfaceFigureLocationInternal { + boolean placeFigures(Player player, int figureCount); + HasAction tryToPlaceFigures(Player player, int count); + ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources); + boolean skipAction(Player player); + HasAction tryToMakeAction(Player player); + boolean newTurn(); //{returns true if end of game is implied by given location} + + String state(); +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnFieldsAdaptor.java b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnFieldsAdaptor.java new file mode 100644 index 00000000..59ea0f5b --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnFieldsAdaptor.java @@ -0,0 +1,68 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.ActionResult; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.Player; + +public class PlaceOnFieldsAdaptor implements InterfaceFigureLocationInternal { + private ToolMakerHutFields toolMakerHutFields; + public PlaceOnFieldsAdaptor(ToolMakerHutFields toolMakerHutFields){ + this.toolMakerHutFields = toolMakerHutFields; + } + + @Override + public boolean placeFigures(Player player, int figureCount) { + if(figureCount >= 1 && toolMakerHutFields.canPlaceOnFields(player)){ + toolMakerHutFields.placeOnFields(player); + return true; + }else{ + return false; + } + } + + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + if(!player.playerBoard().hasFigures(count)){ + return HasAction.NO_ACTION_POSSIBLE; + } + + if(toolMakerHutFields.canPlaceOnFields(player)){ + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + return HasAction.NO_ACTION_POSSIBLE; + } + + @Override + public ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources) { + if(!toolMakerHutFields.canPlaceOnFields(player)){ + return ActionResult.FAILURE; + } + + if(toolMakerHutFields.actionFields(player)) { + return ActionResult.ACTION_DONE; + } + + return ActionResult.FAILURE; + } + + @Override + public boolean skipAction(Player player) { + return false; + } + + @Override + public HasAction tryToMakeAction(Player player) { + if(toolMakerHutFields.canPlaceOnFields(player)){ + return HasAction.WAITING_FOR_PLAYER_ACTION; + }else{ + return HasAction.NO_ACTION_POSSIBLE; + } + } + + @Override + public boolean newTurn() { + return false; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnHutAdaptor.java b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnHutAdaptor.java new file mode 100644 index 00000000..0973c324 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnHutAdaptor.java @@ -0,0 +1,39 @@ +package sk.uniba.fmph.dcs.game_board; + + +import sk.uniba.fmph.dcs.stone_age.ActionResult; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.Player; + +public class PlaceOnHutAdaptor implements InterfaceFigureLocationInternal{ + @Override + public boolean placeFigures(Player player, int figureCount) { + return false; + } + + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources) { + return ActionResult.FAILURE; + } + + @Override + public boolean skipAction(Player player) { + return false; + } + + @Override + public HasAction tryToMakeAction(Player player) { + return ActionResult.FAILURE; + } + + @Override + public boolean newTurn() { + return false; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnToolMakerAdaptor.java b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnToolMakerAdaptor.java new file mode 100644 index 00000000..8a305c1b --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/PlaceOnToolMakerAdaptor.java @@ -0,0 +1,70 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.ActionResult; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.Player; + +public class PlaceOnToolMakerAdaptor implements InterfaceFigureLocationInternal{ + +private final ToolMakerHutFields toolMakerHutFields; + + public PlaceOnToolMakerAdaptor(ToolMakerHutFields toolMakerHutFields){ + this.toolMakerHutFields = toolMakerHutFields; + } + + @Override + public boolean placeFigures(Player player, int figureCount) { + if(figureCount >= 1 && toolMakerHutFields.canPlaceOnToolMaker(player)){ + toolMakerHutFields.placeOnToolMaker(player); + return true; + }else{ + return false; + } + } + + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + if(!player.playerBoard().hasFigures(count)){ + return HasAction.NO_ACTION_POSSIBLE; + } + + if(toolMakerHutFields.canPlaceOnToolMaker(player)){ + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + return HasAction.NO_ACTION_POSSIBLE; + } + + @Override + public ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources) { + if(!toolMakerHutFields.canPlaceOnToolMaker(player)){ + return ActionResult.FAILURE; + } + + if(toolMakerHutFields.actionToolMaker(player)) { + return ActionResult.ACTION_DONE; + } + + return ActionResult.FAILURE; + } + + @Override + public boolean skipAction(Player player) { + return false; + } + + @Override + public HasAction tryToMakeAction(Player player) { + if(toolMakerHutFields.canPlaceOnToolMaker(player)){ + return HasAction.WAITING_FOR_PLAYER_ACTION; + }else{ + return HasAction.NO_ACTION_POSSIBLE; + } + } + + @Override + public boolean newTurn() { + return false; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/ResourceSource.java b/src/main/java/sk/uniba/fmph/dcs/game_board/ResourceSource.java new file mode 100644 index 00000000..7696781a --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/ResourceSource.java @@ -0,0 +1,166 @@ +package sk.uniba.fmph.dcs.game_board; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.Map; + +public final class ResourceSource implements InterfaceFigureLocationInternal { + private final String name; + private final Effect resource; + private final int maxFigures; + private final int maxFigureColors; + private final ArrayList figures; + + public ResourceSource(String name, Effect resource, int maxFigures, int maxFigureColors) { + if (!resource.isResourceOrFood()) { + throw new IllegalArgumentException("Resource must be food or resource"); + } + this.name = name; + this.resource = resource; + this.maxFigures = maxFigures; + this.maxFigureColors = maxFigureColors; + this.figures = new ArrayList<>(); + } + + @Override + public boolean placeFigures(Player player, int figureCount) { + // Check if player can place figures here + if (!canPlaceFigures(player, figureCount)) { + return false; + } + + // Add the figures + for (int i = 0; i < figureCount; i++) { + figures.add(player.playerOrder()); + } + return true; + } + + @Override + public HasAction tryToPlaceFigures(Player player, int count) { + if (!player.playerBoard().hasFigures(count)) { + return HasAction.NO_ACTION_POSSIBLE; + } + + if (canPlaceFigures(player, count)) { + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + return HasAction.NO_ACTION_POSSIBLE; + } + + @Override + public ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources) { + // Convert arrays to collections for existing logic + Collection inputs = Arrays.asList(inputResources); + Collection outputs = Arrays.asList(outputResources); + + // Verify it's this player's figures + if (!hasFiguresFromPlayer(player.playerOrder())) { + return ActionResult.FAILURE; + } + + // Resource sources don't take input resources + if (!inputs.isEmpty()) { + return ActionResult.FAILURE; + } + + // Resource sources must output exactly one type of resource per figure + int playerFigureCount = countPlayerFigures(player.playerOrder()); + if (outputs.size() != playerFigureCount) { + return ActionResult.FAILURE; + } + + for (Effect output : outputs) { + if (output != this.resource) { + return ActionResult.FAILURE; + } + } + + return ActionResult.ACTION_DONE_WAIT_FOR_TOOL_USE; + } + + @Override + public HasAction tryToMakeAction(Player player) { + if (hasFiguresFromPlayer(player.playerOrder())) { + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + return HasAction.NO_ACTION_POSSIBLE; + } + + @Override + public boolean skipAction(Player player) { + return false; // Can't skip resource gathering + } + + @Override + public boolean newTurn() { + figures.clear(); + return false; // Resource sources don't trigger game end + } + + private boolean canPlaceFigures(Player player, int figureCount) { + // Check if player has enough figures + if (!player.playerBoard().hasFigures(figureCount)) { + return false; + } + + // Check if space available + if (figures.size() + figureCount > maxFigures) { + return false; + } + + // Check if player already has figures here + if (hasFiguresFromPlayer(player.playerOrder())) { + return false; + } + + // Check number of different players + if (!figures.isEmpty() && !containsPlayerOrder(figures, player.playerOrder())) { + int currentColors = countDistinctPlayers(); + if (currentColors >= maxFigureColors) { + return false; + } + } + + return true; + } + + private boolean hasFiguresFromPlayer(PlayerOrder player) { + return containsPlayerOrder(figures, player); + } + + private int countPlayerFigures(PlayerOrder player) { + int count = 0; + for (PlayerOrder p : figures) { + if (p.equals(player)) { + count++; + } + } + return count; + } + + private int countDistinctPlayers() { + return (int) figures.stream().distinct().count(); + } + + private boolean containsPlayerOrder(Collection collection, PlayerOrder player) { + return collection.stream().anyMatch(p -> p.equals(player)); + } + + public String state() { + Map state = Map.of( + "name", name, + "resource", resource, + "maxFigures", maxFigures, + "maxFigureColors", maxFigureColors, + "figures", figures.stream().map(PlayerOrder::getOrder).toList() + ); + return new JSONObject(state).toString(); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/RewardMenu.java b/src/main/java/sk/uniba/fmph/dcs/game_board/RewardMenu.java new file mode 100644 index 00000000..c1aad915 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/RewardMenu.java @@ -0,0 +1,125 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * The {@code RewardMenu} class manages the reward distribution process in the game. It allows players + * to claim rewards from a menu based on their turn order and checks the availability of rewards + * before granting them. + * + *

This class implements the {@link InterfaceTakeReward} interface to handle reward-related actions + * and supports automatic or manual claiming of rewards depending on the context. + */ +public class RewardMenu implements InterfaceTakeReward { + + private ArrayList rewards; + private Player[] players; + private ArrayList remainingPlayers; + + /** + * Constructs a new {@code RewardMenu} with the specified players. It initializes the list of + * remaining players eligible to claim rewards in turn order. + * + * @param players an array of players participating in the reward menu + */ + public RewardMenu(Player[] players) { + this.players = players; + this.remainingPlayers = new ArrayList<>(); + for (Player player : players) { + remainingPlayers.add(player.playerOrder()); + } + } + + /** + * Initializes the reward menu with the specified list of effects (rewards). + * + * @param menu an array of {@link Effect} objects representing the available rewards + */ + public void initiate(Effect[] menu) { + this.rewards.addAll(List.of(menu)); + } + + /** + * Allows a player to claim a reward from the menu, provided the reward is available and the player + * is eligible to claim it. + * + * @param player the {@link PlayerOrder} of the player attempting to take a reward + * @param reward the {@link Effect} representing the reward to be claimed + * @return {@code true} if the reward was successfully claimed; {@code false} otherwise + */ + @Override + public boolean takeReward(PlayerOrder player, Effect reward) { + if (!rewards.contains(reward)) { + return false; + } + if (!remainingPlayers.contains(player)) { + return false; + } + for (Player p : players) { + if (p.playerOrder().equals(player)) { + remainingPlayers.remove(player); + rewards.remove(reward); + p.playerBoard().giveEffect(List.of(reward)); + return true; + } + } + return false; + } + + /** + * Determines the possible action for the specified player with respect to the reward menu. + * + * @param player the {@link PlayerOrder} of the player attempting to take an action + * @return a {@link HasAction} indicating the action's feasibility: + *

    + *
  • {@code NO_ACTION_POSSIBLE} if no action can be performed
  • + *
  • {@code AUTOMATIC_ACTION_DONE} if an automatic action was taken
  • + *
  • {@code WAITING_FOR_PLAYER_ACTION} if the player must take an action manually
  • + *
+ */ + @Override + public HasAction tryMakeAction(PlayerOrder player) { + if (rewards.isEmpty()) { + return HasAction.NO_ACTION_POSSIBLE; + } + if (!remainingPlayers.contains(player)) { + return HasAction.NO_ACTION_POSSIBLE; + } + if (rewards.size() == 1) { + for (Player p : players) { + if (p.playerOrder().equals(player)) { + p.playerBoard().giveEffect(rewards); + remainingPlayers.remove(player); + return HasAction.AUTOMATIC_ACTION_DONE; + } + } + } + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + /** + * Returns a JSON string representing the current state of the reward menu. + * + *

The JSON object contains the following fields: + *

    + *
  • {@code "rewards"}: the list of available rewards
  • + *
  • {@code "players"}: the array of players in the reward menu
  • + *
  • {@code "remainingPlayers"}: the list of players still eligible to claim rewards
  • + *
+ * + * @return a JSON string representation of the reward menu state + */ + public String State() { + Map state = Map.of( + "rewards", rewards, + "players", players, + "remainingPlayers", remainingPlayers + ); + return new JSONObject(state).toString(); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/SimpleBuilding.java b/src/main/java/sk/uniba/fmph/dcs/game_board/SimpleBuilding.java index 5d4c9d36..317f27d7 100644 --- a/src/main/java/sk/uniba/fmph/dcs/game_board/SimpleBuilding.java +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/SimpleBuilding.java @@ -32,4 +32,9 @@ public OptionalInt build(final Collection resources) { } return OptionalInt.of(sum); } + + @Override + public String state() { + return ""; + } } diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/Throw.java b/src/main/java/sk/uniba/fmph/dcs/game_board/Throw.java new file mode 100644 index 00000000..2bdbf94d --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/Throw.java @@ -0,0 +1,16 @@ +package sk.uniba.fmph.dcs.game_board; + +import java.util.Random; + +public class Throw { + + // I can't use the method name "throw" because it causes a syntax error. + public static int[] throw_(int dices){ + int[] result = new int[dices]; + Random rand = new Random(); + for (int i = 0; i < dices; i++) { + result[i] = rand.nextInt(6)+1; + } + return result; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFields.java b/src/main/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFields.java new file mode 100644 index 00000000..cf679962 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFields.java @@ -0,0 +1,126 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.ArrayList; +import java.util.Map; + +public class ToolMakerHutFields{ + private PlayerOrder toolMakerFigures; + private PlayerOrder hutFigures; + private PlayerOrder fieldsFigures; + + private final int restriction; + + + public ToolMakerHutFields(int numberOfPlayers){ + if(numberOfPlayers<4){ + this.restriction = 2; + }else{ + this.restriction = 3; + } + } + + private boolean checkRestriction(){ + int result = 0; + if(toolMakerFigures != null) { + result++; + } + if(fieldsFigures != null){ + result++; + } + if(hutFigures != null){ + result++; + } + + return result < restriction; + } + + + + public boolean placeOnToolMaker(Player player){ + if(!canPlaceOnToolMaker(player)){ + return false; + } + + toolMakerFigures = player.playerOrder(); + return true; + } + + public boolean actionToolMaker(Player player){ + if(!player.playerOrder().equals(toolMakerFigures)){ + return false; + } + ArrayList list = new ArrayList<>(); + list.add(Effect.TOOL); + player.playerBoard().giveEffect(list); + return true; + } + // + public boolean canPlaceOnToolMaker(Player player){ + return toolMakerFigures == null && checkRestriction(); + } + + public boolean placeOnHut(Player player){ + if(!canPlaceOnHut(player)){ + return false; + } + + hutFigures = player.playerOrder(); + return true; + } + + public boolean canPlaceOnHut(Player player){ + return hutFigures == null && checkRestriction(); + } + + public boolean actionHut(Player player){ + if(!player.playerOrder().equals(hutFigures)){ + return false; + } + + player.playerBoard().giveFigure(); + return true; + } + + public boolean placeOnFields(Player player){ + if(!canPlaceOnFields(player)){ + return false; + } + + fieldsFigures = player.playerOrder(); + return true; + + } + + public boolean actionFields(Player player){ + if(!player.playerOrder().equals(fieldsFigures)){ + return false; + } + ArrayList list = new ArrayList<>(); + list.add(Effect.FIELD); + player.playerBoard().giveEffect(list); + return true; + } + + public boolean canPlaceOnFields(Player player){ + return fieldsFigures == null && checkRestriction(); + } + public boolean newTurn(){ + toolMakerFigures = null; + hutFigures = null; + fieldsFigures = null; + return true; + } + + public String state(){ + Map state = Map.of( + "toolMakerFigures", toolMakerFigures.toString(), + "hutFigures", hutFigures.toString(), + "fieldsFigures", fieldsFigures.toString()); + return new JSONObject(state).toString(); + } + + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_board/VariableBuilding.java b/src/main/java/sk/uniba/fmph/dcs/game_board/VariableBuilding.java new file mode 100644 index 00000000..b2870c91 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_board/VariableBuilding.java @@ -0,0 +1,50 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; +import java.util.Collection; +import java.util.OptionalInt; +import java.util.HashSet; + +public class VariableBuilding implements Building { + private final int numberOfResources; + private final int numberOfResourceTypes; + + public VariableBuilding(int numberOfResources, int numberOfResourceTypes) { + this.numberOfResources = numberOfResources; + this.numberOfResourceTypes = numberOfResourceTypes; + } + + @Override + public OptionalInt build(Collection resources) { + if (resources.size() != numberOfResources) { + return OptionalInt.empty(); + } + + // Check all resources are valid + for (Effect resource : resources) { + if (!resource.isResource()) { + return OptionalInt.empty(); + } + } + + // Count unique resource types + HashSet uniqueResources = new HashSet<>(resources); + if (uniqueResources.size() != numberOfResourceTypes) { + return OptionalInt.empty(); + } + + // Calculate points + int points = 0; + for (Effect resource : resources) { + points += resource.points(); + } + + return OptionalInt.of(points); + } + + @Override + public String state() { + return String.format("VariableBuilding[resources=%d,types=%d]", + numberOfResources, numberOfResourceTypes); + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/AllPlayersTakeARewardState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/AllPlayersTakeARewardState.java new file mode 100644 index 00000000..eef74970 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/AllPlayersTakeARewardState.java @@ -0,0 +1,67 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import org.apache.commons.lang3.tuple.Pair; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.Collection; + +public class AllPlayersTakeARewardState implements InterfaceGamePhaseState{ + + InterfaceTakeReward reward; + + + @Override + public ActionResult placeFigures(PlayerOrder player, Location location, int figuresCount) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAction(PlayerOrder player, Location location, Collection inputResources, Collection outputResources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult skipAction(PlayerOrder player, Location location) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult useTools(PlayerOrder player, int toolIndex) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult noMoreToolsThisThrow(PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult feedTribe(PlayerOrder player, Collection resources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult doNotFeedThisTurn(PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAllPlayersTakeARewardChoice(PlayerOrder player, Effect reward) { + return this.reward.takeReward(player, reward) + ? ActionResult.ACTION_DONE + : ActionResult.FAILURE; + } + + @Override + public HasAction tryToMakeAutomaticAction(PlayerOrder player) { + if(reward.playerHasAllRewards(player)) return HasAction.NO_ACTION_POSSIBLE; + Pair res = reward.playerLastReward(); + if (res != null) { + PlayerOrder playerLast = res.getLeft(); + Effect lastEffect = res.getRight(); + reward.takeReward(playerLast, lastEffect); + return HasAction.AUTOMATIC_ACTION_DONE; + } + return HasAction.WAITING_FOR_PLAYER_ACTION; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/FeedTribeState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/FeedTribeState.java new file mode 100644 index 00000000..d8c72abe --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/FeedTribeState.java @@ -0,0 +1,79 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import sk.uniba.fmph.dcs.stone_age.ActionResult; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.HasAction; +import sk.uniba.fmph.dcs.stone_age.Location; +import sk.uniba.fmph.dcs.stone_age.PlayerOrder; +import sk.uniba.fmph.dcs.stone_age.InterfaceFeedTribe; + +import java.util.Collection; +import java.util.Map; + +public final class FeedTribeState implements InterfaceGamePhaseState { + + private final Map playerTribes; + + public FeedTribeState(Map playerTribes) { + this.playerTribes = playerTribes; + } + + @Override + public ActionResult placeFigures(final PlayerOrder player, final Location location, final int figuresCount) { + return ActionResult.FAILURE; + } + @Override + public ActionResult makeAction(final PlayerOrder player, final Location location, + final Collection inputResources, final Collection outputResources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult skipAction(final PlayerOrder player, final Location location) { + return ActionResult.FAILURE; + + } + + @Override + public ActionResult useTools(final PlayerOrder player, final int toolIndex) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult noMoreToolsThisThrow(final PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult feedTribe(final PlayerOrder player, final Collection resources) { + if (playerTribes.get(player).feedTribe(resources)) { + return ActionResult.ACTION_DONE; + } + return ActionResult.FAILURE; + } + + @Override + public ActionResult doNotFeedThisTurn(final PlayerOrder player) { + if (playerTribes.get(player).doNotFeedThisTurn()) { + return ActionResult.ACTION_DONE; + } + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAllPlayersTakeARewardChoice(final PlayerOrder player, final Effect reward) { + return ActionResult.FAILURE; + } + + @Override + public HasAction tryToMakeAutomaticAction(final PlayerOrder player) { + if (playerTribes.get(player).isTribeFed()) { + return HasAction.NO_ACTION_POSSIBLE; + } + if (playerTribes.get(player).feedTribeIfEnoughFood()) { + return HasAction.AUTOMATIC_ACTION_DONE; + } + + return HasAction.WAITING_FOR_PLAYER_ACTION; + } +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/GamePhaseController.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/GamePhaseController.java index cf94b721..f8e4bb8c 100644 --- a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/GamePhaseController.java +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/GamePhaseController.java @@ -21,7 +21,7 @@ public final class GamePhaseController implements InterfaceGamePhaseController { private GamePhase gamePhase; public GamePhaseController(final Map dispatchers, - final PlayerOrder startingPlayer) { + final PlayerOrder startingPlayer) { this.roundStartingPlayer = startingPlayer; this.currentPlayer = startingPlayer; this.currentPlayerTakingReward = Optional.empty(); @@ -31,89 +31,89 @@ public GamePhaseController(final Map dispatc private boolean checkPlayersTurn(final PlayerOrder player) { switch (gamePhase) { - case PLACE_FIGURES: - case MAKE_ACTION: - case WAITING_FOR_TOOL_USE: - return player.equals(currentPlayer); - case ALL_PLAYERS_TAKE_A_REWARD: - return currentPlayerTakingReward.isPresent() && player.equals(currentPlayerTakingReward.get()); - case FEED_TRIBE: - return true; - case NEW_ROUND: - return false; - case GAME_END: - throw new AssertionError(); - default: - return false; + case PLACE_FIGURES: + case MAKE_ACTION: + case WAITING_FOR_TOOL_USE: + return player.equals(currentPlayer); + case ALL_PLAYERS_TAKE_A_REWARD: + return currentPlayerTakingReward.isPresent() && player.equals(currentPlayerTakingReward.get()); + case FEED_TRIBE: + return true; + case NEW_ROUND: + return false; + case GAME_END: + throw new AssertionError(); + default: + return false; } } private void progressStateAfterSuccessfulAction() { switch (gamePhase) { - case PLACE_FIGURES: - case FEED_TRIBE: - currentPlayer = currentPlayer.forward(); - break; - case MAKE_ACTION: - case WAITING_FOR_TOOL_USE: - break; - case ALL_PLAYERS_TAKE_A_REWARD: - if (currentPlayerTakingReward.isPresent()) { - currentPlayerTakingReward = Optional.of(currentPlayerTakingReward.get().forward()); - } - break; - case NEW_ROUND: - gamePhase = GamePhase.PLACE_FIGURES; - roundStartingPlayer = roundStartingPlayer.forward(); - currentPlayer = roundStartingPlayer; - break; - case GAME_END: - default: - throw new AssertionError(); + case PLACE_FIGURES: + case FEED_TRIBE: + currentPlayer = currentPlayer.forward(); + break; + case MAKE_ACTION: + case WAITING_FOR_TOOL_USE: + break; + case ALL_PLAYERS_TAKE_A_REWARD: + if (currentPlayerTakingReward.isPresent()) { + currentPlayerTakingReward = Optional.of(currentPlayerTakingReward.get().forward()); + } + break; + case NEW_ROUND: + gamePhase = GamePhase.PLACE_FIGURES; + roundStartingPlayer = roundStartingPlayer.forward(); + currentPlayer = roundStartingPlayer; + break; + case GAME_END: + default: + throw new AssertionError(); } } private void progressStateAfterNoActionPossible() { switch (gamePhase) { - case PLACE_FIGURES: - case FEED_TRIBE: - case MAKE_ACTION: - currentPlayer = currentPlayer.forward(); - break; - case ALL_PLAYERS_TAKE_A_REWARD: - case WAITING_FOR_TOOL_USE: - currentPlayerTakingReward = Optional.empty(); - gamePhase = GamePhase.MAKE_ACTION; - break; - case NEW_ROUND: - gamePhase = GamePhase.GAME_END; - break; - case GAME_END: - default: - throw new AssertionError(); + case PLACE_FIGURES: + case FEED_TRIBE: + case MAKE_ACTION: + currentPlayer = currentPlayer.forward(); + break; + case ALL_PLAYERS_TAKE_A_REWARD: + case WAITING_FOR_TOOL_USE: + currentPlayerTakingReward = Optional.empty(); + gamePhase = GamePhase.MAKE_ACTION; + break; + case NEW_ROUND: + gamePhase = GamePhase.GAME_END; + break; + case GAME_END: + default: + throw new AssertionError(); } } private void progressStateAfterNoActionPossibleByAnyPlayer() { switch (gamePhase) { - case PLACE_FIGURES: - currentPlayer = roundStartingPlayer; - gamePhase = GamePhase.MAKE_ACTION; - break; - case MAKE_ACTION: - currentPlayer = roundStartingPlayer; - gamePhase = GamePhase.FEED_TRIBE; - break; - case FEED_TRIBE: - currentPlayer = roundStartingPlayer; - gamePhase = GamePhase.NEW_ROUND; - break; - case NEW_ROUND: - case WAITING_FOR_TOOL_USE: - case ALL_PLAYERS_TAKE_A_REWARD: - case GAME_END: - default: - throw new AssertionError(); + case PLACE_FIGURES: + currentPlayer = roundStartingPlayer; + gamePhase = GamePhase.MAKE_ACTION; + break; + case MAKE_ACTION: + currentPlayer = roundStartingPlayer; + gamePhase = GamePhase.FEED_TRIBE; + break; + case FEED_TRIBE: + currentPlayer = roundStartingPlayer; + gamePhase = GamePhase.NEW_ROUND; + break; + case NEW_ROUND: + case WAITING_FOR_TOOL_USE: + case ALL_PLAYERS_TAKE_A_REWARD: + case GAME_END: + default: + throw new AssertionError(); } } @@ -150,21 +150,21 @@ private void tryToDoFurtherActions() { HasAction actionResult = dispatcher.tryToMakeAutomaticAction(player); switch (actionResult) { - case WAITING_FOR_PLAYER_ACTION: - return; - case AUTOMATIC_ACTION_DONE: - firstUnsuccessfulPlayer = null; - progressStateAfterSuccessfulAction(); - break; - case NO_ACTION_POSSIBLE: - if (firstUnsuccessfulPlayer == null) { - firstUnsuccessfulPlayer = player; - unsuccessfulGamePhase = gamePhase; - } - progressStateAfterNoActionPossible(); - continue; - default: - throw new AssertionError(); + case WAITING_FOR_PLAYER_ACTION: + return; + case AUTOMATIC_ACTION_DONE: + firstUnsuccessfulPlayer = null; + progressStateAfterSuccessfulAction(); + break; + case NO_ACTION_POSSIBLE: + if (firstUnsuccessfulPlayer == null || unsuccessfulGamePhase != gamePhase) { + firstUnsuccessfulPlayer = player; + unsuccessfulGamePhase = gamePhase; + } + progressStateAfterNoActionPossible(); + continue; + default: + throw new AssertionError(); } } } @@ -177,19 +177,19 @@ public boolean placeFigures(final PlayerOrder player, final Location location, f ActionResult actionResult = dispatcher.placeFigures(player, location, figuresCount); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } public boolean makeAction(final PlayerOrder player, final Location location, - final Collection inputResources, final Collection outputResources) { + final Collection inputResources, final Collection outputResources) { if (!checkPlayersTurn(player)) { return false; } @@ -197,22 +197,22 @@ public boolean makeAction(final PlayerOrder player, final Location location, ActionResult actionResult = dispatcher.makeAction(player, location, inputResources, outputResources); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - case ACTION_DONE_WAIT_FOR_TOOL_USE: - progressStateToolUse(); - tryToDoFurtherActions(); - return true; - case ACTION_DONE_ALL_PLAYERS_TAKE_A_REWARD: - progressStateAllPlayersTakeAReward(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + case ACTION_DONE_WAIT_FOR_TOOL_USE: + progressStateToolUse(); + tryToDoFurtherActions(); + return true; + case ACTION_DONE_ALL_PLAYERS_TAKE_A_REWARD: + progressStateAllPlayersTakeAReward(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -224,14 +224,14 @@ public boolean skipAction(final PlayerOrder player, final Location location) { ActionResult actionResult = dispatcher.skipAction(player, location); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -243,14 +243,14 @@ public boolean useTools(final PlayerOrder player, final int toolIndex) { ActionResult actionResult = dispatcher.useTools(player, toolIndex); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -262,14 +262,14 @@ public boolean noMoreToolsThisThrow(final PlayerOrder player) { ActionResult actionResult = dispatcher.noMoreToolsThisThrow(player); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterNoActionPossible(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterNoActionPossible(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -281,14 +281,14 @@ public boolean feedTribe(final PlayerOrder player, final Collection reso ActionResult actionResult = dispatcher.feedTribe(player, resources); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -300,14 +300,14 @@ public boolean doNotFeedThisTurn(final PlayerOrder player) { ActionResult actionResult = dispatcher.doNotFeedThisTurn(player); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } @@ -319,14 +319,14 @@ public boolean makeAllPlayersTakeARewardChoice(final PlayerOrder player, final E ActionResult actionResult = dispatcher.makeAllPlayersTakeARewardChoice(player, reward); switch (actionResult) { - case FAILURE: - return false; - case ACTION_DONE: - progressStateAfterSuccessfulAction(); - tryToDoFurtherActions(); - return true; - default: - throw new AssertionError(); + case FAILURE: + return false; + case ACTION_DONE: + progressStateAfterSuccessfulAction(); + tryToDoFurtherActions(); + return true; + default: + throw new AssertionError(); } } diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/InterfaceTakeReward.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/InterfaceTakeReward.java new file mode 100644 index 00000000..d8698753 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/InterfaceTakeReward.java @@ -0,0 +1,12 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import org.apache.commons.lang3.tuple.Pair; +import sk.uniba.fmph.dcs.stone_age.*; +import sk.uniba.fmph.dcs.stone_age.PlayerOrder; + +public interface InterfaceTakeReward { + boolean takeReward(PlayerOrder player, Effect effect); + HasAction tryMakeAction(PlayerOrder player); + boolean playerHasAllRewards(PlayerOrder player); + Pair playerLastReward(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/MakeActionState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/MakeActionState.java new file mode 100644 index 00000000..85305638 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/MakeActionState.java @@ -0,0 +1,204 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.Collection; +import java.util.Map; + +/** + * The {@code MakeActionState} class represents the "Make Action" phase in the Stone Age game. + * During this phase, the current player performs actions with their placed figures on the board. + * This class implements the {@link InterfaceGamePhaseState} interface and provides the logic + * for handling player actions specific to this phase. + */ +public class MakeActionState implements InterfaceGamePhaseState { + + /** + * A map of all locations on the game board and their corresponding figure locations. + * This map is used to delegate actions to the appropriate location. + */ + private final Map places; + + /** + * The player who is currently taking their turn during the "Make Action" phase. + */ + private final PlayerOrder currentPlayer; + + /** + * Constructs a new {@code MakeActionState} with the specified places and current player. + * + * @param places a map of locations to their corresponding figure locations + * @param currentPlayer the player who is currently taking their turn + */ + public MakeActionState(Map places, PlayerOrder currentPlayer) { + this.places = places; + this.currentPlayer = currentPlayer; + } + + /** + * Players cannot place figures during the "Make Action" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param playerOrder the player attempting to place figures + * @param location the location where the player wants to place figures + * @param figuresCount the number of figures the player wants to place + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult placeFigures(PlayerOrder playerOrder, Location location, int figuresCount) { + // Players cannot place figures during the 'Make Action' phase + return ActionResult.FAILURE; + } + + /** + * Allows the current player to perform an action at a specified location. + * The action is delegated to the corresponding {@link InterfaceFigureLocation}. + * + * @param player the player attempting to make an action + * @param location the location where the action is to be made + * @param inputResources resources provided as input for the action + * @param outputResources resources expected as output from the action + * @return the result of the action as an {@link ActionResult} + */ + @Override + public ActionResult makeAction(PlayerOrder player, Location location, Collection inputResources, Collection outputResources) { + // Check if the player is the current player + if (!player.equals(currentPlayer)) { + return ActionResult.FAILURE; + } + + InterfaceFigureLocation figureLocation = places.get(location); + if (figureLocation == null) { + return ActionResult.FAILURE; + } + + // Attempt to make the action at the specified location + return figureLocation.makeAction(player, inputResources, outputResources); + } + + /** + * Allows the current player to skip an action at a specified location. + * The skip action is delegated to the corresponding {@link InterfaceFigureLocation}. + * + * @param player the player attempting to skip an action + * @param location the location where the action is to be skipped + * @return {@code ActionResult.ACTION_DONE} if the action was successfully skipped, + * {@code ActionResult.FAILURE} otherwise + */ + @Override + public ActionResult skipAction(PlayerOrder player, Location location) { + // Check if the player is the current player + if (!player.equals(currentPlayer)) { + return ActionResult.FAILURE; + } + + InterfaceFigureLocation figureLocation = places.get(location); + if (figureLocation == null) { + return ActionResult.FAILURE; + } + + boolean success = figureLocation.skipAction(player); + return success ? ActionResult.ACTION_DONE : ActionResult.FAILURE; + } + + /** + * Tool usage is not directly handled in this class during the "Make Action" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param playerOrder the player attempting to use a tool + * @param toolIndex the index of the tool being used + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult useTools(PlayerOrder playerOrder, int toolIndex) { + return ActionResult.FAILURE; + } + + /** + * Indicating that the player will not use more tools for the current throw. + * This method always returns {@link ActionResult#FAILURE} as tool usage is not handled here. + * + * @param playerOrder the player indicating no more tool usage + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult noMoreToolsThisThrow(PlayerOrder playerOrder) { + return ActionResult.FAILURE; + } + + /** + * Feeding the tribe is not handled during the "Make Action" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param playerOrder the player attempting to feed their tribe + * @param resources the resources being used to feed the tribe + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult feedTribe(PlayerOrder playerOrder, Collection resources) { + // Feeding the tribe happens during the FeedTribe phase + return ActionResult.FAILURE; + } + + /** + * Indicating that the player chooses not to feed their tribe this turn. + * This method always returns {@link ActionResult#FAILURE} as it's not applicable in this phase. + * + * @param playerOrder the player choosing not to feed their tribe + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult doNotFeedThisTurn(PlayerOrder playerOrder) { + // Feeding the tribe happens during the FeedTribe phase + return ActionResult.FAILURE; + } + + /** + * Making all players take a reward choice is not applicable during the "Make Action" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param playerOrder the player attempting to make a reward choice + * @param reward the reward being chosen + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult makeAllPlayersTakeARewardChoice(PlayerOrder playerOrder, Effect reward) { + // Not applicable during the 'Make Action' phase + return ActionResult.FAILURE; + } + + /** + * Attempts to perform any automatic actions for the current player during the "Make Action" phase. + * Checks if the player has any actions to perform and returns the appropriate {@link HasAction} status. + * + * @param player the player attempting to perform automatic actions + * @return {@code HasAction.WAITING_FOR_PLAYER_ACTION} if the player has actions to perform, + * {@code HasAction.NO_ACTION_POSSIBLE} if no actions are possible, + * {@code HasAction.AUTOMATIC_ACTION_DONE} if an automatic action was performed + */ + @Override + public HasAction tryToMakeAutomaticAction(PlayerOrder player) { + if (!player.equals(currentPlayer)) { + return HasAction.NO_ACTION_POSSIBLE; + } + + boolean hasWaitingActions = false; + + for (InterfaceFigureLocation figureLocation : places.values()) { + HasAction hasAction = figureLocation.tryToMakeAction(player); + if (hasAction == HasAction.WAITING_FOR_PLAYER_ACTION) { + hasWaitingActions = true; + break; + } + if (hasAction == HasAction.AUTOMATIC_ACTION_DONE) { + return HasAction.AUTOMATIC_ACTION_DONE; + } + } + + if (hasWaitingActions) { + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + + return HasAction.NO_ACTION_POSSIBLE; + } +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/NewRoundState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/NewRoundState.java new file mode 100644 index 00000000..11aa0084 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/NewRoundState.java @@ -0,0 +1,175 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.Collection; + +/** + * The {@code NewRoundState} class represents the "New Round" phase in the Stone Age game. + * During this phase, the game prepares for a new round by resetting locations and checking + * for end-game conditions. This class implements the {@link InterfaceGamePhaseState} interface + * and provides the logic for handling the automatic transition to the next phase. + */ +public class NewRoundState implements InterfaceGamePhaseState { + + /** + * An interface that handles the initialization of a new turn. + * It provides the {@code newTurn()} method to reset the game state for the new round. + */ + private final InterfaceNewTurn places; + + /** + * A flag indicating whether the new round has been initialized. + * This ensures that the initialization occurs only once per round. + */ + private boolean roundInitialized = false; + + /** + * Constructs a new {@code NewRoundState} with the specified {@code InterfaceNewTurn} instance. + * + * @param places an instance of {@link InterfaceNewTurn} used to initialize the new round + */ + public NewRoundState(InterfaceNewTurn places) { + this.places = places; + this.roundInitialized = false; + } + + /** + * Players cannot place figures during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to place figures + * @param location the location where the player wants to place figures + * @param figuresCount the number of figures the player wants to place + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult placeFigures(PlayerOrder player, Location location, int figuresCount) { + // Players cannot place figures during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Players cannot perform actions during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to make an action + * @param location the location where the action is to be made + * @param inputResources resources provided as input for the action + * @param outputResources resources expected as output from the action + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult makeAction(PlayerOrder player, Location location, Collection inputResources, + Collection outputResources) { + // Players cannot make actions during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Skipping actions is not applicable during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to skip an action + * @param location the location where the action is to be skipped + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult skipAction(PlayerOrder player, Location location) { + // No actions to skip during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Tool usage is not applicable during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to use a tool + * @param toolIndex the index of the tool being used + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult useTools(PlayerOrder player, int toolIndex) { + // Tool usage is not applicable during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Indicating that the player will not use more tools for the current throw is not applicable. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player indicating no more tool usage + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult noMoreToolsThisThrow(PlayerOrder player) { + // Not applicable during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Feeding the tribe is not handled during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to feed their tribe + * @param resources the resources being used to feed the tribe + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult feedTribe(PlayerOrder player, Collection resources) { + // Feeding the tribe happens during the 'Feed Tribe' phase + return ActionResult.FAILURE; + } + + /** + * Indicating that the player chooses not to feed their tribe this turn is not applicable. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player choosing not to feed their tribe + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult doNotFeedThisTurn(PlayerOrder player) { + // Not applicable during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Making all players take a reward choice is not applicable during the "New Round" phase. + * This method always returns {@link ActionResult#FAILURE}. + * + * @param player the player attempting to make a reward choice + * @param reward the reward being chosen + * @return {@code ActionResult.FAILURE} indicating the action is not allowed + */ + @Override + public ActionResult makeAllPlayersTakeARewardChoice(PlayerOrder player, Effect reward) { + // Not applicable during the 'New Round' phase + return ActionResult.FAILURE; + } + + /** + * Attempts to perform any automatic actions required during the "New Round" phase. + * This method initializes the new round by calling {@code places.newTurn()} and + * checks if the game has ended. It ensures that the initialization occurs only once. + * + * @param player the player for whom the automatic action is being attempted + * @return {@code HasAction.AUTOMATIC_ACTION_DONE} if the new round was initialized, + * {@code HasAction.NO_ACTION_POSSIBLE} if the game has ended or initialization already occurred + */ + @Override + public HasAction tryToMakeAutomaticAction(PlayerOrder player) { + if (!roundInitialized) { + boolean gameEnded = places.newTurn(); + roundInitialized = true; + if (gameEnded) { + return HasAction.NO_ACTION_POSSIBLE; + } else { + return HasAction.AUTOMATIC_ACTION_DONE; + } + } else { + // Round has already been initialized + return HasAction.NO_ACTION_POSSIBLE; + } + } +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresState.java new file mode 100644 index 00000000..32c64221 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresState.java @@ -0,0 +1,107 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import java.util.Collection; +import java.util.Map; + +import sk.uniba.fmph.dcs.stone_age.*; + +/** + * Represents the game phase where players place their figures on locations. + * Only figure placement actions are valid during this phase. + * + * Note: This class does not check if it's the player's turn - that validation + * is handled by GamePhaseController before any actions are delegated here. + * This separation of concerns keeps turn management logic in one place. + */ +public class PlaceFiguresState implements InterfaceGamePhaseState { + + /** + * Maps game locations to their corresponding figure placement handlers + */ + private final Map places; + + /** + * Creates a new PlaceFiguresState with the given location mappings + * + * @param places Map of locations to their figure placement handlers + */ + public PlaceFiguresState(Map places) { + this.places = places; + } + + /** + * Attempts to place figures at the specified location + * + * @param player The player attempting to place figures + * @param location The location where figures should be placed + * @param figuresCount Number of figures to place + * @return ACTION_DONE if placement successful, FAILURE otherwise + */ + @Override + public ActionResult placeFigures(PlayerOrder player, Location location, int figuresCount) { + InterfaceFigureLocation place = places.get(location); + if (place == null) { + return ActionResult.FAILURE; + } + + return place.placeFigures(player, figuresCount) + ? ActionResult.ACTION_DONE + : ActionResult.FAILURE; + } + + /** + * Checks if the player can make any automatic figure placements + * + * @param player The player to check for possible actions + * @return WAITING_FOR_PLAYER_ACTION if player has figures to place, + * NO_ACTION_POSSIBLE if no figures can be placed + */ + @Override + public HasAction tryToMakeAutomaticAction(PlayerOrder player) { + for (InterfaceFigureLocation place : places.values()) { + HasAction result = place.tryToPlaceFigures(player, 1); + if (result == HasAction.WAITING_FOR_PLAYER_ACTION) { + return HasAction.WAITING_FOR_PLAYER_ACTION; + } + } + return HasAction.NO_ACTION_POSSIBLE; + } + + // The following methods return FAILURE as they are not valid actions during the Place Figures phase + + @Override + public ActionResult makeAction(PlayerOrder player, Location location, + Collection inputResources, Collection outputResources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult skipAction(PlayerOrder player, Location location) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult useTools(PlayerOrder player, int toolIndex) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult noMoreToolsThisThrow(PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult feedTribe(PlayerOrder player, Collection resources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult doNotFeedThisTurn(PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAllPlayersTakeARewardChoice(PlayerOrder player, Effect reward) { + return ActionResult.FAILURE; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/WaitingForToolUseState.java b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/WaitingForToolUseState.java new file mode 100644 index 00000000..089b37eb --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/game_phase_controller/WaitingForToolUseState.java @@ -0,0 +1,71 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.Collection; + +public class WaitingForToolUseState implements InterfaceGamePhaseState { + + private InterfaceToolUse toolUse; + private PlayerOrder currentPlayer; + + WaitingForToolUseState(PlayerOrder player) { + currentPlayer = player; + } + + @Override + public ActionResult placeFigures(PlayerOrder player, Location location, int figuresCount) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAction(PlayerOrder player, Location location, Collection inputResources, Collection outputResources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult skipAction(PlayerOrder player, Location location) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult useTools(PlayerOrder player, int toolIndex) { + if(player.equals(currentPlayer)) { + if(toolUse.canUseTools()){ + toolUse.useTool(toolIndex); //nothing stops player from using same tool several times + return ActionResult.ACTION_DONE; + } + } + return ActionResult.FAILURE; + } + + @Override + public ActionResult noMoreToolsThisThrow(PlayerOrder player) { + toolUse.finishUsingTools(); + return ActionResult.ACTION_DONE; //ACTION_DONE_ALL_PLAYERS_TAKE_A_REWARD? + } + + @Override + public ActionResult feedTribe(PlayerOrder player, Collection resources) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult doNotFeedThisTurn(PlayerOrder player) { + return ActionResult.FAILURE; + } + + @Override + public ActionResult makeAllPlayersTakeARewardChoice(PlayerOrder player, Effect reward) { + return ActionResult.FAILURE; + } + + @Override + public HasAction tryToMakeAutomaticAction(PlayerOrder player) { + if (player.equals(currentPlayer)) { //??? + if(toolUse.canUseTools()) return HasAction.WAITING_FOR_PLAYER_ACTION; + return HasAction.NO_ACTION_POSSIBLE; + } + return HasAction.NO_ACTION_POSSIBLE; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/EndOfGameEffect.java b/src/main/java/sk/uniba/fmph/dcs/player_board/EndOfGameEffect.java new file mode 100644 index 00000000..2247db5a --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/EndOfGameEffect.java @@ -0,0 +1,17 @@ +package sk.uniba.fmph.dcs.player_board; + +public enum EndOfGameEffect { + Farmer, + ToolMaker, + Builder, + Shaman, + Medicine, + Art, + Music, + Writing, + Sundial, + Pottery, + Transport, + Weaving + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerBoard.java b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerBoard.java new file mode 100644 index 00000000..86c844c2 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerBoard.java @@ -0,0 +1,31 @@ +package sk.uniba.fmph.dcs.player_board; + +import java.util.*; + +public class PlayerBoard { + private int points; + private int houses; + private final int playerFigures; + + public PlayerBoard() { + this.points = 0; + this.houses = 0; + this.playerFigures = 5; + } + + public void addPoints(int points) { + this.points += points; + } + + public void addHouse() { + + } + + public void addEndGamePoints() { + + } + + public String state() { + return ""; + } +} diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCards.java b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCards.java new file mode 100644 index 00000000..be8ce604 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCards.java @@ -0,0 +1,63 @@ +package sk.uniba.fmph.dcs.player_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.InterfaceGetState; + +import java.util.HashMap; +import java.util.Map; +public class PlayerCivilizationCards implements InterfaceGetState { + private final Map endOfGameEffectMap = new HashMap<>(); + + public void addEndOfGameEffects(EndOfGameEffect[] effects){ + for (EndOfGameEffect effect : effects) { + if (endOfGameEffectMap.containsKey(effect)) { + endOfGameEffectMap.put(effect, endOfGameEffectMap.get(effect) + 1); + } else { + endOfGameEffectMap.put(effect, 1); + } + } + } + + public int calculateEndOfGameCivilizationCardPoints(int buildings, int tools, int fields, int figures){ + int sumOfEndOfGameEffects = 0; + Map greenCards = new HashMap<>(); + for(EndOfGameEffect effect: endOfGameEffectMap.keySet()){ + switch (effect){ + case Farmer -> sumOfEndOfGameEffects += fields * endOfGameEffectMap.get(effect); + case ToolMaker -> sumOfEndOfGameEffects += tools * endOfGameEffectMap.get(effect); + case Builder -> sumOfEndOfGameEffects += buildings * endOfGameEffectMap.get(effect); + case Shaman -> sumOfEndOfGameEffects += figures * endOfGameEffectMap.get(effect); + default -> greenCards.put(effect, endOfGameEffectMap.get(effect)); + } + } + + while (true){ + int countCurrentCards = 0; + for(EndOfGameEffect effect: greenCards.keySet()){ + int numberOfCards = greenCards.get(effect); + if(numberOfCards > 0){ + countCurrentCards++; + greenCards.put(effect, numberOfCards - 1); + } + } + if(countCurrentCards == 0){ + break; + } + int toCheck = (int) Math.pow(countCurrentCards, 2); + sumOfEndOfGameEffects += toCheck; + } + + return sumOfEndOfGameEffects; + } + + + @Override + public String state() { + Map state = Map.of( + "endOfGameEffectMap", endOfGameEffectMap + ); + + return new JSONObject(state).toString(); + } + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerFigures.java b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerFigures.java new file mode 100644 index 00000000..d62afd00 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerFigures.java @@ -0,0 +1,49 @@ +package sk.uniba.fmph.dcs.player_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.InterfaceGetState; + +import java.util.Map; + +public class PlayerFigures implements InterfaceGetState { +private int totalFigures; +private int figures; + + public void addNewFigure(){ + if(totalFigures < 10){ + totalFigures++; + } + } + + public boolean hasFigures(int count){ + return figures >= count; + } + + public int getTotalFigures() { + return totalFigures; + } + + public boolean takeFigures(int count){ + if(hasFigures(count)){ + figures = figures - count; + return true; + }else{ + return false; + } + } + + public void newTurn (){ + figures = totalFigures; + } + + @Override + public String state(){ + Map state = Map.of( + "figures", figures, + "totalFigures", totalFigures + ); + + return new JSONObject(state).toString(); + } + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerResourcesAndFood.java b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerResourcesAndFood.java new file mode 100644 index 00000000..e0e7306d --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerResourcesAndFood.java @@ -0,0 +1,132 @@ +package sk.uniba.fmph.dcs.player_board; + +import sk.uniba.fmph.dcs.stone_age.Effect; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * The {@code PlayerResourcesAndFood} class represents a player's resources and food in the Stone Age game. + * It manages the player's inventory by allowing the addition and removal of resources, + * checking resource availability, and calculating resources for final scoring. + */ +public class PlayerResourcesAndFood { + /** + * A map that keeps track of the quantity of each type of resource the player has. + * The key is an {@link Effect} representing the resource type, and the value is the quantity. + */ + private final Map resources; + + /** + * Constructs a new {@code PlayerResourcesAndFood} instance and initializes the player's resources. + * The player starts with a default amount of food and zero of other resources. + */ + public PlayerResourcesAndFood() { + this.resources = new HashMap<>(); + initializeResources(); + } + + /** + * Initializes the player's starting resources. + * By default, the player starts with 12 food tokens and zero of all other resources. + */ + private void initializeResources() { + resources.put(Effect.FOOD, 12); // Players start with 12 food tokens + resources.put(Effect.WOOD, 0); + resources.put(Effect.CLAY, 0); + resources.put(Effect.STONE, 0); + resources.put(Effect.GOLD, 0); + } + + /** + * Checks if the player has at least the required amount of each resource specified in the collection. + * + * @param requiredResources a collection of {@link Effect} representing the required resources. + * @return {@code true} if the player has enough of each required resource; {@code false} otherwise. + */ + public boolean hasResources(Collection requiredResources) { + Map requiredCounts = new HashMap<>(); + // Count the number of each required resource + for (Effect resource : requiredResources) { + requiredCounts.put(resource, requiredCounts.getOrDefault(resource, 0) + 1); + } + // Check if the player has enough of each required resource + for (Map.Entry entry : requiredCounts.entrySet()) { + Effect resource = entry.getKey(); + int requiredAmount = entry.getValue(); + int availableAmount = resources.getOrDefault(resource, 0); + if (availableAmount < requiredAmount) { + return false; + } + } + return true; + } + + /** + * Removes the specified resources from the player's inventory if the player has enough. + * If the player lacks any of the required resources, no resources are removed. + * + * @param resourcesToTake a collection of {@link Effect} representing the resources to remove. + * @return {@code true} if the resources were successfully removed; {@code false} if the player did not have enough resources. + */ + public boolean takeResources(Collection resourcesToTake) { + if (!hasResources(resourcesToTake)) { + return false; + } + // Remove each resource from the player's inventory + for (Effect resource : resourcesToTake) { + resources.put(resource, resources.get(resource) - 1); + } + return true; + } + + /** + * Adds the specified resources to the player's inventory. + * + * @param resourcesToAdd a collection of {@link Effect} representing the resources to add. + */ + public void giveResources(Collection resourcesToAdd) { + for (Effect resource : resourcesToAdd) { + resources.put(resource, resources.getOrDefault(resource, 0) + 1); + } + } + + /** + * Calculates the total number of non-food resources the player has for final scoring. + * In the Stone Age game, resources other than food contribute to the player's score at the end of the game. + * + * @return the total number of non-food resources. + */ + public int numberOfResourcesForFinalPoints() { + int total = 0; + for (Map.Entry entry : resources.entrySet()) { + Effect resource = entry.getKey(); + if (resource != Effect.FOOD) { + total += entry.getValue(); + } + } + return total; + } + + /** + * Returns a string representation of the player's current resources. + * The string includes each resource type and the quantity the player has. + * + * @return a string representing the player's resources, formatted as "RESOURCE: quantity, ...". + */ + public String state() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : resources.entrySet()) { + sb.append(entry.getKey().toString()) + .append(": ") + .append(entry.getValue()) + .append(", "); + } + // Remove trailing comma and space if necessary + if (sb.length() > 0) { + sb.setLength(sb.length() - 2); // Remove trailing ", " + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerTools.java b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerTools.java new file mode 100644 index 00000000..601bc795 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/player_board/PlayerTools.java @@ -0,0 +1,72 @@ +package sk.uniba.fmph.dcs.player_board; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.stone_age.InterfaceGetState; + +import java.util.*; + +public class PlayerTools implements InterfaceGetState { + + + private final int[] tools = new int[3]; + private final boolean[] usedTools = new boolean[3]; + private int totalToolsCount; + private int roundToolsCount; + private final List additionalTools = new ArrayList<>(); + + public void newTurn() { + Arrays.fill(usedTools, false); + roundToolsCount = totalToolsCount; + totalToolsCount = 0; + } + + public void addTool() { + if (totalToolsCount < 12) { + int position = totalToolsCount % 3; + int value = 1 + totalToolsCount / 3; + tools[position] = value; + totalToolsCount++; + roundToolsCount++; + } + } + + public void addSingleUseTool(int strength) { + additionalTools.add(strength); + totalToolsCount += strength; + roundToolsCount += strength; + } + + public Optional useTool(int index) { + if (index > 2) { + int additionalIndex = index % 3; + int additionalToolValue = additionalTools.get(additionalIndex); + totalToolsCount -= additionalToolValue; + roundToolsCount -= additionalToolValue; + additionalTools.remove(additionalIndex); + return Optional.of(additionalToolValue); + } else { + roundToolsCount = roundToolsCount - tools[index]; + usedTools[index] = true; + return Optional.of(tools[index]); + } + } + + public boolean hasSufficientTools(int goal) { + return goal <= roundToolsCount; + } + + + @Override + public String state() { + Map state = Map.of( + "tools", tools, + "usedTools", usedTools, + "totalToolsCount", totalToolsCount, + "roundToolsCount", roundToolsCount, + "additionalTools", additionalTools + ); + + return new JSONObject(state).toString(); + } + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/Effect.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/Effect.java index 1a1bdfee..917bfd79 100644 --- a/src/main/java/sk/uniba/fmph/dcs/stone_age/Effect.java +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/Effect.java @@ -1,7 +1,7 @@ package sk.uniba.fmph.dcs.stone_age; public enum Effect { - FOOD, WOOD, CLAY, STONE, GOLD, TOOL, FIELD, BUILDING, ONE_TIME_TOOL2, ONE_TIME_TOOL3, ONE_TIME_TOOL4; + FOOD, WOOD, CLAY, STONE, GOLD, TOOL, FIELD,CARD,POINT, BUILDING, ONE_TIME_TOOL2, ONE_TIME_TOOL3, ONE_TIME_TOOL4; private static final int FOOD_POINTS = 2; private static final int WOOD_POINTS = 3; diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/EndOfGameEffect.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/EndOfGameEffect.java new file mode 100644 index 00000000..9130017a --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/EndOfGameEffect.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public enum EndOfGameEffect { + FARMER, TOOL_MAKER, BUILDER, SHAMAN, MEDICINE, ART, MUSIC, WRITING,SUNDIAL, POTTERY, TRANSPORT, WEAVING +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/ImmediateEffect.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/ImmediateEffect.java new file mode 100644 index 00000000..773cbcea --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/ImmediateEffect.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public enum ImmediateEffect { + ThrowWood, ThrowClay, ThrowStone, ThrowGold, POINT, FOOD, WOOD, CLAY, STONE, GOLD, CARD, Tool, Field, OneTimeTool2, OneTimeTool3,OneTimeTool4, ArbitraryResource, AllPlayersTakeReward +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFeedTribe.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFeedTribe.java new file mode 100644 index 00000000..ca1288c9 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFeedTribe.java @@ -0,0 +1,10 @@ +package sk.uniba.fmph.dcs.stone_age; + +import java.util.Collection; + +public interface InterfaceFeedTribe { + boolean feedTribeIfEnoughFood(); + boolean feedTribe(Collection resources); + boolean doNotFeedThisTurn(); + boolean isTribeFed(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocation.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocation.java new file mode 100644 index 00000000..a399537d --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocation.java @@ -0,0 +1,12 @@ +package sk.uniba.fmph.dcs.stone_age; + +import java.util.*; + +public interface InterfaceFigureLocation { + boolean placeFigures(PlayerOrder player, int figureCount); + HasAction tryToPlaceFigures(PlayerOrder player, int count); + ActionResult makeAction(PlayerOrder player, Collection inputResources, Collection outputResources); + boolean skipAction(PlayerOrder player); + HasAction tryToMakeAction(PlayerOrder player); + boolean newTurn(); +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocationInternal.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocationInternal.java new file mode 100644 index 00000000..6c8438af --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceFigureLocationInternal.java @@ -0,0 +1,12 @@ +package sk.uniba.fmph.dcs.stone_age; + +import java.util.Collection; + +public interface InterfaceFigureLocationInternal { + boolean placeFigures(Player player, int figureCount); + HasAction tryToPlaceFigures(Player player, int count); + ActionResult makeAction(Player player, Effect[] inputResources, Effect[] outputResources); + boolean skipAction(Player player); + HasAction tryToMakeAction(Player player); + boolean newTurn(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceGetState.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceGetState.java new file mode 100644 index 00000000..5ead090c --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceGetState.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceGetState { + String state(); +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceNewTurn.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceNewTurn.java new file mode 100644 index 00000000..5c472851 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceNewTurn.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceNewTurn { + boolean newTurn(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfacePlayerBoardGameBoard.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfacePlayerBoardGameBoard.java new file mode 100644 index 00000000..f4383b49 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfacePlayerBoardGameBoard.java @@ -0,0 +1,18 @@ +package sk.uniba.fmph.dcs.stone_age; + +import sk.uniba.fmph.dcs.game_board.CivilizationCard; + +import java.util.Collection; +import java.util.Optional; + +public interface InterfacePlayerBoardGameBoard { + void giveEffect(Collection stuff); + void giveFigure(); + void giveEndOfGameEffect(Collection stuff); + void giveCard(CivilizationCard card); + boolean takeResources(Collection stuff); + boolean takeFigures(int count); + boolean hasFigures(int count); + boolean hasSufficientTools(int goal); + Optional useTool(int idx); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObservable.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObservable.java new file mode 100644 index 00000000..6508bdbb --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObservable.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceStoneAgeObservable { + void registerObserver(int playerId, InterfaceStoneAgeObserver observer); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObserver.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObserver.java new file mode 100644 index 00000000..fe93c234 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceStoneAgeObserver.java @@ -0,0 +1,5 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceStoneAgeObserver { + void update(String gameState); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceTakeReward.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceTakeReward.java new file mode 100644 index 00000000..f2fc84dc --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceTakeReward.java @@ -0,0 +1,8 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceTakeReward { + + public boolean takeReward(PlayerOrder player, Effect reward); + public HasAction tryMakeAction(PlayerOrder player); + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceToolUse.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceToolUse.java new file mode 100644 index 00000000..60089afc --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/InterfaceToolUse.java @@ -0,0 +1,7 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface InterfaceToolUse { + boolean useTool(int idx); + boolean canUseTools(); + boolean finishUsingTools(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/Player.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/Player.java new file mode 100644 index 00000000..729a2fa5 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/Player.java @@ -0,0 +1,6 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface Player { + PlayerOrder playerOrder(); + InterfacePlayerBoardGameBoard playerBoard(); +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/PlayerBoard.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/PlayerBoard.java new file mode 100644 index 00000000..41323eba --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/PlayerBoard.java @@ -0,0 +1,7 @@ +package sk.uniba.fmph.dcs.stone_age; + +public interface PlayerBoard { + boolean hasFigures(int count); + void giveEffect(Effect[] stuff); + +} diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeGame.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeGame.java new file mode 100644 index 00000000..53a29b58 --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeGame.java @@ -0,0 +1,18 @@ +package sk.uniba.fmph.dcs.stone_age; + +import java.util.HashMap; +import java.util.Map; + +public class StoneAgeGame { + Map players; + + public StoneAgeGame(int amount) { + players = new HashMap<>(); + for (int i = 1; i <= amount; i++) { + PlayerOrder po = new PlayerOrder(i, amount); + players.put(po, i); + } + } +} + + diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeObservable.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeObservable.java new file mode 100644 index 00000000..f1adac5b --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/StoneAgeObservable.java @@ -0,0 +1,11 @@ +package sk.uniba.fmph.dcs.stone_age; + +import java.util.*; + + + +public class StoneAgeObservable { + public String notify(String gamestate){ + return ""; + }; +} \ No newline at end of file diff --git a/src/main/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatus.java b/src/main/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatus.java new file mode 100644 index 00000000..4012142b --- /dev/null +++ b/src/main/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatus.java @@ -0,0 +1,111 @@ +package sk.uniba.fmph.dcs.stone_age; + +import org.json.JSONObject; +import sk.uniba.fmph.dcs.player_board.PlayerFigures; + +import java.util.Collection; +import java.util.Map; +import sk.uniba.fmph.dcs.player_board.PlayerFigures; + +public final class TribeFedStatus implements InterfaceFeedTribe, InterfaceNewTurn { + private static final int MAX_FIELDS = 10; + private static final int FOOD_PER_FIGURE = 1; + + private final PlayerFigures figures; + private boolean tribeFed; + private int fields; + + public TribeFedStatus(PlayerFigures figures) { + this.figures = figures; + this.tribeFed = false; + this.fields = 0; + } + + @Override + public boolean feedTribeIfEnoughFood() { + if (tribeFed) { + return false; + } + + int requiredFood = calculateRequiredFood(); + int foodFromFields = Math.min(fields, requiredFood); + int remainingFood = requiredFood - foodFromFields; + + // If we have enough fields to feed everyone, mark as fed + if (remainingFood == 0) { + tribeFed = true; + return true; + } + + return false; + } + + @Override + public boolean feedTribe(Collection resources) { + if (tribeFed) { + return false; + } + + int requiredFood = calculateRequiredFood(); + int foodFromFields = Math.min(fields, requiredFood); + int remainingFood = requiredFood - foodFromFields; + + // Count food resources provided + int foodProvided = 0; + for (Effect resource : resources) { + if (resource != Effect.FOOD) { + return false; // Only food resources allowed + } + foodProvided++; + } + + // Check if enough food was provided + if (foodProvided == remainingFood) { + tribeFed = true; + return true; + } + + return false; + } + + @Override + public boolean doNotFeedThisTurn() { + if (tribeFed) { + return false; + } + tribeFed = true; + return true; + } + + @Override + public boolean isTribeFed() { + return tribeFed; + } + + @Override + public void newTurn() { //TODO --> boolean newTurn() + tribeFed = false; + } + + public void addField() { + if (fields < MAX_FIELDS) { + fields++; + } + } + + public int getFields() { + return fields; + } + + private int calculateRequiredFood() { + return figures.getTotalFigures() * FOOD_PER_FIGURE; + } + + public String state() { + Map state = Map.of( + "tribeFed", tribeFed, + "fields", fields + ); + return new JSONObject(state).toString(); + } +} diff --git a/src/task/Assignment.md b/src/task/Assignment.md new file mode 100644 index 00000000..fcc10ddd --- /dev/null +++ b/src/task/Assignment.md @@ -0,0 +1,42 @@ +# Assignment - Stone Age game + +Your task is to construct a component implementing the logic of board game Stone Age. These are the [rules](https://images.zmangames.com/filer_public/ee/88/ee888bec-5000-4566-9fb0-8c6475e706a0/zm7260_stone_age_rules.pdf). + +## Design + +I prepared a [design](pts24.png) template for your implementation. Besides that I prepared [simple description of the classes](classes.md) with estimations + +Note that the design is by no means complete. Most notably +- You have to add appropriate constructors. Chose your constructors in a way that enables good testing. E.g. card deck and building stacks do not need to be full. +- You have to add interfaces that separate classes that need to be separated. +- You probably want to do something with Bag class to handle the randomness involved better. +Also, it is highly likely, that I overlooked something and there are some issues that need to be resolved. Feel free to do necessary changes and adjustments. + +I recommend to stick to the following timeline +- 11. 11. create teams, understand game rules, understand the design (so we can have a meaningful discussion on the lecture). +- 20. 11. the common part of the project is finished +- 27. 11. you are done + +## Implementation remarks + +You should build your project either in Python or in Java. I started the projects for you, including several tools that might help you. See [pts24-python](https://github.com/relatko/pts1-23-python) and [pts24-java](https://github.com/relatko/pts1-23-java). Note that if you use Python, your code should be type annotated and mypy --strict should show no errors. Do not use any except for when handling json. + +This is a fairly large project and it would take too long to implement. Thus you will collaborate while implementing the project. + +* Create groups of, ideally, six to eight people. and fork the repository you agree to use. Green classes are provided in the project (Java GamePhaseController will be implemented soonish). +* Collaboratively implement the elements marked white (trivial) and blue, including factories and integration tests for two components. For blue elements create a PR (typically one PR per element, but you may merge several very trivial ones into one PR), discuss it and merge it into the master (I recommend main branch protection requiring PR with at least 1 approval). Blue classes are not of particular interest to me. The amount of points given here is quite limited (up to 20 points including non-coding aspects of common work like like making and reviewing PRs), so do not overthink stuff, but make it sound enough so you actually can write working integration tests. This also implies that the test coverage does not have to be great in these classes. While collaborating create new features in own branches and merge it to main with a PR after code review is done. Turn on branch protection. Try to keep CI green (you can enforce this by branch protection rules). You may use github issues to your the progress. You may split large task into smaller tasks. You may want to meet one time before you start your work. Splitting L tasks is a good thing to do. +It is expected that everybody prepares and reviews 3-5 PRs. +* After this, you should add your own implementation of the red classes (including unit tests) and prepare two integration tests. To reduce the amount of work necessary, it is sufficient to create one end to end scenario for each of these integration test. + +Unit tests for Game should be solitary. Do a sociable unit test of CivilisationCardPlace that also uses CivilisationCardDeck. Tests of ResourceSource must be solitary. You can discuss technical aspects of the implementation, but everybody should implement the red classes and the red integration tests on his own. You can either work on local machine or use a private repository for your effort. + +You should use Git and produce a reasonable history of commits. Note that to work on the red classes you do not need the blue ones. You are also free to adjust the implementation of blue and green classes according to the needs of your project (but, the changes, hopefully should not be large, a good example is changing interface name, adding a new interface, ...). + + + +## Instructions for submitting your project + +Send your solution to [lukotka.pts@gmail.com](lukotka.pts@gmail.com). The deadline is 30.11.2022 23:59:59. The solutions sent later will be accepted, however the number of points awarded may be reduced. + +Send the solution to me either as compressed folders containing the whole repository (including the hidden .git files) or as a link to private repository with read access granted (GitHub handle `relatko`). Attach a link to the public repository for the team part of the project and link PRs you have made and PRs you have reviewed. + diff --git a/src/task/classes.md b/src/task/classes.md new file mode 100644 index 00000000..49753ca0 --- /dev/null +++ b/src/task/classes.md @@ -0,0 +1,326 @@ +# Stone Age + +The component responsible for running the game according to the rules. + +*Estimation for top level integration tests and factories:* 3h + + +## Facade classes + +### StoneAgeGame class + +This is an adapter class that converts external representation to internal representation. Namely it converts system-wide player ids to PlayerOrder objects relevant to the game and is the root for converting game state representation to JSON. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* 0.5h + +### StoneAgeObservable class + +Implements observer pattern. According to the rules the civilisation cards should be visible only to player. First of all, this is not that important as the information is available, if players remember it. Second, this cannot be realised for terminal multiplayer client. Thus we ship this aspect. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XS + + +## PlayerBoard component + +The component implements functionality associated with information present on player board (+ player points and fields). Besides that by applying informatin expert principle, this component is also responsible for tracking tribe feeding. + +*Integration tests:* Focus on interactions involving multiple classes like tribe feeding and final points calculation. + +*Estimation for integration tests and factories:* M + +### PlayerBoard class + +Composes state and the final points calculation. Stores points and houses (as there is no significant behaviour associated to these attributes.) + +*Unit tests:* Yes. + +*Estimation:* S + +### PlayerBoardFacade + +Facade class for PlayerBoard component that makes it easier for other components to interact with PlayerBoard. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XS + +### PlayerResourcesAndFood class + +Stores resources and food, calculates points for leftover resources. + +*Unit tests:* Yes. + +*Estimation:* S + +### PlayerFigures class + +Handles player figures that are on player board and are free to be used. Note that instead of moving figures back from game board we just reset the number of figures at the start of new turn. + +*Unit tests:* Yes. Very simple. + +*Estimation:* XS + +### PlayerCivilisationCards class + +Stores player civilisation card long term effects and calculates the final points associated with them. + +*Unit tests:* Yes. Makes sense to produce quite a few unit test to be sure that this functionality is implemented correctly. + +*Estimation:* S + +### PlayerTools class + +Handless player tools and one time use tools + +*Unit tests:* Yes. This is quite a complex class, and requires significatn unit tests + +*Estimation:* M + +### TribeFedStatus class + +Tracks number of fields and tribe feeding. SetTribeFed should be used in case of -10 point hit chosen. + +*Unit tests:* Yes. Tests may be sociable + +*Estimation:* L + + +## GameBoard component + +Implements behaviours associated with game board (except for tracking points aand fields), related to player actions. + +*Integration tests:* Yes. Focus on interaction involving RewardMenu, CurrentThrow + +*Estimation for integration tests and factories:* L (you can split this task into more smaller tasks) + +## GameBoard class + +Puts game board state together. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XXS + +### FigureLocationAdaptor + +Adaptor replacing PlayerOrder with Player structures. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XXS + +### PlaceOnToolMakerAdaptor class + +Adaptor between InterfaceFigureLocationInternal and ToolMakerHutFields for tool maker action. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XXS + +### PlaceOnHutAdaptor class + +Adaptor between InterfaceFigureLocationInternal and ToolMakerHutFields for hut action. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XXS + +### PlaceOnFieldsAdaptor class + +Adaptor between InterfaceFigureLocationInternal and ToolMakerHutFields for fields action. + +*Unit tests:* No. It is an adaptor/facade class with very simple to no responsibilities. Testing is best handled by an integration test + +*Estimation:* XS + +### ToolMakerHutsFields class + +Implements these three actions and interaction among them (when the game has less players) + +*Unit tests:* Yes. Focus on the interaction. + +*Estimation:* M + +### ResourceSource class + +Implements actions Hunting, Forest, Clay mound, Quarry, River. + +*Unit tests:* Yes. Focus on interaction between players. + +*Estimation:* M + +### CurrectThrow class + +Handles die throws for resources (Food, Wood, Clay, Stone, Gold) and with improving the throws with tools. + +*Unit tests:* Yes. + +*Estimation:* M + +### Throw class + +Encapsulates the random effect. + +*Unit tests:* No. Class needs to be mocked in integration tests + +*Estimation:* XS + +### CivilisationCardPlace class + +Implements civilisation card actions + +*Unit tests:* Yes. May be sociable with CivilisationCardDeck. Things to focus include paying sufficint resources, validating that imediate effect evaluation was invoked, end of game effect was forwarded to player board, and that filling up the places on end of turn is handled properly. The instances may assume that newTurn is called in certain order. If so, do not forget to documment it and do not forget to test the behaviour in NewRoundState. + +*Estimation:* L (you can split it into smaller tasks) + +### CivilisationCardDeck class + +Implements civilisation card deck. + +*Unit tests:* Not necessary. Class is very simple. Sociable unit tests with CivilisationCardPlace is sufficient. + +*Estimation:* XS + +### RewardMenu class + +Represents the choices available in All Players Take A Reward imediate civilisation card effect. + +*Unit tests:* Yes. + +*Estimation:* M + +### AllPlayersTakeAReward class + +Represents corresponding immediate civilisation card effect. Throws dices and initializes reward menu. + +*Unit tests:* Yes. + +*Estimation:* M + +### GetSomethingThrow class + +Represents corresponding immediate civilisation card effect. Uses CurrentThrow to perform the effect + +*Unit tests:* Yes. + +*Estimation:* S + +### GetSomethingFixed class + +Represents corresponding immediate civilisation card effect. Uses CurrentThrow to perform the effect + +*Unit tests:* Yes. + +*Estimation:* XS + +### GetSomethingChoice class + +Represents corresponding immediate civilisation card effect. Uses CurrentThrow to perform the effect + +*Unit tests:* Yes. + +*Estimation:* XS + +### GetCard class + +Represents corresponding immediate civilisation card effect. Uses CurrentThrow to perform the effect + +*Unit tests:* Yes. + +*Estimation:* S + +### BouldingTile class + +Represents building tiles, including hidden buildings and associated actions + +*Unit tests:* Yes. May be sociable with e.g. SimpleBuilding + +*Estimation:* L + +### ArbiraryBuilding class + +Represents building that allows 1-7 resources of any kind to be spent + +*Unit tests:* Yes. + +*Estimation:* XS + +### VariableBuilding class + +Represents building that allows spending fixed number of resources of given number of resource type. + +*Unit tests:* Yes. Be careful + +*Estimation:* S + +### SimpleBuilding class + +Represents building that prescribes resources spent. + +*Unit tests:* Yes. + +*Estimation:* XS + + +## GamePhaseController component + +This components track game phases. + +*Integration tests:* Focus on game ending scenatios and interactions not covered by GamePhaseController unit test + +*Estimation for integration tests and factories:* M + +### GamePhaseController class + +Represents state transitions in the game. Besides moving the state after each succesfull player action the class tries to continue with player actions that do not require input. + +*Unit tests:* Yes. Focus on transition + +*Estimation:* XL + +### PlaceFigure class + +*Unit tests:* Yes. Focus on tryToMakeAutomaticAction method. + +*Estimation:* S + +### MakeActionState class + +*Unit tests:* Yes. Focus on tryToMakeAutomaticAction method. + +*Estimation:* S + +### NewRoundState class + +*Unit tests:* Yes. Focus on game end test. + +*Estimation:* S + +### FeedTribe class + +*Unit tests:* Yes. Focus on tryToMakeAutomaticAction method. + +*Estimation:* S + +### WaiitingForToolUse class + +*Unit tests:* Yes. Focus on tryToMakeAutomaticAction method. + +*Estimation:* S + +### AllPlayesTakeAReward class + +*Unit tests:* Yes. Focus on tryToMakeAutomaticAction method. + +*Estimation:* S + +### GameEnd class + +*Unit tests:* Yes. + +*Estimation:* XS + diff --git a/src/task/pts24.pdf b/src/task/pts24.pdf new file mode 100644 index 00000000..b20d2307 Binary files /dev/null and b/src/task/pts24.pdf differ diff --git a/src/task/pts24.png b/src/task/pts24.png new file mode 100644 index 00000000..4a988fdf Binary files /dev/null and b/src/task/pts24.png differ diff --git a/src/task/pts24.uxf b/src/task/pts24.uxf new file mode 100644 index 00000000..9a645aa6 --- /dev/null +++ b/src/task/pts24.uxf @@ -0,0 +1,490 @@ +10Space for diagram notesUMLClass114070500180<<Interface>> +InterfaceStoneAgeGame +-- +placeFigures(playerId: int, location:Location, figuresCount: int): bool +makeAction(playerId: int, location: Location, usedResources: Effect[], + desiredResources: Effect[]): bool +skipAction(playerId: int, location: Location): bool (only if resources are required) +useTools(playerId: int, toolIndex: int): bool {affects last action} +noMoreToolsThisThrow(playerId: int): bool +feedTribe(playerId: int, resources: Effect[]): bool +doNotFeedThisTurn(playerId: int): bool +makeAllPlayersTakeARewardChoice(playerId: int, reward: Effect):boolUMLClass21309021080<<Interface>> +InterfaceStoneAgeObserver +-- +update(gameState: string) +UMLClass7600140290<<Enumeration>> +Location +-- +ToolMaker +Hut +Field +HuntingGrounds +Forest +ClayMound +Quary +River +CivilizationCard1 +CivilizationCard2 +CivilizationCard3 +CivilizationCard4 +BuildingTile1 +BuildingTile2 +BuildingTile3 +BuildingTile4 + + + + +UMLClass9200170280<<Enumeration>> +Effect +-- +Food +Wood +Clay +Stone +Gold +Tool +Field +Building +OneTimeTool2 +OneTimeTool3 +OneTimeTool4 +-- +isResourceOrFood(): bool +isResource(): bool +points(): int {is 0 if not + ResourceOrFood} +UMLClass2340640360180bg=green +GamePhaseController +-- +roundStartingPlayer: PlayerOrder +gamePhase: GamePhase +currentPlayer: PlayerOrder +currentPlayerTakingReward: Optional[PlayerOrder] +dispatchers: map<GamePhase, InterfaceGamePhaseState> +-- +-checkPlayersTurn(player: PlayerOrder): bool +-progressStateAfterSuccesfullAction() +-progressStateAfterNoActionPossible() +-progressStateAfterNoActionPossibleByAnyPlayer() +-tryToDoFurtherActions() +UMLClass1260380260100bg=blue +StoneAgeGame +-- +players: map<PlayerOrder, int> +UMLClass1420177030060bg=blue +BuildingTile +-- +figures:PlayerOrder[] +-- +state(): stringUMLClass168038045060bg=blue +StoneAgeObservable +-- +notify(gameState: string) {Also removes information that should be invisible, + but we ignore this}UMLClass16809040080<<Interface>> +InterfaceStoneAgeObservable +-- +registerObserver(playerId: int,observer: InterfaceStoneAgeObserver) +Relation151041019030lt=<-170;10;10;10Relation23905703090lt=<<.10;10;10;70Relation187016030240lt=<<.10;10;10;220Relation2120160110250lt=.> +<<requires>> + + +10;230;30;230;30;10UMLClass22601450140150<<Enumeration>> +GamePhase +-- +PlaceFigures +MakeAction +FeedTribe +NewRound +WaitingForToolsUse +AllPlayersTakeAReward +GameEnd +UMLClass2240840490200<<Interface>> +InterfaceGamePhaseState +-- +placeFigures(player: PlayerOrder, location:Location, figuresCount: int): ActionResult +makeAction(player: PlayerOrder, location: Location, inputResources: Effect[], + outputResources: Effect[]): ActionResult +skipAction(player: PlayerOrder, location: Location): ActionResult +useTools(player: PlayerOrder, toolIndex: int):ActionResult +noMoreToolsThisThrow(player: PlayerOrder): ActionResult +feedTribe(player: PlayerOrder, resources: Effect[]): ActionResult +doNotFeedThisTurn(player: PlayerOrder): ActionResult +makeAllPlayersTakeARewardChoice(player: PlayerOrder, reward: Effect): ActionResult +tryToMakeAutomaticAction(player: PlayerOrder): HasAction + +Relation24908103050lt=<.10;30;10;10UMLClass601900220100<<Enumeration>> +ActionResult +-- +Failure +ActionDone +ActionDoneWaitForToolsUse +ActionDoneAllPlayersTakeReward +UMLClass701750200100<<Enumeration>> +HasAction +-- +WaitingForPlayerAction +AutomaticActionDone +NoActionPossible + +UMLNote2900510430580*Player on turn* + +Class is responsible for checking if the player is on turn. +PlaceFigures: player must match currentPlayer +MakeAction: player must match currentPlayer +FeedTribe: any player is fine +NewRound: assertion failure (calls should not happen) +WaitingForToolsUse: player must match currentPlayer +WaitingForActionChice: player must match currentPlayer +AllPlayersTakeAReward: player must match currentPlayerTakingReward + +*TryToMakeAutomaticAction* + +*PlaceFigures:* +- has free figures -> WaitingForPlayerAction +- otherwise -> NoActionPossible +*MakeAction:* +- figures on board -> WaitingForPlayerAction +- otherwise -> NoActionPossible +*FeedTribe:* +- tribe already fed -> NoActionPossible +- has enough food to feed tribe -> AutomaticActionDone (feed tribe) +- otherwise -> WaitingForPlayerAction +*NewRound:* +- round not initialized -> AutomaticActionDone (initialize round) +- game ended -> NoActionPossible +*WaitingForToolsUse:* +- not enough tools to improve roll outcome -> NoActionPossible +- otherwise -> WaitingForPlayerAction +*AllPlayersTakeAReward:* +- all players have reward - NoActionPossible +- last player gets reward - AutomaticActionDone (gets the last reward) +- otherwise -> WaitingForPlayerAction +*GameEnd:* +- WaitingForPlayerAction + +If NoActionPossible for each player, next phase can start.UMLClass14501090270120bg=red +ResourceSource +-- +name: string +Resource: Effect +maxFigures: int +maxFigureColours: int +figures: PlayerOrder[] +-- +state(): stringUMLClass103060036060GameBoard +-- +state(): string +bg=blue +UMLClass580530310130bg=blue +PlayerBoard +-- +points: int +houses: int +-- +addPoints(points: int) +addHouse() +addEndOfGamePoints() +state(): string +UMLClass1090850290260bg=blue +ToolMakerHutFields +-- +toolMakerFigures: PlayerOrder[] +hutFigures:PlayerOrder[] +fieldsFigures: PlayerOrder[] +restriction: int +-- +placeOnToolMaker(player: Player): bool +actionToolMaker(player: Player): bool +canPlaceOnToolMaker(player: Player): bool +placeOnHut(figures: Player): bool +actionHut(player: Player): bool +canPlaceOnHut(player: Player) +placeOnFields(player: Player): bool +actionFields(player: Player): bool +canPlaceOnFields(player: Player) +newTurn() +state(): stringUMLClass1380186034070<<interface>> +Building +-- +build(resources: Effect[]): Optional[int] {returns points} +state(): string +UMLClass1420138030080bg=red +CivilizationCardPlace +-- +requiredResources: int +figures:PlayerOrder[] +-- +state(): stringUMLClass1100143021060bg=red +CivilizationCardDeck +-- +getTop(): Optional[CivilizationCard] +state: string()UMLClass150520250120bg=blue +PlayerResourcesAndFood +-- +resources: map<Effect, int> +-- +hasResources(resources: Effect[]): bool +takeResources(resources: Effect[]): bool +giveResources(resources: Effect[]): bool +numberOfResourcesForFinalPoints(): int +state: string() + + +UMLClass570710220150bg=blue +PlayerTools +-- +tools: int[] +usedTools bnool[] +-- +newTurn() +addTool() +addSingleUseTool(strength: int) +useTool(index: int): Optional[int] +hasSufficientTools(goal: int): bool +state(): stringUMLClass1630199013040bg=green +SimpleBuilding +-- +resources: Effect[] +UMLClass1430199019060bg=blue +VariableBuilding +-- +numberOfResources: int +numberOfResourceTypes: int +UMLClass1230199019050bg=blue +ArbitraryBuilding +-- +maxNumberOfResources: int +Relation151019203090lt=<<.10;10;10;70Relation1320195039060lt=.370;40;370;10;10;10;10;40Relation153018203060lt=<<<<<-10;10;10;40UMLClass660162026070<<Datatype>> +CivilisationCard +-- +immediateEffect:ImmediateEffectType[] +endOfGameEffect: EndOfGameEffectType[] +Relation10606503801160lt=<<<<<-10;10;10;1140;360;1140Relation106010305030lt=-30;10;10;10UMLClass13801240270100bg=red +CurrentThrow +-- +throwsFor: Effect +throwResult: int +-- +initiate(player: Player, effect: Effect, dices: int) +state(): stringUMLClass1360162014030AllPlayersTakeReward +bg=blue +UMLClass8101720130230<<Enumeration>> +EndOfGameEffect +-- +Farmer +ToolMaker +Builder +Shaman +Medicine +Art +Music +Writing +Sundial +Pottery +Transport +Weaving + + +UMLClass6401720140240<<Enumeration>> +ImmediateEffect +-- +ThrowWood +ThrowClay +ThrowStone +ThrowGold +Point +Wood +Clay +Stone +Gold +Card +ArbitraryResource +Food +AllPlayersTakeReward +UMLClass2440106029040bg=blue +PlaceFiguresState +-- +places: Map[Location, InterfaceFigureLocation]UMLClass2440113029040bg=blue +MakeActionState +-- +places: Map[Location, InterfaceFigureLocation]UMLClass2550127018030bg=blue +FeedTribeState +UMLClass2530120020040bg=blue +NewRoundState +-- +places: InterfaceFigureLocation[]UMLClass2550133018030bg=blue +WaitingForToolUseStateUMLClass2520139021030bg=blue +AllPlayersTakeARewardStateUMLClass2580144015030bg=blue +GameEndStateRelation272089060590lt=<<.10;10;40;10;40;570;10;570UMLClass18401040370160<<Interface>> +InterfaceFigureLocation +-- +placeFigures(player: PlayerOrder, figureCount: int):bool +tryToPlaceFigures(player: PlayerOrder, count: int): HasAction +makeAction(player: PlayerOrder, inputResources: Effect[], + outputResources: Effect[]): ActionResult +skipAction(player: PlayerOrder): bool +tryToMakeAction(player: PlayerOrder): HasAction +newTurn(): bool {returns true if end of game is implied + by given location} +Relation272010706030lt=.40;10;10;10Relation1770700190360lt=<<.170;340;170;10;10;10UMLClass1540100018030bg=blue +PlaceOnHutAdaptorUMLClass1540104018030bg=blue +PlaceOnFieldsAdaptorUMLClass154096018030bg=blue +PlaceOnToolMakerAdaptorRelation171011406030lt=.40;10;10;10Relation16707103070lt=.>10;10;10;50Relation171010006030lt=.40;10;10;10Relation103083043030lt=.410;10;10;10Relation2200114026030lt=<.10;10;240;10Relation272011406030lt=.40;10;10;10Relation1370104019030lt=<-10;10;170;10Relation1370100019030lt=<-10;10;170;10Relation137096019030lt=<-10;10;170;10Relation1060139038030lt=-10;10;360;10Relation12004008040lt=<. +10;20;60;20UMLClass150890220190bg=blue +PlayerFigures +-- +totalFigures:int +figures:int +-- +addNewFigure() +hasFigures(count: int): bool +getTotalFigures(): int +takeFigures(count: int): bool +newTurn() +state: string() +-- +{Adding figures when you have more +than 10 has no effect} + + + +Relation39056021030lt=<<<<<-190;10;10;10UMLClass1140131017060bg=blue +Throw +-- +throw(dices: int): int[]Relation1300132010030lt=->80;10;10;10UMLClass6301250280150<<Interface>> +InterfacePlayerBoardGameBoard +-- +giveEffect(stuff: Effect[]) +giveEndOfGameEfect(stuff: EndOfGameEffect[]) +takeResources(stuff: Effect[]): bool +takeFigures(count: int): bool +hasFigures(count: int): bool +hasSufficientTools(goal: int): bool +useTool(idx: int): Optional[int] +Relation84065030420lt=<-10;10;10;400Relation10307005601140lt=. +390;1120;10;1120;10;10;540;10UMLClass680105020030bg=blue +PlayerBoardGameBoardFacadeRelation750107030200lt=.>>10;10;10;180Relation152012003060lt=<-10;40;10;10Relation900128050030lt=<.10;10;480;10UMLGeneric90500830620symbol=component +PlayerBoardUMLGeneric22306005701020symbol=component +GamePhaseConrollerUMLGeneric10105508001520symbol=component +GameBoardUMLClass1440760350170<<Interface>> +InterfaceFigureLocationInternal +-- +placeFigures(player: Player, figureCount: int):bool +tryToPlaceFigures(player: Player, count: int): HasAction +makeAction(player: Player, inputResources: Effect[], + outputResources: Effect[]): ActionResult +skipAction(player: Player): bool +tryToMakeAction(player: Player): HasAction +newTurn(): bool {returns true if end of game is implied by + given location} + +UMLClass157069021030bg=blue +FigureLocationAdaptorUMLClass150059028070<<Datatype>> +Player +-- +playerOrder: PlayerOrder +playerBoard: InterfacePlayerBoardGameBoardRelation1060116041030lt=-390;10;10;10Relation37012702200860lt=<.10;90;10;840;2120;840;2120;10;2180;10Relation54012102010900lt=<.10;100;10;880;1930;880;1930;10;1990;10Relation23901070160160lt=.50;10;10;10;10;140;140;140Relation272012006030lt=.40;10;10;10Relation272012706030lt=.40;10;10;10Relation1640129025030lt=.>>10;10;230;10UMLClass1380149030070<<Interface>> +EvaluateCivilisationCardImmediateEffect +-- +performEffect(player: Player, choice: Effect): boolUMLClass1360167014040bg=blue +GetSomethingFixed +-- +effect: Effect[] +UMLClass1560162013040bg=blue +GetSomethingThrow +-- +resource: Effect[] +UMLClass1560167018040bg=blue +GetChoice +-- +numberOfResources: Effect[]Relation1520155030190lt=<<.10;10;10;170Relation149016709030lt=.70;10;10;10Relation149016209030lt=.10;10;70;10Relation16401320170340lt=<-10;10;150;10;150;320;50;320Relation1300134080310lt=<-10;10;50;10;50;290;60;290Relation1030153037030lt=.10;10;350;10Relation171092060890lt=<<.40;10;40;870;10;870UMLClass105040016060<<Interface>> +InterfaceGetState +-- +state(): stringRelation106045030170lt=<<.10;10;10;150Relation670440400110lt=<<.380;10;10;10;10;90UMLClass2150400640180<<Interface>> +InterfaceGamePhaseController +-- +placeFigures(player: PlayerOrder, location:Location, figuresCount: int):bool +makeAction(player: PlayerOrder, location: Location, inputResources: Effect[], outputResources: Effect[]): bool +skipAction(player: PlayerOrder, location: Location): bool +useTools(player: PlayerOrder, toolIndex: int): bool +noMoreToolsThisThrow(player: PlayerOrder): bool +feedTribe(player: PlayerOrder, resources: Effect[]): bool +doNotFeedThisTurn(player: PlayerOrder): bool +makeAllPlayersTakeARewardChoice(player: PlayerOrder, reward: Effect): bool +state(): string +Relation151046066030lt=<.640;10;10;10UMLClass1870128029080InterfaceToolUse +-- +useTool(idx: int):bool +canUseTools(): bool +finishUsingTools(): bool + + +Relation137024030160lt=<<.10;10;10;140UMLClass450920340110bg=blue +PlayerCivilisationCards +-- +endOfGameEffects: map<EndOfGameEffectType, int> +-- +addEndOfGameEffects(effects: EndOfGameEffectType[]) +calculateEndOfGameCivilisationCardPoints(buildings: int, + tools: int, fields:int, figures:int): int +state: string() + +Relation2150133042030lt=<.10;10;400;10Relation146014503060lt=.>10;10;10;40Relation1300144014030lt=<-10;10;120;10Relation106015809030lt=-10;10;70;10UMLClass11301570170120bg=blue +RewardMenu +-- +menu: Effect[] +players: Player[] +-- +initiate(menu: Effect[]) +state(): stringRelation129016309030lt=<-10;10;70;10UMLClass1860157031070<<Interface>> +InterfaceTakeReward +-- +takeReward(player: PlayerOrder, reward: Effect): bool +tryMakeAction(player: PlayerOrder): HasAction +Relation1030217012030lt=.10;10;100;10UMLClass70163020090<<Dataclass>> +PlayerOrder +-- +order: int +players: int +-- +next():PlayerOrder +Relation21601400380210lt=<.10;190;50;190;50;10;360;10Relation1290159059030lt=<<.570;10;10;10Relation17109606030lt=.40;10;10;10Relation171010406030lt=.40;10;10;10Relation1060126034030lt=-320;10;10;10Relation106014406030lt=-10;10;40;10Relation171014306030lt=.40;10;10;10Relation36073023030lt=-210;10;10;10Relation36089013030lt=-110;10;10;10Relation46056030380lt=-10;10;10;360Relation103010708030lt=.10;10;60;10UMLClass2601250210110<<Interface>> +InterfaceFeedTribe +-- +feedTribeIfEnoughFood(): bool +feedTribe(resources: Effect[]): bool +doNotFeedThisTurn(): bool +isTribeFed(): bool +UMLClass490125012060<<Interface>> +InterfaceNewTurn +-- +newTurn() + +Relation3701130410140lt=<<.10;120;10;10;390;10Relation540113030140lt=<<.10;120;10;10UMLClass150670220200bg=blue +TribeFedStatus +-- +tribeFed: bool +fields: int +-- +addField() +newTurn() +feedTribeIfEnoughFood(): bool +feedTribe(resources: Effect[]): bool +setTribeFed(): bool +isTribeFed(): bool +state(): string +-- +{Adding fields when you have more +than 10 has no effect} +Relation11055060230lt=<-40;10;10;10;10;210;40;210Relation16601330130120lt=<- + +< next 10;50;10;20;70;20;70;100;60;100Relation272013906030lt=.40;10;10;10Relation272013406030lt=.40;10;10;10UMLClass1460172013030bg=blue +GetCard +Relation11001480380280lt=<-10;10;10;260;360;260Relation11079060150lt=<-40;130;10;130;10;10;40;10UMLGeneric030028601880symbol=component +StoneAgeRelation272085020030lt=.10;10;180;10UMLClass13903108020bg=red +UMLClass13705608020bg=red +UMLClass45051011020bg=blue +UMLClass243061017020bg=blue +Relation1030164012030lt=.10;10;100;10Relation1030118044030lt=.10;10;420;10Relation1030141041030lt=.10;10;390;10 \ No newline at end of file diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuildingTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuildingTest.java new file mode 100644 index 00000000..db8137f6 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/ArbitraryBuildingTest.java @@ -0,0 +1,36 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.Effect; +import java.util.List; +import java.util.OptionalInt; + +import static org.junit.Assert.*; + +public class ArbitraryBuildingTest { + @Test + public void test1() { + List resources = List.of(); + ArbitraryBuilding building = new ArbitraryBuilding(3); + + OptionalInt result = building.build(resources); + assertEquals(building.build(resources), OptionalInt.empty()); + } + + @Test + public void test2() { + List resources = List.of(Effect.WOOD, Effect.CLAY, Effect.STONE, Effect.STONE); + + ArbitraryBuilding building = new ArbitraryBuilding(4); + OptionalInt result = building.build(resources); + + assertTrue(result.isPresent()); + assertEquals(result.getAsInt(), 17); + } + + @Test + public void testStateMethod() { + ArbitraryBuilding building = new ArbitraryBuilding(5); + assertEquals("ArbitraryBuilding[maxResources=5]", building.state()); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlaceTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlaceTest.java new file mode 100644 index 00000000..8aa15425 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/CivilizationCardPlaceTest.java @@ -0,0 +1,75 @@ +package sk.uniba.fmph.dcs.game_board; + +import sk.uniba.fmph.dcs.stone_age.EndOfGameEffect; +import sk.uniba.fmph.dcs.stone_age.ImmediateEffect; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class CivilizationCardPlaceTest { + private CivilizationCardDeck deck; + private CivilizationCardDeck getDeck(){ + List allCards = new ArrayList<>(); + // Dice roll (10 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.WRITING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.SUNDIAL))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.POTTERY))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TRANSPORT))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.FARMER, EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TOOL_MAKER, EndOfGameEffect.TOOL_MAKER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TOOL_MAKER, EndOfGameEffect.TOOL_MAKER))); + + // Food (7 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.MEDICINE))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.POTTERY))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.WEAVING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.WEAVING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.FARMER, EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.BUILDER))); + + // Resource (5 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE, ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.TRANSPORT))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.CLAY), Arrays.asList(EndOfGameEffect.SHAMAN, EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.GOLD), Arrays.asList(EndOfGameEffect.SHAMAN))); + + // Resources with dice roll (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ThrowStone), Arrays.asList(EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ThrowGold), Arrays.asList(EndOfGameEffect.ART))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.WOOD), Arrays.asList(EndOfGameEffect.SHAMAN, EndOfGameEffect.SHAMAN))); + + // Victory points (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.MUSIC))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.MUSIC))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + + // Extra tool tile (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Tool), Arrays.asList(EndOfGameEffect.ART))); + + // Agriculture (2 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Field), Arrays.asList(EndOfGameEffect.SUNDIAL))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Field), Arrays.asList(EndOfGameEffect.FARMER))); + + // Civilization card for final scoring (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.CARD), Arrays.asList(EndOfGameEffect.WRITING))); + + // One-use tool (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool2), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool3), Arrays.asList(EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool4), Arrays.asList(EndOfGameEffect.BUILDER))); + + // Any 2 resources (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ArbitraryResource, ImmediateEffect.ArbitraryResource), Arrays.asList(EndOfGameEffect.MEDICINE))); + Collections.shuffle(allCards); + return new CivilizationCardDeck(allCards); + } + + +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/CurrentThrowTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/CurrentThrowTest.java new file mode 100644 index 00000000..973f0eba --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/CurrentThrowTest.java @@ -0,0 +1,129 @@ +package sk.uniba.fmph.dcs.game_board; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.List; +import java.util.Optional; + +public class CurrentThrowTest { + + private CurrentThrow currentThrow; + private Player mockPlayer; + private Effect mockEffect; + + private static class MockPlayer implements Player { + private final PlayerOrder order; + private final PlayerBoard board; + + public MockPlayer(int orderNum, PlayerBoard board) { + this.order = new PlayerOrder(orderNum, 2); + this.board = board; + } + + @Override + public PlayerOrder playerOrder() { + return order; + } + + @Override + public InterfacePlayerBoardGameBoard playerBoard() { + return (InterfacePlayerBoardGameBoard) board; + } + } + private static class MockPlayerBoard implements PlayerBoard { + private boolean hasFiguresResponse = true; + + @Override + public boolean hasFigures(int count) { + return hasFiguresResponse; + } + + @Override + public void giveEffect(Effect[] stuff) { + + } + + public void setHasFiguresResponse(boolean response) { + this.hasFiguresResponse = response; + } + } + @Before + public void setUp() { + currentThrow = new CurrentThrow(); + + MockPlayerBoard mockPlayerBoard = new MockPlayerBoard(); + // Mocking a player + mockPlayer = new MockPlayer(0, mockPlayerBoard); + mockPlayer.playerBoard().giveEffect(List.of(new Effect[]{Effect.ONE_TIME_TOOL2})); + // Mocking an effect (assuming Effect is an enum or a class) + mockEffect = Effect.WOOD; + } + + @Test + public void testInitiate() { + currentThrow.initiate(mockPlayer, mockEffect, 3); + + String state = currentThrow.state(); + assertTrue(state.contains("throwsFor")); + assertTrue(state.contains("WOOD")); + assertTrue(state.contains("dices")); + assertTrue(state.contains("dicesResults")); + } + + @Test + public void testUseToolSuccess() { + // Assuming the player starts with tools available + currentThrow.initiate(mockPlayer, mockEffect, 3); + + boolean result = currentThrow.useTool(1); // Tool index 1 + assertTrue(result); + } + + @Test + public void testUseToolFailureWhenFinished() { + currentThrow.initiate(mockPlayer, mockEffect, 3); + currentThrow.finishUsingTools(); + + boolean result = currentThrow.useTool(1); + assertFalse(result); + } + + @Test + public void testCanUseTools() { + currentThrow.initiate(mockPlayer, mockEffect, 3); + boolean result = currentThrow.canUseTools(); + assertTrue(result); + } + + @Test + public void testFinishUsingToolsSuccess() { + currentThrow.initiate(mockPlayer, Effect.WOOD, 3); + + boolean result = currentThrow.finishUsingTools(); + assertTrue(result); + } + + @Test + public void testFinishUsingToolsFailureWhenNotResourceOrFood() { + // Assuming there's an Effect that is not a resource or food + Effect invalidEffect = Effect.ONE_TIME_TOOL2; // Example invalid effect + currentThrow.initiate(mockPlayer, invalidEffect, 3); + + boolean result = currentThrow.finishUsingTools(); + assertFalse(result); + } + + @Test + public void testState() { + currentThrow.initiate(mockPlayer, mockEffect, 3); + String state = currentThrow.state(); + + assertTrue(state.contains("throwsFor")); + assertTrue(state.contains("WOOD")); + assertTrue(state.contains("dices")); + assertTrue(state.contains("dicesResults")); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/GameBoardTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/GameBoardTest.java new file mode 100644 index 00000000..ae7d83a6 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/GameBoardTest.java @@ -0,0 +1,146 @@ +package sk.uniba.fmph.dcs.game_board; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.*; + +public class GameBoardTest { + + private GameBoard gameBoard; + private List players; + private Building[] buildings; + private CivilizationCard[] civilizationCards; + + private static class MockPlayerBoard implements PlayerBoard { + private boolean hasFiguresResponse = true; + + @Override + public boolean hasFigures(int count) { + return hasFiguresResponse; + } + + @Override + public void giveEffect(Effect[] stuff) { + + } + + public void setHasFiguresResponse(boolean response) { + this.hasFiguresResponse = response; + } + } + private static class MockPlayer implements Player { + private final PlayerOrder order; + private final PlayerBoard board; + + public MockPlayer(int orderNum, PlayerBoard board) { + this.order = new PlayerOrder(orderNum, 2); + this.board = board; + } + + @Override + public PlayerOrder playerOrder() { + return order; + } + + @Override + public InterfacePlayerBoardGameBoard playerBoard() { + return (InterfacePlayerBoardGameBoard) board; + } + } + + @Before + public void setUp() { + // Create mock players + players = new ArrayList<>(); + + MockPlayerBoard mockBoard1 = new MockPlayerBoard(); + MockPlayerBoard mockBoard2 = new MockPlayerBoard(); + MockPlayer mockPlayer1 = new MockPlayer(0, mockBoard1); + MockPlayer mockPlayer2 = new MockPlayer(1, mockBoard2); + players.add(mockPlayer1); + players.add(mockPlayer2); + + // Create mock buildings + buildings = new Building[4]; + for (int i = 0; i < 4; i++) { + buildings[i] = new SimpleBuilding(List.of(Effect.WOOD)); + } + + // Create mock civilization cards + civilizationCards = new CivilizationCard[4]; + for (int i = 0; i < 4; i++) { + civilizationCards[i] = new CivilizationCard(List.of(ImmediateEffect.CLAY), List.of(EndOfGameEffect.FARMER)); + } + + // Initialize the GameBoard + gameBoard = new GameBoard(players, buildings, civilizationCards); + } + + @Test + public void testInitialization() { + String state = gameBoard.state(); + + // Verify that key locations are present in the state + assertTrue(state.contains("TOOL_MAKER")); + assertTrue(state.contains("HUT")); + assertTrue(state.contains("FIELD")); + assertTrue(state.contains("FOREST")); + assertTrue(state.contains("CLAY_MOUND")); + assertTrue(state.contains("QUARY")); + assertTrue(state.contains("RIVER")); + assertTrue(state.contains("BUILDING_TILE1")); + assertTrue(state.contains("BUILDING_TILE2")); + assertTrue(state.contains("BUILDING_TILE3")); + assertTrue(state.contains("BUILDING_TILE4")); + assertTrue(state.contains("CIVILISATION_CARD1")); + assertTrue(state.contains("CIVILISATION_CARD2")); + assertTrue(state.contains("CIVILISATION_CARD3")); + assertTrue(state.contains("CIVILISATION_CARD4")); + } + + @Test + public void testLocationsState() { + // Check that each location's state is non-empty and valid + String state = gameBoard.state(); + + assertTrue(state.contains("Wood forest")); + assertTrue(state.contains("Clay mound")); + assertTrue(state.contains("Stone quarry")); + assertTrue(state.contains("Gold river")); + } + + @Test + public void testBuildingTilesInitialization() { + // Verify that each building tile has been initialized correctly + String state = gameBoard.state(); + + assertTrue(state.contains("Building1")); + assertTrue(state.contains("Building2")); + assertTrue(state.contains("Building3")); + assertTrue(state.contains("Building4")); + } + + @Test + public void testCivilizationCardInitialization() { + // Verify that each civilization card has been initialized correctly + String state = gameBoard.state(); + + assertTrue(state.contains("Card1")); + assertTrue(state.contains("Card2")); + assertTrue(state.contains("Card3")); + assertTrue(state.contains("Card4")); + } + + @Test + public void testStateFormat() { + // Verify the overall format of the state is a valid JSON string + String state = gameBoard.state(); + + assertTrue(state.startsWith("{")); + assertTrue(state.endsWith("}")); + assertTrue(state.contains("TOOL_MAKER")); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/GetCardTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/GetCardTest.java new file mode 100644 index 00000000..bb0416f8 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/GetCardTest.java @@ -0,0 +1,100 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.Effect; +import sk.uniba.fmph.dcs.stone_age.EndOfGameEffect; +import sk.uniba.fmph.dcs.stone_age.ImmediateEffect; +import sk.uniba.fmph.dcs.stone_age.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GetCardTest { + private Player player; + private CivilizationCardDeck deck; + private GetCard getCard; + private static CivilizationCardDeck getDeck(){ + List allCards = new ArrayList<>(); + // Dice roll (10 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.WRITING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.SUNDIAL))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.POTTERY))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TRANSPORT))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.FARMER, EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TOOL_MAKER, EndOfGameEffect.TOOL_MAKER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.AllPlayersTakeReward), Arrays.asList(EndOfGameEffect.TOOL_MAKER, EndOfGameEffect.TOOL_MAKER))); + + // Food (7 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.MEDICINE))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.POTTERY))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.WEAVING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.WEAVING))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.FARMER, EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD, ImmediateEffect.FOOD), Arrays.asList(EndOfGameEffect.BUILDER))); + + // Resource (5 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE, ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.TRANSPORT))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.FARMER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.STONE), Arrays.asList(EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.CLAY), Arrays.asList(EndOfGameEffect.SHAMAN, EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.GOLD), Arrays.asList(EndOfGameEffect.SHAMAN))); + + // Resources with dice roll (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ThrowStone), Arrays.asList(EndOfGameEffect.SHAMAN))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ThrowGold), Arrays.asList(EndOfGameEffect.ART))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.WOOD), Arrays.asList(EndOfGameEffect.SHAMAN, EndOfGameEffect.SHAMAN))); + + // Victory points (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.MUSIC))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.MUSIC))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.POINT, ImmediateEffect.POINT, ImmediateEffect.POINT), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + + // Extra tool tile (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Tool), Arrays.asList(EndOfGameEffect.ART))); + + // Agriculture (2 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Field), Arrays.asList(EndOfGameEffect.SUNDIAL))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.Field), Arrays.asList(EndOfGameEffect.FARMER))); + + // Civilization card for final scoring (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.CARD), Arrays.asList(EndOfGameEffect.WRITING))); + + // One-use tool (3 cards) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool2), Arrays.asList(EndOfGameEffect.BUILDER, EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool3), Arrays.asList(EndOfGameEffect.BUILDER))); + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.OneTimeTool4), Arrays.asList(EndOfGameEffect.BUILDER))); + + // Any 2 resources (1 card) + allCards.add(new CivilizationCard(Arrays.asList(ImmediateEffect.ArbitraryResource, ImmediateEffect.ArbitraryResource), Arrays.asList(EndOfGameEffect.MEDICINE))); + Collections.shuffle(allCards); + return new CivilizationCardDeck(allCards); + } + + @Before + public void setUp(){ + deck = this.getDeck(); + getCard = new GetCard(deck); + player = GetSomethingChoiceTest.getCardPlayerMaker(player, 0, new GetSomethingChoiceTest.GetCardPlayerBoardGameBoard(5)); + } + + @Test + public void test1(){ + assertTrue(getCard.performEffect(player, Effect.CARD)); + assertFalse(getCard.performEffect(player, Effect.STONE)); + + while(!deck.getTop().isEmpty()){ + } + + assertFalse(getCard.performEffect(player, Effect.CARD)); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoiceTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoiceTest.java new file mode 100644 index 00000000..fddac26e --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/GetSomethingChoiceTest.java @@ -0,0 +1,134 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class GetSomethingChoiceTest { + private GetSomethingChoice getSomethingChoice; + private Player player1; + private Player player2; + public static Player getCardPlayerMaker(Player player, int orderNum, InterfacePlayerBoardGameBoard board) { + PlayerOrder order = new PlayerOrder(orderNum, 4); + player = new Player() { + + public PlayerOrder playerOrder() { + return order; + } + public InterfacePlayerBoardGameBoard playerBoard() { + return board; + } + }; + return player; + } + + public static class GetCardPlayerBoardGameBoard implements InterfacePlayerBoardGameBoard { + private List resources = new ArrayList<>(); + private int figures = 0; + private List cards = new ArrayList<>(); + private List endOfGameEffects = new ArrayList<>(); + private List tools = new ArrayList<>(); + + public GetCardPlayerBoardGameBoard(int figureCount){ + this.figures = figureCount; + } + + @Override + public void giveEffect(Collection stuff) { + resources.addAll(stuff); + for(Effect effect : stuff){ + if(effect.equals(Effect.TOOL)){ + tools.add(1); + } + } + } + + @Override + public void giveFigure() { + figures++; + } + + @Override + public void giveEndOfGameEffect(Collection stuff) { + endOfGameEffects.addAll(stuff); + } + + @Override + public void giveCard(CivilizationCard card) { + cards.add(card); + } + + @Override + public boolean takeResources(Collection stuff) { + if (resources.containsAll(stuff)) { + resources.removeAll(stuff); + return true; + } + return false; + } + + @Override + public boolean takeFigures(int count) { + if (figures >= count) { + figures -= count; + return true; + } + return false; + } + + @Override + public boolean hasFigures(int count) { + return figures >= count; + } + + @Override + public boolean hasSufficientTools(int goal) { + for (int tool : tools) { + if (tool >= goal) { + return true; + } + } + return false; + } + + @Override + public Optional useTool(int idx) { + if (idx >= 0 && idx < tools.size()) { + return Optional.of(tools.remove(idx)); + } + return Optional.empty(); + } + } + + @Before + public void setUp(){ + player1 = getCardPlayerMaker(player1, 0, new GetCardPlayerBoardGameBoard(5)); + player2 = getCardPlayerMaker(player2, 1, new GetCardPlayerBoardGameBoard(5)); + } + + @Test + public void test1(){ + getSomethingChoice = new GetSomethingChoice(2); + assertTrue(getSomethingChoice.performEffect(player1, Effect.WOOD)); + assertTrue(getSomethingChoice.performEffect(player1, Effect.WOOD)); + assertFalse(getSomethingChoice.performEffect(player1, Effect.WOOD)); + } + + @Test + public void test2(){ + getSomethingChoice = new GetSomethingChoice(2); + assertTrue(getSomethingChoice.performEffect(player1, Effect.WOOD)); + assertFalse(getSomethingChoice.performEffect(player2, Effect.WOOD)); + assertTrue(getSomethingChoice.performEffect(player1, Effect.WOOD)); + assertFalse(getSomethingChoice.performEffect(player1, Effect.GOLD)); + } + + +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/RewardMenuTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/RewardMenuTest.java new file mode 100644 index 00000000..20798bc1 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/RewardMenuTest.java @@ -0,0 +1,144 @@ +package sk.uniba.fmph.dcs.game_board; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.*; + + +public class RewardMenuTest { + + private RewardMenu rewardMenu; + private Player mockPlayer1; + private Player mockPlayer2; + private PlayerBoard mockBoard1; + private PlayerBoard mockBoard2; + private Effect mockEffect1; + private Effect mockEffect2; + private static class MockPlayerBoard implements PlayerBoard { + private boolean hasFiguresResponse = true; + + @Override + public boolean hasFigures(int count) { + return hasFiguresResponse; + } + + @Override + public void giveEffect(Effect[] stuff) { + + } + + public void setHasFiguresResponse(boolean response) { + this.hasFiguresResponse = response; + } + } + + private static class MockPlayer implements Player { + private final PlayerOrder order; + private final PlayerBoard board; + + public MockPlayer(int orderNum, PlayerBoard board) { + this.order = new PlayerOrder(orderNum, 2); + this.board = board; + } + + @Override + public PlayerOrder playerOrder() { + return order; + } + + @Override + public InterfacePlayerBoardGameBoard playerBoard() { + return (InterfacePlayerBoardGameBoard) board; + } + } + + @Before + public void setUp() { + mockBoard1 = new MockPlayerBoard(); + mockBoard2 = new MockPlayerBoard(); + mockPlayer1 = new MockPlayer(0, mockBoard1); + mockPlayer2 = new MockPlayer(1, mockBoard2); + + // Mocking effects + mockEffect1 = Effect.WOOD; // Dummy effect + mockEffect2 = Effect.STONE; // Dummy effect + + // Creating a RewardMenu instance + rewardMenu = new RewardMenu(new Player[]{mockPlayer1, mockPlayer2}); + } + + @Test + public void testInitiate() { + rewardMenu.initiate(new Effect[]{mockEffect1, mockEffect2}); + + // Check if the rewards list was correctly initialized + assertTrue(rewardMenu.State().contains(mockEffect1.toString())); + assertTrue(rewardMenu.State().contains(mockEffect2.toString())); + } + + @Test + public void testTakeRewardSuccess() { + rewardMenu.initiate(new Effect[]{mockEffect1, mockEffect2}); + + boolean result = rewardMenu.takeReward(mockPlayer1.playerOrder(), mockEffect1); + assertTrue(result); + + // Check that the reward was removed from the list + assertFalse(rewardMenu.State().contains(mockEffect1.toString())); + + } + + @Test + public void testTakeRewardFailInvalidReward() { + rewardMenu.initiate(new Effect[]{mockEffect2}); + + boolean result = rewardMenu.takeReward(mockPlayer1.playerOrder(), mockEffect1); + assertFalse(result); + } + + @Test + public void testTakeRewardFailPlayerNotInQueue() { + rewardMenu.initiate(new Effect[]{mockEffect1}); + + rewardMenu.takeReward(mockPlayer1.playerOrder(), mockEffect1); + boolean result = rewardMenu.takeReward(mockPlayer1.playerOrder(), mockEffect1); + + assertFalse(result); + } + + @Test + public void testTryMakeActionNoActionPossible() { + HasAction result = rewardMenu.tryMakeAction(mockPlayer1.playerOrder()); + assertEquals(HasAction.NO_ACTION_POSSIBLE, result); + } + + @Test + public void testTryMakeActionAutomaticActionDone() { + rewardMenu.initiate(new Effect[]{mockEffect1}); + + HasAction result = rewardMenu.tryMakeAction(mockPlayer1.playerOrder()); + assertEquals(HasAction.AUTOMATIC_ACTION_DONE, result); + + } + + @Test + public void testTryMakeActionWaitingForPlayerAction() { + rewardMenu.initiate(new Effect[]{mockEffect1, mockEffect2}); + + HasAction result = rewardMenu.tryMakeAction(mockPlayer1.playerOrder()); + assertEquals(HasAction.WAITING_FOR_PLAYER_ACTION, result); + } + + @Test + public void testState() { + rewardMenu.initiate(new Effect[]{mockEffect1, mockEffect2}); + + String state = rewardMenu.State(); + + // Verify that the state contains the expected elements + assertTrue(state.contains("rewards")); + assertTrue(state.contains("players")); + assertTrue(state.contains("remainingPlayers")); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFieldsTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFieldsTest.java new file mode 100644 index 00000000..af49c8cb --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/ToolMakerHutFieldsTest.java @@ -0,0 +1,61 @@ +package sk.uniba.fmph.dcs.game_board; + +import org.junit.Before; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.Player; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ToolMakerHutFieldsTest { + private ToolMakerHutFields toolMakerHutFields; + private Player player1; + + private Player player2; + private Player player3; + private Player player4; + + @Before + public void setUp(){ + player1 = GetSomethingChoiceTest.getCardPlayerMaker(player1,0, new GetSomethingChoiceTest.GetCardPlayerBoardGameBoard(5)); + player2 = GetSomethingChoiceTest.getCardPlayerMaker(player2,1, new GetSomethingChoiceTest.GetCardPlayerBoardGameBoard(5)); + player3 = GetSomethingChoiceTest.getCardPlayerMaker(player3,2, new GetSomethingChoiceTest.GetCardPlayerBoardGameBoard(5)); + player4 = GetSomethingChoiceTest.getCardPlayerMaker(player4,3, new GetSomethingChoiceTest.GetCardPlayerBoardGameBoard(5)); + toolMakerHutFields = new ToolMakerHutFields(4); + } + + + @Test + public void test1(){ + assertTrue(toolMakerHutFields.canPlaceOnToolMaker(player1)); + assertTrue(toolMakerHutFields.placeOnToolMaker(player1)); + + + assertFalse(toolMakerHutFields.canPlaceOnToolMaker(player2)); + + assertTrue(toolMakerHutFields.canPlaceOnHut(player2)); + assertTrue(toolMakerHutFields.placeOnHut(player2)); + + assertFalse(toolMakerHutFields.canPlaceOnToolMaker(player3)); + + assertTrue(toolMakerHutFields.canPlaceOnFields(player3)); + assertTrue(toolMakerHutFields.placeOnFields(player3)); + + assertFalse(toolMakerHutFields.placeOnFields(player4)); + + + //Try to make action as other player + assertFalse(toolMakerHutFields.actionToolMaker(player4)); + assertFalse(toolMakerHutFields.actionHut(player4)); + assertFalse(toolMakerHutFields.actionFields(player4)); + //Player on toolMaker action + assertTrue(toolMakerHutFields.actionToolMaker(player1)); + assertTrue(toolMakerHutFields.actionHut(player2)); + assertTrue(toolMakerHutFields.actionFields(player3)); + + + toolMakerHutFields.newTurn(); + } + + +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_board/VariableBuildingTest.java b/src/test/java/sk/uniba/fmph/dcs/game_board/VariableBuildingTest.java new file mode 100644 index 00000000..eab22fe7 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_board/VariableBuildingTest.java @@ -0,0 +1,56 @@ +package sk.uniba.fmph.dcs.game_board; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import org.junit.Test; +import sk.uniba.fmph.dcs.stone_age.Effect; + +public class VariableBuildingTest { + + @Test + public void test_calculation_1(){ + List buildingResources = Arrays.asList(Effect.WOOD, Effect.WOOD, Effect.WOOD, Effect.GOLD); + + VariableBuilding building = new VariableBuilding(4, 2); + + assertEquals(building.build(buildingResources), OptionalInt.of(15)); + + assertEquals(building.state(), String.format("VariableBuilding[resources=%d,types=%d]", 4, 2)); + } + + @Test + public void test_calculation_2(){ + List buildingResources = Arrays.asList(Effect.WOOD, Effect.WOOD, Effect.GOLD); + + VariableBuilding building = new VariableBuilding(4, 2); + + assertEquals(building.build(buildingResources), OptionalInt.empty()); + + assertEquals(building.state(), String.format("VariableBuilding[resources=%d,types=%d]", 4, 2)); + } + @Test + public void test_calculation_3(){ + List buildingResources = Arrays.asList(Effect.WOOD, Effect.CLAY, Effect.STONE, Effect.GOLD); + + VariableBuilding building = new VariableBuilding(4, 4); + + assertEquals(building.build(buildingResources), OptionalInt.of(18)); + + assertEquals(building.state(), String.format("VariableBuilding[resources=%d,types=%d]", 4, 4)); + } + @Test + public void test_calculation_4(){ + List buildingResources = Arrays.asList(Effect.WOOD, Effect.CLAY, Effect.STONE); + + VariableBuilding building = new VariableBuilding(4, 4); + + assertEquals(building.build(buildingResources), OptionalInt.empty()); + + assertEquals(building.state(), String.format("VariableBuilding[resources=%d,types=%d]", 4, 4)); + } + +} diff --git a/src/test/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresStateTest.java b/src/test/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresStateTest.java new file mode 100644 index 00000000..a7b31dd9 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/game_phase_controller/PlaceFiguresStateTest.java @@ -0,0 +1,122 @@ +package sk.uniba.fmph.dcs.game_phase_controller; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Collection; +import java.util.ArrayList; + +import sk.uniba.fmph.dcs.stone_age.*; + +public class PlaceFiguresStateTest { + private PlaceFiguresState state; + private MockFigureLocation mockLocation; + private PlayerOrder player; + + private class MockFigureLocation implements InterfaceFigureLocation { + private HasAction nextTryResponse = HasAction.NO_ACTION_POSSIBLE; + private boolean nextPlaceResponse = false; + + @Override + public boolean placeFigures(PlayerOrder player, int figureCount) { + return nextPlaceResponse; + } + + @Override + public HasAction tryToPlaceFigures(PlayerOrder player, int count) { + return nextTryResponse; + } + + @Override + public ActionResult makeAction(PlayerOrder player, Collection inputResources, Collection outputResources) { + return ActionResult.FAILURE; + } + + @Override + public boolean skipAction(PlayerOrder player) { + return false; + } + + @Override + public HasAction tryToMakeAction(PlayerOrder player) { + return HasAction.NO_ACTION_POSSIBLE; + } + + @Override + public boolean newTurn() { + return false; + } + + public void setNextTryResponse(HasAction response) { + this.nextTryResponse = response; + } + + public void setNextPlaceResponse(boolean response) { + this.nextPlaceResponse = response; + } + } + + @Before + public void setUp() { + mockLocation = new MockFigureLocation(); + Map places = new HashMap<>(); + places.put(Location.HUNTING_GROUNDS, mockLocation); + state = new PlaceFiguresState(places); + player = new PlayerOrder(0, 2); + } + + @Test + public void testPlaceFiguresSuccess() { + mockLocation.setNextPlaceResponse(true); + assertEquals(ActionResult.ACTION_DONE, + state.placeFigures(player, Location.HUNTING_GROUNDS, 1)); + } + + @Test + public void testPlaceFiguresFailure() { + mockLocation.setNextPlaceResponse(false); + assertEquals(ActionResult.FAILURE, + state.placeFigures(player, Location.HUNTING_GROUNDS, 1)); + } + + @Test + public void testPlaceFiguresInvalidLocation() { + assertEquals(ActionResult.FAILURE, + state.placeFigures(player, Location.TOOL_MAKER, 1)); + } + + @Test + public void testTryToMakeAutomaticActionWaiting() { + mockLocation.setNextTryResponse(HasAction.WAITING_FOR_PLAYER_ACTION); + assertEquals(HasAction.WAITING_FOR_PLAYER_ACTION, + state.tryToMakeAutomaticAction(player)); + } + + @Test + public void testTryToMakeAutomaticActionNoAction() { + mockLocation.setNextTryResponse(HasAction.NO_ACTION_POSSIBLE); + assertEquals(HasAction.NO_ACTION_POSSIBLE, + state.tryToMakeAutomaticAction(player)); + } + + @Test + public void testInvalidActionsReturnFailure() { + assertEquals(ActionResult.FAILURE, + state.makeAction(player, Location.HUNTING_GROUNDS, new ArrayList<>(), new ArrayList<>())); + assertEquals(ActionResult.FAILURE, + state.skipAction(player, Location.HUNTING_GROUNDS)); + assertEquals(ActionResult.FAILURE, + state.useTools(player, 0)); + assertEquals(ActionResult.FAILURE, + state.noMoreToolsThisThrow(player)); + assertEquals(ActionResult.FAILURE, + state.feedTribe(player, new ArrayList<>())); + assertEquals(ActionResult.FAILURE, + state.doNotFeedThisTurn(player)); + assertEquals(ActionResult.FAILURE, + state.makeAllPlayersTakeARewardChoice(player, Effect.WOOD)); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCardsTest.java b/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCardsTest.java new file mode 100644 index 00000000..33c6625e --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerCivilizationCardsTest.java @@ -0,0 +1,45 @@ +package sk.uniba.fmph.dcs.player_board; + +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class PlayerCivilizationCardsTest { + private PlayerCivilizationCards playerCivilizationCards; + @Test + public void addEndOfGameEffects(){ + playerCivilizationCards = new PlayerCivilizationCards(); + + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Art}); + assertEquals(1, playerCivilizationCards.getEndOfGameEffectMap().get(EndOfGameEffect.Art)); + + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Art, EndOfGameEffect.Art}); + assertEquals(3,playerCivilizationCards.getEndOfGameEffectMap().get(EndOfGameEffect.Art)); + + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Farmer, EndOfGameEffect.Farmer}); + assertEquals(2, playerCivilizationCards.getEndOfGameEffectMap().get(EndOfGameEffect.Farmer)); + assertEquals(3,playerCivilizationCards.getEndOfGameEffectMap().get(EndOfGameEffect.Art)); + } + + @Test + public void countEndOfGamePoints(){ + playerCivilizationCards = new PlayerCivilizationCards(); + + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Farmer, EndOfGameEffect.Farmer, EndOfGameEffect.Farmer}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Builder, EndOfGameEffect.Builder}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Shaman, EndOfGameEffect.Shaman,EndOfGameEffect.Shaman}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.ToolMaker}); + + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Art, EndOfGameEffect.Art}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Music, EndOfGameEffect.Music}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Pottery}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Sundial}); + playerCivilizationCards.addEndOfGameEffects(new EndOfGameEffect[]{EndOfGameEffect.Transport}); + + assertEquals(82, playerCivilizationCards.calculateEndOfGameCivilizationCardPoints(3,8,4,9)); + + + } + + +} \ No newline at end of file diff --git a/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerToolsTest.java b/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerToolsTest.java new file mode 100644 index 00000000..cc09d706 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/player_board/PlayerToolsTest.java @@ -0,0 +1,133 @@ +package sk.uniba.fmph.dcs.player_board; + +import org.junit.Test; +import sk.uniba.fmph.dcs.player_board.PlayerTools; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class PlayerToolsTest { + + private PlayerTools playerTools; + + + @Test + public void tryToAddOneTool() throws NoSuchFieldException, IllegalAccessException { + playerTools = new PlayerTools(); + + playerTools.addTool(); + assertEquals(1, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertArrayEquals(new int[]{1, 0, 0}, playerTools.getTools()); + } + + @Test + public void tryToAddSomeTools(){ + playerTools = new PlayerTools(); + + for(int i = 0; i < 7; i++){ + playerTools.addTool(); + } + assertEquals(7, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + } + + @Test + public void addMoreThanPossibleTools(){ + + playerTools = new PlayerTools(); + + for(int i = 0; i < 15; i++){ + playerTools.addTool(); + } + assertEquals(12, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertArrayEquals(new int[]{4, 4, 4}, playerTools.getTools()); + } + + @Test + public void addSingleUseTool(){ + playerTools = new PlayerTools(); + + for(int i = 0; i < 7; i++){ + playerTools.addTool(); + } + + playerTools.addSingleUseTool(2); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertEquals(9, playerTools.getTotalToolsCount()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + + List expected = new ArrayList<>(); + expected.add(2); + assertEquals(expected,playerTools.getAdditionalTools()); + + //Add another SingleUseTool with power 3 + playerTools.addSingleUseTool(3); + assertEquals(12, playerTools.getRoundToolsCount()); + assertEquals(12, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + + expected.add(3); + assertEquals(expected,playerTools.getAdditionalTools()); + + } + + @Test + public void useTool(){ + playerTools = new PlayerTools(); + + for(int i = 0; i < 7; i++){ + playerTools.addTool(); + } + + playerTools.useTool(2); + assertEquals(7, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, true}, playerTools.getUsedTools()); + assertEquals(5, playerTools.getRoundToolsCount()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + } + + @Test + public void useSingleUseTool(){ + playerTools = new PlayerTools(); + + for(int i = 0; i < 7; i++){ + playerTools.addTool(); + } + + playerTools.addSingleUseTool(2); + playerTools.addSingleUseTool(3); + assertEquals(12, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + List expected = new ArrayList<>(); + expected.add(2); + expected.add(3); + assertEquals(expected,playerTools.getAdditionalTools()); + + + playerTools.useTool(4); + expected.remove(1); + + assertEquals(expected,playerTools.getAdditionalTools()); + assertEquals(9, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertEquals(9, playerTools.getRoundToolsCount()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + + playerTools.useTool(3); + expected.remove(0); + + assertEquals(expected,playerTools.getAdditionalTools()); + assertEquals(7, playerTools.getTotalToolsCount()); + assertArrayEquals(new boolean[]{false, false, false}, playerTools.getUsedTools()); + assertEquals(7, playerTools.getRoundToolsCount()); + assertArrayEquals(new int[]{3, 2, 2}, playerTools.getTools()); + } + + +} \ No newline at end of file diff --git a/src/test/java/sk/uniba/fmph/dcs/stone_age/ResourceSourceTest.java b/src/test/java/sk/uniba/fmph/dcs/stone_age/ResourceSourceTest.java new file mode 100644 index 00000000..36aac342 --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/stone_age/ResourceSourceTest.java @@ -0,0 +1,111 @@ +package sk.uniba.fmph.dcs.stone_age; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; + +public class ResourceSourceTest { + private ResourceSource source; + private Player mockPlayer1; + private Player mockPlayer2; + private PlayerBoard mockBoard1; + private PlayerBoard mockBoard2; + + private class MockPlayerBoard implements PlayerBoard { + private boolean hasFiguresResponse = true; + + @Override + public boolean hasFigures(int count) { + return hasFiguresResponse; + } + + public void setHasFiguresResponse(boolean response) { + this.hasFiguresResponse = response; + } + } + + private class MockPlayer implements Player { + private final PlayerOrder order; + private final PlayerBoard board; + + public MockPlayer(int orderNum, PlayerBoard board) { + this.order = new PlayerOrder(orderNum, 2); + this.board = board; + } + + @Override + public PlayerOrder playerOrder() { + return order; + } + + @Override + public PlayerBoard playerBoard() { + return board; + } + } + + @Before + public void setUp() { + mockBoard1 = new MockPlayerBoard(); + mockBoard2 = new MockPlayerBoard(); + mockPlayer1 = new MockPlayer(0, mockBoard1); + mockPlayer2 = new MockPlayer(1, mockBoard2); + source = new ResourceSource("Forest", Effect.WOOD, 7, 2); + } + + @Test + public void testPlaceFiguresSuccess() { + assertTrue(source.placeFigures(mockPlayer1, 2)); + } + + @Test + public void testPlaceFiguresNoFigures() { + ((MockPlayerBoard)mockBoard1).setHasFiguresResponse(false); + assertFalse(source.placeFigures(mockPlayer1, 2)); + } + + @Test + public void testPlaceFiguresExceedMax() { + assertTrue(source.placeFigures(mockPlayer1, 7)); + assertFalse(source.placeFigures(mockPlayer2, 1)); + } + + @Test + public void testPlaceFiguresExceedColors() { + assertTrue(source.placeFigures(mockPlayer1, 2)); + assertTrue(source.placeFigures(mockPlayer2, 2)); + MockPlayer mockPlayer3 = new MockPlayer(2, new MockPlayerBoard()); + assertFalse(source.placeFigures(mockPlayer3, 2)); + } + + @Test + public void testMakeActionSuccess() { + source.placeFigures(mockPlayer1, 2); + Collection input = new ArrayList<>(); + Collection output = new ArrayList<>(); + output.add(Effect.WOOD); + output.add(Effect.WOOD); + assertEquals(ActionResult.ACTION_DONE_WAIT_FOR_TOOL_USE, + source.makeAction(mockPlayer1, input, output)); + } + + @Test + public void testMakeActionWrongResourceCount() { + source.placeFigures(mockPlayer1, 2); + Collection input = new ArrayList<>(); + Collection output = new ArrayList<>(); + output.add(Effect.WOOD); + assertEquals(ActionResult.FAILURE, + source.makeAction(mockPlayer1, input, output)); + } + + @Test + public void testNewTurnClearsFigures() { + assertTrue(source.placeFigures(mockPlayer1, 2)); + source.newTurn(); + assertTrue(source.placeFigures(mockPlayer1, 2)); + } +} diff --git a/src/test/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatusTest.java b/src/test/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatusTest.java new file mode 100644 index 00000000..dcb1459b --- /dev/null +++ b/src/test/java/sk/uniba/fmph/dcs/stone_age/TribeFedStatusTest.java @@ -0,0 +1,76 @@ +package sk.uniba.fmph.dcs.stone_age; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; + +public class TribeFedStatusTest { + private TribeFedStatus status; + + @Before + public void setUp() { + status = new TribeFedStatus(); + } + + @Test + public void testInitialState() { + assertFalse(status.isTribeFed()); + assertEquals(0, status.getFields()); + } + + @Test + public void testAddField() { + status.addField(); + assertEquals(1, status.getFields()); + } + + @Test + public void testAddFieldLimit() { + for(int i = 0; i < 12; i++) { + status.addField(); + } + assertEquals(10, status.getFields()); + } + + @Test + public void testFeedTribeWithFood() { + Collection food = new ArrayList<>(); + food.add(Effect.FOOD); + assertTrue(status.feedTribe(food)); + assertTrue(status.isTribeFed()); + } + + @Test + public void testFeedTribeWithInsufficientFood() { + Collection food = new ArrayList<>(); + assertFalse(status.feedTribe(food)); + assertFalse(status.isTribeFed()); + } + + @Test + public void testNewTurnResetsFedStatus() { + Collection food = new ArrayList<>(); + food.add(Effect.FOOD); + status.feedTribe(food); + assertTrue(status.isTribeFed()); + + status.newTurn(); + assertFalse(status.isTribeFed()); + } + + @Test + public void testDoNotFeedThisTurn() { + assertTrue(status.doNotFeedThisTurn()); + assertTrue(status.isTribeFed()); + } + + @Test + public void testFeedTribeIfEnoughFood() { + // This would need integration with PlayerResourcesAndFood + // to test properly, as it depends on available food resources + assertFalse(status.feedTribeIfEnoughFood()); + } +}