A Roguelike in Common Lisp, Part 1

For the past few months, I have been dabbling in Common Lisp occasionally, and I think it would be nice to get into a meatier project to expose some gaps in my learning.  I have been inspired by Steve Losh’s Caves of Clojure series of posts to write my own roguelike game.  Steve himself follows Trystan Spangler’s series, which used Java for this purpose.

I will structure these posts at approximately a 1:1 ratio with Trystan’s original series, though I’m not likely to complete all of that series.

I will post the code for this project at https://gitlab.com/cory/cl-rogue/

Summary

In this first part, we will set up our project, install the required dependencies, and get some text rendered onto the screen.  Our environment is SBCL 1.4.5 on Ubuntu 18.04.

The code for this part can be found at https://gitlab.com/cory/cl-rogue/tree/part-01

cl-charms

We will use cl-charms 0.2.0 as our libcurses interface.  cl-charms is a set of Common Lisp bindings to the libcurses interface, which lets us build a terminal user interface for our game.

SLIME considerations

emacs is not a terminal that cl-charms can handle, so we have to start a swank server in a separate process and connect SLIME to it remotely.

code

We generate the project with cl-project

(ql:quickload :cl-project)
(cl-project:make-project #p"~/Documents/Code/cl-rogue"
  :author "Cory Chamblin"
  :email "c@chambl.in"
  :license "BSD"
  :depends-on '(:cl-charms))

This is the cl-rogue.lisp program I came up with.

(defpackage cl-rogue
  (:use :cl)
  (:export #:main))
(in-package :cl-rogue)

(defun render (window)
  (charms:clear-window window)
  (charms:write-string-at-point window "CL-Rogue!" 0 0)
  (charms:write-string-at-point window "press q to quit" 0 1))

(defun main ()
  (charms:with-curses ()
    (charms:disable-echoing)
    (charms:enable-raw-input :interpret-control-characters t)
    (let ((window charms:*standard-window*))
      (loop named :driver-loop
	 do (progn
	      (render window)
	      (when (char= #\q (charms:get-char window :ignore-error t))
		(return-from :driver-loop)))))))

For now, we enter into a loop that draws a simple message to the screen and waits for the user to press a key. If that key is q, the program exits.

execution

I evaluate (main) in the (remote) SLIME repl.  Since I put the directory in my asdf search path, I am also able to run it from the command line with sbcl --eval "(asdf:load-system :cl-rogue)" --eval "(cl-rogue:main)".

Screenshot_2018-08-20_21-28-46

It’s not much yet, but hopefully we will soon be writing much more interesting console code.