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

好的,我即将完成我的最新项目,一个在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 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
    ing
    board 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不是一个数字。
    当输入一个数字作为选项时:(我不知道是什么坏了。