Created
December 19, 2014 10:23
-
-
Save Arnauld/0a04ee2a80f323eafcfe to your computer and use it in GitHub Desktop.
Platinum-rift [JAVA]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.ByteArrayOutputStream; | |
import java.io.FilterInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.PrintStream; | |
import java.util.*; | |
/** | |
* | |
*/ | |
class Player { | |
private static boolean DEBUG = true; | |
public static void main(String args[]) { | |
new Player().start(); | |
} | |
// | |
private Scanner in; | |
private final PrintStream out; | |
private final PrintStream debug; | |
private final ByteArrayOutputStream bout = new ByteArrayOutputStream(); | |
// | |
public final Random random = new Random(); | |
// | |
private int round = 0; | |
private int playerCount; | |
private int myId; | |
private int zoneCount; | |
private Zone[] zones; | |
// | |
private int availablePlatinum; | |
private List<Region> regions; | |
// | |
private List<Move> moves = new ArrayList<Move>(); | |
private List<Purchase> purchases = new ArrayList<Purchase>(); | |
public Player() { | |
this(System.in, System.out, System.err); | |
} | |
public Player(InputStream in, PrintStream out, PrintStream debug) { | |
changeInput(in); | |
this.out = out; | |
this.debug = debug; | |
} | |
//----------------------------------------------------- | |
// __ _ ___ ___ ___ ___ ___ ___ _ __ | |
// _____ / _` |/ __/ __/ _ \/ __/ __|/ _ \| '__| | |
// |_____| (_| | (_| (_| __/\__ \__ \ (_) | | | |
// \__,_|\___\___\___||___/___/\___/|_| | |
// | |
//----------------------------------------------------- | |
public int round() { | |
return round; | |
} | |
public void changeRound(int round) { | |
this.round = round; | |
} | |
public void changeInput(InputStream in) { | |
if (DEBUG) | |
in = new FilterInputStream(in) { | |
@Override | |
public int read() throws IOException { | |
int read = super.read(); | |
if (read != -1) | |
bout.write(read); | |
return read; | |
} | |
@Override | |
public int read(byte[] b, int off, int len) throws IOException { | |
int read = super.read(b, off, len); | |
if (read > 0) { | |
bout.write(b, off, read); | |
} | |
return read; | |
} | |
}; | |
this.in = new Scanner(in); | |
} | |
public int zoneCount() { | |
return zoneCount; | |
} | |
public int playerCount() { | |
return playerCount; | |
} | |
public int getMyId() { | |
return myId; | |
} | |
public Zone[] getZones() { | |
return zones; | |
} | |
public List<Move> getMoves() { | |
return moves; | |
} | |
public List<Purchase> getPurchases() { | |
return purchases; | |
} | |
public int getAvailablePlatinum() { | |
return availablePlatinum; | |
} | |
public Zone zone(int zoneId) { | |
return zones[zoneId]; | |
} | |
private Map<Integer, Zone> newZoneByIdMap() { | |
Map<Integer, Zone> zoneById = new HashMap<Integer, Zone>(); | |
for (Zone zone : zones) { | |
zoneById.put(zone.zoneId, zone); | |
} | |
return zoneById; | |
} | |
public List<Region> getRegions() { | |
return regions; | |
} | |
//--------------------------------------------------------------- | |
// _ | |
// __ _ __ _ _ __ ___ ___ | | ___ ___ _ __ | |
// _____ / _` |/ _` | '_ ` _ \ / _ \ | |/ _ \ / _ \| '_ \ | |
// |_____| (_| | (_| | | | | | | __/ | | (_) | (_) | |_) | | |
// \__, |\__,_|_| |_| |_|\___| |_|\___/ \___/| .__/ | |
// |___/ |_| | |
//--------------------------------------------------------------- | |
public void start() { | |
initializeFromInput(); | |
// game loop | |
while (true) { | |
round++; | |
updateStatusFromInput(); | |
processIA(); | |
} | |
} | |
public void processIA() { | |
moves.clear(); | |
purchases.clear(); | |
think(); | |
for (Zone zone : zones) { | |
packOrders(zone); | |
} | |
display(out, moves); | |
display(out, purchases); | |
} | |
//------------------------------------------------------------------------- | |
// _ | |
// (_) __ _ | |
// _____| |/ _` | | |
// |_____| | (_| | | |
// |_|\__,_| | |
//------------------------------------------------------------------------- | |
private void think() { | |
if (round == 1) { | |
initialSpawn(); | |
} else { | |
takeOrders(); | |
//expand(); | |
} | |
} | |
private void takeOrders() { | |
List<Order> orders = new ArrayList<Order>(); | |
Context ctx = newContext(); | |
for (Zone zone : zones) { | |
zone.clearAssignmentsAndStats(zones, myId); | |
if (zone.region.isOwned(myId)) | |
continue; | |
if (zone.isSpawnPossible(myId)) | |
orders.add(new Spawn(zone, myId)); | |
if (zone.myPods(myId) > 0) { | |
if (zone.notOwnedBy(myId)) { | |
orders.add(new Fight(zone)); | |
orders.add(new Flee(zone)); | |
} else { | |
orders.add(new Support(zone)); | |
} | |
} else if (zone.notOwnedBy(myId)) { | |
orders.add(new Conquer(zone, zone.maxOtherPods(myId))); | |
} | |
} | |
// sort in reverse order | |
// so that removing the last element is less consuming | |
Collections.sort(orders, orderPriorityReverseComparator(ctx)); | |
List<Order> selected = new ArrayList<Order>(); | |
for (int i = orders.size() - 1; i >= 0; i--) { | |
Order order = orders.remove(i); | |
if (order.evaluate(ctx)) { | |
selected.add(order); | |
// readjust priority based on previous decision... | |
Collections.sort(orders, orderPriorityReverseComparator(ctx)); | |
} | |
} | |
for (Order order : selected) { | |
order.execute(ctx); | |
} | |
} | |
public Context newContext() { | |
return new Context(myId, zones, random, availablePlatinum / 20, round); | |
} | |
private void initialSpawn() { | |
Collections.sort(regions, regionPlatinumSourceComparator); | |
int remainingBuy = availablePlatinum / 20; | |
Iterator<Integer> buyings = distribute(remainingBuy).iterator(); | |
Context ctx = new Context(myId, zones, random, remainingBuy, round); | |
int nbMaxInitialSpawnPerRegion = 3; | |
for (Region r : regions) { | |
int nb = nbMaxInitialSpawnPerRegion; | |
Collections.sort(r.zones, zonePlatinumSourceComparator(ctx)); | |
for (Zone z : r.zones) { | |
if (buyings.hasNext()) { | |
z.purchasedNb = buyings.next(); | |
} else { | |
return; | |
} | |
nb--; | |
if (nb == 0) | |
break; | |
} | |
} | |
} | |
public static List<Integer> distribute(int remainingBuy) { | |
List<Integer> xs = new ArrayList<Integer>(); | |
if (remainingBuy > 3) { | |
xs.add(3); | |
remainingBuy = remainingBuy - 3; | |
} | |
int nb = remainingBuy / 3; | |
int n = nb; | |
while (n > 0) { | |
xs.add(2); | |
n--; | |
} | |
n = remainingBuy - 2 * nb; | |
while (n > 0) { | |
xs.add(1); | |
n--; | |
} | |
return xs; | |
} | |
private void packOrders(Zone z) { | |
if (z.purchasedNb > 0) | |
purchases.add(new Purchase(z.purchasedNb, z.zoneId)); | |
int lastZId = -1; | |
Move move = null; | |
for (Integer zId : z.assignedToThis) { | |
if (move == null || zId != lastZId) { | |
lastZId = zId; | |
move = new Move(zId, z.zoneId); | |
moves.add(move); | |
} | |
move.nb++; | |
} | |
} | |
//--------------------------------- | |
// _ | |
// (_) ___ | |
// _____| |/ _ \ | |
// |_____| | (_) | | |
// |_|\___/ | |
//--------------------------------- | |
public void updateStatusFromInput() { | |
availablePlatinum = in.nextInt(); | |
in.nextLine(); | |
for (int i = 0; i < zoneCount(); i++) { | |
int zId = in.nextInt(); // this zone's ID | |
zones[zId].update( | |
in.nextInt(), // the player who owns this zone (-1 otherwise) | |
in.nextInt(), // player 0's PODs on this zone | |
in.nextInt(), // player 1's PODs on this zone | |
in.nextInt(), // player 2's PODs on this zone (always 0 for a two player game) | |
in.nextInt()); // player 3's PODs on this zone (always 0 for a two or three player game) | |
in.nextLine(); | |
} | |
for (Region r : regions) { | |
r.invalidate(); | |
} | |
// -- no more input there... | |
if (DEBUG) { | |
String string = bout.toString(); | |
debug.println("::processIA::" + round); | |
debug.println(string); | |
bout.reset(); | |
} | |
} | |
public void initializeFromInput() { | |
playerCount = in.nextInt(); | |
myId = in.nextInt(); | |
zoneCount = in.nextInt(); // the amount of zones on the map | |
int linkCount = in.nextInt(); | |
in.nextLine(); | |
zones = new Zone[zoneCount]; | |
for (int i = 0; i < zoneCount; i++) { | |
int zoneId = in.nextInt(); // this zone's ID (between 0 and zoneCount-1) | |
int platinumSource = in.nextInt(); // the amount of Platinum this zone can provide per game turn | |
in.nextLine(); | |
Zone zone = new Zone(zoneId, platinumSource); | |
zones[zoneId] = zone; | |
} | |
for (int i = 0; i < linkCount; i++) { | |
int zone1 = in.nextInt(); | |
int zone2 = in.nextInt(); | |
zones[zone1].linkedTo(zone2); | |
zones[zone2].linkedTo(zone1); | |
in.nextLine(); | |
} | |
regions = new ArrayList<Region>(); | |
Map<Integer, Zone> zoneById = newZoneByIdMap(); | |
while (!zoneById.isEmpty()) { | |
Region region = new Region(regions.size()); | |
region.traverse(zoneById); | |
regions.add(region); | |
} | |
} | |
//------------------------------------------------------------------------- | |
// __ _ | |
// /__\ ___ __ _(_) ___ _ __ | |
// / \/// _ \/ _` | |/ _ \| '_ \ | |
// / _ \ __/ (_| | | (_) | | | | | |
// \/ \_/\___|\__, |_|\___/|_| |_| | |
// |___/ | |
//------------------------------------------------------------------------- | |
public static class Region { | |
private final Integer regionId; | |
private List<Zone> zones = new ArrayList<Zone>(); | |
private Boolean owned; | |
private int numberOfPlatinumSources; | |
public Region(int regionId) { | |
this.regionId = regionId; | |
} | |
public int platinumSources() { | |
return numberOfPlatinumSources; | |
} | |
public int zoneCount() { | |
return zones.size(); | |
} | |
public boolean isOwned(int myId) { | |
if (owned == null) { | |
owned = true; | |
for (Zone zone : zones) { | |
if (zone.ownerId != myId) { | |
owned = false; | |
break; | |
} | |
} | |
} | |
return owned; | |
} | |
public void traverse(Map<Integer, Zone> zoneById) { | |
Zone zone = zoneById.values().iterator().next(); | |
List<Zone> toVisit = new ArrayList<Zone>(); | |
toVisit.add(zone); | |
while (!toVisit.isEmpty()) { | |
Zone z = removeLast(toVisit); | |
for (Integer zId : z.linkedTo) { | |
Zone linked = zoneById.remove(zId); | |
if (linked != null) | |
toVisit.add(linked); | |
} | |
z.region = this; | |
declareZone(z); | |
} | |
} | |
private boolean declareZone(Zone z) { | |
numberOfPlatinumSources += z.numberOfPlatinumSources; | |
return zones.add(z); | |
} | |
private Zone removeLast(List<Zone> list) { | |
return list.remove(list.size() - 1); | |
} | |
public void invalidate() { | |
owned = null; | |
} | |
public int[] numberOfZoneOwnedByOthers(int myId) { | |
int nbMine = 0, nbOthers = 0; | |
for (Zone zone : zones) { | |
if (zone.ownerId == -1) | |
continue; | |
if (zone.ownerId == myId) | |
nbMine++; | |
else | |
nbOthers++; | |
} | |
return new int[]{nbMine, nbOthers}; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// _ | |
// /\/\ (_)___ ___ | |
// / \| / __|/ __| | |
// / /\/\ \ \__ \ (__ | |
// \/ \/_|___/\___| | |
//------------------------------------------------------------------------- | |
private static Comparator<? super Order> orderPriorityReverseComparator(final Context ctx) { | |
return new Comparator<Order>() { | |
@Override | |
public int compare(Order z1, Order z2) { | |
return -Integer.compare(z1.priority(ctx), z2.priority(ctx)); | |
} | |
}; | |
} | |
private static Comparator<? super Integer> surroundedByNotOwnedComparator(final Context ctx) { | |
return new Comparator<Integer>() { | |
@Override | |
public int compare(Integer z1, Integer z2) { | |
int nb1 = countNotOwnedAround(z1); | |
int nb2 = countNotOwnedAround(z2); | |
return -Integer.compare(nb1, nb2); | |
} | |
private int countNotOwnedAround(Integer zId) { | |
return ctx.numberOfNotOwnedZoneAround(zId); | |
} | |
}; | |
} | |
private static Comparator<? super Region> regionPlatinumSourceComparator = new Comparator<Region>() { | |
@Override | |
public int compare(Region r1, Region r2) { | |
return -Integer.compare(r1.platinumSources(), r2.platinumSources()); | |
} | |
}; | |
private static Comparator<? super Zone> zonePlatinumSourceComparator(final Context ctx) { | |
return new Comparator<Zone>() { | |
@Override | |
public int compare(Zone z1, Zone z2) { | |
int compare = -Integer.compare(z1.numberOfPlatinumSources, z2.numberOfPlatinumSources); | |
if (compare == 0) { | |
return -Integer.compare(ctx.numberOfPlatinumSourcesAround(z1), | |
ctx.numberOfPlatinumSourcesAround(z2)); | |
} | |
return compare; | |
} | |
}; | |
} | |
protected static <T> T randomlyPickOne(Random random, List<T> elements) { | |
if (elements.isEmpty()) | |
return null; | |
int index = random.nextInt(elements.size()); | |
return elements.get(index); | |
} | |
private static void display(PrintStream out, List<? extends Displayable> displayables) { | |
if (displayables.isEmpty()) | |
out.println(Wait); | |
else { | |
for (int i = 0; i < displayables.size(); i++) { | |
Displayable displayable = displayables.get(i); | |
if (i > 0) | |
out.print(" "); | |
out.print(displayable.display()); | |
} | |
out.println(); | |
} | |
} | |
//------------------------------------------------------------------------- | |
// _____ | |
// / _ / ___ _ __ ___ | |
// \// / / _ \| '_ \ / _ \ | |
// / //\ (_) | | | | __/ | |
// /____/\___/|_| |_|\___| | |
//------------------------------------------------------------------------- | |
public static class Zone { | |
public final Integer zoneId; | |
public final int numberOfPlatinumSources; | |
public Region region; | |
public final List<Integer> linkedTo = new ArrayList<Integer>(); | |
public List<Integer> shuffledLinks; | |
// | |
public int ownerId; | |
public int[] pods = new int[4]; | |
// | |
private int freeForOther; | |
public int assignedToOtherZones = 0; | |
public int purchasedNb = 0; | |
public List<Integer> assignedToThis = new ArrayList<Integer>(); | |
public Zone(int zoneId, int numberOfPlatinumSources) { | |
this.zoneId = zoneId; | |
this.numberOfPlatinumSources = numberOfPlatinumSources; | |
} | |
public Integer regionId() { | |
return region.regionId; | |
} | |
public void linkedTo(int otherZoneId) { | |
linkedTo.add(otherZoneId); | |
} | |
public void update(int ownerId, int podsP0, int podsP1, int podsP2, int podsP3) { | |
this.ownerId = ownerId; | |
this.pods[0] = podsP0; | |
this.pods[1] = podsP1; | |
this.pods[2] = podsP2; | |
this.pods[3] = podsP3; | |
} | |
public int myPods(int myId) { | |
return pods[myId]; | |
} | |
public int maxOtherPods(int myId) { | |
int max = 0; | |
for (int i = 0; i < 4; i++) { | |
if (i != myId) | |
max = Math.max(max, pods[i]); | |
} | |
return max; | |
} | |
public boolean isSpawnPossible(int myId) { | |
return ownerId == myId || ownerId == -1; | |
} | |
public boolean notOwnedBy(int myId) { | |
return ownerId != myId; | |
} | |
public void clearAssignmentsAndStats(Zone[] zones, int myId) { | |
purchasedNb = 0; | |
freeForOther = 0; | |
assignedToOtherZones = 0; | |
assignedToThis.clear(); | |
} | |
public List<Integer> shuffledLinks() { | |
if (shuffledLinks == null) | |
shuffledLinks = new ArrayList<Integer>(linkedTo); | |
Collections.shuffle(shuffledLinks); | |
return shuffledLinks; | |
} | |
@Override | |
public String toString() { | |
return "Zone{" + | |
"zoneId=" + zoneId + | |
'}'; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ _ | |
// / __\___ _ __ | |_ _____ _| |_ | |
// / / / _ \| '_ \| __/ _ \ \/ / __| | |
// / /__| (_) | | | | || __/> <| |_ | |
// \____/\___/|_| |_|\__\___/_/\_\\__| | |
//------------------------------------------------------------------------- | |
public static class Context { | |
private final int myId; | |
private final Zone[] zones; | |
private final Random random; | |
private final int round; | |
private final int availableForSpawn; | |
// | |
private final int[] consumed; | |
private final int[] affected; | |
private int spawnConsumed; | |
private List<String> spawnReasons = new ArrayList<String>(); | |
public int leroyCount = 2; | |
public Context(int myId, | |
Zone[] zones, | |
Random random, | |
int availableForSpawn, | |
int round) { | |
this.myId = myId; | |
this.zones = zones; | |
this.random = random; | |
this.availableForSpawn = availableForSpawn; | |
this.round = round; | |
// | |
this.consumed = new int[zones.length]; | |
this.affected = new int[zones.length]; | |
} | |
public Zone zone(int zoneId) { | |
return zones[zoneId]; | |
} | |
public int availablePods(int zoneId) { | |
Zone zone = zones[zoneId]; | |
return zone.myPods(myId) - consumed[zoneId]; | |
} | |
public int maxOtherPods(int zoneId) { | |
Zone zone = zones[zoneId]; | |
return zone.maxOtherPods(myId); | |
} | |
public Random random() { | |
return random; | |
} | |
public void consume(int zoneId) { | |
consumed[zoneId]++; | |
} | |
public void consumeSpawn(String reason) { | |
consumeSpawn(1, reason); | |
} | |
public void consumeSpawn(int spawned, String reason) { | |
for (int i = 0; i < spawned; i++) | |
spawnReasons.add(reason); | |
spawnConsumed += spawned; | |
} | |
public boolean ownedByMe(int zoneId) { | |
Zone zone = zones[zoneId]; | |
return zone.ownerId == myId; | |
} | |
public boolean ownedByMeOrNeutral(int zoneId) { | |
Zone zone = zones[zoneId]; | |
return zone.ownerId == myId || zone.ownerId == -1; | |
} | |
public boolean ownedByMeOrNeutralOrEmpty(int zoneId) { | |
Zone zone = zones[zoneId]; | |
return zone.ownerId == myId | |
|| zone.ownerId == -1 | |
|| zone.maxOtherPods(myId) == 0; | |
} | |
public int availableForSpawn(int zoneId) { | |
if (ownedByMeOrNeutral(zoneId)) | |
return availableForSpawn - spawnConsumed; | |
return 0; | |
} | |
public int maxAround(Zone zone) { | |
int max = 0; | |
for (Integer zId : zone.linkedTo) { | |
int nb = zones[zId].maxOtherPods(myId); | |
if (nb > max) | |
max = nb; | |
} | |
return max; | |
} | |
public Set<Integer> searchAllNearestZoneNotMineOrWithEnemies(Zone zone, int radius) { | |
Path path = new Path(zone.zoneId, notMineOrWithEnemies(zones, myId), acceptAll, radius, zones); | |
return path.computeAllNextMoves(); | |
} | |
public Set<Integer> searchAllNearestZoneWithEnemies(Zone zone, int radius) { | |
Path path = new Path(zone.zoneId, withEnemies(zones, myId), acceptAll, radius, zones); | |
return path.computeAllNextMoves(); | |
} | |
public Set<Integer> searchAllNearestUnexploredZone(Zone zone, int radius) { | |
Path path = new Path(zone.zoneId, | |
notMineOrWithEnemies(zones, myId), | |
noEnemyPods(zones, myId), | |
radius, zones); | |
List<Integer> found = new ArrayList<Integer>(path.collectAllZoneOfInterest()); | |
if (found.isEmpty()) | |
return Collections.emptySet(); | |
Collections.sort(found, surroundedByNotOwnedComparator(this)); | |
path = new Path(zone.zoneId, isEqual(found.get(0)), noEnemyPods(zones, myId), radius, zones); | |
path.computeOnePath(); | |
HashSet<Integer> integers = new HashSet<Integer>(); | |
integers.add(path.nextMove()); | |
return integers; | |
} | |
public int affectedAt(Zone zone) { | |
return zone.myPods(myId) + affected[zone.zoneId]; | |
} | |
public int affectedAt(int zoneId) { | |
return zones[zoneId].myPods(myId) + affected[zoneId]; | |
} | |
public void affectsAt(int nb, Zone zone) { | |
affected[zone.zoneId] += nb; | |
} | |
public void affectsAt(int nb, int zoneId) { | |
affected[zoneId] += nb; | |
} | |
public int availableAround(Integer zoneId) { | |
int availableAround = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
availableAround += availablePods(zId); | |
} | |
return availableAround; | |
} | |
public int numberOfControlledZoneAround(Integer zoneId) { | |
int nb = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
if (availablePods(zId) > 0 || affectedAt(zId) > 0) | |
nb++; | |
} | |
return nb; | |
} | |
public boolean isRegionNotInfestedYet(Zone zone) { | |
if (isInfected(zone.zoneId)) | |
return false; // not not! => | |
for (Zone other : zone.region.zones) { | |
if (isInfected(other.zoneId)) | |
return false; | |
} | |
return true; | |
} | |
private boolean isInfected(int zId) { | |
return (affectedAt(zId) > 0 || zones[zId].ownerId == myId || availablePods(zId) > 0); | |
} | |
public int regionSize(Zone zone) { | |
return zone.region.zoneCount(); | |
} | |
public int numberOfPlatinumSourcesAroundMinusEnemies(Zone zone) { | |
int nb = 0; | |
for (Integer zId : zones[zone.zoneId].linkedTo) { | |
nb += zones[zId].numberOfPlatinumSources - zones[zId].maxOtherPods(myId); | |
} | |
return nb; | |
} | |
public int numberOfPlatinumSourcesAround(Zone zone) { | |
int nb = 0; | |
for (Integer zId : zones[zone.zoneId].linkedTo) { | |
nb += zones[zId].numberOfPlatinumSources; | |
} | |
return nb; | |
} | |
public int numberOfOwnedZonesAround(Integer zoneId) { | |
int nb = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
if (zones[zId].ownerId == myId && zones[zId].maxOtherPods(myId) == 0) | |
nb++; | |
} | |
return nb; | |
} | |
public int numberOfEnemyZonesAround(Integer zoneId) { | |
int nb = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
int ownerId = zones[zId].ownerId; | |
if (ownerId != myId && ownerId != -1) | |
nb++; | |
} | |
return nb; | |
} | |
public int numberOfNotOwnedZoneAround(Integer zoneId) { | |
int nb = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
int ownerId = zones[zId].ownerId; | |
if (ownerId != myId) | |
nb++; | |
} | |
return nb; | |
} | |
public Integer selectLinkWithTheHighestNumberOfPodsAvailable(Integer zoneId) { | |
Integer selected = null; | |
int maxAvailable = 0; | |
for (Integer zId : zones[zoneId].linkedTo) { | |
int available = availablePods(zId); | |
if (available > maxAvailable) | |
selected = zId; | |
} | |
return selected; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ | |
// /___\_ __ __| | ___ _ __ | |
// // // '__/ _` |/ _ \ '__| | |
// / \_//| | | (_| | __/ | | |
// \___/ |_| \__,_|\___|_| | |
//------------------------------------------------------------------------- | |
public interface Order { | |
int priority(Context context); | |
boolean evaluate(Context context); | |
void execute(Context context); | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ ____ | |
// /___\_ __ __| | ___ _ __ / / _\_ __ __ ___ ___ __ | |
// // // '__/ _` |/ _ \ '__/ /\ \| '_ \ / _` \ \ /\ / / '_ \ | |
// / \_//| | | (_| | __/ | / / _\ \ |_) | (_| |\ V V /| | | | | |
// \___/ |_| \__,_|\___|_|/_/ \__/ .__/ \__,_| \_/\_/ |_| |_| | |
// |_| | |
//------------------------------------------------------------------------- | |
public static class Spawn implements Order { | |
private final Zone zone; | |
private final int myId; | |
private int spawned; | |
private int lastPriority; | |
public Spawn(Zone zone, int myId) { | |
this.zone = zone; | |
this.myId = myId; | |
} | |
@Override | |
public int priority(Context ctx) { | |
int weight = 0; | |
if (zone.notOwnedBy(myId) && ctx.affectedAt(zone) == 0) { | |
weight += 2 * (zone.numberOfPlatinumSources + 1); | |
weight += ctx.numberOfPlatinumSourcesAroundMinusEnemies(zone); | |
} | |
weight += 2 * Math.min(2, ctx.numberOfEnemyZonesAround(zone.zoneId)); | |
weight += Math.min(2, ctx.numberOfOwnedZonesAround(zone.zoneId)); | |
weight += (ctx.isRegionNotInfestedYet(zone) && ctx.regionSize(zone) > 10) ? 30 : 0; | |
int[] ints = zone.region.numberOfZoneOwnedByOthers(ctx.myId); | |
weight += (ints[1] - ints[0]) / (ints[1] + 1); | |
lastPriority = 80 - weight; | |
return lastPriority; | |
} | |
@Override | |
public boolean evaluate(Context context) { | |
spawned = 0; | |
int spawn = context.availableForSpawn(zone.zoneId); | |
if (spawn == 0) | |
return false; | |
int maxAround = context.maxAround(zone); | |
if (maxAround == 0) { | |
spawned = 1; | |
} else { | |
int min = Math.max(1, maxAround - context.availablePods(zone.zoneId) + 1); | |
spawned = Math.min(min, spawn); | |
} | |
if (spawned > 0) { | |
context.consumeSpawn(spawned, getClass().getSimpleName() + "::" + zone.zoneId); | |
context.affectsAt(spawned, zone); | |
} | |
return true; | |
} | |
@Override | |
public void execute(Context context) { | |
zone.purchasedNb += spawned; | |
} | |
public String toString() { | |
return "P" + lastPriority + "/Spawn#" + zone.zoneId; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ ____ _ | |
// /___\_ __ __| | ___ _ __ / / _\_ _ _ __ _ __ ___ _ __| |_ | |
// // // '__/ _` |/ _ \ '__/ /\ \| | | | '_ \| '_ \ / _ \| '__| __| | |
// / \_//| | | (_| | __/ | / / _\ \ |_| | |_) | |_) | (_) | | | |_ | |
// \___/ |_| \__,_|\___|_|/_/ \__/\__,_| .__/| .__/ \___/|_| \__| | |
// |_| |_| | |
//------------------------------------------------------------------------- | |
public static class Support implements Order { | |
private final Zone zone; | |
private List<Integer> dst = new ArrayList<Integer>(); | |
private int lastPriority = 90; | |
public Support(Zone zone) { | |
this.zone = zone; | |
} | |
@Override | |
public int priority(Context context) { | |
return lastPriority; | |
} | |
@Override | |
public boolean evaluate(Context context) { | |
dst.clear(); | |
int myPods = context.availablePods(zone.zoneId); | |
if (myPods == 0) | |
return false; | |
for (Integer zId : zone.linkedTo) { | |
if (context.ownedByMeOrNeutral(zId)) { | |
dst.add(zId); | |
} | |
} | |
// at least one neighbor to move to | |
if (dst.isEmpty()) | |
return false; | |
List<Integer> affected = new ArrayList<Integer>(); | |
// -- | |
Set<Integer> nextMovesToNearest; | |
if(context.leroyCount > 0) { | |
nextMovesToNearest = context.searchAllNearestUnexploredZone(zone, 8); | |
if (!nextMovesToNearest.isEmpty()) { | |
int p = myPods; | |
myPods = affect(context, myPods, context.leroyCount, affected, nextMovesToNearest); | |
context.leroyCount = context.leroyCount - (p - myPods); | |
} | |
} | |
nextMovesToNearest = context.searchAllNearestZoneWithEnemies(zone, 8); | |
if (!nextMovesToNearest.isEmpty()) { | |
myPods = affect(context, myPods, Integer.MAX_VALUE, affected, nextMovesToNearest); | |
} else { | |
nextMovesToNearest = context.searchAllNearestZoneNotMineOrWithEnemies(zone, 8); | |
if (!nextMovesToNearest.isEmpty()) { | |
myPods = affect(context, myPods, Integer.MAX_VALUE, affected, nextMovesToNearest); | |
} | |
} | |
while (myPods > 0) { | |
Integer zId = randomlyPickOne(context.random(), dst); | |
affected.add(zId); | |
context.affectsAt(1, zId); | |
myPods--; | |
} | |
dst = (affected); | |
return true; | |
} | |
private int affect(Context context, int myPods, int maxToSpread, List<Integer> affected, Set<Integer> nextMovesToNearest) { | |
while (maxToSpread > 0 && myPods > 0) { | |
for (Integer zId : nextMovesToNearest) { | |
affected.add(zId); | |
context.affectsAt(1, zId); | |
myPods--; | |
maxToSpread--; | |
if (maxToSpread == 0 || myPods == 0) | |
return myPods; | |
} | |
} | |
return myPods; | |
} | |
@Override | |
public void execute(Context context) { | |
for (Integer zId : dst) { | |
context.zone(zId).assignedToThis.add(zone.zoneId); | |
} | |
} | |
public String toString() { | |
return "P" + lastPriority + "/Support#" + zone.zoneId; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ _____ | |
// /___\_ __ __| | ___ _ __ / / __\___ _ __ __ _ _ _ ___ _ __ | |
// // // '__/ _` |/ _ \ '__/ / / / _ \| '_ \ / _` | | | |/ _ \ '__| | |
// / \_//| | | (_| | __/ | / / /__| (_) | | | | (_| | |_| | __/ | | |
// \___/ |_| \__,_|\___|_|/_/\____/\___/|_| |_|\__, |\__,_|\___|_| | |
// |_| | |
//------------------------------------------------------------------------- | |
public static class Conquer implements Order { | |
private final Zone dst; | |
private final int maxOtherPods; | |
private final List<Integer> supportedBy = new ArrayList<Integer>(); | |
private int lastPriority; | |
public Conquer(Zone dst, int maxOtherPods) { | |
this.dst = dst; | |
this.maxOtherPods = maxOtherPods; | |
} | |
@Override | |
public int priority(Context context) { | |
lastPriority = (6 - dst.numberOfPlatinumSources) + 4 - Math.min(4, maxOtherPods); | |
return lastPriority; | |
} | |
@Override | |
public boolean evaluate(Context context) { | |
supportedBy.clear(); | |
int myPods = context.availablePods(dst.zoneId); | |
int otherPods = context.availablePods(dst.zoneId); | |
int availableAround = context.availableAround(dst.zoneId); | |
if (// myPods + availableAround < 4 && // otherwise keep the zone! | |
myPods + availableAround <= otherPods) // or conquer it :) | |
return false; | |
int required = otherPods - myPods + 1; | |
int toConsume = Math.min(otherPods + 1, myPods); | |
while (toConsume > 0) { | |
context.consume(dst.zoneId); | |
context.affectsAt(1, dst.zoneId); | |
required--; | |
toConsume--; | |
} | |
while (required > 0) { | |
Integer zId = context.selectLinkWithTheHighestNumberOfPodsAvailable(dst.zoneId); | |
if (zId != null && context.availablePods(zId) > 0) { | |
supportedBy.add(zId); | |
context.consume(zId); | |
context.affectsAt(1, dst); | |
required--; | |
} | |
} | |
return true; | |
} | |
@Override | |
public void execute(Context context) { | |
dst.assignedToThis.addAll(supportedBy); | |
} | |
public String toString() { | |
return "P" + lastPriority + "/Conquer#" + dst.zoneId; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ _____ _ | |
// /___\_ __ __| | ___ _ __ / / __\ | ___ ___ | |
// // // '__/ _` |/ _ \ '__/ / _\ | |/ _ \/ _ \ | |
// / \_//| | | (_| | __/ | / / / | | __/ __/ | |
// \___/ |_| \__,_|\___|_|/_/\/ |_|\___|\___| | |
//------------------------------------------------------------------------- | |
public static class Flee implements Order { | |
private final Zone zone; | |
private int lastPriority = 100; | |
public Flee(Zone zone) { | |
this.zone = zone; | |
} | |
@Override | |
public int priority(Context context) { | |
return lastPriority; | |
} | |
@Override | |
public boolean evaluate(Context context) { | |
for (Integer zId : zone.linkedTo) { | |
if (context.ownedByMeOrNeutral(zId)) | |
return true; | |
} | |
return false; | |
} | |
@Override | |
public void execute(Context context) { | |
Integer thisZoneId = zone.zoneId; | |
int myPods = context.availablePods(thisZoneId); | |
while (myPods > 0) { | |
Integer zId = randomlyPickOne(context.random(), zone.linkedTo); | |
context.consume(thisZoneId); | |
context.affectsAt(1, zId); | |
context.zone(zId).assignedToThis.add(thisZoneId); | |
myPods--; | |
} | |
} | |
public String toString() { | |
return "P" + lastPriority + "/Flee#" + zone.zoneId; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ _ _____ _ _ _ | |
// /___\_ __ __| | ___ _ __ / / __(_) __ _| |__ | |_ | |
// // // '__/ _` |/ _ \ '__/ / _\ | |/ _` | '_ \| __| | |
// / \_//| | | (_| | __/ | / / / | | (_| | | | | |_ | |
// \___/ |_| \__,_|\___|_|/_/\/ |_|\__, |_| |_|\__| | |
// |___/ | |
//------------------------------------------------------------------------- | |
public static class Fight implements Order { | |
private final Zone zone; | |
private final List<Integer> supportedBy = new ArrayList<Integer>(); | |
private int purchaseNb = 0; | |
private int lastPriority; | |
public Fight(Zone zone) { | |
this.zone = zone; | |
} | |
@Override | |
public int priority(Context context) { | |
this.lastPriority = (6 - zone.numberOfPlatinumSources); | |
return lastPriority; | |
} | |
@Override | |
public boolean evaluate(Context context) { | |
supportedBy.clear(); | |
purchaseNb = 0; | |
int myPods = context.availablePods(zone.zoneId); | |
int otherPods = context.maxOtherPods(zone.zoneId); | |
int availableAround = context.availableAround(zone.zoneId); | |
int availableSpawn = context.availableForSpawn(zone.zoneId); | |
if (myPods + availableAround + availableSpawn <= otherPods) // (1) | |
return false; | |
int required = otherPods - myPods + 1; | |
while (required > 0) { | |
Integer zId = randomlyPickOne(context.random(), zone.linkedTo); | |
if (zId != null && context.availablePods(zId) > 0) { | |
supportedBy.add(zId); | |
context.consume(zId); | |
context.affectsAt(1, zone); | |
required--; | |
} | |
// no more available around; | |
// one will rely on spawn afterwards | |
if (required > 0 && context.availableAround(zone.zoneId) == 0) | |
break; | |
} | |
// availableSpawn should be valid at this point due to equation (1) | |
while (required > 0) { | |
purchaseNb++; | |
context.consumeSpawn(getClass().getSimpleName() + "::" + zone.zoneId); | |
context.affectsAt(1, zone); | |
required--; | |
} | |
return true; | |
} | |
@Override | |
public void execute(Context context) { | |
zone.purchasedNb += purchaseNb; | |
zone.assignedToThis.addAll(supportedBy); | |
} | |
public String toString() { | |
return "P" + lastPriority + "/Fight#" + zone.zoneId; | |
} | |
} | |
//------------------------------------------------------------------------- | |
// ___ ___ _ | |
// / _ \__ _ _ __ ___ ___ /___\_ __ __| | ___ _ __ | |
// / /_\/ _` | '_ ` _ \ / _ \_____ // // '__/ _` |/ _ \ '__| | |
// / /_\\ (_| | | | | | | __/_____/ \_//| | | (_| | __/ | | |
// \____/\__,_|_| |_| |_|\___| \___/ |_| \__,_|\___|_| | |
//------------------------------------------------------------------------- | |
public static final String Wait = "WAIT"; | |
public interface Displayable { | |
String display(); | |
} | |
public static class Purchase implements Displayable { | |
public int nb; | |
public int zoneTo; | |
public Purchase(int nb, int zoneTo) { | |
this.nb = nb; | |
this.zoneTo = zoneTo; | |
} | |
@Override | |
public String display() { | |
return nb + " " + zoneTo; | |
} | |
} | |
public static class Move implements Displayable { | |
public int nb; | |
public int zoneFrom; | |
public int zoneTo; | |
public Move(int zoneFrom, int zoneTo) { | |
this.zoneFrom = zoneFrom; | |
this.zoneTo = zoneTo; | |
} | |
@Override | |
public String display() { | |
return nb + " " + zoneFrom + " " + zoneTo; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
Move move = (Move) o; | |
return zoneFrom == move.zoneFrom && zoneTo == move.zoneTo; | |
} | |
@Override | |
public int hashCode() { | |
return 31 * zoneFrom + zoneTo; | |
} | |
} | |
//------------------------------------------- | |
// ___ _ _ _ | |
// / _ \_ __ ___ __| (_) ___ __ _| |_ ___ | |
// / /_)/ '__/ _ \/ _` | |/ __/ _` | __/ _ \ | |
// / ___/| | | __/ (_| | | (_| (_| | || __/ | |
// \/ |_| \___|\__,_|_|\___\__,_|\__\___| | |
//------------------------------------------- | |
public interface Predicate { | |
boolean test(Integer zId); | |
} | |
public static Predicate isEqual(final Integer target) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return zId.equals(target); | |
} | |
}; | |
} | |
public static Predicate emptyButNotMine(final Zone[] zones, final int myId) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return zones[zId].ownerId != myId && zones[zId].maxOtherPods(myId) == 0; | |
} | |
}; | |
} | |
public static Predicate notMine(final Zone[] zones, final int myId) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return zones[zId].ownerId != myId; | |
} | |
}; | |
} | |
public static Predicate notMineOrWithEnemies(final Zone[] zones, final int myId) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return zones[zId].ownerId != myId || zones[zId].maxOtherPods(myId) > 0; | |
} | |
}; | |
} | |
public static Predicate acceptAll = new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return true; | |
} | |
}; | |
public static Predicate noEnemyPods(final Zone[] zones, final int myId) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
return zones[zId].maxOtherPods(myId) == 0; | |
} | |
}; | |
} | |
public static Predicate withEnemies(final Zone[] zones, final int myId) { | |
return new Predicate() { | |
@Override | |
public boolean test(Integer zId) { | |
Zone zone = zones[zId]; | |
return (zone.ownerId != myId && zone.ownerId != -1) || zone.maxOtherPods(myId) > 0; | |
} | |
}; | |
} | |
//------------------------------------------- | |
// _ _ | |
// _ __ __ _| |_| |__ | |
// | '_ \ / _` | __| '_ \ | |
// | |_) | (_| | |_| | | | | |
// | .__/ \__,_|\__|_| |_| | |
// |_| | |
//------------------------------------------- | |
public static class Path { | |
public final int start; | |
public final Predicate end; | |
private final Predicate zoneAccepted; | |
private final int ringLimit; | |
public final Player.Zone[] zones; | |
private List<Integer> onePath; | |
public Path(int start, Predicate end, Predicate zoneAccepted, int ringLimit, Zone[] zones) { | |
this.start = start; | |
this.end = end; | |
this.zoneAccepted = zoneAccepted; | |
this.ringLimit = ringLimit; | |
this.zones = zones; | |
} | |
public void computeOnePath() { | |
Set<Integer> ringP = new HashSet<Integer>(); | |
Set<Integer> ring0 = new HashSet<Integer>(); | |
ring0.add(start); | |
List<Set<Integer>> alreadyTraversed = new ArrayList<Set<Integer>>(); | |
while (!ring0.isEmpty()) { | |
Set<Integer> ringN = new HashSet<Integer>(); | |
for (Integer zId : ring0) { | |
for (Integer link : zones[zId].linkedTo) { | |
if (!zoneAccepted.test(link)) | |
continue; | |
if (end.test(link)) { | |
onePath = extractPath(link, zId, alreadyTraversed); | |
return; | |
} | |
if (ringP.contains(link) || ring0.contains(link)) | |
continue; | |
ringN.add(link); | |
} | |
} | |
alreadyTraversed.add(ringN); | |
ringP = ring0; | |
ring0 = ringN; | |
if (ringLimit > 0 && alreadyTraversed.size() > ringLimit) | |
return; | |
} | |
} | |
private List<Integer> extractPath(Integer end, Integer zId, List<Set<Integer>> alreadyTraversed) { | |
List<Integer> path = new ArrayList<Integer>(); | |
path.add(end); | |
path.add(zId); | |
if (zId != start) { | |
Integer cId = zId; | |
ringLbl: | |
for (int i = alreadyTraversed.size() - 2; i >= 0; i--) { | |
Set<Integer> ring = alreadyTraversed.get(i); | |
for (Integer link : zones[cId].shuffledLinks()) { | |
if (ring.contains(link)) { | |
path.add(link); | |
cId = link; | |
continue ringLbl; | |
} | |
} | |
} | |
path.add(start); | |
} | |
return path; | |
} | |
public List<Integer> getOnePath() { | |
return onePath; | |
} | |
public Integer nextMove() { | |
return nextMove(onePath); | |
} | |
private static Integer nextMove(List<Integer> path) { | |
if (path == null) | |
return null; | |
if (path.size() >= 2) | |
return path.get(path.size() - 2); | |
return path.get(path.size() - 1); | |
} | |
public Set<Integer> collectAllZoneOfInterest() { | |
Set<Integer> ringP = new HashSet<Integer>(); | |
Set<Integer> ring0 = new HashSet<Integer>(); | |
ring0.add(start); | |
Set<Integer> reachables = new HashSet<Integer>(); | |
List<Set<Integer>> alreadyTraversed = new ArrayList<Set<Integer>>(); | |
while (!ring0.isEmpty()) { | |
Set<Integer> ringN = new HashSet<Integer>(); | |
for (Integer zId : ring0) { | |
for (Integer link : zones[zId].linkedTo) { | |
if (!zoneAccepted.test(link)) | |
continue; | |
if (ringP.contains(link) || ring0.contains(link)) | |
continue; | |
if (end.test(link)) { | |
reachables.add(link); | |
} | |
ringN.add(link); | |
} | |
} | |
alreadyTraversed.add(ringN); | |
ringP = ring0; | |
ring0 = ringN; | |
if (ringLimit > 0 && alreadyTraversed.size() > ringLimit) | |
return reachables; | |
} | |
return reachables; | |
} | |
public Set<Integer> computeAllNextMoves() { | |
Set<Integer> ringP = new HashSet<Integer>(); | |
Set<Integer> ring0 = new HashSet<Integer>(); | |
ring0.add(start); | |
Set<Integer> nextMoves = new HashSet<Integer>(); | |
List<Set<Integer>> alreadyTraversed = new ArrayList<Set<Integer>>(); | |
while (!ring0.isEmpty()) { | |
Set<Integer> ringN = new HashSet<Integer>(); | |
for (Integer zId : ring0) { | |
for (Integer link : zones[zId].linkedTo) { | |
if (!zoneAccepted.test(link)) | |
continue; | |
if (ringP.contains(link) || ring0.contains(link)) | |
continue; | |
if (end.test(link)) { | |
List<Integer> path = extractPath(link, zId, alreadyTraversed); | |
Integer nextMove = nextMove(path); | |
if (nextMove != null) { | |
nextMoves.add(nextMove); | |
} | |
} | |
ringN.add(link); | |
} | |
} | |
if (!nextMoves.isEmpty()) { | |
return nextMoves; | |
} | |
alreadyTraversed.add(ringN); | |
ringP = ring0; | |
ring0 = ringN; | |
if (ringLimit > 0 && alreadyTraversed.size() > ringLimit) | |
return nextMoves; | |
} | |
return nextMoves; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
...