A great inspirational thread, thanks. Add a few sensor modules more and you have a great sensor subsystem capable of doing actual localization and mapping and obstacle avoidance! If you have a well working and calibrated gyro running, to give you a heading that doesn't drift on a small time scale (seconds), you may even implement a "drunk mode" which scans the environment by oscillating the heading while moving, filling the dead angles of the sensors.
For routeplanning, I have good experience in implementing A*, or more specifically, the theta* variant, with added twist of testing whether the robot can turn, in addition to line-of-sight tests. If your robot isn't round and can hit obstacles during turning, this is of course important. For a robot that is bigger than one "pixel" in the grid, and can possibly turn, I found out that a good optimization is to use a 1-bit lookup table for the robot shape, for example, for a 32*32 pixel robot area, rotated in 16 different directions would be const uint32_t shape_lut[16][32] and accessed like shape_lut[direction][y] |= (1<<x). Now you can do comparisons to your map with a single bitshift and single AND operation, and compare 32 spots at once. On my theta* routeplanning, this optimization alone brought around 20x speedup IIRC.
I don't know if A* or Theta* is the most efficient or simplest for a tight and complex maze, it probably isn't; however it works great for real-world routeplanning where following a wall tightly would result in a long and stupid route, and it's simple to implement, the pseudocode widely available by googling actually works.
But routefinding and solving the maze is always the easy problem, by orders of magnitude, when you compare it to the problem of creating an accurate map with your sensor data and localizing in it. If you want to work on the maze solving first, make those corridors wider than 1 pixel, use some non-zero dimensions for the robot, and add some "noise" so that the walls are jagged.