예제 #1
0
    def __init__(self, perma_board = []):
        """ if we want walls/hospital we give them in perma_board
            perma_board should be formatted as a 64x64 2D list of pixel values """

        # the set of things on the board which never change
        # currently includes HOSPITAL and WALL
        # formatted as a 64x64 2D list of pixel values
        self.perma_board = perma_board
        if len(self.perma_board) == 0:
            for i in range(64):
                self.perma_board += [[EMPTY]*64]

        # the set of locations on the board where tanks can spawn
        # excludes HOSPITAL and WALL and anything adjacent to either
        # formatted as a 64x64 2D list of pixel values
        self.available_spawn_locs = [space for space in np.nindex(64,64)]
        self.available_spawn_locs = filter(lambda x: true_for_each(lambda y: self.perma_board[y[0],y[1]] == EMPTY, Tank.three_by_three(x)), self.available_spawn_locs)

        # purely aesthetic features which never interact
        # includes EYE for instance
        # formatted as a 64x64 2D list of pixel values
        self.ghost_board = []
        for i in range(64):
            self.ghost_board += [[EMPTY]*64]

        self.scores          = {}
        self.load_colors()
        self.tanks           = {}
        self.board           = copy.deepcopy(self.perma_board)
        self.bullets         = []
        self.t_minus         = TURN_RATE
        self.last_time_stamp = time.time()
        self.fast_forward    = False

        self.pending_tank_ids = []

        self.scomm = ServerCommunicator()
예제 #2
0
class Game:

    def __init__(self, perma_board = []):
        """ if we want walls/hospital we give them in perma_board
            perma_board should be formatted as a 64x64 2D list of pixel values """

        # the set of things on the board which never change
        # currently includes HOSPITAL and WALL
        # formatted as a 64x64 2D list of pixel values
        self.perma_board = perma_board
        if len(self.perma_board) == 0:
            for i in range(64):
                self.perma_board += [[EMPTY]*64]

        # the set of locations on the board where tanks can spawn
        # excludes HOSPITAL and WALL and anything adjacent to either
        # formatted as a 64x64 2D list of pixel values
        self.available_spawn_locs = [space for space in np.nindex(64,64)]
        self.available_spawn_locs = filter(lambda x: true_for_each(lambda y: self.perma_board[y[0],y[1]] == EMPTY, Tank.three_by_three(x)), self.available_spawn_locs)

        # purely aesthetic features which never interact
        # includes EYE for instance
        # formatted as a 64x64 2D list of pixel values
        self.ghost_board = []
        for i in range(64):
            self.ghost_board += [[EMPTY]*64]

        self.scores          = {}
        self.load_colors()
        self.tanks           = {}
        self.board           = copy.deepcopy(self.perma_board)
        self.bullets         = []
        self.t_minus         = TURN_RATE
        self.last_time_stamp = time.time()
        self.fast_forward    = False

        self.pending_tank_ids = []

        self.scomm = ServerCommunicator()


    # ------ FUNCTIONS TO UPDATE THE GAME -------

    def update(self):
        """ update everything and deal with turns """

        # check to see how much time has passed
        new_time = time.time()
        dt = new_time - self.last_time_stamp
        self.last_time_stamp = new_time

        if dt > MAX_DT_PER_UPDATE or self.fast_forward:
            dt = MAX_DT_PER_UPDATE


        # if we haven't reached the next turn, just update everything
        if dt < self.t_minus:
            self.real_time_update(dt)

        # if we HAVE reached the next turn, run up until the turn
        # then do the turn
        # then run the remaining time
        else:

            self.real_time_update(self.t_minus)

            # add new tanks, if necessary
            for newid in self.pending_tank_ids:
                for t in self.tanks.itervalues():
                    if t.ID == newid:
                        t.reload_ai()
                        break
                else:
                    # Tank doesn't already exist! 
                    # Add it if there is an AI and there is a color left to assign to
                    if os.path.isfile("../data/"+newid+".py"):
                        if len(self.color_queue) > 0:
                            try:
                                (x,y) = get_rand_spawn_space()
                                newtank = Tank(newid,
                                              "../data/"+newid+".py",
                                              copy.deepcopy(self.perma_board),
                                              x,y)
                            except SandboxCodeExecutionFailed:
                                # Couldn't create tank. Skip to next tank
                                pass
                            else:
                                self.assign_color(newtank)
                                self.tanks[newid] = newtank
                                self.scores[newid] = 0
                                # Move on to next tank
            self.pending_tank_ids = []

            # take the turns!
            tank_coords = {}
            # record positions so that we can give info to the AIs
            for t in self.tanks.itervalues():
                tank_coords[t.ID] = [t.x_pos,t.y_pos]
            # run each individual AI in a random order
            random_tanks = self.tanks.values()
            random.shuffle(random_tanks)
            for t in random_tanks:
                bullet = t.take_turn(tank_coords)
                if bullet:
                    self.bullets += [bullet]
            # update all the appropriate stats
            for t in self.tanks.itervalues():
                t.update_stat_file()

            self.real_time_update(dt - self.t_minus)
            self.t_minus = TURN_RATE


    def real_time_update(self, dt):
        """ update positions, kill things, in real time
            ASSUMES THAT self.t_minus >= dt
            that is, that no turn happens in the middle of dt"""

        self.t_minus -= dt

        # copy the permanent features before we add on tanks,bullets,etc
        self.board = copy.deepcopy(self.perma_board)

        # CURRENTLY WE CLEAR THE GHOST_BOARD EVERY FRAME
        # THIS MAY CHANGE IN THE FUTURE AS WE ADD MORE ANIMATIONS
        self.ghost_board = []
        for i in range(64):
            self.ghost_board += [[EMPTY]*64]

        # bullets move first thus if they get shot they can escape their mama tank
        for b in self.bullets:

            # move the bullet
            b.move(dt)
            pos = b.get_pixel_pos()
            x = pos[0]
            y = pos[1]

            # kill the bullet if it hits a wall
            if (x < 0) or (y < 0) or (x > 63) or (y > 63):
                self.bullets.remove(b)
            elif (self.board[y][x] == WALL):
                self.bullets.remove(b)
            else:
                self.board[y][x] = BULLET

        # then tanks move
        for k in self.tanks.keys():

            if k:

                t = self.tanks[k]

                t.move(dt)

                # check to see if the tank hits a wall
                positions = t.get_pixel_pos() # <-- actually 9 positions
                for p in positions:
                    x = p[0]
                    y = p[1]
                    # if you hit a wall or go off the edge of the screen, or we hit a tank, don't move
                    if (x < 0) or (y < 0) or (x > 63) or (y > 63) or (self.board[y][x] == WALL) or (self.board[y][x] < 10):
                        t.move(-1.0*dt)
                        break

                # update the pixels on the board
                positions = t.get_pixel_pos() # <-- actually 9 positions
                for p in positions:
                    x = p[0]
                    y = p[1]
                    # if you hit a bullet:
                    #   find the bullet, kill it, take damage, record your aggressor
                    if (self.board[y][x] == BULLET) and not t.is_dead():
                        for b in self.bullets:
                            b_pos = b.get_pixel_pos()
                            b_x = b_pos[0]
                            b_y = b_pos[1]

                            if (x==b_x) and (y==b_y):
                                bullet_id = b.ID
                                self.bullets.remove(b)
                                t.damage(BULLET_DM)
                                t.damage_IDs += [bullet_id]
                                if bullet_id in self.tanks:
                                    self.tanks[bullet_id].score += 1
                                    self.scores[bullet_id] += 1
                                if t.is_dead():
                                    self.scomm.death_event(b.ID, t.ID)
                                    self.return_color(t)
                                    t.cleanup()
                                    del self.tanks[k]
                                    break

                    # if you're on the hospital, heal yourself
                    elif (self.board[y][x] == HOSPITAL) and (not t.recently_healed):
                        t.heal(HOSPITAL_RATE, dt)
                        t.recently_healed = True
                    # finally set the pixel to be a tank
                    self.board[y][x] = t.color

                # once the tank is done moving, reset so it can be healed next update
                t.recently_healed = False

                # if t died, reset any pixels that were written before the tank died
                if t.is_dead():
                    for p in positions:
                        if self.board[y][x] == t.color:
                            self.board[y][x] = EMPTY

                # otherwise add the "eye" of the tank to the ghost_board
                # according to the direction in the map below, which 
                # corresponds to the angle the tank is pointing
                #
                #     1 2 3
                #     0 x 4
                #     7 6 5
                #
                else:
                    t_angle_scaled = (int(round(math.atan2(t.y_vel,t.x_vel)*8/(2*math.pi)))+4)%8
                    eye_x = int(round(t.x_pos))
                    eye_y = int(round(t.y_pos))
                    if t_angle_scaled == 0:
                        eye_x -= 1
                    elif t_angle_scaled == 1:
                        eye_x -= 1
                        eye_y -= 1
                    elif t_angle_scaled == 2:
                        eye_y -= 1
                    elif t_angle_scaled == 3:
                        eye_y -= 1
                        eye_x += 1
                    elif t_angle_scaled == 4:
                        eye_x += 1
                    elif t_angle_scaled == 5:
                        eye_x += 1
                        eye_y += 1
                    elif t_angle_scaled == 6:
                        eye_y += 1
                    elif t_angle_scaled == 7:
                        eye_x -= 1
                        eye_y += 1

                    self.ghost_board[eye_y][eye_x] = EYE

        # finally draw the ghost board over the regular board
        for y in range(len(self.board)):
            for x in range(len(self.board[y])):
                ghost = self.ghost_board[y][x]
                if ghost != EMPTY:
                    self.board[y][x] = ghost




    # ------ FUNCTIONS TO DRAW THE GAME -------



    def draw_board(self):

        # draw the board to STDOUT
        if OUTPUT_STDOUT:
            win.refresh()
            for i in range (0,len(self.board)):
                for j in range (0,len(self.board[0])):
                    n = self.board[i][j]
                    if n == 10 or n == 15:
                        color = 2
                    elif n == 11:
                        color = 2
                    elif n == 12:
                        color = 3
                    elif n == 13:
                        color = 4
                    else:
                        color = 5
                    win.addch(i,j,DEBUG_STRINGS[n],curses.color_pair(color))

        # draw the board to the LED matrix
        if OUTPUT_LED:
            assert len(self.board) == 64,    'Board width  not 64, update display output'
            assert len(self.board[0]) == 64, 'Board height not 64, update display output'
            mymap = [[(y*4,x*4,0) for x in range(64)] for y in range(64)]
            bytes_to_write = [0 for i in range(3*64*64)]
            for row in range(32/2):
                for col in range(64):
                    bytes_to_write[(    row *64+col)*3*2+0] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+ 0)-1][-col-1]][0]]]
                    bytes_to_write[(    row *64+col)*3*2+1] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+ 0)-1][-col-1]][1]]]
                    bytes_to_write[(    row *64+col)*3*2+2] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+ 0)-1][-col-1]][2]]]

                    bytes_to_write[(    row *64+col)*3*2+3] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+16)-1][-col-1]][0]]]
                    bytes_to_write[(    row *64+col)*3*2+4] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+16)-1][-col-1]][1]]]
                    bytes_to_write[(    row *64+col)*3*2+5] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+16)-1][-col-1]][2]]]

                    bytes_to_write[((16+row)*64+col)*3*2+0] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+32)-1][-col-1]][0]]]
                    bytes_to_write[((16+row)*64+col)*3*2+1] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+32)-1][-col-1]][1]]]
                    bytes_to_write[((16+row)*64+col)*3*2+2] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+32)-1][-col-1]][2]]]

                    bytes_to_write[((16+row)*64+col)*3*2+3] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+48)-1][-col-1]][0]]]
                    bytes_to_write[((16+row)*64+col)*3*2+4] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+48)-1][-col-1]][1]]]
                    bytes_to_write[((16+row)*64+col)*3*2+5] = rev_bits_table[gamma_correction_table[COLORS[self.board[-(row+48)-1][-col-1]][2]]]
            reset_fpga()
            with open("/dev/spidev0.1", "wb") as spifile:
                while bytes_to_write:
                    spifile.write(bytearray(bytes_to_write[:spi_max_write_sz]))
                    bytes_to_write = bytes_to_write[spi_max_write_sz:]

        # send the board to javascript
        if USE_SIMULATOR:
            js.globals.update_board(self.board)

    # ------ MISCELLANEOUS THINGS THE GAME NEEDS TO DO -------

    def true_for_each(func, list):
        for e in list:
            if not func e:
                return False

        return True

    def get_rand_spawn_space(self):

        (x,y) = (0,0)
        new_tank = None
        new_pixels = None
        for i in range(100):
            should_break = True
            (x,y) = random.choice(self.available_spawn_locs)
            new_pixels = Tank.three_by_three((x,y))
            for t in self.tanks:
                if len(filter(lambda x: x in new_pixels, t.get_pixel_pos())) > 0:
                    should_break = False
                    break
            if should_break:
                break
        return (x,y)


    def load_test_tanks(self):

        tank_1 = Tank("penis",
                      "ais/test_1.py",
                      copy.deepcopy(self.perma_board),
                      27,27)
        self.scores["penis"] = 0
        tank_2 = Tank("dickbutt",
                      "ais/test_2.py",
                      copy.deepcopy(self.perma_board),
                      12,22)
        self.scores["dickbutt"] = 0
        tank_3 = Tank("sex",
                      "ais/test_3.py",
                      copy.deepcopy(self.perma_board),
                      5,12)
        self.scores["sex"] = 0
        poop   = Tank("poop",
                      "ais/wall_hugger.py",
                      copy.deepcopy(self.perma_board),
                      57,49)
        self.scores["poop"] = 0
        doctor = Tank("doctor_love",
                      "ais/doctor.py",
                      copy.deepcopy(self.perma_board),
                      5,4)
        self.scores["doctor_love"] = 0

        self.tanks =  {"penis"         : tank_1,
                       "dickbutt"      : tank_2,
                       "sex"           : tank_3,
                       "doctor_love"   : doctor,
                       "poop"          : poop, }

        for t in self.tanks.itervalues():
            self.assign_color(t)

    def load_colors(self):
        """ creates a queue from the list of possible tank colors"""
        self.color_queue = deque(range(10))

    def assign_color(self,tank):
        """ removes a color from the queue of available colors 
            and gives it to a tank """
        tank.color = self.color_queue.popleft()

    def return_color(self,tank):
        """ removes a color from a tank
            and adds it back to the queue of available colors """
        self.color_queue.append(tank.color)

    def save_leaderboard(self):
        leaderboardfile = "../data/leaderboard.json"
        alive_tanks = self.tanks.values()
        survivors = sorted([{
                    'id':t.ID,
                    'age':t.age,
                    'color':TANK_COLORS[t.color],
                } for t in alive_tanks], key=lambda l: l['age'], reverse=True)
        score = sorted([{
                    'id':ID,
                    'score':score,
                    'msg':'Score: {}'.format(score),
                    'color':(TANK_COLORS[self.tanks[ID].color]
                        if ID in self.tanks.keys() else (30,30,30)),
                } for ID,score in self.scores.iteritems()], key=lambda l: l['score'], reverse=True)
        leaderboard = {
            'survivors':survivors,
            'score':score,
        }
        if USE_SIMULATOR:
            js.globals.handle_leaderboard(json.dumps(leaderboard))
        else:
            with open(leaderboardfile, 'w') as f:
                f.write(json.dumps(leaderboard))