-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsnake.py
213 lines (177 loc) · 7.9 KB
/
snake.py
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
"""
Filename: snake.py
Author: Kyle Cookerly
Description: The Snake class for terminal-based snake clone
"""
class Snake:
"""Class represents the snake"""
UP_KEY = ord("w")
DOWN_KEY = ord("s")
LEFT_KEY = ord("a")
RIGHT_KEY = ord("d")
QUIT_KEY = ord("q")
PAUSE_KEY = ord(" ")
SEGMENT_CHAR = "#"
INITIAL_LENGTH = 3
STARTING_X = 30
STARTING_Y = 9
snake_position = [STARTING_X, STARTING_Y]
snake_body = [snake_position[:]] * INITIAL_LENGTH
key = None
game_over = False
last_valid_key = None
def __init__(self, window, width, height):
"""
:param window: the window object that the game creates
:param width: the width of the board
:param height: the height of the board
"""
self.window = window
self.board_width = width
self.board_height = height
# TODO move the user input to the game class and pass in the key to this function
def move_position(self):
"""
Takes input from the keyboard for the snake movement using 'W' 'A' 'S' 'D' keys
If there is no input, movement continues in direction of the last key press
Invalid input will continue the snake on the path of the last valid key press
"""
# TODO find a better way to handle the nested ifs
movement = self.window.getch()
self.key = self.key if movement == -1 else movement
# if the snake is moving up ignore the key press if the down key is pressed
if self.key == self.UP_KEY:
if self.last_valid_key != self.DOWN_KEY:
self.last_valid_key = self.UP_KEY
self.move_up()
else:
self.move_down()
# if the snake is moving left ignore the key press if the right key is pressed
elif self.key == self.LEFT_KEY:
if self.last_valid_key != self.RIGHT_KEY:
self.last_valid_key = self.LEFT_KEY
self.move_left()
else:
self.move_right()
# if the snake is moving down ignore the key press if the up key is pressed
elif self.key == self.DOWN_KEY:
if self.last_valid_key != self.UP_KEY:
self.last_valid_key = self.DOWN_KEY
self.move_down()
else:
self.move_up()
# if the snake is moving right ignore the key press if the left key is pressed
elif self.key == self.RIGHT_KEY:
if self.last_valid_key != self.LEFT_KEY:
self.last_valid_key = self.RIGHT_KEY
self.move_right()
else:
self.move_left()
elif self.key == self.QUIT_KEY:
self.game_over = True
elif self.key == self.PAUSE_KEY:
# TODO do something better with pausing, make it so the snake doesn't get shorter when the game is paused
pass
else:
# Any invalid key press makes the snake continue moving in last valid direction
if self.last_valid_key == self.UP_KEY:
self.move_up()
elif self.last_valid_key == self.LEFT_KEY:
self.move_left()
elif self.last_valid_key == self.DOWN_KEY:
self.move_down()
elif self.last_valid_key == self.RIGHT_KEY:
self.move_right()
def display_snake(self):
"""Draws the snake to the console"""
end_of_snake = self.snake_body[-1][:]
for i in range(len(self.snake_body) - 1, 0, -1):
self.snake_body[i] = self.snake_body[i - 1]
if end_of_snake not in self.snake_body:
self.window.addch(end_of_snake[1], end_of_snake[0], " ") # Erases the end of the snake
self.window.addch(self.snake_position[1], self.snake_position[0], self.SEGMENT_CHAR) # Draws the snake head
self.snake_body[0] = self.snake_position[:]
def check_tail_collision(self):
"""Checks if the snake head collides with the tail, gameover if true"""
if self.key == self.UP_KEY and not self.did_go_back_on_self():
if self.window.inch(self.snake_position[1], self.snake_position[0]) == ord(self.SEGMENT_CHAR):
self.game_over = True
elif self.key == self.LEFT_KEY and not self.did_go_back_on_self():
if self.window.inch(self.snake_position[1], self.snake_position[0]) == ord(self.SEGMENT_CHAR):
self.game_over = True
elif self.key == self.DOWN_KEY and not self.did_go_back_on_self():
if self.window.inch(self.snake_position[1], self.snake_position[0]) == ord(self.SEGMENT_CHAR):
self.game_over = True
elif self.key == self.RIGHT_KEY and not self.did_go_back_on_self():
if self.window.inch(self.snake_position[1], self.snake_position[0]) == ord(self.SEGMENT_CHAR):
self.game_over = True
# TODO better name
def jump_snake_position(self):
"""
Function stops the wall bug from crashing the game if you get stuck between the walls.
Places the snake on the opposite side if you are in the range to get stuck in the wall.
Thus, if the snake is moving on the boundary itself, parallel with the wall, it will be
moved to the other side.
"""
# top wall
if self.snake_position[1] == self.board_height - 1 and self.key == self.LEFT_KEY:
self.snake_position[1] = 1
elif self.snake_position[1] == self.board_height - 1 and self.key == self.RIGHT_KEY:
self.snake_position[1] = 1
# bottom wall
if self.snake_position[1] == 0 and self.key == self.LEFT_KEY:
self.snake_position[1] = self.board_height - 2
elif self.snake_position[1] == 0 and self.key == self.RIGHT_KEY:
self.snake_position[1] = self.board_height - 2
# left wall
if self.snake_position[0] == 0 and self.key == self.UP_KEY:
self.snake_position[0] = self.board_width - 2
elif self.snake_position[0] == 0 and self.key == self.DOWN_KEY:
self.snake_position[0] = self.board_width - 2
# right wall
if self.snake_position[0] == self.board_width - 1 and self.key == self.UP_KEY:
self.snake_position[0] = 1
elif self.snake_position[0] == self.board_width - 1 and self.key == self.DOWN_KEY:
self.snake_position[0] = 1
def is_game_over(self):
"""Returns boolean for main game loop to end or continue"""
return self.game_over
def grow_snake(self):
"""Appends the snake when called adding 1 segment to the end of its body"""
self.snake_body.append(self.snake_body[-1])
# TODO better name?
def did_go_back_on_self(self):
"""
Checks if the snake went back on its self
eg. did the user press to go down if the snake was moving up
thus running over the second snake segment
"""
if self.last_valid_key == self.UP_KEY:
if self.key == self.DOWN_KEY:
return True
elif self.last_valid_key == self.DOWN_KEY:
if self.key == self.UP_KEY:
return True
elif self.last_valid_key == self.LEFT_KEY:
if self.key == self.RIGHT_KEY:
return True
elif self.last_valid_key == self.RIGHT_KEY:
if self.key == self.LEFT_KEY:
return True
return False
def move_up(self):
self.snake_position[1] -= 1
def move_down(self):
self.snake_position[1] += 1
def move_left(self):
self.snake_position[0] -= 1
def move_right(self):
self.snake_position[0] += 1
def get_snake_head_x(self):
return self.snake_position[0]
def get_snake_head_y(self):
return self.snake_position[1]
def set_snake_head_x(self, x_val: int):
self.snake_position[0] = x_val
def set_snake_head_y(self, y_val: int):
self.snake_position[1] = y_val