Ejemplo n.º 1
0
    def __create_map(self):
        self.map = MapPanel(0,
                            0,
                            self.MAP_WIDTH,
                            self.MAP_HEIGHT + 1,
                            self.EMPTY,
                            border=PanelBorder.create(bottom="-"))

        self.panels += [self.map]
        self.place_objects(self.TREE, self.NUM_OF_ROCKS_START)
        self.place_objects(self.ROCK, self.NUM_OF_TREES_START)
        self.place_objects(self.SNOWMAN, self.NUM_OF_SNOWMAN_START)
        self.place_objects(self.COIN, self.NUM_OF_COINS_START)
        self.place_objects(self.HEART, self.NUM_OF_HEARTS_START)
        self.place_objects(self.JUMP, self.NUM_OF_JUMPS_START)

        # make a clearing for the player
        for y in range(8):
            for x in range(self.MAP_WIDTH):
                self.map[(x, self.MAP_HEIGHT - 1 - y)] = self.EMPTY

        # place decorative trees
        self.map[(self.player_pos[0] + 5, self.player_pos[1])] = self.TREE
        self.map[(self.player_pos[0] - 5, self.player_pos[1])] = self.TREE

        # place initial hearts
        self.map[(self.player_pos[0], self.player_pos[1] - 2)] = self.HEART
        self.map[(self.player_pos[0], self.player_pos[1] - 3)] = self.HEART
Ejemplo n.º 2
0
 def init_board(self):
     self.map = MapPanel(0,
                         0,
                         self.MAP_WIDTH,
                         self.MAP_HEIGHT,
                         self.EMPTY,
                         border=PanelBorder.create(bottom=self.MAP_BOTTOM))
     self.panels.append(self.map)
Ejemplo n.º 3
0
    def init_board(self):
        self.map = MapPanel(0, 0, self.MAP_WIDTH, self.MAP_HEIGHT, self.EMPTY,
                            border=PanelBorder.create(bottom="-"))
        self.panels += [self.map]

        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER_C
        self.map[(self.player_right[0], self.player_right[1])] = self.PLAYER_R
        self.map[(self.player_left[0], self.player_left[1])] = self.PLAYER_L

        self.draw_level()
Ejemplo n.º 4
0
    def init_board(self):
        self.map = MapPanel(0, 3, self.MAP_WIDTH, self.MAP_HEIGHT + 1,
                            self.EMPTY)
        #                            border=PanelBorder.create(bottom="-"))

        self.panels += [self.map]

        mapmap = {
            '|': self.WALL,
            '-': self.HYPHEN,
            'L': self.L,
            '7': self.SEVEN,
            'J': self.J,
            'F': self.F,
            '.': self.DOT,
            'O': self.POWER,
            '=': self.DOOR,
            '#': self.FULL,
            ' ': self.EMPTY,
            'B': self.BLINKY,
            'P': self.PINKY,
            'I': self.INKY,
            'C': self.CLYDE
        }

        # reset positions first so ghosts not in maze when filled
        self.reset_positions()

        # make fruit counter negative to eliminate the case when you
        # switch levels when a fruit is visible
        self.fruit_visible = -1

        # open map file
        x = 0
        y = 0
        with open("map.txt") as f:
            for line in f:
                x = 0
                for char in line:
                    if char != '\n':
                        self.map[(x, y)] = mapmap[char]
                    x += 1
                y += 1

        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER

        self.redraw_ghosts()

        self.redraw_lives()

        self.print_ready()
Ejemplo n.º 5
0
def test_edges():
    panel = MapPanel(1, 1, 2, 2)
    frame = GridFrameBuffer(5, 5, init_value=" ")
    panel.add("1", (0, 0))
    panel.add("2", (1, 0))
    panel.add("3", (0, 1))
    panel.add("4", (1, 1))
    panel.redraw(frame)
    exp = ["     ", " 12  ", " 34  ", "     ", "     "]
    exp_frame = GridFrameBuffer.from_string_array(exp)
    assert frame == exp_frame
Ejemplo n.º 6
0
 def init_board(self):
     self.map = MapPanel(1,
                         1,
                         self.MAP_WIDTH,
                         self.MAP_HEIGHT,
                         self.EMPTY,
                         border=PanelBorder.create(bottom=True,
                                                   left=True,
                                                   right=True,
                                                   top=True))
     self.panels += [self.map]
Ejemplo n.º 7
0
    def __create_map(self):
        self.map = MapPanel(0,
                            0,
                            self.MAP_WIDTH,
                            self.MAP_HEIGHT + 1,
                            self.EMPTY,
                            border=PanelBorder.create(bottom=True,
                                                      left=True,
                                                      right=True,
                                                      top=True))
        self.panels += [self.map]

        self.place_bikes()
        for i in self.CORRUPTION:
            self.map[i.pos()] = i.char
        self.map[self.USER.pos()] = self.USER.char
Ejemplo n.º 8
0
class SpaceInvaders(GridGame):
    MAP_WIDTH = 60
    MAP_HEIGHT = 25
    SCREEN_WIDTH = 60
    SCREEN_HEIGHT = MAP_HEIGHT + 6
    MSG_START = 20
    MAX_MSG_LEN = SCREEN_WIDTH - MSG_START - 1
    CHAR_WIDTH = 16
    CHAR_HEIGHT = 16
    GAME_TITLE = "Space Invaders"
    CHAR_SET = "resources/terminal16x16_gs_ro.png"

    NUM_OF_INVADERS = 10
    TOTAL_INVADERS = 10
    MAX_TURNS = 900

    score = 0

    # we use these for moving the mothership easily
    LEFT = -1
    RIGHT = 1
    mothership_direction = RIGHT

    MOTHERSHIP_SPEED = 3
    mothership_exists = False

    MOTHERSHIP_L = chr(241)
    MOTHERSHIP_C = chr(242)
    MOTHERSHIP_R = chr(243)

    MOTHERSHIP_POINTS = 300
    INVADER0_POINTS = 50
    INVADER1_POINTS = 40
    INVADER2_POINTS = 30

    INVADER0 = chr(244)  # worth 50 points
    INVADER1 = chr(245)  # worth 40 points
    INVADER2 = chr(246)  # worth 30 points

    # create a list of sprite to blit by type on redraw
    INVADER_SPRITE = [INVADER0, INVADER1, INVADER2]
    BARRIER_1 = chr(247)
    BARRIER_2 = chr(248)
    BARRIER_3 = chr(249)
    BARRIER_4 = chr(250)
    MISSILE = chr(251)
    BULLET = chr(252)
    PLAYER_L = chr(253)
    PLAYER_C = chr(254)
    PLAYER_R = chr(255)
    EMPTY = ' '
    OUT_OF_BOUNDS = chr(240)
    fire_rate = 2  # the fire rate of invaders

    def __init__(self, random):
        self.random = random
        self.running = True
        self.centerx = self.MAP_WIDTH // 2
        self.centery = self.MAP_HEIGHT // 2
        self.player_pos = [self.centerx, (int)(self.MAP_HEIGHT * .99)]
        self.player_right = [self.centerx + 1, (int)(self.MAP_HEIGHT * .99)]
        self.player_left = [self.centerx - 1, (int)(self.MAP_HEIGHT * .99)]
        self.invaders = []
        self.drops_eaten = 0
        self.invaders_left = 0
        self.missiles_left = 0
        self.apple_pos = []
        self.objects = []
        self.turns = 0
        self.bullets_fired = 0
        self.level = 0
        self.gravity_power = 1
        self.bullet_speed = 3
        self.invader_speed = 1
        self.placed_invaders = 0
        self.movement_direction = Direction.RIGHT
        self.msg_panel = MessagePanel(self.MSG_START, self.MAP_HEIGHT + 1, self.SCREEN_WIDTH - self.MSG_START, 5)
        self.status_panel = StatusPanel(0, self.MAP_HEIGHT + 1, self.MSG_START, 5)
        self.panels = [self.msg_panel, self.status_panel]
        self.msg_panel.add("Welcome to " + self.GAME_TITLE + "!!!")
        self.lives = 3
        self.life_lost = False
        self.at_bottom = False

        self.debug = False

    def init_board(self):
        self.map = MapPanel(0, 0, self.MAP_WIDTH, self.MAP_HEIGHT, self.EMPTY,
                            border=PanelBorder.create(bottom="-"))
        self.panels += [self.map]

        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER_C
        self.map[(self.player_right[0], self.player_right[1])] = self.PLAYER_R
        self.map[(self.player_left[0], self.player_left[1])] = self.PLAYER_L

        self.draw_level()

    def start_game(self):
        # This is a hack to make sure that the map array is setup before the player makes their first move.
        self.player.bot_vars = self.get_vars_for_bot()

    def create_new_player(self, prog):
        self.player = DefaultGridPlayer(prog, self.get_move_consts())
        return self.player

    def draw_level(self):
        if self.debug:
            print("Redrawing map! turn: %d" % (self.turns))

        start_barrier = 5  # we want to offset the first barrier
        barrier_height = 3
        barrier_width = 5
        set_sb = False
        self.mothership_exists = False

        for w in range(0, 60):
            for h in range(0, 25):
                self.map[(w,h)] = self.EMPTY


        for w in range(0, 60):
            for h in range(0, 25):
                # generating the invaders -- 5 rows of 11, alternating columns and rows
                if h < 10 and w >= 20 and w <= 40:
                    if (w % 2 == 0) and (h % 2 == 1):
                        if h == 9 or h == 7:
                            self.invaders.append(Invader((w, h), 2))
                            self.map[(w, h)] = self.INVADER2
                        elif h == 5 or h == 3:
                            self.invaders.append(Invader((w, h), 1))
                            self.map[(w, h)] = self.INVADER1
                        elif h == 1:
                            self.invaders.append(Invader((w, h), 0))
                            self.map[(w, h)] = self.INVADER0

                # generate the barriers
                if h >= self.MAP_HEIGHT - 1 - barrier_height and h < self.MAP_HEIGHT - 1:
                    # it's a barrier row
                    if w >= start_barrier and w <= start_barrier + barrier_width:  # we draw the barrier
                        self.map[(w, h)] = self.BARRIER_4
                        if w == start_barrier + barrier_width:
                            set_sb = True
            if set_sb:
                start_barrier += 12  # to achieve spacing between barriers...hopefully
                set_sb = False
        self.set_bottom_invaders()

    def set_bottom_invaders(self):
        # all_invaders = [ x for x in self.invaders ]
        cols = {}
        # sort all the invaders into columns
        for invader in self.invaders:
            pos = invader.get_pos()
            if pos[0] in cols:
                cols[pos[0]].append(invader)
            else:
                empty = []
                empty.append(invader)
                cols[pos[0]] = empty
        # sort each column on y value descending (high y values are "lower")
        for i in range(0, self.MAP_WIDTH):
            if i in cols:
                cols[i] = sorted(cols[i], key=lambda x: x.get_pos()[1], reverse=True)
                cols[i][0].set_bottom(True)

    def handle_mothership(self):
        # if there is a bullet in center_positon, center_position + direction * 1, * 2, or * 3 or * 4 (the right/left requires the *4, because the offset from center position is 1), or any of those positions are out of bounds, remove the entire mothership
        # move the center location over by 3.
        if not self.mothership_exists:
            return
        old_center = self.map.get_all_pos(self.MOTHERSHIP_C).pop()[0]
        redraw = True
        for i in range(0, 5):
            test_x = old_center + i * self.mothership_direction
            if test_x < 0 or test_x >= self.MAP_WIDTH:  # we fell off the map
                # remove mothership
                redraw = False
                self.mothership_exists = False

        clear_l = self.map.get_all_pos(self.MOTHERSHIP_L).pop()
        clear_c = self.map.get_all_pos(self.MOTHERSHIP_C).pop()
        clear_r = self.map.get_all_pos(self.MOTHERSHIP_R).pop()
        self.map[clear_l] = self.EMPTY
        self.map[clear_c] = self.EMPTY
        self.map[clear_r] = self.EMPTY

        if redraw:
            new_l = (clear_l[0] + 3 * self.mothership_direction, 0)
            new_c = (clear_c[0] + 3 * self.mothership_direction, 0)
            new_r = (clear_r[0] + 3 * self.mothership_direction, 0)

            self.map[new_l] = self.MOTHERSHIP_L
            self.map[new_c] = self.MOTHERSHIP_C
            self.map[new_r] = self.MOTHERSHIP_R

        return

    def launch_mothership(self):

        if self.turns % 45 == 0:  # launch mothership every 45 turns
            self.mothership_exists = True
            # launch the ship
            # if the turns are even, we launch from right and vice versa
            center_x = 1  # when launching from left we have to leave space for the left element
            if self.turns % 2 == 0:
                center_x = (int)(self.MAP_WIDTH * .99) - 1
                self.mothership_direction = self.LEFT
                # launch from right
                # the top row is 0
            else:
                self.mothership_direction = self.RIGHT

            if self.debug:
                print("Launching mothership at %d (turn: %d)" % (center_x, self.turns))

            position_l = (center_x - 1, 0)
            position_c = (center_x, 0)
            position_r = (center_x + 1, 0)
            self.map[position_l] = self.MOTHERSHIP_L
            self.map[position_c] = self.MOTHERSHIP_C
            self.map[position_r] = self.MOTHERSHIP_R

    def fire_missiles(self):
        for invader in self.invaders:
            if invader.get_bottom() and not invader.get_missile():  # it can fire
                invader_pos = invader.get_pos()
                # first we determine if the invader can fire...are there any invaders below it?
                # second we determine (randomly) if the invader will fire
                fire = self.random.randint(0, 30 - self.fire_rate)  # hacky way of increasing fire percentage
                if fire == 2:  # hacky way to set it to fire at a low percentage only
                    missile_pos = (invader_pos[0], invader_pos[1] + self.gravity_power)
                    # fixed missile collision problem at the bottom

                    if (missile_pos[0] == self.player_pos[0] and missile_pos[1] == self.player_pos[1]) or (missile_pos[0] == self.player_left[0] and missile_pos[1] == self.player_left[1]) or (missile_pos[0] == self.player_right[0] and missile_pos[1] == self.player_right[1]):
                        if self.debug:
                           print("You lost a life!")
                        self.msg_panel.add(["You lost a life!"])
                        self.map[(self.player_pos[0], self.player_pos[1])] = self.EMPTY
                        self.map[(self.player_right[0], self.player_right[1])] = self.EMPTY
                        self.map[(self.player_left[0], self.player_left[1])] = self.EMPTY
                        self.lives -= 1
                        # reset to center
                        self.player_pos = [self.centerx, (int)(self.MAP_HEIGHT * .99)]
                        self.player_right = [self.centerx + 1, (int)(self.MAP_HEIGHT * .99)]
                        self.player_left = [self.centerx - 1, (int)(self.MAP_HEIGHT * .99)]
                        # remove all missiles
                        for invader in self.invaders:
                           invader.set_missile(False)
                        self.lost_life = True
                        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER_C
                        self.map[(self.player_left[0], self.player_left[1])] = self.PLAYER_L
                        self.map[(self.player_right[0], self.player_right[1])] = self.PLAYER_R
                        # fixed collision problem at the bottom
                    if missile_pos[1] < self.MAP_HEIGHT:
                        invader.set_missile(missile_pos)

    def fire_turret(self):
        # place the bullet one over the position
        # can only have on bullet on the screen at once
        if len(self.map.get_all_pos(self.BULLET)) == 0:
            bullet_pos = (self.player_pos[0], self.player_pos[1] - 1)
            if self.is_barrier(self.map[bullet_pos]):
                self.map[bullet_pos] = self.decrement_barrier(self.map[bullet_pos])
            elif self.map[bullet_pos] == self.MISSILE:
                self.map[bullet_pos] = self.EMPTY
            else:
                self.map[bullet_pos] = self.BULLET
            self.bullets_fired += 1

    def is_barrier(self, c):
        if c == self.BARRIER_1 or c == self.BARRIER_2 or c == self.BARRIER_3 or c == self.BARRIER_4:
            return True
        return False

    def decrement_barrier(self, c):
        if c == self.BARRIER_1:
            return self.EMPTY
        elif c == self.BARRIER_2:
            return self.BARRIER_1
        elif c == self.BARRIER_3:
            return self.BARRIER_2
        elif c == self.BARRIER_4:
            return self.BARRIER_3
        else:
            return self.EMPTY

    def move_invaders(self):


        # determine if we can continue moving in the same direction (nothing will fall off the edge)
        move_down = False
        positions = None
        if self.movement_direction == Direction.RIGHT:
            # sort descending by x value
            positions = sorted([x.get_pos() for x in self.invaders], key=lambda x: x[0], reverse=True)
            # TODO: will this ever occur when we are not testing? Like when someone wins?
            if len(positions) == 0:
                return

            if positions[0][0] + 1 >= self.MAP_WIDTH:
                move_down = True
                self.movement_direction = Direction.LEFT

        elif self.movement_direction == Direction.LEFT:
            positions = sorted([x.get_pos() for x in self.invaders], key=lambda x: x[0], reverse=False)
            if positions[0][0] - 1 < 0:
                move_down = True
                self.fire_rate += 1  # every time they move down, they fire a little bit faster
                self.movement_direction = Direction.RIGHT
            # sort ascending by x value
        if move_down:
            self.move_invaders_down()
            self.move_invaders()  # to move one in the new direction after going down
        elif not move_down:
            movement = self.invader_speed
            if self.movement_direction == Direction.LEFT:
                movement *= -1  # go the other direction
            for invader in self.invaders:
                pos = invader.get_pos()
                new_pos = (pos[0] + movement, pos[1])
                # if not self.map[new_pos] == self.BULLET: 
                invader.set_pos(new_pos)
                # else:
                #    #if its a barrier, we need to decrement it
                #    #if its a bullet, we need to remove it
                #    if self.map[new_pos] == self.BULLET:
                #      self.map[new_pos] == self.EMPTY
                #      print("collision with a bullet!")
                #    elif is_barrier(self.map[new_pos]):
                #      self.map[new_pos] = decrement_barrier(self.map[new_pos])
                #    self.invaders.remove(invader)

    def move_invaders_down(self):
        for invader in self.invaders:
            pos = invader.get_pos()
            new_pos = (pos[0], pos[1] + 1)
            if new_pos[1] < self.MAP_HEIGHT:
                # if self.map[new_pos] != self.BULLET: #it wasn't a hit
                invader.set_pos(new_pos)
                # else: #it was hit
                #    self.map[new_pos] = self.EMPTY

                #    self.invaders.remove(invader)
            else:
                self.at_bottom = True

    def move_bullets(self):
        # there should only be one tbh
        # we need to get the list of all invader positions
        invader_positions = [x.get_pos() for x in self.invaders]
        missile_positions = [x.get_missile() for x in self.invaders]

        # we generate a list of all the mothership positions that we will encounter
        mothership_locations = []
        if self.mothership_exists:
            mothership_locations.append(self.map.get_all_pos(self.MOTHERSHIP_L).pop())
            mothership_locations.append(self.map.get_all_pos(self.MOTHERSHIP_C).pop())
            mothership_locations.append(self.map.get_all_pos(self.MOTHERSHIP_R).pop())
            # we add 2 because we need to detect a collision with the left/right, as well as the center.

        for pos in sorted(self.map.get_all_pos(self.BULLET), key=lambda x: x[1], reverse=False):
            still_exists = True
            # we iterate over all the positions that the bullet "warped" through to detect any collisions
            for i in range(0, self.bullet_speed):  # 0 - 1 so we clear the initial position
                clear = (pos[0], pos[1] - i)
                if clear[1] >= 0 and still_exists:
                    if clear in invader_positions:
                        # we need to find which invader it was and delete it
                        for invader in self.invaders:
                            if invader.get_pos() == clear:

                                # increment the score for the aliens
                                if invader.sprite == 0:
                                    self.score += self.INVADER0_POINTS
                                    if self.debug:
                                        print("Hit INVADER0! (%d left)" % (len(self.invaders)))
                                if invader.sprite == 1:
                                    self.score += self.INVADER1_POINTS
                                    if self.debug:
                                        print("Hit INVADER1! (%d left)" % (len(self.invaders)))
                                if invader.sprite == 2:
                                    self.score += self.INVADER2_POINTS
                                    if self.debug:
                                        print("Hit INVADER2! (%d left)" % (len(self.invaders)))



                                self.invaders.remove(invader)
                        still_exists = False
                        self.map[clear] = self.EMPTY
                        self.map[pos] = self.EMPTY
                    elif clear in mothership_locations:
                        still_exists = False
                        # remove the mothership from map
                        self.map[self.map.get_all_pos(self.MOTHERSHIP_L).pop()] = self.EMPTY
                        self.map[self.map.get_all_pos(self.MOTHERSHIP_R).pop()] = self.EMPTY
                        self.map[self.map.get_all_pos(self.MOTHERSHIP_C).pop()] = self.EMPTY
                        self.mothership_exists = False
                        self.score += self.MOTHERSHIP_POINTS

                    elif self.map[clear] == self.MISSILE:
                        # we need to track downt he invader which owns this missile
                        for invader in self.invaders:
                            if invader.get_missile() == clear:
                                invader.set_missile(False)
                        self.map[pos] = self.EMPTY
                        self.map[clear] = self.EMPTY
                        still_exists = False
                    elif self.is_barrier(self.map[clear]):
                        self.map[clear] = self.decrement_barrier(self.map[clear])
                        self.map[pos] = self.EMPTY
                        still_exists = False
                    else:
                        self.map[clear] = self.EMPTY
                        self.map[pos] = self.EMPTY

            new_pos = (pos[0], pos[1] - self.bullet_speed)
            if new_pos[1] >= 0 and still_exists:
                if new_pos in invader_positions:
                    for invader in self.invaders:
                        if invader.get_pos() == new_pos:
                            self.invaders.remove(invader)
                    still_exists = False
                    self.map[clear] = self.EMPTY
                elif new_pos in missile_positions:
                    for invader in self.invaders:
                        if invader.get_missile() == new_pos:
                            invader.set_missile(False)
                    still_exists = False
                    self.map[new_pos] = self.EMPTY
                elif self.is_barrier(self.map[new_pos]):
                    self.map[new_pos] = self.decrement_barrier(self.map[new_pos])
                    still_exists = False
                if still_exists:
                    self.map[new_pos] = self.BULLET
                    self.map[clear] = self.EMPTY
            # if not still_exists:
            #    self.map[new_pos] = self.EMPTY

    def do_turn(self):
        self.handle_key(self.player.move)
        self.player.bot_vars = self.get_vars_for_bot()
        # End of the game
        if self.turns >= self.MAX_TURNS:
            self.running = False
            self.msg_panel.add("You are out of moves.")
        if self.at_bottom:
            # Not quite sure why it's not adding this message to the panel or updating the lives counter at the bottom.
            # It's a UI "feature"
            self.msg_panel.add(["They have invaded!"])
            self.lives = 0
        if self.lives == 0:
            self.running = False
            msg = "You lost all your lives"
            self.msg_panel.add(msg)
            if self.debug:
                print(msg)
        if self.life_lost:
            self.life_lost = False
            msg = "You lost a life"
            self.msg_panel.add(msg)
            if self.debug:
                print(msg)


    def handle_key(self, key):
        self.turns += 1

        self.map[(self.player_pos[0], self.player_pos[1])] = self.EMPTY
        self.map[(self.player_right[0], self.player_right[1])] = self.EMPTY
        self.map[(self.player_left[0], self.player_left[1])] = self.EMPTY
        # if key == "w":
        # self.player_pos[1] -= 1
        # if key == "s":
        # self.player_pos[1] += 1
        if key == "a":
            if self.player_left[0] - 1 >= 0:
                self.player_pos[0] -= 1
                self.player_right[0] -= 1
                self.player_left[0] -= 1
        if key == "d":
            if self.player_right[0] + 1 < self.MAP_WIDTH:
                self.player_pos[0] += 1
                self.player_right[0] += 1
                self.player_left[0] += 1
        if key == "w":
            self.fire_turret()
        if key == "Q":
            self.running = False
            return

        # move the invaders
        self.move_bullets()  # we do hits detection first
        if len(self.invaders) == 0:
            self.level += 1
            if self.debug:
                print("*************************************************")
                print("No more invaders! New level: %d" % self.level)
                print("*************************************************")

            self.draw_level()
            return

        self.move_invaders()
        self.move_missiles(self.gravity_power)  # move all drops down 1
        self.handle_mothership()

        # collision detection
        position = self.map[(self.player_pos[0], self.player_pos[1])]
        position_left = self.map[(self.player_left[0], self.player_left[1])]
        position_right = self.map[(self.player_right[0], self.player_right[1])]

        collision = False
#        if position == self.MISSILE or position == self.INVADER2 or position == self.INVADER1 or position == self.INVADER0:
#            collision = True
#        if position_left == self.MISSILE or position == self.INVADER2 or position == self.INVADER1 or position == self.INVADER0:
#            collision = True
#        if position_right == self.MISSILE or position == self.INVADER2 or position == self.INVADER1 or position == self.INVADER0:
#            collision = True

        # fixed collision issue at the bottom
        for invader in self.invaders:
           invader_pos = invader.get_pos()
           if self.player_pos[0] == invader_pos[0] and self.player_pos[1] == invader_pos[1]:
              collision = True
           elif self.player_left[0] == invader_pos[0] and self.player_left[1] == invader_pos[1]:
              collision = True
           elif self.player_right[0] == invader_pos[0] and self.player_right[1] == invader_pos[1]:
              collision = True
           elif position == self.MISSILE or position_left == self.MISSILE or position_right == self.MISSILE:
              collision = True
       # fixed collision issue at the bottom






        # self.msg_panel.remove("You lost a life!")
        if collision:
            if self.debug:
                print("You lost a life!")
            self.msg_panel.add(["You lost a life!"])
            position = self.EMPTY  # clear the position
            self.lives -= 1
            # reset to center
            self.player_pos = [self.centerx, (int)(self.MAP_HEIGHT * .99)]
            self.player_right = [self.centerx + 1, (int)(self.MAP_HEIGHT * .99)]
            self.player_left = [self.centerx - 1, (int)(self.MAP_HEIGHT * .99)]
            # remove all missiles
            for invader in self.invaders:
                invader.set_missile(False)
            self.lost_life = True
        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER_C
        self.map[(self.player_left[0], self.player_left[1])] = self.PLAYER_L
        self.map[(self.player_right[0], self.player_right[1])] = self.PLAYER_R

        # Fire the missiles
        self.fire_missiles()

        self.launch_mothership()

        # first we clear all the prevoius invaders
        for old_invader in self.map.get_all_pos(self.INVADER2):
            self.map[old_invader] = self.EMPTY
        for old_invader in self.map.get_all_pos(self.INVADER1):
            self.map[old_invader] = self.EMPTY
        for old_invader in self.map.get_all_pos(self.INVADER0):
            self.map[old_invader] = self.EMPTY
        for old_missile in self.map.get_all_pos(self.MISSILE):
            self.map[old_missile] = self.EMPTY

        for invader in self.invaders:
            self.map[invader.get_pos()] = self.INVADER_SPRITE[invader.sprite]
            if invader.get_missile():
                self.map[invader.get_missile()] = self.MISSILE

    def move_missiles(self, gravity_power):  # gravity power is the number of positions a drop will fall per turn
        for invader in self.invaders:
            pos = invader.get_missile()
            if pos:
                # drop each by gravity_power
                new_pos = (pos[0], pos[1] + gravity_power)
                invader.set_missile(False)
                if new_pos[1] < self.MAP_HEIGHT:
                    if self.map[new_pos] == self.BULLET:
                        self.map[new_pos] = self.EMPTY
                    elif self.map[new_pos] == self.PLAYER_L or self.map[new_pos] == self.PLAYER_C or self.map[
                        new_pos] == self.PLAYER_R:
                        self.life_lost()
                    elif self.is_barrier(self.map[new_pos]):
                        self.map[new_pos] = self.decrement_barrier(self.map[new_pos])
                    else:
                        invader.set_missile(new_pos)
                        self.map[new_pos] = self.MISSILE
                else:  # it fell off the map
                    self.missiles_left -= 1

    def is_running(self):
        return self.running

    def get_vars_for_bot(self):
        bot_vars = {}

        # player x location (center)
        # mothership x location(center)
        bonus_ship_x = -1
        if self.mothership_exists:
            bonus_ship_x = self.map.get_all_pos(self.MOTHERSHIP_C).pop()[0]

        # for these, we send an array where 0 = y and 1 = the character (or self.EMPTY if nothing)
        # we send -1 if the location is out of bounds (for the left-1 and right+1)
        player_x = self.player_pos[0]

        player_left_minus_one = self.EMPTY
        for h in range(self.MAP_HEIGHT - 2, 0, -1):
            if player_x - 2 < 0:
                player_left_minus_one = self.OUT_OF_BOUNDS
            elif not self.map[(player_x - 2, h)] == self.EMPTY:
                player_left_minus_one = self.map[(player_x - 2, h)]
                break

        player_left = self.EMPTY
        for h in range(self.MAP_HEIGHT - 2, 0, -1):
            if not self.map[(player_x - 1, h)] == self.EMPTY:
                player_left = self.map[(player_x - 1, h)]
                break

        player_center = self.EMPTY
        for h in range(self.MAP_HEIGHT - 2, 0, -1):
            if not self.map[(player_x, h)] == self.EMPTY:
                player_center = self.map[(player_x, h)]
                break

        player_right = self.EMPTY
        for h in range(self.MAP_HEIGHT - 2, 0, -1):
            if not self.map[(player_x + 1, h)] == self.EMPTY:
                player_right = self.map[(player_x + 1, h)]
                break

        player_right_plus_one = self.EMPTY
        for h in range(self.MAP_HEIGHT - 2, 0, -1):
            if player_x + 2 >= self.MAP_WIDTH:
                player_right_plus_one = self.OUT_OF_BOUNDS
            elif not self.map[(player_x + 2, h)] == self.EMPTY:
                player_right_plus_one = self.map[(player_x + 2, h)]
                break

        bot_vars["bonus_ship_x"] = bonus_ship_x
        bot_vars["player_x"] = player_x
        bot_vars["player_left_minus_one"] = ord(player_left_minus_one)
        bot_vars["player_left"] = ord(player_left)
        bot_vars["player_center"] = ord(player_center)
        bot_vars["player_right"] = ord(player_right)
        bot_vars["player_right_plus_one"] = ord(player_right_plus_one)

        bot_vars["map_array"] = self.get_map_array_tuple()

        # TODO: pass in the map to the bot

        return bot_vars

    def get_map_array_tuple(self):
        map_arr = []
        for w in range(0, self.MAP_WIDTH):
            w_arr = []
            for h in range(0, self.MAP_HEIGHT):
                w_arr.append(ord(self.map.p_to_char[(w, h)]))
            map_arr.append(tuple(w_arr))

        return tuple(map_arr)

    @staticmethod
    def default_prog_for_bot(language):
        if language == GameLanguage.LITTLEPY:
            return open(os.path.join(os.path.dirname(__file__), "resources/sample_bot.lp"), "r").read()

    @staticmethod
    def get_intro():
        return open(os.path.join(os.path.dirname(__file__), "resources/intro.md"), "r").read()
        # return "Welcome to Space Invaders"

    def get_score(self):
        return self.score

    def draw_screen(self, frame_buffer):
        # if not self.running:
        # self.msg_panel += [""+str(self.drops_eaten)+" drops. Good job!"]

        # Update Status
        self.status_panel["Invaders"] = len(self.invaders)
        self.status_panel["Lives"] = str(self.lives)
        self.status_panel["Move"] = str(self.turns) + " of " + str(self.MAX_TURNS)
        self.status_panel["Score"] = str(self.score)

        for panel in self.panels:
            panel.redraw(frame_buffer)

    @staticmethod
    def get_move_consts():
        return ConstMapping({"west": ord("a"),
                             "east": ord("d"),
                             "fire": ord("w"),
                             "stay": ord("s"),
                             "MOTHERSHIP_L": ord(SpaceInvaders.MOTHERSHIP_L),
                             "MOTHERSHIP_C": ord(SpaceInvaders.MOTHERSHIP_C),
                             "MOTHERSHIP_R": ord(SpaceInvaders.MOTHERSHIP_R),
                             "INVADER_0": ord(SpaceInvaders.INVADER0),
                             "INVADER_1": ord(SpaceInvaders.INVADER1),
                             "INVADER_2": ord(SpaceInvaders.INVADER2),
                             "BARRIER_1": ord(SpaceInvaders.BARRIER_1),
                             "BARRIER_2": ord(SpaceInvaders.BARRIER_2),
                             "BARRIER_3": ord(SpaceInvaders.BARRIER_3),
                             "BARRIER_4": ord(SpaceInvaders.BARRIER_4),
                             "MISSILE": ord(SpaceInvaders.MISSILE),
                             "BULLET": ord(SpaceInvaders.BULLET),
                             "PLAYER_L": ord(SpaceInvaders.PLAYER_L),
                             "PLAYER_C": ord(SpaceInvaders.PLAYER_C),
                             "PLAYER_R": ord(SpaceInvaders.PLAYER_R),
                             "EMPTY": ord(' '),
                             "OUT_OF_BOUNDS": ord(SpaceInvaders.OUT_OF_BOUNDS),
                             "MAP_HEIGHT": SpaceInvaders.MAP_HEIGHT,
                             "MAP_WIDTH": SpaceInvaders.MAP_WIDTH,
                             })
Ejemplo n.º 9
0
class PacBot(GridGame):
    MAP_WIDTH = 30
    MAP_HEIGHT = 33
    SCREEN_WIDTH = MAP_WIDTH
    SCREEN_HEIGHT = MAP_HEIGHT + 6
    MSG_START = 20
    MAX_MSG_LEN = SCREEN_WIDTH - MSG_START - 1
    CHAR_WIDTH = 16
    CHAR_HEIGHT = 16
    GAME_TITLE = "Pac-Bot"
    CHAR_SET = "terminal16x16_gs_ro.png"
    LIVES_START = 4

    SENSE_DIST = 20

    MAX_TURNS = 5000

    # starting positions
    PLAYER_START_X = 14
    PLAYER_START_Y = 24
    BLINKY_START_X = 14
    BLINKY_START_Y = 12
    # start in house for real
    PINKY_START_X = 14
    PINKY_START_Y = 15
    INKY_START_X = 12
    INKY_START_Y = 15
    CLYDE_START_X = 16
    CLYDE_START_Y = 15

    # Ghost scatter mode targets
    BLINKY_TARGET_X = 26
    BLINKY_TARGET_Y = -2
    PINKY_TARGET_X = 3
    PINKY_TARGET_Y = -2
    INKY_TARGET_X = 28
    INKY_TARGET_Y = 34
    CLYDE_TARGET_X = 1
    CLYDE_TARGET_Y = 34

    #    # start in hallway for testing
    #    PINKY_START_X = 15
    #    PINKY_START_Y = 12
    #    INKY_START_X = 16
    #    INKY_START_Y = 12
    #    CLYDE_START_X = 17
    #    CLYDE_START_Y = 12

    PLAYER = '@'
    EMPTY = '\0'
    # APPLE = 'O'
    FULL = chr(224)
    DOT = chr(225)
    POWER = chr(226)
    DOOR = chr(227)
    WALL = chr(228)
    HYPHEN = chr(229)
    J = chr(230)
    L = chr(231)
    F = chr(232)
    SEVEN = chr(233)

    # ghosts
    BLINKY = chr(234)
    PINKY = chr(235)
    INKY = chr(236)
    CLYDE = chr(237)
    EDIBLE_BLINKY = chr(218)
    EDIBLE_PINKY = chr(219)
    EDIBLE_INKY = chr(220)
    EDIBLE_CLYDE = chr(221)
    EYES = chr(238)
    EDIBLE = chr(239)

    # fruit
    CHERRY = chr(240)
    STRAWBERRY = chr(241)
    ORANGE = chr(242)
    BELL = chr(243)
    APPLE = chr(244)
    MELON = chr(245)
    GALAXIAN = chr(246)
    KEY = chr(247)
    STAR = chr(43)
    FRUITS = [CHERRY, STRAWBERRY, ORANGE, ORANGE, APPLE, APPLE, MELON, MELON, GALAXIAN, GALAXIAN, BELL, BELL, KEY]
    FRUIT_POINTS = {CHERRY: 100, STRAWBERRY: 300, ORANGE: 500, APPLE: 700,
                    MELON: 1000, GALAXIAN: 2000, BELL: 3000, KEY: 5000}

    # points
    DOT_POINTS = 10
    POWER_POINTS = 50
    GHOST_BASE_POINTS = 200
    ENERGIZED_TURNS = 50
    TOTAL_PELLETS = 253

    # classes of objects for sensors
    GHOST = chr(254)
    FRUIT = chr(255)

    def __init__(self, random):
        self.sensor_coords = []  # variables for adjustable sensors from LP
        self.random = random
        self.running = True
        self.colliding = False
        self.energized = 0  # positive means energized for that many turns
        self.ghost_multiplier = 1
        self.lives = 3
        self.player_pos = [self.PLAYER_START_X, self.PLAYER_START_Y]
        self.score = 0
        self.extra_life = False
        self.objects = []
        self.turns = 0
        self.level = 0
        self.score_panel = StatusPanel(0, 0, self.MAP_WIDTH, 3)
        self.panels = [self.score_panel]
        self.pellets_eaten = 0
        self.fruit_visible = -1

        # array of ghost objects
        # ghosts use the functions in the PacBot object
        self.ghosts = {}
        self.ghosts['blinky'] = Ghost("blinky", self.BLINKY, self.EDIBLE_BLINKY, self.BLINKY_START_X, self.BLINKY_START_Y)
        self.ghosts['pinky'] = Ghost("pinky", self.PINKY, self.EDIBLE_PINKY, self.PINKY_START_X, self.PINKY_START_Y)
        self.ghosts['inky'] = Ghost("inky", self.INKY, self.EDIBLE_INKY, self.INKY_START_X, self.INKY_START_Y)
        self.ghosts['clyde'] = Ghost("clyde", self.CLYDE, self.EDIBLE_CLYDE, self.CLYDE_START_X, self.CLYDE_START_Y)

        # self.__create_map()

    def get_fruit_for_level(self):

        if self.level > len(self.FRUITS):
            return self.KEY
        else:
            return self.FRUITS[self.level]

    def print_ready(self):
        x = 12
        y = 18
        for char in "READY!":
            self.map[(x, y)] = char
            x += 1

    def print_game_over(self):
        x = 10
        y = 18
        for char in "GAME OVER":
            self.map[(x, y)] = char
            x += 1

    def erase_ready(self):
        x = 12
        y = 18
        for char in "READY!":
            self.map[(x, y)] = self.EMPTY
            x += 1

    def redraw_lives(self):

        # erase status line
        for x in range(self.MAP_WIDTH - 1):
            self.map[(1 + x, 33)] = self.EMPTY

        # redraw lives
        for x in range(self.LIVES_START):
            if self.lives > x:
                self.map[(1 + x, 33)] = self.PLAYER

        # redraw the lower-right bar of fruits

        x = self.MAP_WIDTH - 2 - self.level + 1
        for level in range(self.level + 1):
            self.map[(x, 33)] = self.FRUITS[level]
            x = x + 1

    def reset_positions(self):

        self.map[(self.player_pos[0], self.player_pos[1])] = self.EMPTY

        self.player_pos[0] = self.PLAYER_START_X
        self.player_pos[1] = self.PLAYER_START_Y

        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER

        self.energized = 0

        for g in self.ghosts:

            ghost = self.ghosts[g]

            ghost.vulnerable = 0

            # remove ghosts from their current locations on the map
            # make sure to drop anything they're "carrying"
            if ghost.saved_object:
                self.map[(ghost.pos[0], ghost.pos[1])] = ghost.saved_object
                ghost.saved_object = None
            else:
                self.map[(ghost.pos[0], ghost.pos[1])] = self.EMPTY

            ghost.alive = True
            ghost.mode = "scatter"
            if ghost.name == "blinky":
                ghost.pos[0] = ghost.start_x
                ghost.pos[1] = ghost.start_y
                ghost.in_house = True
            elif ghost.name == "pinky":
                ghost.pos[0] = ghost.start_x
                ghost.pos[1] = ghost.start_y
                ghost.in_house = True
            elif ghost.name == "inky":
                ghost.pos[0] = ghost.start_x
                ghost.pos[1] = ghost.start_y
                ghost.in_house = True
            elif ghost.name == "clyde":
                ghost.pos[0] = ghost.start_x
                ghost.pos[1] = ghost.start_y
                ghost.in_house = True

        self.redraw_ghosts()

    def init_board(self):
        self.map = MapPanel(0, 3, self.MAP_WIDTH, self.MAP_HEIGHT + 1, self.EMPTY)
        #                            border=PanelBorder.create(bottom="-"))

        self.panels += [self.map]

        mapmap = {'|': self.WALL,
                  '-': self.HYPHEN,
                  'L': self.L,
                  '7': self.SEVEN,
                  'J': self.J,
                  'F': self.F,
                  '.': self.DOT,
                  'O': self.POWER,
                  '=': self.DOOR,
                  '#': self.FULL,
                  ' ': self.EMPTY,
                  'B': self.BLINKY,
                  'P': self.PINKY,
                  'I': self.INKY,
                  'C': self.CLYDE}

        # reset positions first so ghosts not in maze when filled
        self.reset_positions()

        # make fruit counter negative to eliminate the case when you
        # switch levels when a fruit is visible
        self.fruit_visible = -1

        # open map file
        x = 0
        y = 0
        with open("map.txt") as f:
            for line in f:
                x = 0
                for char in line:
                    if char != '\n':
                        self.map[(x, y)] = mapmap[char]
                    x += 1
                y += 1

        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER

        self.redraw_ghosts()

        self.redraw_lives()

        self.print_ready()


    def create_new_player(self, prog):
        self.player = DefaultGridPlayer(prog, self.get_move_consts())

        return self.player

    def start_game(self):
        pass

    def do_turn(self):
        self.handle_key(self.player.move)
        self.update_vars_for_player()

    def place_objects(self, char, count, replace=False):
        placed_objects = 0
        while placed_objects < count:
            x = self.random.randint(0, self.MAP_WIDTH - 1)
            y = self.random.randint(0, self.MAP_HEIGHT - 1)

            if self.map[(x, y)] == self.EMPTY:
                self.map[(x, y)] = char
                placed_objects += 1
            elif replace:
                # we can replace objects that exist
                self.map[(x, y)] = char
                placed_objects += 1

    def is_ghost(self, item):
        if item == self.BLINKY or item == self.PINKY or item == self.INKY or item == self.CLYDE or item == self.EDIBLE_BLINKY or item == self.EDIBLE_PINKY or item == self.EDIBLE_INKY or item == self.EDIBLE_CLYDE:
            return True
        else:
            return False

    def is_fruit(self, item):
        if item in self.FRUITS:
            return True
        else:
            return False

    def is_blocked(self, item):
        # returns true if the cell in the map is obstructed

        if item == self.DOT or item == self.POWER or item == self.EMPTY or self.is_ghost(item) or self.is_fruit(item) or item == self.PLAYER:
            # since ghosts use is_blocked, players need to be included
            # in things that do not block motion. Strange but true!
            return False
        else:
            return True

    def get_ghost_by_xy(self, x, y):
        for ghost in self.ghosts:
            this_ghost = self.ghosts[ghost]
            if this_ghost.pos[0] == x and this_ghost.pos[1] == y:
                return this_ghost

    def redraw_ghost(self, ghost):
        if ghost.alive:
            if ghost.vulnerable > 10:
                self.map[(ghost.pos[0], ghost.pos[1])] = ghost.edible_char
            elif ghost.vulnerable > 0:
                # blink to "warn" pac-bot of their impending switch
                if self.turns % 2:
                    self.map[(ghost.pos[0], ghost.pos[1])] = ghost.edible_char
                else:
                    self.map[(ghost.pos[0], ghost.pos[1])] = ghost.char
            else:
                self.map[(ghost.pos[0], ghost.pos[1])] = ghost.char

    def redraw_ghosts(self):
        for g in self.ghosts:
            ghost = self.ghosts[g]
            self.redraw_ghost(ghost)

    def check_ghost_collisions(self):
        # detect collisions -- is there a ghost at player's location?
        if self.is_ghost(self.map[(self.player_pos[0], self.player_pos[1])]):

            # figure out which ghost the player has collided with to see
            # if they are vulnerable
            ghost = self.get_ghost_by_xy(self.player_pos[0], self.player_pos[1])

            if ghost.vulnerable > 0:
                if (self.player_pos[0] == 14 or self.player_pos[0] == 15) and self.player_pos[1] == 13:
#                     if self.player_pos[1] == 13:
                        self.player_pos[1] = 12   #fixed bug: Eating ghost and door.
#                        if ghost.pos[0] == self.player_pos[0] and ghost.pos[1] == self.player_pos[1]:
#                              ghost.alive = False
#                              self.score += self.ghost_multiplier * self.GHOST_BASE_POINTS
#                              if ghost_saved_object == self.DOT:
#                                   self.score += self.DOT_POINTS
#                                   self.pellets_eaten += 1
#                                   ghost.saved_object = None
#                              elif ghost.saved_object == self.POWER:
#                                   self.score += self.POWER_POINTS
#                                   self.pellets_eaten += 1
#                                   ghost.saved_object = None
#                              self.ghost_multiplier += 1
                else:
                     ghost.alive = False  # ghost has been eaten!
                     self.score += self.ghost_multiplier * self.GHOST_BASE_POINTS


                     # check to see if ghost is "holding" a dot / power
                     if ghost.saved_object == self.DOT:
                        self.score += self.DOT_POINTS
                        self.pellets_eaten += 1
                        ghost.saved_object = None
                     elif ghost.saved_object == self.POWER:
                        self.score += self.POWER_POINTS
                        self.pellets_eaten += 1
                        ghost.saved_object = None

                     # increase the score multiplier for ghosts eaten in this
                     # round
                     self.ghost_multiplier += 1

            else:

                # touching ghosts is bad for you
                self.lives -= 1
                self.reset_positions()

    def move_ghost(self, ghost):
            # Ghost movement reference: https://gameinternals.com/understanding-pac-man-ghost-behavior
            # Known deficiencies:
            #   Pinky and Inky do not chase properly
            #   

        def remove_backwards(dirs):
            '''Given a list of directions to move remove the option to reverse direction'''
            if len(dirs) > 1:
                if ghost.previous_dir == 'w' and 's' in dirs:
                    dirs.remove('s')
                elif ghost.previous_dir == 's' and 'w' in dirs:
                    dirs.remove('w')
                elif ghost.previous_dir == 'a' and 'd' in dirs:
                    dirs.remove('d')
                elif ghost.previous_dir == 'd' and 'a' in dirs:
                    dirs.remove('a')
            
            return dirs
        
        def dir_to_target():
            '''Find the legal moves that are closest to the current target.'''
            min_dist = self.map_distance(ghost.pos, ghost.target_pos) + 100
            dirs = []
            for delta in [(1, 0, 'd'), (-1, 0, 'a'), (0, 1, 's'), (0, -1, 'w')]:
                new_pos = (ghost.pos[0] + delta[0], ghost.pos[1] + delta[1])
                item = self.map[new_pos]
                print(self.is_blocked(item))

                # Living ghosts can't go back in the house, but dead ones can
                if ghost.alive:
                    if (not self.is_blocked(item)) and (not self.is_ghost(item)):
                        distance = self.map_distance(new_pos, ghost.target_pos)
                        if distance < min_dist:
                            min_dist = distance
                            dirs = [delta[2]]
                        elif distance == min_dist:
                            dirs.append(delta[2])
                else:
                    if (item == self.DOOR) or (not self.is_blocked and not self.is_ghost(item)):
                        distance = self.map_distance(new_pos, ghost.target_pos)
                        if distance < min_dist:
                            min_dist = distance
                            dirs = [delta[2]]
                        elif distance == min_dist:
                            dirs.append(delta[2])
            
            return dirs

        def pick_dir(dirs):
            '''Given a list of legal and equally good moves, pick one according to "up > left > down"'''
            choice = dirs[0]
            if len(dirs) > 1:
                    if 'w' in dirs:
                        choice = 'w'
                    elif 'a' in dirs:
                        choice = 'a'
                    elif 's' in dirs:
                        choice = 's'
                    else:
                        choice = 'd'

            return choice

        def ghost_replace_item():
            '''If ghost saved an object, drop the object before moving the ghost to the new location, 
            otherwise, erase the ghost's current location'''
            if ghost.saved_object:
                self.map[(ghost.pos[0], ghost.pos[1])] = ghost.saved_object
                ghost.saved_object = None
            else:
                self.map[(ghost.pos[0], ghost.pos[1])] = self.EMPTY

        def update_ghost_pos(choice):
            if choice == 'a':
                ghost.pos[0] -= 1
            elif choice == 'd':
                ghost.pos[0] += 1
            elif choice == 'w':
                ghost.pos[1] -= 1
            elif choice == 's':
                ghost.pos[1] += 1
            ghost.previous_dir = ghost.direction
            ghost.direction = choice

            # if the ghost is just north of the door, set it so that
            # they can't go back into the house
            if self.map[(ghost.pos[0], ghost.pos[1] + 1)] == self.DOOR:
                ghost.in_house = False

        def ghost_pickup_item():
            # if there is already something at the ghost's new
            # location (a fruit, pellet, or energizer), save it by
            # having the ghost "pick it up"
            if self.map[(ghost.pos[0], ghost.pos[1])] not in [self.EMPTY, self.PLAYER]:
                ghost.saved_object = self.map[(ghost.pos[0], ghost.pos[1])]

        if ghost.alive == False:
            # If a dead ghost has reached its target it is alive again and in the house
            if ghost.pos[0] == ghost.start_x and ghost.pos[1] == ghost.start_y:
                ghost.vulnerable = 0
                ghost.alive = True
                ghost.in_house = True
            else:
                # if the ghost is "dead" then it should move back to the house
                ghost.target_pos = (ghost.start_x, ghost.start_y)
                
                dirs = dir_to_target()
                dirs = remove_backwards(dirs)
                choice = pick_dir(dirs)
                update_ghost_pos(choice)

        if ghost.alive and ghost.mode == "chase":
            # chase pac-bot
            if ghost.name == 'blinky':
                ghost.target_pos = self.player_pos
            elif ghost.name == 'pinky':
                # TODO: Pinky should target 4 tiles ahead of player, but need player orientation for that
                ghost.target_pos = self.player_pos
            elif ghost.name == 'inky':
                # TODO: Inky's target is likewise dependent on the orientation of the player
                ghost.target_pos = self.player_pos
            elif ghost.name == 'clyde':
                player_distance = self.map_distance(ghost.pos, self.player_pos)
                if (player_distance > 8):
                    ghost.target_pos = self.player_pos
                else:
                    ghost.target_pos = (self.CLYDE_TARGET_X, self.CLYDE_TARGET_Y)
            
            dirs = dir_to_target()
            dirs = remove_backwards(dirs)
            choice = pick_dir(dirs)
            ghost_replace_item()
            update_ghost_pos(choice)

        elif ghost.alive and ghost.mode == "scatter":
            # go to individual corners
            if ghost.name == 'blinky':
                ghost.target_pos = (self.BLINKY_TARGET_X, self.BLINKY_TARGET_Y)
            elif ghost.name == 'pinky':
                ghost.target_pos = (self.PINKY_TARGET_X, self.PINKY_TARGET_Y)
            elif ghost.name == 'inky':
                ghost.target_pos = (self.INKY_TARGET_X, self.INKY_TARGET_Y)
            elif ghost.name == 'clyde':
                ghost.target_pos = (self.CLYDE_TARGET_X, self.CLYDE_TARGET_Y)
            
            print(ghost.name)
            print(ghost.pos)
            print("Previous: ", ghost.previous_dir)
            dirs = dir_to_target()
            print(dirs)
            dirs = remove_backwards(dirs)
            print(dirs)
            choice = pick_dir(dirs)
            ghost_replace_item()
            update_ghost_pos(choice)

        elif ghost.alive:  # mode is frightened
            # run away from pac-bot

            dirs = []  # list of directions we can go

            # determine which directions are open
            item = self.map[(ghost.pos[0] + 1, ghost.pos[1])]
            if not self.is_blocked(item) and not self.is_ghost(item):
                dirs.append("d")

            item = self.map[(ghost.pos[0] - 1, ghost.pos[1])]
            if not self.is_blocked(item) and not self.is_ghost(item):
                dirs.append("a")

            item = self.map[(ghost.pos[0], ghost.pos[1] + 1)]
            if not self.is_blocked(item) and not self.is_ghost(item):
                dirs.append("s")

            item = self.map[(ghost.pos[0], ghost.pos[1] - 1)]
            if ghost.in_house and item == self.DOOR or not self.is_blocked(item) and not self.is_ghost(item):
                dirs.append("w")

            if len(dirs) > 0:

                # if current direction is available, keep going that way
                # (this is historical ghost behavior)

                if ghost.direction in dirs and ghost.in_house == False:

                    choice = ghost.direction

                else:

                    direction = self.random.randint(0, len(dirs) - 1)
                    choice = dirs[direction]

                    ghost.direction = choice

                # if ghost saved an object, drop the object before
                # moving the ghost to the new location, otherwise, 
                # erase the ghost's current location
                if ghost.saved_object:
                    self.map[(ghost.pos[0], ghost.pos[1])] = ghost.saved_object
                    ghost.saved_object = None
                else:
                    self.map[(ghost.pos[0], ghost.pos[1])] = self.EMPTY

                if choice == 'a':
                    ghost.pos[0] -= 1
                elif choice == 'd':
                    ghost.pos[0] += 1
                elif choice == 'w':
                    ghost.pos[1] -= 1
                elif choice == 's':
                    ghost.pos[1] += 1

                # if there is already something at the ghost's new
                # location (a fruit, pellet, or energizer), save it by
                # having the ghost "pick it up"
                if self.map[(ghost.pos[0], ghost.pos[1])] not in [self.EMPTY, self.PLAYER]:
                    ghost.saved_object = self.map[(ghost.pos[0], ghost.pos[1])]

                # if the ghost is just north of the door, set it so that
                # they can't go back into the house
                if self.map[(ghost.pos[0], ghost.pos[1] + 1)] == self.DOOR:
                    ghost.in_house = False

        if ghost.pos[0] == 0 and ghost.pos[1] == 15:
            ghost.pos[0] = 28
        elif ghost.pos[0] == 29 and ghost.pos[1] == 15:
            ghost.pos[0] = 1

        ghost.vulnerable -= 1  # draw down the time the ghost is vulnerable

        # draw the ghost into the map spot so that other ghosts
        # won't share the same spot
        self.redraw_ghost(ghost)
        self.check_ghost_collisions()

    def handle_key(self, key):

        self.turns += 1

        if self.energized > 0:  # count down powered turns
            self.energized -= 1
            if self.energized == 0:
                self.ghost_multiplier = 1  # reset for next time

        if self.pellets_eaten == 1:
            self.erase_ready()

        if DEBUG:
            print("turn: %d player started at (%d, %d)" % (self.turns, self.player_pos[0], self.player_pos[1]))

        self.map[(self.player_pos[0], self.player_pos[1])] = self.EMPTY

        item = self.map[(self.player_pos[0] - 1, self.player_pos[1])]
        if key == "a" and not self.is_blocked(item):
            self.player_pos[0] -= 1

        item = self.map[(self.player_pos[0] + 1, self.player_pos[1])]
        if key == "d" and not self.is_blocked(item):
            self.player_pos[0] += 1

        item = self.map[(self.player_pos[0], self.player_pos[1] - 1)]
        if key == "w" and not self.is_blocked(item):
            self.player_pos[1] -= 1

        item = self.map[(self.player_pos[0], self.player_pos[1] + 1)]
        if key == "s" and not self.is_blocked(item):
            self.player_pos[1] += 1


        if key == "Q":
            self.running = False
            return

        self.check_ghost_collisions()

        # add score based on new position
        if self.map[(self.player_pos[0], self.player_pos[1])] == self.DOT:
            self.score += self.DOT_POINTS
            self.pellets_eaten += 1
        if self.map[(self.player_pos[0], self.player_pos[1])] == self.POWER:
            self.score += self.POWER_POINTS
            self.energized = self.ENERGIZED_TURNS
            self.pellets_eaten += 1

            # make all ghosts vulnerable:

            for g in self.ghosts:
                ghost = self.ghosts[g]
                ghost.vulnerable = self.ENERGIZED_TURNS
                self.redraw_ghosts()

            self.pellets_eaten += 1

        # make fruit appear
        if self.pellets_eaten == 70 or self.pellets_eaten == 170:
            self.map[(14, 18)] = self.get_fruit_for_level()
            self.fruit_visible = 50
        
        # make fruit disappear
        if self.fruit_visible > 0:
            self.fruit_visible = self.fruit_visible - 1
            
        if self.fruit_visible == 0:
            self.map[(14, 18)] = self.EMPTY

        # handle eating fruit
        item = self.map[(self.player_pos[0], self.player_pos[1])]
        if item in self.FRUITS:
            self.score += self.FRUIT_POINTS[item]
            self.fruit_visible = 0

        # handle clearing the board
        if (self.pellets_eaten != 0 and ((self.pellets_eaten % self.TOTAL_PELLETS) == 0)):
            self.level += 1
            self.pellets_eaten = 0
            self.init_board()
            # self.__create_map()

        # handle extra life
        if self.score >= 10000 and self.extra_life == False:
            print ("Gave extra life!")
            self.extra_life = True
            self.lives += 1

        # handle traveling through tunnels in either direction
        if self.player_pos[0] == 0 and self.player_pos[1] == 15:
            self.player_pos[0] = 28
        elif self.player_pos[0] == 29 and self.player_pos[1] == 15:
            self.player_pos[0] = 1

        # put the player in the new position
        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER

        # draw the life meter at the bottom
        self.redraw_lives()

        # move ghosts -- speed based on level
        if self.level == 0 and self.turns % 3 == 0 or self.level == 1 and self.turns % 2 == 0 or self.level >= 2:
            for g in self.ghosts:
                ghost = self.ghosts[g]
                self.move_ghost(ghost)

        if DEBUG:
            print("turn: %d player ended at (%d, %d)" % (self.turns, self.player_pos[0], self.player_pos[1]))

        # End of the game
        if self.turns >= self.MAX_TURNS:
            self.running = False
        elif self.lives <= 0:
            self.running = False

        # vars should be gotten at the end of handle_turn, because vars
        # affect the *next* turn...

    def is_running(self):
        return self.running

    def read_bot_state(self, state):
        None

    def get_map_array_tuple(self):
        map_arr = []
        for w in range(0, self.MAP_WIDTH):
            w_arr = []
            for h in range(0, self.MAP_HEIGHT):
#                 item = self.map[(w,h)]
#                 if self.is_blocked(item):
#                     item = ord(self.WALL)
#                 elif self.is_ghost(item):
#                     item = ord(self.GHOST)
#                 elif self.is_fruit(item):
#                     item = ord(self.FRUIT)
#                 else:
#                     item = ord(item)
# 
#                 w_arr.append(item)
#                item = ord(self.map.p_to_char[(w,h)])
#                if self.is_blocked(chr(item)):
#                    item = ord(self.WALL)
                w_arr.append(ord(self.map.p_to_char[(w,h)]))
            map_arr.append(tuple(w_arr))

        return tuple(map_arr)

    

    def update_vars_for_player(self):
        bot_vars = {}

        # what borders player?
        dirmod = {'sense_w': [-1, 0], 'sense_e': [1, 0], 'sense_n': [0, -1], 'sense_s': [0, 1]}

        for sense in dirmod:
            xmod = dirmod[sense][0]
            ymod = dirmod[sense][1]
            obj = self.map[(self.player_pos[0] + xmod, self.player_pos[1] + ymod)]

            if self.is_blocked(obj):
                bot_vars[sense] = ord(self.WALL)
            elif self.is_ghost(obj):
                bot_vars[sense] = ord(self.GHOST)
            elif self.is_fruit(obj):
                bot_vars[sense] = ord(self.FRUIT)
            elif obj == self.DOT:
                bot_vars[sense] = ord(self.DOT)
            elif obj == self.POWER:
                bot_vars[sense] = ord(self.POWER)
            else:
                bot_vars[sense] = ord(self.EMPTY)

        bot_vars['lives'] = self.lives
        bot_vars['energized'] = self.energized
        bot_vars['level'] = self.level
        bot_vars["dot_x"] = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.DOT, default=(0, 0))[0]
        bot_vars["dot_y"] = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.DOT, default=(0, 0))[1]
        bot_vars["power_x"] = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.POWER, default=(0, 0))[0]
        bot_vars["power_y"] = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.POWER, default=(0, 0))[1]
        bot_vars["player_x"] = self.player_pos[0]
        bot_vars["player_y"] = self.player_pos[1]
        
        # get locations for ghosts -- edible or not
        offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.BLINKY, default=(0, 0))

        if offset[0] == 0 and offset[1] == 0:
            # blinky doesn't exist, so he must be edible_blinky!
            offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.EDIBLE_BLINKY, default=(0, 0))

        bot_vars["blinky_x"] = offset[0]
        bot_vars["blinky_y"] = offset[1]
        
        # rinse and repeat for other ghosts

        offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.INKY, default=(0, 0))

        if offset[0] == 0 and offset[1] == 0:
            offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.EDIBLE_INKY, default=(0, 0))

        bot_vars["inky_x"] = offset[0]
        bot_vars["inky_y"] = offset[1]
        
        offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.PINKY, default=(0, 0))

        if offset[0] == 0 and offset[1] == 0:
            offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.EDIBLE_PINKY, default=(0, 0))

        bot_vars["pinky_x"] = offset[0]
        bot_vars["pinky_y"] = offset[1]
        
        offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.CLYDE, default=(0, 0))

        if offset[0] == 0 and offset[1] == 0:
            offset = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), self.EDIBLE_CLYDE, default=(0, 0))

        bot_vars["clyde_x"] = offset[0]
        bot_vars["clyde_y"] = offset[1]
        
        # map_array is broken so why slow things down?
        #bot_vars["map_array"] = self.get_map_array_tuple()


        # find closest fruit -- if one exists
        cand_x = 0
        cand_y = 0
        bot_vars['fruit_x'] = 0
        bot_vars['fruit_y'] = 0
        lastfruit = None

        for fruit in self.FRUITS:

            # if we've seen this fruit already, skip it
            if fruit == lastfruit:
                continue
            else:
                lastfruit = fruit

            cand_x = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), fruit, default=(0, 0))[0]
            cand_y = self.map.get_x_y_dist_to_foo(tuple(self.player_pos), fruit, default=(0, 0))[1]

            if cand_x != 0 or cand_y != 0:
                bot_vars['fruit_x'] = cand_x
                bot_vars['fruit_y'] = cand_y
                break

        if DEBUG:
            print("bot_vars:")
            for key in bot_vars.keys():
                if key != "map_array":
                    print("%s : %s" % (key, bot_vars[key]))

        if DUMP_BOT_VARS:
            if "map_array" in bot_vars:
                print("map_array:")
                for row in bot_vars['map_array']:
                    print(row)


        self.player.bot_vars = bot_vars

    @staticmethod
    def default_prog_for_bot(language):
        if language == GameLanguage.LITTLEPY:
            return open("bot.lp", "r").read()

    @staticmethod
    def get_intro():
        return open("intro.md", "r").read()

    @staticmethod
    def get_move_consts():
        return ConstMapping({"west": ord("a"),
                             "east": ord("d"),
                             "south": ord("s"),
                             "north": ord("w"),
                             "DOT": ord(PacBot.DOT),
                             "POWER": ord(PacBot.POWER),
                             "WALL": ord(PacBot.WALL),
                             "GHOST": ord(PacBot.GHOST),
                             "INKY": ord(PacBot.INKY),
                             "PINKY": ord(PacBot.PINKY),
                             "BLINKY": ord(PacBot.BLINKY),
                             "CLYDE": ord(PacBot.CLYDE),
                             "EDIBLE": ord(PacBot.EDIBLE),
                             "EDIBLE_INKY": ord(PacBot.EDIBLE_INKY),
                             "EDIBLE_PINKY": ord(PacBot.EDIBLE_PINKY),
                             "EDIBLE_BLINKY": ord(PacBot.EDIBLE_BLINKY),
                             "EDIBLE_CLYDE": ord(PacBot.EDIBLE_CLYDE),
                             "FRUIT": ord(PacBot.FRUIT),
                             "FRUIT": ord(PacBot.CHERRY),
                             "FRUIT": ord(PacBot.STRAWBERRY),
                             "FRUIT": ord(PacBot.ORANGE),
                             "FRUIT": ord(PacBot.BELL),
                             "FRUIT": ord(PacBot.APPLE),
                             "FRUIT": ord(PacBot.MELON),
                             "FRUIT": ord(PacBot.GALAXIAN),
                             "FRUIT": ord(PacBot.KEY),
                             "FRUIT": ord(PacBot.STAR),
                             "EMPTY": ord(PacBot.EMPTY),
                             "PLAYER": ord(PacBot.PLAYER),
                             "map_height": PacBot.MAP_HEIGHT,
                             "map_width": PacBot.MAP_WIDTH,
                             })
    
    @staticmethod
    def map_distance(pos, target):
        pos_x, pos_y = pos
        tar_x, tar_y = target
        return math.sqrt((tar_x - pos_x) ** 2 + (tar_y - pos_y) ** 2)

    def get_score(self):
        return self.score

    def draw_screen(self, frame_buffer):
        if not self.running:
            self.print_game_over()
        # Update Status
        self.score_panel["Score"] = self.score

        for panel in self.panels:
            panel.redraw(frame_buffer)
Ejemplo n.º 10
0
class Ski(GridGame):
    MAP_WIDTH = 60
    MAP_HEIGHT = 30
    SCREEN_WIDTH = 60
    SCREEN_HEIGHT = MAP_HEIGHT + 6
    MSG_START = 20
    MAX_MSG_LEN = SCREEN_WIDTH - MSG_START - 1
    CHAR_WIDTH = 16
    CHAR_HEIGHT = 16
    GAME_TITLE = "Ski"
    CHAR_SET = "terminal16x16_gs_ro.png"
    NUM_OF_SENSORS = 8

    SENSE_DIST = 20

    LEVELUP_RESPONSES = [
        "The forest seems to be getting more dense!",
        "Are there more trees here or what?", "Watch out!",
        "Better pay attention!"
    ]

    ROBOT_CRASH_RESPONSES = [
        "OOF!", "OWWWIE!", "THAT'S GONNA LEAVE A MARK!", "BONK!"
    ]
    ROBOT_HEART_RESPONSES = [
        "Wow, I feel a lot better!", "Shazam!", "That's the ticket!", "Yes!!!"
    ]
    ROBOT_COIN_RESPONSES = [
        "Cha-ching!", "Badabing!", "Bling! Bling!", "Wahoo!"
    ]
    ROBOT_FLYING_RESPONSES = [
        "I'm free as a bird now!", "It's a bird, it's a plane...", "Cowabunga!"
    ]

    NUM_OF_ROCKS_START = 30
    NUM_OF_TREES_START = 30
    NUM_OF_SPIKES_START = 30
    NUM_OF_SNOWMAN_START = 30
    NUM_OF_COINS_START = 1
    NUM_OF_HEARTS_START = 1
    NUM_OF_JUMPS_START = 1
    MAX_TURNS = 500
    MAX_FLYING = 10
    FLYING_POINTS = 5
    COIN_POINTS = 25
    HOUSE_ODDS = 500  # e.g., 1/500

    PLAYER = '@'
    EMPTY = '\0'
    HEART = chr(3)
    COIN = chr(4)
    ROCK = chr(15)
    SPIKE = chr(16)
    SNOWMAN = chr(17)
    TRACKS = chr(29)
    TREE = chr(30)
    JUMP = chr(31)
    DEAD = chr(1)
    FLY = chr(2)
    CRASH = chr(8)
    HOUSE = chr(9)

    def __init__(self, random):
        self.random = random
        self.running = True
        self.colliding = False
        self.saved_object = None  # stores a map item we're "on top of"
        self.last_move = 'w'  # need this to restore objects
        self.flying = 0  # set to some value and decrement (0 == on ground)
        self.hp = 1
        self.player_pos = [int(self.MAP_WIDTH / 2), self.MAP_HEIGHT - 4]
        self.score = 0
        self.objects = []
        self.turns = 0
        self.level = 1
        self.msg_panel = MessagePanel(self.MSG_START, self.MAP_HEIGHT + 1,
                                      self.SCREEN_WIDTH - self.MSG_START, 5)
        self.status_panel = StatusPanel(0, self.MAP_HEIGHT + 1, self.MSG_START,
                                        5)
        self.panels = [self.msg_panel, self.status_panel]
        self.msg_panel.add("Velkommen to Robot Backcountry Skiing!")
        self.msg_panel.add("Move left and right! Don't crash!")

        self.__create_map()

    def __create_map(self):
        self.map = MapPanel(0,
                            0,
                            self.MAP_WIDTH,
                            self.MAP_HEIGHT + 1,
                            self.EMPTY,
                            border=PanelBorder.create(bottom="-"))

        self.panels += [self.map]
        self.place_objects(self.TREE, self.NUM_OF_ROCKS_START)
        self.place_objects(self.ROCK, self.NUM_OF_TREES_START)
        self.place_objects(self.SNOWMAN, self.NUM_OF_SNOWMAN_START)
        self.place_objects(self.COIN, self.NUM_OF_COINS_START)
        self.place_objects(self.HEART, self.NUM_OF_HEARTS_START)
        self.place_objects(self.JUMP, self.NUM_OF_JUMPS_START)

        # make a clearing for the player
        for y in range(8):
            for x in range(self.MAP_WIDTH):
                self.map[(x, self.MAP_HEIGHT - 1 - y)] = self.EMPTY

        # place decorative trees
        self.map[(self.player_pos[0] + 5, self.player_pos[1])] = self.TREE
        self.map[(self.player_pos[0] - 5, self.player_pos[1])] = self.TREE

        # place initial hearts
        self.map[(self.player_pos[0], self.player_pos[1] - 2)] = self.HEART
        self.map[(self.player_pos[0], self.player_pos[1] - 3)] = self.HEART

    def init_board(self):
        pass

    def create_new_player(self, prog):
        self.player = SkiPlayer(prog, self.get_move_consts(),
                                self.NUM_OF_SENSORS)
        # place player
        self.map[(self.player_pos[0], self.player_pos[1])] = self.PLAYER

        self.update_vars_for_player()
        return self.player

    def start_game(self):
        pass

    def place_objects(self, char, count, replace=False):
        placed_objects = 0
        while placed_objects < count:
            x = self.random.randint(0, self.MAP_WIDTH - 1)
            y = self.random.randint(0, self.MAP_HEIGHT - 1)

            if self.map[(x, y)] == self.EMPTY:
                self.map[(x, y)] = char
                placed_objects += 1
            elif replace:
                # we can replace objects that exist
                self.map[(x, y)] = char
                placed_objects += 1

    def make_new_row(self):
        for x in range(self.MAP_WIDTH):
            here = self.random.randint(0, self.MAX_TURNS)
            if here <= self.turns:
                which = self.random.randint(0, 2)
                if which == 0:
                    self.map[(x, 0)] = self.ROCK
                elif which == 1:
                    self.map[(x, 0)] = self.TREE
                elif which == 2:
                    self.map[(x, 0)] = self.SNOWMAN

        if self.random.randint(0, 100) > 33:
            self.map[(self.random.randint(0,
                                          self.MAP_WIDTH - 1), 0)] = self.HEART

        if self.random.randint(0, 100) > 33:
            self.map[(self.random.randint(0,
                                          self.MAP_WIDTH - 1), 0)] = self.COIN

        if self.random.randint(0, 100) > 33:
            self.map[(self.random.randint(0,
                                          self.MAP_WIDTH - 1), 0)] = self.JUMP

        if self.random.randint(0, self.HOUSE_ODDS) == 1:
            self.map[(self.random.randint(0,
                                          self.MAP_WIDTH - 1), 0)] = self.HOUSE

    def save_object(self, obj):
        self.saved_object = obj

    def restore_object_tracks(self):

        # restore an object you went over or make tracks

        # where should the object be restored?
        y = 1  # it's always going to be behind us
        x = 0  # we will set the x value accordingly

        if self.last_move == 'a':
            x = 1
        elif self.last_move == 'd':
            x = -1
        elif self.last_move == 'w':
            x = 0

        if self.saved_object:
            if self.last_move == 't':
                # if the player previously teleported when on an
                # obstacle, just destroy the obstacle. We can't put it
                # back where it was because we don't know (x, y) for the
                # player due to map shifting, and we can't draw it under
                # us or we will collide with it twice!
                self.msg_panel.add("Teleporting destroyed the object!")
                self.saved_object = None
            else:
                # if the player didn't teleport, put object back
                self.map[(self.player_pos[0] + x,
                          self.player_pos[1] + y)] = self.saved_object
                self.saved_object = None
        else:
            if self.flying < 1:
                if self.map[(self.player_pos[0] + x,
                             self.player_pos[1] + y)] == self.EMPTY:
                    self.map[(self.player_pos[0] + x,
                              self.player_pos[1] + y)] = self.TRACKS

    def shift_map(self):
        # shift all rows down
        dx = int(self.MAP_WIDTH / 2) - self.player_pos[0]
        self.map.shift_all((dx, 1), wrap_x=True)
        self.player_pos[0] += dx

        self.make_new_row()

        self.restore_object_tracks()

    def do_turn(self):
        self.handle_key(self.player.move)
        self.update_vars_for_player()

    def handle_key(self, key):
        if (DEBUG):
            print("Move {}".format(self.player.move))
            print("Key {}".format(key))
        self.turns += 1
        if self.flying > 0:
            self.score += self.FLYING_POINTS
            self.flying -= 1
            self.msg_panel.add("In flight for " + str(self.flying) +
                               " turns...")
            if self.flying == 0:
                self.msg_panel.add("Back on the ground!")
        else:
            self.score += 1

        if self.turns % 30 == 0:
            self.level += 1
        if (DEBUG):
            print("player_pos[0] {} player_pos[1] {}".format(
                self.player_pos[0], self.player_pos[1]))
        self.map[(self.player_pos[0], self.player_pos[1])] = self.EMPTY

        if key == "a":
            self.player_pos[0] -= 1
        if key == "d":
            self.player_pos[0] += 1
        if key == "w":
            pass
        if key == "t":
            # horizontal-only teleporting code
            self.msg_panel.add("TELEPORT! (-1 HP)")
            self.hp -= 1
            self.player_pos[0] = self.random.randint(0, self.MAP_WIDTH - 1)

        if key == "Q":
            self.running = False
            return

        if (DEBUG):
            print("Key was: %s turn #%d" % (key, self.turns))

        self.last_move = key  # save last move for saved_object restoration

        # shift the map
        self.shift_map()

        self.colliding = False  # reset colliding variable

        # check for various types of collisions (good and bad)
        if self.map[(self.player_pos[0], self.player_pos[1])] == self.ROCK:
            self.save_object(self.ROCK)
            if self.flying == 0:
                self.colliding = True
                self.hp -= 10
                self.msg_panel.add(
                    self.random.choice(
                        list(
                            set(self.ROBOT_CRASH_RESPONSES) -
                            set(self.msg_panel.get_current_messages()))))

        elif self.map[(self.player_pos[0], self.player_pos[1])] == self.TREE:
            self.save_object(self.TREE)
            if self.flying == 0:
                self.colliding = True
                self.hp -= 2
                self.msg_panel.add(
                    self.random.choice(
                        list(
                            set(self.ROBOT_CRASH_RESPONSES) -
                            set(self.msg_panel.get_current_messages()))))

        elif self.map[(self.player_pos[0],
                       self.player_pos[1])] == self.SNOWMAN:
            if self.flying == 0:
                self.colliding = True
                self.hp -= 1
                self.msg_panel.add(
                    self.random.choice(
                        list(
                            set(self.ROBOT_CRASH_RESPONSES) -
                            set(self.msg_panel.get_current_messages()))))
            else:
                self.save_object(
                    self.SNOWMAN)  # flying over snowmen is nondestructive

        elif self.map[(self.player_pos[0], self.player_pos[1])] == self.HEART:
            if self.flying == 0:
                if self.hp < 10:
                    self.hp += 1
                    self.msg_panel.add(
                        self.random.choice(
                            list(
                                set(self.ROBOT_HEART_RESPONSES) -
                                set(self.msg_panel.get_current_messages()))))
                else:
                    self.msg_panel.add("Your HP is already full!")
            else:
                self.save_object(self.HEART)

        elif self.map[(self.player_pos[0], self.player_pos[1])] == self.HOUSE:
            if self.flying == 0:
                self.hp = 10
                self.msg_panel.add("This cabin was very refreshing!")
            else:
                self.save_object(self.HOUSE)

        elif self.map[(self.player_pos[0], self.player_pos[1])] == self.COIN:
            if self.flying == 0:
                self.score += self.COIN_POINTS
                self.msg_panel.add(
                    self.random.choice(
                        list(
                            set(self.ROBOT_COIN_RESPONSES) -
                            set(self.msg_panel.get_current_messages()))))
            else:
                self.save_object(self.COIN)

        elif self.map[(self.player_pos[0], self.player_pos[1])] == self.JUMP:
            if self.flying == 0:
                self.save_object(self.JUMP)
                self.flying += self.random.randint(2, self.MAX_FLYING)
                self.msg_panel.add(
                    self.random.choice(
                        list(
                            set(self.ROBOT_FLYING_RESPONSES) -
                            set(self.msg_panel.get_current_messages()))))
            else:
                self.save_object(self.JUMP)

        # draw player
        if self.flying < 1:
            if self.colliding:
                self.map[(self.player_pos[0], self.player_pos[1])] = self.CRASH
            else:
                self.map[(self.player_pos[0],
                          self.player_pos[1])] = self.PLAYER

        else:
            self.map[(self.player_pos[0], self.player_pos[1])] = self.FLY

        # Check for game-ending state:
        if self.turns >= self.MAX_TURNS:
            self.running = False
        elif self.hp <= 0:
            self.running = False
            self.map[(self.player_pos[0], self.player_pos[1])] = self.DEAD

    def is_running(self):
        return self.running

    def get_map_array_tuple(self):
        map_arr = []
        for w in range(0, self.MAP_WIDTH):
            w_arr = []
            for h in range(0, self.MAP_HEIGHT):
                w_arr.append(ord(self.map.p_to_char[(w, h)]))
            map_arr.append(tuple(w_arr))

        return tuple(map_arr)

    def update_vars_for_player(self):
        bot_vars = {
            "jump_x":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.JUMP,
                                         default=(0, 0))[0],
            "jump_y":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.JUMP,
                                         default=(0, 0))[1],
            "heart_x":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.HEART,
                                         default=(0, 0))[0],
            "heart_y":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.HEART,
                                         default=(0, 0))[1],
            "coin_x":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.COIN,
                                         default=(0, 0))[0],
            "coin_y":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.COIN,
                                         default=(0, 0))[1],
            "house_x":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.HOUSE,
                                         default=(0, 0))[0],
            "house_y":
            self.map.get_x_y_dist_to_foo(tuple(self.player_pos),
                                         self.HOUSE,
                                         default=(0, 0))[1],
            "map_width":
            self.MAP_WIDTH,
            "map_height":
            self.MAP_HEIGHT,
            "hp":
            0,
            "flying":
            0,
            "s1":
            0,
            "s2":
            0,
            "s3":
            0,
            "s4":
            0,
            "s5":
            0,
            "s6":
            0,
            "s7":
            0
        }

        # go through self.sensor_coords and retrieve the map item at the
        # position relative to the player
        for i in range(self.NUM_OF_SENSORS):
            if (i < len(self.player.sensor_coords)):
                sensor = "s" + str(i + 1)
                x_offset = self.player.sensor_coords[i][0]
                y_offset = self.player.sensor_coords[i][1]

                bot_vars[sensor] = ord(
                    self.map[(self.player_pos[0] + int(x_offset),
                              self.player_pos[1] + int(y_offset))])

                if (DEBUG):
                    print(
                        "sensor: %s - i: %d - x_off: %s y_off: %s content: %s"
                        % (sensor, i, x_offset, y_offset, bot_vars[sensor]))

                if bot_vars[sensor] == 64:
                    bot_vars[sensor] = 0

        bot_vars['hp'] = self.hp
        bot_vars['flying'] = self.flying
        bot_vars['map_array'] = self.get_map_array_tuple()

        if DEBUG:
            print("Printing bot_vars:")
            for key in bot_vars.keys():
                if key != "map_array":
                    print("%s ==> %s" % (key, bot_vars[key]))
                else:
                    # sort of pretty print the map
                    for rownum in range(len(bot_vars['map_array'])):
                        print("%02d: " % (rownum), end='')
                        for val in bot_vars['map_array'][rownum]:
                            print("%02d " % (val), end='')
                        print("")
                    print(
                        "Note: map printed sideways for terminal readability (bottom on right)"
                    )
                    print(
                        "Note: robot already dead in last frame -- 2nd to last more useful!"
                    )
                    print("")

        self.player.bot_vars = bot_vars

    @staticmethod
    def default_prog_for_bot(language):
        if language == GameLanguage.LITTLEPY:
            return open("bot.lp", "r").read()

    @staticmethod
    def get_intro():
        return open("intro.md", "r").read()

    @staticmethod
    def get_move_consts():
        return ConstMapping({
            "teleport": ord("t"),
            "west": ord("a"),
            "east": ord("d"),
            "heart": ord(Ski.HEART),
            "coin": ord(Ski.COIN),
            "rock": ord(Ski.ROCK),
            "spikes": ord(Ski.SPIKE),
            "snowman": ord(Ski.SNOWMAN),
            "tracks": ord(Ski.TRACKS),
            "tree": ord(Ski.TREE),
            "jump": ord(Ski.JUMP),
            "house": ord(Ski.HOUSE),
        })

    @staticmethod
    def get_move_names():
        names = Game.get_move_names()
        names.update({ord("t"): "teleport"})
        names.update({ord("d"): "east"})
        names.update({ord("a"): "west"})
        names.update({ord(Ski.HEART): "heart"})
        names.update({ord(Ski.COIN): "coin"})
        names.update({ord(Ski.ROCK): "rock"})
        names.update({ord(Ski.SPIKE): "spikes"})
        names.update({ord(Ski.SNOWMAN): "snowman"})
        names.update({ord(Ski.TRACKS): "tracks"})
        names.update({ord(Ski.TREE): "tree"})
        names.update({ord(Ski.JUMP): "jump"})
        names.update({ord(Ski.HOUSE): "house"})
        return names

    def get_score(self):
        return self.score

    def draw_screen(self, frame_buffer):
        # End of the game
        if self.turns >= self.MAX_TURNS:
            self.msg_panel.add("You are out of moves.")
        elif self.hp <= 0:
            self.msg_panel.add("You sustained too much damage!")

        if not self.running:
            self.msg_panel.add("GAME 0VER: Score:" + str(self.score))

        # Update Status
        self.status_panel["Score"] = self.score
        self.status_panel["Move"] = str(self.turns) + " of " + str(
            self.MAX_TURNS)
        self.status_panel["HP"] = self.HEART * self.hp

        for panel in self.panels:
            panel.redraw(frame_buffer)
Ejemplo n.º 11
0
class FlappyBird(GridGame):
    MAP_WIDTH = 40
    MAP_HEIGHT = 35
    SCREEN_WIDTH = 40
    SCREEN_HEIGHT = MAP_HEIGHT + 6
    MSG_START = 20
    MAX_MSG_LEN = SCREEN_WIDTH - MSG_START - 1
    CHAR_WIDTH = 16
    CHAR_HEIGHT = 16
    GAME_TITLE = "Flappy Robot"

    EMPTY = ' '
    PLAYER = '@'

    PIPE = chr(11 * 16 + 3)  #'|'
    PIPE_TOP_LEFT = chr(13 * 16 + 4)
    PIPE_TOP_RIGHT = chr(11 * 16 + 14)
    PIPE_BUTTOM_LEFT = chr(13 * 16 + 5)
    PIPE_BUTTOM_RIGHT = chr(11 * 16 + 8)

    MAP_BOTTOM = chr(12 * 16 + 4)

    PLAYER_X = 2
    MAX_DOWNWARD_SPEED = 4

    CHAR_SET = "resources/terminal16x16_gs_ro.png"

    PASSING_SOUNDS = ['Swish!', 'Whoosh!', 'Swoosh!', 'Chirp', 'Tweet']

    def __init__(self, random):
        self.random = random
        self.running = True

        self.player_y = self.MAP_HEIGHT // 2
        self.player_v = 0
        self.x = 0
        self.pipes_passed = 0
        self.pipes = []

        self.msg_panel = MessagePanel(self.MSG_START,
                                      self.MAP_HEIGHT + 1,
                                      self.SCREEN_WIDTH - self.MSG_START,
                                      5,
                                      padding=PanelPadding.create(top=1,
                                                                  right=1,
                                                                  bottom=1))
        self.status_panel = StatusPanel(0,
                                        self.MAP_HEIGHT + 1,
                                        self.MSG_START,
                                        5,
                                        padding=PanelPadding.create(top=1,
                                                                    left=1,
                                                                    bottom=1))
        self.panels = [self.status_panel, self.msg_panel]
        self.msg_panel.add("Welcome to")
        self.msg_panel.add("   " + self.GAME_TITLE + "!!!")
        self.msg_panel.add("Don't hit the pipes")

    def init_board(self):
        self.map = MapPanel(0,
                            0,
                            self.MAP_WIDTH,
                            self.MAP_HEIGHT,
                            self.EMPTY,
                            border=PanelBorder.create(bottom=self.MAP_BOTTOM))
        self.panels.append(self.map)

    def create_new_player(self, prog):
        self.player = DefaultGridPlayer(prog, self.get_move_consts())
        self.map[(self.PLAYER_X, self.player_y)] = self.PLAYER

        self.update_vars_for_player()
        return self.player

    def start_game(self):
        pass

    def do_turn(self):
        # print("doing turn")
        self.handle_key(self.player.move)
        self.move_pipes()
        self.draw_pipe_if_needed()
        self.update_vars_for_player()

    def handle_key(self, key):
        if key == 'Q':
            self.running = False
            return

        self.map[(self.PLAYER_X, self.player_y)] = self.EMPTY
        if key == 'w' or key == ' ':
            self.player_y -= 2
            self.player_v = -1
        else:
            self.player_y += self.player_v
            self.player_v += 1 if self.player_v < self.MAX_DOWNWARD_SPEED else 0
        self.x += 1
        self.map.shift_all((-1, 0))
        new_pos = (self.PLAYER_X, self.player_y)
        if not self.map.in_bounds(new_pos) or self.map[new_pos] == self.PIPE:
            self.running = False
            # self.msg_panel.add("You flu into a pipe!")
        else:
            self.map[(self.PLAYER_X, self.player_y)] = self.PLAYER

    def move_pipes(self):
        num_pipes_before = len(self.pipes)
        self.pipes = [pipe.shift() for pipe in self.pipes if pipe.x > 1]
        num_pipes_after = len(self.pipes)
        self.pipes_passed += num_pipes_before - num_pipes_after

    def draw_pipe_if_needed(self):
        if not self.pipes:
            self.pipes.append(self.create_pipe())
            self.draw_pipe(self.pipes[-1])
        else:
            last_pipe = self.pipes[-1]
            dist_from_last_pipe = self.MAP_WIDTH - last_pipe.x
            print(dist_from_last_pipe)
            if dist_from_last_pipe > (20 - (self.x // 100)):
                self.pipes.append(self.create_pipe())
                self.draw_pipe(self.pipes[-1])
        return

        # print(f"space: {(20 - (self.x//25))}")
        # if self.x % (20 - (self.x//100)) == 0:
        #     position = self.random.choice(range(10, self.MAP_HEIGHT-10))
        #     for i in range(self.MAP_HEIGHT):
        #         if abs(position-i) > 3:
        #             self.map[(self.MAP_WIDTH - 1, i)] = self.PIPE
        #             self.map[(self.MAP_WIDTH - 2, i)] = self.PIPE

    def draw_pipe(self, pipe):
        print(f'Drawing {pipe}')
        for i in range(self.MAP_HEIGHT):
            if abs(pipe.y - i) > 3:
                if pipe.y - i == 4:
                    self.map[(pipe.x, i)] = self.PIPE_TOP_LEFT
                    self.map[(pipe.x + 1, i)] = self.PIPE_TOP_RIGHT
                elif pipe.y - i == -4:
                    self.map[(pipe.x, i)] = self.PIPE_BUTTOM_LEFT
                    self.map[(pipe.x + 1, i)] = self.PIPE_BUTTOM_RIGHT
                else:
                    self.map[(pipe.x, i)] = self.PIPE
                    self.map[(pipe.x + 1, i)] = self.PIPE

    def create_pipe(self):
        print('Creating pipe')
        y = self.random.choice(range(10, self.MAP_HEIGHT - 10))
        return Pipe(self.MAP_WIDTH - 2, y)

    def update_vars_for_player(self):
        bot_vars = {
            "y":
            self.player_y,
            "next_pipe_y":
            self.pipes[0].y if self.pipes else -1,
            "dist_to_next_pipe":
            self.pipes[0].x - self.PLAYER_X if self.pipes else 9999,
        }
        self.player.bot_vars = bot_vars

    def is_running(self):
        return self.running

    def draw_screen(self, frame_buffer):
        if not self.running:
            self.msg_panel.add("Game Over.")
            if self.pipes_passed == 0:
                self.msg_panel.add("Better luck")
                self.msg_panel.add("      next time :(")
            else:
                self.msg_panel.add("     Good job!")

        self.status_panel["Meters Flown"] = self.x
        self.status_panel["Pipes Passed"] = self.pipes_passed
        self.status_panel["Score"] = self.get_score()

        for panel in self.panels:
            panel.redraw(frame_buffer)

    def get_score(self):
        return self.x + (self.pipes_passed * 50)

    @staticmethod
    def get_intro():
        return open("resources/intro.md", "r").read()

    @staticmethod
    def default_prog_for_bot(language):
        if language == GameLanguage.LITTLEPY:
            return open("resources/flappy_bot.lp", "r").read()

    @staticmethod
    def get_move_consts():
        return ConstMapping({
            "flap": ord(" "),
            "up": ord("w"),
            "down": ord("s"),
            "glide": ord("d")
        })