def make_game():
    """Builds and returns a Hello World game."""
    return ascii_art.ascii_art_to_game(
        HELLO_ART,
        what_lies_beneath=' ',
        sprites={'1': ascii_art.Partial(SlidingSprite, 0),
                 '2': ascii_art.Partial(SlidingSprite, 1),
                 '3': ascii_art.Partial(SlidingSprite, 2),
                 '4': ascii_art.Partial(SlidingSprite, 3)},
        drapes={'@': RollingDrape},
        z_order='12@34')
    def testChangingZOrdering(self):
        """Game entities can change the Z-ordering."""
        # Not helpful in this test, since argument lists are long:
        # pylint: disable=g-long-lambda

        # Our test takes place in this very tiny world:
        art = ['.abc.']

        # Here we make the game.
        engine = ascii_art.ascii_art_to_game(
            art=art, what_lies_beneath='.',
            # a, b, and c are sprites.
            sprites=dict(a=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         b=ascii_art.Partial(tt.TestMazeWalker, impassable=''),
                         c=ascii_art.Partial(tt.TestMazeWalker, impassable='')),
            # Note this initial z-ordering.
            z_order='abc')

        ### GAME ITERATION #0. Nothing happens; we just get the game started.

        engine.its_showtime()

        ### GAME ITERATION #1. All of our sprites move to stand on top of one
        ### another. No Z-order change yet.

        observation, unused_reward, unused_discount = engine.play(
            {'a': 'e', 'c': 'w'})
        self.assertBoard(observation.board, ['..c..'])

        ### GAME ITERATION #2. b moves in front of c. Z-ordering should be 'acb'.

        tt.pre_update(engine, 'b',
                      lambda actions, board, layers, backdrop, things, the_plot: (
                          the_plot.change_z_order('b', 'c')))
        observation, unused_reward, unused_discount = engine.play(None)
        self.assertBoard(observation.board, ['..b..'])

        ### GAME ITERATION #2. c moves to the back. Z-ordering should be 'cab'.

        tt.pre_update(engine, 'c',
                      lambda actions, board, layers, backdrop, things, the_plot: (
                          the_plot.change_z_order('c', None)))
        observation, unused_reward, unused_discount = engine.play(None)
        self.assertBoard(observation.board, ['..b..'])

        ### GAME ITERATION #3. b moves to the back. Z-ordering should be 'bca'.

        tt.pre_update(engine, 'b',
                      lambda actions, board, layers, backdrop, things, the_plot: (
                          the_plot.change_z_order('b', None)))
        observation, unused_reward, unused_discount = engine.play(None)
        self.assertBoard(observation.board, ['..a..'])
Exemplo 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])

    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')

    return ascii_art.ascii_art_to_game(
        STAR_ART, 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)},
        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', 'P'], ['@']],
        z_order='abc@#P')
Exemplo n.º 4
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),
            ],
        )
Exemplo n.º 5
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)),
            ],
        )
Exemplo n.º 6
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)),
            ],
        )
    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)

        ### 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)