Skip to content

Commit

Permalink
PlacePanel: Limit max based on unit requirements. (#11887)
Browse files Browse the repository at this point in the history
This change caps the max for each unit type in the place panel to the max that can be placed for that type when considering "units which require units" logic (i.e. factory types).

This partially prevents the error "Cannot place more units which require units, than production capacity of territories with the required units" after making the selection in the place panel.

Note: That error can still happen when selecting multiple units of different types that surpass the max.
  • Loading branch information
asvitkine authored Aug 21, 2023
1 parent f76d73e commit 26d7866
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.triplea.java.collections.CollectionUtils;
import org.triplea.java.collections.IntegerMap;
Expand Down Expand Up @@ -974,6 +975,11 @@ protected Collection<Unit> getUnitsToBePlaced(
} else {
placeableUnits2 = placeableUnits;
}
// Limit count of each unit type to the max that can be placed based on unit requirements.
for (UnitType ut : placeableUnits2.stream().map(Unit::getType).collect(Collectors.toSet())) {
var unitsOfType = CollectionUtils.getMatches(placeableUnits2, Matches.unitIsOfType(ut));
placeableUnits2.removeAll(getUnitsThatCantBePlacedThatRequireUnits(unitsOfType, to));
}
// now check stacking limits
return UnitStackingLimitFilter.filterUnits(
placeableUnits2, PLACEMENT_LIMIT, player, to, produced.getOrDefault(to, List.of()));
Expand Down Expand Up @@ -1471,20 +1477,25 @@ private Predicate<Unit> unitWhichRequiresUnitsHasRequiredUnits(

private boolean getCanAllUnitsWithRequiresUnitsBePlacedCorrectly(
final Collection<Unit> units, final Territory to) {
return getUnitsThatCantBePlacedThatRequireUnits(units, to).isEmpty();
}

private Collection<Unit> getUnitsThatCantBePlacedThatRequireUnits(
final Collection<Unit> units, final Territory to) {
if (!Properties.getUnitPlacementRestrictions(getData().getProperties())
|| units.stream().noneMatch(Matches.unitRequiresUnitsOnCreation())) {
return true;
return List.of();
}
final IntegerMap<Territory> producersMap = getMaxUnitsToBePlacedMap(units, to, player);
final List<Territory> producers = getAllProducers(to, player, units);
if (producers.isEmpty()) {
return false;
return units;
}
producers.sort(getBestProducerComparator(to, units, player));
final Collection<Unit> unitsLeftToPlace = new ArrayList<>(units);
for (final Territory t : producers) {
if (unitsLeftToPlace.isEmpty()) {
return true;
return List.of();
}
final int productionHere = producersMap.getInt(t);
final List<Unit> canBePlacedHere =
Expand All @@ -1499,7 +1510,7 @@ private boolean getCanAllUnitsWithRequiresUnitsBePlacedCorrectly(
CollectionUtils.getNMatches(canBePlacedHere, productionHere, it -> true);
unitsLeftToPlace.removeAll(placedHere);
}
return unitsLeftToPlace.isEmpty();
return unitsLeftToPlace;
}

private Comparator<Territory> getBestProducerComparator(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package games.strategy.triplea.delegate;

import static games.strategy.triplea.Constants.DAMAGE_FROM_BOMBING_DONE_TO_UNITS_INSTEAD_OF_TERRITORIES;
import static games.strategy.triplea.Constants.UNIT_PLACEMENT_RESTRICTIONS;
import static games.strategy.triplea.delegate.GameDataTestUtil.unitType;
import static games.strategy.triplea.delegate.Matches.unitIsOfType;
import static games.strategy.triplea.delegate.MockDelegateBridge.newDelegateBridge;
import static games.strategy.triplea.delegate.remote.IAbstractPlaceDelegate.BidMode.NOT_BID;
Expand Down Expand Up @@ -145,6 +148,26 @@ void testCanNotProduceThatManyUnits() {
assertEquals(2, response.getMaxUnits());
}

@Test
void testCanNotProduceThatManyUnitsDueToRequiresUnits() {
gameData.getProperties().set(UNIT_PLACEMENT_RESTRICTIONS, true);
// Needed for canProduceXUnits to work. (!)
gameData.getProperties().set(DAMAGE_FROM_BOMBING_DONE_TO_UNITS_INSTEAD_OF_TERRITORIES, true);
final var factory2 = unitType("factory2", gameData);
final var infantry2 = unitType("infantry2", gameData);

final var threeInfantry2 = create(british, infantry2, 3);
final var fourInfantry2 = create(british, infantry2, 4);

uk.getUnitCollection().clear();
assertError(delegate.canUnitsBePlaced(uk, threeInfantry2, british));
uk.getUnitCollection().addAll(create(british, factory2, 1));
assertValid(delegate.canUnitsBePlaced(uk, threeInfantry2, british));
assertError(delegate.canUnitsBePlaced(uk, fourInfantry2, british));
final PlaceableUnits response = delegate.getPlaceableUnits(fourInfantry2, uk);
assertThat(response.getUnits(), hasSize(3));
}

@Test
void testAlreadyProducedUnits() {
delegate.setProduced(Map.of(westCanada, create(british, infantry, 2)));
Expand Down
16 changes: 16 additions & 0 deletions game-app/game-core/src/test/resources/DelegateTest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@
<unit name="submarine"/>
<unit name="factory"/>
<unit name="aaGun"/>
<unit name="factory2"/>
<unit name="infantry2"/>
</unitList>
<gamePlay>
<delegate name="initDelegate" javaClass="games.strategy.triplea.delegate.InitializationDelegate"/>
Expand Down Expand Up @@ -718,6 +720,20 @@
type="unitType">
<option name="isFactory" value="true"/>
</attachment>


<attachment name="unitAttachment" attachTo="factory2" javaClass="UnitAttachment" type="unitType">
<option name="isFactory" value="true"/>
<option name="canProduceXUnits" value="3"/>
</attachment>
<attachment name="unitAttachment" attachTo="infantry2" javaClass="UnitAttachment" type="unitType">
<option name="movement" value="1"/>
<option name="transportCost" value="1"/>
<option name="attack" value="1"/>
<option name="defense" value="2"/>
<option name="requiresUnits" value="factory2"/>
</attachment>

<attachment name="unitAttachment"
attachTo="aaGun"
javaClass="UnitAttachment"
Expand Down

0 comments on commit 26d7866

Please sign in to comment.