about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2023-06-01 23:24:36 -0500
committerCase Duckworth2023-06-01 23:24:36 -0500
commitbba87d2d253200d177daae44a5df969a0149dca2 (patch)
treecc2e62fe77af2b9fc2d7ccef6e56565b5b02780c
parentChange name to apple and add makefile (diff)
downloadapple-bba87d2d253200d177daae44a5df969a0149dca2.tar.gz
apple-bba87d2d253200d177daae44a5df969a0149dca2.zip
Made the snake smart and added collision detection
- I made the snake smart!
  - now it follows the appple around the screen trying to eat it
  - figuring out collision logic and making sure the snake didn't eat itself and all was .. whoo boy, it was wild
  - but in the end it works well enough!
- Added game win/lose conditions
- Added fruit
  - will make that work tomorrow
  - hacky AS HECK! here be dragons yall
  - collision detection is full of special cases, and I don't like how it works
-rw-r--r--README.md15
-rwxr-xr-xapple.scm226
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))))))