Skip to content

Commit

Permalink
v1.1 comments + doc + general integer game state codes.
Browse files Browse the repository at this point in the history
  • Loading branch information
amtoine committed Jul 5, 2021
1 parent 6a58e39 commit 6e4a9b7
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 48 deletions.
15 changes: 8 additions & 7 deletions src/Apple.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import numpy as np

from src.utils import log


class Apple:
"""
Expand All @@ -26,17 +24,20 @@ def spawn(self, y, x, h, w):
Args
----
y : int
y : 0 <= int < curses.LINES
the y coordinate of the top left corner of the game area.
x : int
x : 0 <= int < curses.COLS
the x coordinate of the top left corner of the game area.
h : int
h : 0 <= int < curses.LINES - y
the height of the game area.
w : int
w : 0 <= int < curses.COLS - x
the width of the game area.
Returns
-------
None
"""
self.pos = (np.random.randint(y, y + h), np.random.randint(x, x + w))
log(self.pos)

def show(self, stdscr):
"""
Expand Down
11 changes: 7 additions & 4 deletions src/Snake.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,20 @@ def spawn(self, y, x, h, w, init_length=3):
Args
----
y : int >= 0
y : 0 <= int < curses.LINES
the y coordinate of the top left corner of the game area.
x : int >= 0
x : 0 <= int < curses.COLS
the x coordinate of the top left corner of the game area.
h : int >= 0
h : 0 <= int < curses.LINES - y
the height of the game area.
w : int >= 0
w : 0 <= int < curses.COLS - x
the width of the game area.
init_length : int >= 0, optional
the initial length of the snake.
Returns
-------
None
"""
# cache the scene.
self.scene = y, x, h, w
Expand Down
178 changes: 144 additions & 34 deletions src/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,186 @@

_wall = 10

MENU = 1
PLAY = 2
LOST = 3


def init_borders(y, x, h, w):
"""
Initializes the borders of the game board.
Args
----
y : 0 <= int < curses.LINES
the y coordinate of the top left corner of the game area.
x : 0 <= int < curses.COLS
the x coordinate of the top left corner of the game area.
h : 0 <= int < curses.LINES - y
the height of the game area.
w : 0 <= int < curses.COLS - x
the width of the game area.
Returns
-------
top, bottom, left, right : tuple of list of int 2-uples
the positions of all the cells constituting the borders of the board.
"""
top = [(y - 1, x + dx - 1) for dx in range(w + 2)]
bottom = [(y + h, x + dx - 1) for dx in range(w + 2)]
left = [(y + dy, x - 1) for dy in range(h)]
right = [(y + dy, x + w) for dy in range(h)]
return top, bottom, left, right


def blit(stdscr, borders, snake, apples, score, game_state, debug_msg, fps):
stdscr.erase()

for y, x in borders:
stdscr.insch(y, x, ' ', curses.color_pair(_wall))

snake.show(stdscr)
for apple in apples:
apple.show(stdscr)

msgs = [
# f"{snake.trail = }",
# f"{len(snake.body) = }",
f"{score = }",
f"{game_state = }",
f"{debug_msg = }"
]
h = 0 if snake.get_head()[0] > curses.LINES // 2 else curses.LINES - len(msgs)
for row, msg in zip(range(len(msgs)), list(map(str, msgs))):
stdscr.addstr(h + row, 0, msg)

stdscr.refresh()
time.sleep(1 / fps)


def handle_input(c, game_state, scene, snake, apples):
"""
Handles the input of the game.
Args
----
c : int
the input code.
game_state : int
the current state of the game.
scene : 4-uple of ints
the geometry of the game area.
snake : src.Snake.Snake
the snake.
apples : list of src.Apple.Apple instances.
all the apples in the game.
Returns
-------
quit, reset, new_game_state : bool, int, int
a signal telling if the game is over, a reset integer for the score and the new state of the game.
"""
# quit the game.
if c in [27, ord('q')]:
return True, None, None

# respawn the objects if 'r' is pressed whilst not playing
reset = 0
new_game_state = game_state
if c == ord('r'):
if game_state != "play":
if game_state == LOST:
snake.spawn(*scene)
for apple in apples:
apple.spawn(*scene)
reset = 1
game_state = "play"
new_game_state = PLAY

# switch between the menu and the play when 'enter' is pressed.
if c == 10:
game_state = "menu" if game_state == "play" else "play"
new_game_state = MENU if game_state == PLAY else PLAY

return False, 1 - reset, game_state
return False, 1 - reset, new_game_state


def menu():
"""
---
Args
----
Returns
-------
"""
pass


def play(c, snake, apples, score):
"""
Plays one step of the game of Snake.
Args
----
c : int
the input code.
snake : src.Snake.Snake
the snake.
apples : list of src.Apple.Apple instances.
all the apples in the game.
score : int
the current score in the game.
Returns
-------
new_score, new_game_state, new_debug_msg : int, int, str
the new score, the new state of the game, the new debug message.
"""
# update the snake.
snake.change_direction(c)
snake.move()
code = snake.update(snake.is_eating(apples))

# if the snake is dead, beep + stop game + change debug message. Otherwise, increment the score.
if code < Snake.NOTHING:
curses.beep()
game_state = "lost"
new_game_state = LOST
if code == Snake.OUTSIDE:
debug_msg = f"hit walls -> {score}"
new_debug_msg = f"hit walls -> {score}"
elif code == Snake.SELF_BITE:
debug_msg = f"self_intersect -> {score}"
new_debug_msg = f"self_intersect -> {score}"
else:
debug_msg = None
return None, game_state, debug_msg
new_debug_msg = None
return None, new_game_state, new_debug_msg
else:
return score + code, None, None


def blit(stdscr, borders, snake, apples, score, game_state, debug_msg, fps):
"""
Blits all the objects of the game onto the screen.
Args
----
stdscr : _curses.window
the screen object.
borders : list of int 2-uples
the positions of all the borders elements.
snake : src.Snake.Snake
the snake.
apples : list of src.Apple.Apple instances.
all the apples in the game.
score : int
the current score in the game.
game_state : int
the current state of the game.
debug_msg : str
the current debug message.
fps ; int
the frame rate of the game.
Returns
-------
None
"""
# erase the screen.
stdscr.erase()

# print the borders.
for y, x in borders:
stdscr.insch(y, x, ' ', curses.color_pair(_wall))

# show the entities.
snake.show(stdscr)
for apple in apples:
apple.show(stdscr)

# print some strings.
msgs = [
# f"{snake.trail = }",
# f"{len(snake.body) = }",
f"{score = }",
f"{game_state = }",
f"{debug_msg = }"
]
h = 0 if snake.get_head()[0] > curses.LINES // 2 else curses.LINES - len(msgs)
for row, msg in zip(range(len(msgs)), list(map(str, msgs))):
stdscr.addstr(h + row, 0, msg)

# refresh and wait.
stdscr.refresh()
time.sleep(1 / fps)
8 changes: 5 additions & 3 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from src.errors import CustomError

from src.game import _wall
from src.game import PLAY
from src.game import MENU
from src.game import init_borders
from src.game import handle_input
from src.game import menu
Expand Down Expand Up @@ -53,7 +55,7 @@ def main(stdscr):
apple.spawn(*scene)

# internal variables of the game.
game_state = "play"
game_state = PLAY
game_debug_msg = ''
game_score = 0

Expand All @@ -68,9 +70,9 @@ def main(stdscr):
game_score *= reset

# run the current game state code snippet.
if game_state == "menu":
if game_state == MENU:
menu()
elif game_state == "play":
elif game_state == PLAY:
new_score, new_game_state, new_debug_msg = play(c, snake, apples, game_score)
if new_score:
game_score = new_score
Expand Down

0 comments on commit 6e4a9b7

Please sign in to comment.