As far as I can tell, this solution is correct, but is simply too deep. I think there are two possible solutions to this problem:
- Simply increase max-lisp-eval-depth for the section of code that will run here. This would work, but I'd have to calculate it dynamically based on the minefield size. Googling has indicated that increasing it is not a problem, but I don't know whether there is a higher limit I should be worried about, or whether this is a bad idea for another reason. I would prefer to do this, as I believe my code is concise and straightforward as it is.
- Rewrite the code to simulate recursion -- creating a stack, marking the current cell as revealed, and if it wasn't previously revealed and it is zero, pushing the neighboring cells onto it, and looping. The downside here is that the code would be more complex and less elegant.
The offending code is as follows:
Code: Select all
(defun minesweeper-pick (x y &optional suppress-field) ;; Bug is here -- recursion is too deep: blows past max-list-eval-depth if you hit a sparse section of the field.
"Select the square at position (x, y) to reveal. A user-facing function."
(unless (minesweeper-is-revealed x y) ;; If (x, y) has already been picked, do nothing
(let ((val (minesweeper-view-mine x y 't)))
(if (eq val ?X) ;; the user has picked a mine.
(minesweeper-lose-game x y)
(progn
(minesweeper-set-revealed x y 't)
(when (eq val ?0)
(minesweeper-pick-around x y)) ;; Here's the mutual recursion that is the problem.
(if (eq minesweeper-mines-left 0)
(mineweeper-win-game)
(unless suppress-field
(minesweeper-print-field))))))))
(defun minesweeper-pick-around (x y)
"Pick all the squares around (x, y). As a precondition, (x, y) should be zero."
(mapcar '(lambda (position)
(minesweeper-pick (car position) (cadr position) 't))
(minesweeper-neighbors x y))) ;; minesweeper-neighbors returns a list of all the valid neighbors of (x, y)