Universität Ulm, Abteilung Künstliche Intelligenz
Projekt: Tic-Tac-Toe CLIM
Lisp-Program zum Projekt Tic-Tac-Toe CLIM -- unvollständig, zur weiteren Bearbeitung
;;; To make things easier we load all symbols into
;;; the CLIM-USER package
(in-package :clim-user)
;;; loading the tictactoe code:
;;; this file should hold the tictactoe game code
;;; remember to change the in-package of the file `tic-prog' to (in-package :clim-user)
(load "tic-prog")
;;; tic-prog.lisp should be the filename of the tictactoe game code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Defining some globals
;;; the size of a ttt-cell on the screen in units
(defconstant *cell-size* 20)
;;; the thickness of the stone-lines on the screen in units
(defconstant *line-thickness* 3)
;;; the thickness of the grid-lines on the screen in units
(defconstant *grid-line-thickness* 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; utility functions
;;; this function should clear the application frames 'display pane
;;; and should call the display function 'draw-tictactoe
(defun redraw-tictactoe (frame)
;; ...
)
;;; here the actual-state slot of the application-frame should be initialized
;;; to point to a game state of the global game tree
(defun start-game (frame)
;; ...
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Defining the application
;;; this should be the application frame, with some globals as slots
(define-application-frame tictactoe ()
;; 4 slots: graphic-zoom-factor to scale the screen output
;; actual-state points to the actual state of the game
;; when the game is running, else nil
;; human-player: one of 'a 'b. Who starts the game?
;; human-sign: one of 'croos 'circle. Who has which stones?
((graphic-zoom-factor :initform 4
:accessor tictactoe-graphic-zoom-factor)
(actual-state :initform nil
:accessor tictactoe-actual-state)
(human-player :initform 'b
:accessor tictactoe-human-player)
(human-sign :initform 'cross
:accessor tictactoe-human-sign))
;; ...
)
;;; initializing our game tree before opening the application
(defmethod run-frame-top-level :before ((frame tictactoe) &key)
;; ...
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; we need a presentation type to present our stones/cells
;;; a cell is identified by the integer value of its position in
;;; the play field
(define-presentation-type tictactoe-cell ()
:inherit-from '(integer 0 8))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; application commands
;; the user can place stones (not a menu command!)
(define-tictactoe-command com-place-stone ((cell 'tictactoe-cell))
;; set the local state:
(let ((actual-state (tictactoe-actual-state *application-frame*)))
;; first checking the users move:
(setf actual-state
(find (put-player-on-field (state-field actual-state)
(tictactoe-human-player *application-frame*)
cell)
(state-next-states actual-state)
:test #'(lambda (x y) (equal x (state-field y)))))
;; now make the AI move:
(when (state-next-states actual-state)
(setf actual-state
(my-favorite-state
(state-next-states actual-state)
(adversary (tictactoe-human-player *application-frame*)))))
;; write the local state back to the frame
(setf (tictactoe-actual-state *application-frame*)
actual-state)))
;; we want to translate clicks into place-stone commands
;; but only the empty fields are clickable
(define-presentation-to-command-translator place-stone
(tictactoe-cell com-place-stone tictactoe
:documentation "Place Stone"
:tester ((object)
(and
;; is the cell empty?
(eq 'u (nth object (state-field (tictactoe-actual-state *application-frame*))))
;; when there are no more moves the user can't select anything
(state-next-states (tictactoe-actual-state *application-frame*)))))
(object)
(list object))
;; starting the game
;; define here an application command menu to start a new game of tictactoe
;; ...
;; terminating the game
;; define here an application command menu to exit the application
;; ...
;; the user can set some options
;; define here an application command menu to bring up the setup dialog
;; (inside this the (later defined) 'tictactoe-setup-dialog function
;; should be called)
;; ...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; The drawing functions:
;;; draw the grid around the game cells
(defun draw-grid (frame pane)
;; draw the game grid on the pane stream
;; something like this:
;; | |
;;---|---|---
;; | |
;;---|---|---
;; | |
;; ...
)
;;; Drawing the stones:
;; all stones are parametrized by a scaling factor. Scaling could also be done
;; be the clim drawing option 'scaling'.
;; we need to draw this empty stone, to enable empty fields to be clicked by th
;; user.
(defun draw-empty-stone (pane size-factor)
(let ((xy-length (* *cell-size* size-factor)))
(draw-rectangle* pane
0 xy-length
xy-length 0
:ink +white+)))
(defun draw-cross-stone (pane size-factor)
;; draw a red cross using the global *cell-size* to get the size right
;; ...
)
(defun draw-circle-stone (pane size-factor)
;; draw a blue circle using the global *cell-size* to get the size right
;; ...
)
;;; draw the complete ticactoe pane
(defmethod draw-tictactoe ((frame tictactoe) stream &key max-width max-height)
;; first draw the grid:
(draw-grid frame stream)
;; when the game is running draw also the rest:
(when (tictactoe-actual-state frame)
;; the game is running, and we display the state:
(let ((actual-field (state-field (tictactoe-actual-state frame))))
(formatting-table (stream :x-spacing 0 :y-spacing 0)
(dotimes (row *field-length*)
(formatting-row (stream)
(dotimes (column *field-length*)
(let* ((value (nth (coord row column) actual-field))
(cell-id (coord row column)))
(formatting-cell (stream :align-x :center
:align-y :center
:min-width (* *cell-size* (tictactoe-graphic-zoom-factor frame))
:min-height (* *cell-size* (tictactoe-graphic-zoom-factor frame)))
(with-output-as-presentation
(stream cell-id 'tictactoe-cell)
(case value
(u (draw-empty-stone stream (tictactoe-graphic-zoom-factor frame)))
(a (if (eq (tictactoe-human-sign frame)
'circle)
(draw-cross-stone stream (tictactoe-graphic-zoom-factor frame))
(draw-circle-stone stream (tictactoe-graphic-zoom-factor frame))))
(b (if (eq (tictactoe-human-sign frame)
'circle)
(draw-circle-stone stream (tictactoe-graphic-zoom-factor frame))
(draw-cross-stone stream (tictactoe-graphic-zoom-factor frame)))))))))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; the setup dialog
(defun tictactoe-setup-dialog (frame stream)
;; we need to hold the orginal setup because the user can cancel the operation
;; and we then should restore the orginal state
(let* ((org-graphic-zoom-factor (tictactoe-graphic-zoom-factor frame))
(graphic-zoom-factor org-graphic-zoom-factor)
(org-human-player (tictactoe-human-player frame))
(human-player org-human-player)
(org-human-sign (tictactoe-human-sign frame))
(human-sign org-human-sign))
(restart-case
(progn
(accepting-values (stream :own-window t :label "Setup"
:align-prompts :left)
(setq graphic-zoom-factor
;; replace this default value 4 below by an call to
;; to the macro accept, to generate another control.
;; This control should be a slider.
;; The user can select the graphic scaling size between 1 and 6
;; ....
4)
(setf (slot-value frame 'graphic-zoom-factor) graphic-zoom-factor)
(redraw-tictactoe frame)
(terpri stream)
(setq human-sign
(accept `((completion (circle cross))
:name-key ,#'(lambda (x) x)
:printer ,#'(lambda (object stream)
(if (eq object 'circle)
(draw-circle-stone stream 1)
(draw-cross-stone stream 1))))
:view '(radio-box-view
:orientation :vertical)
:stream stream
:query-identifier 'human-sign
:default human-sign
:prompt "Human Sign"))
(setf (tictactoe-human-sign frame) human-sign)
(redraw-tictactoe frame)
(terpri stream)
(setf human-player
(accept `((completion (a b))
:name-key ,#'(lambda (x) (if (eq x 'a)
"Human"
"AI")))
:view '(radio-box-view
:orientation :vertical)
:stream stream
:default human-player
:query-identifier 'human-player
:prompt "Starting Player")))
(unless (eq human-player org-human-player)
(setf (tictactoe-human-player frame) human-player)
;; restart game because the human-player has changed:
(start-game frame)
))
;; if the user selected the cancel Button, then nothing changes:
(abort ()
(setf (tictactoe-graphic-zoom-factor frame) org-graphic-zoom-factor)
(setf (tictactoe-human-sign frame) org-human-sign)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; the toplevel function
;;; remember to type (in-package :clim-user) in the listener, before calling this function!
(defun tictactoe ()
;; this function should create an instance of our tictactoe-application-frame using a new
;; lisp subprocess (as described in the lecture)
;; ...
)
M.
Luther ,1.Okt. 1998