-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain_normal.py
433 lines (357 loc) · 17.8 KB
/
main_normal.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# Created by Kayden Kehe, May 2022
# This version is for those using a normal Raspberry Pi Pico without a battery
# REMEMBER TO RENAME THIS FILE TO "main.py"
from random import randint, choice
from sh1106 import SH1106_I2C
from utime import sleep, time
from machine import Pin, I2C
# Configure display
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
display = SH1106_I2C(128, 64, i2c, None, 60)
# Configure buttons
restartButton = Pin(22, Pin.IN, Pin.PULL_UP)
leftButton = Pin(28, Pin.IN, Pin.PULL_UP)
rightButton = Pin(1, Pin.IN, Pin.PULL_UP)
upButton = Pin(17, Pin.IN, Pin.PULL_UP)
downButton = Pin(14, Pin.IN, Pin.PULL_UP)
score = 0
games = [1, 2, 3, 4, 5]
currentGame = games[0]
# Turn display on and set black
display.sleep(False)
display.fill(0)
display.rotate(180)
# Create outline
display.fill_rect(0, 0, 128, 2, 1)
display.fill_rect(0, 62, 128, 2, 1)
display.fill_rect(0, 0, 2, 64, 1)
display.fill_rect(126, 0, 2, 64, 1)
def clearScreen(): display.fill_rect(2, 2, 124, 60, 0) # Fill screen with black
def renderMenu():
clearScreen()
display.text('PRESS START', 20, 16, 1)
if currentGame == 1:
display.text('SNAKE', 42, 26, 1)
# Snake symbol
display.fill_rect(32, 41, 4, 12, 1)
display.fill_rect(32, 41, 12, 4, 1)
display.fill_rect(44, 41, 4, 12, 1)
display.fill_rect(44, 49, 12, 4, 1)
display.fill_rect(56, 41, 4, 12, 1)
display.fill_rect(56, 41, 12, 4, 1)
display.fill_rect(68, 41, 4, 12, 1)
display.fill_rect(68, 49, 12, 4, 1)
display.fill_rect(80, 41, 4, 12, 1)
display.fill_rect(80, 41, 12, 4, 1)
elif currentGame == 2:
display.text('BREAKOUT', 32, 26, 1)
# Space Invaders symbol
display.fill_rect(48, 52, 30, 2, 1)
display.fill_rect(48, 50, 30, 2, 1)
display.fill_rect(48, 48, 30, 2, 1)
display.fill_rect(50, 46, 26, 2, 1)
display.fill_rect(58, 44, 10, 2, 1)
display.fill_rect(60, 42, 6, 2, 1)
display.fill_rect(62, 40, 2, 2, 1)
elif currentGame == 3:
display.text('PONG', 46, 26, 1)
# Pong symbol
display.fill_rect(44, 40, 2, 16, 1)
display.fill_rect(78, 40, 2, 16, 1)
display.fill_rect(66, 45, 2, 4, 1)
display.fill_rect(65, 46, 4, 2, 1)
elif currentGame == 4:
display.text('DODGE', 42, 26, 1)
# Dodge symbol
display.fill_rect(56, 40, 12, 14, 1)
display.fill_rect(55, 41, 14, 12, 1)
display.fill_rect(50, 54, 2, 4, 1)
display.fill_rect(49, 55, 4, 2, 1)
display.fill_rect(48, 40, 2, 4, 1)
display.fill_rect(47, 41, 4, 2, 1)
display.fill_rect(75, 45, 2, 4, 1)
display.fill_rect(74, 46, 4, 2, 1)
elif currentGame == 5:
display.text('SPACE RACE', 24, 26, 1)
# Space Race symbol
display.fill_rect(46, 40, 6, 2, 1)
display.fill_rect(48, 42, 4, 2, 1)
display.fill_rect(46, 54, 6, 2, 1)
display.fill_rect(48, 52, 4, 2, 1)
display.fill_rect(52, 40, 4, 16, 1)
display.fill_rect(56, 41, 2, 14, 1)
display.fill_rect(58, 42, 4, 12, 1)
display.fill_rect(60, 42, 4, 12, 1)
display.fill_rect(62, 43, 2, 10, 1)
display.fill_rect(64, 43, 2, 10, 1)
display.fill_rect(66, 43, 2, 10, 1)
display.fill_rect(68, 43, 2, 10, 1)
display.fill_rect(70, 44, 2, 8, 1)
display.fill_rect(72, 44, 2, 8, 1)
display.fill_rect(74, 45, 2, 6, 1)
display.fill_rect(76, 46, 2, 4, 1)
display.fill_rect(78, 47, 2, 2, 1)
display.text(str(currentGame), 116, 52, 1)
display.show()
def startGame():
clearScreen()
global score
score = 0
# Snake
# Snake
# The snake code here is hot garbage. I kept adding features to make it play more nicely, but the features were poorly integrated.
# It works really well, so I'm just choosing to avert my gaze.
if currentGame == 1:
# These variables are being used to make sure velocity can't be changed twice on one button press
leftUsable = True
upUsable = True
rightUsable = True
downUsable = True
snakeHead = [randint(6, 27) * 4, randint(6, 10) * 4] # Position of head of snake
snakeBody = [[snakeHead[0], snakeHead[1]]] # Contains all positions of snake body parts
applePos = [randint(1, 30) * 4, randint(1, 14) * 4]
while applePos in snakeBody: applePos = [randint(1, 30) * 4, randint(1, 14) * 4] # Position of apple
# For velocity, [0, 1] means down, [0, -1] means up, [1, 0] means left, and [-1, 0] means right
snakeInitVelocityX = choice([-1, 1, 0, 0])
snakeInitVelocityY = choice([-1, 1]) if snakeInitVelocityX == 0 else 0
snakeVelocity = [snakeInitVelocityX, snakeInitVelocityY]
timeStep = 0
movementQueue = [snakeVelocity] * 5
secondGrowth = False
while True:
clearScreen()
# End game if snake touches wall or itself
if snakeHead[0] < 2 or snakeHead[0] > 122 or snakeHead[1] < 2 or snakeHead[1] > 58 or snakeHead in snakeBody[:-1]:
score = (len(snakeBody) - 9) // 2
break
if timeStep % 2 == 0:
if downButton.value() == 1: downUsable = True
if upButton.value() == 1: upUsable = True
if rightButton.value() == 1: rightUsable = True
if leftButton.value() == 1: leftUsable = True
# Change velocity based on button press
if downButton.value() == 0 and snakeVelocity != [0, -1] and downUsable:
movementQueue.insert(0, [0, 1])
downUsable = False
if upButton.value() == 0 and snakeVelocity != [0, 1] and upUsable:
movementQueue.insert(0, [0, -1])
upUsable = False
if rightButton.value() == 0 and snakeVelocity != [1, 0] and rightUsable:
movementQueue.insert(0, [-1, 0])
rightUsable = False
if leftButton.value() == 0 and snakeVelocity != [-1, 0] and leftUsable:
movementQueue.insert(0, [1, 0])
leftUsable = False
movementQueue = movementQueue[:5]
snakeVelocity = movementQueue.pop(0)
while len(movementQueue) < 5: movementQueue.insert(0, snakeVelocity)
# Display body of snake
for body in snakeBody:
display.fill_rect(body[0], body[1], 4, 4, 1)
display.fill_rect(applePos[0], applePos[1], 4, 4, 1) # Display apple
# Update snake based on velocity
if snakeVelocity[0] != 0: snakeHead[0] = snakeHead[0] + snakeVelocity[0] * 2
elif snakeVelocity[1] != 0: snakeHead[1] = snakeHead[1] + snakeVelocity[1] * 2
snakeBody.append([snakeHead[0], snakeHead[1]])
# Remove oldest bit of snake unless game started fewer than 8 timesteps ago OR the snake is on the apple
if snakeHead == applePos:
applePos = [randint(1, 30) * 4, randint(1, 14) * 4]
while applePos in snakeBody: applePos = [randint(1, 30) * 4, randint(1, 14) * 4]
secondGrowth = True
elif timeStep >= 8 and secondGrowth == False: snakeBody.pop(0)
elif timeStep >= 8 and secondGrowth == True: secondGrowth = False
display.show()
timeStep += 1
sleep(0.01 if timeStep >= 8 else 0.04)
# Breakout
elif currentGame == 2:
gameOver = False
playerPos = [52, 58]
ballPos = [randint(2, 126), randint(44, 56)]
ballVelocity = [choice([-1, 1]), -1]
# Set positions of blocks to be broken
blocksPos = []
for i in range(5):
for j in range(12):
blocksPos.append([5 + 10 * j, 14 + 4 * i])
while not gameOver:
clearScreen()
display.fill_rect(playerPos[0], playerPos[1], 16, 2, 1)
# Display each block and destroy them if the ball is touching them
for blockPos in blocksPos:
if ballPos[0] - blockPos[0] <= 8 and ballPos[0] - blockPos[0] >= -2 and ballPos[1] - blockPos[1] <= 2 and ballPos[1] - blockPos[1] >= -2:
blocksPos.remove(blockPos)
if ballPos[1] - blockPos[1] == 0 or ballPos[1] - blockPos[1] == -1: ballVelocity[0] = -1 if ballVelocity[0] == 1 else 1
else: ballVelocity[1] = -1 if ballVelocity[1] == 1 else 1
score += 1
else: display.fill_rect(blockPos[0], blockPos[1], 8, 2, 1)
# Change ball velocity when it hits a surface
if ballPos[0] >= 122: ballVelocity[0] = -1
elif ballPos[0] <= 4: ballVelocity[0] = 1
if ballPos[1] <= 2 : ballVelocity[1] = 1
elif ballPos[1] >= 56 and ballPos[0] - playerPos[0] <= 16 and ballPos[0] - playerPos[0] >= 0: ballVelocity[1] = -1
elif ballPos[1] > 56: gameOver = True # Ball goes past player
# Update ball position
ballPos[0] = ballPos[0] + ballVelocity[0]
ballPos[1] = ballPos[1] + ballVelocity[1]
display.fill_rect(ballPos[0], ballPos[1], 2, 2, 1)
# Move player
if leftButton.value() == 0 and playerPos[0] < 108: playerPos[0] = playerPos[0] + 2
if rightButton.value() == 0 and playerPos[0] > 4: playerPos[0] = playerPos[0] - 2
display.show()
sleep(0.001)
# Pong
elif currentGame == 3:
timeStep = 0
wallPos = [126, 0]
wallDirection = -1
playerPos = [4, 24]
gameOver = False
# Position and velocity of all four balls
ballsPos = [] # [[randint(14, 124), randint(2, 62)], [randint(14, 124), randint(2, 62)], [randint(14, 124), randint(2, 62)], [randint(14, 124), randint(2, 62)], [randint(14, 124), randint(2, 62)], [randint(14, 124), randint(2, 62)]]
ballsVelocity = [] # [] [[choice([-1, 1]), choice([-1, 1])], [choice([-1, 1]), choice([-1, 1])], [choice([-1, 1]), choice([-1, 1])], [choice([-1, 1]), choice([-1, 1])], [choice([-1, 1]), choice([-1, 1])], [choice([-1, 1]), choice([-1, 1])]]
for i in range(3):
ballsPos.append([randint(14, 124), randint(2, 62)])
ballsVelocity.append([choice([-1, 1]), choice([-1, 1])])
while not gameOver:
clearScreen()
display.fill_rect(wallPos[0], wallPos[1], 2, 64, 1)
display.fill_rect(playerPos[0], playerPos[1], 2, 20, 1)
for ballPos, ballVelocity in zip(ballsPos, ballsVelocity):
# Change ball velocity when it hits surface
if ballPos[0] >= wallPos[0] - 2: ballVelocity[0] = -1
if ballPos[1] >= 58: ballVelocity[1] = -1
elif ballPos[1] <= 4: ballVelocity[1] = 1
# Ball hits player paddle
if ballPos[0] == 6 and ballPos[1] - playerPos[1] <= 20 and ballPos[1] - playerPos[1] > 0:
ballVelocity[0] = 1
score += 1
if ballPos[0] < 6: gameOver = True # Ball goes past player
# Update ball position
ballPos[0] = ballPos[0] + ballVelocity[0]
ballPos[1] = ballPos[1] + ballVelocity[1]
display.fill_rect(ballPos[0], ballPos[1], 2, 2, 1)
# Move player
if upButton.value() == 0 and playerPos[1] > 4: playerPos[1] = playerPos[1] - 2
if downButton.value() == 0 and playerPos[1] < 40: playerPos[1] = playerPos[1] + 2
display.show()
sleep(0.001)
timeStep += 1
if timeStep % 3 == 0:
if wallPos[0] <= 32: wallDirection = 1
elif wallPos[0] >= 126: wallDirection = -1
wallPos[0] = wallPos[0] + wallDirection
# Dodge
elif currentGame == 4:
beginTime = time()
playerPos = [9, 8]
ballsPos = []
ballsVelocity = []
for _ in range(6):
ballsPos.append([randint(12, 124), randint(12, 62)])
ballsVelocity.append([choice([-1, 1]), choice([-1, 1])])
gameOver = False
# Make at most four balls double speed
speedBalls = [randint(0,5), randint(0,5), randint(0,5), randint(0,5)]
while not gameOver:
clearScreen()
display.fill_rect(playerPos[0], playerPos[1], 8, 10, 1)
display.fill_rect(playerPos[0] - 1, playerPos[1] + 1, 10, 8, 1)
for ballPos, ballVelocity in zip(ballsPos, ballsVelocity):
# Change ball velocity when it hits surface
if ballPos[0] >= 124: ballVelocity[0] = -1
elif ballPos[0] <= 4: ballVelocity[0] = 1
if ballPos[1] >= 60: ballVelocity[1] = -1
elif ballPos[1] <= 2: ballVelocity[1] = 1
# Update ball position
if ballsPos.index(ballPos) in speedBalls:
ballPos[0] = ballPos[0] + ballVelocity[0] * 2
ballPos[1] = ballPos[1] + ballVelocity[1] * 2
else:
ballPos[0] = ballPos[0] + ballVelocity[0]
ballPos[1] = ballPos[1] + ballVelocity[1]
display.fill_rect(ballPos[0], ballPos[1], 2, 2, 1)
if ballPos[0] - playerPos[0] <= 10 and ballPos[0] - playerPos[0] >= -3 and ballPos[1] - playerPos[1] <= 10 and ballPos[1] - playerPos[1] >= -3:
score = round(time() - beginTime)
gameOver = True
if leftButton.value() == 0 and playerPos[0] <= 114: playerPos[0] = playerPos[0] + 2
if rightButton.value() == 0 and playerPos[0] >= 7: playerPos[0] = playerPos[0] - 2
if upButton.value() == 0 and playerPos[1] >= 5: playerPos[1] = playerPos[1] - 2
if downButton.value() == 0 and playerPos[1] <= 48: playerPos[1] = playerPos[1] + 2
display.show()
sleep(0.001)
# Space Race
elif currentGame == 5:
gameOver = False
playerPos = [62, 41]
playerHeight = 0
blocksPos = [] # Includes xPos, yPos, and velocity
# Create floor
for i in range(7):
blocksPos.append([41 + 7 * i, 55, 0])
# Create first two obstacles
for i in range(2):
blocksPos.append([randint(34, 90), 16 + 16 * i, choice([-1, 1])])
while not gameOver:
clearScreen()
display.fill_rect(34, 0, 2, 128, 1)
display.fill_rect(92, 0, 2, 128, 1)
for i, blockPos in enumerate(blocksPos):
# See if block is colliding with player
if blockPos[0] - playerPos[0] <= 6 and blockPos[0] - playerPos[0] >= -6 and blockPos[1] - playerPos[1] <= 10 and blockPos[1] - playerPos[1] >= -2: gameOver = True
# See if block is colliding with wall
if blockPos[0] > 88: blocksPos[i][2] = -1
elif blockPos[0] < 36: blocksPos[i][2] = 1
if blockPos[1] > 135: blocksPos.remove(blockPos)
else:
blocksPos[i][0] = blockPos[0] + blockPos[2]
display.fill_rect(blockPos[0], blockPos[1], 4, 2, 1)
if upButton.value() == 0:
# Move player up at start
if playerHeight <= 12:
playerPos[1] = playerPos[1] - 1
playerHeight += 1
# Move obstacles down after start
else:
for blockPos in blocksPos: blockPos[1] = blockPos[1] + 1
playerHeight += 1
if playerHeight % 16 == 0:
# Create new obstacle when the player travels up for 16 frames
blocksPos.append([randint(34, 90), 0, choice([-1, 1])])
score += 1
display.fill_rect(playerPos[0], playerPos[1], 4, 10, 1)
display.fill_rect(playerPos[0] - 1, playerPos[1] + 1, 6, 8, 1)
display.show()
sleep(0.001)
clearScreen()
# Read and write highscores
highscore = 0
with open('highscores.txt', 'r+') as hs:
hsLines = hs.readlines()
if score > int(hsLines[currentGame - 1].strip()): hsLines[currentGame - 1] = str(score) + '\n'
hs.seek(0)
hs.write(''.join(hsLines))
highscore = int(hsLines[currentGame - 1].strip())
display.text('GAME OVER', 28, 16, 1)
display.text(f'SCORE: {score}', 28, 28, 1)
display.text(f'BEST: {highscore}', 28, 40, 1)
display.show()
sleep(0.5)
def mainLoop():
global currentGame
canNavigate = True # Prevent holding down to scroll through menu
while True:
if upButton.value() == 1 and downButton.value() == 1: canNavigate = True
if restartButton.value() == 0: startGame() # Start game when start button pressed
# Cycle through menu when up/down buttons pressed
elif upButton.value() == 0 and canNavigate:
canNavigate = False
currentGame = games[(currentGame) % len(games)]
renderMenu()
elif downButton.value() == 0 and canNavigate:
canNavigate = False
currentGame = games[(currentGame - 2) % len(games)]
renderMenu()
sleep(0.1)
renderMenu()
mainLoop()