Thanks for posting the source version. It's nice to see a game posted here that I can run on Linux. It's always really disappointing when someone someone posts something I want to try and it's windows-only. Your code is also really legible, especially compared to some other pygame projects I've read in the past.
I also have a suggestion to improve the fluidity of the movement code: the "requires perfect alignment when going through gaps" seems to have been solved in a couple of different ways (that I have noticed) in commercial 2D games. One way is by using tile based movement, but tile based movement sucks unless you're making a puzzler or RPG and sometimes it even sucks then. The way I like better for pixel-based movement is that the game detects if a player has just missed a gap and sort of pulls them sideways at about one pixel per frame until the hitbox is aligned so that they can go through. It tends to trigger when about half your hitbox or so is over the gap, which sort of looks natural. You can find it in a lot of the
2D Zeldas, and it's easy to imagine that they would feel much clunkier without this minor detail. For example, near the start of that video where the player rolls into the corner slightly, if the game did not autocorrect his movement he would have just stopped awkwardly because the game doesn't allow direction change / influence during a roll.
I was actually going to suggest code to implement this in 'World.py' (particularly as a proof of concept to myself that this really does improve the way movement feels) but to tell the truth I'm stuck as to how gap detection would best be done. On the one hand, you could write a function that just iterates all walls and checks if there's an adjacent wall to the one you're colliding with in the direction you're moving in, and then returns whether it's above, below, left or right of you, or whether there isn't one. This seems kind of wasteful, though. It requires an extra O(n) on the number of walls every time you collide, and the code hardly sounds elegant. On the other, you could have each wall store the walls above, below, left and right of it as a kind of 'linked grid', but this seems like unnecessary bulk added to the walls class.
So yeah, sorry for providing you with a big long description of the suggestion for such a minor detail and then not helping at all on the implementation. I thought it might be useful to post anyway because I think a lot of people don't know about this whole auto-cornering thing and if my theory about how it works is correct then it might help more than one game be less clunky.