Skip to content

Instantly share code, notes, and snippets.

@yhirose
Created July 7, 2022 10:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yhirose/53522b49dfcf262e7ea67f2e559cbcb5 to your computer and use it in GitHub Desktop.
Save yhirose/53522b49dfcf262e7ea67f2e559cbcb5 to your computer and use it in GitHub Desktop.
Froggy - Simple game on Terminal
#!/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