diff options
Diffstat (limited to 'game.scm')
-rwxr-xr-x | game.scm | 408 |
1 files changed, 0 insertions, 408 deletions
diff --git a/game.scm b/game.scm deleted file mode 100755 index 7a43720..0000000 --- a/game.scm +++ /dev/null | |||
@@ -1,408 +0,0 @@ | |||
1 | #!/bin/sh | ||
2 | #| -*- scheme -*- | ||
3 | exec csi -R r7rs -ss "$0" "$@" | ||
4 | game --- 2023 lisp game jam | ||
5 | (C) Case Duckworth <acdw@acdw.net> | ||
6 | Distributed under the terms of the LATCRIFPL, v1.0. | ||
7 | See 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)))))) | ||