Pygame 2D Platformer

As a final project for my Python class in college, I was tasked to make pretty much anything I wanted using Python.
Given that I have been interested in game development ever since the very first time I learned to code, I decided to try and make a game.
My assignment had some restrictions, essentially making it so that I had to use the Pygame library in order to make the game.
There are arguably better and newer libraries to make games with Python, but Pygame ended up being an interesting challenge given its restrictions.

I decided that given Pygame's methods, it was easiest to make a 2-dimensional platformer.
The entire game is in a single file. You can view the source code by clicking the button below.

The game is very simple: your goal is to get to the green block in order to win. There are enemies that can hurt you, you have health, and lose the game if it reaches zero.
The player also has a weapon to defeat enemies; you can fire a magic bolt of your own by pressing B.

A gif of the gameplay in action
The gif above doesn't really do the game justice; Pygame actually runs pretty smooth, but is surprisingly demanding on your computer(given how "slow" Python is).
The complicated part of this project, and the system I'm most proud of, is the data structures responsible for the levels themselves.
Normally in Pygame, you could manually define each retangle and where they are, creating a level.
However, I wanted a system that was modular, easily expandable, and easy to edit levels themselves.
I split up the hard-coded screen size into a grid, and then created a 2-dimensional array of chars to hold a "block type" for each grid space.
Then, by assigning each type of block to a char value, I could easily edit this array to change the level as I wanted.

The level editor
In the screenshot above you can see this in action. I made some shortcuts for some rows (the "list(Level.fR)" to make a full row of bricks), but the rest is the level itself.
All it takes is to change a single character and a grid could have an added block, an enemy, or the functional blocks shown in the gif.
The game is then strung together with multiple levels, with logic coded to move the player to the other side of the screen when they reach the edge. This gives the illusion that the player is moving to different rooms.
Additonally, these levels could be edited at runtime: the blue blocks or enemies that are destroyed, for example, save their states if you leave and return to the room. This is because the level itself is edited when these actions take place.