[mcclim-devel] A mcclim app which could possibly be used as a demo

Christophe Rhodes csr21 at cam.ac.uk
Sat Aug 20 10:21:55 UTC 2005


David Christiansen <chri0665 at uidaho.edu> writes:

> For my AI class at the University of Idaho, I wrote a program that  
> will play Connect 4 against you.  I used McCLIM, and just tested it  
> against the CVS version.  It demonstrates drawing, presentation-to- 
> command translators, and a few other things.  I've also commented it  
> a great deal, as Lisp was not the first language of the TA who graded  
> my work.  The file is attached to this email, in case you'd like to  
> use it as example code for McCLIM.  I'm fairly new to Lisp, so parts  
> may be sub-optimal.  If they are, please let me know and I'll improve  
> them.

This is cool.  I'll comment a little bit on small elements of Lisp
style below (without having run the program yet, which is a bit
dangerous :-), but before I do that I'd quite like to draw your
attention to
  <http://www.cl.cam.ac.uk/~pz215/papers/connect4.pdf>
which is mostly a discussion about how to subvert online tournaments,
but also contains some discussion about the "closed-form" solution of
Connect 4 and a reference to the solution by Allis.

> ;;;; Connect 4 game by David Christiansen
> (defpackage connect4
>   (:nicknames :connect4)
>   (:use :common-lisp-user :clim :clim-lisp)
          ^^^^^^^^^^^^^^^^^
I can conceive no world where this is a good idea.  The contents of
CL-USER are completely undefined in general.

>   (:export run-connect4))
>
> (in-package :connect4)
>
> ;;; Establish various options prior to evaluating forms that depend on
> ;;; them.  Think of this section as being analogous to a C program's
> ;;; config.h file.
> (eval-when (:compile-toplevel :load-toplevel :execute)
>   [...]
>   (proclaim '(type (integer 0 30) *rows* *cols* *default-depth*))
>   (proclaim '(type (integer 0 50) *piece-radius*)))

You would avoid the eval-when completely by changing the two PROCLAIMs
here to DECLAIM:
  (declaim (type (integer 0 30) *rows* *cols* *default-depth*))
and 
  (declaim (type (integer 0 50) *piece-radius*))
as then all of these operators (DEFPARAMETER, DEFCONSTANT, DECLAIM)
are defined to have enough compile-time effect to ensure correct
compilation of the rest of the file.

> ;;; Convenience function to make later code more readable.
> (declaim (inline col-top))
> (defun col-top (board col)
>   "Return the index of the top piece in a column."
>   (aref (board-tops board) col))

One more subjective point: there are many ways of abbreviating things,
but only one of spelling it out in full... col-top might be better as
column-top.  (Depending on your use of it, it might be more "natural"
in lisp to use column-height instead, returning the index of the first
free space: Lisp is 0-based inclusive at the start of sequences and
exclusive at the end.  This does depend on use, though.  Do you use -1
for empty columns?)

> ;;; causes the loop to terminate and return the current score.  THIS
> ;;; MACRO IS NOT GENERAL UTILITY!  EXPANSIONS CONTAIN NASTY FREE
> ;;; VARIABLES!
> (defmacro goodness-loop ((&rest loop-keywords-for-init)
>                          current-form
>                          &optional bail-form)

One way of expressing the lack of general-purpose of a macro such as
this, which depends on various variable names being bound, is to make
it a local macro.  You could place it in a macrolet in DEFUN GOODNESS,
and then no-one would ever be tempted to use it elsewhere.

These comments aside, good stuff! :-)

Cheers,

Christophe



More information about the mcclim-devel mailing list