Hello,
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.
-David Christiansen
David Christiansen chri0665@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