Applicative game media now instantiated. I started with three goals in mind for media: Must be able to play sound, must be convenient to structure complex autostereogram illusions, and must be convenient for plain text as well. To this end I have selected common lisp (duh) format control strings and arguments as my universal media control. It is a complete programming language built around pretty printing. Because it can do anything, sound and complex lispy post-processing are easy. A simple convention and say, SDL2 will also let me drive pixel mapped graphics via format control strings in the future too. ACL2 is not suitable itself for media side effects. Its role is constrained to proving the applicative logic of the game's state to the media representation namely the format controls and their arguments. Let's look at my tictacto, though it could also be baduk which would be cooler actually. The game will be expressed as a proven (in some senses) acl2 applicative function on a list stored in a single threaded object, followed by a normal lisp - sbcl - evaluating acl2's media output. ACL2 !> (new-game game-state) ACL2 !> (fmt-game game-state) (FORMAT T "~{~a~a~a~%~a~a~a~%~a~a~a~%~}" '(#\. #\. #\. #\. #\. #\. #\. #\. #\.)) ;;Over in sbcl's * * (FORMAT T "~{~a~a~a~%~a~a~a~%~a~a~a~%~}" '(#\. #\. #\. #\. #\. #\. #\. #\. #\.)) ... ... ... NIL ACL2 !>(play-x game-state 1 1) ACL2 !>(play-o game-state 0 2) ACL2 !>(fmt-game game-state) (FORMAT T "~{~a~a~a~%~a~a~a~%~a~a~a~%~}" '(#\. #\. #\. #\. #\x #\. #\o #\. #\.)) * (FORMAT T "~{~a~a~a~%~a~a~a~%~a~a~a~%~}" '(#\. #\. #\. #\. #\x #\. #\o #\. #\.)) ... .x. o.. NIL Clearly I can maintain state in the game-state single threaded object, and I allege deterministically produce a common lisp format expression media depiction of the game state evaluable to some literal media which in this case is a three by three game board that gets hugged and kissed. Text is convenient here and always, but all sorts of media control is possible and fairly convenient. On the topic of ACL2 particularly, you might notice that my measure conjectures are always trivial, and the way I have expressed myself has not required any lemmas or particular rewrite rules from me. I think it is good that my robot team mate needs relatively little input from me. I will revisit this towards the end of this post. For now, let me present my ACL2 logic. ;;Same everything as before; I am noting game-state here since it is used. (defstobj game-state (sparse-objects :type t :initially nil) (subject-cursor :type integer :initially 0) (object-cursor :type integer :initially 0) (chars-in :type t :initially nil)) ;;The test world of tictacto/a very unfair baduk totally explicitly (defun new-tictacto-board () '(((row . 0) (col . 0) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 1) (col . 0) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 2) (col . 0) (fmt-ctl . "~a~%") (fmt-args . (#\.))) ((row . 0) (col . 1) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 1) (col . 1) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 2) (col . 1) (fmt-ctl . "~a~%") (fmt-args . (#\.))) ((row . 0) (col . 2) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 1) (col . 2) (fmt-ctl . "~a") (fmt-args . (#\.))) ((row . 2) (col . 2) (fmt-ctl . "~a~%") (fmt-args . (#\.))))) ;;Transform the game sparse objects (actually dense here) into a lisp format (defun get-lisp-format (input fmt-ctls fmt-args) (if (= 0 (length input)) `(format t ,(concatenate 'string "~{" fmt-ctls "~}") ',fmt-args) (get-lisp-format (cdr input) (concatenate 'string fmt-ctls (cdr (assoc 'fmt-ctl (car input)))) (append fmt-args (cdr (assoc 'fmt-args (car input))))))) ;;Change the character displayed without changing the control string, ;;By prepending a new fmt-args since lisp/acl2's assoc only finds ;;first matches. Yes this function is terribly named. (defun row-col2char (row col char input output) (if (= 0 (length input)) output (let ((pushp (if (subsetp-equal `((row . ,row) (col . ,col)) (car input)) `((fmt-args . (,char))) nil))) (row-col2char row col char (cdr input) (append output (list (append pushp (car input)))))))) ;;That was everything functional, onto ;;;game-state ;;I'm not trying to prove I have handled every side-effect error scenario with my stobj ;;Hence I am declaring verify-guards to nil for stateful functions here. (defun new-game (game-state) (declare (xargs :stobjs (game-state) :verify-guards nil)) (update-sparse-objects (new-tictacto-board) game-state)) (defun fmt-game (game-state) (declare (xargs :stobjs (game-state) :verify-guards nil)) (get-lisp-format (sparse-objects game-state) "" nil)) (defun play-x (game-state row col) (declare (xargs :stobjs (game-state) :verify-guards nil)) (let* ((board (sparse-objects game-state)) (new-board (row-col2char row col #\x board nil))) (update-sparse-objects new-board game-state))) (defun play-o (game-state row col) (declare (xargs :stobjs (game-state) :verify-guards nil)) (let* ((board (sparse-objects game-state)) (new-board (row-col2char row col #\o board nil))) (update-sparse-objects new-board game-state))) Easy easy easy. The trouble I mentioned yesterday is not visible anywhere because I decided to just use a simple element by element O(n) filtering of an input list into an output list for everything. What I had been working on was pulling out subsequences relying on some well-ordering I will look at in a moment complicated by col-min and col-max not necessarily being the edges of a row. Anyway, everything is pretty great now without even trying. I keep saying common lisp format can and is appropriate for arbitrary media because it furnishes arbitrary functionality like this (on top of its iteration, gotos, logic and so on): (in-package cl-user) (defun beep-if-true (stream bool-arg &rest flags) (declare (ignore flags)) (format stream "~@[~1@*~]" bool-arg #\Bel)) (format t "~/cl-user::beep-if-true/" t) (format t "~/cl-user::beep-if-true/" nil) ;I assume my hacky scripting and ;typos achieved the following which is normally used to craft a response to put into the stream at the function call position, but does not necessarily have to. For example it could enqueue post processing of the output, or drive Simple Directmedia Layer 2 through ecl's sffi. Something I did not end up using explicitly in this example is that I modified my insertion sort from before to sort lists having row and col conses in row-major order. The only change was this: (defun leq-pushp (a b) (let ((row-a (cdr (assoc 'row a))) (row-b (cdr (assoc 'row (car b)))) (col-a (cdr (assoc 'col a))) (col-b (cdr (assoc 'col (car b))))) (cond ((< row-a row-b) (append (list a) b)) ((and (= row-a row-b) (<= col-a col-b)) (append (list a) b)) (t nil)))) which, accepted trivially, did not change the logic of my acl2 'insert-sorted or 'insertion-sort. By always being sorted through 'insert-sorted (defun insert-sorted (a b result) (if (= 0 (length b)) (append result (list a)) (let ((final-tail (leq-pushp a b))) (if final-tail (append result final-tail) (insert-sorted a (cdr b) (append result (list (car b)))))))) I trivially avoid lots of pathological cases. The reason that I have not had any lemmas or fiddly rewrite rules so far is that I am not proving any very strong theorems. Regarding insertion sort (defun insertion-sort (input result) (if (= 0 (length input)) result (insertion-sort (cdr input) (insert-sorted (car input) result nil)))) I checked weak theorems like (thm (implies (< (cdr (assoc 'row a)) (cdr (assoc 'row b))) (= (search (list a) (insertion-sort (list a b) nil)) 0))) or in English, if I 'insertion-sort (list a b) and a's row is less than b's, a is in position 0. The usual stronger theories of a sorting function are that the output is in fact sorted, and that the output is some permutation of the input. I think a weak theory like- in this kind of scenario, a will be in position 0 is often useful on its own. These weak theories are not usually worth lodging as rewrite rules. When I was thumbing through an ACL2 book, I found the book's insertion sort example, which required proving some supporting lemmas in order to admit the function. My form didn't need any though. The next two things I want for a game, being a notion of a clock and network multiplayer are mostly about side effects. I de finitely need some healthier networking. I will develop a clock and some networking on other topics, then introduce them to my theory of games afterwards. --- Post script Ultimately I expect to do: acl2 -> ecl -> sdl2 executable preserving the logic of acl2.