diff options
-rw-r--r-- | README.md | 15 | ||||
-rwxr-xr-x | apple.scm | 226 |
2 files changed, 186 insertions, 55 deletions
diff --git a/README.md b/README.md index 9951e43..95b97b6 100644 --- a/README.md +++ b/README.md | |||
@@ -29,6 +29,21 @@ Here is a picture of the cool rock I found: | |||
29 | 29 | ||
30 | ## Development log | 30 | ## Development log |
31 | 31 | ||
32 | ### 2023-06-01 | ||
33 | |||
34 | - Changed name to *apple* | ||
35 | - Added a makefile for compilation | ||
36 | |||
37 | - I made the snake smart! | ||
38 | - now it follows the appple around the screen trying to eat it | ||
39 | - figuring out collision logic and making sure the snake didn't eat itself and all was .. whoo boy, it was wild | ||
40 | - but in the end it works well enough! | ||
41 | - Added game win/lose conditions | ||
42 | - Added fruit | ||
43 | - will make that work tomorrow | ||
44 | - hacky AS HECK! here be dragons yall | ||
45 | - collision detection is full of special cases, and I don't like how it works | ||
46 | |||
32 | ### 2023-05-31 | 47 | ### 2023-05-31 |
33 | 48 | ||
34 | - Added color using the (yolk attrs) library | 49 | - Added color using the (yolk attrs) library |
diff --git a/apple.scm b/apple.scm index 5dae4eb..41e12f6 100755 --- a/apple.scm +++ b/apple.scm | |||
@@ -30,24 +30,37 @@ See COPYING for details. | |||
30 | 30 | ||
31 | (define (main args) | 31 | (define (main args) |
32 | (parameterize ((world (make-world 80 25))) | 32 | (parameterize ((world (make-world 80 25))) |
33 | (parameterize ((me (let-values (((x y) (random-point (world)))) | 33 | (parameterize ((me (make-thing 'me (world) |
34 | (make-thing 'me (world) | 34 | (with-attrs '(red) "ó") 10 10 2)) |
35 | (with-attrs '(red) "ó") x y 2))) | 35 | (snake (make-snake (world) 5 (current-jiffy) 200 'left |
36 | (snake (let-values (((x y) (random-point (world)))) | 36 | (make-thing 'snake-head (world) |
37 | (make-snake (world) 5 (current-jiffy) 100 'left | 37 | "<" 20 20 2) |
38 | (make-thing 'snake-head (world) | 38 | (list))) |
39 | "<" x y 2) | 39 | (fruit (make-thing 'fruit (world) |
40 | (list (make-thing 'snake-tail (world) | 40 | (random-choice (fruits)) 10 20 1))) |
41 | "-" (+ 1 x) y 2)))))) | 41 | (case (dynamic-wind game-setup |
42 | (dynamic-wind game-setup | 42 | game-loop |
43 | game-loop | 43 | game-cleanup) |
44 | game-cleanup))) | 44 | ((win) (print "Congrats!")) |
45 | ((lose) (print "Better luck next time.")) | ||
46 | (else)) | ||
47 | (print "Thanks for playing!"))) | ||
45 | #t) | 48 | #t) |
46 | 49 | ||
50 | ;;; Parameters | ||
47 | (define me (make-parameter #f)) | 51 | (define me (make-parameter #f)) |
48 | (define world (make-parameter #f)) | ||
49 | (define snake (make-parameter #f)) | 52 | (define snake (make-parameter #f)) |
53 | (define fruit (make-parameter #f)) | ||
54 | (define world (make-parameter #f)) | ||
55 | (define game-end (make-parameter #f)) | ||
56 | |||
57 | (define fruits | ||
58 | (make-parameter (list (with-attrs '(yellow) ")") | ||
59 | (with-attrs '(green) "ò") | ||
60 | (with-attrs '(magenta) "%") | ||
61 | (with-attrs '(blue) "*")))) | ||
50 | 62 | ||
63 | ;;; Main game elements | ||
51 | (define (game-setup) | 64 | (define (game-setup) |
52 | ;; Prepare terminal | 65 | ;; Prepare terminal |
53 | (stty '(raw (not echo))) | 66 | (stty '(raw (not echo))) |
@@ -72,16 +85,20 @@ See COPYING for details. | |||
72 | (define (game-loop) | 85 | (define (game-loop) |
73 | (call/cc | 86 | (call/cc |
74 | (lambda (return) | 87 | (lambda (return) |
75 | (let loop ((now (current-jiffy))) | 88 | (parameterize ((game-end return)) |
76 | (handle-input return) | 89 | (let loop ((now (current-jiffy))) |
77 | (snake-step (snake) now) | 90 | (unless (and (eq? (thing-x (fruit)) (thing-x (me))) |
78 | (world-draw (world)) | 91 | (eq? (thing-y (fruit)) (thing-y (me)))) |
79 | (loop (current-jiffy)))))) | 92 | (thing-place! (fruit))) |
80 | 93 | (handle-input) | |
81 | (define (handle-input done) | 94 | (snake-step (snake) now) |
95 | (world-draw (world)) | ||
96 | (loop (current-jiffy))))))) | ||
97 | |||
98 | (define (handle-input) | ||
82 | (if (char-ready?) | 99 | (if (char-ready?) |
83 | (match (readch) | 100 | (match (readch) |
84 | (#\q (done)) | 101 | ((or #\q #\) ((game-end) 'quit)) |
85 | (#\k (up!)) | 102 | (#\k (up!)) |
86 | (#\j (down!)) | 103 | (#\j (down!)) |
87 | (#\l (right!)) | 104 | (#\l (right!)) |
@@ -95,7 +112,7 @@ See COPYING for details. | |||
95 | (#\D (left!)) | 112 | (#\D (left!)) |
96 | (_ #f))) | 113 | (_ #f))) |
97 | (_ #f))) | 114 | (_ #f))) |
98 | (x (log* x))) | 115 | (_ #f)) |
99 | #f)) | 116 | #f)) |
100 | 117 | ||
101 | (define (readch) | 118 | (define (readch) |
@@ -126,6 +143,9 @@ See COPYING for details. | |||
126 | (loop (random-x world) (random-y world)) | 143 | (loop (random-x world) (random-y world)) |
127 | (values x y)))) | 144 | (values x y)))) |
128 | 145 | ||
146 | (define (random-choice . choices) | ||
147 | (list-ref choices (random-int (length choices)))) | ||
148 | |||
129 | ;;; Drawing stuff | 149 | ;;; Drawing stuff |
130 | 150 | ||
131 | (define (draw . instructions) | 151 | (define (draw . instructions) |
@@ -198,10 +218,19 @@ See COPYING for details. | |||
198 | (thing-place! (wall w x y))) | 218 | (thing-place! (wall w x y))) |
199 | (else))) | 219 | (else))) |
200 | world) | 220 | world) |
221 | (thing-pos-randomize! (me)) | ||
201 | (thing-place! (me)) | 222 | (thing-place! (me)) |
223 | (thing-pos-randomize! (snake-head (snake))) | ||
202 | (thing-place! (snake-head (snake))) | 224 | (thing-place! (snake-head (snake))) |
203 | (for-each (lambda (t) (thing-place! t)) | 225 | (for-each (lambda (t) (thing-place! t)) |
204 | (snake-tail (snake)))) | 226 | (snake-tail (snake))) |
227 | (thing-pos-randomize! (fruit)) | ||
228 | (thing-place! (fruit))) | ||
229 | |||
230 | (define (thing-pos-randomize! thing) | ||
231 | (let-values (((x y) (random-point (thing-world thing)))) | ||
232 | (thing-x-set! thing x) | ||
233 | (thing-y-set! thing y))) | ||
205 | 234 | ||
206 | ;;; Things | 235 | ;;; Things |
207 | 236 | ||
@@ -249,7 +278,8 @@ See COPYING for details. | |||
249 | (other (world-get world new-x new-y))) | 278 | (other (world-get world new-x new-y))) |
250 | (cond | 279 | (cond |
251 | ((and (thing? other) | 280 | ((and (thing? other) |
252 | (> (thing-z other) (thing-z thing)))) | 281 | (<= (thing-z thing) (thing-z other))) |
282 | (handle-collision thing other)) | ||
253 | (else | 283 | (else |
254 | (cond ((< new-x 1) | 284 | (cond ((< new-x 1) |
255 | (set! new-x 1)) | 285 | (set! new-x 1)) |
@@ -261,6 +291,40 @@ See COPYING for details. | |||
261 | (set! new-y (- (world-height world) 1)))) | 291 | (set! new-y (- (world-height world) 1)))) |
262 | (thing-move! thing new-x new-y))))) | 292 | (thing-move! thing new-x new-y))))) |
263 | 293 | ||
294 | ;;; XXX: these are hacky | ||
295 | (define (me? x) | ||
296 | (and (thing? x) | ||
297 | (eq? 'me (thing-name x)))) | ||
298 | |||
299 | (define (snake-head? x) | ||
300 | (and (thing? x) | ||
301 | (eq? 'snake-head (thing-name x)))) | ||
302 | ;;; hacky-end | ||
303 | |||
304 | (define (handle-collision a b) | ||
305 | (cond | ||
306 | ((or (and (me? a) (snake-head? b)) | ||
307 | (and (snake-head? a) (me? b))) | ||
308 | (game-lose)) | ||
309 | ((or (and (snake-head? a) (thing? b)) | ||
310 | (and (thing? a) (snake-head? b))) | ||
311 | (game-win)) | ||
312 | (else))) | ||
313 | |||
314 | (define (game-win) | ||
315 | (draw (cursor-move 10 10) | ||
316 | "YOU WIN!") | ||
317 | (readch) | ||
318 | (newline) | ||
319 | ((game-end) 'win)) | ||
320 | |||
321 | (define (game-lose) | ||
322 | (draw (cursor-move 10 10) | ||
323 | "YOU LOSE :(") | ||
324 | (readch) | ||
325 | (newline) | ||
326 | ((game-end) 'lose)) | ||
327 | |||
264 | (define (thing-up! thing) | 328 | (define (thing-up! thing) |
265 | (thing-move-relative! thing 0 -1)) | 329 | (thing-move-relative! thing 0 -1)) |
266 | 330 | ||
@@ -281,31 +345,38 @@ See COPYING for details. | |||
281 | (define (thing-above thing) | 345 | (define (thing-above thing) |
282 | (if (< (thing-y thing) 1) | 346 | (if (< (thing-y thing) 1) |
283 | #f | 347 | #f |
284 | (world-get (thing-world thing) | 348 | (let ((t (world-get (thing-world thing) |
285 | (thing-x thing) | 349 | (thing-x thing) |
286 | (- (thing-y thing) 1)))) | 350 | (- (thing-y thing) 1)))) |
351 | (and (thing? t) | ||
352 | (thing-name t))))) | ||
287 | 353 | ||
288 | (define (thing-below thing) | 354 | (define (thing-below thing) |
289 | (if (> (thing-y thing) (world-height (thing-world thing))) | 355 | (if (> (thing-y thing) (world-height (thing-world thing))) |
290 | #f | 356 | #f |
291 | (world-get (thing-world thing) | 357 | (let ((t (world-get (thing-world thing) |
292 | (thing-x thing) | 358 | (thing-x thing) |
293 | (+ (thing-y thing) 1)))) | 359 | (+ (thing-y thing) 1)))) |
360 | (and (thing? t) | ||
361 | (thing-name t))))) | ||
294 | 362 | ||
295 | (define (thing-to-left thing) | 363 | (define (thing-to-left thing) |
296 | (if (< (thing-x thing) 1) | 364 | (if (< (thing-x thing) 1) |
297 | #f | 365 | #f |
298 | (world-get (thing-world thing) | 366 | (let ((t (world-get (thing-world thing) |
299 | (- (thing-x thing) 1) | 367 | (- (thing-x thing) 1) |
300 | (thing-y thing)))) | 368 | (thing-y thing)))) |
369 | (and (thing? t) | ||
370 | (thing-name t))))) | ||
301 | 371 | ||
302 | (define (thing-to-right thing) | 372 | (define (thing-to-right thing) |
303 | (if (> (thing-x thing) (world-width (thing-world thing))) | 373 | (if (> (thing-x thing) (world-width (thing-world thing))) |
304 | #f | 374 | #f |
305 | (world-get (thing-world thing) | 375 | (let ((t (world-get (thing-world thing) |
306 | (+ (thing-x thing) 1) | 376 | (+ (thing-x thing) 1) |
307 | (thing-y thing) | 377 | (thing-y thing)))) |
308 | ))) | 378 | (and (thing? t) |
379 | (thing-name t))))) | ||
309 | 380 | ||
310 | ;;; snake | 381 | ;;; snake |
311 | 382 | ||
@@ -347,17 +418,18 @@ See COPYING for details. | |||
347 | (define (snake-step snake now) | 418 | (define (snake-step snake now) |
348 | (when (> now (+ (snake-mtime snake) (snake-wait snake))) | 419 | (when (> now (+ (snake-mtime snake) (snake-wait snake))) |
349 | (let ((new-tail (make-thing 'snake-tail (snake-world snake) | 420 | (let ((new-tail (make-thing 'snake-tail (snake-world snake) |
350 | (if (snake-vertical? snake) | 421 | "∙" |
351 | "|" | 422 | #;(if (snake-vertical? snake) |
352 | "-") | 423 | "|" |
424 | "-") | ||
353 | (snake-x snake) | 425 | (snake-x snake) |
354 | (snake-y snake) | 426 | (snake-y snake) |
355 | (snake-z snake)))) | 427 | (snake-z snake)))) |
356 | (snake-update-head! snake) | 428 | (snake-update-head! snake) |
429 | (snake-update-direction! snake) | ||
357 | (apply thing-move-relative! | 430 | (apply thing-move-relative! |
358 | (snake-head snake) | 431 | (snake-head snake) |
359 | (direction->velocity (snake-direction snake))) | 432 | (direction->velocity (snake-direction snake))) |
360 | (snake-update-direction! snake) | ||
361 | (for-each (lambda (t) (world-set! (snake-world snake) | 433 | (for-each (lambda (t) (world-set! (snake-world snake) |
362 | (thing-x t) | 434 | (thing-x t) |
363 | (thing-y t) | 435 | (thing-y t) |
@@ -391,18 +463,62 @@ See COPYING for details. | |||
391 | ((left) "<") | 463 | ((left) "<") |
392 | ((up) "^")))) | 464 | ((up) "^")))) |
393 | 465 | ||
466 | (define (snake-decide-move snake) ; snake => direction | ||
467 | (let* ((head (snake-head snake)) | ||
468 | (direction (snake-direction snake)) | ||
469 | (snake-x (snake-x snake)) | ||
470 | (snake-y (snake-y snake)) | ||
471 | (me-x (thing-x (me))) | ||
472 | (me-y (thing-y (me))) | ||
473 | (new-dir | ||
474 | ;; Initial decision | ||
475 | (cond | ||
476 | ;; If `me' is right there, move toward it | ||
477 | ((eq? (thing-above head) 'me) 'up) | ||
478 | ((eq? (thing-below head) 'me) 'down) | ||
479 | ((eq? (thing-to-right head) 'me) 'right) | ||
480 | ((eq? (thing-to-left head) 'me) 'left) | ||
481 | ;; Otherwise, if about to collide with something, dodge it. | ||
482 | ((or (and (thing-above head) | ||
483 | (eq? direction 'up)) | ||
484 | (and (thing-below head) | ||
485 | (eq? direction 'down))) | ||
486 | (if (thing-to-left head) 'right 'left)) | ||
487 | ((or (and (thing-to-right head) | ||
488 | (eq? direction 'right)) | ||
489 | (and (thing-to-left head) | ||
490 | (eq? direction 'left))) | ||
491 | (if (thing-above head) 'down 'up)) | ||
492 | ;; Otherwise, move toward `me' | ||
493 | ((< snake-x me-x) | ||
494 | (if (> (abs (- snake-x me-x)) | ||
495 | (abs (- snake-y me-y))) | ||
496 | 'right | ||
497 | (if (< snake-y me-y) 'down 'up))) | ||
498 | ((> snake-x me-x) | ||
499 | (if (> (abs (- snake-x me-x)) | ||
500 | (abs (- snake-y me-y))) | ||
501 | 'left | ||
502 | (if (< snake-y me-y) 'down 'up))) | ||
503 | ((= snake-x me-x) (cond | ||
504 | ((< snake-y me-y) 'down) | ||
505 | ((> snake-y me-y) 'up) | ||
506 | (else ((game-end) 'lose)))) | ||
507 | ;; Otherwise, keep going how we're going | ||
508 | (else (snake-direction snake))))) | ||
509 | ;; Don't let the snake double back into itself | ||
510 | (cond | ||
511 | ((or (and (eq? direction 'right) | ||
512 | (eq? new-dir 'left)) | ||
513 | (and (eq? direction 'left) | ||
514 | (eq? new-dir 'right))) | ||
515 | (if (< snake-y me-y) 'down 'up)) | ||
516 | ((or (and (eq? direction 'up) | ||
517 | (eq? new-dir 'down)) | ||
518 | (and (eq? direction 'down) | ||
519 | (eq? new-dir 'up))) | ||
520 | (if (< snake-x me-x) 'right 'left)) | ||
521 | (else new-dir)))) | ||
522 | |||
394 | (define (snake-update-direction! snake) | 523 | (define (snake-update-direction! snake) |
395 | (let ((head (snake-head snake))) | 524 | (snake-direction-set! snake (snake-decide-move 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)))))) | ||