summaryrefslogtreecommitdiff
path: root/pokemontools/vba/autoplayer.py
diff options
context:
space:
mode:
Diffstat (limited to 'pokemontools/vba/autoplayer.py')
-rw-r--r--pokemontools/vba/autoplayer.py820
1 files changed, 454 insertions, 366 deletions
diff --git a/pokemontools/vba/autoplayer.py b/pokemontools/vba/autoplayer.py
index 9aa8f4a..af14d47 100644
--- a/pokemontools/vba/autoplayer.py
+++ b/pokemontools/vba/autoplayer.py
@@ -4,492 +4,580 @@ Programmatic speedrun of Pokémon Crystal
"""
import os
-# bring in the emulator and basic tools
-import vba
-
-def main():
- """
- Start the game.
- """
- vba.load_rom()
+import pokemontools.configuration as configuration
- # get past the opening sequence
- skip_intro()
-
- # walk to mom and handle her text
- handle_mom()
-
- # walk outside into new bark town
- walk_into_new_bark_town()
-
- # walk to elm and do whatever he wants
- handle_elm("totodile")
-
- new_bark_level_grind(10, skip=False)
+# bring in the emulator and basic tools
+import vba as _vba
def skippable(func):
"""
Makes a function skippable.
- Saves the state before and after the function runs.
- Pass "skip=True" to the function to load the previous save
- state from when the function finished.
+ Saves the state before and after the function runs. Pass "skip=True" to the
+ function to load the previous save state from when the function finished.
"""
def wrapped_function(*args, **kwargs):
+ self = args[0]
skip = True
+ override = True
if "skip" in kwargs.keys():
skip = kwargs["skip"]
del kwargs["skip"]
+ if "override" in kwargs.keys():
+ override = kwargs["override"]
+ del kwargs["override"]
+
# override skip if there's no save
if skip:
full_name = func.__name__ + "-end.sav"
- if not os.path.exists(os.path.join(vba.save_state_path, full_name)):
+ if not os.path.exists(os.path.join(self.config.save_state_path, full_name)):
skip = False
return_value = None
if not skip:
- vba.save_state(func.__name__ + "-start", override=True)
+ if override:
+ self.cry.save_state(func.__name__ + "-start", override=override)
+
return_value = func(*args, **kwargs)
- vba.save_state(func.__name__ + "-end", override=True)
+
+ if override:
+ self.cry.save_state(func.__name__ + "-end", override=override)
elif skip:
- vba.set_state(vba.load_state(func.__name__ + "-end"))
+ self.cry.vba.state = self.cry.load_state(func.__name__ + "-end")
return return_value
return wrapped_function
-@skippable
-def skip_intro():
+class Runner(object):
"""
- Skip the game boot intro sequence.
+ ``Runner`` is used to represent a set of functions that control an instance
+ of the emulator. This allows for automated runs of games.
"""
+ pass
- # copyright sequence
- vba.nstep(400)
+class SpeedRunner(Runner):
+ def __init__(self, cry=None, config=None):
+ super(SpeedRunner, self).__init__()
- # skip the ditto sequence
- vba.press("a")
- vba.nstep(100)
+ self.cry = cry
- # skip the start screen
- vba.press("start")
- vba.nstep(100)
+ if not config:
+ config = configuration.Config()
- # click "new game"
- vba.press("a", holdsteps=50, aftersteps=1)
+ self.config = config
- # skip text up to "Are you a boy? Or are you a girl?"
- vba.crystal.text_wait()
+ def setup(self):
+ """
+ Configure this ``Runner`` instance to contain a reference to an active
+ emulator session.
+ """
+ if not self.cry:
+ self.cry = _vba.crystal(config=self.config)
- # select "Boy"
- vba.press("a", holdsteps=50, aftersteps=1)
+ def main(self):
+ """
+ Main entry point for complete control of the game as the main player.
+ """
+ # get past the opening sequence
+ self.skip_intro(skip=True)
- # text until "What time is it?"
- vba.crystal.text_wait()
+ # walk to mom and handle her text
+ self.handle_mom(skip=True)
- # select 10 o'clock
- vba.press("a", holdsteps=50, aftersteps=1)
+ # walk outside into new bark town
+ self.walk_into_new_bark_town(skip=True)
- # yes i mean it
- vba.press("a", holdsteps=50, aftersteps=1)
+ # walk to elm and do whatever he wants
+ self.handle_elm("totodile", skip=True)
- # "How many minutes?" 0 min.
- vba.press("a", holdsteps=50, aftersteps=1)
+ self.new_bark_level_grind(17, skip=False)
- # "Who! 0 min.?" yes/no select yes
- vba.press("a", holdsteps=50, aftersteps=1)
+ @skippable
+ def skip_intro(self, stop_at_name_selection=False):
+ """
+ Skip the game boot intro sequence.
+ """
- # read text until name selection
- vba.crystal.text_wait()
+ # copyright sequence
+ self.cry.nstep(400)
- # select "Chris"
- vba.press("d", holdsteps=10, aftersteps=1)
- vba.press("a", holdsteps=50, aftersteps=1)
+ # skip the ditto sequence
+ self.cry.vba.press("a")
+ self.cry.nstep(100)
- def overworldcheck():
- """
- A basic check for when the game starts.
- """
- return vba.get_memory_at(0xcfb1) != 0
+ # skip the start screen
+ self.cry.vba.press("start")
+ self.cry.nstep(100)
- # go until the introduction is done
- vba.crystal.text_wait(callback=overworldcheck)
+ # click "new game"
+ self.cry.vba.press("a", hold=50, after=1)
- return
+ # skip text up to "Are you a boy? Or are you a girl?"
+ self.cry.text_wait()
-@skippable
-def handle_mom():
- """
- Walk to mom. Handle her speech and questions.
- """
+ # select "Boy"
+ self.cry.vba.press("a", hold=50, after=1)
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
+ # text until "What time is it?"
+ self.cry.text_wait()
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
+ # select 10 o'clock
+ self.cry.vba.press("a", hold=50, after=1)
- vba.crystal.move("d")
- vba.crystal.move("d")
+ # yes i mean it
+ self.cry.vba.press("a", hold=50, after=1)
- # move into mom's line of sight
- vba.crystal.move("d")
+ # "How many minutes?" 0 min.
+ self.cry.vba.press("a", hold=50, after=1)
- # let mom talk until "What day is it?"
- vba.crystal.text_wait()
+ # "Who! 0 min.?" yes/no select yes
+ self.cry.vba.press("a", hold=50, after=1)
- # "What day is it?" Sunday
- vba.press("a", holdsteps=10) # Sunday
+ # read text until name selection
+ self.cry.text_wait()
- vba.crystal.text_wait()
+ if stop_at_name_selection:
+ return
- # "SUNDAY, is it?" yes/no
- vba.press("a", holdsteps=10) # yes
+ # select "Chris"
+ self.cry.vba.press("d", hold=10, after=1)
+ self.cry.vba.press("a", hold=50, after=1)
- vba.crystal.text_wait()
+ def overworldcheck():
+ """
+ A basic check for when the game starts.
+ """
+ return self.cry.vba.memory[0xcfb1] != 0
- # "Is it Daylight Saving Time now?" yes/no
- vba.press("a", holdsteps=10) # yes
+ # go until the introduction is done
+ self.cry.text_wait(callback=overworldcheck)
- vba.crystal.text_wait()
+ return
- # "AM DST, is that OK?" yes/no
- vba.press("a", holdsteps=10) # yes
+ @skippable
+ def handle_mom(self):
+ """
+ Walk to mom. Handle her speech and questions.
+ """
- # text until "know how to use the PHONE?" yes/no
- vba.crystal.text_wait()
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
- # press yes
- vba.press("a", holdsteps=10)
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
- # wait until mom is done talking
- vba.crystal.text_wait()
+ self.cry.move("d")
+ self.cry.move("d")
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ # move into mom's line of sight
+ self.cry.move("d")
- return
+ # let mom talk until "What day is it?"
+ self.cry.text_wait()
-@skippable
-def walk_into_new_bark_town():
- """
- Walk outside after talking with mom.
- """
+ # "What day is it?" Sunday
+ self.cry.vba.press("a", hold=10) # Sunday
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("l")
- vba.crystal.move("l")
+ self.cry.text_wait()
- # walk outside
- vba.crystal.move("d")
+ # "SUNDAY, is it?" yes/no
+ self.cry.vba.press("a", hold=10) # yes
-@skippable
-def handle_elm(starter_choice):
- """
- Walk to Elm's Lab and get a starter.
- """
+ self.cry.text_wait()
- # walk to the lab
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("u")
- vba.crystal.move("u")
+ # "Is it Daylight Saving Time now?" yes/no
+ self.cry.vba.press("a", hold=10) # yes
- # walk into the lab
- vba.crystal.move("u")
+ self.cry.text_wait()
- # talk to elm
- vba.crystal.text_wait()
+ # "AM DST, is that OK?" yes/no
+ self.cry.vba.press("a", hold=10) # yes
- # "that I recently caught." yes/no
- vba.press("a", holdsteps=10) # yes
+ # text until "know how to use the PHONE?" yes/no
+ self.cry.text_wait()
- # talk to elm some more
- vba.crystal.text_wait()
+ # press yes
+ self.cry.vba.press("a", hold=10)
- # talking isn't done yet..
- vba.crystal.text_wait()
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # wait until mom is done talking
+ self.cry.text_wait()
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
- # move toward the pokeballs
- vba.crystal.move("r")
+ return
- # move to cyndaquil
- vba.crystal.move("r")
+ @skippable
+ def walk_into_new_bark_town(self):
+ """
+ Walk outside after talking with mom.
+ """
- moves = 0
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # walk outside
+ self.cry.move("d")
+
+ @skippable
+ def handle_elm(self, starter_choice):
+ """
+ Walk to Elm's Lab and get a starter.
+ """
+
+ # walk to the lab
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # walk into the lab
+ self.cry.move("u")
+
+ # talk to elm
+ self.cry.text_wait()
+
+ # "that I recently caught." yes/no
+ self.cry.vba.press("a", hold=10) # yes
+
+ # talk to elm some more
+ self.cry.text_wait()
+
+ # talking isn't done yet..
+ self.cry.text_wait()
+ self.cry.text_wait()
+ self.cry.text_wait()
+
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
+
+ # move toward the pokeballs
+ self.cry.move("r")
+
+ # move to cyndaquil
+ self.cry.move("r")
- if starter_choice.lower() == "cyndaquil":
moves = 0
- if starter_choice.lower() == "totodile":
- moves = 1
- else:
- moves = 2
- for each in range(0, moves):
- vba.crystal.move("r")
+ if starter_choice.lower() == "cyndaquil":
+ moves = 0
+ elif starter_choice.lower() == "totodile":
+ moves = 1
+ else:
+ moves = 2
- # face the pokeball
- vba.crystal.move("u")
+ for each in range(0, moves):
+ self.cry.move("r")
- # select it
- vba.press("a", holdsteps=10, aftersteps=0)
+ # face the pokeball
+ self.cry.move("u")
- # wait for the image to pop up
- vba.crystal.text_wait()
+ # select it
+ self.cry.vba.press("a", hold=10, after=0)
- # wait for the image to close
- vba.crystal.text_wait()
+ # wait for the image to pop up
+ self.cry.text_wait()
- # wait for the yes/no box
- vba.crystal.text_wait()
+ # wait for the image to close
+ self.cry.text_wait()
- # press yes
- vba.press("a", holdsteps=10, aftersteps=0)
+ # wait for the yes/no box
+ self.cry.text_wait()
- # wait for elm to talk a bit
- vba.crystal.text_wait()
+ # press yes
+ self.cry.vba.press("a", hold=10, after=0)
- # TODO: why didn't that finish his talking?
- vba.crystal.text_wait()
+ # wait for elm to talk a bit
+ self.cry.text_wait()
- # give a nickname? yes/no
- vba.press("d", holdsteps=10, aftersteps=0) # move to "no"
- vba.press("a", holdsteps=10, aftersteps=0) # no
+ # TODO: why didn't that finish his talking?
+ self.cry.text_wait()
- # TODO: why didn't this wait until he was completely done?
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # give a nickname? yes/no
+ self.cry.vba.press("d", hold=10, after=0) # move to "no"
+ self.cry.vba.press("a", hold=10, after=0) # no
- # get the phone number
- vba.crystal.text_wait()
+ # TODO: why didn't this wait until he was completely done?
+ self.cry.text_wait()
+ self.cry.text_wait()
- # talk with elm a bit more
- vba.crystal.text_wait()
+ # get the phone number
+ self.cry.text_wait()
- # TODO: and again.. wtf?
- vba.crystal.text_wait()
+ # talk with elm a bit more
+ self.cry.text_wait()
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
- # move down
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ # move down
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
- # move into the researcher's line of sight
- vba.crystal.move("d")
+ # move into the researcher's line of sight
+ self.cry.move("d")
- # get the potion from the person
- vba.crystal.text_wait()
- vba.crystal.text_wait()
+ # get the potion from the person
+ self.cry.text_wait()
+ self.cry.text_wait()
- # wait for the script to end
- vba.crystal.wait_for_script_running()
+ # wait for the script to end
+ self.cry.wait_for_script_running()
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
- # go outside
- vba.crystal.move("d")
+ # go outside
+ self.cry.move("d")
- return
+ return
-@skippable
-def new_bark_level_grind(level):
- """
- Do level grinding in New Bark.
+ @skippable
+ def new_bark_level_grind(self, level, walk_to_grass=True):
+ """
+ Do level grinding in New Bark.
- Starting just outside of Elm's Lab, do some level grinding until the first
- partymon level is equal to the given value..
- """
+ Starting just outside of Elm's Lab, do some level grinding until the
+ first partymon level is equal to the given value..
+ """
- # walk to the grass area
- new_bark_level_grind_walk_to_grass(skip=False)
+ # walk to the grass area
+ if walk_to_grass:
+ self.new_bark_level_grind_walk_to_grass(skip=False)
- # TODO: walk around in grass, handle battles
- walk = ["d", "d", "u", "d", "u", "d"]
- for direction in walk:
- vba.crystal.move(direction)
+ last_direction = "u"
- # wait for wild battle to completely start
- vba.crystal.text_wait()
+ # walk around in the grass until a battle happens
+ while self.cry.vba.memory[0xd22d] == 0:
+ if last_direction == "u":
+ direction = "d"
+ else:
+ direction = "u"
- attacks = 5
+ self.cry.move(direction)
- while attacks > 0:
- # FIGHT
- vba.press("a", holdsteps=10, aftersteps=1)
+ last_direction = direction
- # wait to select a move
- vba.crystal.text_wait()
+ # wait for wild battle to completely start
+ self.cry.text_wait()
- # SCRATCH
- vba.press("a", holdsteps=10, aftersteps=1)
+ attacks = 5
- # wait for the move to be over
- vba.crystal.text_wait()
+ while attacks > 0:
+ # FIGHT
+ self.cry.vba.press("a", hold=10, after=1)
- hp = ((vba.get_memory_at(0xd218) << 8) | vba.get_memory_at(0xd217))
- print "enemy hp is: " + str(hp)
+ # wait to select a move
+ self.cry.text_wait()
- if hp == 0:
- print "enemy hp is zero, exiting"
- break
- else:
+ # SCRATCH
+ self.cry.vba.press("a", hold=10, after=1)
+
+ # wait for the move to be over
+ self.cry.text_wait()
+
+ hp = self.cry.get_enemy_hp()
print "enemy hp is: " + str(hp)
- attacks = attacks - 1
-
- while vba.get_memory_at(0xd22d) != 0:
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # wait for the map to finish loading
- vba.nstep(50)
-
- print "okay, back in the overworld"
-
- # move up
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
-
- # move into new bark town
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # move up
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
-
- # move to the door
- vba.crystal.move("r")
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # walk in
- vba.crystal.move("u")
-
- # move up to the healing thing
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("u")
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # face it
- vba.crystal.move("u")
-
- # interact
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # wait for yes/no box
- vba.crystal.text_wait()
-
- # press yes
- vba.press("a", holdsteps=10, aftersteps=1)
-
- # TODO: when is healing done?
-
- # wait until the script is done running
- vba.crystal.wait_for_script_running()
-
- # wait for it to be really really done
- vba.nstep(50)
-
- vba.crystal.move("r")
- vba.crystal.move("r")
-
- # move to the door
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- # walk out
- vba.crystal.move("d")
-
- # check partymon1 level
- if vba.get_memory_at(0xdcfe) < level:
- new_bark_level_grind(level, skip=False)
- else:
- return
+ if hp == 0:
+ print "enemy hp is zero, exiting"
+ break
+ else:
+ print "enemy hp is: " + str(hp)
+
+ attacks = attacks - 1
+
+ while self.cry.vba.memory[0xd22d] != 0:
+ self.cry.vba.press("a", hold=10, after=1)
+
+ # wait for the map to finish loading
+ self.cry.vba.step(count=50)
+
+ # This is used to handle any additional textbox that might be up on the
+ # screen. The debug parameter is set to True so that max_wait is
+ # enabled. This might be a textbox that is still waiting around because
+ # of some faint during the battle. I am not completely sure why this
+ # happens.
+ self.cry.text_wait(max_wait=30, debug=True)
+
+ print "okay, back in the overworld"
+
+ cur_hp = ((self.cry.vba.memory[0xdd01] << 8) | self.cry.vba.memory[0xdd02])
+ move_pp = self.cry.vba.memory[0xdcf6] # move 1 pp
+
+ # if pokemon health is >20, just continue
+ # if move 1 PP is 0, just continue
+ if cur_hp > 20 and move_pp > 5 and self.cry.vba.memory[0xdcfe] < level:
+ self.cry.move("u")
+ return self.new_bark_level_grind(level, walk_to_grass=False, skip=False)
+
+ # move up
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # move into new bark town
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # move up
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+
+ # move to the door
+ self.cry.move("r")
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # walk in
+ self.cry.move("u")
+
+ # move up to the healing thing
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("u")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # face it
+ self.cry.move("u")
+
+ # interact
+ self.cry.vba.press("a", hold=10, after=1)
+
+ # wait for yes/no box
+ self.cry.text_wait()
+
+ # press yes
+ self.cry.vba.press("a", hold=10, after=1)
+
+ # TODO: when is healing done?
+
+ # wait until the script is done running
+ self.cry.wait_for_script_running()
+
+ # wait for it to be really really done
+ self.cry.vba.step(count=50)
+
+ self.cry.move("r")
+ self.cry.move("r")
+
+ # move to the door
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+
+ # walk out
+ self.cry.move("d")
+
+ # check partymon1 level
+ if self.cry.vba.memory[0xdcfe] < level:
+ self.new_bark_level_grind(level, skip=False)
+ else:
+ return
+
+ @skippable
+ def new_bark_level_grind_walk_to_grass(self):
+ """
+ Move to just above the grass from outside Elm's lab.
+ """
+
+ self.cry.move("d")
+ self.cry.move("d")
+
+ self.cry.move("l")
+ self.cry.move("l")
+
+ self.cry.move("d")
+ self.cry.move("d")
+
+ self.cry.move("l")
+ self.cry.move("l")
-@skippable
-def new_bark_level_grind_walk_to_grass():
+ # move to route 29 past the trees
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+ self.cry.move("l")
+
+ # move to just above the grass
+ self.cry.move("d")
+ self.cry.move("d")
+ self.cry.move("d")
+
+def bootstrap(runner=None, cry=None):
"""
- Move to just above the grass from outside Elm's lab.
+ Setup the initial game and return the state. This skips the intro and
+ performs some other actions to get the game to a reasonable starting state.
"""
+ if not runner:
+ runner = SpeedRunner(cry=cry)
+ runner.setup()
+
+ # skip=False means always run the skip_intro function regardless of the
+ # presence of a saved after state.
+ runner.skip_intro(skip=True)
+
+ # keep a reference of the current state
+ state = runner.cry.vba.state
+
+ runner.cry.vba.shutdown()
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- vba.crystal.move("d")
- vba.crystal.move("d")
-
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # move to route 29 past the trees
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
- vba.crystal.move("l")
-
- # move to just above the grass
- vba.crystal.move("d")
- vba.crystal.move("d")
- vba.crystal.move("d")
+ return state
+
+def main():
+ """
+ Setup a basic ``SpeedRunner`` instance and then run the runner.
+ """
+ runner = SpeedRunner()
+ runner.setup()
+ return runner.main()
if __name__ == "__main__":
main()