Lisp 检查Tic-Tac-Toe中的胜利
好的,我即将完成我的最新项目,一个在Common Lisp中实现Tic-Tac-Toe的(承认不是很好)项目(整个程序都可用),但最后一部分我被卡住了;我不知道如何让我的函数检查赢家的工作。函数(及其从属函数)如下所示:Lisp 检查Tic-Tac-Toe中的胜利,lisp,common-lisp,Lisp,Common Lisp,好的,我即将完成我的最新项目,一个在Common Lisp中实现Tic-Tac-Toe的(承认不是很好)项目(整个程序都可用),但最后一部分我被卡住了;我不知道如何让我的函数检查赢家的工作。函数(及其从属函数)如下所示: (defun check-for-win () (cond ((is-line 1 2 3) t) ((is-line 1 4 7) t) ((is-line 1 5 9) t) ((is-line
(defun check-for-win ()
(cond ((is-line 1 2 3) t)
((is-line 1 4 7) t)
((is-line 1 5 9) t)
((is-line 2 5 8) t)
((is-line 3 6 9) t)
((is-line 3 5 7) t)
((is-line 4 5 6) t)
((is-line 7 8 9) t))
nil)
(defun is-line (a b c)
(let ((a (aref *board* (- a 1)))
(b (aref *board* (- b 1)))
(c (aref *board* (- c 1))))
(if (and
(eql a b)
(eql a c)
(eql b c))
t
nil)))
(虽然缩进不太深),在
(is行)
中,a、b和c(在获胜场景中)将被设置为符号(或者:X
或者:O
)。如何使相等性检查生效?在defun
语句中有隐式progn
,因此它的计算方式如下:
检查win
中有两条语句:cond
和nil
。根据progn
求值规则,任何调用都将返回nil
的值,而cond
的结果将被忽略
请尝试以下代码:
(defun check-for-win ()
(cond ((is-line 1 2 3) t)
((is-line 1 4 7) t)
((is-line 1 5 9) t)
((is-line 2 5 8) t)
((is-line 3 6 9) t)
((is-line 3 5 7) t)
((is-line 4 5 6) t)
((is-line 7 8 9) t)
(:else nil)))
(defun winning-position-p ()
(or (cells-match-p 1 2 3)
(cells-match-p 1 4 7)
(cells-match-p 1 5 9)
(cells-match-p 2 5 8)
(cells-match-p 3 6 9)
(cells-match-p 3 5 7)
(cells-match-p 4 5 6)
(cells-match-p 7 8 9)))
(defun cells-match-p (a b c)
(and (eql (board-ref a)
(board-ref b))
(eql (board-ref a)
(board-ref c)))
(defun board-ref (cell)
;; Could check for errors here.
(aref *board* (1- cell)))
:else
只是一个关键字,与任何关键字一样,它的计算结果为true。您可以使用任何其他关键字,也可以仅使用true
。因此,如果之前没有给出真的语句,cond
(以及所有函数)的结果将是nil
,这也与Andrei的修复方法一起修复了一些其他问题
首先,调整play()功能中的逻辑流
;;; Play the game
(defun play (&optional switch-p)
(when switch-p (switch-player))
(check-choice (read-choice))
;;; Check if we should continue playing.
(when (and
(not (check-for-win))
(not (stalemate)))
(play t))
;;; Check for win FIRST (last move in possible stalemate may be a win)
(when (check-for-win)
(format t "~a has won! " *player*)
(if (y-or-n-p "Play again? ")
(play-again)
(quit)))
;;; Check for stalemate.
(when (stalemate)
(if (y-or-n-p "~%~%Stalemate! Play again? ")
(play-again)
(quit))))
第二,调整check-choice()函数
;;; Changed (> choice 1) to (> choice 0) otherwise square 1 is always invalid.
(defun check-choice (choice)
(if (and
(numberp choice)
(> choice 0)
(< choice 10))
(select choice)
(progn
(format t "~%Invalid choice.~%")
(check-choice (read-choice)))))
;;;已将(>选项1)更改为(>选项0),否则平方1始终无效。
(取消取消选中选项(选项)
(如有)(及
(数字选择)
(>选择0)
(<选择10))
(选择选项)
(项目
(格式t“~%无效选择。~%”)
(勾选选项(读选项(())))
第一部分的问题是,如果最后一步是剩下的唯一一步,并且是胜利的一步,那么程序会在胜利之前报告僵局
第二部分中的问题是,square 1始终报告无效选择,因为它不大于自身值。在CHECK-FOR-WIN中:
COND对于它的目标来说是一个糟糕的选择。思考
关于它:如果有任何一行是,您希望函数返回T
返回T,否则返回NIL。这就是定义
因此,转储COND并在中收集IS-LINE调用
单人房或单人房。你可以用一些来缩短它,但是
结果可能太“聪明”
在线
让我们把这个从里到外:首先,EQL是可传递的,所以如果您知道
(eqlab)和(eqlac),那么测试(eqlbc)是冗余的
现在,如果,是绝对不可原谅的。从字面上讲,这和做一件事是一样的
if (x)
return true;
else
return false;
用大括号语言。你已经有了你想要的真实值
返回,所以只返回那个
最后,像您正在使用的那样对变量进行阴影处理是一种不好的方式
出租汽车。无论如何,我会说,扔一个EQL
无论如何,将预计算数组引用的必要性降低到几乎为零
大体上
Common Lisp中用于命名谓词(函数
return或者T或者NIL)就是产生一个名词短语
描述他们测试的目的,并加上“p”。所以我想
WINNING-POSITION-P和CELLS-MATCH-P将是更好的名称
我认为写一个函数来获取内容可能是个好主意
与使用AREF相反的板正方形,因为后者暴露
具体实施情况。即使这是一个相对次要的问题
在这种情况下,养成一个好习惯
遵循这些建议将产生此代码:
(defun check-for-win ()
(cond ((is-line 1 2 3) t)
((is-line 1 4 7) t)
((is-line 1 5 9) t)
((is-line 2 5 8) t)
((is-line 3 6 9) t)
((is-line 3 5 7) t)
((is-line 4 5 6) t)
((is-line 7 8 9) t)
(:else nil)))
(defun winning-position-p ()
(or (cells-match-p 1 2 3)
(cells-match-p 1 4 7)
(cells-match-p 1 5 9)
(cells-match-p 2 5 8)
(cells-match-p 3 6 9)
(cells-match-p 3 5 7)
(cells-match-p 4 5 6)
(cells-match-p 7 8 9)))
(defun cells-match-p (a b c)
(and (eql (board-ref a)
(board-ref b))
(eql (board-ref a)
(board-ref c)))
(defun board-ref (cell)
;; Could check for errors here.
(aref *board* (1- cell)))
(defun winning-position-p()
(或(cells-match-p123)
(cells-match-P1 4 7)
(cells-match-P1 5 9)
(cells-match-P2 5 8)
(cells-match-P3 6 9)
(cells-match-P3 5 7)
(cells-match-P4 5 6)
(cells-match-P7 8 9)))
(defun cells-match-p(a、b、c)
(和(eql(板参考a)
(董事会参考文件b)
(eql(电路板参考a)
(委员会参考文件c)))
(拆卸板参考(单元)
可以在这里检查错误。
(aref*董事会*(1-单元)))
利用第一类函数的强大功能并消除代码重复(这也会产生修复原始问题的副作用:) 关于
setf
ingboard ref
,这种常见情况实际上相当简单
(defun (setf board-ref) (val cell)
(setf (aref *board* (1- cell)) val))
如果你在
:else nil
周围加上括号,那就行了。谢谢!非常感谢你的建设性批评。我对Lisp真的很陌生,我仍在努力用Lisp-y的方式思考,所以我真的很感谢你向我指出这些事情。:@Cirno我如何制作(setf(board ref choice)*marker*)
工作,如果你不介意的话?@安德鲁:你需要defsetf
ot。但这并不是一项简单的任务——您将不得不进入较低的抽象层——“地点”的概念。另一种方法是使用具有访问器的类和对象。我只需执行(defun set board ref(choice item)(setf(aref*board*(1-choice))item))
,然后再担心像DEFSETF表单这样复杂的事情。@Cirno我定义了该函数,并用函数调用替换了代码,但是现在我得到了参数X不是一个数字。
当输入一个数字作为选项时:(我不知道是什么坏了。