Example #1
0
def score_planet(pw: planet_wars.PlanetWars, p: planet_wars.Planet):
    """
    Function to give a planet a score based on many factors.
    :param pw: `PlanetWars` object
    :param p: `Planet` object
    :return: `float` score of planet
    """

    raw_score = get_raw_score(p)

    structural_score = 1 - (pythag(MY_PLANETS_CENTER,
                                   (p.x(), p.y())) / pw.map_size)

    surrounding_score = 0
    for planet in filter(lambda _p: _p != p, pw.planets()):
        temp = (
            1 -
            (pw.distance(p.planet_id(), planet.planet_id()) / pw.map_size))**5
        surrounding_score += get_raw_score(planet) * temp
    surrounding_score /= pw.total_growth

    latency_score = p.latency / pw.map_size

    center_score = 1 - (pw.distance(p.planet_id(), 0) / pw.map_size)

    score = 0
    score += raw_score
    score += STRUCTURAL_FACTOR * structural_score
    score += SURROUNDING_FACTOR * surrounding_score
    score += LATENCY_FACTOR * latency_score
    score += CENTER_FACTOR * center_score

    return score
Example #2
0
def furthest_meaningful_planet(pw: planet_wars.PlanetWars,
                               planet: planet_wars.Planet, owner: int):
    planets = tuple(filter(lambda p: p.owner() == owner, pw.planets()))
    fleets = tuple(filter(lambda f: f.owner() == owner, pw.fleets()))

    furthest_distance = 0
    for other_planet in planets:
        furthest_distance = max(
            furthest_distance,
            pw.distance(other_planet.planet_id(), planet.planet_id()))
    for fleet in fleets:
        furthest_distance = max(
            furthest_distance,
            fleet.turns_remaining() +
            pw.distance(fleet.destination_planet(), planet.planet_id()))

    return furthest_distance
Example #3
0
def turn_to_take(pw: planet_wars.PlanetWars, my_planet: planet_wars.Planet,
                 neutral_planet: planet_wars.Planet):
    """
    Finds the minimum turns to take `neutral_planet` with `my_planet`.
    :param pw: `PlanetWars` object
    :param my_planet: `Planet` object
    :param neutral_planet: `Planet` object
    :return: `int` turns to take the planet
    """

    distance = pw.distance(my_planet.planet_id(), neutral_planet.planet_id())
    if my_planet.num_ships() > neutral_planet.num_ships():
        return distance
    else:
        lacking_ships = neutral_planet.num_ships() - my_planet.num_ships() + 1
        for t in range(pw.map_size):
            lacking_ships -= my_planet.my_arriving_ships[
                t] + my_planet.growth_rate()
            if lacking_ships <= 0:
                break
        else:
            return 999999
        return distance + t
Example #4
0
def expand(pw: planet_wars.PlanetWars,
           expand_limit: int = 99,
           possible_planets=None,
           reckless: bool = False):
    """
    Expand to neutral planets with all ships. Designed to come after `defend_possible()` because this doesn't account
    for possible attacks from the opponent.
    :param pw: `PlanetWars` object
    :param expand_limit: `int` the maximum number of planets to expand to.
    :param possible_planets: `list` of `Planet` objects, the planets to consider expanding to. None -> all
    :param reckless: `bool` whether to care about the defensibility of the planet
    :return: None
    """

    expand_limit = min(expand_limit, len(pw.neutral_planets()))

    if possible_planets is None:
        possible_planets = filter(lambda p: p not in pw.my_future_neutrals,
                                  pw.neutral_planets())

    possible_planets = filter(lambda p: p not in pw.enemy_future_neutrals,
                              possible_planets)
    sorted_planets = sorted(
        possible_planets,
        key=lambda p:
        (score_planet(pw, p) - get_raw_score(p) + return_ships(pw, p) /
         (pw.map_size / 2)) / (p.num_ships() + 1),
        reverse=True)

    for _ in range(expand_limit):
        for attack_planet in sorted_planets[:expand_limit]:
            if not (attack_planet.latency > 0 and attack_planet.num_ships() < attack_planet.growth_rate()) and \
                    not reckless and not defensible(pw, attack_planet):
                continue
            # if not reckless and not defensible(pw, attack_planet):
            #     continue

            quickest_planet = min(
                pw.my_planets(),
                key=lambda p: turn_to_take(pw, p, attack_planet))

            closest_distance = pw.map_size
            for enemy_planet in pw.enemy_planets():
                closest_distance = min(
                    closest_distance,
                    pw.distance(enemy_planet.planet_id(),
                                attack_planet.planet_id()))
            for enemy_planet in pw.enemy_future_neutrals:
                closest_distance = min(
                    closest_distance,
                    pw.distance(enemy_planet.planet_id(),
                                attack_planet.planet_id()) +
                    pw.enemy_future_neutrals[enemy_planet][0])

            if turn_to_take(pw, quickest_planet,
                            attack_planet) > closest_distance:
                continue

            if quickest_planet.num_ships() > attack_planet.num_ships():
                pw.issue_order(quickest_planet.planet_id(),
                               attack_planet.planet_id(),
                               attack_planet.num_ships() + 1)
                quickest_planet.remove_ships(attack_planet.num_ships() + 1)
                pw.my_future_neutrals[attack_planet] = (pw.distance(
                    quickest_planet.planet_id(), attack_planet.planet_id()), 1)

                for planet in pw.planets():
                    planet.my_maximum_ships[pw.distance(quickest_planet.planet_id(), planet.planet_id()) - 1] -= \
                        attack_planet.num_ships()
                    planet.my_maximum_ships[
                        pw.distance(quickest_planet.planet_id(),
                                    attack_planet.planet_id()) +
                        pw.distance(attack_planet.planet_id(),
                                    planet.planet_id())] += 1
                    for t in range(
                            pw.distance(quickest_planet.planet_id(),
                                        attack_planet.planet_id()) +
                            pw.distance(attack_planet.planet_id(),
                                        planet.planet_id()), 2 * pw.map_size):
                        planet.my_maximum_ships[
                            t] += attack_planet.growth_rate()

                expand_limit -= 1
                sorted_planets.remove(attack_planet)
                break
            else:
                quickest_planet.num_ships(0)
                return
        else:
            break
Example #5
0
class Engine(Debuggable):
    def __init__(self, mapp, enemy_cmd, my_bot_class, timeout=1000, max_turns=200):
        super(Engine, self).__init__()
        self.mapp = mapp
        self.timeout = timeout
        self.max_turns = max_turns
        self.enemy_cmd = enemy_cmd
        self.pw = PlanetWars()
        try:
            self.my_bot = my_bot_class()
        except:
            self.my_bot = None
        self.debug_name = "engine"
        self.playback = ""

    @property
    def turn(self):
        return self.pw.turn

    def plus_turn(self):
        self.pw.turn += 1

    def load_map_data(self):
        f = open(self.mapp)
        data = "\n".join([line for line in f])
        f.close()
        return data

    def load_init_state(self):
        '''load map data and make initial game state'''
        self.pw = PlanetWars()
        self.pw.load_data(self.load_map_data())
        self.playback = ":".join(["%.10f,%.10f,%d,%d,%d" % (p.x, p.y, p.owner, p.num_ships, p.growth_rate)
                                  for p in self.pw.planets]) + "|"

    def _departure(self, enemy_fleets, my_fleets):
        for i, fleets in enumerate((my_fleets, enemy_fleets,)):
            for src, dest, num_ships in fleets:
                dist = self.pw.distance(src,dest)
                self.pw.fleets.append(Fleet(i+1,num_ships,src,dest,dist,dist))
                #TODO make check for ships availability
                self.pw.planets[src].num_ships -= num_ships
                
    def _advancement(self):
        for fl in self.pw.fleets:
            fl.turns_remaining -= 1
        for pl in self.pw.planets:
            if pl.owner > 0:
                pl.num_ships += pl.growth_rate

    def _get_participants(self, pl):
        participants = {pl.owner:pl.num_ships}
        updated_fleets = []
        for fl in self.pw.fleets:
            if fl.dest == pl.id and fl.turns_remaining <= 0:
                if not fl.owner in participants:
                    participants[fl.owner] = fl.num_ships
                else:
                    participants[fl.owner] += fl.num_ships
            else:
                updated_fleets.append(fl)
        self.pw.fleets = updated_fleets
        return participants

    def _get_winner_second(self, participants):
        winner = Fleet(0, 0)
        second = Fleet(0, 0)
        for k, v in participants.items():
            if v > second.num_ships:
                if v > winner.num_ships:
                    second = winner
                    winner = Fleet(k, v)
                else:
                    second = Fleet(k, v)
        return winner, second

    def _process_arrival(self, pl, winner, second):
        if winner.num_ships > second.num_ships:
            pl.num_ships = winner.num_ships - second.num_ships
            pl.owner = winner.owner
        else:
            pl.num_ships = 0

    def _arrival(self):
        for pl in self.pw.planets:
            participants = self._get_participants(pl)
            winner, second = self._get_winner_second(participants)
            self._process_arrival(pl, winner, second)

    def game_state_update(self, enemy_fleets, my_fleets):
        self._departure(enemy_fleets, my_fleets)
        self._advancement()
        self._arrival()

    @property
    def winner(self):
        return "Old" if self.pw.winner == 2 else "New" if self.pw.winner == 1 else "Draw"

    def set_enemy_standard_io(self):
        process = subprocess.Popen(self.enemy_cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
        self.stdin, self.stdout = process.stdin, process.stdout

    def run(self):
        self.load_init_state()
        self.set_enemy_standard_io()
        self.my_bot.via_standard_io = False
        try:
            while True:
                self.make_turn()
        except EndOfTheGame:
            return

    def communicate_enemy_bot(self):
        self.stdin.write(self.pw.repr_for_enemy() + "go\n")
        self.stdin.flush()
        enemy_orders = []
        while True:
            line = self.stdout.readline().replace("\n", "")
            self.debug("> %s" % line)
            self.print_it("> %s" % line)
            if line.startswith("go"):
                break
            enemy_orders.append(map(int, line.split(" ")))
        return enemy_orders

    def communicate_my_bot(self):
        self.my_bot.load_data(repr(self.pw))
        self.my_bot.do_turn()
        self.print_it("\n".join(["< %d %d %d" % order for order in self.my_bot.real_orders]))

    def make_turn(self):
        self.plus_turn()
        self.print_it("Turn #%d" % self.turn)
        self.pw.is_game_over(self.max_turns)
        enemy_orders = self.communicate_enemy_bot()
        self.communicate_my_bot()
        self.game_state_update(enemy_orders, self.my_bot.real_orders)
        self.print_play_back()

    def print_play_back(self):
        if self.debug_enabled:
            planets = ["%d.%d" % (p.owner, p.num_ships) for p in self.pw.planets]
            fleets = ["%d.%d.%d.%d.%d.%d" % (f.owner, f.num_ships, f.src, f.dest, f.total_trip_length, f.turns_remaining)
                      for f in self.pw.fleets]
            self.playback += ",".join(planets + fleets) + ":"