Skip to content

Instantly share code, notes, and snippets.

@tshirtman
Last active January 7, 2020 22:17
Show Gist options
  • Save tshirtman/f132a71dd389ec8e86521cb71d2cb6cf to your computer and use it in GitHub Desktop.
Save tshirtman/f132a71dd389ec8e86521cb71d2cb6cf to your computer and use it in GitHub Desktop.
snaaaaake
from random import randint
from kivy.app import App
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ListProperty, NumericProperty
from kivy.vector import Vector
from kivy.core.window import Window
KV = '''
<Snake>:
motion_size: app.motion_size
canvas:
Color:
rgba: 1, 1, 1, 1
Line:
points: self.head + self.points + self.tail
width: self.line_width
cap: 'round'
Color:
rgba: 1, 0, 0, .5
Point:
points: self.points
pointsize: self.line_width
Label:
text: '{}'.format(root.length)
<GameBoard>:
<Apple>:
size: 10, 10
pos: app.apple[0] - self.size[0] / 2, app.apple[1] - self.size[1] / 2
canvas:
Ellipse:
size: self.size
pos: self.pos
GameBoard:
Apple:
'''
class Snake(Widget):
line_width = NumericProperty(10)
length = NumericProperty(100)
head = ListProperty([0, 0])
points = ListProperty([])
tail = ListProperty([0, 0])
head_target = ListProperty([0, 0])
motion_size = NumericProperty(10)
time = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.reset()
def update_time(self, dt):
self.time += dt
if self.time > .5:
self.move()
self.time %= .5
def move(self):
self.head_target = self.vector * self.motion_size + self.head
anim = Animation(
head=self.head_target,
line_width=8,
t='out_quad',
d=.1
)
anim.bind(
on_complete=self.update,
on_progress=self.detect_collision,
)
anim.start(self)
def update(self, *args):
self.eat()
self.update_tail()
def eat(self):
if self.head == app.apple:
self.length += self.motion_size
app.spawn_apple()
def update_tail(self):
length = self.length
points = self.head + self.points + self.tail
current_length = sum(
(
Vector(points[2 * i], points[2 * i + 1])
- Vector(points[2 * i + 2], points[2 * i + 3])
).length()
for i in range(len(points) // 2 - 1)
)
if current_length <= self.length:
return
if self.points:
point = Vector(*self.points[-2:])
else:
point = self.head
diff = Vector(*self.tail) - point
target = Vector(*self.tail) - diff.normalize() * self.motion_size
anim = Animation(
tail=target,
line_width=10,
d=.1,
t='out_quad'
)
if point == target:
anim.bind(on_complete=self.pop_tail)
anim.start(self)
def pop_tail(self, *args):
self.points = self.points[:-2]
def turn(self, direction):
if direction == 'up':
vector = Vector(0, 1)
elif direction == 'down':
vector = Vector(0, -1)
elif direction == 'left':
vector = Vector(-1, 0)
elif direction == 'right':
vector = Vector(1, 0)
if vector != self.vector:
self.points = self.head_target + self.points
self.vector = vector
def detect_collision(self, *args):
# we collide if our head is between any pair of consequitive
# points
points = self.points + self.tail
head = self.head
for i in range(1, len(points) // 2 - 1):
p1 = points[2 * i : 2 * i + 2]
p2 = points[2 * i + 2 : 2 * i + 4]
if head[0] == p1[0] == p2[0]:
print("vertical", head, p1, p2)
a, b = sorted((p1[1], p2[1]))
if a <= head[1] <= b:
self.reset()
if head[1] == p1[1] == p2[1]:
print("horizontal", head, p1, p2)
a, b = sorted((p1[0], p2[0]))
if a <= head[0] <= b:
self.reset()
def reset(self):
Animation.cancel_all(self, 'head', 'line_width')
self.vector = Vector(1, 0)
self.length = 2
self.points = []
self.head = [10 * self.motion_size, 10 * self.motion_size]
self.tail = [9 * self.motion_size, 10 * self.motion_size]
class GameBoard(Widget):
pass
class Apple(Widget):
pass
class Application(App):
motion_size = NumericProperty(50)
apple = ListProperty([0, 0])
def build(self):
root = Builder.load_string(KV)
self.snake = snake = Snake()
root.add_widget(snake)
self.spawn_apple()
Window.bind(on_key_down=self.handle_key)
Clock.schedule_interval(snake.update_time, 0)
return root
def handle_key(self, window, keycode, text, modifiers, arg):
if keycode == 274:
self.snake.turn('down')
if keycode == 273:
self.snake.turn('up')
if keycode == 276:
self.snake.turn('left')
if keycode == 275:
self.snake.turn('right')
def spawn_apple(self):
self.apple = (
max(100, min(Window.width - 100, randint(1, 50) * self.motion_size)),
max(100, min(Window.height - 100, randint(1, 10) * self.motion_size)),
)
if __name__ == "__main__":
app = Application()
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment