Yellow Rabbit

Трилобит: начинаем общаться с игроком

Игра Трилобит на Lisp: взаимодействие с игроком

Продолжаем развивать игру, описанную тут, тут и тут.

Интерфейс

Вот и подошло время обзавестись небольшой системой взаимодействия с человеком. Для начала покажем доску:


;; *** UI
;; show board
(defun draw-board (board)
  (flet
    ((get-cell-char (cell)
                    (cond
                      ((eql cell *ai-cell*)    #\A)
                      ((eql cell *human-cell*) #\H)
                      (t #\.))))
    (loop
      for j below *board-height*
      for row-idx from 0 by *board-width*
      do (progn
           (fresh-line)
           (loop
             for i below *board-width*
             for idx from row-idx
             for cell = (get-cell board idx)
             do (princ (get-cell-char cell)))))
    (fresh-line)
    (loop 
      for i below *board-width*
      do (princ (code-char (+ 97 i))))))

Под доской печатаются буквенные обозначения колонок, они нам понадобятся для ввода ходов человека.


* (draw-board (new-board))

...
...
...
abc

Небольшая вспомогательная функция для вывода победителя. Победитель - это непроигравший в текущей позиции :smile:


;; announce winner
(defun announce-winner (tree)
  (fresh-line)
  (if (game-node-failp tree)
    (format t "The winner is ~a"
            (get-player-str (change-player (game-node-player tree))))
    (princ "There are no winners.")))

Полностью информация об игровой ситуации включает в себя доску и текущего активного игрока.


;; show situation
(defun print-info (tree)
  (fresh-line)
  (format t "current player = ~a" (get-player-str (game-node-player tree)))
  (draw-board (game-node-board tree)))

Попробуем на первоначальной доске:


* (print-info (game-tree (new-board) *ai-player* -1))

current player = The Evil AI
...
...
...
abc
NIL
*

Обработка хода человека проста: предлагается ввести число соответствующее буквенному обозначению колонки. Заполненные колонки не предлагаются для выбора. Правда не проверяется корректность данных, но для наших целей это не критично.


;; handle human
(defun handle-human (tree)
  (fresh-line)
  (princ "choose your move:")
  (let ((moves (game-node-moves tree)))
    (labels ((print-moves (lst n) 
                          (unless (null lst)
                            (let* ((move (car lst))
                                   (action (code-char (+ 97 (mod (car move) *board-width*)))))
                              (fresh-line)
                              (format t "~a. ~a" n action)
                              (print-moves (cdr lst) (1+ n))))))
      (print-moves moves 1))
    (fresh-line)
    (cadr (nth (1- (read)) moves))))

Главный цикл

Самая важная функция программы. Даёт возможность ходить компьютеру и человеку по очереди, проверяя на победу.


;; main loop
(defun play (tree)
  (print-info tree)
  (if (game-node-failp tree)
    (announce-winner tree)
    (if (eq *ai-player* (game-node-player tree))
      (play (handle-computer tree))
      (play (handle-human tree)))))

Полностью небольшая партия:


* (play (game-tree (new-board) *ai-player* -1))

current player = The Evil AI
...
...
...
abc
current player = Human
...
...
A..
abc
choose your move:
1. a
2. b
3. c
1
current player = The Evil AI
...
H..
A..
abc
current player = Human
...
H..
AA.
abc
choose your move:
1. a
2. b
3. c
1
current player = The Evil AI
H..
H..
AA.
abc
current player = Human
H..
HA.
AA.
abc
choose your move:
1. b
2. c
2
current player = The Evil AI
H..
HA.
AAH
abc
current player = Human
HA.
HA.
AAH
abc
The winner is The Evil AI
NIL

Компьютер выиграл :frowning: Хотя AI играет довольно странно, он всё равно выиграл.