Permalink
Browse files

final submission

  • Loading branch information...
1 parent 75e4aeb commit 5fc7e0c5937690161d89b88b787a85f2ee639bbb Alex Pinkin committed Nov 30, 2010
Sorry, we could not display the entire diff because too many files (881) changed.
View
12 .gitignore
@@ -1,7 +1,7 @@
-my_planet_wars.*
-planetwars/*.pyc
-*.class
-*.log
-log.txt
-visualizer/generated.htm
+my_planet_wars.*
+planetwars/*.pyc
+*.class
+*.log
+log.txt
+visualizer/generated.htm
*.bak
View
838 MyBot.py
@@ -1,418 +1,566 @@
from planetwars import BaseBot, Game
-from planetwars.universe2 import Universe2
+from planetwars.universe3 import Universe3
from planetwars.planet import Planet
-from planetwars.planet2 import Planet2
+from planetwars.player import PLAYER1, PLAYER2, NOBODY
+from planetwars.planet2 import Planet2, getLogger
from planetwars.universe import player, Fleet
-from logging import getLogger, sys
+from logging import getLogger
import planetwars.planet
from math import ceil
+from copy import copy
+import random
+
+HORIZON_FIRST = 40
+HORIZON = 40
+ATTACK_SCORE_THRESHOLD_FIRST = 0
+ATTACK_SCORE_THRESHOLD = 140
+ATTACK_SCORE_ENEMY_MULTIPLIER = 2
log = getLogger(__name__)
-# map 46 is non-deterministic against 104
-# linear programming to pick best moves
-# attack from multiple planets
-# defend from multiple planets
-# scoring function to 200 turns?
-# scoring function needs to take planet's neighbours into account
-# overestimating max_aid?
-# performance issue
+def zeros(rows,cols):
+ row = []
+ data = []
+ for i in range(cols):
+ row.append(0)
+ for i in range(rows):
+ data.append(row[:])
+ return data
+
+# v = list of item values or profit
+# w = list of item weight or cost
+# W = max weight or max cost for the knapsack
+def zeroOneKnapsack(v, w, W):
+ # c is the cost matrix
+ c = []
+ n = len(v)
+ c = zeros(n,W+1)
+ for i in range(0,n):
+ #for ever possible weight
+ for j in range(0,W+1):
+ #can we add this item to this?
+ if (w[i] > j):
+ c[i][j] = c[i-1][j]
+ else:
+ c[i][j] = max(c[i-1][j],v[i] +c[i-1][j-w[i]])
+ return [c[n-1][W], getUsedItems(w,c)]
+
+# w = list of item weight or cost
+# c = the cost matrix created by the dynamic programming solution
+def getUsedItems(w,c):
+ # item count
+ i = len(c)-1
+ currentW = len(c[0])-1
+ # set everything to not marked
+ marked = []
+ for i in range(i+1):
+ marked.append(0)
+ while (i >= 0 and currentW >=0):
+ if (i==0 and c[i][currentW] >0 )or c[i][currentW] != c[i-1][currentW]:
+ marked[i] =1
+ currentW = currentW-w[i]
+ i = i-1
+ return marked
+
+class Move(object):
+ def __init__(self, source, target, turn, ship_count):
+ self.source = source
+ self.target = target
+ self.turn = turn
+ self.ship_count = int(ship_count)
+
+ def __repr__(self):
+ return "Move from %s to %s at turn %s with %s ships" % (self.source, self.target, self.turn, self.ship_count)
+
class MyBot(BaseBot):
- def zeros(self,rows,cols):
- row = []
- data = []
- for i in range(cols):
- row.append(0)
- for i in range(rows):
- data.append(row[:])
- return data
-
- # v = list of item values or profit
- # w = list of item weight or cost
- # W = max weight or max cost for the knapsack
- def zeroOneKnapsack(self, v, w, W):
- # c is the cost matrix
- c = []
- n = len(v)
- c = self.zeros(n,W+1)
- for i in range(0,n):
- #for ever possible weight
- for j in range(0,W+1):
- #can we add this item to this?
- if (w[i] > j):
- c[i][j] = c[i-1][j]
- else:
- c[i][j] = max(c[i-1][j],v[i] +c[i-1][j-w[i]])
- return [c[n-1][W], self.getUsedItems(w,c)]
-
- # w = list of item weight or cost
- # c = the cost matrix created by the dynamic programming solution
- def getUsedItems(self,w,c):
- # item count
- i = len(c)-1
- currentW = len(c[0])-1
- # set everything to not marked
- marked = []
- for i in range(i+1):
- marked.append(0)
- while (i >= 0 and currentW >=0):
- if (i==0 and c[i][currentW] >0 )or c[i][currentW] != c[i-1][currentW]:
- marked[i] =1
- currentW = currentW-w[i]
- i = i-1
- return marked
+ def __init__(self, universe):
+ self.universe = universe
+ self.scheduled_moves_at_turn= {}
def total_fleet_ship_count(self, owner):
return sum( [ fleet.ship_count for fleet in self.universe.find_fleets(owner) ] )
- def closest_enemy_planet_distance(self, p):
- return min((lambda ep:ep.distance(p))(ep) for ep in self.universe.enemy_planets)
+ def get_neutrals_under_player_attack(self, player):
+ result = []
+ for planet in self.nobodies_planets:
+ if sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player ] ) > 0:
+ result.append(planet)
+ return result
- def enemy_ships_reinforcing(self, planet, turn):
- return sum( [ fleet.ship_count for fleet in planet.reinforcement_fleets if fleet.owner in player.NOT_ME and fleet.turns_remaining <= turn ] )
+ def get_available_ships_within_distance(self, planet_to_attack, player, distance):
+ result = 0
+ for planet in (list(self.universe.find_planets(player)) + self.get_neutrals_under_player_attack(player)):
+ if planet.id != planet_to_attack.id and planet.distance(planet_to_attack) <= distance and self.ships_needed[planet] == 0:
+ ships_avail = self.ships_available_at_turn[planet][distance-planet.distance(planet_to_attack)]
+ result += ships_avail
+ return result
- def my_fleets_attacking(self, planet):
- return sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME] )
+ def get_attack_score(self, planet_to_attack, future_owner, distance):
+ turns = self.max_distance_between_planets - distance + HORIZON
+ attack_score = turns * planet_to_attack.growth_rate
+ if future_owner in player.ENEMIES:
+ attack_score *= ATTACK_SCORE_ENEMY_MULTIPLIER
+ return attack_score
- def closest_to_enemy_neutral_under_my_attack(self):
- best_distance = 1000000
- result_planet = None
- for planet in self.universe.nobodies_planets:
- if self.my_fleets_attacking(planet) > 0:
- distance = self.enemy_com.distance(planet)
- if distance < best_distance:
- best_distance = distance
- result_planet = planet
- return result_planet
+ def get_scheduled_fleets_to(self, planet):
+ result = []
+ for moves in self.scheduled_moves_at_turn.values():
+ for move in moves:
+ if move.target == planet:
+ distance = move.source.distance(move.target)
+ turns_remaining = distance + (move.turn - self.universe.game.turn_count)
+ fleet = Fleet(self.universe,random.randint(1,1000000),1, move.ship_count, move.source.id, move.target.id, distance, turns_remaining)
+ result.append(fleet)
+ return result
+
+ def get_scheduled_fleets_from(self, planet):
+ result = []
+ for moves in self.scheduled_moves_at_turn.values():
+ for move in moves:
+ if move.source == planet:
+ turns_remaining = move.turn - self.universe.game.turn_count
+ fleet = Fleet(self.universe,random.randint(1,1000000),1, move.ship_count, move.source.id, move.target.id, turns_remaining, turns_remaining)
+ result.append(fleet)
+ return result
def get_attack_ship_count_first_turn(self, planet_to_attack, my_home, enemy_home):
my_dist = my_home.distance(planet_to_attack)
enemy_dist = enemy_home.distance(planet_to_attack)
- #log.info("Distances for %s are %s %s" % (planet_to_attack, my_dist, enemy_dist))
if my_dist < enemy_dist:
return planet_to_attack.ship_count+1
if my_dist == enemy_dist and planet_to_attack.ship_count <= planet_to_attack.growth_rate:
return planet_to_attack.ship_count+1
return 1000000
- def get_ship_surplus(self, my_planet):
- result = 1000000;
- for enemy_planet in self.universe.enemy_planets:
- distance = enemy_planet.distance(my_planet)
- max_aid = self.max_aid_at_turn[my_planet][distance]
- self_aid = self.planet_timeline[my_planet][distance-1][1] if self.planet_timeline[my_planet][distance-1][0] == player.ME else 0
- my_max_aid = self.max_aid_at_turn[my_planet][distance] + self_aid
- surplus = max(my_max_aid - max_aid, 0)
- result = min(result, surplus)
- return result
+ def closest_enemy_planet(self, p):
+ if len(self.enemy_planets) == 0:
+ return None
- def get_neutrals_under_enemy_attack(self):
- result = []
- for planet in self.universe.nobodies_planets:
- if sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner in player.NOT_ME ] ) > 0:
- result.append(planet)
- return result
+ sorted_planets = sorted(self.enemy_planets, key=lambda ep : p.distance(ep) + ep.id/1000000.0)
+ return sorted_planets[0]
- def get_neutrals_under_my_attack(self):
- result = []
- for planet in self.universe.nobodies_planets:
- if sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME ] ) > 0:
- result.append(planet)
- return result
+ def closest_enemy_planet_distance(self, p):
+ if len(self.enemy_planets) == 0:
+ return 1000000
+ return min((lambda ep:ep.distance(p))(ep) for ep in self.enemy_planets)
+
+ def my_fleets_attacking(self, planet):
+ return sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME] )
+
+ def closest_to_enemy_neutral_under_my_attack(self):
+ best_distance = 1000000
+ result_planet = None
+ for planet in self.nobodies_planets:
+ if self.my_fleets_attacking(planet) > 0 or planet in self.planets_attacked:
+ distance = self.enemy_com.distance(planet)
+ if distance < best_distance:
+ best_distance = distance
+ result_planet = planet
+ return result_planet
+
+ def decrease_ships_available(self, planet, start_turn, ship_count):
+ for turn in range(start_turn, self.max_distance_between_planets + 21):
+ self.ships_available_at_turn[planet][turn] -= ship_count
+
+ def send_fleet(self, source, target, ship_count):
+ if source.owner == PLAYER1 and ship_count > 0 and ship_count <= source.ship_count:
+ source.send_fleet(target, ship_count)
+ else:
+ log.info("Error sending fleet from %s to %s with % ships" % (source, target, ship_count))
+
+ def doScheduled(self):
+ log.info("Scheduled move phase")
+
+ # execute delayed moves first
+ if self.scheduled_moves_at_turn.has_key(self.current_turn):
+ for move in self.scheduled_moves_at_turn[self.current_turn]:
+ if move.ship_count <= move.source.ship_count and move.ship_count > 0 and move.source.owner == PLAYER1 and move.source.ship_count >= move.ship_count:
+ self.send_fleet(move.source, move.target, move.ship_count)
+ self.decrease_ships_available(move.source, 0, move.ship_count)
+ else:
+ log.info("Can't execute move: %s, ships avail: %s" % (move, self.ships_available_at_turn[move.source][0]))
+ del self.scheduled_moves_at_turn[self.current_turn]
def doPrep(self):
log.info("Prep phase")
+ if self.current_turn == 1:
+ self.my_home = list(self.my_planets)[0]
+ self.enemy_home = list(self.enemy_planets)[0]
+
self.max_distance_between_planets = 0
- for p1 in self.universe.all_planets:
- for p2 in self.universe.all_planets:
+ for p1 in self.all_planets:
+ for p2 in self.all_planets:
self.max_distance_between_planets = max(self.max_distance_between_planets, p1.distance(p2))
- #log.info("Max distance: %s" % self.max_distance_between_planets)
-
# calculate current high level metrics
- self.my_total_ships_available = 0
- self.my_total_ships = 0
- self.my_total_growth_rate = 0
- self.enemy_total_ships_available = 0
- self.enemy_total_ships = 0
- self.enemy_total_growth_rate = 0
- self.ships_available = {}
+ self.total_ships = {PLAYER1:0, PLAYER2:0}
+ self.total_growth_rate = {PLAYER1:0, PLAYER2:0}
+ self.ships_available_at_turn = {}
self.ships_needed = {}
+ self.ships_needed_at_turn = {}
+ self.ships_needed_timeline = {}
self.planet_timeline = {}
- for planet in self.universe.all_planets:
- if len(planet.attacking_fleets) == 0:
- self.ships_available[planet] = planet.ship_count
+
+ for planet in self.all_planets:
+ self.ships_available_at_turn[planet] = {}
+ scheduled_fleets_to_planet = self.get_scheduled_fleets_to(planet)
+ scheduled_fleets_from_planet = self.get_scheduled_fleets_from(planet)
+ self.planet_timeline[planet] = planet.in_future_timeline(self.max_distance_between_planets + 20, scheduled_fleets_to_planet, scheduled_fleets_from_planet)
+ need_help = False
+ prev_owner = planet.owner
+ for step in self.planet_timeline[planet]:
+ owner = step[0]
+ ship_count = step[1]
+ if owner != prev_owner and prev_owner == planet.owner and prev_owner != NOBODY and not need_help:
+ self.ships_needed[planet] = ship_count
+ self.ships_needed_at_turn[planet] = self.planet_timeline[planet].index(step) + 1
+ need_help = True
+ self.ships_needed_timeline[planet] = [ship_count]
+ #log.info("Planet %s needs help %s at %s" % (planet, ship_count, self.ships_needed_at_turn[planet]))
+ if need_help and owner == prev_owner:
+ delta = self.planet_timeline[planet].index(step) + 1 - self.ships_needed_at_turn[planet]
+ ships_needed_delta = ship_count - delta * 2 * planet.growth_rate
+ self.ships_needed_timeline[planet].append(ships_needed_delta)
+ prev_owner = owner
+ if not need_help:
self.ships_needed[planet] = 0
- simulation_distance = self.max_distance_between_planets
- self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
- else:
- simulation_distance = self.max_distance_between_planets
- self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
- max_needed = 0
min_available = 1000000
- #log.info("timeline for %s: %s" % (planet, self.planet_timeline[planet]))
- for step in self.planet_timeline[planet]:
- owner = step[0]
+ step_index = len(self.planet_timeline[planet])
+ for step in reversed(self.planet_timeline[planet]):
ship_count = step[1]
- if owner != planet.owner:
- max_needed = max(max_needed, ship_count)
- else:
- min_available = min(min_available, ship_count)
- if max_needed > 0:
- # do we bail if we are going to lose this planet anyway?
- self.ships_available[planet] = 0
- self.ships_needed[planet] = max_needed
- else:
- self.ships_available[planet] = min_available
- self.ships_needed[planet] = 0
-
- if (planet.owner == player.ME):
- self.my_total_ships_available += self.ships_available[planet]
- self.my_total_growth_rate += planet.growth_rate
- self.my_total_ships += planet.ship_count
+ min_available = min(min_available, ship_count)
+ if step[0] == NOBODY:
+ min_available = 0
+ if min_available < 0:
+ log.info("Negative min_available: %s for %s" % (min_available, planet))
+ min_available = 0
+ self.ships_available_at_turn[planet][step_index] = min_available
+ #log.info("avail for %s at %s: %s" % (planet, step_index, min_available))
+ step_index -= 1
+ self.ships_available_at_turn[planet][0] = max(0,min(planet.ship_count, self.ships_available_at_turn[planet][1] - planet.growth_rate))
else:
- self.enemy_total_ships_available += self.ships_available[planet]
- self.enemy_total_growth_rate += planet.growth_rate
- self.enemy_total_ships += planet.ship_count
- #log.info("avail ships for %s: %s" % (planet, self.ships_available[planet]))
-
- # prevent initial overexpansion
- if self.universe.game.turn_count <= 2:
- for my_planet in self.universe.my_planets:
- for enemy_planet in self.universe.enemy_planets:
- max_enemy_fleet = self.ships_available[enemy_planet]
- distance = my_planet.distance(enemy_planet)
- ships_needed_for_safety = max_enemy_fleet-distance*my_planet.growth_rate
- if ships_needed_for_safety > (my_planet.ship_count - self.ships_available[my_planet]):
- deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available[my_planet])
- #log.info("deficit for %s: %s" % (my_planet, deficit))
- if deficit > self.ships_available[my_planet]:
- deficit = self.ships_available[my_planet]
-
- self.ships_available[my_planet] -= deficit
- self.my_total_ships_available -= deficit
-
- self.my_total_ships += self.total_fleet_ship_count(player.ME)
- self.enemy_total_ships += self.total_fleet_ship_count(player.NOT_ME)
+ for step_index in range(0, len(self.planet_timeline[planet])+1):
+ self.ships_available_at_turn[planet][step_index] = 0
+ if planet.owner != NOBODY:
+ self.total_ships[planet.owner] += planet.ship_count
+ self.total_growth_rate[planet.owner] += planet.growth_rate
+ self.total_ships[PLAYER1] += self.total_fleet_ship_count(PLAYER1)
+ self.total_ships[PLAYER2] += self.total_fleet_ship_count(PLAYER2)
+
+ for my_planet in [self.my_home]:
+ for enemy_planet in [self.enemy_home]:
+ if my_planet.owner != PLAYER1 or enemy_planet.owner != PLAYER2:
+ continue
+ max_enemy_fleet = self.ships_available_at_turn[enemy_planet][0]
+ distance = my_planet.distance(enemy_planet)
+ ships_needed_for_safety = max_enemy_fleet-(self.planet_timeline[my_planet][distance-1][1] - my_planet.ship_count)
+ if ships_needed_for_safety > (my_planet.ship_count - self.ships_available_at_turn[my_planet][0]):
+ deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available_at_turn[my_planet][0])
+ #log.info("deficit for %s: %s, max enemy fleet %s" % (my_planet, deficit, max_enemy_fleet))
+ if deficit > self.ships_available_at_turn[my_planet][0]:
+ deficit = self.ships_available_at_turn[my_planet][0]
+ self.decrease_ships_available(my_planet, 0, deficit)
+ #log.info("final ships avail for %s: %s" % (my_planet, self.ships_available_at_turn[my_planet][0]))
# calculate enemy's center of mass
weighted_x = 0
weighted_y = 0
div = 0
- for planet in self.universe.enemy_planets:
- weighted_x += planet.position.x * (self.ships_available[planet] + planet.growth_rate)
- weighted_y += planet.position.y * (self.ships_available[planet] + planet.growth_rate)
- div += self.ships_available[planet] + planet.growth_rate
+ for planet in self.enemy_planets:
+ weighted_x += planet.position.x * (self.ships_available_at_turn[planet][0] + planet.growth_rate)
+ weighted_y += planet.position.y * (self.ships_available_at_turn[planet][0] + planet.growth_rate)
+ div += self.ships_available_at_turn[planet][0] + planet.growth_rate
if div == 0:
div = 1
self.enemy_com = Planet(self.universe, 666, weighted_x/div, weighted_y/div, 2, 0, 0)
- # For every planet, and every turn, calculate how many ships the enemy CAN sent to it's aid
- self.max_aid_at_turn = {}
- enemy_planets_incl_candidates = list(self.universe.enemy_planets) + self.get_neutrals_under_enemy_attack()
- for planet in self.universe.all_planets:
- self.max_aid_at_turn[planet] = {}
- for turn in range(1, self.max_distance_between_planets+1):
- max_aid = 0
- #for enemy_planet in self.universe.all_planets:
- for enemy_planet in enemy_planets_incl_candidates:
- if enemy_planet.id != planet.id and planet.distance(enemy_planet) < turn:
- enemy_planet_time_step = self.planet_timeline[enemy_planet][turn - planet.distance(enemy_planet)]
- if (enemy_planet_time_step[0] in player.ENEMIES):
- max_aid += enemy_planet_time_step[1]
- #log.info("adding to max aid: %s" % enemy_planet_time_step[1])
- else:
- if enemy_planet.id != planet.id and planet.distance(enemy_planet) == turn:
- enemy_planet_time_step = self.planet_timeline[enemy_planet][0]
- if (enemy_planet_time_step[0] in player.ENEMIES):
- max_aid += enemy_planet.ship_count
- if self.planet_timeline[planet][turn-1][0] in player.ENEMIES:
- max_aid += self.planet_timeline[planet][turn-1][1]
- #log.info("self aid: %s" % self.planet_timeline[planet][turn-1][1])
- self.max_aid_at_turn[planet][turn] = max_aid
- #log.info("Max aid for %s at %s: %s" % (planet.id, turn, self.max_aid_at_turn[planet][turn]))
- #log.info("Max aid: %s" % self.max_aid_at_turn)
-
- # For every planet, and every turn, calculate how many ships I CAN send to its aid
- self.my_max_aid_at_turn = {}
- #my_planets_incl_candidates = list(self.universe.my_planets) + self.get_neutrals_under_my_attack()
- my_planets = self.universe.my_planets
- for planet in self.universe.all_planets:
- self.my_max_aid_at_turn[planet] = {}
- for turn in range(1, self.max_distance_between_planets+1):
- max_aid = 0
- #for enemy_planet in self.universe.all_planets:
- #for my_planet in my_planets_incl_candidates:
- for my_planet in my_planets:
- if my_planet.id != planet.id and planet.distance(my_planet) < turn:
- my_planet_time_step = self.planet_timeline[my_planet][turn - planet.distance(my_planet)]
- if (my_planet_time_step[0] == player.ME):
- max_aid += my_planet_time_step[1]
- #log.info("adding to my max aid: %s" % my_planet_time_step[1])
- else:
- if my_planet.id != planet.id and planet.distance(my_planet) == turn:
- my_planet_time_step = self.planet_timeline[my_planet][0]
- if (my_planet_time_step[0] == player.ME):
- max_aid += my_planet.ship_count
- #if self.planet_timeline[planet][turn-1][0] == player.ME:
- #max_aid += self.planet_timeline[planet][turn-1][1]
- #log.info("self aid: %s" % self.planet_timeline[planet][turn-1][1])
- self.my_max_aid_at_turn[planet][turn] = max_aid
- #log.info("My Max aid for %s at %s: %s" % (planet.id, turn, self.my_max_aid_at_turn[planet][turn]))
- #log.info("My Max aid: %s" % self.my_max_aid_at_turn)
-
- log.info("MY STATUS: %s/%s - %s available" % (self.my_total_ships, self.my_total_growth_rate, self.my_total_ships_available))
- log.info("ENEMY STATUS: %s/%s - %s available" % (self.enemy_total_ships, self.enemy_total_growth_rate, self.enemy_total_ships_available))
- #log.info("ENEMY COM: %s, %s" % (self.enemy_com.position.x, self.enemy_com.position.y))
-
- def doDefenseOffense(self):
- log.info("Offense/Defense phase")
-
- possible_moves = []
- for my_planet in self.universe.my_planets:
- for planet_to_attack in self.universe.all_planets:
- if planet_to_attack.id == my_planet.id:
- continue
- attack_distance = my_planet.distance(planet_to_attack)
- planet_to_attack_future = self.planet_timeline[planet_to_attack][attack_distance-1]
- planet_to_attack_future_owner = planet_to_attack_future[0]
- cost_to_conquer = -1
- time_to_profit = 0
- if planet_to_attack_future_owner == player.NOBODY:
- cost_to_conquer = planet_to_attack_future[1]
- if planet_to_attack.growth_rate > 0:
- time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate))
+ # For every planet/turn, calculate max aid each player can send
+ self.max_aid_at_turn = {PLAYER1:{}, PLAYER2:{}}
+ for player in (PLAYER1 | PLAYER2):
+ source_planets = list(self.universe.find_planets(player)) + self.get_neutrals_under_player_attack(player)
+ for planet in self.all_planets:
+ self.max_aid_at_turn[player][planet] = {}
+ for turn in range(1, self.max_distance_between_planets+21):
+ max_aid = 0
+ for source_planet in source_planets:
+ if source_planet.id != planet.id and planet.distance(source_planet) < turn:
+ source_planet_time_step = self.planet_timeline[source_planet][turn - planet.distance(source_planet) - 1]
+ if (source_planet_time_step[0] == player):
+ #log.info("Max aid by %s for %s from %s at %s: %s" % (player.id, planet.id, source_planet.id, turn, source_planet_time_step[1]))
+ max_aid += source_planet_time_step[1]
+ else:
+ if source_planet.id != planet.id and planet.distance(source_planet) == turn:
+ if (source_planet.owner == player):
+ max_aid += source_planet.ship_count
+ self.max_aid_at_turn[player][planet][turn] = max_aid
+ #log.info("Max aid by %s for %s at %s: %s" % (player.id, planet.id, turn, self.max_aid_at_turn[player][planet][turn]))
+
+ log.info("MY STATUS: %s/%s" % (self.total_ships[PLAYER1], self.total_growth_rate[PLAYER1]))
+ log.info("ENEMY STATUS: %s/%s" % (self.total_ships[PLAYER2], self.total_growth_rate[PLAYER2]))
+
+ def doDefense(self):
+ log.info("Defense phase")
+
+ planets_to_defend = list(self.universe.find_planets(PLAYER1)) + self.get_neutrals_under_player_attack(PLAYER1)
+ for planet_to_defend in sorted(planets_to_defend, key=lambda p: p.growth_rate + p.id/1000000.0, reverse=True):
+ ships_to_send = self.ships_needed[planet_to_defend]
+ if ships_to_send <= 0:
+ continue
+ min_distance = self.max_distance_between_planets
+ max_distance = self.ships_needed_at_turn[planet_to_defend]
+ for my_planet in self.my_planets:
+ distance = my_planet.distance(planet_to_defend)
+ min_distance = min(min_distance, distance)
+ min_distance = max(min_distance, 1)
+ timeline = [elem for elem in self.ships_needed_timeline[planet_to_defend] if elem > 0]
+ ship_counts_to_attempt = sorted(list(set(timeline)), key=lambda p : p, reverse=True)
+ #log.info("evaluating defense for %s needed %s" % (planet_to_defend, ship_counts_to_attempt))
+ defended = False
+
+ avail_ships_within_distance = {}
+ for ships_to_send in ship_counts_to_attempt:
+ for distance in range(min_distance, max_distance+1):
+ # calculate if we can get enough ships from my planets to planet_to_defend within 'distance' turns
+ ships_avail_to_defend = 0
+ if avail_ships_within_distance.has_key((planet_to_defend, distance)):
+ ships_avail_to_defend = avail_ships_within_distance[(planet_to_defend, distance)]
else:
- time_to_profit = 1000000
- if (time_to_profit+attack_distance) >= self.max_distance_between_planets:
- time_to_profit = self.max_distance_between_planets - attack_distance
- #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
- else:
- if planet_to_attack_future_owner in player.ENEMIES:
- cost_to_conquer = 0
-
- can_hold = True
- for turn in range(attack_distance,attack_distance+time_to_profit):
- max_aid = self.max_aid_at_turn[planet_to_attack][turn]
- my_max_aid = self.my_max_aid_at_turn[planet_to_attack][turn] - (cost_to_conquer + 1)
- if max_aid > my_max_aid:
- can_hold = False
+ ships_avail_to_defend = self.get_available_ships_within_distance(planet_to_defend, PLAYER1, distance)
+ avail_ships_within_distance[(planet_to_defend, distance)] = ships_avail_to_defend
+ #log.info("Ships avail to defend %s within %s dist: %s" % (planet_to_defend, distance, ships_avail_to_defend))
+ if ships_avail_to_defend >= ships_to_send:
+ ships_left_to_send = ships_to_send
+ for source_planet in sorted(list(self.my_planets) + self.get_neutrals_under_player_attack(PLAYER1), key=lambda p : p.distance(planet_to_defend) + p.id/1000000.0):
+ if self.ships_needed[source_planet] > 0:
+ continue
+ #log.info("evaluating for D: %s" % (source_planet))
+ current_distance = source_planet.distance(planet_to_defend)
+ ships_avail = self.ships_available_at_turn[source_planet][distance-current_distance]
+ if source_planet.id != planet_to_defend.id and ships_avail > 0:
+ #log.info("Ships avail from %s: %s at dist %s, dist = %s" % (source_planet, ships_avail, current_distance, distance))
+ ships_to_send = min(ships_left_to_send, ships_avail)
+ if current_distance == distance:
+ #log.info("defending avail from %s: %s at dist %s" % (source_planet, ships_to_send, current_distance))
+ self.send_fleet(source_planet, planet_to_defend, ships_to_send)
+ if current_distance < distance:
+ future_turn = self.current_turn + (distance - current_distance)
+ future_move = Move(source_planet, planet_to_defend, future_turn, ships_to_send)
+ log.info("Scheduled move: %s" % future_move)
+ if not self.scheduled_moves_at_turn.has_key(future_turn):
+ self.scheduled_moves_at_turn[future_turn] = []
+ self.scheduled_moves_at_turn[future_turn].append(future_move)
+ ships_left_to_send -= ships_to_send
+ self.decrease_ships_available(source_planet, 0, ships_to_send)
+ if ships_left_to_send == 0:
+ defended = True
+ break
+ if defended:
break
- if not can_hold:
+ if defended:
+ break
+
+ def doFirstTurnOffense(self):
+ candidates = []
+ candidate_map = {}
+ home_planet_distance = self.my_home.distance(self.enemy_home)
+ ships_available = min(self.my_home.ship_count, self.my_home.growth_rate * (home_planet_distance+0))
+
+ i = 0
+ max_attack_distance=0
+ for p in sorted(self.nobodies_planets, key=lambda p : self.get_attack_ship_count_first_turn(p, self.my_home, self.enemy_home) + p.id/1000000.0):
+ if p.distance(self.my_home) < p.distance(self.enemy_home) or p.distance(self.my_home) == p.distance(self.enemy_home):
+ if p.distance(self.my_home) == p.distance(self.enemy_home) and p.ship_count > 10:
+ continue
+ candidates.append(p)
+ candidate_map[i] = p
+ max_attack_distance = max(max_attack_distance, p.distance(self.my_home))
+ i += 1
+
+ weights = []
+ profits = []
+ for c in candidates:
+ weight = self.get_attack_ship_count_first_turn(c, self.my_home, self.enemy_home)
+ attack_score = (self.max_distance_between_planets - c.distance(self.my_home) + HORIZON_FIRST) * c.growth_rate - (weight - 1)
+ if attack_score < ATTACK_SCORE_THRESHOLD_FIRST:
+ attack_score = 0
+ weights.append(weight)
+ profits.append(attack_score)
+ #log.info("candidate %s: score %s, weight %s" % (c, attack_score, weight))
+
+ best_planets_to_attack = zeroOneKnapsack(profits,weights,ships_available)
+ #log.info("best planets: %s, ships_avail: %s" % (best_planets_to_attack,ships_available))
+
+ sorted_moves = []
+ for i in range(len(best_planets_to_attack[1])):
+ if (best_planets_to_attack[1][i] != 0):
+ planet_to_attack = candidate_map[i]
+ ships_to_send = planet_to_attack.ship_count+1
+ self.send_fleet(self.my_home, planet_to_attack, ships_to_send)
+ self.decrease_ships_available(self.my_home, 0, ships_to_send)
+ self.planets_attacked.append(planet_to_attack)
+
+ def doOffense(self):
+ log.info("Offense phase")
+ if self.current_turn == 1:
+ self.doFirstTurnOffense()
+ return
+
+ best_planet_to_attack = None
+ while True:
+ best_planet_to_attack = None
+ best_planet_to_attack_score = 0
+ best_planet_to_attack_distance = 0
+ best_planet_to_attack_ships_to_send = 0
+ for planet_to_attack in self.all_planets:
+ if planet_to_attack in self.planets_attacked:
continue
+ min_distance = self.max_distance_between_planets
+ max_distance = 0
+ for my_planet in self.my_planets:
+ distance = my_planet.distance(planet_to_attack)
+ min_distance = min(min_distance, distance)
+ max_distance = max(max_distance, distance)
+ for fleet in self.universe.find_fleets(owner=PLAYER2, destination=planet_to_attack):
+ max_distance = max(max_distance, fleet.turns_remaining)
+ #log.info("Max distance for %s: %s" % (planet_to_attack, max_distance))
+ min_distance = max(min_distance, 1)
+ for distance in range(min_distance, max_distance+1):
+ # calculate how many ships we need to get from my planets to planet_to_attack within 'distance' turns
+ planet_to_attack_future = self.planet_timeline[planet_to_attack][distance-1]
+ planet_to_attack_future_owner = planet_to_attack_future[0]
+ if planet_to_attack_future_owner == PLAYER1:
+ break
+ cost_to_conquer = 0 if planet_to_attack_future_owner == PLAYER2 else -1
+ time_to_profit = 0
+ if planet_to_attack_future_owner == player.NOBODY:
+ cost_to_conquer = planet_to_attack_future[1]
+ time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate)) if planet_to_attack.growth_rate > 0 else 1000000
+ if planet_to_attack_future_owner == NOBODY and self.enemy_com.distance(planet_to_attack) < distance:
+ break
+ #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
+
+ can_hold = True
+ for turn in range(distance, min(distance+time_to_profit+1, self.max_distance_between_planets + 20)):
+ enemy_max_aid = self.max_aid_at_turn[PLAYER2][planet_to_attack][turn]
+ if planet_to_attack_future_owner == player.PLAYER2:
+ enemy_max_aid += self.planet_timeline[planet_to_attack][turn+time_to_profit-1][1]
+ my_max_aid = self.max_aid_at_turn[PLAYER1][planet_to_attack][turn] - cost_to_conquer + planet_to_attack.growth_rate * (turn-distance)
+ if enemy_max_aid > my_max_aid:
+ can_hold = False
+ #log.info("can't hold %s at turn %s, enemy %s, me %s" % (planet_to_attack, turn, enemy_max_aid, my_max_aid))
+ break
+ if not can_hold:
+ continue
- max_aid = self.max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit]
- my_max_aid = self.my_max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit] - (cost_to_conquer + 1)
- if planet_to_attack_future_owner in player.ENEMIES:
- my_max_aid = 0
- ships_to_send = cost_to_conquer + max(max_aid - my_max_aid, 0) + 1
- #log.info("Evaluating attack of %s from %s, max %s, mymax %s, cost %s, ships %s" % (planet_to_attack, my_planet, max_aid, my_max_aid, cost_to_conquer, ships_to_send))
- if planet_to_attack_future_owner != player.ME and ships_to_send > 0 and ships_to_send <= self.ships_available[my_planet]:
- if self.planet_timeline[planet_to_attack][attack_distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][attack_distance-2][0] == player.NOBODY:
+ simulation_distance = min(distance+time_to_profit, self.max_distance_between_planets + 20)
+ if simulation_distance <= 0:
continue
- attack_score = (self.max_distance_between_planets - attack_distance + 40) * planet_to_attack.growth_rate
- if planet_to_attack_future_owner in player.ENEMIES:
- attack_score *= 2
- if planet_to_attack_future_owner in player.ENEMIES or (attack_score-cost_to_conquer) >= 140:
- possible_moves.append((my_planet, planet_to_attack, ships_to_send, attack_score))
- #log.info("Attack score of %s from %s is: %s - %s ships" % (planet_to_attack, my_planet, attack_score, ships_to_send))
-
- # execute the best moves
- planets_attacked = []
- sorted_moves = sorted(possible_moves, key=lambda m : m[3] + m[1].growth_rate/1000.0 + m[1].id/1000000.0, reverse=True)
- log.info("Best moves: %s" % len(sorted_moves))
-
- if self.universe.game.turn_count == 1:
- candidates = []
- candidate_map = {}
- my_home = list(self.universe.my_planets)[0]
- enemy_home = list(self.universe.enemy_planets)[0]
- home_planet_distance = my_home.distance(enemy_home)
- ships_available = min(my_home.ship_count, my_home.growth_rate * home_planet_distance)
-
- i = 0
- max_attack_distance=0
- for p in sorted(self.universe.nobodies_planets, key=lambda p : self.get_attack_ship_count_first_turn(p, my_home, enemy_home) + p.id/1000000.0):
- if p.distance(my_home) < p.distance(enemy_home) or p.distance(my_home) == p.distance(enemy_home):
- if p.distance(my_home) == p.distance(enemy_home) and p.ship_count > 10:
+ enemy_max_aid = self.max_aid_at_turn[PLAYER2][planet_to_attack][simulation_distance]
+ if planet_to_attack_future_owner == player.PLAYER2:
+ enemy_max_aid += self.planet_timeline[planet_to_attack][simulation_distance-1][1]
+ my_max_aid = self.max_aid_at_turn[PLAYER1][planet_to_attack][simulation_distance] - (cost_to_conquer + 1) if planet_to_attack_future_owner == NOBODY else 0
+ if planet_to_attack_future_owner == NOBODY and self.closest_enemy_planet_distance(planet_to_attack) > distance:
+ ships_to_send = cost_to_conquer + 1
+ else:
+ ships_to_send = cost_to_conquer + max(enemy_max_aid - my_max_aid, 0) + 1
+ #log.info("aids for %s at distance %s: enemy %s , me %s, cost %s" % (planet_to_attack, distance, enemy_max_aid, my_max_aid, cost_to_conquer))
+
+ # calculate if we can get enough ships from my planets to planet_to_attack within 'distance' turns
+ ships_avail_to_attack = self.get_available_ships_within_distance(planet_to_attack, PLAYER1, distance)
+ #log.info("avail to attack: %s, need to send %s" % (ships_avail_to_attack, ships_to_send))
+ if ships_avail_to_attack >= ships_to_send:
+ if self.planet_timeline[planet_to_attack][distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][distance-2][0] == player.NOBODY:
+ continue
+
+ attack_score = self.get_attack_score(planet_to_attack, planet_to_attack_future_owner, distance)
+ #log.info("Attack score of %s at dist %s is: %s - %s ships, cost %s" % (planet_to_attack, distance, attack_score, ships_to_send, cost_to_conquer))
+ if planet_to_attack_future_owner in player.ENEMIES or (attack_score-cost_to_conquer) >= ATTACK_SCORE_THRESHOLD:
+ if attack_score > best_planet_to_attack_score:
+ best_planet_to_attack_score = attack_score
+ best_planet_to_attack = planet_to_attack
+ best_planet_to_attack_distance = distance
+ best_planet_to_attack_ships_to_send = ships_to_send
+ break
+
+
+ if best_planet_to_attack is None:
+ return
+
+ log.info("Best planet to attack: %s at dist %s with score %s" % (best_planet_to_attack, best_planet_to_attack_distance, best_planet_to_attack_score))
+
+ ships_left_to_send = best_planet_to_attack_ships_to_send
+ source_planets = list(self.my_planets) + self.get_neutrals_under_player_attack(PLAYER1)
+ for source_planet in sorted(source_planets, key=lambda p : p.distance(best_planet_to_attack) + p.id/1000000.0):
+ distance = source_planet.distance(best_planet_to_attack)
+ if distance > best_planet_to_attack_distance:
continue
- candidates.append(p)
- candidate_map[i] = p
- max_attack_distance = max(max_attack_distance, p.distance(my_home))
- i += 1
-
- weights = []
- profits = []
- for c in candidates:
- attack_score = (self.max_distance_between_planets - c.distance(my_home) + 40) * c.growth_rate
- weight = self.get_attack_ship_count_first_turn(c, my_home, enemy_home)
- weights.append(weight)
- profits.append(attack_score)
-
- #log.info("weights: %s" % weights)
- #log.info("profits: %s" % profits)
- #log.info("available: %s" % ships_available)
-
- best_planets_to_attack = self.zeroOneKnapsack(profits,weights,ships_available)
- #log.info("best planets: %s" % best_planets_to_attack)
-
- sorted_moves = []
- for i in range(len(best_planets_to_attack[1])):
- if (best_planets_to_attack[1][i] != 0):
- planet_to_attack = candidate_map[i]
- sorted_moves.append((my_home, planet_to_attack, planet_to_attack.ship_count+1, 0))
-
- for move in sorted_moves:
- ships_to_send = move[2]
- planet_to_attack = move[1]
- my_planet = move[0]
- if ships_to_send <= self.ships_available[my_planet] and planet_to_attack not in planets_attacked:
- my_planet.send_fleet(planet_to_attack, ships_to_send)
- self.ships_available[my_planet] -= ships_to_send
- planets_attacked.append(planet_to_attack)
+ ships_avail = self.ships_available_at_turn[source_planet][best_planet_to_attack_distance-distance]
+ #log.info("ships avail to attack from %s at dist %s: %s" % (source_planet, best_planet_to_attack_distance-distance, ships_avail))
+ if self.ships_needed[source_planet] > 0:
+ ships_avail = 0
+ if source_planet.id != best_planet_to_attack.id and ships_avail > 0:
+ ships_to_send = min(ships_left_to_send, ships_avail)
+ #log.info("ships to send from %s: %s" % (source_planet, ships_to_send))
+ if distance == best_planet_to_attack_distance and source_planet.owner == PLAYER1:
+ self.send_fleet(source_planet, best_planet_to_attack, ships_to_send)
+ if distance < best_planet_to_attack_distance:
+ future_turn = self.current_turn + (best_planet_to_attack_distance - distance)
+ future_move = Move(source_planet, best_planet_to_attack, future_turn, ships_to_send)
+ log.info("Scheduled move: %s" % future_move)
+ if not self.scheduled_moves_at_turn.has_key(future_turn):
+ self.scheduled_moves_at_turn[future_turn] = []
+ self.scheduled_moves_at_turn[future_turn].append(future_move)
+ ships_left_to_send -= ships_to_send
+ self.decrease_ships_available(source_planet, 0, ships_to_send)
+ if ships_left_to_send == 0:
+ break
+ self.planets_attacked.append(best_planet_to_attack)
def doPostOffense(self):
log.info("Post-Offense phase")
- if len(self.universe.enemy_planets) == 0:
+ if len(self.enemy_planets) == 0:
return
- planets_to_send_to = self.universe.my_planets
+ planets_to_send_to = copy(self.my_planets)
neutral_candidate = self.closest_to_enemy_neutral_under_my_attack()
- if neutral_candidate is not None and neutral_candidate.ship_count <= 10:
- planets_to_send_to |= neutral_candidate
-
- # cache closest and com enemy planet distances
- closest_enemy_planet_distance_map = {}
- com_enemy_planet_distance_map = {}
- for planet in planets_to_send_to:
- closest_enemy_planet_distance_map[planet] = self.closest_enemy_planet_distance(planet)
- com_enemy_planet_distance_map[planet] = self.enemy_com.distance(planet)
-
- my_nearest_to_enemy_planets = sorted(planets_to_send_to, key=lambda p : p.distance(self.enemy_com) + p.id/1000000.0)
-
- for source_planet in self.universe.my_planets:
- if self.ships_available[source_planet] > 0:
- #log.info("Post-Offense for %s" % source_planet)
- for dest_planet in my_nearest_to_enemy_planets:
- distance = source_planet.distance(dest_planet)
- if distance > 0 and distance < com_enemy_planet_distance_map[source_planet]:
- if com_enemy_planet_distance_map[dest_planet] < com_enemy_planet_distance_map[source_planet] and \
- closest_enemy_planet_distance_map[dest_planet] <= closest_enemy_planet_distance_map[source_planet]:
- source_planet.send_fleet(dest_planet, self.ships_available[source_planet])
- self.ships_available[source_planet] = 0
- break
-
+ if neutral_candidate is not None:
+ planets_to_send_to = planets_to_send_to | neutral_candidate
+
+ for source_planet in self.my_planets:
+ closest_enemy_planet = self.closest_enemy_planet(source_planet)
+ #log.info("Eval Post-Offense for %s: closest enemy is %s" % (source_planet, closest_enemy_planet))
+ min_distance_to_enemy = 1000000
+ dest_planet = None
+ for planet_to_send_to in sorted(planets_to_send_to, key=lambda p : p.id if p.id != source_planet.id else 1000000):
+ if source_planet.distance(planet_to_send_to) < source_planet.distance(closest_enemy_planet) \
+ and planet_to_send_to.distance(closest_enemy_planet) < min_distance_to_enemy:
+ min_distance_to_enemy = planet_to_send_to.distance(closest_enemy_planet)
+ dest_planet = planet_to_send_to
+ if dest_planet is not None and source_planet.id != dest_planet.id and self.ships_available_at_turn[source_planet][0] > 0:
+ ships_to_send = min(self.ships_available_at_turn[source_planet][0], source_planet.ship_count)
+ self.send_fleet(source_planet, dest_planet, ships_to_send)
+ self.decrease_ships_available(source_planet, 0, ships_to_send)
def do_turn(self):
- if len(self.universe.my_planets) == 0:
+ self.all_planets = self.universe.all_planets
+ self.my_planets = self.universe.my_planets
+ self.enemy_planets = self.universe.enemy_planets
+ self.nobodies_planets = self.universe.nobodies_planets
+ self.not_my_planets = self.universe.not_my_planets
+ self.current_turn = self.universe.game.turn_count
+ self.planets_attacked = []
+
+ if len(self.my_planets) == 0:
return
self.doPrep()
- self.doDefenseOffense()
+ self.doScheduled()
+ self.doDefense()
+ self.doOffense()
self.doPostOffense()
-Game(MyBot, universe_class=Universe2, planet_class=Planet2)
+Game(MyBot, universe_class=Universe3, planet_class=Planet2)
View
11 README
@@ -1,8 +1,9 @@
-BIG FAT WARNING: In the latest release self.universe.planets and universe.fleets are
-no longer dicts but also sets (to be consistent with .my_planets etc.).
-So if you are using those in your code, update it accordingly!
+This is RebelXT's submission for Google AI Challenge.
+See http://ai-contest.com for context.
+
+Link to my profile: http://ai-contest.com/profile.php?user_id=5853
+
+Python 2.5 or 2.6 is required.
-This is an alternative python interface to the Google AI-Contest (http://ai-contest.com).
-See the example bots and planetwars/universe.py and planetwars/game.py for a bit of documentation.
View
225 archive/MyBot_100.py
@@ -0,0 +1,225 @@
+from planetwars import BaseBot, Game
+from planetwars.universe2 import Universe2
+from planetwars.planet import Planet
+from planetwars.planet2 import Planet2
+from planetwars.universe import player, Fleet
+from logging import getLogger, sys
+import planetwars.planet
+from math import ceil
+
+log = getLogger(__name__)
+
+# prev 15 issue not re-inforcing attack on center
+# dual 21 - issue with me losing a small planet and exposing large neighbours to attack
+# linear programming to pick best moves
+# attack from multiple planets
+# defend from multiple planets
+# scoring function to 200 turns?
+# scoring function needs to take planet's neighbours into account
+# overestimating max_aid?
+# performance issue
+class MyBot(BaseBot):
+
+ def total_fleet_ship_count(self, owner):
+ return sum( [ fleet.ship_count for fleet in self.universe.find_fleets(owner) ] )
+
+ def closest_enemy_planet_distance(self, p):
+ return min((lambda ep:ep.distance(p))(ep) for ep in self.universe.enemy_planets)
+
+ def enemy_ships_reinforcing(self, planet, turn):
+ return sum( [ fleet.ship_count for fleet in planet.reinforcement_fleets if fleet.owner in player.NOT_ME and fleet.turns_remaining <= turn ] )
+
+ def doPrep(self):
+ log.info("Prep phase")
+
+ self.max_distance_between_planets = 0
+ for p1 in self.universe.all_planets:
+ for p2 in self.universe.all_planets:
+ self.max_distance_between_planets = max(self.max_distance_between_planets, p1.distance(p2))
+ #log.info("Max distance: %s" % self.max_distance_between_planets)
+
+
+ # calculate current high level metrics
+ self.my_total_ships_available = 0
+ self.my_total_ships = 0
+ self.my_total_growth_rate = 0
+ self.enemy_total_ships_available = 0
+ self.enemy_total_ships = 0
+ self.enemy_total_growth_rate = 0
+ self.ships_available = {}
+ self.ships_needed = {}
+ self.planet_timeline = {}
+ for planet in self.universe.all_planets:
+ if len(planet.attacking_fleets) == 0:
+ self.ships_available[planet] = planet.ship_count
+ self.ships_needed[planet] = 0
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ else:
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ max_needed = 0
+ min_available = 1000000
+ #log.info("timeline for %s: %s" % (planet, self.planet_timeline[planet]))
+ for step in self.planet_timeline[planet]:
+ owner = step[0]
+ ship_count = step[1]
+ if owner != planet.owner:
+ max_needed = max(max_needed, ship_count)
+ else:
+ min_available = min(min_available, ship_count)
+ if max_needed > 0:
+ # do we bail if we are going to lose this planet anyway?
+ self.ships_available[planet] = 0
+ self.ships_needed[planet] = max_needed
+ else:
+ self.ships_available[planet] = min_available
+ self.ships_needed[planet] = 0
+
+ if (planet.owner == player.ME):
+ self.my_total_ships_available += self.ships_available[planet]
+ self.my_total_growth_rate += planet.growth_rate
+ self.my_total_ships += planet.ship_count
+ else:
+ self.enemy_total_ships_available += self.ships_available[planet]
+ self.enemy_total_growth_rate += planet.growth_rate
+ self.enemy_total_ships += planet.ship_count
+ #log.info("avail ships for %s: %s" % (planet, self.ships_available[planet]))
+
+ # prevent initial overexpansion
+ if self.universe.game.turn_count <= 2:
+ for my_planet in self.universe.my_planets:
+ for enemy_planet in self.universe.enemy_planets:
+ max_enemy_fleet = self.ships_available[enemy_planet]
+ distance = my_planet.distance(enemy_planet)
+ ships_needed_for_safety = max_enemy_fleet-distance*my_planet.growth_rate
+ if ships_needed_for_safety > (my_planet.ship_count - self.ships_available[my_planet]):
+ deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available[my_planet])
+ #log.info("deficit for %s: %s" % (my_planet, deficit))
+ if deficit > self.ships_available[my_planet]:
+ deficit = self.ships_available[my_planet]
+
+ self.ships_available[my_planet] -= deficit
+ self.my_total_ships_available -= deficit
+
+ self.my_total_ships += self.total_fleet_ship_count(player.ME)
+ self.enemy_total_ships += self.total_fleet_ship_count(player.NOT_ME)
+
+ # calculate enemy's center of mass
+ weighted_x = 0
+ weighted_y = 0
+ div = 0
+ for planet in self.universe.enemy_planets:
+ weighted_x += planet.position.x * (self.ships_available[planet] + planet.growth_rate)
+ weighted_y += planet.position.y * (self.ships_available[planet] + planet.growth_rate)
+ div += self.ships_available[planet] + planet.growth_rate
+ if div == 0:
+ div = 1
+
+ self.enemy_com = Planet(self.universe, 666, weighted_x/div, weighted_y/div, 2, 0, 0)
+
+ # For every planet, and every turn, calculate how many ships the enemy CAN sent to it's aid
+ self.max_aid_at_turn = {}
+ for planet in self.universe.all_planets:
+ self.max_aid_at_turn[planet] = {}
+ for turn in range(1, self.max_distance_between_planets+1):
+ max_aid = 0
+ for enemy_planet in self.universe.all_planets:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) < turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][turn - planet.distance(enemy_planet)]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet_time_step[1]
+ if self.planet_timeline[planet][turn-1][0] in player.ENEMIES:
+ max_aid += self.planet_timeline[planet][turn-1][1]
+ self.max_aid_at_turn[planet][turn] = max_aid
+ #log.info("Max aid for %s at %s: %s" % (planet.id, turn, self.max_aid_at_turn[planet][turn]))
+ #log.info("Max aid: %s" % self.max_aid_at_turn)
+
+ log.info("MY STATUS: %s/%s - %s available" % (self.my_total_ships, self.my_total_growth_rate, self.my_total_ships_available))
+ log.info("ENEMY STATUS: %s/%s - %s available" % (self.enemy_total_ships, self.enemy_total_growth_rate, self.enemy_total_ships_available))
+ #log.info("ENEMY COM: %s, %s" % (self.enemy_com.position.x, self.enemy_com.position.y))
+
+ def doDefenseOffense(self):
+ log.info("Offense/Defense phase")
+
+ possible_moves = []
+ for my_planet in self.universe.my_planets:
+ for planet_to_attack in self.universe.all_planets:
+ if planet_to_attack.id == my_planet.id:
+ continue
+ attack_distance = my_planet.distance(planet_to_attack)
+ planet_to_attack_future = self.planet_timeline[planet_to_attack][attack_distance-1]
+ planet_to_attack_future_owner = planet_to_attack_future[0]
+ cost_to_conquer = -1
+ time_to_profit = 0
+ if planet_to_attack_future_owner == player.NOBODY:
+ cost_to_conquer = planet_to_attack_future[1]
+ if planet_to_attack.growth_rate > 0:
+ time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate))
+ if (time_to_profit+attack_distance) >= self.max_distance_between_planets:
+ time_to_profit = self.max_distance_between_planets - attack_distance
+ #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
+ else:
+ if planet_to_attack_future_owner in player.ENEMIES:
+ cost_to_conquer = 0
+
+ max_aid = self.max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit]
+ ships_to_send = cost_to_conquer + max_aid + 1
+ if planet_to_attack_future_owner != player.ME and ships_to_send > 0 and ships_to_send <= self.ships_available[my_planet]:
+ if self.planet_timeline[planet_to_attack][attack_distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][attack_distance-2][0] == player.NOBODY:
+ continue
+ attack_score = (self.max_distance_between_planets - attack_distance + 40) * planet_to_attack.growth_rate
+ possible_moves.append((my_planet, planet_to_attack, ships_to_send, attack_score))
+ #log.info("Attack score of %s from %s is: %s - %s ships" % (planet_to_attack, my_planet, attack_score, ships_to_send))
+
+ # execute the best moves
+ planets_attacked = []
+ sorted_moves = sorted(possible_moves, key=lambda m : m[3], reverse=True)
+ log.info("Best moves: %s" % len(sorted_moves))
+
+ for move in sorted_moves:
+ ships_to_send = move[2]
+ planet_to_attack = move[1]
+ my_planet = move[0]
+ if ships_to_send <= self.ships_available[my_planet] and planet_to_attack not in planets_attacked:
+ my_planet.send_fleet(planet_to_attack, ships_to_send)
+ self.ships_available[my_planet] -= ships_to_send
+ planets_attacked.append(planet_to_attack)
+
+ def doPostOffense(self):
+ log.info("Post-Offense phase")
+ if len(self.universe.enemy_planets) == 0:
+ return
+
+ planets_to_send_to = self.universe.my_planets
+ # cache closest and com enemy planet distances
+ closest_enemy_planet_distance_map = {}
+ com_enemy_planet_distance_map = {}
+ for planet in planets_to_send_to:
+ closest_enemy_planet_distance_map[planet] = self.closest_enemy_planet_distance(planet)
+ com_enemy_planet_distance_map[planet] = self.enemy_com.distance(planet)
+
+ my_nearest_to_enemy_planets = sorted(planets_to_send_to, key=lambda p : p.distance(self.enemy_com) + p.id/1000000.0)
+
+ for source_planet in self.universe.my_planets:
+ if self.ships_available[source_planet] > 0:
+ #log.info("Post-Offense for %s" % source_planet)
+ for dest_planet in my_nearest_to_enemy_planets:
+ distance = source_planet.distance(dest_planet)
+ if distance > 0 and closest_enemy_planet_distance_map[dest_planet] <= closest_enemy_planet_distance_map[source_planet]:
+ if com_enemy_planet_distance_map[dest_planet] < com_enemy_planet_distance_map[source_planet]:
+ source_planet.send_fleet(dest_planet, self.ships_available[source_planet])
+ self.ships_available[source_planet] = 0
+ break
+
+
+ def do_turn(self):
+ if len(self.universe.my_planets) == 0:
+ return
+
+ self.doPrep()
+ self.doDefenseOffense()
+ self.doPostOffense()
+
+
+Game(MyBot, universe_class=Universe2, planet_class=Planet2)
View
333 archive/MyBot_101.py
@@ -0,0 +1,333 @@
+from planetwars import BaseBot, Game
+from planetwars.universe2 import Universe2
+from planetwars.planet import Planet
+from planetwars.planet2 import Planet2
+from planetwars.universe import player, Fleet
+from logging import getLogger, sys
+import planetwars.planet
+from math import ceil
+
+log = getLogger(__name__)
+
+# FIXED bug: 0 growth planets should not be conquered
+# dual 21 - issue with me losing a small planet and exposing large neighbours to attack
+# linear programming to pick best moves
+# attack from multiple planets
+# defend from multiple planets
+# scoring function to 200 turns?
+# scoring function needs to take planet's neighbours into account
+# overestimating max_aid?
+# performance issue
+class MyBot(BaseBot):
+
+ def zeros(self,rows,cols):
+ row = []
+ data = []
+ for i in range(cols):
+ row.append(0)
+ for i in range(rows):
+ data.append(row[:])
+ return data
+
+ # v = list of item values or profit
+ # w = list of item weight or cost
+ # W = max weight or max cost for the knapsack
+ def zeroOneKnapsack(self, v, w, W):
+ # c is the cost matrix
+ c = []
+ n = len(v)
+ c = self.zeros(n,W+1)
+ for i in range(0,n):
+ #for ever possible weight
+ for j in range(0,W+1):
+ #can we add this item to this?
+ if (w[i] > j):
+ c[i][j] = c[i-1][j]
+ else:
+ c[i][j] = max(c[i-1][j],v[i] +c[i-1][j-w[i]])
+ return [c[n-1][W], self.getUsedItems(w,c)]
+
+ # w = list of item weight or cost
+ # c = the cost matrix created by the dynamic programming solution
+ def getUsedItems(self,w,c):
+ # item count
+ i = len(c)-1
+ currentW = len(c[0])-1
+ # set everything to not marked
+ marked = []
+ for i in range(i+1):
+ marked.append(0)
+ while (i >= 0 and currentW >=0):
+ if (i==0 and c[i][currentW] >0 )or c[i][currentW] != c[i-1][currentW]:
+ marked[i] =1
+ currentW = currentW-w[i]
+ i = i-1
+ return marked
+
+ def total_fleet_ship_count(self, owner):
+ return sum( [ fleet.ship_count for fleet in self.universe.find_fleets(owner) ] )
+
+ def closest_enemy_planet_distance(self, p):
+ return min((lambda ep:ep.distance(p))(ep) for ep in self.universe.enemy_planets)
+
+ def enemy_ships_reinforcing(self, planet, turn):
+ return sum( [ fleet.ship_count for fleet in planet.reinforcement_fleets if fleet.owner in player.NOT_ME and fleet.turns_remaining <= turn ] )
+
+ def my_fleets_attacking(self, planet):
+ return sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME] )
+
+ def closest_to_enemy_neutral_under_my_attack(self):
+ best_distance = 1000000
+ result_planet = None
+ for planet in self.universe.nobodies_planets:
+ if self.my_fleets_attacking(planet) > 0:
+ distance = self.enemy_com.distance(planet)
+ if distance < best_distance:
+ best_distance = distance
+ result_planet = planet
+ return result_planet
+
+ def doPrep(self):
+ log.info("Prep phase")
+
+ self.max_distance_between_planets = 0
+ for p1 in self.universe.all_planets:
+ for p2 in self.universe.all_planets:
+ self.max_distance_between_planets = max(self.max_distance_between_planets, p1.distance(p2))
+ #log.info("Max distance: %s" % self.max_distance_between_planets)
+
+
+ # calculate current high level metrics
+ self.my_total_ships_available = 0
+ self.my_total_ships = 0
+ self.my_total_growth_rate = 0
+ self.enemy_total_ships_available = 0
+ self.enemy_total_ships = 0
+ self.enemy_total_growth_rate = 0
+ self.ships_available = {}
+ self.ships_needed = {}
+ self.planet_timeline = {}
+ for planet in self.universe.all_planets:
+ if len(planet.attacking_fleets) == 0:
+ self.ships_available[planet] = planet.ship_count
+ self.ships_needed[planet] = 0
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ else:
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ max_needed = 0
+ min_available = 1000000
+ #log.info("timeline for %s: %s" % (planet, self.planet_timeline[planet]))
+ for step in self.planet_timeline[planet]:
+ owner = step[0]
+ ship_count = step[1]
+ if owner != planet.owner:
+ max_needed = max(max_needed, ship_count)
+ else:
+ min_available = min(min_available, ship_count)
+ if max_needed > 0:
+ # do we bail if we are going to lose this planet anyway?
+ self.ships_available[planet] = 0
+ self.ships_needed[planet] = max_needed
+ else:
+ self.ships_available[planet] = min_available
+ self.ships_needed[planet] = 0
+
+ if (planet.owner == player.ME):
+ self.my_total_ships_available += self.ships_available[planet]
+ self.my_total_growth_rate += planet.growth_rate
+ self.my_total_ships += planet.ship_count
+ else:
+ self.enemy_total_ships_available += self.ships_available[planet]
+ self.enemy_total_growth_rate += planet.growth_rate
+ self.enemy_total_ships += planet.ship_count
+ #log.info("avail ships for %s: %s" % (planet, self.ships_available[planet]))
+
+ # prevent initial overexpansion
+ if self.universe.game.turn_count <= 2:
+ for my_planet in self.universe.my_planets:
+ for enemy_planet in self.universe.enemy_planets:
+ max_enemy_fleet = self.ships_available[enemy_planet]
+ distance = my_planet.distance(enemy_planet)
+ ships_needed_for_safety = max_enemy_fleet-distance*my_planet.growth_rate
+ if ships_needed_for_safety > (my_planet.ship_count - self.ships_available[my_planet]):
+ deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available[my_planet])
+ #log.info("deficit for %s: %s" % (my_planet, deficit))
+ if deficit > self.ships_available[my_planet]:
+ deficit = self.ships_available[my_planet]
+
+ self.ships_available[my_planet] -= deficit
+ self.my_total_ships_available -= deficit
+
+ self.my_total_ships += self.total_fleet_ship_count(player.ME)
+ self.enemy_total_ships += self.total_fleet_ship_count(player.NOT_ME)
+
+ # calculate enemy's center of mass
+ weighted_x = 0
+ weighted_y = 0
+ div = 0
+ for planet in self.universe.enemy_planets:
+ weighted_x += planet.position.x * (self.ships_available[planet] + planet.growth_rate)
+ weighted_y += planet.position.y * (self.ships_available[planet] + planet.growth_rate)
+ div += self.ships_available[planet] + planet.growth_rate
+ if div == 0:
+ div = 1
+
+ self.enemy_com = Planet(self.universe, 666, weighted_x/div, weighted_y/div, 2, 0, 0)
+
+ # For every planet, and every turn, calculate how many ships the enemy CAN sent to it's aid
+ self.max_aid_at_turn = {}
+ for planet in self.universe.all_planets:
+ self.max_aid_at_turn[planet] = {}
+ for turn in range(1, self.max_distance_between_planets+1):
+ max_aid = 0
+ for enemy_planet in self.universe.all_planets:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) < turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][turn - planet.distance(enemy_planet)]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet_time_step[1]
+ #log.info("adding to max aid: %s" % enemy_planet_time_step[1])
+ else:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) == turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][0]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet.ship_count
+ if self.planet_timeline[planet][turn-1][0] in player.ENEMIES:
+ max_aid += self.planet_timeline[planet][turn-1][1]
+ #log.info("self aid: %s" % self.planet_timeline[planet][turn-1][1])
+ self.max_aid_at_turn[planet][turn] = max_aid
+ #log.info("Max aid for %s at %s: %s" % (planet.id, turn, self.max_aid_at_turn[planet][turn]))
+ #log.info("Max aid: %s" % self.max_aid_at_turn)
+
+ log.info("MY STATUS: %s/%s - %s available" % (self.my_total_ships, self.my_total_growth_rate, self.my_total_ships_available))
+ log.info("ENEMY STATUS: %s/%s - %s available" % (self.enemy_total_ships, self.enemy_total_growth_rate, self.enemy_total_ships_available))
+ #log.info("ENEMY COM: %s, %s" % (self.enemy_com.position.x, self.enemy_com.position.y))
+
+ def doDefenseOffense(self):
+ log.info("Offense/Defense phase")
+
+ possible_moves = []
+ for my_planet in self.universe.my_planets:
+ for planet_to_attack in self.universe.all_planets:
+ if planet_to_attack.id == my_planet.id:
+ continue
+ attack_distance = my_planet.distance(planet_to_attack)
+ planet_to_attack_future = self.planet_timeline[planet_to_attack][attack_distance-1]
+ planet_to_attack_future_owner = planet_to_attack_future[0]
+ cost_to_conquer = -1
+ time_to_profit = 0
+ if planet_to_attack_future_owner == player.NOBODY:
+ cost_to_conquer = planet_to_attack_future[1]
+ if planet_to_attack.growth_rate > 0:
+ time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate))
+ else:
+ time_to_profit = 1000000
+ if (time_to_profit+attack_distance) >= self.max_distance_between_planets:
+ time_to_profit = self.max_distance_between_planets - attack_distance
+ #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
+ else:
+ if planet_to_attack_future_owner in player.ENEMIES:
+ cost_to_conquer = 0
+
+ max_aid = self.max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit]
+ ships_to_send = cost_to_conquer + max_aid + 1
+ if planet_to_attack_future_owner != player.ME and ships_to_send > 0 and ships_to_send <= self.ships_available[my_planet]:
+ if self.planet_timeline[planet_to_attack][attack_distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][attack_distance-2][0] == player.NOBODY:
+ continue
+ attack_score = (self.max_distance_between_planets - attack_distance + 40) * planet_to_attack.growth_rate
+ possible_moves.append((my_planet, planet_to_attack, ships_to_send, attack_score))
+ log.info("Attack score of %s from %s is: %s - %s ships" % (planet_to_attack, my_planet, attack_score, ships_to_send))
+
+ # execute the best moves
+ planets_attacked = []
+ sorted_moves = sorted(possible_moves, key=lambda m : m[3], reverse=True)
+ log.info("Best moves: %s" % len(sorted_moves))
+
+ if self.universe.game.turn_count == 1:
+ candidates = []
+ candidate_map = {}
+ my_home = list(self.universe.my_planets)[0]
+ enemy_home = list(self.universe.enemy_planets)[0]
+ home_planet_distance = my_home.distance(enemy_home)
+ ships_available = min(my_home.ship_count, my_home.growth_rate * home_planet_distance)
+
+ i = 0
+ max_attack_distance=0
+ for p in sorted(self.universe.nobodies_planets, key=lambda p : p.ship_count):
+ if p.distance(my_home) < p.distance(enemy_home):
+ candidates.append(p)
+ candidate_map[i] = p
+ max_attack_distance = max(max_attack_distance, p.distance(my_home))
+ i += 1
+
+ weights = []
+ profits = []
+ for c in candidates:
+ attack_score = (self.max_distance_between_planets - c.distance(my_home) + 40) * c.growth_rate
+ weights.append(c.ship_count+1)
+ profits.append(attack_score)
+
+ log.info("weights: %s" % weights)
+ log.info("profits: %s" % profits)
+ log.info("available: %s" % ships_available)
+
+ best_planets_to_attack = self.zeroOneKnapsack(profits,weights,ships_available)
+ log.info("best planets: %s" % best_planets_to_attack)
+
+ sorted_moves = []
+ for i in range(len(best_planets_to_attack[1])):
+ if (best_planets_to_attack[1][i] != 0):
+ planet_to_attack = candidate_map[i]
+ sorted_moves.append((my_home, planet_to_attack, planet_to_attack.ship_count+1, 0))
+
+ for move in sorted_moves:
+ ships_to_send = move[2]
+ planet_to_attack = move[1]
+ my_planet = move[0]
+ if ships_to_send <= self.ships_available[my_planet] and planet_to_attack not in planets_attacked:
+ my_planet.send_fleet(planet_to_attack, ships_to_send)
+ self.ships_available[my_planet] -= ships_to_send
+ planets_attacked.append(planet_to_attack)
+
+ def doPostOffense(self):
+ log.info("Post-Offense phase")
+ if len(self.universe.enemy_planets) == 0:
+ return
+
+ planets_to_send_to = self.universe.my_planets
+ neutral_candidate = self.closest_to_enemy_neutral_under_my_attack()
+ if neutral_candidate is not None:
+ planets_to_send_to |= neutral_candidate
+
+ # cache closest and com enemy planet distances
+ closest_enemy_planet_distance_map = {}
+ com_enemy_planet_distance_map = {}
+ for planet in planets_to_send_to:
+ closest_enemy_planet_distance_map[planet] = self.closest_enemy_planet_distance(planet)
+ com_enemy_planet_distance_map[planet] = self.enemy_com.distance(planet)
+
+ my_nearest_to_enemy_planets = sorted(planets_to_send_to, key=lambda p : p.distance(self.enemy_com) + p.id/1000000.0)
+
+ for source_planet in self.universe.my_planets:
+ if self.ships_available[source_planet] > 0:
+ #log.info("Post-Offense for %s" % source_planet)
+ for dest_planet in my_nearest_to_enemy_planets:
+ distance = source_planet.distance(dest_planet)
+ if distance > 0 and closest_enemy_planet_distance_map[dest_planet] <= closest_enemy_planet_distance_map[source_planet]:
+ if com_enemy_planet_distance_map[dest_planet] < com_enemy_planet_distance_map[source_planet]:
+ source_planet.send_fleet(dest_planet, self.ships_available[source_planet])
+ self.ships_available[source_planet] = 0
+ break
+
+
+ def do_turn(self):
+ if len(self.universe.my_planets) == 0:
+ return
+
+ self.doPrep()
+ self.doDefenseOffense()
+ self.doPostOffense()
+
+
+Game(MyBot, universe_class=Universe2, planet_class=Planet2)
View
335 archive/MyBot_102.py
@@ -0,0 +1,335 @@
+from planetwars import BaseBot, Game
+from planetwars.universe2 import Universe2
+from planetwars.planet import Planet
+from planetwars.planet2 import Planet2
+from planetwars.universe import player, Fleet
+from logging import getLogger, sys
+import planetwars.planet
+from math import ceil
+
+log = getLogger(__name__)
+
+# FIXED bug: 0 growth planets should not be conquered
+# dual 21 - issue with me losing a small planet and exposing large neighbours to attack
+# linear programming to pick best moves
+# attack from multiple planets
+# defend from multiple planets
+# scoring function to 200 turns?
+# scoring function needs to take planet's neighbours into account
+# overestimating max_aid?
+# performance issue
+class MyBot(BaseBot):
+
+ def zeros(self,rows,cols):
+ row = []
+ data = []
+ for i in range(cols):
+ row.append(0)
+ for i in range(rows):
+ data.append(row[:])
+ return data
+
+ # v = list of item values or profit
+ # w = list of item weight or cost
+ # W = max weight or max cost for the knapsack
+ def zeroOneKnapsack(self, v, w, W):
+ # c is the cost matrix
+ c = []
+ n = len(v)
+ c = self.zeros(n,W+1)
+ for i in range(0,n):
+ #for ever possible weight
+ for j in range(0,W+1):
+ #can we add this item to this?
+ if (w[i] > j):
+ c[i][j] = c[i-1][j]
+ else:
+ c[i][j] = max(c[i-1][j],v[i] +c[i-1][j-w[i]])
+ return [c[n-1][W], self.getUsedItems(w,c)]
+
+ # w = list of item weight or cost
+ # c = the cost matrix created by the dynamic programming solution
+ def getUsedItems(self,w,c):
+ # item count
+ i = len(c)-1
+ currentW = len(c[0])-1
+ # set everything to not marked
+ marked = []
+ for i in range(i+1):
+ marked.append(0)
+ while (i >= 0 and currentW >=0):
+ if (i==0 and c[i][currentW] >0 )or c[i][currentW] != c[i-1][currentW]:
+ marked[i] =1
+ currentW = currentW-w[i]
+ i = i-1
+ return marked
+
+ def total_fleet_ship_count(self, owner):
+ return sum( [ fleet.ship_count for fleet in self.universe.find_fleets(owner) ] )
+
+ def closest_enemy_planet_distance(self, p):
+ return min((lambda ep:ep.distance(p))(ep) for ep in self.universe.enemy_planets)
+
+ def enemy_ships_reinforcing(self, planet, turn):
+ return sum( [ fleet.ship_count for fleet in planet.reinforcement_fleets if fleet.owner in player.NOT_ME and fleet.turns_remaining <= turn ] )
+
+ def my_fleets_attacking(self, planet):
+ return sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME] )
+
+ def closest_to_enemy_neutral_under_my_attack(self):
+ best_distance = 1000000
+ result_planet = None
+ for planet in self.universe.nobodies_planets:
+ if self.my_fleets_attacking(planet) > 0:
+ distance = self.enemy_com.distance(planet)
+ if distance < best_distance:
+ best_distance = distance
+ result_planet = planet
+ return result_planet
+
+ def doPrep(self):
+ log.info("Prep phase")
+
+ self.max_distance_between_planets = 0
+ for p1 in self.universe.all_planets:
+ for p2 in self.universe.all_planets:
+ self.max_distance_between_planets = max(self.max_distance_between_planets, p1.distance(p2))
+ #log.info("Max distance: %s" % self.max_distance_between_planets)
+
+
+ # calculate current high level metrics
+ self.my_total_ships_available = 0
+ self.my_total_ships = 0
+ self.my_total_growth_rate = 0
+ self.enemy_total_ships_available = 0
+ self.enemy_total_ships = 0
+ self.enemy_total_growth_rate = 0
+ self.ships_available = {}
+ self.ships_needed = {}
+ self.planet_timeline = {}
+ for planet in self.universe.all_planets:
+ if len(planet.attacking_fleets) == 0:
+ self.ships_available[planet] = planet.ship_count
+ self.ships_needed[planet] = 0
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ else:
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ max_needed = 0
+ min_available = 1000000
+ #log.info("timeline for %s: %s" % (planet, self.planet_timeline[planet]))
+ for step in self.planet_timeline[planet]:
+ owner = step[0]
+ ship_count = step[1]
+ if owner != planet.owner:
+ max_needed = max(max_needed, ship_count)
+ else:
+ min_available = min(min_available, ship_count)
+ if max_needed > 0:
+ # do we bail if we are going to lose this planet anyway?
+ self.ships_available[planet] = 0
+ self.ships_needed[planet] = max_needed
+ else:
+ self.ships_available[planet] = min_available
+ self.ships_needed[planet] = 0
+
+ if (planet.owner == player.ME):
+ self.my_total_ships_available += self.ships_available[planet]
+ self.my_total_growth_rate += planet.growth_rate
+ self.my_total_ships += planet.ship_count
+ else:
+ self.enemy_total_ships_available += self.ships_available[planet]
+ self.enemy_total_growth_rate += planet.growth_rate
+ self.enemy_total_ships += planet.ship_count
+ #log.info("avail ships for %s: %s" % (planet, self.ships_available[planet]))
+
+ # prevent initial overexpansion
+ if self.universe.game.turn_count <= 2:
+ for my_planet in self.universe.my_planets:
+ for enemy_planet in self.universe.enemy_planets:
+ max_enemy_fleet = self.ships_available[enemy_planet]
+ distance = my_planet.distance(enemy_planet)
+ ships_needed_for_safety = max_enemy_fleet-distance*my_planet.growth_rate
+ if ships_needed_for_safety > (my_planet.ship_count - self.ships_available[my_planet]):
+ deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available[my_planet])
+ #log.info("deficit for %s: %s" % (my_planet, deficit))
+ if deficit > self.ships_available[my_planet]:
+ deficit = self.ships_available[my_planet]
+
+ self.ships_available[my_planet] -= deficit
+ self.my_total_ships_available -= deficit
+
+ self.my_total_ships += self.total_fleet_ship_count(player.ME)
+ self.enemy_total_ships += self.total_fleet_ship_count(player.NOT_ME)
+
+ # calculate enemy's center of mass
+ weighted_x = 0
+ weighted_y = 0
+ div = 0
+ for planet in self.universe.enemy_planets:
+ weighted_x += planet.position.x * (self.ships_available[planet] + planet.growth_rate)
+ weighted_y += planet.position.y * (self.ships_available[planet] + planet.growth_rate)
+ div += self.ships_available[planet] + planet.growth_rate
+ if div == 0:
+ div = 1
+
+ self.enemy_com = Planet(self.universe, 666, weighted_x/div, weighted_y/div, 2, 0, 0)
+
+ # For every planet, and every turn, calculate how many ships the enemy CAN sent to it's aid
+ self.max_aid_at_turn = {}
+ for planet in self.universe.all_planets:
+ self.max_aid_at_turn[planet] = {}
+ for turn in range(1, self.max_distance_between_planets+1):
+ max_aid = 0
+ for enemy_planet in self.universe.all_planets:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) < turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][turn - planet.distance(enemy_planet)]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet_time_step[1]
+ #log.info("adding to max aid: %s" % enemy_planet_time_step[1])
+ else:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) == turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][0]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet.ship_count
+ if self.planet_timeline[planet][turn-1][0] in player.ENEMIES:
+ max_aid += self.planet_timeline[planet][turn-1][1]
+ #log.info("self aid: %s" % self.planet_timeline[planet][turn-1][1])
+ self.max_aid_at_turn[planet][turn] = max_aid
+ #log.info("Max aid for %s at %s: %s" % (planet.id, turn, self.max_aid_at_turn[planet][turn]))
+ #log.info("Max aid: %s" % self.max_aid_at_turn)
+
+ log.info("MY STATUS: %s/%s - %s available" % (self.my_total_ships, self.my_total_growth_rate, self.my_total_ships_available))
+ log.info("ENEMY STATUS: %s/%s - %s available" % (self.enemy_total_ships, self.enemy_total_growth_rate, self.enemy_total_ships_available))
+ #log.info("ENEMY COM: %s, %s" % (self.enemy_com.position.x, self.enemy_com.position.y))
+
+ def doDefenseOffense(self):
+ log.info("Offense/Defense phase")
+
+ possible_moves = []
+ for my_planet in self.universe.my_planets:
+ for planet_to_attack in self.universe.all_planets:
+ if planet_to_attack.id == my_planet.id:
+ continue
+ attack_distance = my_planet.distance(planet_to_attack)
+ planet_to_attack_future = self.planet_timeline[planet_to_attack][attack_distance-1]
+ planet_to_attack_future_owner = planet_to_attack_future[0]
+ cost_to_conquer = -1
+ time_to_profit = 0
+ if planet_to_attack_future_owner == player.NOBODY:
+ cost_to_conquer = planet_to_attack_future[1]
+ if planet_to_attack.growth_rate > 0:
+ time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate))
+ else:
+ time_to_profit = 1000000
+ if (time_to_profit+attack_distance) >= self.max_distance_between_planets:
+ time_to_profit = self.max_distance_between_planets - attack_distance
+ #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
+ else:
+ if planet_to_attack_future_owner in player.ENEMIES:
+ cost_to_conquer = 0
+
+ max_aid = self.max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit]
+ ships_to_send = cost_to_conquer + max_aid + 1
+ if planet_to_attack_future_owner != player.ME and ships_to_send > 0 and ships_to_send <= self.ships_available[my_planet]:
+ if self.planet_timeline[planet_to_attack][attack_distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][attack_distance-2][0] == player.NOBODY:
+ continue
+ attack_score = (self.max_distance_between_planets - attack_distance + 40) * planet_to_attack.growth_rate
+ #attack_score = max(0, (200 - self.universe.game.turn_count - attack_distance) * planet_to_attack.growth_rate)
+ possible_moves.append((my_planet, planet_to_attack, ships_to_send, attack_score))
+ log.info("Attack score of %s from %s is: %s - %s ships" % (planet_to_attack, my_planet, attack_score, ships_to_send))
+
+ # execute the best moves
+ planets_attacked = []
+ sorted_moves = sorted(possible_moves, key=lambda m : m[3], reverse=True)
+ log.info("Best moves: %s" % len(sorted_moves))
+
+ if self.universe.game.turn_count == 1:
+ candidates = []
+ candidate_map = {}
+ my_home = list(self.universe.my_planets)[0]
+ enemy_home = list(self.universe.enemy_planets)[0]
+ home_planet_distance = my_home.distance(enemy_home)
+ ships_available = min(my_home.ship_count, my_home.growth_rate * home_planet_distance)
+
+ i = 0
+ max_attack_distance=0
+ for p in sorted(self.universe.nobodies_planets, key=lambda p : p.ship_count):
+ if p.distance(my_home) < p.distance(enemy_home):
+ candidates.append(p)
+ candidate_map[i] = p
+ max_attack_distance = max(max_attack_distance, p.distance(my_home))
+ i += 1
+
+ weights = []
+ profits = []
+ for c in candidates:
+ attack_score = (self.max_distance_between_planets - c.distance(my_home) + 40) * c.growth_rate
+ #attack_score = max(0, (200 - self.universe.game.turn_count - c.distance(my_home)) * c.growth_rate)
+ weights.append(c.ship_count+1)
+ profits.append(attack_score)
+
+ log.info("weights: %s" % weights)
+ log.info("profits: %s" % profits)
+ log.info("available: %s" % ships_available)
+
+ best_planets_to_attack = self.zeroOneKnapsack(profits,weights,ships_available)
+ log.info("best planets: %s" % best_planets_to_attack)
+
+ sorted_moves = []
+ for i in range(len(best_planets_to_attack[1])):
+ if (best_planets_to_attack[1][i] != 0):
+ planet_to_attack = candidate_map[i]
+ sorted_moves.append((my_home, planet_to_attack, planet_to_attack.ship_count+1, 0))
+
+ for move in sorted_moves:
+ ships_to_send = move[2]
+ planet_to_attack = move[1]
+ my_planet = move[0]
+ if ships_to_send <= self.ships_available[my_planet] and planet_to_attack not in planets_attacked:
+ my_planet.send_fleet(planet_to_attack, ships_to_send)
+ self.ships_available[my_planet] -= ships_to_send
+ planets_attacked.append(planet_to_attack)
+
+ def doPostOffense(self):
+ log.info("Post-Offense phase")
+ if len(self.universe.enemy_planets) == 0:
+ return
+
+ planets_to_send_to = self.universe.my_planets
+ neutral_candidate = self.closest_to_enemy_neutral_under_my_attack()
+ if neutral_candidate is not None:
+ planets_to_send_to |= neutral_candidate
+
+ # cache closest and com enemy planet distances
+ closest_enemy_planet_distance_map = {}
+ com_enemy_planet_distance_map = {}
+ for planet in planets_to_send_to:
+ closest_enemy_planet_distance_map[planet] = self.closest_enemy_planet_distance(planet)
+ com_enemy_planet_distance_map[planet] = self.enemy_com.distance(planet)
+
+ my_nearest_to_enemy_planets = sorted(planets_to_send_to, key=lambda p : p.distance(self.enemy_com) + p.id/1000000.0)
+
+ for source_planet in self.universe.my_planets:
+ if self.ships_available[source_planet] > 0:
+ #log.info("Post-Offense for %s" % source_planet)
+ for dest_planet in my_nearest_to_enemy_planets:
+ distance = source_planet.distance(dest_planet)
+ if distance > 0 and closest_enemy_planet_distance_map[dest_planet] <= closest_enemy_planet_distance_map[source_planet]:
+ if distance < com_enemy_planet_distance_map[source_planet]:
+ source_planet.send_fleet(dest_planet, self.ships_available[source_planet])
+ self.ships_available[source_planet] = 0
+ break
+
+
+ def do_turn(self):
+ if len(self.universe.my_planets) == 0:
+ return
+
+ self.doPrep()
+ self.doDefenseOffense()
+ self.doPostOffense()
+
+
+Game(MyBot, universe_class=Universe2, planet_class=Planet2)
View
335 archive/MyBot_103.py
@@ -0,0 +1,335 @@
+from planetwars import BaseBot, Game
+from planetwars.universe2 import Universe2
+from planetwars.planet import Planet
+from planetwars.planet2 import Planet2
+from planetwars.universe import player, Fleet
+from logging import getLogger, sys
+import planetwars.planet
+from math import ceil
+
+log = getLogger(__name__)
+
+# FIXED bug: 0 growth planets should not be conquered
+# dual 21 - issue with me losing a small planet and exposing large neighbours to attack
+# linear programming to pick best moves
+# attack from multiple planets
+# defend from multiple planets
+# scoring function to 200 turns?
+# scoring function needs to take planet's neighbours into account
+# overestimating max_aid?
+# performance issue
+class MyBot(BaseBot):
+
+ def zeros(self,rows,cols):
+ row = []
+ data = []
+ for i in range(cols):
+ row.append(0)
+ for i in range(rows):
+ data.append(row[:])
+ return data
+
+ # v = list of item values or profit
+ # w = list of item weight or cost
+ # W = max weight or max cost for the knapsack
+ def zeroOneKnapsack(self, v, w, W):
+ # c is the cost matrix
+ c = []
+ n = len(v)
+ c = self.zeros(n,W+1)
+ for i in range(0,n):
+ #for ever possible weight
+ for j in range(0,W+1):
+ #can we add this item to this?
+ if (w[i] > j):
+ c[i][j] = c[i-1][j]
+ else:
+ c[i][j] = max(c[i-1][j],v[i] +c[i-1][j-w[i]])
+ return [c[n-1][W], self.getUsedItems(w,c)]
+
+ # w = list of item weight or cost
+ # c = the cost matrix created by the dynamic programming solution
+ def getUsedItems(self,w,c):
+ # item count
+ i = len(c)-1
+ currentW = len(c[0])-1
+ # set everything to not marked
+ marked = []
+ for i in range(i+1):
+ marked.append(0)
+ while (i >= 0 and currentW >=0):
+ if (i==0 and c[i][currentW] >0 )or c[i][currentW] != c[i-1][currentW]:
+ marked[i] =1
+ currentW = currentW-w[i]
+ i = i-1
+ return marked
+
+ def total_fleet_ship_count(self, owner):
+ return sum( [ fleet.ship_count for fleet in self.universe.find_fleets(owner) ] )
+
+ def closest_enemy_planet_distance(self, p):
+ return min((lambda ep:ep.distance(p))(ep) for ep in self.universe.enemy_planets)
+
+ def enemy_ships_reinforcing(self, planet, turn):
+ return sum( [ fleet.ship_count for fleet in planet.reinforcement_fleets if fleet.owner in player.NOT_ME and fleet.turns_remaining <= turn ] )
+
+ def my_fleets_attacking(self, planet):
+ return sum( [ 1 for fleet in planet.attacking_fleets if fleet.owner == player.ME] )
+
+ def closest_to_enemy_neutral_under_my_attack(self):
+ best_distance = 1000000
+ result_planet = None
+ for planet in self.universe.nobodies_planets:
+ if self.my_fleets_attacking(planet) > 0:
+ distance = self.enemy_com.distance(planet)
+ if distance < best_distance:
+ best_distance = distance
+ result_planet = planet
+ return result_planet
+
+ def doPrep(self):
+ log.info("Prep phase")
+
+ self.max_distance_between_planets = 0
+ for p1 in self.universe.all_planets:
+ for p2 in self.universe.all_planets:
+ self.max_distance_between_planets = max(self.max_distance_between_planets, p1.distance(p2))
+ #log.info("Max distance: %s" % self.max_distance_between_planets)
+
+
+ # calculate current high level metrics
+ self.my_total_ships_available = 0
+ self.my_total_ships = 0
+ self.my_total_growth_rate = 0
+ self.enemy_total_ships_available = 0
+ self.enemy_total_ships = 0
+ self.enemy_total_growth_rate = 0
+ self.ships_available = {}
+ self.ships_needed = {}
+ self.planet_timeline = {}
+ for planet in self.universe.all_planets:
+ if len(planet.attacking_fleets) == 0:
+ self.ships_available[planet] = planet.ship_count
+ self.ships_needed[planet] = 0
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ else:
+ simulation_distance = self.max_distance_between_planets
+ self.planet_timeline[planet] = planet.in_future_timeline(simulation_distance)
+ max_needed = 0
+ min_available = 1000000
+ #log.info("timeline for %s: %s" % (planet, self.planet_timeline[planet]))
+ for step in self.planet_timeline[planet]:
+ owner = step[0]
+ ship_count = step[1]
+ if owner != planet.owner:
+ max_needed = max(max_needed, ship_count)
+ else:
+ min_available = min(min_available, ship_count)
+ if max_needed > 0:
+ # do we bail if we are going to lose this planet anyway?
+ self.ships_available[planet] = 0
+ self.ships_needed[planet] = max_needed
+ else:
+ self.ships_available[planet] = min_available
+ self.ships_needed[planet] = 0
+
+ if (planet.owner == player.ME):
+ self.my_total_ships_available += self.ships_available[planet]
+ self.my_total_growth_rate += planet.growth_rate
+ self.my_total_ships += planet.ship_count
+ else:
+ self.enemy_total_ships_available += self.ships_available[planet]
+ self.enemy_total_growth_rate += planet.growth_rate
+ self.enemy_total_ships += planet.ship_count
+ #log.info("avail ships for %s: %s" % (planet, self.ships_available[planet]))
+
+ # prevent initial overexpansion
+ if self.universe.game.turn_count <= 2:
+ for my_planet in self.universe.my_planets:
+ for enemy_planet in self.universe.enemy_planets:
+ max_enemy_fleet = self.ships_available[enemy_planet]
+ distance = my_planet.distance(enemy_planet)
+ ships_needed_for_safety = max_enemy_fleet-distance*my_planet.growth_rate
+ if ships_needed_for_safety > (my_planet.ship_count - self.ships_available[my_planet]):
+ deficit = ships_needed_for_safety - (my_planet.ship_count - self.ships_available[my_planet])
+ #log.info("deficit for %s: %s" % (my_planet, deficit))
+ if deficit > self.ships_available[my_planet]:
+ deficit = self.ships_available[my_planet]
+
+ self.ships_available[my_planet] -= deficit
+ self.my_total_ships_available -= deficit
+
+ self.my_total_ships += self.total_fleet_ship_count(player.ME)
+ self.enemy_total_ships += self.total_fleet_ship_count(player.NOT_ME)
+
+ # calculate enemy's center of mass
+ weighted_x = 0
+ weighted_y = 0
+ div = 0
+ for planet in self.universe.enemy_planets:
+ weighted_x += planet.position.x * (self.ships_available[planet] + planet.growth_rate)
+ weighted_y += planet.position.y * (self.ships_available[planet] + planet.growth_rate)
+ div += self.ships_available[planet] + planet.growth_rate
+ if div == 0:
+ div = 1
+
+ self.enemy_com = Planet(self.universe, 666, weighted_x/div, weighted_y/div, 2, 0, 0)
+
+ # For every planet, and every turn, calculate how many ships the enemy CAN sent to it's aid
+ self.max_aid_at_turn = {}
+ for planet in self.universe.all_planets:
+ self.max_aid_at_turn[planet] = {}
+ for turn in range(1, self.max_distance_between_planets+1):
+ max_aid = 0
+ for enemy_planet in self.universe.all_planets:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) < turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][turn - planet.distance(enemy_planet)]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet_time_step[1]
+ #log.info("adding to max aid: %s" % enemy_planet_time_step[1])
+ else:
+ if enemy_planet.id != planet.id and planet.distance(enemy_planet) == turn:
+ enemy_planet_time_step = self.planet_timeline[enemy_planet][0]
+ if (enemy_planet_time_step[0] in player.ENEMIES):
+ max_aid += enemy_planet.ship_count
+ if self.planet_timeline[planet][turn-1][0] in player.ENEMIES:
+ max_aid += self.planet_timeline[planet][turn-1][1]
+ #log.info("self aid: %s" % self.planet_timeline[planet][turn-1][1])
+ self.max_aid_at_turn[planet][turn] = max_aid
+ #log.info("Max aid for %s at %s: %s" % (planet.id, turn, self.max_aid_at_turn[planet][turn]))
+ #log.info("Max aid: %s" % self.max_aid_at_turn)
+
+ log.info("MY STATUS: %s/%s - %s available" % (self.my_total_ships, self.my_total_growth_rate, self.my_total_ships_available))
+ log.info("ENEMY STATUS: %s/%s - %s available" % (self.enemy_total_ships, self.enemy_total_growth_rate, self.enemy_total_ships_available))
+ #log.info("ENEMY COM: %s, %s" % (self.enemy_com.position.x, self.enemy_com.position.y))
+
+ def doDefenseOffense(self):
+ log.info("Offense/Defense phase")
+
+ possible_moves = []
+ for my_planet in self.universe.my_planets:
+ for planet_to_attack in self.universe.all_planets:
+ if planet_to_attack.id == my_planet.id:
+ continue
+ attack_distance = my_planet.distance(planet_to_attack)
+ planet_to_attack_future = self.planet_timeline[planet_to_attack][attack_distance-1]
+ planet_to_attack_future_owner = planet_to_attack_future[0]
+ cost_to_conquer = -1
+ time_to_profit = 0
+ if planet_to_attack_future_owner == player.NOBODY:
+ cost_to_conquer = planet_to_attack_future[1]
+ if planet_to_attack.growth_rate > 0:
+ time_to_profit = int(ceil((cost_to_conquer+0.001)/planet_to_attack.growth_rate))
+ else:
+ time_to_profit = 1000000
+ if (time_to_profit+attack_distance) >= self.max_distance_between_planets:
+ time_to_profit = self.max_distance_between_planets - attack_distance
+ #log.info("Time to profit for %s is %s" % (planet_to_attack, time_to_profit))
+ else:
+ if planet_to_attack_future_owner in player.ENEMIES:
+ cost_to_conquer = 0
+
+ max_aid = self.max_aid_at_turn[planet_to_attack][attack_distance+time_to_profit]
+ ships_to_send = cost_to_conquer + max_aid + 1
+ if planet_to_attack_future_owner != player.ME and ships_to_send > 0 and ships_to_send <= self.ships_available[my_planet]:
+ if self.planet_timeline[planet_to_attack][attack_distance-1][0] in player.ENEMIES and self.planet_timeline[planet_to_attack][attack_distance-2][0] == player.NOBODY:
+ continue
+ attack_score = (self.max_distance_between_planets - attack_distance + 40) * planet_to_attack.growth_rate
+ #attack_score = max(0, (200 - self.universe.game.turn_count - attack_distance) * planet_to_attack.growth_rate)
+ possible_moves.append((my_planet, planet_to_attack, ships_to_send, attack_score))
+ log.info("Attack score of %s from %s is: %s - %s ships" % (planet_to_attack, my_planet, attack_score, ships_to_send))
+
+ # execute the best moves
+ planets_attacked = []
+ sorted_moves = sorted(possible_moves, key=lambda m : m[3], reverse=True)
+ log.info("Best moves: %s" % len(sorted_moves))
+
+ if self.universe.game.turn_count == 1:
+ candidates = []
+ candidate_map = {}
+ my_home = list(self.universe.my_planets)[0]
+ enemy_home = list(self.universe.enemy_planets)[0]
+ home_planet_distance = my_home.distance(enemy_home)
+ ships_available = min(my_home.ship_count, my_home.growth_rate * home_planet_distance)
+
+ i = 0
+ max_attack_distance=0
+ for p in sorted(self.universe.nobodies_planets, key=lambda p : p.ship_count):
+ if p.distance(my_home) < p.distance(enemy_home):
+ candidates.append(p)
+ candidate_map[i] = p
+ max_attack_distance = max(max_attack_distance, p.distance(my_home))
+ i += 1
+
+ weights = []
+ profits = []
+ for c in candidates:
+ attack_score = (self.max_distance_between_planets - c.distance(my_home) + 40) * c.growth_rate
+ #attack_score = max(0, (200 - self.universe.game.turn_count - c.distance(my_home)) * c.growth_rate)
+ weights.append(c.ship_count+1)
+ profits.append(attack_score)
+
+ log.info("weights: %s" % weights)
+ log.info("profits: %s" % profits)
+ log.info("available: %s" % ships_available)
+
+ best_planets_to_attack = self.zeroOneKnapsack(profits,weights,ships_available)
+ log.info("best planets: %s" % best_planets_to_attack)
+
+ sorted_moves = []
+ for i in range(len(best_planets_to_attack[1])):
+ if (best_planets_to_attack[1][i] != 0):
+ planet_to_attack = candidate_map[i]
+ sorted_moves.append((my_home, planet_to_attack, planet_to_attack.ship_count+1, 0))
+
+ for move in sorted_moves:
+ ships_to_send = move[2]
+ planet_to_attack = move[1]
+ my_planet = move[0]
+ if ships_to_send <= self.ships_available[my_planet] and planet_to_attack not in planets_attacked:
+ my_planet.send_fleet(planet_to_attack, ships_to_send)
+ self.ships_available[my_planet] -= ships_to_send
+ planets_attacked.append(planet_to_attack)
+
+ def doPostOffense(self):
+ log.info("Post-Offense phase")
+ if len(self.universe.enemy_planets) == 0:
+ return
+
+ planets_to_send_to = self.universe.my_planets
+ neutral_candidate = self.closest_to_enemy_neutral_under_my_attack()
+ if neutral_candidate is not None:
+ planets_to_send_to |= neutral_candidate
+
+ # cache closest and com enemy planet distances
+ closest_enemy_planet_distance_map = {}
+ com_enemy_planet_distance_map = {}
+ for planet in planets_to_send_to:
+ closest_enemy_planet_distance_map[planet] = self.closest_enemy_planet_distance(planet)
+ com_enemy_planet_distance_map[planet] = self.enemy_com.distance(planet)
+
+ my_nearest_to_enemy_planets = sorted(planets_to_send_to, key=lambda p : p.distance(self.enemy_com) + p.id/1000000.0)
+
+ for source_planet in self.universe.my_planets:
+ if self.ships_available[source_planet] > 0:
+ #log.info("Post-Offense for %s" % source_planet)
+ for dest_planet in my_nearest_to_enemy_planets:
+ distance = source_planet.distance(dest_planet)
+ if distance > 0 and distance < com_enemy_planet_distance_map[source_planet]:
+ if com_enemy_planet_distance_map[dest_planet] < com_enemy_planet_distance_map[source_planet]:
+ source_planet.send_fleet(dest_planet, self.ships_available[source_planet])
+ self.ships_available[source_planet] = 0
+ break
+
+
+ def do_turn(self):
+ if len(self.universe.my_planets) == 0:
+ return
+
+ self.doPrep()
+ self.doDefenseOffense()
+ self.doPostOffense()
+
+
+Game(MyBot, universe_class=Universe2, planet_class=Planet2)
View
336 archive/MyBot_104.py
@@ -0,0 +1,336 @@
+from planetwars import BaseBot, Game
+from planetwars.universe2 import Universe2
+from planetwars.planet import Planet
+from planetwars.planet2 import Planet2
+from planetwars.universe import player, Fleet