I based the code below on this article: http://kevman3d.blogspot.com/2015/07/basic-games-in-python-1982-would-be.html
and on the ZX BASIC in this image:
10 LET P=0
20 LET T=P
30 FOR Z=1 T0 10
35 CLS
37 PRINT AT 12,0;T
40 LET R=INT (RND*17)
50 FOR Y=0 TO 10
60 PRINT AT Y,R;"O"
70 LET N=P(INKEY$="4")-(INKEY$="1")
80 IF N<0 OR N>15 THEN LET N=P
100 PRINT AT 11,P;" ";AT 11,N;"┗┛";AT Y,R;" "
110 LET P=N
120 NEXT Y
130 LET T=T+(P=R OR P+1=R)
150 NEXT Z
160 PRINT AT 12,0;"YOU SCORED ";T;"/10"
170 PAUSE 4E4
180 RUN
I also shared it on Code Review Stack Exchange, and got a very helpful response refactoring it into high quality Python code complete with type hints.
However, for my purposes I'm wanting to keep the level of knowledge required to make this work a little less advanced, including avoiding the use of OOP. I basically want to maintain the "spirit of ZX BASIC" but make the code "not awful." The use of functions is fine, as we were allowed GOSUB
back in the day.
I'm pretty dubious about the approach of using nested FOR
loops inside the main game loop to make the game work, but at the same time I'm curious to see how well the BASIC paradigm maps onto the more event driven approach of Pygame, so I'd welcome any comments on the pros and cons of this approach.
More specifically,
Is there somewhere I can put the exit code
if event.type == pygame.QUIT
where it will work during game rounds, without having to repeat the code elsewhere?How would this game be implemented if I were to avoid the use of
FOR
loops / nestedFOR
loops?Are there any points of best practice for pygame/Python which I have violated?
What improvements can you suggest, bearing in mind my purpose is to write good Pygame code while maintaining the "spirit" of the ZX81 games?
Any input much appreciated. I'm also curious to see a full listing implementing some of the ideas arising from my initial attempt if anyone is willing to provide one.
import pygame
import random
import sys
# Define colors and other global constants
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
TEXT_SIZE = 16
SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE)
NUM_ROUNDS = 5
def print_at_pos(row_num, col_num, item):
"""Blits text to row, col position."""
screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE))
# Set up stuff
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Dropout")
game_font = pygame.font.SysFont('consolas', TEXT_SIZE)
# Create clock to manage how fast the screen updates
clock = pygame.time.Clock()
# initialize some game variables
player_pos, new_player_pos, coin_row, score = 0, 0, 0, 0
# -------- Main Program Loop -----------
while True:
score = 0
# Each value of i represents 1 round
for i in range(NUM_ROUNDS):
coin_col = random.randint(0, 15)
# Each value of j represents one step in the coin's fall
for j in range(11):
pygame.event.get()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_RIGHT]:
new_player_pos = player_pos + 1
elif pressed[pygame.K_LEFT]:
new_player_pos = player_pos - 1
if new_player_pos < 0 or new_player_pos > 15:
new_player_pos = player_pos
# --- Game logic
player_pos = new_player_pos
coin_row = j
if player_pos + 1 == coin_col and j == 10:
score += 1
# --- Drawing code
# First clear screen
screen.fill(WHITE)
player_icon = game_font.render("|__|", True, BLACK, WHITE)
print_at_pos(10, new_player_pos, player_icon)
coin_text = game_font.render("O", True, BLACK, WHITE)
print_at_pos(coin_row, coin_col, coin_text)
score_text = game_font.render(f"SCORE: {score}", True, BLACK, WHITE)
print_at_pos(12, 0, score_text)
# --- Update the screen.
pygame.display.flip()
# --- Limit to 6 frames/sec maximum. Adjust to taste.
clock.tick(8)
msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, BLACK, WHITE)
print_at_pos(5, 0, msg_text)
pygame.display.flip()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
if event.type == pygame.KEYDOWN:
waiting = False