要解决实际问题,您需要使用
LET
而不是
SETQ
以定义局部变量。特别是在
NEGAMAX
功能:
(defun negamax(board player depth)
(cond ((deep-enough board player)
(cons (evaluate board player depth) board))
(t (let ((best-value -10)
(best-path nil)
(successors (generate-moves board player)))
(loop for successor in successors
do (let* ((result (negamax successor (opposite player) (+ depth 1)))
(value (- (first result))))
(when (> value best-value)
(setf best-value value)
(setf best-path successor))))
(cons best-value best-path)))))
代码还有其他问题。我会专注于
PLAY-AI
这里将保持简短,但这些也适用于代码的其余部分。
-
使用单词之间的破折号而不是camelCase的常规Lisp约定命名变量。就像你处理函数一样。
-
中有重复代码
COND
(所有内容来自
(PRINT-BOARD ...)
). 你应该把它移到
条件
.
-
你应该把它分成更小的函数。至少使用一个单独的函数要求玩家输入。
-
IMO,使用起来会更干净
LOOP
而不是
DO
在这里或者更好,使用
iterate
.如果你有
quicklisp
设置,你可以
(ql:quickload :iterate)
(use-package :iterate)
-
结束消息(“xwon”、“draw”)可以移动到循环之外或在另一个函数中完成。
-
你要检查每一个玩家在游戏循环的每一次迭代中是否赢得了游戏。因为只有一个玩家移动,所以检查该玩家是否获胜就足够了。而且
DRAW-P
不需要检查其中一个玩家是否获胜,因为在呼叫之前会进行检查
拉P
无论如何
-
列表的随机访问效率非常低,因此最好为电路板使用数组。我没有修复这个问题,因为它需要修改大部分代码。
下面是一个使用iterate进行了一些清理的版本:
(defun ask-move (board player)
(format t "~&Enter move for ~a's: " player)
(iter (for move = (make-move board player (read)))
(when move (return move))
(format t "~&Illegal move. Try again: ")))
(defun game-loop ()
(let ((player 'x)
(board (list nil nil nil nil nil nil nil nil nil)))
(print-board board)
(iter (setf board (if (eq player 'x)
(ask-move board player)
(rest (negamax board player 1))))
(print-board board)
(cond ((won-p board player) (return player))
((draw-p board) (return :draw)))
(setf player (opposite player)))))
(defun play-ai ()
(case (game-loop)
(x (format t "~&Player wins!"))
(o (format t "~&AI wins!"))
(:draw (format t "~&Draw!"))))
或者与常规循环相同。差别不大,但iterate的语法稍好一些。
(defun ask-move (board player)
(format t "~&Enter move for ~a's: " player)
(loop
for move = (make-move board player (read))
when move do (return move)
do (format t "~&Illegal move. Try again: ")))
(defun game-loop ()
(let ((player 'x)
(board (list nil nil nil nil nil nil nil nil nil)))
(print-board board)
(loop
do (setf board (if (eq player 'x)
(ask-move board player)
(rest (negamax board player 1))))
do (print-board board)
when (won-p board player) do (return player)
when (draw-p board) do (return :draw)
do (setf player (opposite player)))))
(defun play-ai ()
(case (game-loop)
(x (format t "~&Player wins!"))
(o (format t "~&AI wins!"))
(:draw (format t "~&Draw!"))))