;;;; -*- mode:Lisp; syntax:Common-Lisp; package:user -*- ;;;;
;;;; Copyright 1992 Patrick H. Winston.  All rights reserved.
;;;; Version 1.1.1, copied from master file on 23 Apr 93       
;;;; 
;;;; This software is licensed by Patrick H. Winston (licensor) for
;;;; instructional use with the textbooks ``Artificial Intelligence,'' by
;;;; Patrick H. Winston, and ``Lisp,'' by Patrick H. Winston and Berthold
;;;; K. P. Horn.  Your are free to make copies of this software and
;;;; modify it for such instructional use as long as:
;;;; 1. You keep this notice intact.
;;;; 2. You cause any modified files to carry a prominent notice stating
;;;;    that you modified the files and the date of your modifications.
;;;; This software is licensed ``AS IS'' without warranty and the licensor
;;;; shall have no liability for any alleged defect or damages.

;;;; REMARKS

#|
Works on a nest list of numbers, which simulates a game tree.
Thus, there is no specific game involved.  The virtue is that
you can experiment with the procedure on any tree you like.
|#

;;;; ALPHA-BETA PROCEDURE

(defun alpha-beta (top-level-function root-node)
  "
  Purpose:	Alpha-beta search.
  Arguments:	Minimizer or maximizer function, the game tree.
  Returns:	Nothing.
  Remarks:	MINIMIZER and MAXIMIZER do all the work.
  "
  (format t "~%~%Starting alpha-beta search ...")
  (multiple-value-bind (score path)
      (funcall top-level-function root-node -10000 +10000 0 nil)
    (format t "~%The winning score is ~a, ~
	         found at the end of ~a." score path)
    (values)))

(defun maximizer (node alpha beta depth path)
  "
  Remarks:	Much like MINIMIZER.
  "
  (let* ((children (children node)) (best-path nil))
    (if (endp children)
	;;If there are no children, report the static value:
	(values (score node) path)
      ;;Otherwise, check out the children; return alpha and the best path:
      (dotimes (n (length children) (values alpha best-path))
	(let ((child (nth n children)))
	  (multiple-value-bind (test-score test-path)
	      (minimizer child alpha beta (1+ depth) (append path (list n)))
	    ;;Determine if the child is the best so far:
	    (when (> test-score alpha)
	      ;;Reset alpha:
	      (setf alpha test-score)
	      ;;Reset the best path:
	      (setf best-path test-path)))
	  ;;Check for cutoff condition:
	  (when (and (>= alpha beta) (< n (1- (length children))))
	    (format t "~%Maximizer alpha-beta cutoff, depth ~a, path ~a."
		    depth path)
	    (return alpha)))))))

(defun minimizer (node alpha beta depth path)
  "
  Remarks:	Much like MAXIMIZER.  See MAXIMIZER for comments.
  "
  (let* ((children (children node)) (best-path nil))
    (if (endp children)
	(values (score node) path)
      (dotimes (n (length children) (values beta best-path))
	(let ((child (nth n children)))
	  (multiple-value-bind (test-score test-path)
	      (maximizer child alpha beta (1+ depth) (append path (list n)))
	    (when (< test-score beta)
	      (setf beta test-score)
	      (setf best-path test-path))
	    (when (and (>= alpha beta) (< n (1- (length children))))
	      (format t "~%Minimizer alpha-beta cutoff, depth ~a, path ~a."
		      depth path)
	      (return beta))))))))

;;;; ACCESS PROCEDURES

(defun children (node) (if (listp node) node nil))

(defun score (node) (if (listp node) (error "No score available") node))

