use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class AbstractPlaceDelegate method canUnitsBePlaced.
public String canUnitsBePlaced(final Territory to, final Collection<Unit> units, final PlayerID player) {
final Collection<Unit> allowedUnits = getUnitsToBePlaced(to, units, player);
if (allowedUnits == null || !allowedUnits.containsAll(units)) {
return "Cannot place these units in " + to.getName();
}
final IntegerMap<String> constructionMap = howManyOfEachConstructionCanPlace(to, to, units, player);
for (final Unit currentUnit : CollectionUtils.getMatches(units, Matches.unitIsConstruction())) {
final UnitAttachment ua = UnitAttachment.get(currentUnit.getType());
/*
* if (ua.getIsFactory() && !ua.getIsConstruction())
* constructionMap.add("factory", -1);
* else
*/
constructionMap.add(ua.getConstructionType(), -1);
}
if (!constructionMap.isPositive()) {
return "Too many constructions in " + to.getName();
}
final List<Territory> capitalsListOwned = new ArrayList<>(TerritoryAttachment.getAllCurrentlyOwnedCapitals(player, getData()));
if (!capitalsListOwned.contains(to) && isPlacementInCapitalRestricted(player)) {
return "Cannot place these units outside of the capital";
}
if (to.isWater()) {
final String canLand = validateNewAirCanLandOnCarriers(to, units);
if (canLand != null) {
return canLand;
}
} else {
// make sure we own the territory
if (!to.getOwner().equals(player)) {
if (GameStepPropertiesHelper.isBid(getData())) {
final PlayerAttachment pa = PlayerAttachment.get(to.getOwner());
if ((pa == null || pa.getGiveUnitControl() == null || !pa.getGiveUnitControl().contains(player)) && !to.getUnits().anyMatch(Matches.unitIsOwnedBy(player))) {
return "You don't own " + to.getName();
}
} else {
return "You don't own " + to.getName();
}
}
// make sure all units are land
if (units.isEmpty() || !units.stream().allMatch(Matches.unitIsNotSea())) {
return "Cant place sea units on land";
}
}
// make sure we can place consuming units
if (!canWeConsumeUnits(units, to, false, null)) {
return "Not Enough Units To Upgrade or Be Consumed";
}
// now check for stacking limits
final Collection<UnitType> typesAlreadyChecked = new ArrayList<>();
for (final Unit currentUnit : units) {
final UnitType ut = currentUnit.getType();
if (typesAlreadyChecked.contains(ut)) {
continue;
}
typesAlreadyChecked.add(ut);
final int maxForThisType = UnitAttachment.getMaximumNumberOfThisUnitTypeToReachStackingLimit("placementLimit", ut, to, player, getData());
if (CollectionUtils.countMatches(units, Matches.unitIsOfType(ut)) > maxForThisType) {
return "UnitType " + ut.getName() + " is over stacking limit of " + maxForThisType;
}
}
if (!PlayerAttachment.getCanTheseUnitsMoveWithoutViolatingStackingLimit("placementLimit", units, to, player, getData())) {
return "Units Cannot Go Over Stacking Limit";
}
// now return null (valid placement) if we have placement restrictions disabled in game options
if (!isUnitPlacementRestrictions()) {
return null;
}
// account for any unit placement restrictions by territory
for (final Unit currentUnit : units) {
final UnitAttachment ua = UnitAttachment.get(currentUnit.getType());
// Can be null!
final TerritoryAttachment ta = TerritoryAttachment.get(to);
if (ua.getCanOnlyBePlacedInTerritoryValuedAtX() != -1 && ua.getCanOnlyBePlacedInTerritoryValuedAtX() > (ta == null ? 0 : ta.getProduction())) {
return "Cannot place these units in " + to.getName() + " due to Unit Placement Restrictions on Territory Value";
}
final String[] terrs = ua.getUnitPlacementRestrictions();
final Collection<Territory> listedTerrs = getListedTerritories(terrs);
if (listedTerrs.contains(to)) {
return "Cannot place these units in " + to.getName() + " due to Unit Placement Restrictions";
}
if (Matches.unitCanOnlyPlaceInOriginalTerritories().test(currentUnit) && !Matches.territoryIsOriginallyOwnedBy(player).test(to)) {
return "Cannot place these units in " + to.getName() + " as territory is not originally owned";
}
}
return null;
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class AbstractPlaceDelegate method canProduce.
/**
* Tests if this territory can produce units. (Does not check if it has space left to do so)
*
* @param producer
* - Territory doing the producing.
* @param to
* - Territory to be placed in.
* @param units
* - Units to be placed.
* @param player
* - Player doing the placing.
* @param simpleCheck
* - If true you return true even if a factory is not present. Used when you do not want an infinite loop
* (getAllProducers ->
* canProduce -> howManyOfEachConstructionCanPlace -> getAllProducers -> etc)
* @return - null if allowed to produce, otherwise an error String.
*/
protected String canProduce(final Territory producer, final Territory to, final Collection<Unit> units, final PlayerID player, final boolean simpleCheck) {
// units can be null if we are just testing the territory itself...
final Collection<Unit> testUnits = (units == null ? new ArrayList<>() : units);
final boolean canProduceInConquered = isPlacementAllowedInCapturedTerritory(player);
if (!producer.getOwner().equals(player)) {
// sea constructions require either owning the sea zone or owning a surrounding land territory
if (producer.isWater() && testUnits.stream().anyMatch(Matches.unitIsSea().and(Matches.unitIsConstruction()))) {
boolean ownedNeighbor = false;
for (final Territory current : getData().getMap().getNeighbors(to, Matches.territoryIsLand())) {
if (current.getOwner().equals(player) && (canProduceInConquered || !wasConquered(current))) {
ownedNeighbor = true;
break;
}
}
if (!ownedNeighbor) {
return producer.getName() + " is not owned by you, and you have no owned neighbors which can produce";
}
} else {
return producer.getName() + " is not owned by you";
}
}
// make sure the territory wasnt conquered this turn
if (!canProduceInConquered && wasConquered(producer)) {
return producer.getName() + " was conquered this turn and cannot produce till next turn";
}
if (isPlayerAllowedToPlacementAnyTerritoryOwnedLand(player) && Matches.territoryIsLand().test(to) && Matches.isTerritoryOwnedBy(player).test(to)) {
return null;
}
if (isPlayerAllowedToPlacementAnySeaZoneByOwnedLand(player) && Matches.territoryIsWater().test(to) && Matches.isTerritoryOwnedBy(player).test(producer)) {
return null;
}
if (simpleCheck) {
return null;
}
// make sure some unit has fullfilled requiresUnits requirements
if (isUnitPlacementRestrictions() && !testUnits.isEmpty() && testUnits.stream().noneMatch(unitWhichRequiresUnitsHasRequiredUnits(producer, true))) {
return "You do not have the required units to build in " + producer.getName();
}
if (to.isWater() && (!isWW2V2() && !isUnitPlacementInEnemySeas()) && to.getUnits().anyMatch(Matches.enemyUnit(player, getData()))) {
return "Cannot place sea units with enemy naval units";
}
// make sure there is a factory
if (wasOwnedUnitThatCanProduceUnitsOrIsFactoryInTerritoryAtStartOfStep(producer, player)) {
return null;
}
// check to see if we are producing a factory or construction
if (testUnits.stream().anyMatch(Matches.unitIsConstruction())) {
if (howManyOfEachConstructionCanPlace(to, producer, testUnits, player).totalValues() > 0) {
return null;
}
return "No more Constructions Allowed in " + producer.getName();
}
// check we havent just put a factory there (should we be checking producer?)
if (getAlreadyProduced(producer).stream().anyMatch(Matches.unitCanProduceUnits()) || getAlreadyProduced(to).stream().anyMatch(Matches.unitCanProduceUnits())) {
return "Factory in " + producer.getName() + " cant produce until 1 turn after it is created";
}
return "No Factory in " + producer.getName();
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class AbstractPlaceDelegate method getMaxUnitsToBePlacedMap.
/**
* Returns -1 somewhere in the map if can place unlimited units.
*/
protected IntegerMap<Territory> getMaxUnitsToBePlacedMap(final Collection<Unit> units, final Territory to, final PlayerID player, final boolean countSwitchedProductionToNeighbors) {
final IntegerMap<Territory> maxUnitsToBePlacedMap = new IntegerMap<>();
final List<Territory> producers = getAllProducers(to, player, units);
if (producers.isEmpty()) {
return maxUnitsToBePlacedMap;
}
producers.sort(getBestProducerComparator(to, units, player));
final Collection<Territory> notUsableAsOtherProducers = new ArrayList<>(producers);
final Map<Territory, Integer> currentAvailablePlacementForOtherProducers = new HashMap<>();
for (final Territory producerTerritory : producers) {
final Collection<Unit> unitsCanBePlacedByThisProducer = (isUnitPlacementRestrictions() ? CollectionUtils.getMatches(units, unitWhichRequiresUnitsHasRequiredUnits(producerTerritory, true)) : new ArrayList<>(units));
final int prodT = getMaxUnitsToBePlacedFrom(producerTerritory, unitsCanBePlacedByThisProducer, to, player, countSwitchedProductionToNeighbors, notUsableAsOtherProducers, currentAvailablePlacementForOtherProducers);
maxUnitsToBePlacedMap.put(producerTerritory, prodT);
}
return maxUnitsToBePlacedMap;
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class AbstractPlaceDelegate method getMaxUnitsToBePlacedFrom.
/**
* Returns -1 if can place unlimited units.
*/
protected int getMaxUnitsToBePlacedFrom(final Territory producer, final Collection<Unit> units, final Territory to, final PlayerID player, final boolean countSwitchedProductionToNeighbors, final Collection<Territory> notUsableAsOtherProducers, final Map<Territory, Integer> currentAvailablePlacementForOtherProducers) {
// we may have special units with requiresUnits restrictions
final Collection<Unit> unitsCanBePlacedByThisProducer = (isUnitPlacementRestrictions() ? CollectionUtils.getMatches(units, unitWhichRequiresUnitsHasRequiredUnits(producer, true)) : new ArrayList<>(units));
if (unitsCanBePlacedByThisProducer.size() <= 0) {
return 0;
}
// if its an original factory then unlimited production
// Can be null!
final TerritoryAttachment ta = TerritoryAttachment.get(producer);
final Predicate<Unit> factoryMatch = Matches.unitIsOwnedAndIsFactoryOrCanProduceUnits(player).and(Matches.unitIsBeingTransported().negate()).and(producer.isWater() ? Matches.unitIsLand().negate() : Matches.unitIsSea().negate());
final Collection<Unit> factoryUnits = producer.getUnits().getMatches(factoryMatch);
// boolean placementRestrictedByFactory = isPlacementRestrictedByFactory();
final boolean unitPlacementPerTerritoryRestricted = isUnitPlacementPerTerritoryRestricted();
final boolean originalFactory = (ta != null && ta.getOriginalFactory());
final boolean playerIsOriginalOwner = factoryUnits.size() > 0 && this.player.equals(getOriginalFactoryOwner(producer));
final RulesAttachment ra = (RulesAttachment) player.getAttachment(Constants.RULES_ATTACHMENT_NAME);
final Collection<Unit> alreadProducedUnits = getAlreadyProduced(producer);
final int unitCountAlreadyProduced = alreadProducedUnits.size();
if (originalFactory && playerIsOriginalOwner) {
if (ra != null && ra.getMaxPlacePerTerritory() != -1) {
return Math.max(0, ra.getMaxPlacePerTerritory() - unitCountAlreadyProduced);
}
return -1;
}
// Restricts based on the STARTING number of units in a territory (otherwise it is infinite placement)
if (unitPlacementPerTerritoryRestricted) {
if (ra != null && ra.getPlacementPerTerritory() > 0) {
final int allowedPlacement = ra.getPlacementPerTerritory();
final int ownedUnitsInTerritory = CollectionUtils.countMatches(to.getUnits().getUnits(), Matches.unitIsOwnedBy(player));
if (ownedUnitsInTerritory >= allowedPlacement) {
return 0;
}
if (ra.getMaxPlacePerTerritory() == -1) {
return -1;
}
return Math.max(0, ra.getMaxPlacePerTerritory() - unitCountAlreadyProduced);
}
}
// a factory can produce the same number of units as the number of PUs the territory generates each turn (or not, if
// it has canProduceXUnits)
final int maxConstructions = howManyOfEachConstructionCanPlace(to, producer, unitsCanBePlacedByThisProducer, player).totalValues();
final boolean wasFactoryThereAtStart = wasOwnedUnitThatCanProduceUnitsOrIsFactoryInTerritoryAtStartOfStep(producer, player);
// If there's NO factory, allow placement of the factory
if (!wasFactoryThereAtStart) {
if (ra != null && ra.getMaxPlacePerTerritory() > 0) {
return Math.max(0, Math.min(maxConstructions, ra.getMaxPlacePerTerritory() - unitCountAlreadyProduced));
}
return Math.max(0, maxConstructions);
}
// getHowMuchCanUnitProduce accounts for IncreasedFactoryProduction, but does not account for maxConstructions
int production = TripleAUnit.getProductionPotentialOfTerritory(unitsAtStartOfStepInTerritory(producer), producer, player, getData(), true, true);
// increase the production by the number of constructions allowed
if (maxConstructions > 0) {
production += maxConstructions;
}
// return 0 if less than 0
if (production < 0) {
return 0;
}
production += CollectionUtils.countMatches(alreadProducedUnits, Matches.unitIsConstruction());
// Now we check if units we have already produced here could be produced by a different producer
int unitCountHaveToAndHaveBeenBeProducedHere = unitCountAlreadyProduced;
if (countSwitchedProductionToNeighbors && unitCountAlreadyProduced > 0) {
if (notUsableAsOtherProducers == null) {
throw new IllegalStateException("notUsableAsOtherProducers cannot be null if countSwitchedProductionToNeighbors is true");
}
if (currentAvailablePlacementForOtherProducers == null) {
throw new IllegalStateException("currentAvailablePlacementForOtherProducers cannot be null if countSwitchedProductionToNeighbors is true");
}
int productionCanNotBeMoved = 0;
int productionThatCanBeTakenOver = 0;
// try to find a placement move (to an adjacent sea zone) that can be taken over by some other territory factory
for (final UndoablePlacement placementMove : placements) {
if (placementMove.getProducerTerritory().equals(producer)) {
final Territory placeTerritory = placementMove.getPlaceTerritory();
final Collection<Unit> unitsPlacedByCurrentPlacementMove = placementMove.getUnits();
// for our special 'move shit around' methods.
if (!placeTerritory.isWater() || (isUnitPlacementRestrictions() && unitsPlacedByCurrentPlacementMove.stream().anyMatch(Matches.unitRequiresUnitsOnCreation()))) {
productionCanNotBeMoved += unitsPlacedByCurrentPlacementMove.size();
} else {
final int maxProductionThatCanBeTakenOverFromThisPlacement = unitsPlacedByCurrentPlacementMove.size();
// find other producers for this placement move to the same water territory
final List<Territory> newPotentialOtherProducers = getAllProducers(placeTerritory, player, unitsCanBePlacedByThisProducer);
newPotentialOtherProducers.removeAll(notUsableAsOtherProducers);
newPotentialOtherProducers.sort(getBestProducerComparator(placeTerritory, unitsCanBePlacedByThisProducer, player));
int productionThatCanBeTakenOverFromThisPlacement = 0;
for (final Territory potentialOtherProducer : newPotentialOtherProducers) {
Integer potential = currentAvailablePlacementForOtherProducers.get(potentialOtherProducer);
if (potential == null) {
potential = getMaxUnitsToBePlacedFrom(potentialOtherProducer, unitsPlacedInTerritorySoFar(placeTerritory), placeTerritory, player);
}
if (potential == -1) {
currentAvailablePlacementForOtherProducers.put(potentialOtherProducer, -1);
productionThatCanBeTakenOverFromThisPlacement = maxProductionThatCanBeTakenOverFromThisPlacement;
break;
}
final int needed = maxProductionThatCanBeTakenOverFromThisPlacement - productionThatCanBeTakenOverFromThisPlacement;
final int surplus = potential - needed;
if (surplus > 0) {
currentAvailablePlacementForOtherProducers.put(potentialOtherProducer, surplus);
productionThatCanBeTakenOverFromThisPlacement += needed;
} else {
currentAvailablePlacementForOtherProducers.put(potentialOtherProducer, 0);
productionThatCanBeTakenOverFromThisPlacement += potential;
notUsableAsOtherProducers.add(potentialOtherProducer);
}
if (surplus >= 0) {
break;
}
}
if (productionThatCanBeTakenOverFromThisPlacement > maxProductionThatCanBeTakenOverFromThisPlacement) {
throw new IllegalStateException("productionThatCanBeTakenOverFromThisPlacement should never be larger " + "than maxProductionThatCanBeTakenOverFromThisPlacement");
}
productionThatCanBeTakenOver += productionThatCanBeTakenOverFromThisPlacement;
}
if (productionThatCanBeTakenOver >= unitCountAlreadyProduced - productionCanNotBeMoved) {
break;
}
}
}
unitCountHaveToAndHaveBeenBeProducedHere = Math.max(0, unitCountAlreadyProduced - productionThatCanBeTakenOver);
}
if (ra != null && ra.getMaxPlacePerTerritory() > 0) {
return Math.max(0, Math.min(production - unitCountHaveToAndHaveBeenBeProducedHere, ra.getMaxPlacePerTerritory() - unitCountHaveToAndHaveBeenBeProducedHere));
}
return Math.max(0, production - unitCountHaveToAndHaveBeenBeProducedHere);
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class AbstractPlaceDelegate method placeUnits.
@Override
public String placeUnits(final Collection<Unit> units, final Territory at, final BidMode bidMode) {
if (units == null || units.isEmpty()) {
return null;
}
final String error = isValidPlacement(units, at, player);
if (error != null) {
return error;
}
final List<Territory> producers = getAllProducers(at, player, units);
producers.sort(getBestProducerComparator(at, units, player));
final IntegerMap<Territory> maxPlaceableMap = getMaxUnitsToBePlacedMap(units, at, player, true);
// sort both producers and units so that the "to/at" territory comes first, and so that all constructions come first
// this is because the PRODUCER for ALL CONSTRUCTIONS must be the SAME as the TERRITORY they are going into
final List<Unit> unitsLeftToPlace = new ArrayList<>(units);
unitsLeftToPlace.sort(getUnitConstructionComparator());
final List<Unit> remainingUnitsToPlace = new ArrayList<>(player.getUnits().getUnits());
remainingUnitsToPlace.removeAll(unitsLeftToPlace);
while (!unitsLeftToPlace.isEmpty() && !producers.isEmpty()) {
// Get next producer territory
final Territory producer = producers.remove(0);
int maxPlaceable = maxPlaceableMap.getInt(producer);
if (maxPlaceable == 0) {
if (bidMode == BidMode.NOT_BID) {
continue;
}
maxPlaceable = 1;
}
// units may have special restrictions like RequiresUnits
final List<Unit> unitsCanBePlacedByThisProducer;
if (bidMode == BidMode.BID) {
unitsCanBePlacedByThisProducer = new ArrayList<>(unitsLeftToPlace);
} else {
unitsCanBePlacedByThisProducer = (isUnitPlacementRestrictions() ? CollectionUtils.getMatches(unitsLeftToPlace, unitWhichRequiresUnitsHasRequiredUnits(producer, true)) : new ArrayList<>(unitsLeftToPlace));
}
unitsCanBePlacedByThisProducer.sort(getHardestToPlaceWithRequiresUnitsRestrictions(true));
final int maxForThisProducer = getMaxUnitsToBePlacedFrom(producer, unitsCanBePlacedByThisProducer, at, player);
// don't forget that -1 == infinite
if (maxForThisProducer == -1 || maxForThisProducer >= unitsCanBePlacedByThisProducer.size()) {
performPlaceFrom(producer, unitsCanBePlacedByThisProducer, at, player);
unitsLeftToPlace.removeAll(unitsCanBePlacedByThisProducer);
continue;
}
final int neededExtra = unitsCanBePlacedByThisProducer.size() - maxForThisProducer;
if (maxPlaceable > maxForThisProducer) {
freePlacementCapacity(producer, neededExtra, unitsCanBePlacedByThisProducer, at, player);
final int newMaxForThisProducer = getMaxUnitsToBePlacedFrom(producer, unitsCanBePlacedByThisProducer, at, player);
if (newMaxForThisProducer != maxPlaceable && neededExtra > newMaxForThisProducer) {
throw new IllegalStateException("getMaxUnitsToBePlaced originally returned: " + maxPlaceable + ", \nWhich is not the same as it is returning after using freePlacementCapacity: " + newMaxForThisProducer + ", \nFor territory: " + at.getName() + ", Current Producer: " + producer.getName() + ", All Producers: " + producers + ", \nUnits Total: " + MyFormatter.unitsToTextNoOwner(units) + ", Units Left To Place By This Producer: " + MyFormatter.unitsToTextNoOwner(unitsCanBePlacedByThisProducer));
}
}
final Collection<Unit> placedUnits = CollectionUtils.getNMatches(unitsCanBePlacedByThisProducer, maxPlaceable, Matches.always());
performPlaceFrom(producer, placedUnits, at, player);
unitsLeftToPlace.removeAll(placedUnits);
}
if (!unitsLeftToPlace.isEmpty()) {
getDisplay().reportMessageToPlayers(Collections.singletonList(player), Collections.emptyList(), "Not enough unit production territories available", "Unit Placement Canceled");
}
// play a sound
if (units.stream().anyMatch(Matches.unitIsInfrastructure())) {
bridge.getSoundChannelBroadcaster().playSoundForAll(SoundPath.CLIP_PLACED_INFRASTRUCTURE, player);
} else if (units.stream().anyMatch(Matches.unitIsSea())) {
bridge.getSoundChannelBroadcaster().playSoundForAll(SoundPath.CLIP_PLACED_SEA, player);
} else if (units.stream().anyMatch(Matches.unitIsAir())) {
bridge.getSoundChannelBroadcaster().playSoundForAll(SoundPath.CLIP_PLACED_AIR, player);
} else {
bridge.getSoundChannelBroadcaster().playSoundForAll(SoundPath.CLIP_PLACED_LAND, player);
}
return null;
}
Aggregations