This repository has been archived by the owner on May 20, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnake.lisp
118 lines (101 loc) · 4.45 KB
/
snake.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
;;;; snake.lisp
(in-package #:snake)
;; TODO(bsvercl): It would be nice to have highscores.
(defparameter +segment-size+ 25)
(defparameter +screen-width+ 800)
(defparameter +screen-height+ 600)
(defparameter +segments-across-width+ (floor (/ +screen-width+ +segment-size+)))
(defparameter +segments-across-height+ (floor (/ +screen-height+ +segment-size+)))
(defparameter +snake-color+ (gamekit:vec4 1.0 0.75 0.5 1.0))
(defparameter +food-color+ (gamekit:vec4 0.5 0.25 1.0 1.0))
(defparameter +grid-color+ (gamekit:vec4 0.9 0.9 0.9 0.5))
(defparameter +transparent+ (gamekit:vec4))
(defclass snake ()
((segments :initarg :segments :accessor segments-of)
(direction :initarg :direction :accessor direction-of)
(color :initform +snake-color+ :accessor color-of))
(:documentation "The moving thing, usually user controlled."))
(defun make-snake (starting-position &optional (direction (gamekit:vec2)))
"Creates a SNAKE with STARTING-POSITION and DIRECTION."
(make-instance 'snake :segments (list starting-position)
:direction direction))
(defun snake-position (snake)
"Head position of SNAKE."
(first (segments-of snake)))
(defmethod (setf snake-position) (pos (snake snake))
(setf (first (segments-of snake)) pos))
(defun snake-tail (snake)
"The rest of the SNAKE."
(rest (segments-of snake)))
(defun change-direction (snake direction)
"Set DIRECTION-OF SNAKE to DIRECTION.. maybe"
(let ((current (direction-of snake)))
;; TODO: This is so bad.
(cond
((and (eq current :left)
(eq direction :right))
(setf (direction-of snake) :left))
((and (eq current :right)
(eq direction :left))
(setf (direction-of snake) :right))
((and (eq current :up)
(eq direction :down))
(setf (direction-of snake) :up))
((and (eq current :down)
(eq direction :up))
(setf (direction-of snake) :down))
(t (setf (direction-of snake) direction)))))
(defun advance (snake ate-food-p)
"Moves the SNAKE according to it's DIRECTION."
(with-slots (segments direction) snake
(let* ((position (snake-position snake))
;; The position of the next head.
(new-head (gamekit:add position (direction-to-vec direction)))
;; If we ate the food we do not chop off the end of the SEGMENTS.
(which-segments (if ate-food-p segments (butlast segments)))
(new-segments (push new-head which-segments)))
(setf segments new-segments)
;; Wrap SNAKE around the boundaries
(setf (snake-position snake) (mod-vec (snake-position snake)
(gamekit:vec2 +segments-across-width+
+segments-across-height+))))))
(defun hit-itself (snake)
"Did I just eat myself?"
(let ((head-position (snake-position snake)))
(loop for segment in (snake-tail snake)
when (bodge-math:vec= segment head-position)
do (return-from hit-itself t))))
(gamekit:defgame snake-game ()
((current-state))
(:viewport-title "Snake")
(:viewport-width +screen-width+)
(:viewport-height +screen-height+)
(:act-rate 10))
(defmethod gamekit:post-initialize ((this snake-game))
(with-slots (current-state) this
(labels ((start ()
(setf current-state (make-instance 'game-state :end #'end)))
(end ()
(setf current-state (make-instance 'game-over-state :restart #'start))))
(setf current-state (make-instance 'main-menu-state :start #'start)))
(macrolet ((%%binder (key &body body)
"Binds one KEY to execute BODY on press."
`(gamekit:bind-button ,key :pressed #'(lambda () ,@body)))
;; TODO(bsvercl): This might not need to be a macro, but
;; that's just me being a little pedantic.
(%binder ((&rest keys))
"Binds all KEYS."
`(dolist (key ,keys)
(%%binder key (handle-key current-state key)))))
(%binder '(:w :a :s :d :space :q :e)))))
(defmethod gamekit:act ((this snake-game))
(with-slots (current-state) this
(update current-state)))
(defmethod gamekit:draw ((this snake-game))
(with-slots (current-state) this
(draw current-state)))
(defun play (&optional blocking)
"Let's get crackalackin'."
(gamekit:start 'snake-game :viewport-resizable nil
:blocking blocking
:swap-interval 1))