----------------------------------- Using Common Lisp with UR Quagents --------------------------------- by Tyler Green for questions or corrections please email me tgreen@cs.rochester.edu The purpose of this guide is help students who have chosen to use Common Lisp with the quagents system. The first section explains how to set up sockets so lisp and quagents communicate properly. The second section provides project specific tips and places to look for libraries to supplement your code with. Hopefully this will enable people to spend more time working on the specified problem rather than mudding around trying to just get things to work. --------------------- Section I: Sockets --------------------- First log onto a csug machine. For the purposes of this tutorial, we will use cycle3. Substitute the name of the machine you are logged into where applicable. Now start both a lisp session ( I recommend using emacs and slime) and the quagenst program. First we need to enter the ACL socket package ( in-package :acl-socket ) Next we create the socket that we will use to communicate with the quagent protocol. ( setf *qk* ( make-socket :remote-host "cycle3" :remote-port 33333 :type :stream :format :text))) The protocol will spawn a quagent after this socket/stream is opened. We can use the format function to send commands to our quagent in the form of text data over our *qk* stream. For instance, the walkby command would be issued as follows. ( format *qk* "do walkby 100~%" ) The new line character ~% is important to ensure that the command will be understood and executed properly by the quagent protocol. We now need to force lisp to output this message to our stream. This is done using the force-output function, which dumps all the waiting text onto the stream/socket. ( force-output *qk* ) Our command now should be sent to the controller and the quagent should walk forward 100 units. To receive commands we will use the listen and read-line functions. First we need to check if there is any available data on the stream. ( listen *qk* ) If this returns nil, then there is no text data to be read. Lisp will signal and error if you try to use read-line in this situation. However, if listen returns T, then there are messages from the quagent controller that are available to be read. ( read-line *qk* ) This will output a single message from the quagent controller to the standard output. ---------------------------------- Section II: Project Specifics ---------------------------------- Production Systems Instead of using JESS ( and Java ), there is a very similar extension program called LISA that is written in lisp and provides the same functionality as JESS. Once loaded, LISA is available from within the lisp so the full power of both can be exploited from the REPL. http://lisa.sourceforge.net http://lispwire.com/entry-ai-prod-lisa-des Natural Language Understanding Peter Norvig has parsers from both AIMA and Paradigms of AI Programming books available at www.norvig.com Vision To parse the image buffer taken by the look command, we need to read the response from the quagent controller a little differently because it is in binary instead of text form. First output the look command ( format *qk* "do look~%" ) ( force-output *qk* ) Assuming the stream was completely clear before this command, and the message was received properly, we now have a confirmation message followed by a large image buffer waiting on the *qk* stream to be read. The first part is in text format like the other responses, but the second part, the image buffer, is in binary form so we need to use the read-byte function instead of read-line. This function reads in a single byte at a time and is called as follows: ( read-byte *qk* ) Example Code: Drunken Master This is a minimal lisp version of the java example. Notice how much shorter and cleaner it is. ;;;;;;;;;;;;;;;;; ;; Start code here ;; Remember you will have to call spawn and drunk-walk from the REPL ;; to get things started ( in-package :acl-socket ) ( defvar *qk* ) ( defun spawn ( &optional ( server "cycle3")) "Creates a socket connection and orients the quagent properly to begin" ( setf *qk* ( make-socket :remote-host server :remote-port 33333 :type :stream :format :text))) ( defun act ( action n ) ( format *qk* "do ~S ~S~%" action n ) ( force-output *qk* ) t ) ( defun drunk-walk () ( loop ( act 'walk ( random 100 )) ( act 'turnby ( random 360 )))) ;;end code General Links lisp hyperspec http://www.lispworks.com/documentation/HyperSpec/Front/Contents.htm allegro common lisp documentation http://www.franz.com/support/documentation/6.2/doc/contents.htm common lisp wiki: general resources of all kinds http://www.cliki.net/index