Created
July 7, 2022 10:47
-
-
Save yhirose/53522b49dfcf262e7ea67f2e559cbcb5 to your computer and use it in GitHub Desktop.
Froggy - Simple game on Terminal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import curses | |
import datetime | |
import locale | |
import noise | |
ROWS = 16 | |
COLS = 16 | |
def get_rows_and_cols(scr): | |
rows, cols = scr.getmaxyx() | |
return rows, int((cols - 1) / 2) | |
def output(scr, y, x, s): | |
rows, cols = get_rows_and_cols(scr) | |
offx = int((cols - COLS) / 2) | |
offy = int((rows - ROWS) / 2) | |
scr.addstr(y + offy, (x + offx) * 2, s) | |
def get_perlin_noise(base, a): | |
x = int((noise.pnoise1(base * a) + 1) / 2 * ROWS) | |
y = int((noise.pnoise1((base + 10000) * a) + 1) / 2 * COLS) | |
return x, y | |
class Frog: | |
def __init__(self): | |
self.x = int(COLS / 2) - 1 | |
self.y = int(ROWS / 2) - 1 | |
self.bonus = 0 | |
self.energy = -1 | |
def draw(self, scr): | |
if self.energy > 0: | |
output(scr, self.y, self.x, '๐ฑ') | |
self.energy -= 1 | |
elif self.bonus > 0: | |
output(scr, self.y, self.x, '๐') | |
self.bonus -= 1 | |
else: | |
output(scr, self.y, self.x, '๐ธ') | |
def move(self, scr, x, y): | |
self.x += x | |
self.y += y | |
if (self.x < 0): | |
self.x = 0 | |
if (self.x >= COLS): | |
self.x = COLS - 1 | |
if (self.y < 0): | |
self.y = 0 | |
if (self.y >= ROWS): | |
self.y = ROWS - 1 | |
def move_by_key(self, scr, key): | |
x = 0 | |
y = 0 | |
if key == curses.KEY_LEFT or key == ord('h'): | |
x = -1 | |
elif key == curses.KEY_RIGHT or key == ord('l'): | |
x = 1 | |
elif key == curses.KEY_UP or key == ord('k'): | |
y = -1 | |
elif key == curses.KEY_DOWN or key == ord('j'): | |
y = 1 | |
self.move(scr, x, y) | |
def eat(self): | |
if self.energy == -1: | |
if self.bonus == 0: | |
self.bonus = 50 | |
else: | |
self.bonus = 0 | |
self.energy = -1 | |
def eaten(self): | |
if self.energy == -1: | |
self.energy = 50 | |
def alive(self): | |
return self.energy != 0 | |
class Food: | |
def __init__(self, foods, speed, score): | |
self.foods = foods | |
self.speed = speed | |
self.score = score | |
self.food_index = 0 | |
self.base = int(datetime.datetime.now().timestamp() % 1000) * 10000 | |
x, y = self.next_pos() | |
while abs(x - (ROWS / 2)) < 4 or abs(y - (COLS / 2)) < 4: | |
self.base += 1 | |
x, y = self.next_pos() | |
def draw(self, scr): | |
food = self.foods[self.food_index] | |
output(scr, self.y, self.x, food) | |
def move(self, scr): | |
x, y = self.next_pos() | |
self.x = x | |
self.y = y | |
self.base += 1 | |
def eaten(self): | |
if self.food_index < len(self.foods): | |
self.food_index += 1 | |
self.base += 100000 | |
def remaining(self): | |
return len(self.foods) - self.food_index | |
def next_pos(self): | |
return get_perlin_noise(self.base, self.speed) | |
class Enemy: | |
def __init__(self, emoji, speed): | |
self.emoji = emoji | |
self.speed = speed | |
self.base = int(datetime.datetime.now().timestamp() % 1000) | |
x, y = self.next_pos() | |
while abs(x - (ROWS / 2)) < 6 or abs(y - (COLS / 2)) < 6: | |
self.base += 1 | |
x, y = self.next_pos() | |
def draw(self, scr): | |
output(scr, self.y, self.x, self.emoji) | |
def move(self, scr): | |
x, y = self.next_pos() | |
self.x = x | |
self.y = y | |
self.base += 1 | |
def next_pos(self): | |
return get_perlin_noise(self.base, self.speed) | |
def check(scr, frog, foods, enemies, score): | |
for food in foods: | |
if food.remaining() > 0: | |
if frog.x == food.x and frog.y == food.y: | |
if frog.bonus > 0: | |
score += food.score * 2 | |
else: | |
score += food.score | |
frog.eat() | |
food.eaten() | |
for enemy in enemies: | |
if frog.x == enemy.x and frog.y == enemy.y: | |
frog.eaten() | |
return score | |
def draw_top_status_bar(scr, score, time): | |
output(scr, 1, 1, 'SCORE: {}'.format(score)) | |
output(scr, 1, COLS - 5, 'TIME: {}'.format(time)) | |
def draw_bottom_status_bar(scr, fruit, dessert): | |
count = fruit.remaining() + dessert.remaining() | |
output(scr, ROWS - 1, COLS - 5, 'FOOD: {}'.format(count)) | |
def draw_screen(scr, frog, fruit, dessert, eagle, snake, score, time): | |
scr.clear() | |
draw_top_status_bar(scr, score, time) | |
draw_bottom_status_bar(scr, fruit, dessert) | |
if frog.energy > 0: | |
output(scr, ROWS - 1, 1, 'ENERGY: {}'.format(frog.energy)) | |
elif frog.bonus > 0: | |
output(scr, ROWS - 1, 1, 'BONUS: {}'.format(frog.bonus)) | |
frog.draw(scr) | |
for food in [fruit, dessert]: | |
if food.remaining() > 0: | |
food.draw(scr) | |
for enemy in [eagle, snake]: | |
enemy.draw(scr) | |
scr.refresh() | |
def draw_opening(scr, time): | |
scr.clear() | |
draw_top_status_bar(scr, 0, time) | |
output(scr, int(ROWS / 2) - 1, int(COLS / 2 - 4), '๐ธ Hungry Froggy ๐') | |
output(scr, ROWS - 2, int(COLS / 2 - 5), "Hit Space key to start") | |
scr.refresh() | |
def draw_game_over(scr, score, time, frog, fruit, dessert): | |
scr.clear() | |
draw_top_status_bar(scr, score, time) | |
draw_bottom_status_bar(scr, fruit, dessert) | |
if frog.alive(): | |
if time > 0: | |
output(scr, 6, int(COLS / 2 - 5), '๐ Conguratulations ๐') | |
else: | |
output(scr, 6, int(COLS / 2 - 2), 'TIME UP โฑ') | |
else: | |
output(scr, 6, int(COLS / 2 - 3), 'GAME OVER ๐ฑ') | |
output(scr, 10, int(COLS / 2 - 3), "Hit Space key") | |
scr.refresh() | |
def wait_key(scr, chars): | |
while True: | |
key = scr.getch() | |
for char in chars: | |
if key == ord(char): | |
return char | |
def run(scr): | |
time = 99 | |
draw_opening(scr, time) | |
if wait_key(scr, [' ', 'q']) == 'q': | |
return False | |
score = 0 | |
tic = 0 | |
frog = Frog() | |
fruit = Food('๐๐๐๐๐๐๐๐๐๐๐๐ฅ๐๐ ๐ฅ๐ถ๐ฅ๐ฅ๐๐ ๐ฝ๐๐๐๐๐ฅ๐ฅ๐๐ฑ๐ฃ๐๐๐ง๐ฅ๐ข๐๐ฎ๐๐ฒ๐ฅ', 0.01, 1) | |
dessert = Food('๐ฆ๐ฉ๐ง๐ง๐จ๐ช๐ซ๐๐ฐ๐ฎ๐ญ๐ฟ๐บ๐ฅ๐ท๐ถ๐น๐ผ๐ต๐ธ', 0.02, 2) | |
eagle = Enemy('๐ฆ ', 0.03) | |
snake = Enemy('๐', 0.02) | |
scr.nodelay(True) | |
while True and time > 0 and frog.alive() and (fruit.remaining() > 0 or dessert.remaining() > 0): | |
curses.flushinp() | |
curses.napms(100) | |
key = scr.getch() | |
if key == ord('q'): | |
time = 0 | |
break | |
frog.move_by_key(scr, key) | |
fruit.move(scr) | |
dessert.move(scr) | |
eagle.move(scr) | |
snake.move(scr) | |
score = check(scr, frog, [fruit, dessert], [eagle, snake], score) | |
draw_screen(scr, frog, fruit, dessert, eagle, snake, score, time) | |
if tic % 10 == 0: | |
time -= 1 | |
tic += 1 | |
if frog.alive(): | |
score += time | |
curses.napms(1200) | |
scr.nodelay(False) | |
draw_game_over(scr, score, time, frog, fruit, dessert) | |
return wait_key(scr, [' ', 'q']) != 'q' | |
def main(stdscr): | |
curses.use_default_colors() | |
curses.curs_set(0) | |
ok = True | |
while ok: | |
ok = run(stdscr) | |
curses.endwin() | |
if __name__ == '__main__': | |
locale.setlocale(locale.LC_ALL, '') | |
curses.wrapper(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment