about summary refs log tree commit diff stats
path: root/apple.scm
diff options
context:
space:
mode:
Diffstat (limited to 'apple.scm')
-rwxr-xr-xapple.scm408
1 files changed, 408 insertions, 0 deletions
diff --git a/apple.scm b/apple.scm new file mode 100755 index 0000000..5dae4eb --- /dev/null +++ b/apple.scm
@@ -0,0 +1,408 @@
1#!/bin/sh
2#| -*- scheme -*-
3exec csi -R r7rs -ss "$0" "$@"
4apple --- 2023 lisp game jam
5(C) Case Duckworth <acdw@acdw.net>
6Distributed under the terms of the LATCRIFPL, v1.0.
7See COPYING for details.
8|#
9
10(import (scheme base)
11 (scheme time)
12 (scheme write)
13 (chicken io)
14 (chicken port)
15 (chicken random)
16 (yolk common)
17 (yolk xterm)
18 (yolk erase)
19 (yolk cursor)
20 (yolk attrs)
21 (matchable)
22 (stty))
23
24(define (log* . xs)
25 (with-output-to-port (current-error-port)
26 (lambda ()
27 (for-each (lambda (x) (display x) (display " ") x)
28 xs)
29 (newline))))
30
31(define (main args)
32 (parameterize ((world (make-world 80 25)))
33 (parameterize ((me (let-values (((x y) (random-point (world))))
34 (make-thing 'me (world)
35 (with-attrs '(red) "รณ") x y 2)))
36 (snake (let-values (((x y) (random-point (world))))
37 (make-snake (world) 5 (current-jiffy) 100 'left
38 (make-thing 'snake-head (world)
39 "<" x y 2)
40 (list (make-thing 'snake-tail (world)
41 "-" (+ 1 x) y 2))))))
42 (dynamic-wind game-setup
43 game-loop
44 game-cleanup)))
45 #t)
46
47(define me (make-parameter #f))
48(define world (make-parameter #f))
49(define snake (make-parameter #f))
50
51(define (game-setup)
52 ;; Prepare terminal
53 (stty '(raw (not echo)))
54 (set-buffering-mode! (current-input-port) #:none 1)
55 (set-buffering-mode! (current-output-port) #:none 1)
56 (draw alt-buffer-enable
57 erase-screen
58 (cursor-save)
59 invisible-cursor)
60 ;; Set up world
61 (world-init (world))
62 (world-draw (world)))
63
64(define (game-cleanup)
65 ;; Restore terminal
66 (stty '(cooked echo))
67 (draw cursor-home
68 (cursor-restore)
69 alt-buffer-disable
70 visible-cursor))
71
72(define (game-loop)
73 (call/cc
74 (lambda (return)
75 (let loop ((now (current-jiffy)))
76 (handle-input return)
77 (snake-step (snake) now)
78 (world-draw (world))
79 (loop (current-jiffy))))))
80
81(define (handle-input done)
82 (if (char-ready?)
83 (match (readch)
84 (#\q (done))
85 (#\k (up!))
86 (#\j (down!))
87 (#\l (right!))
88 (#\h (left!))
89 ;; Escape characters
90 (#\escape (match (readch)
91 (#\[ (match (readch)
92 (#\A (up!))
93 (#\B (down!))
94 (#\C (right!))
95 (#\D (left!))
96 (_ #f)))
97 (_ #f)))
98 (x (log* x)))
99 #f))
100
101(define (readch)
102 (let ((c (integer->char (read-byte))))
103 c))
104
105;;; random
106
107(define random-int
108 (case-lambda
109 (() (pseudo-random-integer 100))
110 ((max) (pseudo-random-integer max))
111 ((min max)
112 (let ((max (if (> max min) max min))
113 (min (if (< min max) min max)))
114 (+ min (pseudo-random-integer (- max min)))))))
115
116(define (random-x world)
117 (random-int 1 (- (world-width world) 1)))
118
119(define (random-y world)
120 (random-int 1 (- (world-height world) 1)))
121
122(define (random-point world)
123 (let loop ((x (random-x world))
124 (y (random-y world)))
125 (if (world-get world x y)
126 (loop (random-x world) (random-y world))
127 (values x y))))
128
129;;; Drawing stuff
130
131(define (draw . instructions)
132 (for-each (lambda (i)
133 (cond
134 ((string? i) (display i))
135 ((list? i) (apply draw i))
136 (else (error "Don't know how to draw" i))))
137 instructions))
138
139;;; World
140
141(define-record-type <world>
142 (%make-world width height map)
143 world?
144 (width world-width world-width-set!)
145 (height world-height world-height-set!)
146 (map world-map world-map-set!))
147
148(define (in-bounds? world x y)
149 (or (< x (world-width world))
150 (> x 0)
151 (< y (world-height world))
152 (> y 0)))
153
154(define (coords->index world x y)
155 (if (in-bounds? world x y)
156 (+ x (* (world-width world) y))
157 (error "Out of bounds"
158 (list (world-width world) (world-height world))
159 (list x y))))
160
161(define (world-get world x y)
162 (vector-ref (world-map world) (coords->index world x y)))
163
164(define (world-set! world x y obj)
165 (vector-set! (world-map world)
166 (coords->index world x y)
167 obj))
168
169(define (make-world width height)
170 (%make-world width height (make-vector (* width height) #f)))
171
172(define (for-world proc world)
173 (do ((y 1 (+ y 1)))
174 ((= y (world-height world)) #t)
175 (do ((x 1 (+ x 1)))
176 ((= x (world-width world)) #t)
177 (proc world x y))))
178
179(define (world-draw world)
180 (draw cursor-home)
181 (for-world (lambda (w x y)
182 (cond
183 ((thing? (world-get w x y))
184 (draw (cursor-move x y)
185 (thing-look (world-get w x y))))
186 (else
187 (draw (cursor-move x y)
188 " "))))
189 world))
190
191(define (world-init world)
192 (for-world (lambda (w x y)
193 (cond
194 ((or (= x 1)
195 (= y 1)
196 (= x (- (world-width world) 1))
197 (= y (- (world-height world) 1)))
198 (thing-place! (wall w x y)))
199 (else)))
200 world)
201 (thing-place! (me))
202 (thing-place! (snake-head (snake)))
203 (for-each (lambda (t) (thing-place! t))
204 (snake-tail (snake))))
205
206;;; Things
207
208(define-record-type <thing>
209 (make-thing name world look x y z)
210 thing?
211 (name thing-name)
212 (world thing-world thing-world-set!)
213 (look thing-look thing-look-set!)
214 (x thing-x thing-x-set!)
215 (y thing-y thing-y-set!)
216 (z thing-z thing-z-set!))
217
218(define (wall w x y)
219 (make-thing 'wall w "#" x y 100))
220
221(define (thing-attr-get thing attr)
222 (let ((x (assoc attr (thing-attrs thing))))
223 (if x
224 (cdr x)
225 #f)))
226
227(define (thing-attr-set! thing attr val)
228 (let ((x (assoc attr (thing-attrs thing))))
229 (if x
230 (set-cdr! x val)
231 (thing-attrs-set! thing
232 (cons (cons attr val)
233 (thing-attrs thing))))))
234
235(define (thing-place! thing)
236 (world-set! (thing-world thing) (thing-x thing) (thing-y thing) thing))
237
238(define (thing-move! thing x y)
239 (let ((world (thing-world thing)))
240 (world-set! world (thing-x thing) (thing-y thing) #f)
241 (thing-x-set! thing x)
242 (thing-y-set! thing y)
243 (world-set! world x y thing)))
244
245(define (thing-move-relative! thing dx dy)
246 (let* ((world (thing-world thing))
247 (new-x (+ (thing-x thing) dx))
248 (new-y (+ (thing-y thing) dy))
249 (other (world-get world new-x new-y)))
250 (cond
251 ((and (thing? other)
252 (> (thing-z other) (thing-z thing))))
253 (else
254 (cond ((< new-x 1)
255 (set! new-x 1))
256 ((> new-x (- (world-width world) 1))
257 (set! new-x (- (world-width world) 1))))
258 (cond ((< new-y 1)
259 (set! new-y 1))
260 ((> new-y (- (world-height world) 1))
261 (set! new-y (- (world-height world) 1))))
262 (thing-move! thing new-x new-y)))))
263
264(define (thing-up! thing)
265 (thing-move-relative! thing 0 -1))
266
267(define (thing-down! thing)
268 (thing-move-relative! thing 0 1))
269
270(define (thing-right! thing)
271 (thing-move-relative! thing 1 0))
272
273(define (thing-left! thing)
274 (thing-move-relative! thing -1 0))
275
276(define (up!) (thing-up! (me)))
277(define (down!) (thing-down! (me)))
278(define (left!) (thing-left! (me)))
279(define (right!) (thing-right! (me)))
280
281(define (thing-above thing)
282 (if (< (thing-y thing) 1)
283 #f
284 (world-get (thing-world thing)
285 (thing-x thing)
286 (- (thing-y thing) 1))))
287
288(define (thing-below thing)
289 (if (> (thing-y thing) (world-height (thing-world thing)))
290 #f
291 (world-get (thing-world thing)
292 (thing-x thing)
293 (+ (thing-y thing) 1))))
294
295(define (thing-to-left thing)
296 (if (< (thing-x thing) 1)
297 #f
298 (world-get (thing-world thing)
299 (- (thing-x thing) 1)
300 (thing-y thing))))
301
302(define (thing-to-right thing)
303 (if (> (thing-x thing) (world-width (thing-world thing)))
304 #f
305 (world-get (thing-world thing)
306 (+ (thing-x thing) 1)
307 (thing-y thing)
308 )))
309
310;;; snake
311
312(define-record-type <snake>
313 (make-snake world length mtime wait direction head tail)
314 snake?
315 (length snake-length snake-length-set!)
316 (world snake-world snake-world-set!)
317 (mtime snake-mtime snake-mtime-set!)
318 (direction snake-direction snake-direction-set!)
319 (wait snake-wait snake-wait-set!)
320 (head snake-head snake-head-set!)
321 (tail snake-tail snake-tail-set!))
322
323(define (snake-x snake)
324 (thing-x (snake-head snake)))
325
326(define (snake-y snake)
327 (thing-y (snake-head snake)))
328
329(define (snake-z snake)
330 (thing-z (snake-head snake)))
331
332(define (take* xs n)
333 ;;; Like `take' from SRFI 1, but doesn't choke on too-short lists.
334 (unless (and (integer? n)
335 (> n 0))
336 (error "Must take non-negative integer" n))
337 (let loop ((xs xs)
338 (n n)
339 (acc '()))
340 (if (or (null? xs)
341 (zero? n))
342 (reverse acc)
343 (loop (cdr xs)
344 (- n 1)
345 (cons (car xs) acc)))))
346
347(define (snake-step snake now)
348 (when (> now (+ (snake-mtime snake) (snake-wait snake)))
349 (let ((new-tail (make-thing 'snake-tail (snake-world snake)
350 (if (snake-vertical? snake)
351 "|"
352 "-")
353 (snake-x snake)
354 (snake-y snake)
355 (snake-z snake))))
356 (snake-update-head! snake)
357 (apply thing-move-relative!
358 (snake-head snake)
359 (direction->velocity (snake-direction snake)))
360 (snake-update-direction! snake)
361 (for-each (lambda (t) (world-set! (snake-world snake)
362 (thing-x t)
363 (thing-y t)
364 #f))
365 (snake-tail snake))
366 (snake-tail-set! snake
367 (take* (cons new-tail
368 (snake-tail snake))
369 (snake-length snake)))
370 (for-each (lambda (t) (thing-place! t))
371 (snake-tail snake))
372 (snake-mtime-set! snake now))))
373
374(define (direction->velocity direction)
375 (case direction
376 ((up) '(0 -1))
377 ((down) '(0 1))
378 ((left) '(-1 0))
379 ((right) '(1 0))
380 (else direction)))
381
382(define (snake-vertical? snake)
383 (or (eq? (snake-direction snake) 'up)
384 (eq? (snake-direction snake) 'down)))
385
386(define (snake-update-head! snake)
387 (thing-look-set! (snake-head snake)
388 (case (snake-direction snake)
389 ((right) ">")
390 ((down) "v")
391 ((left) "<")
392 ((up) "^"))))
393
394(define (snake-update-direction! snake)
395 (let ((head (snake-head snake)))
396 (case (snake-direction snake)
397 ((up)
398 (when (thing-above head)
399 (snake-direction-set! snake 'left)))
400 ((down)
401 (when (thing-below head)
402 (snake-direction-set! snake 'right)))
403 ((left)
404 (when (thing-to-left head)
405 (snake-direction-set! snake 'down)))
406 ((right)
407 (when (thing-to-right head)
408 (snake-direction-set! snake 'up))))))