My Programming principles for game dev 1/2


1. Intro

Game dev is the best extant programming. Boring, office-job programming is in general fruitless. The fact that game dev programming is the best and most cutting edge means we must have the crème de la crème of programming languages, tools and style. I think game dev can be understood holistically.

My hope here is to quickly begin exploring something like advanced software engineering for game development myself and with anyone I can find.

I am just going to start sharing some things I think are excellent to do.

I hope to respond to your comments (which I want!) (grr silence).

2. Game Programming

First off, programming is pure self-expression. If you want tips for making money roller-painting walls of houses, I do not need to hear about it. Let us be serious about programming.

I can imagine the allegation that I just pulled the following from a hat. Based on my life experience, these capture the best approach I am aware of to programming in general.

3. Current events

There was just a major release of McCLIM, so game dev with McCLIM is timely. McCLIM is an implementation of the CLIM II spec.

4. In General:

4.1. First half, defended in this article:

  • Lisp is the best language and common lisp is the best one.
  • Iteration should be generated through Richard Waters' Series macro.
  • Other iteration should use Masinter's LOOP facility.
  • McCLIM should generate your graphical application.
  • Applications should be running interactively continuously, not restarted.

4.2. Second half in the next article:

  • New functions (defuns) should be ACL2 admissable first order logic.
  • Program behaviors should come from CLOS mixins.
  • Application behaviors should be CLIM commands
  • "Testing" simply means demoing useage examples.
  • Application events should signal conditions.

4.3. A third half will contain networking

Common lisp is well-studied to be an ideal language for serious network server programming. Stay tuned and we will get there.

5. Details and examples

5.1. In general lisp is the best

Lisp is about as old as fortran and is an extremely high level programming language. Short-lived languages such as Python are constantly rediscovering previously known terrible mistakes for the first time themselves. Consider attempting printing in Pythons 2, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0 etc. We do not have time for this panoply of repeated growing-pain errors in the host language. I am indicating a general trend I perceive in short-lifed languages, please forgive me pythonistas.

Steel bank common lisp is the most popular one. I also think embeddable common lisp, which is lisp as a C/C++ library has especial utility. The other compilers are also all excellent. SICL, CCL, armed bear (JVM), GNU clisp…

5.2. In general iteration should use the series package

One fizzbuzz implementation could look like

(let* ((nos (scan-range :from 1 :upto 100))
       (mod3s (scan-range :from (1- 3) :below 100 :by 3))
       (mod5s (scan-range :from (1- 5) :upto 100 :by 5))
       (fizz (series "fizz"))
       (buzz (series "buzz"))
       (fizzes (#mand (mask mod3s) fizz))
       (buzzes (mapping
		(((mod5) (mask mod5s))
		 ((always-buzz) buzz))
		(and mod5 always-buzz)))
       (nonfizzbuzzes
	 (#Mand (#mnot (#mor fizzes buzzes)) nos)))
  (iterate
    (((fizz) fizzes)
     ((buzz) buzzes)
     (no nonfizzbuzzes))
    (terpri)
    (when fizz (princ fizz))
    (when buzz (princ buzz))
    (when no (princ no))))

which illustrates

  1. scan-range
    • annoyingly since fizzbuzz indexes from 1,
    • the starting 3 is in position 2 and
    • the starting 5 is in position 4
  2. Infinite (series "fizz")
  3. mapping shorthand (#mand .. ..) which expands similarly to the iterate we will see mapping lisp's and.
  4. mask turns #z(1) into the infinite #Z(nil T nil nil nil …) #z is the read macro for an explicit series
  5. mapping (the expanded form)
  6. iterate the same form as the expanded mapping but does not produce a series and evaluates every valid input for a side effect.

According to research done in creating Series, Series should be used instead of loop about 19 out of every 20 cases because it is easier to read and as fast or faster than loops people write.

Series works by performing static analysis of a graph built at macroexpand time and replacing itself with a tight, efficient, in-order traversal. It has been used and studied from the 1970s until today.

Series is not part of the common lisp standard but received an appendix in the second edition of the book, Common Lisp the Language.

5.3. Other iteration with LOOP

When iteration is not in-order-traversal-shaped, we should always use Common Lisp's LOOP facility, which is originally due to Larry Masinter back in LISP360.

(let ((ht (make-hash-table)))
  (setf (gethash 1 ht) 2
	(gethash 3 ht) '4
	(gethash 'bar ht) 'foo
	(gethash 'foo ht) "baz"
	(gethash 'frob ht) 'ulous)
  (loop :for key
	  :being :the :hash-keys
	    :of ht
	      :using (hash-value val)
	:do
	   (print key)
	   (print val)
	   (terpri)
	:collect val :into my-list
	:while (not (equal key 'foo))
	:finally (return (reverse my-list))))

I have often heard it remarked that the loop facility is quite similar to using algol. Its Common Lisp evolution has special significance because the common lisp standard does not require conforming implementations to do tail call optimized recursion because tail call optimization is opaque when debugging.

5.4. McCLIM should generate your graphical application

i.e.

(define-application-frame trivial-app ()
  ((my-slot :initform 'its-trivial :reader my-slot))
  (:pane :application
   :display-function 'trivial-display-function))
(defmethod trivial-display-function
    ((obj trivial-app) pane)
  (print (my-slot obj) pane))
(Defparameter *trivial-app* (make-application-frame
			     'trivial-app))
(run-frame-top-level *trivial-app*)

Loosely, a pane is an output area and a frame is a window. If you repeatedly issue define-application-frame same-frame, McCLIM understands you as wanting to adjust the existing definition rather than nuke and replace it.

5.5. Applications should run interactively continuously

I think closing and reopening an application after changes is a tragedy. In the event a frame needs to be prompted to notice a change while running, we can use lisp's reinitialize-instance on the running frame. I think of it as jiggling the cables.

;;Start in another thread.
(bt:make-thread (lambda () (run-frame-top-level *trivial-app*)))
(define-application-frame trivial-app ()
  ((my-slot :initform 'its-trivial :reader my-slot))
  (:panes
   (app :application
	:display-function 'trivial-display-function)
   (int :interactor))
  (:layouts
   (default (horizontally () app int))))
(reinitialize-instance *trivial-app*)

(splits the running app in half with those two panes now present).

6. That's all for now

Join us for the live show next week, same as every previous week. See https://mastodon.sdf.org/@screwtape/114027056148933440 for example.

Waiting for comments here and on the mastodon.

</div>

Author: screwlisp

Created: 2025-03-16 Sun 14:35

Validate

Get lispmoo2

Comments

Log in with itch.io to leave a comment.

(1 edit)

Mastodon thread.

https://mastodon.sdf.org/@screwtape/114169601706787432

New release McCLIM announcement

https://functional.cafe/@jackdaniel/114142847876282257

Show thread including KMP's notes on Waters and Waters' Series.

https://mastodon.sdf.org/@kentpitman@climatejustice.social/113491564580698892