Your problem is a little different than the standard "maze generation" algorithms, because you want to allow cycles in the path, just no cycles in the walls, and I think this is an important part of the game.
So, I would solve this using a variant of Kruskal's algorithm that satisfies this requirement.
Level 1
- Initialize a disjoint set data structure. Make a set for every cell. Whenever we fill a cell with wall, we will merge its set with the sets of all adjacent filled cells (including diagonal neighbors).
- Fill in all the border cells and merge their sets as indicated above.
- Make a list of all the unfilled cells
- Repeat the following until the list of unfilled cells is empty:
- Choose an unfilled cell from the list at random.
- Check the sets of its 8 neighbors. If any two of them are in the same set, then filling this cell would create a wall cycle. Discard it.
- Otherwise, fill the cell, merging its set with its filled neighbors, and go back to step 4.
When you're done, you will have a pretty dense maze -- it will be impossible to fill any empty cell without creating a wall cycle. If you want a bit more space, you can remember and undo the last fills, or you can just stop filling after a certain number of cells are filled.
Level 2
Instead of using just one list of unfilled cells, divide them into buckets based on the pattern of their neighbors -- eight neighbors filled or unfilled makes 256 possible neighbor patterns.
Then, instead of choosing from the whole bunch randomly, assign different weights to each pattern and assign cells in that bucket a different probability.
This gives you a lot of power to adjust the character of the mazes you create until you find one that's right for you. Maybe you want avoid filling cells adjacent to walls, because that makes your maze too blocky. Maybe you want to prefer filling cells that continue the end of an existing path. Maybe you want to avoid filling cells that make diagonal connections. You can play with the weights you want until you get mazes you like.
I've done a similar thing with more traditional mazes here. Try adjusting the weights.
Note that this algorithm is very fast, with or without level 2. There is no backtracking/retrying, and operations on the disjoint set structure are effectively constant time, which makes the whole thing pretty much O(n)