Продолжаем развивать игру, описанную тут, тут и тут.
Вот и подошло время обзавестись небольшой системой взаимодействия с человеком. Для начала покажем доску:
;; *** 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
Небольшая вспомогательная функция для вывода победителя. Победитель - это непроигравший в текущей позиции
;; 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
Компьютер выиграл Хотя AI играет довольно странно, он всё равно выиграл.