Dice of Doom, Version 2

In Chapter 15, we created the first version of our Dice of Doom game. We are now going to modify some of the functions from that version. To proceed, place the code from that chapter into a file named dice_of_doom_v1.lisp so that we can reference it in this new version (or just download that file from http://landoflisp.com/).

To use our previous Dice of Doom and our new lazy list library, run the following in the REPL:

> (load "dice_of_doom_v1.lisp")
> (load "lazy.lisp")

Next, we’re going to increase the size of our board to a more roomy 4-by-4:

> (defparameter *board-size* 4)
> (defparameter *board-hexnum* (* *board-size* *board-size*))

To allow the game to run at a reasonable speed at this larger size, we’ll make the list of moves at each branch of our game tree a lazy list, instead of just a regular list. By simply converting this one structure in our game from a regular list to a lazy list, the entire game tree will become lazy as a result. To accomplish this, we now need to redefine some of the functions from the first version of our game to use our new lazy list functions.

First, let’s make some small modifications to the functions that calculate the attacking and passing moves possible from a given board position:

(defun add-passing-move (board player spare-dice first-move moves)
    (if first-move
        moves
       (lazy-cons (list nil
                         (game-tree (add-new-dice board player
                                                  (1- spare-dice))
                                    (mod (1+ player) *num-players*)
                                    0
                                    t))
                   moves)))

  (defun attacking-moves (board cur-player spare-dice)
    (labels ((player (pos)
          (car (aref board pos)))
       (dice (pos)
           (cadr (aref board pos))))
     (lazy-mapcan
       (lambda (src)
         (if (eq (player src) cur-player)
            (lazy-mapcan
              (lambda (dst)
                (if (and (not (eq (player dst)
                                  cur-player))
                         (> (dice src) (dice dst)))
                   (make-lazy
                     (list (list (list src dst)
                                 (game-tree (board-attack board
                                                          cur-player
                                                          src
                                                          dst
                                                          (dice src))
                                            cur-player
                                            (+ spare-dice (dice dst))
                                            nil))))
                 (lazy-nil)))
             (make-lazy (neighbors src)))
          (lazy-nil)))
      (make-lazy (loop for n below *board-hexnum*
                        collect n)))))

As you can see, the add-passing-move function needs only one small change. Since the list of moves is now a lazy list, we use lazy-cons to add a passing move to the top of the list of possible moves .

The attacking-moves function requires a few more changes. First, since it now needs to return a lazy list, we use lazy-mapcan in lieu of mapcan in two places as the moves are calculated . The lazy-mapcan function also requires the lists created inside it to be lazy, which we accomplish with the make-lazy function . Also, any place we returned nil we now instead return a lazy-nil . Finally, we also make the list of calculated board positions lazy , since it is fed into the outer lazy-mapcan.

Next, let’s make similar changes to two of the functions that deal with human players:

(defun handle-human (tree)
    (fresh-line)
    (princ "choose your move:")
    (let ((moves (caddr tree)))
      (labels ((print-moves (moves n)
                     (unless (lazy-null moves)
                       (let* ((move (lazy-car moves))
                           (action (car move)))
                          (fresh-line)
                          (format t "˜a. " n)
                          (if action
                            (format t "˜a -> ˜a" (car action) (cadr action))
                           (princ "end turn")))
                       (print-moves (lazy-cdr moves) (1+ n)))))
            (print-moves moves 1))
      (fresh-line)
     (cadr (lazy-nth (1- (read)) moves))))

  (defun play-vs-human (tree)
    (print-info tree)
   (if (not (lazy-null (caddr tree)))
        (play-vs-human (handle-human tree))
      (announce-winner (cadr tree))))

In the handle-human function, we have a local function print-moves, which is a list-eater function across the list of moves. We modify it to use our lazy commands when checking for the end of the list , taking a move off the front of the list , and recursing across the tail of the list . Finally, we modify handle-human to use lazy-nth to pick a move after the human chooses it from the list of options .

In the play-vs-human function, we make just a single pinpoint change. In order to determine whether we’ve reached the end of a game, we need to check whether the list of subsequent possible moves is empty, and then announce the winner. We simply use lazy-null to check if the lazy list of moves is empty .

With these simple changes in place, you can play Dice of Doom against another human on much larger board sizes, since no move in the tree is realized unless one of the players decides to make it. On our larger, 4-by-4 board, enter the following to start a game (just as for version 1 of our game):

> (play-vs-human (game-tree (gen-board) 0 0 t))
current player = a
        a-1 a-3 a-1 b-2
      b-3 a-3 a-3 a-1
    a-3 a-3 b-1 a-2
  b-3 a-3 a-1 a-3
choose your move:
1. 5 -> 10
2. 6 -> 10
3. 9 -> 10
4. 11 -> 10
5. 15 -> 10

Version 1 would screech to a halt the moment this command was executed. This is because it would need to generate the entirety of the game tree, for every possible move of the whole game, before the game would even start playing.

With our lazy version of Dice of Doom, the game starts instantly!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.136.186