adds game

This commit is contained in:
Rosia E Evans 2024-12-01 16:37:27 +00:00
parent 4496ba0811
commit 50e4712545
24 changed files with 528 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

101
display.py Normal file
View file

@ -0,0 +1,101 @@
import curses
from _curses import A_DIM, A_BLINK, A_STANDOUT, A_BOLD
from curses import initscr
from tunnel import Tunnel
class Display:
def __init__(self, scr):
self.scr = scr
curses.noecho()
curses.cbreak()
curses.curs_set(0)
self.darkness_revealed = False
def teardown_curses(self):
curses.nocbreak()
curses.echo()
curses.endwin()
def new_frame(self):
self.scr.clear()
def display_frame(self):
self.scr.refresh()
def display_lose_screen(self):
self.scr.addstr(2, 2, "the small creature died")
self.scr.addstr(3, 2, "the tunnel was empty again")
self.scr.addstr(4, 2, "any key to exit")
self.scr.addstr(6, 13, " \\_")
self.scr.addstr(7, 13, "\\ ")
def display_win_screen(self):
self.scr.addstr(2, 2, "the small creature explored the whole tunnel")
self.scr.addstr(3, 2, "they had a nice time and were ready to go find another")
self.scr.addstr(4, 2, "but first they wanted to sleep")
self.scr.addstr(5, 2, "any key to exit")
self.scr.addstr(7, 8, "Z")
self.scr.addstr(8, 7, "zᶻ")
self.scr.addstr(9, 5, "> ᶻ")
# tunnel is 15 long
def display_tunnel(self, tunnel: Tunnel):
player_pos = 15
y = 3
x = 2
self.scr.addstr(y, x, tunnel.get_as_str(), A_DIM)
self.scr.addch(y, x+player_pos, ">")
if self.darkness_revealed:
self.scr.addch(y, x+player_pos+1, tunnel.next_step.char)
else:
self.scr.addch(y, x+player_pos+1, "", A_DIM)
self.scr.refresh()
def display_lives(self, lives):
lives_string = "lives: " + ("v"*lives)
self.scr.addstr(1, 2, lives_string, A_DIM)
def display_level(self, level):
self.scr.addstr(6, 2, "level: " + str(level), A_DIM)
def display_steps(self, steps, total_steps):
lives_string = "steps: " + str(steps) + "/" + str(total_steps)
self.scr.addstr(5, 2, lives_string, A_DIM)
def display_next_level(self):
self.scr.addstr(2, 2, "The tunnel continues")
self.scr.addstr(3, 2, "deeper to the next level")
self.scr.addstr(5, 12, "")
def display_menu_art(self):
self.scr.addstr(1, 13, " \\_")
self.scr.addstr(2, 13, "\\ <")
self.scr.addstr(4, 2, "A small creature")
self.scr.addstr(4, 19, "explores", A_BOLD)
self.scr.addstr(4, 28, "a tunnel")
def display_menu(self, options, index):
y = 7
x = 13
for i, option in enumerate(options):
if i == index:
self.scr.addstr(y+i, x-1, ">")
self.scr.addstr(y+i, x, option)
else:
self.scr.addstr(y+i, x, option, A_DIM)
# ---o---o---o->-▓

24
enemy.py Normal file
View file

@ -0,0 +1,24 @@
class Enemy:
def __init__(self, char):
self.char = char
self.key = char
class Empty(Enemy):
def __init__(self):
super().__init__(" ")
self.char = "-"
class Goblin(Enemy):
def __init__(self):
super().__init__("o")
class Slime(Enemy):
def __init__(self):
super().__init__("e")
class KnifeRat(Enemy):
def __init__(self):
super().__init__("c")

33
input.py Normal file
View file

@ -0,0 +1,33 @@
import curses
from curses import *
class Input:
def __init__(self, scr):
self.scr = scr
halfdelay(10)
def wait_on_any_key(self) -> int:
try:
key_given = self.scr.getch()
return key_given
except curses.error:
pass
def wait_on_key(self, key):
try:
key_given = self.scr.getch()
if key_given is None:
return False
if key_given == ord(key):
return True
except curses.error:
pass
return False
def wait_indefinitely_for_key(self):
nocbreak()
self.scr.getch()
halfdelay(20)

49
level.py Normal file
View file

@ -0,0 +1,49 @@
from math import sqrt
from typing import Union
from enemy import *
class Level:
def __init__(self, level_gen):
self.level_gen = level_gen
self.steps = 0
self.length = 40
def get_next_step(self) -> Union[Enemy, None]:
self.steps += 1
return self.level_gen(self.steps)
class LevelLibrary:
def __init__(self, levels):
self.levels = levels
self.current_level = 0
def __iter__(self):
return self
def __next__(self):
next_level = self.get_level(self.current_level)
if next_level is None:
raise StopIteration
self.current_level += 1
return next_level
def get_level(self, level_number: int) -> Union[Level, None]:
if level_number >= len(self.levels):
return None
else:
return Level(self.levels[level_number])
level_lib = LevelLibrary(
[
lambda step : Goblin() if step%2==0 else Empty(),
lambda step : Goblin() if step%3==0 else Empty(),
lambda step : Goblin() if step%4==0 else KnifeRat() if step%2==0 else Empty(),
lambda step : Slime() if sqrt((step * 8) + 1).is_integer() else Empty(),
lambda step : KnifeRat() if step%3==0 and step%5==0 else Goblin() if step%3==0 else Slime() if step%5==0 else Empty(),
]
)

69
level_runner.py Normal file
View file

@ -0,0 +1,69 @@
from enum import Enum
from display import Display
from input import Input
from level import Level
from player_stats import PlayerStats
from tunnel import Tunnel
class State(Enum):
GUESSING = 0
REVEALED = 1
class LevelRunner:
def __init__(self, disp: Display, inp: Input, player: PlayerStats):
self.disp = disp
self.inp = inp
self.state = State.GUESSING
self.player = player
def reveal(self):
self.state = State.REVEALED
def move_to_next_step(self, tunnel: Tunnel, level: Level):
tunnel.append_step(level.get_next_step())
self.state = State.GUESSING
def run_level(self, level: Level, level_number: int):
tunnel = Tunnel()
self.player.lives = 10
while True:
self.disp.new_frame()
if self.player.is_dead():
return 0
if level.steps >= level.length:
return 1
self.disp.display_lives(self.player.lives)
self.disp.display_steps(level.steps, level.length)
self.disp.display_level(level_number)
match self.state:
case State.GUESSING:
self.disp.darkness_revealed = False
self.disp.display_tunnel(tunnel)
self.disp.display_frame()
guesses_correct = self.inp.wait_on_key(tunnel.next_step.key)
if not guesses_correct:
self.player.lose_life()
else:
# self.player.heal_life()
pass
self.reveal()
case State.REVEALED:
self.disp.darkness_revealed = True
self.disp.display_tunnel(tunnel)
self.disp.display_frame()
self.inp.wait_on_any_key() # wait on any key
self.move_to_next_step(tunnel, level)

102
main.py Normal file
View file

@ -0,0 +1,102 @@
import curses
from curses import wrapper
from enum import Enum
from time import sleep
from enemy import KnifeRat
from input import Input
from level import Level, level_lib
from display import Display
from level_runner import LevelRunner
from player_stats import PlayerStats
from tunnel import Tunnel
from tutorial import tutorial
class App:
def __init__(self, scr):
self.disp = Display(scr)
self.inp = Input(scr)
def app(self):
selected_item = 0
menu_items = ["play", "tutorial", "quit"]
while True:
self.disp.new_frame()
self.disp.display_menu_art()
self.disp.display_menu(menu_items, selected_item)
self.disp.display_frame()
key = self.inp.wait_on_any_key()
if key == curses.KEY_UP or key == ord("w"):
selected_item -= 1
if selected_item < 0:
selected_item = len(menu_items)-1
elif key == curses.KEY_DOWN or key == ord("s"):
selected_item += 1
if selected_item >= len(menu_items):
selected_item = 0
elif key == ord("\n") or key == ord(" "):
match menu_items[selected_item]:
case "play":
self.cycle_through_levels()
case "tutorial":
self.tutorial()
case "quit":
exit(0)
def cycle_through_levels(self):
stats = PlayerStats()
level_runner = LevelRunner(self.disp, self.inp, stats)
level_lib.current_level = 0
for level in level_lib:
score = level_runner.run_level(level, level_lib.current_level)
if score == 0:
self.defeat_screen()
self.disp.display_next_level()
self.inp.wait_indefinitely_for_key()
self.inp.wait_indefinitely_for_key() # wait twice to debounce
self.win_screen()
def defeat_screen(self):
self.disp.display_lose_screen()
self.inp.wait_indefinitely_for_key()
self.inp.wait_indefinitely_for_key() # wait twice to debounce
exit(0)
def win_screen(self):
self.disp.display_win_screen()
self.inp.wait_indefinitely_for_key()
self.inp.wait_indefinitely_for_key() # wait twice to debounce
exit(0)
def tutorial(self):
tutorial(self.disp, self.inp)
if __name__ == '__main__':
def run(scr):
app = App(scr)
app.app()
wrapper(run)
# todo fix fizzbuzz
# todo tutorial

17
player_stats.py Normal file
View file

@ -0,0 +1,17 @@
class PlayerStats:
def __init__(self):
self.lives = 10
def lose_life(self):
self.lives -= 1
def heal_life(self):
if self.lives < 10:
self.lives += 1
def is_dead(self):
if self.lives <= 0:
return True
else:
return False

19
tunnel.py Normal file
View file

@ -0,0 +1,19 @@
from queue import Queue
from enemy import *
class Tunnel:
def __init__(self):
self.next_step = Empty()
self.contents = Queue()
for i in range(15): self.contents.put(Empty())
print(self.contents.qsize())
def append_step(self, step):
self.contents.get()
self.contents.put(self.next_step)
self.next_step = step
def get_as_str(self) -> str:
result = [i.char for i in self.contents.queue]
return "".join(result)

114
tutorial.py Normal file
View file

@ -0,0 +1,114 @@
from time import sleep
from enemy import Goblin, Empty
from level import level_lib
from level_runner import LevelRunner
from player_stats import PlayerStats
from tunnel import Tunnel
def tutorial(disp, inp):
tunnel = Tunnel()
disp.new_frame()
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(4, 17, "^ this is you")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v its dark, you cant see whats infront of you")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v hit space to reveal the next step")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.darkness_revealed = True
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v hit space again to move forwards")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.darkness_revealed = False
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v this next step has an enemy in it")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v this enemy is an o, hit that key to handle it")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.darkness_revealed = True
tunnel.append_step(Goblin())
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(2, 18, "v there we go! hit space to step forward again")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.darkness_revealed = False
tunnel.append_step(Empty())
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(4, 18, "^ beware that you'll step forward automatically after a second")
disp.scr.addstr(5, 18, " ain't no time for dawdlin' in the tunnels!")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
disp.display_tunnel(tunnel)
disp.scr.addstr(9, 2, "Enter to continue >")
disp.scr.addstr(5, 2, "recognise the pattern, predict the enemies and handle them")
disp.scr.addstr(6, 2, "you'll lose a few lives to start with, but you'll pick it up quickly")
disp.scr.addstr(7, 2, "survive 40 steps to complete a level")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()
disp.new_frame()
level_runner = LevelRunner(disp, inp, PlayerStats())
level_runner.run_level(level_lib.get_level(0), 0)
disp.new_frame()
disp.scr.addstr(2, 2, "You got this! Go explore the real tunnels now!")
disp.display_frame()
sleep(0.5)
inp.wait_indefinitely_for_key()