The World’s Hardest Game is a classic flash rage game, perhaps even the most well-known of its kind. It was first released in december 2007 on the developer’s own site snubbyland, and then in march 2008 on armorgames. The game mechanics are very straight-forward, you control the red square with arrow keys or wasd, and you have to manoeuver from a to b while collecting the yellow circles and avoiding the blue circles.
how game work
Despite having such simple mechanics, this game does have some speedtech, and also emulation inaccuracies. I will explain all of it in excruciating detail.
Hitboxes
There are two separate hitboxes for the player, one of them is just a square, which is used for colliding with coins and checkpoints (so anything yellow and green).
The other hitbox, used for enemies and walls, is much more lenient. It consists of only five pixel-sized points, arranged in a plus-shape on the square sprite. All of the corners are effectively invincible, enabling shenanigans. Subsequent installments in the whg series do add four points on the corners, as a much better approximation of a square.
Envisioning the player as a plus sign makes a lot of the tas much easier to follow and understand.
Wall clip
Due to the player not having corners on its hitbox, moving around wall corners is very smooth. Both corners can overlap quite a bit, getting unintentionally ‘stuck’ on a wall is very hard, making for satisfying gameplay. This does lead to a funny little oversight; if you move diagonally into a wall at the corner, and keep going along the wall, one of the player’s points will be inside the wall. The wall attempts to push the player out, but this can be nullified by continuing to hold the direction opposite to the push. It’s a bit like taking the overlap at the corner and extending it to the rest of the wall.
Wall clips are widely applicable, although because of imprecise positions, not every wall clip is the same. For some walls it’s possible to “sink” a lot deeper into them than others, both normally and by clipping. For example, walls on the left or right can generally be clipped a bit further into than walls on the top or bottom.
Cornerboost
Continuing from a wall clip, what happens if you exit the wall on the other side? Well, the player starts to round the corner, and then another point enters the wall and gets pushed out. Since this push is in the same direction as one of the movement directions, it’s added instead of nullified, and the player moves a grand total of six pixels in one direction in one frame instead of the usual three, saving one whole frame in a bunch of places.
Obviously not exactly the most groundbreaking tech, although sometimes these single framesaves can compound into entirely new routes becoming possible, saving seconds.
Clicking GO
Every fifth level is preceded with a wall of text giving helpful tips, which has to be advanced by clicking the big GO button at the bottom.
Now for a small bit of background, when coolmathgames hosted the game on their site, they made a few minor changes to the game such as removing the iconic L5 text (and later they also specifically requested the developer to make an HTML version of the game). One of these changes was the added functionality of using the spacebar to advance the GO screens. Since holding the spacebar ahead of time also works, this very quickly became the preferred version to speedrun, and to base every mod off of.
Now, when this tas was being converted to libtas, the original version was used, so GO had to be clicked. And this eventually led us to realize that pressing/holding space actually ‘’cost’’ a frame this whole time (and we would’ve easily been able to tell just by looking at the code). This meant that every single tas we created up until this point was improvable by 6 frames, one for each GO. Multiple second barriers, deemed impossible to break, were suddenly shattered. Chaos reigned
Ruffle limitations
Yeah, so basically, there’s a bit more to talk about regarding this game, but it’s not entirely relevant for this tas specifically. Some of the more complex stuff is not currently possible to be implemented (which might be for the best?)
Hitboxes (cont’d)
Time to dive into the actual nitty gritty of flash collision detection. There are two different ways, a point to pixel check and a bounding box check. Coins only use the bounding box check, which means they effectively have a square hitbox. Enemies use both, they first perform a bounding box check to see if they should perform a (more intensive) p2p check. The player will only die if both checks pass on at least one of its five points.
We don’t exactly know how flash works, but we suspect that collision detection is done before rendering a new frame. This poses a problem, because a point to pixel check relies on pixels, which are rendered, whereas a bounding box check (as well as the points on the player) use the actual position regardless of what’s rendered. In practice, this means the enemies are disjointed from their own bounding box, and not every pixel is used for collision detection. The more distance they move per frame, the smaller their hitbox is. Because the points on the player are so spread apart, it’s not too uncommon for an enemy to go directly through the player and miss all of the points, which does not lead to a death. This is commonly referred to as an enemy clip, and is one of the core exploits in this game’s speedrun.
If my hitbox explanation is not entirely clear, I have this handy infographic (thrown together in a few minutes)
Notice the “trail” at the back corners of the enemy. While the hitbox becomes more lenient at the front, it also becomes more restrictive at the back. This is much more egregious with rotating enemies, since their bounding box rotates with them and they need to be encased by another bigger box that always has the same orientation (and changes in size).
Ruffle does not emulate this particular quirk of flash, so the enemy hitboxes are always circles. This removes any possibility for clipping through enemies to save time, but it also introduces some new timesaves due to the lack of the trail.
Resolution
Another side effect of hit detection being based on rendering, is that it will in turn be affected by running the game on a different resolution. This is a lot more impactful than you’d think. The majority of levels have different times depending on what resolution you play on. Certain enemy clips become possible, certain walls allow for deeper clips, there’s quite a lot of variance, both minor and major. In some cases, the fastest known time of a level has only been performed on a single resolution, out of approximately 2125 possibilities that we’ve taken into consideration.
Some of the bigger timesaves happen to be mutually exclusive, so the main tas that the community uses as a benchmark is a ‘sum of best’ of sorts, a “what if we allow changing the resolution during level transitions” kinda tas. RTA does not currently allow changing resolution during a run, and the optimal resolution used for speedruns has changed multiple times.
Changing resolution can also be taken to the extreme; when the game size gets too small, enemies and walls can no longer be rendered and lose their hitboxes, while coins and checkpoints still work fine. Using this to its full extent would save nearly two minutes in the run. To simplify things we’ve decided to make the default size of 550 by 400 pixels the cutoff point for “legal” resolutions, any size smaller than this is not allowed. Even still, some framesaves have been found that are exclusive to a resolution slightly smaller than default, and as such remain unimplemented.
On ruffle, changing the resolution does not appear to affect collision detection in any way. This massively reduces the routing options and makes optimization at a high level a lot easier.
Some of the routes that happen to work on ruffle don’t work on every resolution in flash, most notably L12. There actually exist a bunch of resolutions where flash is slower than ruffle overall.
Ruffle vs Flash
Now that we’ve talked about the differences, time for some raw numbers.
Using the community standard timing that starts at clicking the second play game button, this tas on ruffle has a time of 5:01.633
The flash tas on the default 550 by 400 resolution would be 4:55.900, coincidentally on the lower end of possible times.
The fastest time for any single resolution is 4:54.833, using 603 by 438. The height of 438 is what matters here, the width could be increased arbitrarily (it just adds black bars).
The sum of best individual level tases, with all resolutions above default allowed, is 4:51.800
(and as a sidenote, the slowest resolution we’ve found has a time of 5:03.433)
Here are the original videos for the ruffle tas and the flash tases. Note that these were made with a specialized tas mod, which uses space for the go screens so the times are off by six frames.
And here’s a time comparison for all the levels in ruffle and in the flash sum of best.
Level | Ruffle tas | Flash tas | Difference |
---|
1 | 3,900 | 3,833 | 0,067 |
2 | 3,800 | 3,800 | 0 |
3 | 3,367 | 3,400 | -0,033 |
4 | 3,133 | 3,167 | -0,033 |
5 | 20,867 | 20,400 | 0,467 |
6 | 11,800 | 11,800 | 0 |
7 | 7,767 | 7,433 | 0,333 |
8 | 10,467 | 8,400 | 2,067 |
9 | 9,567 | 7,733 | 1,833 |
10 | 5,833 | 5,567 | 0,267 |
11 | 12,000 | 12,033 | -0,033 |
12 | 12,700 | 12,733 | -0,033 |
13 | 1,867 | 1,867 | 0 |
14 | 4,000 | 3,867 | 0,133 |
15 | 9,233 | 8,267 | 0,967 |
16 | 7,800 | 7,800 | 0 |
17 | 16,033 | 16,000 | 0,033 |
18 | 5,600 | 5,533 | 0,067 |
19 | 3,533 | 3,533 | 0 |
20 | 10,600 | 10,033 | 0,567 |
21 | 6,433 | 6,333 | 0,100 |
22 | 10,500 | 10,300 | 0,200 |
23 | 7,067 | 7,067 | 0 |
24 | 4,967 | 4,933 | 0,033 |
25 | 6,567 | 6,533 | 0,033 |
26 | 16,200 | 15,467 | 0,733 |
27 | 10,067 | 9,600 | 0,467 |
28 | 10,233 | 8,733 | 1,500 |
29 | 8,033 | 7,967 | 0,067 |
30 | 13,733 | 13,700 | 0,033 |
All | 301,633 | 291,800 | 9,833 |
Of course, there are many subtle differences not reflected in these times.
As you can see, ruffle saves one frame in four of the levels, due to the aforementioned lack of trail in the enemy hitbox. In L4, L11 and L12 the respective bottlenecks are reduced by one frame, and in L3 a cornerboost is made possible.
Of course this timesave pales in comparison to what can be done just by clipping through some enemies, which saves a massive amount of time in for example L8 by making a completely different route possible, and in L28 by making the bottom half much more straight-forward. Though it’s also worth noting that some commonly occurring suboptimal flash level times in for example 5, 20 and 27 are tied with ruffle.
As for L9, oh I nearly forgot to mention-
Hidden coin
There exists a second, invisible, inaudible coin in level 9, inside of a wall. It is possible to collect with a wall clip, but only on certain resolutions. Because the level is coded to require exactly one coin to beat, it’s possible to skip the regular coin by getting the other one. Despite being out of the way of the normal route, collecting the hidden coin still saves a decent amount of time.
The best time for L9 with the regular coin is 8.800, so that would still be a timesave of 0,767 over ruffle just from enemy clipping.
Afterword
Anyway, that just about sums up everything related and unrelated to this tas. I suspect whg2 might have a similar thing with ruffle-exclusive framesaves, although the flash tas for that game is much, much more insane. Especially with the added in-game option to change the quality, which only further affects the hitboxes.
As for potential improvements, we’re fairly certain that this tas doesn’t really have any of those under the current circumstances. Maybe someday ruffle will be able to emulate flash hitboxes correctly, and we can submit a new tas utilizing enemy clips. Only time will tell
Credits
Authors of this tas:
GGG502: pretty much everything
Raflikk: saved seven frames across level 21 and 29
Dado: converted inputs to libtas, clicked go
(Fraims: suggested that level 7 might be improvable, does not have any inputs)
Non-exhaustive list of people that have developed tas mods or tases for whg1 in flash:
Pzula, Steev0, Raflikk, GGG502, theme, Hsblue, Vowels, Fraims, Danicker
Special thanks:
Stephen Critoph, for creating this game
Fraims, for writing these submission notes
ikuyo: Claiming for judging.
ikuyo: Delayed while authors work on submission notes