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