Mari0 is a PC game created by stabyourself.net in 2012 that combines the classic Super Mario Bros with the puzzle-platfomer Portal by giving Mario a portal gun. The game launched with 2 "mappacks", with the one most people played being the Super Mario Bros mappack containing the original set of levels. This TAS instead showcases the other mappack which features 18 Portal-inspired puzzles spread across 9 different levels.
After the
previous movie was submitted, Masterjun found out that there is more optimization to be done, and then noahkiq felt like optimizing even more. Going from competition to collaboration, saving every frame we could.
Game objectives
- Aims to complete the Portal mappack as fast as possible
- Abuses programming bugs and oversights
Basics
Frame rate
Unlike usual console games, this game is not programmed simply for a single frame rate. Instead, all new position and speed values are calculated based on how much time passed since the last time the screen was refreshed. Since libTAS emulates a basically perfect frame rate of 60, the time passed between frames barely varies. While there is no limit on how small the frame durations can be, there is a hard coded upwards cap of 0.01666667 seconds, which is about 59.999988 FPS (this means running at 10 FPS simply slows the game down to about 1/6 times the speed, otherwise a lot of clipping would occur). Choosing a frame rate of over 60 would mean less clipping can occur.
This programming also means the unit of movement is based on seconds, not frames. Additionally, the game uses tiles instead of pixels for calculations. In other words, a speed of 9 means 9 tiles per second.
Movement
Horizontal speed when walking on the ground is capped at 6.4, running speed on the ground (holding shift) is capped at 9. In the air, your speed caps at 6.4, unless you're already above 6.4 speed and holding shift, then it caps at 9. This means when slowing down in the air your speed should not go under 6.4, otherwise you won't be able to accelerate without waiting until you touch the ground.
If your horizontal speed exceeds 9, you can only decelerate.
Vertical speed caps at 100.
Portal Delay
After shooting a portal, there is a 0.2 second delay before you can shoot the next portal.
At first glance this should mean 12 frames. However, since 0.2 is stored in a double variable, it's actually around 0.2000000000000000111, so subtracting 1/60 twelve times from the 0.2 variable will result in a very very small non-zero value.
So now, at second glace this should mean it's always 13 frames. However, due to the LÖVE framework not always giving accurate 1/60 frame length values to Mari0, rounding errors can go the other direction.
The result is that the portal delay is either 12 or 13 frames, depending on frame durations which we can't manipulate without losing time.
Tricks
Portal Loops
When Mario falls into a portal, his speed is retained when exiting the other portal. If these portals are parallel, then Mario can fall through one portal to the other and build up a Y-speed of 100 tiles per second. This trick requires several portal-able surfaces, and when those are available, we use portal loops for:
Clipping
Entities in Mari0 only check for collision every frame. This means that if you have the right speed and the right coordinates, you can clip through many objects or walls. Mario's terminal velocity often works perfectly for clipping through slim objects, such as doors or enemies, and can also clip Mario into blocks.
In fact, there are two different ways to clip through doors (as seen in 1-1):
Clipping through Doors v1
Let Mario's X-Position be a.5
when touching a door from the left. By being at a position of under (a-1).5
and then at least a.5
the next frame, you clip through the door. This requires a speed of over 1 tile per frame, or 60 tiles a second. (Since we're running at 60 FPS, and if we were to run at a higher frame rate such as 120, it would require a speed of 120, which is impossible to achieve).
Clipping through Doors v2
Let Mario's X-Position be a.5
again. This time you need a position of under a.5
, and the next frame a position of at least (a+1).75
. This requires a speed of over 75 to pull off.
Cube Jumping
Turns out cubes are solid objects. And you can jump off of solid objects! Very nice.
Manipulating Floating Point Errors in our favor via Pausing
As explained in the Portal Delay section, frame durations vary, changing calculations and the direction of rounding errors. The game isn't processed when paused, so pausing can be used to skip problematic rounding errors.
1-1
Here we use our first portal loop to skip the first two puzzles by clipping through all three doors. Since we have a speed of 100, clipping through a single door is quite lenient, as the speed requirement is only 60. The interesting part is the two doors 2 tiles apart from each other.
The first door is at 40.5
, the second is at 42.5
.
Clipping through the first door with Clip v1
This leaves us with a position of at least 40.5
and a speed of over 60. It is not possible to put an intermediate frame and use Clip v1 again, since the next frame would be at least 41.5
(but we need to be under 41.5
, and slowing down means no speeding up again). So using Clip v1 immediately again would mean going from under 39.5
to at least 42.5
in two frames. So a speed of over 3 tiles per two frames, or 90 tiles per second. The other method would be to put an intermediate frame and use Clip v2. So from 39.5
to 43.75
in three frames, so 4.25 tiles per three frames, or 85 tiles per second.
Clipping through the first door with Clip v2
This leaves us with a position of at least 41.75
and a speed of over 75. We're now too far to use Clip v1 for the second door (requiring a position of under 41.5
). Clearly, the next frame would have a position of at least 43.0
, so there is no space for an intermediate frame. But we are able to use Clip v2 again, if only barely. Going from under 40.5
to at least 43.75
in two frames requires a speed of over 3.25 tiles per two frames, or 97.5 tiles per second.
The first of the described clips worked best with the single door further to the left.
1-2
Here we need to get a lot of downward momentum and quickly, which we gain using the portal-able elevator tiles. The short jump is to delay the entry to the portal until another shot can be fired. The upward momentum is used to grab the cube through the door, and then promptly drop it to slow us down. We also let it gain some downwards speed, as cubes don't lose their speed when grabbed. We then drop it a few frames later when we're in the middle of the door, and as the game has no room to put the cube next to Mario, it gets put under Mario. Mario also lands on the cube, then jumps the same frame to fall faster.
The next puzzle uses a series of portals to launch Mario over the open door in the floor. This door usually prevents you from jumping across and forces you to block the laser with your cube. We instead use our speed to jump right over, then jump off the cube during a jump, which lets us accelerate upwards faster instead of resetting our speed.
1-3
The beginning part here isn't very important, as the next puzzle runs on a frame rule, due to the cube being repeatedly respawned and destroyed. This means our main goal in this level is just to get the cube on the button as soon as possible, which we accomplish by jumping into it from above.
1-4
To skip the first puzzle, all we have to do is grab a cube while under the grill to prevent the cube from being destroyed. The next puzzle is done the intentional way, however, we do steal the cube for the third puzzle. To get this cube past the grill, we release the cube at a specific position so it's dropped past the grill. We then simply place the cube on the button to open the door to the flagpole.
2-1
At the start, instead of running back and forth to switch our gel, we use a quick portal peek to save a few frames from turning around. For the next puzzle, we skip covering the walls in blue gel to walljump, and instead just jump out of a portal.
2-2
Most of this level is just an autoscroller. However, at the end, we can use two very precise jumps off of cubes to skip the moving platform at the end. We pause a couple times to manipulate rounding errors, and place the cube in the opposite direction we want to go, as placing cubes forwards results in Mario pushing the cube until he can't land on it anymore.
2-3
At the start, we build up lots of horizontal speed using a simple jump, and use it to speed through the next puzzles. In the second puzzle, you're normally supposed to block one of the lasers with the cube. However, we can instead use our high speed to jump on the door in the floor before it finishes its animation and becomes intangible. In the next room, we use a portal loop to get Mario over the large wall, and drop our cube into this portal loop. We can then jump off this cube to get to the flagpole without needing the light bridge.
2-4
This level is full of minor speed optimizations which are barely visible, as they usually just save one or two frames. There's a quick bit of portal magic near the end, but is otherwise very straightforward.
3-1
The first puzzle uses an insanely complicated trick where we run right. The second puzzle uses a slight skip where we grab the cube as it falls out of the dispenser, which is surprisingly not the intended solution. We then drop the cube on the button and jump on the last frame of the movie to get Mario to the flagpole.
Special Thanks
- Maurice for porting his game to LÖVE 11.1 when asked for better libTAS support
Quote: "can you stop breaking my game"
- dwangoAC for raising awareness about libTAS and also raising awareness about bugs in libTAS which prevented games from working in the LÖVE framework before
- keylie for quickly fixing libTAS issues
- 102 for creating a modified version of the game which shows a level and game timer, as well as helpful memory values
feos: I'm on the Linux spree! Judging...
feos: Crazy TAS, pure master
junpiece. Accepting to Moons as an optional level set.