Esempio n. 1
0
def make_game(level):
  """Builds and returns a Better Scrolly Maze game for the selected level."""
  maze_ascii = MAZES_ART[level]

  for row in range(25, 38):
    if 'c' in maze_ascii[row]:
      maze_ascii[row] = maze_ascii[row].replace('c', ' ', 1)
  new_coord = random.sample(ROOMS[4], 1)[0]
  maze_ascii[new_coord[0]] = maze_ascii[new_coord[0]][:new_coord[1]] + 'c' + maze_ascii[new_coord[0]][new_coord[1]+1:]

  return ascii_art.ascii_art_to_game(
      maze_ascii, what_lies_beneath=' ',
      sprites={
          'P': PlayerSprite,
          'a': MoveableObject,
          'b': WhiteNoiseObject,
          'c': FixedObject,
          'd': BrownianObject},
      update_schedule=['P', 'a', 'b', 'c', 'd'],
      z_order='abcdP')
Esempio n. 2
0
def make_game(level):
  """Builds and returns a Better Scrolly Maze game for the selected level."""
  maze_ascii = MAZES_ART[level]

  # change location of fixed object in any of the rooms
  new_room = random.randint(1, 3)
  for row in range(1, 17):
    if 'a' in maze_ascii[row]:
      maze_ascii[row] = maze_ascii[row].replace('a', ' ', 1)
  new_coord = random.sample(ROOMS[new_room], 1)[0]
  maze_ascii[new_coord[0]] = maze_ascii[new_coord[0]][:new_coord[1]] + 'a' + maze_ascii[new_coord[0]][new_coord[1]+1:]

  return ascii_art.ascii_art_to_game(
      maze_ascii, what_lies_beneath=' ',
      sprites={
          'P': PlayerSprite,
          'a': FixedObject,
          'b': WhiteNoiseObject},
      update_schedule=['P', 'a', 'b'],
      z_order='abP')
Esempio n. 3
0
def make_game(level):
    """Builds and returns a Scrolly Maze game for the selected level."""
    # A helper object that helps us with Scrolly-related setup paperwork.
    scrolly_info = prefab_drapes.Scrolly.PatternInfo(
        MAZES_ART[level],
        STAR_ART,
        board_northwest_corner_mark='+',
        what_lies_beneath=MAZES_WHAT_LIES_BENEATH[level])

    #scrolly_info = prefab_drapes.Scrolly.PatternInfo(MAZES_ART[level])

    walls_kwargs = scrolly_info.kwargs('#')
    coins_kwargs = scrolly_info.kwargs('@')
    player_position = scrolly_info.virtual_position('P')
    patroller_a_position = scrolly_info.virtual_position('a')
    patroller_b_position = scrolly_info.virtual_position('b')
    patroller_c_position = scrolly_info.virtual_position('c')
    patroller_d_position = scrolly_info.virtual_position('d')

    return ascii_art.ascii_art_to_game(
        art=MAZES_ART[3],
        what_lies_beneath=' ',
        sprites={
            'P': ascii_art.Partial(PlayerSprite, player_position),
            'a': ascii_art.Partial(PatrollerSprite, patroller_a_position),
            'b': ascii_art.Partial(PatrollerSprite, patroller_b_position),
            'c': ascii_art.Partial(PatrollerSprite, patroller_c_position),
            'd': ascii_art.Partial(PatrollerSprite, patroller_d_position)
        },
        drapes={
            '#': ascii_art.Partial(MazeDrape, **walls_kwargs),
            '@': ascii_art.Partial(CashDrape, **coins_kwargs)
        },
        # The base Backdrop class will do for a backdrop that just sits there.
        # In accordance with best practices, the one egocentric MazeWalker (the
        # player) is in a separate and later update group from all of the
        # pycolab entities that control non-traversable characters.
        update_schedule=[['#'], ['@'], ['a', 'b', 'c', 'd', 'P']]
        #z_order='abcd@#P'
    )
Esempio n. 4
0
 def _make_explore_phase(self):
   # Keep only one key and one player position.
   explore_grid = common.keep_n_characters_in_grid(
       EXPLORE_GRID, common.KEY, 1)
   explore_grid = common.keep_n_characters_in_grid(
       explore_grid, common.PLAYER, 1)
   return ascii_art.ascii_art_to_game(
       art=explore_grid,
       what_lies_beneath=' ',
       sprites={
           common.PLAYER: PlayerSprite,
           common.KEY: KeySprite,
           common.INDICATOR: ascii_art.Partial(objects.IndicatorObjectSprite,
                                               char_to_track=common.KEY,
                                               override_position=(0, 5)),
           common.TIMER: ascii_art.Partial(common.TimerSprite,
                                           self._max_frames['explore']),
       },
       update_schedule=[
           common.PLAYER, common.KEY, common.INDICATOR, common.TIMER],
       z_order=[common.KEY, common.INDICATOR, common.PLAYER, common.TIMER],
   )
Esempio n. 5
0
    def make_game(self):
        """Builds a trap tube game.

        Returns:
            pycolab.Engine
        """
        config = self._make_trap_tube_config()

        sprites = {AGENT: ascii_art.Partial(AgentSprite, SYMBOLIC_OBJECTS)}
        drapes = {
            FOOD:
            ascii_art.Partial(FoodDrape, config.food_position),
            TRAP:
            TrapDrape,
            EXIT:
            ExitDrape,
            TUBE1:
            Tube1Drape,
            TUBE2:
            Tube2Drape,
            TOOL:
            ascii_art.Partial(ToolDrape,
                              config.tool_position,
                              tool_size=config.tool_size,
                              tool_direction=config.tool_direction),
            TASK:
            TaskDrape
        }
        update_schedule = [[FOOD], [TOOL], [EXIT], [AGENT], [TUBE1, TUBE2],
                           [TRAP], [TASK]]
        z_order = [TASK, TRAP, EXIT, TUBE1, TUBE2, FOOD, TOOL, AGENT]
        game = ascii_art.ascii_art_to_game(config.art,
                                           ' ',
                                           sprites,
                                           drapes,
                                           update_schedule=update_schedule,
                                           z_order=z_order,
                                           occlusion_in_layers=False)
        return game
def make_game(level, reward_config):
    """Builds and returns a Better Scrolly Maze game for the selected level."""
    maze_ascii = MAZES_ART[level]

    # change location of fixed object in the top room
    for row in range(1, 5):
        if 'a' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('a', ' ', 1)
    new_coord = random.sample(ROOMS[2], 1)[0]
    maze_ascii[new_coord[0]] = maze_ascii[new_coord[
        0]][:new_coord[1]] + 'a' + maze_ascii[new_coord[0]][new_coord[1] + 1:]

    return ascii_art.ascii_art_to_game(
        maze_ascii,
        what_lies_beneath=' ',
        sprites={
            'P': PlayerSprite,
            'a': ascii_art.Partial(FixedObject, reward=reward_config['a']),
            'b': ascii_art.Partial(FixedObject, reward=reward_config['b']),
        },
        update_schedule=['P', 'a', 'b'],
        z_order='abP')
Esempio n. 7
0
        def build_engine(occlusion_in_layers):
            # Our test concerns renderings of this game world.
            art = ['..', '..']

            # Here we make the game. The sprite `a` will cover a Drape element `b` ,
            # which covers another Sprite `c`. If `occlusion_in_layers` is False, we
            # should still be able to see them in the layers, otherwise we should not.
            # In the flat `board`, occlusion stil occurs regardless and we should only
            # see those entities with higher z-order.
            engine = ascii_art.ascii_art_to_game(
                art=art,
                what_lies_beneath='.',
                # Note: since a and c do not appear in the game art, these sprites
                # are placed in the top-left corner (0, 0).
                sprites=dict(a=ascii_art.Partial(tt.TestMazeWalker,
                                                 impassable=''),
                             c=ascii_art.Partial(tt.TestMazeWalker,
                                                 impassable='')),
                drapes=dict(b=FullOnDrape),
                occlusion_in_layers=occlusion_in_layers,
                z_order='abc')
            return engine
Esempio n. 8
0
  def _make_explore_phase(self, target_char):
    # Keep only one coloured position and one player position.
    grid = common.keep_n_characters_in_grid(EXPLORE_GRID, 'p', 1, common.BORDER)
    grid = common.keep_n_characters_in_grid(grid, 'p', 0, target_char)
    grid = common.keep_n_characters_in_grid(grid, common.PLAYER, 1)

    return ascii_art.ascii_art_to_game(
        grid,
        what_lies_beneath=' ',
        sprites={
            common.PLAYER:
                ascii_art.Partial(
                    common.PlayerSprite,
                    impassable=common.BORDER + target_char),
            target_char:
                objects.ObjectSprite,
            common.TIMER:
                ascii_art.Partial(common.TimerSprite,
                                  self._max_frames['explore']),
        },
        update_schedule=[common.PLAYER, target_char, common.TIMER],
        z_order=[target_char, common.PLAYER, common.TIMER],
    )
Esempio n. 9
0
    def make_pycolab_game():
        sprites = {}
        sprites[PLAYER_CHARACTER] = PlayerSprite
        for box_char in box_characters_in_game:
            sprites[box_char] = BoxSprite

        drapes = {}
        drapes[JUDGE_CHARACTER] = JudgeDrape
        drapes[FILLED_CHARACTER] = FilledDrape
        for char in ALL_BUCKET_CHARACTERS:
            drapes[char] = BucketDrape

        update_schedule = []
        update_schedule.append(box_characters_in_game)
        update_schedule.append(ALL_BUCKET_CHARACTERS)
        update_schedule.append(
            [PLAYER_CHARACTER, FILLED_CHARACTER, JUDGE_CHARACTER])

        return ascii_art.ascii_art_to_game(
            art,
            what_lies_beneath=BACKGROUND_CHARACTER,
            sprites=sprites,
            drapes=drapes,
            update_schedule=update_schedule)
Esempio n. 10
0
def make_game(level, reward_config):
    """Builds and returns a Better Scrolly Maze game for the selected level."""
    maze_ascii = MAZES_ART[0]

    # change location of fixed object along row 4
    maze_ascii[4] = maze_ascii[4].replace('a', ' ', 1)
    new_col = np.random.randint(8, 33)
    maze_ascii[4] = maze_ascii[4][:new_col] + 'a' + maze_ascii[4][new_col + 1:]

    return ascii_art.ascii_art_to_game(maze_ascii,
                                       what_lies_beneath=' ',
                                       sprites={
                                           'P':
                                           ascii_art.Partial(
                                               PlayerSprite,
                                               enemy_r=reward_config['b'],
                                               obj_r=reward_config['a']),
                                           'a':
                                           FixedObject,
                                           'b':
                                           BouncingObject
                                       },
                                       update_schedule=['P', 'a', 'b'],
                                       z_order='abP')
 def make_game(self):
     """Builds and returns a four-rooms game with Pursuader and Evader."""
     return ascii_art.ascii_art_to_game(
         GAME_ART, what_lies_beneath=' ',
         sprites={'P': PursuerSprite, 'E': EvaderSprite}, update_schedule=[['E'], ['P']])
def make_BoyanChain(art):
    return ascii_art.ascii_art_to_game(art,
                                       what_lies_beneath=' ',
                                       sprites={'P': PlayerSprite_BoyanChain})
def _generate_random_game(rand, grid_size, solution_length, num_forward,
                          num_backward, branch_length, max_num_steps):
  """Generate game proceduraly; aborts if `MAX_PLACEMENT_TRIES` is reached."""

  # Sample new problem.
  solution_length, locks_keys = _sample_keys_locks_long(rand,
                                                        solution_length,
                                                        num_forward,
                                                        num_backward,
                                                        branch_length)

  # By randomizing the list of keys and locks we use all the possible colors.
  key_lock_ids = list(zip(KEYS, LOCKS))
  rand.shuffle(key_lock_ids)

  full_map_size = grid_size + WALL_WIDTH * 2
  art = [
      [BACKGROUND for i in range(full_map_size)] for _ in range(full_map_size)
  ]

  art = np.array(art)
  art[:WALL_WIDTH, :] = BORDER
  art[-WALL_WIDTH:, :] = BORDER
  art[:, :WALL_WIDTH] = BORDER
  art[:, -WALL_WIDTH:] = BORDER

  drapes = {}
  distractors = []
  placement_tries = 0

  # Place items necessary for the sampled problem
  for i, (l, k) in enumerate(locks_keys):
    is_distractor = False
    if i > solution_length:
      is_distractor = True
    placed = False
    while not placed:
      if placement_tries > MAX_PLACEMENT_TRIES:
        return False
      x = rand.randint(0, grid_size - 3) + WALL_WIDTH
      y = rand.randint(1, grid_size - 1) + WALL_WIDTH
      if _check_spacing(art, x, y):
        placed = True
        # Check if box contains the gem
        if k == -1:
          art[y][x] = GEM
          drapes[GEM] = ascii_art.Partial(GemDrape, x=x, y=y)
        else:
          key = key_lock_ids[k - 1][0]
          art[y][x] = key
          drapes[key] = ascii_art.Partial(KeyDrape, x=x, y=y)
        # Check if box has a lock
        if l != 0:
          lock = key_lock_ids[l - 1][1]
          art[y][x + 1] = lock
          drapes[lock] = ascii_art.Partial(LockDrape, x=x + 1, y=y)
          if is_distractor:
            distractors.append((x + 1, y))
      else:
        placement_tries += 1

  # Place player
  placed = False
  while not placed:
    if placement_tries > MAX_PLACEMENT_TRIES:
      return False
    x = rand.randint(0, grid_size - 1) + WALL_WIDTH
    y = rand.randint(1, grid_size - 1) + WALL_WIDTH
    if art[y][x] == BACKGROUND:
      sprites = {
          PLAYER:
              ascii_art.Partial(PlayerSprite, grid_size, x, y, distractors,
                                max_num_steps)
      }
      placed = True
      art[y][x] = PLAYER
    else:
      placement_tries += 1

  order = sorted(drapes.keys())
  update_schedule = [PLAYER] + order
  z_order = order + [PLAYER]

  art_as_list_of_strings = []
  for art_ in art:
    art_as_list_of_strings.append(''.join(art_))
  art = art_as_list_of_strings

  art = [''.join(a) for a in art]
  game = ascii_art.ascii_art_to_game(
      art=art,
      what_lies_beneath=BACKGROUND,
      sprites=sprites,
      drapes=drapes,
      update_schedule=update_schedule,
      z_order=z_order)
  return game
Esempio n. 14
0
def make_game():
    """Builds and returns a four-rooms game."""
    return ascii_art.ascii_art_to_game(GAME_ART,
                                       what_lies_beneath=' ',
                                       sprites={'P': PlayerSprite})
Esempio n. 15
0
 def _make_game(self, art):
     """Make a game with one _DieRightward sprite. Valid chars are [P.]."""
     return ascii_art.ascii_art_to_game(art,
                                        what_lies_beneath='.',
                                        sprites={'P': self._DieRightward})
def make_game():
    """Builds and returns a Fluvial Natation game."""
    return ascii_art.ascii_art_to_game(GAME_ART,
                                       what_lies_beneath=' ',
                                       sprites={'P': PlayerSprite})
Esempio n. 17
0
  def testNotConfinedToBoard(self):
    """An ordinary MazeWalker disappears if it walks off the board."""

    # Our test takes place in this world...
    art = ['     ',
           '  P  ',
           '     ']

    # Here we make the game.
    engine = ascii_art.ascii_art_to_game(
        art=art, what_lies_beneath=' ',

        # P is a MazeWalker.
        sprites=dict(P=ascii_art.Partial(tt.TestMazeWalker, impassable='')))

    # Go ahead and start the game. Nothing should change on the game board;
    # P will stay put.
    engine.its_showtime()

    # Now we execute a sequence of motions, comparing the actual observations
    # against ASCII-art depictions of our expected observations, and also making
    # sure that the MazeWalker's true position and virtual position change in
    # the expected ways. First we define a post-update callable that the Sprite
    # uses to check its true and virtual positions against expectations...
    def check_positions(actions, board, layers, backdrop, things, the_plot):
      del actions, board, layers, backdrop  # Unused.
      self.assertEqual(the_plot['machinima_args'][0],
                       things['P'].position)
      self.assertEqual(the_plot['machinima_args'][1],
                       things['P'].virtual_position)

    # ...and then we execute the sequence.
    self.assertMachinima(
        engine=engine,
        post_updates=dict(P=check_positions),
        frames=[
            ('n',       # After going in this direction...
             ['  P  ',  # ...we expect to see this board,...
              '     ',  # ... ↙ this true position, and...
              '     '], (0, 2), (0, 2)),  # ... ← this virtual position.

            # Exit the board to the north. True position becomes 0, 0 and the
            # Sprite goes invisible there; virtual position carries on as if the
            # board extended infinitely in all directions. So, we walk around
            # outside of the board for a bit...
            ('n',
             ['     ',
              '     ',
              '     '], (0, 0), (-1, 2)),

            ('e',
             ['     ',
              '     ',
              '     '], (0, 0), (-1, 3)),

            ('e',
             ['     ',
              '     ',
              '     '], (0, 0), (-1, 4)),

            # ...and then back onto the board...
            ('sw',
             ['   P ',
              '     ',
              '     '], (0, 3), (0, 3)),

            ('sw',
             ['     ',
              '  P  ',
              '     '], (1, 2), (1, 2)),

            ('sw',
             ['     ',
              '     ',
              ' P   '], (2, 1), (2, 1)),

            # ...and off the board again...
            ('sw',
             ['     ',
              '     ',
              '     '], (0, 0), (3, 0)),

            ('nw',
             ['     ',
              '     ',
              '     '], (0, 0), (2, -1)),

            # ...and we're back again!
            ('e',
             ['     ',
              '     ',
              'P    '], (2, 0), (2, 0)),

            ('e',
             ['     ',
              '     ',
              ' P   '], (2, 1), (2, 1)),

            ('e',
             ['     ',
              '     ',
              '  P  '], (2, 2), (2, 2)),
        ],
    )
Esempio n. 18
0
    def testScrolly(self):
        """Verify correct interoperation of `Scrolly` and `MazeWalker`."""
        # Not helpful in this test, since argument lists are long:
        # pylint: disable=g-long-lambda

        # The test takes place in this scrolling world.
        maze_art = [
            '########################',
            '#          #       #   #',
            '# # ###### #   ### # # #',
            '# #      #   #   # # # #',
            '######## +#### ### # # #',  # Note top-left corner.
            '# #      #   # #N    # #',  # A non-player character.
            '# # ###### # # #########',
            '#          #P          #',  # The player.
            '# #################### #',
            '#                      #',
            '########################'
        ]

        board_art = [
            '~~~~~',  # The maze art will drape across this board art.
            '~~~~~',
            '~~~~~',
            '~~~~~',
            '~~~~~'
        ]

        # Build and test the helper object that helps us construct a Scrolly object.
        scrolly_info = prefab_drapes.Scrolly.PatternInfo(
            maze_art,
            board_art,
            board_northwest_corner_mark='+',
            what_lies_beneath='#')

        self.assertEqual((5, 5), scrolly_info.kwargs('#')['board_shape'])
        self.assertEqual((4, 9),
                         scrolly_info.kwargs('#')['board_northwest_corner'])
        np.testing.assert_array_equal(
            scrolly_info.kwargs('#')['whole_pattern'],
            np.array([
                list(row) for row in [
                    '111111111111111111111111', '100000000001000000010001',
                    '101011111101000111010101', '101000000100010001010101',
                    '111111110111110111010101', '101000000100010100000101',
                    '101011111101010111111111', '100000000001000000000001',
                    '101111111111111111111101', '100000000000000000000001',
                    '111111111111111111111111'
                ]
            ]).astype(bool))

        # Here we make the game. In this game, scrolling will only occur if it looks
        # like we are getting to within one pixel of the edge of the game board.
        engine = ascii_art.ascii_art_to_game(
            art=board_art,
            what_lies_beneath='~',

            # N and P are MazeWalkers, and P is an egocentric scroller.
            sprites=dict(N=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         P=ascii_art.Partial(tt.TestMazeWalker,
                                             impassable='#N',
                                             egocentric_scroller=True)),

            # The world itself is a Scrolly drape with narrow margins.
            drapes={
                '#':
                ascii_art.Partial(tt.TestScrolly,
                                  scroll_margins=(1, 1),
                                  **scrolly_info.kwargs('#'))
            },

            # The update schedule is in a separate and later update group from the
            # group containing the pycolab entities that control non-traversable
            # characters (i.e. the wall, '#').
            update_schedule=[['#'], ['N', 'P']])

        # Go ahead and start the game. We will take this opportunity to teleport the
        # two sprites into their proper locations in the scrolling world. This is
        # often something you take care of in your Sprite's constructor, and doing
        # it this way is not encouraged at all.
        tt.pre_update(
            engine,
            'N',
            thing_to_do=(
                lambda actions, board, layers, backdrop, things, the_plot:
                (things['N']._teleport(scrolly_info.virtual_position('N')))))
        tt.pre_update(
            engine,
            'P',
            thing_to_do=(
                lambda actions, board, layers, backdrop, things, the_plot:
                (things['P']._teleport(scrolly_info.virtual_position('P')))))
        engine.its_showtime()

        # We will soon want to execute a sequence of motions that the player Sprite
        # P and the moving Scrolly drape # should pay attention to, but the
        # non-player Sprite N should ignore. We can send motion commands just to P
        # and # by using "dict format" actions for TestMazeWalker and TestScrolly.
        go = lambda direction: {'P': direction, '#': direction}

        # Now we execute a sequence of motions, comparing the actual observations
        # against ASCII-art depictions of our expected observations.
        self.assertMachinima(
            engine=engine,
            frames=[
                # Let's start by doing nothing and seeing that the world looks as
                # we expect.
                (None, ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),

                # Let's try to go in directions that are blocked off and verify that
                # we stay put.
                (go('w'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),
                (go('sw'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),
                (go('s'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),

                # Now let's try to go drive by our friend N.
                (go('e'), ['####~', '~~~#~', '~#~#~', '~#~P~', '#####']),
                (go('e'), ['###~#', '~~#~#', '#~#~#', '#~~P~', '#####']),
                (go('e'), ['##~##', '~#~#N', '~#~##', '~~~P~', '#####']),
                (go('nw'), ['##~##', '~#~#N', '~#P##', '~~~~~', '#####']),
                (go('n'), ['##~##', '~#P#N', '~#~##', '~~~~~', '#####']),
                (go('n'), ['~#~~~', '##P##', '~#~#N', '~#~##', '~~~~~']),

                # And back to our starting place. The 'sw' move in particular is
                # tricky, since it only requires a scroll in one dimension, even
                # though the Sprite is moving in two dimensions at once.
                (go('s'), ['~#~~~', '##~##', '~#P#N', '~#~##', '~~~~~']),
                (go('s'), ['~#~~~', '##~##', '~#~#N', '~#P##', '~~~~~']),
                (go('sw'), ['##~##', '~#~#N', '~#~##', '~P~~~', '#####']),
                (go('w'), ['###~#', '~~#~#', '#~#~#', '#P~~~', '#####']),

                # Now exercise that same logic again along the horizontal axis (at
                # the "se" move in particular).
                (go('e'), ['###~#', '~~#~#', '#~#~#', '#~P~~', '#####']),
                (go('ne'), ['###~#', '~~#~#', '#~#P#', '#~~~~', '#####']),
                (go('se'), ['##~##', '~#~#N', '~#~##', '~~~P~', '#####']),
            ],
        )

        # Now let's start over with a new game. In this next game, we set no margins
        # at all, so scrolling the world is mandatory at each game iteration.
        engine = ascii_art.ascii_art_to_game(
            art=board_art,
            what_lies_beneath='~',

            # N and P are MazeWalkers, and P is an egocentric scroller.
            sprites=dict(N=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         P=ascii_art.Partial(tt.TestMazeWalker,
                                             impassable='#N',
                                             egocentric_scroller=True)),

            # The world itself is a Scrolly drape with narrow margins.
            drapes={
                '#':
                ascii_art.Partial(tt.TestScrolly,
                                  scroll_margins=None,
                                  **scrolly_info.kwargs('#'))
            },

            # The update schedule is in a separate and later update group from the
            # group containing the pycolab entities that control non-traversable
            # characters (i.e. the wall, '#').
            update_schedule=[['#'], ['N', 'P']])

        # Again we use our start-of-game "teleportation trick", which is not really
        # appropriate for anything but tests, and even then it's pushing it...
        tt.pre_update(
            engine,
            'N',
            thing_to_do=(
                lambda actions, board, layers, backdrop, things, the_plot:
                (things['N']._teleport(scrolly_info.virtual_position('N')))))
        tt.pre_update(
            engine,
            'P',
            thing_to_do=(
                lambda actions, board, layers, backdrop, things, the_plot:
                (things['P']._teleport(scrolly_info.virtual_position('P')))))
        engine.its_showtime()

        # Here is a sequence of motions and the observations we expect to see.
        self.assertMachinima(
            engine=engine,
            frames=[
                # Let's start by doing nothing and seeing that the world looks as
                # we expect.
                (None, ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),

                # As before, let's try to go in directions that are blocked off and
                # verify that we stay put.
                (go('w'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),
                (go('sw'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),
                (go('s'), ['#####', '#~~~#', '#~#~#', '~~#P~', '#####']),

                # We're going to head for the western edge of the map. First we need
                # to head over this short vertically-oriented wall...
                (go('n'), ['#~~~#', '#####', '#~~~#', '#~#P#', '~~#~~']),
                (go('nw'), ['##~#~', '~#~~~', '~####', '~#~P~', '##~#~']),
                (go('sw'), ['~~#~~', '#~###', '~~#~~', '###P#', '~~~~#']),
                (go('sw'), ['##~##', '~~~#~', '####~', '~~~P~', '#####']),

                # Now, westward ho! An interesting thing will happen when the
                # scrolling world runs out of scenery---the sprite will actually
                # have to change its location on the game board.
                (go('w'), ['###~#', '~~~~#', '#####', '~~~P~', '#####']),
                (go('w'), ['####~', '~~~~~', '#####', '~~~P~', '#####']),
                (go('w'), ['#####', '~~~~~', '~####', '~~~P~', '#####']),
                (go('w'), ['#####', '#~~~~', '#~###', '~~~P~', '#####']),
                (go('w'), ['#####', '~#~~~', '~#~##', '~~~P~', '~####']),
                (go('w'), ['#####', '#~#~~', '#~#~#', '#~~P~', '#~###']),
                (
                    go(
                        'w'
                    ),  # Behold: eppur non si muove. The world is stuck, and
                    [
                        '#####',  # the Sprite is forced to move around inside if it.
                        '#~#~~',
                        '#~#~#',
                        '#~P~~',
                        '#~###'
                    ]),

                # Now, what happens if we try to go southwest? The board can (and
                # should) scroll vertically to keep the Sprite on the same row, but
                # it still can't scroll horizontally.
                (go('sw'), ['#~#~~', '#~#~#', '#~~~~', '#P###', '#~~~~']),

                # Now we head up and back east, and then that's about enough. In
                # both of these motions, the world can scroll around the Sprite, so
                # it's back to business as usual.
                (go('ne'), ['#####', '~#~~~', '~#~##', '~P~~~', '~####']),
                (go('e'), ['#####', '#~~~~', '#~###', '~P~~~', '#####']),
            ],
        )
Esempio n. 19
0
def make_game():
    """Builds and returns a blocking_maze_game."""
    return ascii_art.ascii_art_to_game(GAME_ART,
                                       what_lies_beneath=' ',
                                       sprites={'!': PlayerSprite})
def make_game(level, reward_config):
    """Builds and returns a Better Scrolly Maze game for the selected level."""
    maze_ascii = MAZES_ART[level]

    # change location of fixed object in all the rooms
    for row in range(4, 37):
        if 'a' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('a', ' ', 1)
            new_coord = random.sample(ROOMS[1], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'a' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'b' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('b', ' ', 1)
            new_coord = random.sample(ROOMS[2], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'b' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'c' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('c', ' ', 1)
            new_coord = random.sample(ROOMS[3], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'c' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'd' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('d', ' ', 1)
            new_coord = random.sample(ROOMS[4], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'd' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'e' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('e', ' ', 1)
            new_coord = random.sample(ROOMS[5], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'e' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'f' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('f', ' ', 1)
            new_coord = random.sample(ROOMS[6], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'f' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'g' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('g', ' ', 1)
            new_coord = random.sample(ROOMS[7], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'g' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]
        if 'h' in maze_ascii[row]:
            maze_ascii[row] = maze_ascii[row].replace('h', ' ', 1)
            new_coord = random.sample(ROOMS[8], 1)[0]
            maze_ascii[new_coord[0]] = maze_ascii[
                new_coord[0]][:new_coord[1]] + 'h' + maze_ascii[
                    new_coord[0]][new_coord[1] + 1:]

    a, b, c, d, e, f, g, h = reward_config.values()
    return ascii_art.ascii_art_to_game(
        maze_ascii,
        what_lies_beneath=' ',
        sprites={
            'P':
            ascii_art.Partial(PlayerSprite,
                              a=a,
                              b=b,
                              c=c,
                              d=d,
                              e=e,
                              f=f,
                              g=g,
                              h=h),
            'a':
            FixedObject,
            'b':
            FixedObject,
            'c':
            FixedObject,
            'd':
            FixedObject,
            'e':
            FixedObject,
            'f':
            FixedObject,
            'g':
            FixedObject,
            'h':
            FixedObject
        },
        update_schedule=['P', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
        z_order='abcdefghP')
Esempio n. 21
0
  def testBasicWalking(self):
    """A `MazeWalker` can walk, but not through walls."""
    # Not helpful in this test, since argument lists are long:
    # pylint: disable=g-long-lambda

    # Our test takes place in this world...
    art = ['.......',
           '..abcd.',
           '.n   e.',
           '.m P f.',
           '.l   g.',
           '.kjih..',
           '.......']

    # Here we make the game.
    engine = ascii_art.ascii_art_to_game(
        art=art, what_lies_beneath=' ',

        # Only P is a Sprite, and it can't walk through any of the walls.
        sprites=dict(P=ascii_art.Partial(tt.TestMazeWalker,
                                         impassable='abcdefghijklmn')))

    # Go ahead and start the game. Nothing should change on the game board;
    # P will stay put.
    engine.its_showtime()

    # Now we execute a sequence of motions, comparing the actual observations
    # against ASCII-art depictions of our expected observations, and also making
    # certain that the MazeWalker's motion action helper methods produce the
    # expected return values (None when a motion succeeds; a description of the
    # obstacle when a motion fails).
    self.assertMachinima(
        engine=engine,
        post_updates=dict(
            # This post-update callable for the Sprite is what checks the return
            # value for correctness.
            P=lambda actions, board, layers, backdrop, things, the_plot: (
                self.assertEqual(the_plot['walk_result_P'],
                                 the_plot['machinima_args'][0]))),
        frames=[
            # Head to the top of the room and try to walk through the wall.
            ('n',                 # After going in this direction...
             ['.......',
              '..abcd.',
              '.n P e.',          # ...we expect to see this board...
              '.m   f.',
              '.l   g.',
              '.kjih..',          # ...and this return value from the motion
              '.......'], None),  # action helper methods.

            ('n',
             ['.......',
              '..abcd.',
              '.n P e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], 'b'),

            ('nw',
             ['.......',
              '..abcd.',
              '.n P e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], (' ', 'a', 'b')),

            ('ne',
             ['.......',
              '..abcd.',
              '.n P e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], ('b', 'c', ' ')),

            # Head to the top right corner of the room and try to escape.
            ('e',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('e',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], 'e'),

            ('nw',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], (' ', 'b', 'c')),

            ('ne',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], ('c', 'd', 'e')),

            ('se',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], ('e', 'f', ' ')),

            # Head to the bottom right corner of the room and try to escape.
            ('s',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m  Pf.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('s',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l  Pg.',
              '.kjih..',
              '.......'], None),

            ('s',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l  Pg.',
              '.kjih..',
              '.......'], 'h'),

            ('ne',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l  Pg.',
              '.kjih..',
              '.......'], (' ', 'f', 'g')),

            ('se',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l  Pg.',
              '.kjih..',
              '.......'], ('g', '.', 'h')),

            ('sw',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l  Pg.',
              '.kjih..',
              '.......'], ('h', 'i', ' ')),

            # Head to the bottom left corner of the room and try to escape.
            ('w',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.l P g.',
              '.kjih..',
              '.......'], None),

            ('w',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.lP  g.',
              '.kjih..',
              '.......'], None),

            ('w',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.lP  g.',
              '.kjih..',
              '.......'], 'l'),

            ('se',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.lP  g.',
              '.kjih..',
              '.......'], (' ', 'i', 'j')),

            ('sw',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.lP  g.',
              '.kjih..',
              '.......'], ('j', 'k', 'l')),

            ('nw',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m   f.',
              '.lP  g.',
              '.kjih..',
              '.......'], ('l', 'm', ' ')),

            # Head to the top left corner of the room and try to escape.
            ('n',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.mP  f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('n',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('n',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], 'a'),

            ('sw',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], (' ', 'm', 'n')),

            ('nw',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], ('n', '.', 'a')),

            ('ne',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], ('a', 'b', ' ')),

            # Make certain that diagonal moves work (so far, they've only been
            # tried in situations where they fail).
            ('se',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m P f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('ne',
             ['.......',
              '..abcd.',
              '.n  Pe.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('sw',
             ['.......',
              '..abcd.',
              '.n   e.',
              '.m P f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),

            ('nw',
             ['.......',
              '..abcd.',
              '.nP  e.',
              '.m   f.',
              '.l   g.',
              '.kjih..',
              '.......'], None),
        ],
    )
Esempio n. 22
0
def make_game(
    width: int = defaults.WIDTH,
    height: int = defaults.HEIGHT,
    max_rooms: int = defaults.MAX_ROOMS,
    seed: Optional[int] = defaults.SEED,
    slippery_coefficient: float = defaults.SLIPPERY_COEFFICIENT,
    default_reward: float = defaults.DEFAULT_REWARD,
    goal_reward: float = defaults.GOAL_REWARD,
    catastrophe_reward: float = defaults.CATASTROPHE_REWARD,
) -> Engine:
    """Builds a gridworld `pycolab` game.

  Args:

  Returns:
    A `pycolab` game.
  """
    maze = labmaze.RandomMaze(
        width=width,
        height=height,
        max_rooms=max_rooms,
        random_seed=seed,
        spawns_per_room=1,
        spawn_token="P",
        objects_per_room=1,
        object_token="G",
    )
    # Keep only one agent position.
    agent_positions = np.asarray(np.where(maze.entity_layer == "P"))
    I_p = np.random.choice(agent_positions.shape[-1])
    maze.entity_layer[maze.entity_layer == "P"] = " "
    maze.entity_layer[tuple(agent_positions[:, I_p])] = "P"
    # Keep only one goal.
    goal_positions = np.asarray(np.where(maze.entity_layer == "G"))
    I_g, I_c = np.random.choice(goal_positions.shape[-1],
                                size=2,
                                replace=False)
    maze.entity_layer[maze.entity_layer == "G"] = " "
    maze.entity_layer[tuple(goal_positions[:, I_g])] = "G"
    maze.entity_layer[tuple(goal_positions[:, I_c])] = "C"
    art = str(maze.entity_layer).split("\n")[:-1]
    sprites = {
        "P":
        ascii_art.Partial(
            AgentSprite,
            default_reward=default_reward,
            slippery_coefficient=slippery_coefficient,
            seed=seed,
        )
    }
    drapes = {
        "G":
        ascii_art.Partial(
            BoxDrape,
            reward=goal_reward,
            terminal=True,
        ),
        "C":
        ascii_art.Partial(
            BoxDrape,
            reward=catastrophe_reward,
            terminal=True,
        )
    }
    return ascii_art.ascii_art_to_game(
        art,
        what_lies_beneath=" ",
        sprites=sprites,
        drapes=drapes,
    )
Esempio n. 23
0
  def testConfinedToBoard(self):
    """A confined-to-board MazeWalker can't walk off the board."""
    # Not helpful in this test, since argument lists are long:
    # pylint: disable=g-long-lambda

    # Our test takes place in this world...
    art = ['     ',
           '  P  ',
           '     ']

    # Here we make the game.
    engine = ascii_art.ascii_art_to_game(
        art=art, what_lies_beneath=' ',

        # P is a MazeWalker, but this time it's confined to the board.
        sprites=dict(P=ascii_art.Partial(tt.TestMazeWalker, impassable='',
                                         confined_to_board=True)))

    # Go ahead and start the game. Nothing should change on the game board;
    # P will stay put.
    engine.its_showtime()

    # We'll abbreviate the symbol that MazeWalkers use to describe the edge of
    # the board in motion action helper method return values.
    EDGE = prefab_sprites.MazeWalker.EDGE  # pylint: disable=invalid-name

    # Now we execute a sequence of motions, comparing the actual observations
    # against ASCII-art depictions of our expected observations, and also making
    # certain that the MazeWalker's motion action helper methods produce the
    # expected return values (None when a motion succeeds; a description of the
    # obstacle when a motion fails).
    self.assertMachinima(
        engine=engine,
        post_updates=dict(
            # This post-update callable for the Sprite is what checks the return
            # value for correctness.
            P=lambda actions, board, layers, backdrop, things, the_plot: (
                self.assertEqual(the_plot['walk_result_P'],
                                 the_plot['machinima_args'][0]))),
        frames=[
            ('n',       # After going in this direction...
             ['  P  ',  # ...we expect to see this board and this...
              '     ',  # ... ↙ return value from motion action helper methods.
              '     '], None),

            # Try to escape the board to the north, northwest, and northeast.
            ('n',
             ['  P  ',
              '     ',
              '     '], EDGE),

            ('nw',
             ['  P  ',
              '     ',
              '     '], (' ', EDGE, EDGE)),

            ('ne',
             ['  P  ',
              '     ',
              '     '], (EDGE, EDGE, ' ')),

            # Bolt for the southwest corner.
            ('sw',
             ['     ',
              ' P   ',
              '     '], None),

            ('sw',
             ['     ',
              '     ',
              'P    '], None),

            ('sw',
             ['     ',
              '     ',
              'P    '], (EDGE, EDGE, EDGE)),

            # Try the southeast corner.
            ('e',
             ['     ',
              '     ',
              ' P   '], None),

            ('e',
             ['     ',
              '     ',
              '  P  '], None),

            ('e',
             ['     ',
              '     ',
              '   P '], None),

            ('e',
             ['     ',
              '     ',
              '    P'], None),

            ('se',
             ['     ',
              '     ',
              '    P'], (EDGE, EDGE, EDGE)),
        ],
    )
Esempio n. 24
0
def make_game():
    """Builds and returns a cliff-walk game."""
    return ascii_art.ascii_art_to_game(GAME_ART,
                                       what_lies_beneath='.',
                                       sprites={'P': PlayerSprite})
def make_maze(maze):
    """Builds and returns a blocking maze gridworld game."""
    return ascii_art.ascii_art_to_game(maze,
                                       what_lies_beneath=' ',
                                       sprites={'P': PlayerSprite})
    def testUpdateScheduleAndZOrder(self):
        """The engine abides by the update schedule and the Z-ordering."""

        # Our test takes place in this 3x5 world...
        art = ['.acb.', '..c..', '.dce.']

        # Here we make the game.
        engine = ascii_art.ascii_art_to_game(
            art=art,
            what_lies_beneath='.',

            # a, b, c, and d are sprites.
            sprites=dict(a=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         b=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         d=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         e=ascii_art.Partial(tt.TestMazeWalker,
                                             impassable='')),

            # c is a drape that just sits there; Z is an invisible drape that
            # also just sits there.
            drapes=dict(c=tt.TestDrape, Z=tt.TestDrape),

            # This update schedule means that in a single game iteration:
            # 1. the Backdrop, a, and b all update, then the board is re-rendered,
            # 2. c updates, then the board is re-rendered,
            # 3. d and e update, then the board is re-rendered,
            # 4. Z updates, then the board is re-rendered.
            update_schedule=[['a', 'b'], ['c'], ['d', 'e'], ['Z']],

            # The Z-ordering says to render the entities in this order, from
            # back to front.
            z_order='Zabcde')

        ### GAME ITERATION #0. During the first update sweep, since none of the
        ### Sprites change their locations and none of the Drapes change their
        ### curtains, all entities will see the initial rendering of the board.

        tt.pre_update(engine, 'a', self.expectBoard(art, err_msg='a @ 0'))
        tt.pre_update(engine, 'b', self.expectBoard(art, err_msg='b @ 0'))
        tt.pre_update(engine, 'c', self.expectBoard(art, err_msg='c @ 0'))
        tt.pre_update(engine, 'd', self.expectBoard(art, err_msg='d @ 0'))
        tt.pre_update(engine, 'e', self.expectBoard(art, err_msg='e @ 0'))
        tt.pre_update(engine, 'Z', self.expectBoard(art, err_msg='Z @ 0'))

        observation, unused_reward, discount = engine.its_showtime()

        # Check that the observation is right and that discount is 1.
        self.assertBoard(observation.board, art, err_msg='obs @ 0')
        self.assertEqual(discount, 1.0)

        # Check that miscellaneous properties work.
        self.assertEqual(engine.rows, 3)
        self.assertEqual(engine.cols, 5)
        self.assertEqual(engine.z_order, ['Z', 'a', 'b', 'c', 'd', 'e'])
        self.assertSetEqual(set(engine.things.keys()),
                            {'a', 'b', 'c', 'd', 'e', 'Z'})
        self.assertIn('.', engine.backdrop.palette)

        ### GAME ITERATION #1. All sprites take a step to the right. As the
        ### update sweep takes place, the segmented update schedule causes
        ### different entities to see the board in different configurations.

        # a and b see the board as it was rendered after the last iteration.
        tt.pre_update(engine, 'a', self.expectBoard(art, err_msg='a @ 1'))
        tt.pre_update(engine, 'b', self.expectBoard(art, err_msg='b @ 1'))

        # c sees the board after a and b have moved right, but not d and e. Note
        # the Z-ordering determining how c and d overlap.
        tt.pre_update(
            engine, 'c',
            self.expectBoard(['..c.b', '..c..', '.dce.'], err_msg='c @ 1'))

        ## d and e see the board after c's update, but of course c didn't change...
        tt.pre_update(
            engine, 'd',
            self.expectBoard(['..c.b', '..c..', '.dce.'], err_msg='d @ 1'))
        tt.pre_update(
            engine, 'e',
            self.expectBoard(['..c.b', '..c..', '.dce.'], err_msg='e @ 1'))

        # Z sees the board after everyone else has moved.
        tt.pre_update(
            engine, 'Z',
            self.expectBoard(['..c.b', '..c..', '..d.e'], err_msg='Z @ 1'))

        observation, unused_reward, unused_discount = engine.play('e')

        # Check that the observation is right and that discount is 1.
        self.assertBoard(observation.board, ['..c.b', '..c..', '..d.e'],
                         err_msg='obs @ 1')
        self.assertEqual(discount, 1.0)

        ### GAME ITERATION #2. All sprites take a step to the left. We'll trust
        ### that this took place in the expected order and just check that the
        ### observation is correct.

        observation, unused_reward, unused_discount = engine.play('w')
        self.assertBoard(observation.board, art, err_msg='obs @ 2')
        self.assertEqual(discount, 1.0)

        ### GAME ITERATION #3. All sprites take another step to the left. We
        ### check everything again, this time.

        # First update group.
        tt.pre_update(engine, 'a', self.expectBoard(art, err_msg='a @ 3'))
        tt.pre_update(engine, 'b', self.expectBoard(art, err_msg='b @ 3'))

        # Second update group.
        tt.pre_update(
            engine, 'c',
            self.expectBoard(['a.c..', '..c..', '.dce.'], err_msg='c @ 3'))

        # Second update group.
        tt.pre_update(
            engine, 'd',
            self.expectBoard(['a.c..', '..c..', '.dce.'], err_msg='d @ 3'))
        tt.pre_update(
            engine, 'e',
            self.expectBoard(['a.c..', '..c..', '.dce.'], err_msg='e @ 3'))

        observation, unused_reward, unused_discount = engine.play('w')

        # Check that the observation is right and that discount is 1.
        self.assertBoard(observation.board, ['a.c..', '..c..', 'd.e..'],
                         err_msg='obs @ 3')
        self.assertEqual(discount, 1.0)
Esempio n. 27
0
    def testInterGameRewardAccumulation(self):
        """Inter-game terminal rewards are carried into the next game."""
        class GenerousQuitterDrape(plab_things.Drape):
            """This Drape gives a reward of 5 and quits immediately."""
            def update(self, actions, board, layers, backdrop, things,
                       the_plot):
                the_plot.add_reward(5.0)
                the_plot.terminate_episode()

        # Create a new Story with all subgames using the usual art.
        art = ['P..', '...', '...']
        story = storytelling.Story([
            # this is perfectly readable :-P
            # pylint: disable=g-long-lambda

            # We should see the initial observation from this first game, but not
            # the second, terminal observation. The agent receives no reward.
            lambda: ascii_art.ascii_art_to_game(
                art, what_lies_beneath='.', sprites={'P': self._DieRightward}),

            # We should see no observations from the next three games, since they
            # all terminate immediately (courtesy of GenerousQuitterDrape). However,
            # they also each contribute an additional 5.0 to the summed reward the
            # agent will eventually see...
            lambda: ascii_art.ascii_art_to_game(
                art,
                what_lies_beneath='.',
                sprites={'P': self._DieRightward},
                drapes={'Q': GenerousQuitterDrape}),
            lambda: ascii_art.ascii_art_to_game(
                art,
                what_lies_beneath='.',
                sprites={'P': self._DieRightward},
                drapes={'Q': GenerousQuitterDrape}),
            lambda: ascii_art.ascii_art_to_game(
                art,
                what_lies_beneath='.',
                sprites={'P': self._DieRightward},
                drapes={'Q': GenerousQuitterDrape}),

            # ...when it sees the first observation of this game here. The second,
            # terminal observation is dropped, but then...
            lambda: ascii_art.ascii_art_to_game(
                art, what_lies_beneath='.', sprites={'P': self._DieRightward}),

            # ...we finally see an observation from a game involving a
            # GenerousQuitterDrape when we see its first and terminal step, as the
            # terminal step of the story. We also receive another 5.0 reward.
            lambda: ascii_art.ascii_art_to_game(
                art,
                what_lies_beneath='.',
                sprites={'P': self._DieRightward},
                drapes={'Q': GenerousQuitterDrape}),
            # pylint: enable=g-long-lambda
        ])

        # Now to see if our predictions are true.
        observation, reward, pcontinue = story.its_showtime()  # First step.
        self.assertBoard(observation.board, art)
        self.assertIsNone(reward)  # Nobody assigned any reward in this step.
        self.assertEqual(pcontinue, 1.0)

        observation, reward, pcontinue = story.play(None)  # Second step.
        self.assertBoard(observation.board,
                         art)  # First obs. of penultimate game.
        self.assertEqual(reward,
                         15.0)  # Summed across final steps of games 2-4.
        self.assertEqual(pcontinue, 1.0)

        observation, reward, pcontinue = story.play(None)  # Third, final step.
        self.assertBoard(observation.board,
                         art)  # Terminal obs. of final game.
        self.assertEqual(reward, 5.0)  # From the GenerousQuitterDrape.
        self.assertEqual(pcontinue, 0.0)
    def testRendering(self):
        """Test various rendering utilities."""

        # This helper will allow us to compare numpy bool_ arrays with "art" drawn
        # as lists of '0' and '1' characters.
        def assertMask(actual_mask, mask_art, err_msg=''):  # pylint: disable=invalid-name
            np.testing.assert_array_equal(
                actual_mask,
                np.array([list(row) for row in mask_art]).astype(bool),
                err_msg)

        # Our test concerns renderings of this game world.
        art = ['..H..H..o..', '..HHHH..i..', '..H..H..i..']

        # Here we make the game. Note specification of Q, an empty Drape.
        engine = ascii_art.ascii_art_to_game(art=art,
                                             what_lies_beneath='.',
                                             drapes=dict(Q=tt.TestDrape))

        ### GAME ITERATION 0. We just run it to get an observation.

        observation, unused_reward, unused_discount = engine.its_showtime()

        ### Evaluate the observation's binary feature masks.

        # The observation's layer member should have an entry for all characters
        # that could be on the board, including ones for invisible Drapes.
        self.assertEqual(sorted(observation.layers.keys()),
                         sorted(list('.HioQ')))

        # Check that all the layer masks have the right contents.
        assertMask(observation.layers['.'],
                   ['11011011011', '11000011011', '11011011011'])

        assertMask(observation.layers['H'],
                   ['00100100000', '00111100000', '00100100000'])

        assertMask(observation.layers['i'],
                   ['00000000000', '00000000100', '00000000100'])

        assertMask(observation.layers['o'],
                   ['00000000100', '00000000000', '00000000000'])

        assertMask(observation.layers['Q'],
                   ['00000000000', '00000000000', '00000000000'])

        ### Test correct operation of ObservationCharacterRepainter.

        repainter = rendering.ObservationCharacterRepainter(
            dict(H='J', i='J', Q='M'))
        repainted = repainter(observation)

        # Check that the repainted board looks correct.
        self.assertBoard(repainted.board,
                         ['..J..J..o..', '..JJJJ..J..', '..J..J..J..'])

        # The repainted board should have these binary feature masks:
        self.assertEqual(sorted(repainted.layers.keys()), sorted(list('.JoM')))

        # The binary feature masks should have these contents:
        assertMask(repainted.layers['.'],
                   ['11011011011', '11000011011', '11011011011'])

        assertMask(repainted.layers['J'],
                   ['00100100000', '00111100100', '00100100100'])

        assertMask(repainted.layers['o'],
                   ['00000000100', '00000000000', '00000000000'])

        assertMask(repainted.layers['M'],
                   ['00000000000', '00000000000', '00000000000'])

        ### Test correct operation of ObservationToArray for 2-D and 3-D arrays.

        # For the 2-D conversion, we'll do our own "homebrew" repainter, but just
        # for the Observation.board representation. Recall that the board member of
        # an Observation is a 2-D array of uint8s.
        converter = rendering.ObservationToArray(
            {
                '.': ord(' '),
                'J': ord('#'),
                'o': ord('*'),
                'M': ord('?')
            },
            dtype=np.uint8)
        converted = converter(repainted)
        self.assertBoard(converted,
                         ['  #  #  *  ', '  ####  #  ', '  #  #  #  '])

        # Test that layer permutation happens correctly for the 2-D case.

        converter = rendering.ObservationToArray(
            {
                '.': ord(' '),
                'J': ord('#'),
                'o': ord('*'),
                'M': ord('?')
            },
            dtype=np.uint8,
            permute=(1, 0))
        converted = converter(repainted)
        self.assertBoard(converted, [
            '   ', '   ', '###', ' # ', ' # ', '###', '   ', '   ', '*##',
            '   ', '   '
        ])

        # For the 3-D conversion, we'll create a 3-D feature array that's a lot like
        # our feature masks.
        converter = rendering.ObservationToArray(
            {
                '.': (1, 0, 0, 0),
                'J': (0, 1, 0, 0),
                'o': (0, 0, 1, 0),
                'M': (0, 0, 0, 1)
            },
            dtype=bool)
        converted = converter(repainted)
        self.assertEqual(converted.shape, (4, 3, 11))

        assertMask(converted[0, :],
                   ['11011011011', '11000011011', '11011011011'])

        assertMask(converted[1, :],
                   ['00100100000', '00111100100', '00100100100'])

        assertMask(converted[2, :],
                   ['00000000100', '00000000000', '00000000000'])

        assertMask(converted[3, :],
                   ['00000000000', '00000000000', '00000000000'])

        # And another layer permutation test for the 3-D case.
        converter = rendering.ObservationToArray(
            {
                '.': (1, 0, 0, 0),
                'J': (0, 1, 0, 0),
                'o': (0, 0, 1, 0),
                'M': (0, 0, 0, 1)
            },
            dtype=bool,
            permute=(1, 2, 0))
        converted = converter(repainted)
        self.assertEqual(converted.shape, (3, 11, 4))

        assertMask(converted[..., 0],
                   ['11011011011', '11000011011', '11011011011'])

        assertMask(converted[..., 1],
                   ['00100100000', '00111100100', '00100100100'])

        assertMask(converted[..., 2],
                   ['00000000100', '00000000000', '00000000000'])

        assertMask(converted[..., 3],
                   ['00000000000', '00000000000', '00000000000'])

        ### Test ObservationToFeatureArray, which creates 3-D feature arrays faster.

        converter = rendering.ObservationToFeatureArray('.JoM')
        converted = converter(repainted)
        self.assertEqual(converted.shape, (4, 3, 11))

        assertMask(converted[0, :],
                   ['11011011011', '11000011011', '11011011011'])

        assertMask(converted[1, :],
                   ['00100100000', '00111100100', '00100100100'])

        assertMask(converted[2, :],
                   ['00000000100', '00000000000', '00000000000'])

        assertMask(converted[3, :],
                   ['00000000000', '00000000000', '00000000000'])

        ### Test ObservationToFeatureArray's layer permutation capability.

        converter = rendering.ObservationToFeatureArray('.J',
                                                        permute=(1, 0, 2))
        converted = converter(repainted)
        self.assertEqual(converted.shape, (3, 2, 11))

        assertMask(converted[0, :], ['11011011011', '00100100000'])

        assertMask(converted[1, :], ['11000011011', '00111100100'])

        assertMask(converted[2, :], ['11011011011', '00100100100'])
Esempio n. 29
0
def make_game():
  """Builds and returns an Apprehend game."""
  return ascii_art.ascii_art_to_game(
      GAME_ART, what_lies_beneath=' ',
      sprites={'P': PlayerSprite, 'b': BallSprite},
      update_schedule=['b', 'P'])
Esempio n. 30
0
def make_game(game_type, good_color, bad_color, switched_colors=False):
    if switched_colors:
        good_color, bad_color = bad_color, good_color  # switcheroo

    these_sprites = {}
    these_drapes = {}
    if game_type == "pick_up":
        shape = "square"
        num_objects = PICK_UP_NUM_OBJECTS_PER
        good_obj = good_color + "_" + shape
        bad_obj = bad_color + "_" + shape

        good_char = OBJECTS[good_obj]["char"]
        bad_char = OBJECTS[bad_obj]["char"]

        these_drapes.update({
            good_char:
            ascii_art.Partial(ValueDrape, value=1.),
            bad_char:
            ascii_art.Partial(ValueDrape, value=NEG_VALUE)
        })
    elif game_type == "pusher":
        shape = "tee"
        num_objects = PUSHER_NUM_OBJECTS_PER
        good_obj = good_color + "_" + shape
        bad_obj = bad_color + "_" + shape

        good_char = OBJECTS[good_obj]["char"]
        bad_char = OBJECTS[bad_obj]["char"]

        these_drapes.update({
            good_char:
            ascii_art.Partial(PushableDrape, value=1.),
            bad_char:
            ascii_art.Partial(PushableDrape, value=NEG_VALUE)
        })
    elif game_type == "shooter":
        shape = "diamond"
        num_objects = SHOOTER_NUM_OBJECTS_PER
        good_obj = good_color + "_" + shape
        bad_obj = bad_color + "_" + shape

        good_char = OBJECTS[good_obj]["char"]
        bad_char = OBJECTS[bad_obj]["char"]

        these_drapes.update({
            good_char:
            ascii_art.Partial(ShootableDrape, value=1.),
            bad_char:
            ascii_art.Partial(ShootableDrape, value=NEG_VALUE)
        })
    elif game_type == "sequence_imitation":
        shape = "triangle"
        num_objects = 1
        good_obj = good_color + "_" + shape
        bad_obj = bad_color + "_" + shape

        good_char = OBJECTS[good_obj]["char"]
        bad_char = OBJECTS[bad_obj]["char"]

        these_sprites.update({
            good_char:
            ascii_art.Partial(DancerSprite, value=SEQ_IMIT_VALUE_PER),
            bad_char:
            ascii_art.Partial(DancerSprite,
                              value=SEQ_IMIT_VALUE_PER * NEG_VALUE)
        })
    else:
        raise ValueError("Unknown game type: {}".format(game_type))

    update_schedule = [AGENT_CHAR, good_char, bad_char]
    if game_type == "pusher":
        update_schedule = [good_char, bad_char, AGENT_CHAR]

    grid = []
    grid.append(["#"] * (GRID_SIZE + 2))
    for _ in range(GRID_SIZE):
        grid.append(["#"] + ([" "] * GRID_SIZE) + ["#"])
    grid.append(["#"] * (GRID_SIZE + 2))
    if game_type in ["pick_up", "pusher", "shooter"]:
        locations = [(i, j) for i in range(1, GRID_SIZE + 1)
                     for j in range(1, GRID_SIZE + 1)]
    elif game_type == "sequence_imitation":
        quarts = [1 + GRID_SIZE // 4, GRID_SIZE - GRID_SIZE // 4]
        locations = [(x, y) for x in quarts for y in quarts]

    np.random.shuffle(locations)
    for _ in range(num_objects):
        bad_loc = locations.pop()
        grid[bad_loc[0]][bad_loc[1]] = bad_char
        good_loc = locations.pop()
        grid[good_loc[0]][good_loc[1]] = good_char
    agent_start = locations.pop()
    grid[agent_start[0]][agent_start[1]] = AGENT_CHAR
    these_sprites.update(
        {'A': ascii_art.Partial(PlayerSprite, game_type=game_type)})

    grid = [''.join(l) for l in grid]

    game = ascii_art.ascii_art_to_game(grid,
                                       what_lies_beneath=' ',
                                       sprites=these_sprites,
                                       drapes=these_drapes,
                                       update_schedule=update_schedule)

    if game_type == "pick_up":
        game.the_plot["num_picked_up"] = 0
    elif game_type == "pusher":
        game.the_plot["num_pushed_off"] = 0
        game.the_plot["player_blocked"] = False
    elif game_type == "shooter":
        game.the_plot["num_shot"] = 0
        game.the_plot["heading"] = 0
    elif game_type == "sequence_imitation":
        game.the_plot["good_move"] = -1  # no valid move on first turn
        game.the_plot["bad_move"] = -1
    return game