【问题标题】:Alpha-beta pruning in Common LispCommon Lisp 中的 Alpha-beta 修剪
【发布时间】:2018-04-17 18:51:13
【问题描述】:

我尝试使用维基百科中的伪代码对 Alpha-beta 进行编码。程序到达(EQ depth 0) 后,它会返回启发式值,但深度会继续减小,从而导致循环。现在我的代码如下所示:

(defun ab(tab node depth a b)
(cond ((EQ depth 0) (calculaH tab))
        ((eq (mod depth 2) 0) (setq v -999999) (setq movimiento (sigMov depth node tab))  (loop while (not(null movimiento))  
                                                        do (setq v (max v (ab (nth 3 movimiento) movimiento (- depth 1) a b)))
                                                           (setq a (max a v))
                                                           (cond((<= b a) (break))
                                                                (t (setq movimiento (sigMov depth movimiento tab))))) (return v))

        (t (setq v 999999) (setq movimiento (sigMov depth node tab)) (loop while (not(null movimiento))   
                                                        do (setq v (min v (ab (nth 3 movimiento) movimiento (- depth 1) a b)))
                                                           (setq a (min b v))
                                                           (cond((<= b a) (break))
                                                                (t (setq movimiento (sigMov depth movimiento tab))))) (return v))))

我应该在代码中的某处增加深度值吗?为什么递归不自己增加值?

【问题讨论】:

  • Jorge:问题在于您的代码难以阅读和理解。它的格式不可读,并且充满了未定义的变量。谁知道它在做什么以及为什么以及何时会偶然起作用。
  • 也许您想使用Portacle ?它是一个多平台和可移植的 CL 环境(包含 Emacs、SBCL、Slime、Quicklisp 和 Git),因此它会自动正确缩进您的代码。
  • 欢迎来到 StackOverflow。请按照您创建此帐户时的建议阅读并遵循帮助文档中的发布指南。 Minimal, complete, verifiable example 适用于此。在您发布 MCVE 代码并准确描述问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您描述的问题。

标签: artificial-intelligence common-lisp minimax alpha-beta-pruning


【解决方案1】:

维基百科上的alpha-beta prunning algorithm 几乎可以按原样翻译成 Lisp。因为它使用无限值​​,所以我们不要乱用“999999”,而是定义minmax 函数,它们可以可靠地使用这些特殊值:

(defpackage :alphabeta
  (:use :cl)
  ;; custom min/max functions that support infinity
  (:shadow min max))

(in-package :alphabeta)

(defvar -∞ '-∞ "Negative infinity symbol")
(defvar +∞ '+∞ "Positive infinity symbol")

(defun min (a b)
  (cond
    ((eql a +∞) b)
    ((eql b +∞) a)
    ((eql a -∞) -∞)
    ((eql b -∞) -∞)
    (t (cl:min a b))))

(defun max (a b)
  (cond
    ((eql a -∞) b)
    ((eql b -∞) a)
    ((eql a +∞) +∞)
    ((eql b +∞) +∞)
    (t (cl:max a b))))

代码还依赖于辅助函数,我在这里声明以避免警告:

 ;; You need to implement the followning functions
(declaim (ftype function terminal-node-p heuristic-value children))

然后,可以编写几乎相同的伪代码。为了这个问题,我保留了相同的希腊变量,但正如丹·罗伯逊在 cmets 中指出的那样,这可能会导致意外:

在使用像 α 或 β 这样的名称时要注意的一点是,典型的 Unicode 感知 lisp 实现会将它们大写为 Α 和 Β。你能分辨出 A 和 Α 或 B 和 Β 的区别吗?

(defun alphabeta (node depth α β maximizing-player-p)
  (when (or (= depth 0) (terminal-node-p node))
    (return-from alphabeta (heuristic-value node)))
  (if maximizing-player-p
      (let ((value -∞))
        (dolist (child (children node))
          (setf value (max value (alphabeta child (1- depth) α β nil)))
          (setf α (max α value))
          (when (<= β α)
            ;; β cut-off
            (return)))
        value)
      (let ((value +∞))
        (dolist (child (children node))
          (setf value (min value (alphabeta child (1- depth) α β t)))
          (setf α (min α value))
          (when (<= β α)
            ;; α cut-off
            (return)))
        value)))
  • 切勿将数字与EQ 进行比较。如果您希望只比较数字,请使用 =

  • 始终使用let 引入局部变量,而不是setq 是当前范围内未定义的变量。您的代码失败,因为您的 Lisp 实现在您第一次在未绑定符号上调用 setq 时定义了全局变量。之后,您在递归代码中更改全局变量,这使其功能失调。

  • 不要有过长的行(这在大多数语言中都是如此),正确缩进,将每个新表单从同一个缩进开始放在自己的行上。

    李>
  • Lisp 中的BREAK进入调试器。如果您想提前退出循环,请使用RETURN(这是因为像DO 这样的迭代构造引入了名为nil 的隐式BLOCKs)。

【讨论】:

  • 在使用αβ 之类的名称时要注意的一点是,典型的Unicode 感知lisp 实现会将它们大写为ΑΒ。你能分辨出AΑBΒ 之间的区别吗?
  • @DanRobertson 你是对的,我会在答案中留言。谢谢。
猜你喜欢
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多