Tool-assisted game movies
When human skills are just not enough

Submission #6125: SMBbot's NES Super Mario Bros. "warpless" in 19:26.35

Console: Nintendo Entertainment System
Game name: Super Mario Bros.
Game version: JPN/USA
ROM filename: Super Mario Bros. (JU) [!].nes
Branch: warpless
Emulator: FCEUX 2.2.1
Movie length: 19:26.35
FrameCount: 70096
Re-record count: 7416
Author's real name:
Author's nickname: SMBbot
Submitter: HappyLee
Submitted at: 2018-10-10 13:55:47
Text last edited at: 2018-10-10 17:02:53
Text last edited by: Masterjun
Download: Download (1051886 bytes)
Status: decision: cancelled
Submission instructions
Discuss this submission (also rating / voting)
List all submissions by this submitter
List pages on this site that refer to this submission
View submission text history
Back to the submission list
Author's comments and explanations:
HappyLee: Definitely not intended for publication because it's not as fast as me and Mars608's latest SMB warpless TAS, but pardon me for submitting it here, as it may make an interesting submission (for some).

(Link to video)
This is a full-length TAS done entirely by a bot, an Lua script that I call it SMBbot. It's not an artificial intelligence, but what I call an "artificial idiot", as it's really dumb.

It doesn't read map or level information, doesn't evolve, doesn't have much memory. It basically just jumps randomly, and loads savestates after dying or bumping into a wall.

As dumb as it is, but it's the first bot ever to complete SMB, and in speedrun style! Only 7416 retries, 4:49 in real-time computing, it completes SMB warpless in 19:26.35. Not exactly as fast as best human speedrunners, but surely more stable and fast in average.


One day I was watching LuigI/O (a neural network playing SMB), and I thought, "this isn't intelligent", because after tens of thousands times of improving, LuigI/O still doesn't seem to know what it's doing or basic principles of the game. So I decided to build a smarter AI manually, and see how well it can play the game.

At first, my plan was to let the bot calculate map and analyze possible routes, and calculate jumping time and strength. It's rather complicated, so I wrote random jumping code initially and gave it a go. To my surprise, the bot finished 1-1 easily, and completed the whole World 1 after many attempts at 1-2 pipe jump. And I thought, "this could work", so I continued developing the bot and kept it jumping randomly instead of reading the map.

The basic function is to save state when Mario lands. Each savestate has 10 chances. Mario will do jump randomly in the range between the savestate and the frame Mario will die or fall. This basic function alone is enough to complete World 1, but will be stopped by the high wall in 2-1, so I added some features:

  • Backward jump acceleration: by playing a series of inputs at the beginning, just for fun. Used for every stage start.
  • Wall-allow: if Mario bumps into the same wall over 99 times and still can't jump over it, Mario would be allowed to touch that wall. This allows Mario to jump over the high wall in 2-1 (by walljump or Springboard), and will be used again in places like 8-2.
  • Swimming mode: two swimming probabilities (frequent and not frequent); Mario presses down when X speed > 16; savestate is made every 2 seconds. This allows Mario to complete water stages.
  • Death state: if Mario dies in similar ways too many times from a savestate point, that point might become a death state, which Mario land on the same spot again and then retry. This helps reduce retry count in places like 1-3 where Mario easily falls on the same spot and die meaninglessly too many times.
  • Retry 1/3 of the times when Mario falls. This helps prevent Mario from falling too much, and increase chances like passing 4-4 first maze.
  • Maze detection: if Mario walks through a maze check point and triggered a loop, it would trigger retry. If Mario fails to pass the check point correctly over 20 times, it starts over from the stable state (usually the beginning of the stage, but in maze levels, the last correct check point).
  • Wall-free mode: if Mario bumps into walls over 800 times (would be reduced when screen stops scrolling or Mario fails to solve a maze) and still fails to make progress, Mario would be allowed to touch any wall. There are one or two timers that will judge how Mario is doing after hitting the wall, to prevent meaningless wall bumps. If it's regular maze stage (like 4-4), Mario has 2/3 chance to walk towards left. Mario also has higher chance to do walljumps. Wall-free mode opens till Mario makes real progress. This allows Mario to solve the maze in 4-4 and 8-4, and may become useful in other places, too.
  • Every 1000 unsuccessful deaths brings Mario to the stable state (usually the beginning of the stage). Too many wall bumps from the right side of the screen also, to prevent Mario from being stuck in Warp Zone in 4-2 and 1-2 (sometimes happens).
  • First-frame jump chance increases as Mario's death grows without real progress. This helps to solve hard stages with hard jumps like 4-3.
  • If Mario dies from enemies over 500 times, it starts over from the stable state with a random chance of pressing left for 0 to 2 seconds. This helps if Mario is stuck with a bad Firebar in 5-4 or 6-4.
  • Number of allowed attempts from a savestate (originally 10) increases if Mario dies too much before making to the savestate. This helps to prevent Mario from losing valuable progress.
  • Pipe maze detection: if Mario's in a maze with pipe exits information, a part of pipe exit information is stored to prevent loop. If Mario's in the pipe maze with a non-loop pipe exit information, down is pressed every time Mario lands and sometimes during Mario running. This allows Mario to enter pipes to solve 8-4 maze.

Source code of the script: https://pastebin.com/vNHjw7Ca

I did this experiment of running it 20 times, and here are the results:

Attempt TAS time Retry Real-time Note
#01 19:28.71 11180 5:48
#02 19:29.91 11946 6:42
#03 19:26.35 (2nd) 7416 (1st) 4:49 (1st)
#04 19:30.95 8703 (3rd) 5:14 (2nd) Desynced at 5-3 first lift
#05 19:33.62 11146 6:52
#06 19:28.09 11007 6:52
#07 19:35.1 11202 6:45
#08 19:28.86 8943 5:30
#09 19:32.2 11287 6:26
#10 19:30.94 8572 (2nd) 5:20
#11 19:29.76 11500 6:21
#12 19:34.58 11884 6:57 Desynced in 8-4 water stage
#13 19:32.09 14097 7:48
#14 19:35.18 (last) 11530 7:32
#15 19:25.25 (1st) 11253 6:48 Desynced in 8-4 water stage
#16 19:30.97 9434 5:17 (3rd)
#17 19:30.51 11479 7:06
#18 19:30.11 14670 (last) 7:43
#19 19:29.42 14507 8:37 (last)
#20 19:27.23 (3rd) 11701 7:26
Average 19:30.49 11172.9 6:36

Unfortunately, the best TAS time attempt (#15) desynced in 8-4 water stage, probably due to a bug in my script or a bug of FCEUX. It happens so rarely that I can't detect it or fix it. Since the best attempt is not usable, I have to choose the second best attampt (#03).

Such bug won't happen if I run the bot while recording the movie. In order to save computing time (loading savestates in FCEUX while recording a movie can be quite slow), I used some arrays to store input, and sort them into .fm2 input format after meeting the Princess. Would be best if I can fix this bug so it can generate movies without desync and as fast as possible.

To see the bot working in normal speed (without fast-forward): https://youtu.be/9Tlif_XU4Jg


I think the biggest contribution this project has made is to prove that this game can be solved by a dumb random jumping bot that doesn't know where it is or what it's doing.

Also, since real-time SMB speedrunners have become so good, and generating a decent speedrun by a bot has become so easy, SMB TASers should work hard to bring entertainment to a new level, more like a superplay and less like RTA speedruns or bot playing, which exactly was my idea in the discussion of the the latest SMB warpless TAS.

"Definitely not intended for publication"

Masterjun: Okay. Cancelled for not being a serious submission.

Similar submissions (by title and categories where applicable):