patternMinor
Getting functional Common Lisp code for handling state in a SDL animation
Viewed 0 times
handlinganimationsdllispgettingcodeforstatefunctionalcommon
Problem
The following code draws a red rectangle bouncing between the borders of a white display. I'm not particularly happy with the update function:
```
(require 'lispbuilder-sdl)
(defun update-h-speed (state h-border top-left-border)
(if (or ( (cdr (assoc 'h-coord state)) h-border)) ;;if too right
;; negate the horizontal speed
(acons 'h-speed (- (cdr (assoc 'h-speed state))) state)
state))
(defun update-v-speed (state v-border top-left-border)
(if (or ( (cdr (assoc 'v-coord state)) v-border)) ;;if too low
;; negate the vertical speed
(acons 'v-speed (- (cdr (assoc 'v-speed state))) state)
state))
(defun update-coords (state)
(acons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state)))
(acons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))) state)))
(defun update (state width height size)
(let ((h-border (- width (/ size 2)))
(v-border (- height (/ size 2)))
(top-left-border (/ size 2)))
(update-coords
(update-h-speed
(update-v-speed state v-border top-left-border) h-border top-left-border))))
(defun game (&optional (width 320) (height 240) (speed 2) (size 20))
(let ((decoy (pairlis (list 'h-coord 'v-coord 'h-speed 'v-speed)
(list (/ width 2) (/ height 2) speed speed))))
(sdl:with-init ()
(sdl:window width height :title-caption "My game")
(setf (sdl:frame-rate) 60)
(sdl:with-events ()
(:quit-event () t)
(:key-down-event (:key key)
(when (sdl:key= key :sdl-key-escape) (sdl:push-quit-event)))
(:idle ()
;; update state
(setq decoy (update decoy width height size))
(sdl:clear-display sdl:white)
;; draw the rectangle according to the updated state
(sdl:draw-box (sdl:rectangle-from-midpoint-*
(cdr (assoc 'h-coord decoy)) (cdr (assoc 'v-coord decoy)) size size)
:colo
```
(require 'lispbuilder-sdl)
(defun update-h-speed (state h-border top-left-border)
(if (or ( (cdr (assoc 'h-coord state)) h-border)) ;;if too right
;; negate the horizontal speed
(acons 'h-speed (- (cdr (assoc 'h-speed state))) state)
state))
(defun update-v-speed (state v-border top-left-border)
(if (or ( (cdr (assoc 'v-coord state)) v-border)) ;;if too low
;; negate the vertical speed
(acons 'v-speed (- (cdr (assoc 'v-speed state))) state)
state))
(defun update-coords (state)
(acons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state)))
(acons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))) state)))
(defun update (state width height size)
(let ((h-border (- width (/ size 2)))
(v-border (- height (/ size 2)))
(top-left-border (/ size 2)))
(update-coords
(update-h-speed
(update-v-speed state v-border top-left-border) h-border top-left-border))))
(defun game (&optional (width 320) (height 240) (speed 2) (size 20))
(let ((decoy (pairlis (list 'h-coord 'v-coord 'h-speed 'v-speed)
(list (/ width 2) (/ height 2) speed speed))))
(sdl:with-init ()
(sdl:window width height :title-caption "My game")
(setf (sdl:frame-rate) 60)
(sdl:with-events ()
(:quit-event () t)
(:key-down-event (:key key)
(when (sdl:key= key :sdl-key-escape) (sdl:push-quit-event)))
(:idle ()
;; update state
(setq decoy (update decoy width height size))
(sdl:clear-display sdl:white)
;; draw the rectangle according to the updated state
(sdl:draw-box (sdl:rectangle-from-midpoint-*
(cdr (assoc 'h-coord decoy)) (cdr (assoc 'v-coord decoy)) size size)
:colo
Solution
I would use CLOS for that. The game state is an CLOS object then. If the thing needs a history, then I would add the history to the CLOS object.
Named slots of something in the history of Lisp:
-
assoc lists (ca. 1960)
-
hash-tables
-
structures (70s)
-
classes (70s/80s...)
If you look at your
Sometimes I would want to get rid of the nesting in above function.
Alternatively you can use the backquote notation.
I would add Practical Common Lisp by Peter Seibel to the reading list. It teaches a larger subset of more idiomatic Common Lisp than for example On Lisp.
With CLOS the code looks much simpler. Example without history:
Named slots of something in the history of Lisp:
-
assoc lists (ca. 1960)
-
hash-tables
-
structures (70s)
-
classes (70s/80s...)
If you look at your
update a few things don't look nice:prognis not needed
- way to many
SETQs, construct a return value instead
- the use of an association list, where a CLOS object should have been used
- no explicit return value
(defun update-coords (state)
(acons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state)))
(acons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state)))
state)))Sometimes I would want to get rid of the nesting in above function.
(defun update-coords (state)
(list* (cons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state))))
(cons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))))
state))Alternatively you can use the backquote notation.
, evaluates. ,@ evaluates and splices the result in.(defun update-coords (state)
`((h-coord . ,(+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state))))
(v-coord . ,(+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))))
,@state))I would add Practical Common Lisp by Peter Seibel to the reading list. It teaches a larger subset of more idiomatic Common Lisp than for example On Lisp.
With CLOS the code looks much simpler. Example without history:
(defclass game-state ()
(h-coord v-coord h-speed v-speed))
(defmethod update-coords ((state game-state))
(with-slots (h-coord h-speed v-coord v-speed)
state
(incf h-coord h-speed)
(incf v-coord v-speed))
state)Code Snippets
(defun update-coords (state)
(acons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state)))
(acons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state)))
state)))(defun update-coords (state)
(list* (cons 'h-coord (+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state))))
(cons 'v-coord (+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))))
state))(defun update-coords (state)
`((h-coord . ,(+ (cdr (assoc 'h-coord state))
(cdr (assoc 'h-speed state))))
(v-coord . ,(+ (cdr (assoc 'v-coord state))
(cdr (assoc 'v-speed state))))
,@state))(defclass game-state ()
(h-coord v-coord h-speed v-speed))
(defmethod update-coords ((state game-state))
(with-slots (h-coord h-speed v-coord v-speed)
state
(incf h-coord h-speed)
(incf v-coord v-speed))
state)Context
StackExchange Code Review Q#61689, answer score: 3
Revisions (0)
No revisions yet.