Example #1
0
    def process_frame(self):
        self.countdown -= 1
        if False and (self.countdown <= 0):
            quick_followup = (random.random() < 0.1)
            
            if quick_followup:
                self.countdown = 7
            else:
                self.countdown = int(27* (random.random() * 15))

            what_to_do = random.random()
            if what_to_do < 0.2:
                self.c.send_split()
            else:
                self.c.send_shoot()

        self.log_pos(self.c.player.center)
        self.log_mass(self.c.player.total_mass)
        
        cells = self.c.world.cells.values()
        own_cells = list(self.c.player.own_cells)

        own_total_size = sum( map(lambda cell : cell.size, own_cells) )
        own_total_mass = sum( map(lambda cell : cell.mass, own_cells) )
        n_own_cells = len(own_cells)

        n = 3
        for cell in filter(lambda cell : not cell.is_food and not cell.is_virus and not cell.is_ejected_mass, cells):
            if hasattr(cell,'poslog') and len(cell.poslog) > n+1:
                cellspeed = 0
                for i in range(1,n+1):
                    cellspeed += (cell.poslog[-i] - cell.poslog[-i-1]).len() / n
                
                cellspeed = int(cellspeed*10)/10
                self.data.size_vs_speed[cell.size][cellspeed] += 1

        visible_width = max( map(lambda cell : cell.pos.x - cell.size, cells) ) - min( map(lambda cell : cell.pos.x + cell.size, cells) )
        visible_height = max( map(lambda cell : cell.pos.y - cell.size, cells) ) - min( map(lambda cell : cell.pos.y + cell.size, cells) )

        self.data.size_vs_visible_window[n_own_cells][own_total_size].append((visible_width,visible_height))
        self.data.mass_vs_visible_window[n_own_cells][own_total_mass].append((visible_width,visible_height))


        # log virus sizes
        for cell in cells:
            if cell.is_virus:
                self.data.observed_virus_sizes[cell.size] += 1
        
        # detect re-merging cells
        for cell in own_cells:
            for cell2 in own_cells:
                if cell2 != cell:
                    dist = (cell.pos - cell2.pos).len()
                    expected_dist = cell.size + cell2.size
                    min_dist = max(cell.size, cell2.size)

                    if (dist < (0.9 * expected_dist + 0.1 * min_dist)):
                        is_parent_child = (cell == cell2.parent or cell2 == cell.parent)
                        print("cells seem to be merging! they are "+ ("" if is_parent_child else "NOT ") + "parent and child")
                        pair_id = (min(cell.cid,cell2.cid), max(cell.cid,cell2.cid))
                        
                        if pair_id not in self.data.remerging:
                            self.data.remerging[pair_id] = ReMerging(cell.size, cell2.size, cell.spawntime, cell2.spawntime, is_parent_child, self.c.world.time)
                        else:
                            self.data.remerging[pair_id].end_time = self.c.world.time
                


        # find ejected mass, split cells or viruses that have come to rest
        for cell in cells:
            if hasattr(cell,"parent") and cell.parent != None and not cell.calmed_down:
                # we're only interested in cells with a parent set, because
                # this also implies that we have tracked them since their
                # creation.
                # also, we're only interested in cells that are still flying
                # as a result of being ejected/split.
                
                if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
                    expected_speed = mechanics.speed(cell.size)
                    celltype = "split cell"
                elif cell.is_virus:
                    expected_speed = 1
                    celltype = "virus"
                elif cell.is_ejected_mass:
                    expected_speed = 1
                    celltype = "ejected mass"


                if cell.movement.len() < expected_speed * 1.1:
                    print(celltype+" has come to rest, nframes="+str(len(cell.poslog)))
                    cell.calmed_down = True
                    # TODO: speed log

                    # distance is calculated naively
                    distance = (cell.spawnpoint - cell.pos).len()

                    # distance2 is calculated along the cell's path (will differ if the flight was not colinear)
                    poslog = list(cell.poslog)
                    speeds = list(map(lambda vecs : (vecs[0]-vecs[1]).len(), zip(poslog, poslog[1:])))
                    distance2 = sum(speeds)

                    distance_from_parent = (cell.parentpos_when_spawned - cell.pos).len()

                    self.data.eject_distlogs[celltype] += [(distance, distance2, distance_from_parent, cell.parentsize_when_spawned, len(cell.poslog), speeds)]
                    print("  flown distance = %.2f / %.2f"%(distance,distance2))

                if len(cell.poslog) == 5:
                    # calculate movement direction from the first 5 samples

                    # first check whether they're on a straight line
                    if geometry.is_colinear(cell.poslog) and cell.shoot_vec != None:
                        print(celltype+" direction available!")
                        fly_direction = cell.poslog[-1] - cell.poslog[0]
                        fly_angle = math.atan2(fly_direction.y, fly_direction.x)

                        shoot_angle = math.atan2(cell.shoot_vec.y, cell.shoot_vec.x)


                        deviation = (fly_angle - shoot_angle) % (2*math.pi)
                        if deviation > math.pi: deviation -= 2*math.pi
                        print("  deviation = "+str(deviation*180/math.pi))

                        self.data.eject_deviations[celltype] += [deviation]

                        if (celltype == 'virus'):
                            # FIXME so ugly
                            try:
                                shoot_angle = math.atan2(cell.shoot_vec2.y, cell.shoot_vec2.x)

                                deviation = (fly_angle - shoot_angle) % (2*math.pi)
                                if deviation > math.pi: deviation -= 2*math.pi
                                print("  deviation2= "+str(deviation*180/math.pi))

                                self.data.eject_deviations['virus2'] += [deviation]
                            except AttributeError:
                                print("virus2 not available, wtf?!")

                            try:
                                shoot_angle = math.atan2(cell.shoot_vec3.y, cell.shoot_vec3.x)

                                deviation = (fly_angle - shoot_angle) % (2*math.pi)
                                if deviation > math.pi: deviation -= 2*math.pi
                                print("  deviation3= "+str(deviation*180/math.pi))

                                self.data.eject_deviations['virus3'] += [deviation]
                            except AttributeError:
                                print("virus3 not available")

                    else:
                        print(celltype+" did NOT fly in a straight line, ignoring...")
Example #2
0
    def quality(self, cell, cells, myspeed):
        dd_sq = max((cell.pos[0] - self.c.player.center[0]) ** 2 + (cell.pos[1] - self.c.player.center[1]) ** 2, 0.001)
        sigma = 500 * max(cell.mass, 1)  # TODO FIXME don't try to eat running away cells
        if mechanics.speed(cell) - myspeed >= 0:
            sigma = sigma / 3 / math.exp((mechanics.speed(cell) - myspeed) / 10)

        dist_score = -math.exp(-dd_sq / (2 * sigma ** 2))

        rivals = filter(lambda r: self.rival(r, cell), cells)
        hugecells = filter(self.hugecell, cells)
        splitkillers = filter(self.splitkiller, cells)
        nonsplitkillers = filter(self.nonsplitkiller, cells)

        rival_score = 0
        for r in rivals:
            dd_sq = max(0.001, (r.pos[0] - cell.pos[0]) ** 2 + (r.pos[1] - cell.pos[1]) ** 2)
            sigma = r.size + 100
            rival_score += math.exp(-dd_sq / (2 * sigma ** 2))

        hugecell_score = 0
        for s in hugecells:
            dd_sq = max(0.001, (s.pos[0] - cell.pos[0]) ** 2 + (s.pos[1] - cell.pos[1]) ** 2)
            sigma = s.size + 10
            hugecell_score += math.exp(-dd_sq / (2 * sigma ** 2))

        splitkill_score = 0
        for s in splitkillers:
            dd_sq = max(0.001, (s.pos[0] - cell.pos[0]) ** 2 + (s.pos[1] - cell.pos[1]) ** 2)
            sigma = s.size + 650 + 250
            splitkill_score += math.exp(-dd_sq / (2 * sigma ** 2))

        nonsplitkill_score = 0
        for s in nonsplitkillers:
            dd_sq = max(0.001, (s.pos[0] - cell.pos[0]) ** 2 + (s.pos[1] - cell.pos[1]) ** 2)
            sigma = (75 + s.size) + 250
            nonsplitkill_score += math.exp(-dd_sq / (2 * sigma ** 2))

        density_score = 0
        sigma = 300
        # for f in filter(lambda c : c.is_food and c!=cell, self.c.world.cells.values()):
        #    dd_sq = (f.pos[0]-cell.pos[0])**2 + (f.pos[1]-cell.pos[1])**2
        #    density_score -= math.exp(-dd_sq/(2*sigma**2))

        wall_score = 0
        wall_dist = min(
            cell.pos[0] - self.c.world.top_left[1],
            self.c.world.bottom_right[1] - cell.pos[0],
            cell.pos[1] - self.c.world.top_left[0],
            self.c.world.bottom_right[0] - cell.pos[1],
        )
        sigma = 100
        wall_score = math.exp(-wall_dist ** 2 / (2 * sigma ** 2))

        return (
            0.5 * dist_score
            + 0.2 * rival_score
            + 5.0 * hugecell_score
            + 5.0 * nonsplitkill_score
            + 15 * splitkill_score
            + 0.1 * density_score
            + 5 * wall_score
        )
Example #3
0
    def process_frame(self):
        runaway = False

        my_smallest = min(self.c.player.own_cells, key=lambda cell: cell.mass)
        my_largest = max(self.c.player.own_cells, key=lambda cell: cell.mass)

        cells = filter(lambda r: not r.is_food and not r.is_virus, self.c.world.cells.values())
        friendly_cells = list(filter(lambda c: c.is_virus or c.name in friendly_players, self.c.world.cells.values()))

        if friendly_cells:
            dist_to_friend = min(
                map(lambda c: (self.c.player.center - c.pos).len() - max(my_largest.size, c.size), friendly_cells)
            )
        else:
            dist_to_friend = float("inf")

        if dist_to_friend < 20 or my_largest.mass < 36:
            if self.do_approach_friends:
                print("not approaching friends")
            self.do_approach_friends = False
        elif dist_to_friend > 200 and my_largest.mass > 36 + 10 * 16:
            if not self.do_approach_friends:
                print("approaching friends")
            self.do_approach_friends = True

        if friendly_cells and self.do_approach_friends:
            friend_to_feed = max(friendly_cells, key=lambda c: c.mass)
            if friend_to_feed.mass < 1.25 * my_largest.mass:
                print("friend too small")
                friend_to_feed = None
            if friend_to_feed:
                self.gui.hilight_cell(friend_to_feed, (255, 255, 255), (255, 127, 127), 30)

                self.target_cell = friend_to_feed
                self.target_type = "friend"

        if self.do_approach_friends:
            for c in self.c.player.own_cells:
                self.gui.hilight_cell(c, (255, 255, 255), (255, 127, 127), 20)

        # can this cell feed that cell?
        # "False" means "No, definitely not"
        # "True" means "Maybe"
        def can_feed(this, that):
            if that.is_food or that.is_ejected_mass or that.size < 43:  # too small cells cannot eat the ejected mass
                return False

            relpos = this.pos - that.pos
            dist = relpos.len()
            if dist == 0 or dist >= 700 + this.size + that.size:
                return False

            return check_cell_in_interval(
                this.pos, that, (this.movement_angle - 10 * math.pi / 180, this.movement_angle + 10 * math.pi / 180)
            )

        success_rate = 0
        for my_cell in self.c.player.own_cells:
            try:
                my_cell.movement_angle
            except AttributeError:
                print("cannot calculate shoot angle, too few backlog")
                continue
            # check if ejecting mass would feed a friend
            possibly_feedable_cells = list(filter(lambda c: can_feed(my_cell, c), self.c.world.cells.values()))
            possibly_feedable_cells.sort(key=lambda c: (my_cell.pos - c.pos).len())

            good_intervals = []
            for feedable in possibly_feedable_cells:
                self.gui.hilight_cell(feedable, (255, 192, 127), (127, 127, 255))
                if feedable not in friendly_cells:
                    break

                good_intervals += canonicalize_angle_interval(interval_occupied_by_cell(my_cell.pos, feedable))

            good_intervals = merge_intervals(good_intervals)
            area = interval_area(
                intersection(
                    good_intervals,
                    canonicalize_angle_interval(
                        (
                            my_cell.movement_angle - mechanics.eject_delta * math.pi / 180,
                            my_cell.movement_angle + mechanics.eject_delta * math.pi / 180,
                        )
                    ),
                )
            )
            success_rate += area / (2 * mechanics.eject_delta * math.pi / 180) / len(list(self.c.player.own_cells))

        self.gui.draw_bar(((100, 40), (500, 24)), success_rate, thresh=0.80, color=(0, 0, 127))
        if success_rate >= 0.80:
            self.c.send_shoot()

        # enemy/virus/friend-we-would-kill avoidance
        forbidden_intervals = []
        for cell in self.c.world.cells.values():
            relpos = ((cell.pos[0] - self.c.player.center[0]), (cell.pos[1] - self.c.player.center[1]))
            dist = math.sqrt(relpos[0] ** 2 + relpos[1] ** 2)

            # find out the allowed minimum distance
            allowed_dist = None

            if cell.is_virus:
                if cell.mass < my_largest.mass:
                    allowed_dist = cell.size + 2
                else:
                    allowed_dist = "don't care"
            elif cell in friendly_cells:
                if 1.25 * my_largest.mass > cell.mass:  # we're dangerous to our friends
                    allowed_dist = my_largest.size + 40
            elif (
                cell not in self.c.player.own_cells
                and not cell.is_virus
                and not cell.is_ejected_mass
                and not cell.is_food
            ) and cell.mass + 20 > 1.25 * my_smallest.mass:  # our enemy is, or will be dangerous to us
                if (cell.mass + 20) / 2 < 1.25 * my_smallest.mass:
                    # they can't splitkill us (soon)
                    allowed_dist = cell.size + 75
                elif cell.mass / 15.0 < self.c.player.total_mass:
                    # they can and they will splitkill us
                    allowed_dist = 650 + cell.size
                else:
                    # we're too small, not worth a splitkill. they have absolutely no
                    # chance to chase us
                    allowed_dist = cell.size + 10
            else:
                allowed_dist = "don't care"

            if allowed_dist != "don't care" and dist < allowed_dist:
                try:
                    angle = math.atan2(relpos[1], relpos[0])
                    corridor_halfwidth = math.asin(min(1, cell.size / dist))
                    forbidden_intervals += canonicalize_angle_interval(
                        (angle - corridor_halfwidth, angle + corridor_halfwidth)
                    )
                    runaway = True
                except:
                    print("TODO FIXME: need to handle enemy cell which is in our centerpoint!")
                    print("dist=%.2f, allowed_dist=%.2f" % (dist, allowed_dist))

        # wall avoidance
        if self.c.player.center[0] < self.c.world.top_left[1] + (self.c.player.total_size * 2):
            forbidden_intervals += [(0.5 * pi, 1.5 * pi)]
        if self.c.player.center[0] > self.c.world.bottom_right[1] - (self.c.player.total_size * 2):
            forbidden_intervals += [(0, 0.5 * pi), (1.5 * pi, 2 * pi)]
        if self.c.player.center[1] < self.c.world.top_left[0] + (self.c.player.total_size * 2):
            forbidden_intervals += [(pi, 2 * pi)]
        if self.c.player.center[1] > self.c.world.bottom_right[0] - (self.c.player.total_size * 2):
            forbidden_intervals += [(0, pi)]

        # if there's actually an enemy to avoid:
        if runaway:
            # find the largest non-forbidden interval, and run into this direction.

            forbidden_intervals = merge_intervals(forbidden_intervals)

            allowed_intervals = invert_angle_intervals(forbidden_intervals)

            try:
                (a, b) = find_largest_angle_interval(allowed_intervals)
            except:
                print("TODO FIXME: need to handle no runaway direction being available!")
                (a, b) = (0, 0)

            runaway_angle = (a + b) / 2
            runaway_x, runaway_y = (
                (self.c.player.center[0] + int(100 * math.cos(runaway_angle))),
                (self.c.player.center[1] + int(100 * math.sin(runaway_angle))),
            )

            self.target = (runaway_x, runaway_y)
            self.target_type = None
            self.target_cell = None

            self.color = (255, 0, 0)

            # a bit of debugging information
            for i in forbidden_intervals:
                self.gui.draw_arc(self.c.player.center, self.c.player.total_size + 10, i, (255, 0, 255))

        # if however there's no enemy to avoid, try to feed a friend. or chase food or fly randomly around
        else:
            if self.target_cell != None:
                self.target = tuple(self.target_cell.pos)

                # check if target went out of sight, or became infeasible
                if self.target_cell not in self.c.world.cells.values() or (
                    not self.edible(self.target_cell) and not self.target_cell in friendly_cells
                ):
                    self.target_cell = None
                    self.target_type = None

            elif self.target == tuple(self.c.player.center):
                self.target_type = None
                print("Reached random destination")

            if not self.target_type == "friend":  # i.e. None, random or food
                food = list(filter(self.edible, self.c.world.cells.values()))
                myspeed = mechanics.speed(my_largest)
                food = sorted(food, key=lambda c: self.quality(c, cells, myspeed))

                if len(food) > 0:
                    food_candidate = food[0]

                    if (
                        self.target_type == None
                        or self.target_type == "random"
                        or (
                            self.target_type == "food"
                            and self.quality(food_candidate, cells, myspeed)
                            < self.quality(self.target_cell, cells, myspeed) - 1
                        )
                    ):
                        if self.target_type == "food":
                            print(
                                "abandoning food of value %.3f for %.3f"
                                % (
                                    self.quality(self.target_cell, cells, myspeed),
                                    self.quality(food_candidate, cells, myspeed),
                                )
                            )

                        self.target_cell = food_candidate
                        self.target = (self.target_cell.pos[0], self.target_cell.pos[1])

                        self.target_type = "food"
                        self.color = (0, 0, 255)

                if self.target == None:
                    rx = self.c.player.center[0] + random.randrange(-400, 401)
                    ry = self.c.player.center[1] + random.randrange(-400, 401)
                    self.target = (rx, ry)
                    self.target_type = "random"
                    self.color = (0, 255, 0)
                    print("Nothing to do, heading to random targetination: " + str((rx, ry)))

        # more debugging
        self.gui.draw_line(self.c.player.center, self.target, self.color)

        return self.target
Example #4
0
    def on_world_update_post(self):
        self.newFrame = True

        self.c.world.time = self.time
        self.time += 1

        if self.time % 100 == 0:
            self.cleanup_victims()

        # create and purge poslog history, movement and movement_angle
        for cid in self.history:
            self.history[cid].stale = True

        for cid in self.c.world.cells:
            if cid not in self.history:
                self.history[cid] = CellHistory()
            
            self.history[cid].poslog.append(self.c.world.cells[cid].pos.copy())
            self.c.world.cells[cid].poslog = self.history[cid].poslog
            
            self.history[cid].stale = False

        self.history = {k: v for k, v in self.history.items() if v.stale == False}
        

        for cid in self.c.world.cells:
            cell = self.c.world.cells[cid]

            if not hasattr(cell, "spawntime"):
                cell.spawntime = self.c.world.time

            try:
                oldpos = cell.poslog[-3-1]
                cell.movement = (cell.pos - oldpos)/3
                cell.movement_angle = cell.movement.angle()
            except (AttributeError, IndexError):
                pass


        # create OtherPlayer entries
        otherplayers = {}
        for cell in self.c.world.cells.values():
            playerid = None
            if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
                playerid = (cell.name, cell.color)
            elif cell.is_virus:
                playerid = "virus"
            elif cell.is_food:
                playerid = "food"
            elif cell.is_ejected_mass:
                playerid = "ejected mass"
            else:
                playerid = "???"

            if playerid not in otherplayers:
                otherplayers[playerid] = OtherPlayer(playerid)

            cell.player = otherplayers[playerid]
            cell.player.cells.add(cell)

        # detect split cells and clean up obsolete parent references
        for cell in self.c.world.cells.values():
            # create attribute if not already there
            try:
                cell.parent = cell.parent
            except:
                cell.parent = None
                cell.calmed_down = True

            # clean up obsolete parent references
            if cell.parent and cell.parent.cid not in self.c.world.cells:
                cell.parent = None

            # find split cells
            is_split = False
            if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
                try:
                    if cell.parent == None and cell.movement.len() > 2 * mechanics.speed(cell.size):
                            print("looks like a split!"+str(cell.movement.len() / mechanics.speed(cell.size)))
                            is_split = True
                except AttributeError:
                    pass

                if is_split:
                    history_len = len(cell.poslog)
                    cell.parent = min(cell.player.cells, key=lambda c : (c.poslog[-history_len] - cell.poslog[-history_len]).len() if c != cell and len(c.poslog) >= history_len else float('inf'))
                    try:
                        cell.shoot_vec = cell.parent.movement.copy()
                    except:
                        cell.shoot_vec = None
                    cell.calmed_down = False

            elif cell.is_virus:
                try:
                    if cell.parent == None and cell.movement.len() > 0:
                            print("split virus!")
                            is_split = True
                except AttributeError:
                    pass

                if is_split:
                    cell.parent = min(cell.player.cells, key=lambda c : (c.pos - cell.poslog[0]).len() if c != cell else float('inf'))
                    try:
                        last_feed = self.victims[cell.parent.cid][-1][0]
                        if not last_feed.is_ejected_mass:
                            print("wtf, last virus feed was not ejected mass?!")
                            raise KeyError
                        else:
                            cell.shoot_vec  = cell.parent.pos - last_feed.poslog[0]
                            cell.shoot_vec2 = last_feed.poslog[-1] - last_feed.poslog[0]
                            try:
                                pos_when_shot = last_feed.parent.poslog[-len(last_feed.poslog)]
                                cell.shoot_vec3 = cell.parent.pos - pos_when_shot
                            except:
                                print("MOAAAHH")
                                cell.shoot_vec3 = None
                    except KeyError:
                        print("wtf, no last virus feed?!")
                        cell.shoot_vec  = None
                        cell.shoot_vec2 = None
                        cell.shoot_vec3 = None
                        
                    cell.calmed_down = False
                
            elif cell.is_ejected_mass:
                try:
                    if cell.parent == None and cell.movement.len() > 0:
                            print("ejected mass!")
                            is_split = True
                except AttributeError:
                    pass

                if is_split:
                    history_len = len(cell.poslog)
                    try:
                        cell.parent = min(filter(lambda c : not c.is_ejected_mass and not c.is_food and not c.is_virus and c.color == cell.color, self.c.world.cells.values()), key=lambda c : (c.poslog[-history_len] - cell.poslog[-history_len]).len() if len(c.poslog) >= history_len else float('inf'))
                        try:
                            cell.shoot_vec = cell.parent.movement.copy()
                        except:
                            cell.shoot_vec = None
                        cell.calmed_down = False
                    except ValueError:
                        # if no possible parents are found, min will raise a ValueError. ignore that.
                        pass

            if is_split:
                cell.spawnpoint = cell.pos.copy()
                cell.parentsize_when_spawned = cell.parent.size if cell.parent != None else None
                cell.parentpos_when_spawned = cell.parent.pos.copy() if cell.parent != None else None