summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BestPriceCalculator.java96
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BoughtResources.java32
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourcePool.java8
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/resources/Resources.java13
-rw-r--r--game-engine/src/test/java/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.java37
5 files changed, 149 insertions, 37 deletions
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BestPriceCalculator.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BestPriceCalculator.java
index 0cae3fe2..bde6dcb9 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BestPriceCalculator.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BestPriceCalculator.java
@@ -10,13 +10,26 @@ import org.luxons.sevenwonders.game.boards.Board;
public class BestPriceCalculator {
- private final Resources resources;
+ private final Resources resourcesLeftToPay;
private final List<ResourcePool> pools;
- private BestPriceCalculator(Resources resources, Table table, int playerIndex) {
- this.resources = resources;
+ private final List<BoughtResources> boughtResources;
+
+ private int pricePaid;
+
+ private List<BoughtResources> bestSolution;
+
+ private int bestPrice;
+
+ private BestPriceCalculator(Resources resourcesToPay, Table table, int playerIndex) {
+ Board board = table.getBoard(playerIndex);
+ this.resourcesLeftToPay = resourcesToPay.minus(board.getProduction().getFixedResources());
this.pools = createResourcePools(table, playerIndex);
+ this.boughtResources = new ArrayList<>();
+ this.pricePaid = 0;
+ this.bestSolution = null;
+ this.bestPrice = Integer.MAX_VALUE;
}
private static List<ResourcePool> createResourcePools(Table table, int playerIndex) {
@@ -25,53 +38,76 @@ public class BestPriceCalculator {
Board board = table.getBoard(playerIndex);
TradingRules rules = board.getTradingRules();
- pools.add(new ResourcePool(board.getProduction(), null, rules));
+ // we only take alternative resources here, because fixed resources were already removed for optimization
+ Set<Set<ResourceType>> ownBoardChoices = board.getProduction().getAlternativeResources();
+ pools.add(new ResourcePool(null, rules, ownBoardChoices));
for (Provider provider : providers) {
Board providerBoard = table.getBoard(playerIndex, provider.getBoardPosition());
- ResourcePool pool = new ResourcePool(providerBoard.getPublicProduction(), provider, rules);
+ ResourcePool pool = new ResourcePool(provider, rules, providerBoard.getPublicProduction().asChoices());
pools.add(pool);
}
return pools;
}
public static int bestPrice(Resources resources, Table table, int playerIndex) {
- Board board = table.getBoard(playerIndex);
- Resources leftToPay = resources.minus(board.getProduction().getFixedResources());
- return new BestPriceCalculator(leftToPay, table, playerIndex).bestPrice();
+ BestPriceCalculator bestPriceCalculator = new BestPriceCalculator(resources, table, playerIndex);
+ bestPriceCalculator.computePossibilities();
+ return bestPriceCalculator.bestPrice;
}
- private int bestPrice() {
- if (resources.isEmpty()) {
- return 0;
+ public static List<BoughtResources> bestSolution(Resources resources, Table table, int playerIndex) {
+ BestPriceCalculator bestPriceCalculator = new BestPriceCalculator(resources, table, playerIndex);
+ bestPriceCalculator.computePossibilities();
+ return bestPriceCalculator.bestSolution;
+ }
+
+ private void computePossibilities() {
+ if (resourcesLeftToPay.isEmpty()) {
+ updateBestSolutionIfNeeded();
+ return;
}
- int currentMinPrice = Integer.MAX_VALUE;
for (ResourceType type : ResourceType.values()) {
- if (resources.getQuantity(type) > 0) {
- int minPriceUsingOwnResource = bestPriceWithout(type);
- currentMinPrice = Math.min(currentMinPrice, minPriceUsingOwnResource);
+ if (resourcesLeftToPay.getQuantity(type) > 0) {
+ for (ResourcePool pool : pools) {
+ boolean ownResource = pool.getProvider() == null;
+ if (ownResource) {
+ resourcesLeftToPay.remove(type, 1);
+ computePossibilitiesWhenUsing(type, pool);
+ resourcesLeftToPay.add(type, 1);
+ continue;
+ }
+ BoughtResources boughtRes = new BoughtResources(pool.getProvider(), Resources.of(type));
+ int cost = pool.getCost(type);
+
+ resourcesLeftToPay.remove(type, 1);
+ boughtResources.add(boughtRes);
+ pricePaid += cost;
+ computePossibilitiesWhenUsing(type, pool);
+ pricePaid -= cost;
+ boughtResources.remove(boughtRes);
+ resourcesLeftToPay.add(type, 1);
+ }
}
}
- return currentMinPrice;
}
- private int bestPriceWithout(ResourceType type) {
- resources.remove(type, 1);
- int currentMinPrice = Integer.MAX_VALUE;
- for (ResourcePool pool : pools) {
- int resCostInPool = pool.getCost(type);
- for (Set<ResourceType> choice : pool.getChoices()) {
- if (choice.contains(type)) {
- Set<ResourceType> temp = EnumSet.copyOf(choice);
- choice.clear();
- currentMinPrice = Math.min(currentMinPrice, bestPrice() + resCostInPool);
- choice.addAll(temp);
- }
+ private void computePossibilitiesWhenUsing(ResourceType type, ResourcePool pool) {
+ for (Set<ResourceType> choice : pool.getChoices()) {
+ if (choice.contains(type)) {
+ Set<ResourceType> temp = EnumSet.copyOf(choice);
+ choice.clear();
+ computePossibilities();
+ choice.addAll(temp);
}
}
- resources.add(type, 1);
- return currentMinPrice;
}
+ private void updateBestSolutionIfNeeded() {
+ if (pricePaid < bestPrice) {
+ bestPrice = pricePaid;
+ bestSolution = new ArrayList<>(boughtResources);
+ }
+ }
}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BoughtResources.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BoughtResources.java
index ec261c8c..71293190 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BoughtResources.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/BoughtResources.java
@@ -1,11 +1,21 @@
package org.luxons.sevenwonders.game.resources;
+import java.util.Objects;
+
public class BoughtResources {
private Provider provider;
private Resources resources;
+ public BoughtResources() {
+ }
+
+ public BoughtResources(Provider provider, Resources resources) {
+ this.provider = provider;
+ this.resources = resources;
+ }
+
public Provider getProvider() {
return provider;
}
@@ -21,4 +31,26 @@ public class BoughtResources {
public void setResources(Resources resources) {
this.resources = resources;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BoughtResources that = (BoughtResources) o;
+ return provider == that.provider && Objects.equals(resources, that.resources);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(provider, resources);
+ }
+
+ @Override
+ public String toString() {
+ return "BoughtResources{" + "provider=" + provider + ", resources=" + resources + '}';
+ }
}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourcePool.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourcePool.java
index ad368066..75a02afc 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourcePool.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourcePool.java
@@ -10,8 +10,8 @@ class ResourcePool {
private final TradingRules rules;
- ResourcePool(Production production, Provider provider, TradingRules rules) {
- this.choices = production.asChoices();
+ ResourcePool(Provider provider, TradingRules rules, Set<Set<ResourceType>> choices) {
+ this.choices = choices;
this.provider = provider;
this.rules = rules;
}
@@ -20,6 +20,10 @@ class ResourcePool {
return choices;
}
+ Provider getProvider() {
+ return provider;
+ }
+
int getCost(ResourceType type) {
if (provider == null) {
return 0;
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/Resources.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/Resources.java
index 04278fea..1b263760 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/Resources.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/Resources.java
@@ -13,6 +13,14 @@ public class Resources {
private final Map<ResourceType, Integer> quantities = new EnumMap<>(ResourceType.class);
+ public static Resources of(ResourceType... types) {
+ Resources resources = new Resources();
+ for (ResourceType type : types) {
+ resources.add(type, 1);
+ }
+ return resources;
+ }
+
public void add(ResourceType type, int quantity) {
quantities.merge(type, quantity, (x, y) -> x + y);
}
@@ -88,4 +96,9 @@ public class Resources {
public int hashCode() {
return Objects.hash(quantities);
}
+
+ @Override
+ public String toString() {
+ return "Resources{" + "quantities=" + quantities + '}';
+ }
}
diff --git a/game-engine/src/test/java/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.java b/game-engine/src/test/java/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.java
index e6438789..9f43ec6b 100644
--- a/game-engine/src/test/java/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.java
+++ b/game-engine/src/test/java/org/luxons/sevenwonders/game/resources/BestPriceCalculatorTest.java
@@ -1,6 +1,7 @@
package org.luxons.sevenwonders.game.resources;
import java.util.Arrays;
+import java.util.Collections;
import org.junit.Test;
import org.luxons.sevenwonders.game.api.Table;
@@ -16,6 +17,7 @@ public class BestPriceCalculatorTest {
Table table = TestUtils.createTable(3);
Resources resources = new Resources();
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 0));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 0));
}
@Test
@@ -26,10 +28,16 @@ public class BestPriceCalculatorTest {
Table table = new Table(Arrays.asList(main, right, left));
Resources resources = new Resources();
- resources.add(ResourceType.STONE, 1);
- assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 0));
- assertEquals(2, BestPriceCalculator.bestPrice(resources, table, 1));
- assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 2));
+ resources.add(ResourceType.STONE, 2);
+ assertEquals(2, BestPriceCalculator.bestPrice(resources, table, 0));
+ assertEquals(4, BestPriceCalculator.bestPrice(resources, table, 1));
+ assertEquals(2, BestPriceCalculator.bestPrice(resources, table, 2));
+
+ BoughtResources stoneLeft = new BoughtResources(Provider.LEFT_PLAYER, Resources.of(ResourceType.STONE));
+ BoughtResources stoneRight = new BoughtResources(Provider.RIGHT_PLAYER, Resources.of(ResourceType.STONE));
+ assertEquals(Collections.singletonList(stoneLeft), BestPriceCalculator.bestSolution(resources, table, 0));
+ assertEquals(Arrays.asList(stoneLeft, stoneRight), BestPriceCalculator.bestSolution(resources, table, 1));
+ assertEquals(Collections.singletonList(stoneRight), BestPriceCalculator.bestSolution(resources, table, 2));
}
@Test
@@ -48,6 +56,13 @@ public class BestPriceCalculatorTest {
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 1));
assertEquals(2, BestPriceCalculator.bestPrice(resources, table, 2));
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 3));
+
+ BoughtResources woodLeft = new BoughtResources(Provider.LEFT_PLAYER, Resources.of(ResourceType.WOOD));
+ BoughtResources woodRight = new BoughtResources(Provider.RIGHT_PLAYER, Resources.of(ResourceType.WOOD));
+ assertEquals(Collections.singletonList(woodRight), BestPriceCalculator.bestSolution(resources, table, 0));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 1));
+ assertEquals(Collections.singletonList(woodLeft), BestPriceCalculator.bestSolution(resources, table, 2));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 3));
}
@Test
@@ -68,6 +83,11 @@ public class BestPriceCalculatorTest {
assertEquals(1, BestPriceCalculator.bestPrice(resources, table, 0));
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 1));
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 2));
+
+ BoughtResources woodRight = new BoughtResources(Provider.RIGHT_PLAYER, Resources.of(ResourceType.WOOD));
+ assertEquals(Collections.singletonList(woodRight), BestPriceCalculator.bestSolution(resources, table, 0));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 1));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 2));
}
@Test
@@ -75,7 +95,7 @@ public class BestPriceCalculatorTest {
Board left = TestUtils.createBoard(ResourceType.WOOD);
Board main = TestUtils.createBoard(ResourceType.WOOD);
- main.getProduction().addChoice(ResourceType.ORE, ResourceType.CLAY);
+ main.getProduction().addChoice(ResourceType.CLAY, ResourceType.ORE);
main.getTradingRules().setCost(ResourceType.CLAY, Provider.RIGHT_PLAYER, 1);
Board right = TestUtils.createBoard(ResourceType.WOOD);
@@ -92,5 +112,12 @@ public class BestPriceCalculatorTest {
assertEquals(1, BestPriceCalculator.bestPrice(resources, table, 0));
assertEquals(0, BestPriceCalculator.bestPrice(resources, table, 1));
assertEquals(4, BestPriceCalculator.bestPrice(resources, table, 2));
+
+ BoughtResources oreLeft = new BoughtResources(Provider.LEFT_PLAYER, Resources.of(ResourceType.ORE));
+ BoughtResources clayLeft = new BoughtResources(Provider.LEFT_PLAYER, Resources.of(ResourceType.CLAY));
+ BoughtResources clayRight = new BoughtResources(Provider.RIGHT_PLAYER, Resources.of(ResourceType.CLAY));
+ assertEquals(Collections.singletonList(clayRight), BestPriceCalculator.bestSolution(resources, table, 0));
+ assertEquals(Collections.emptyList(), BestPriceCalculator.bestSolution(resources, table, 1));
+ assertEquals(Arrays.asList(oreLeft, clayLeft), BestPriceCalculator.bestSolution(resources, table, 2));
}
}
bgstack15