labyrinth-cs-proj/snake.py

149 lines
4.5 KiB
Python

#
# Copyright © 2023 adithyagenie
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#
import curses
import random
import time
from curses import textpad
import maze.menu
import maze.modules.maze as m
OPPOSITE_DIRECTION_DICT = {
curses.KEY_UP: curses.KEY_DOWN,
curses.KEY_DOWN: curses.KEY_UP,
curses.KEY_RIGHT: curses.KEY_LEFT,
curses.KEY_LEFT: curses.KEY_RIGHT,
}
DIRECTIONS_LIST = [curses.KEY_RIGHT, curses.KEY_LEFT, curses.KEY_DOWN, curses.KEY_UP]
def create_food(snake, box):
"""Simple function to find coordinates of food which is inside box and not on snake body"""
food = None
while food is None:
food = [
random.randint(box[0][0] + 1, box[1][0] - 1),
random.randint(box[0][1] + 1, box[1][1] - 1),
]
if food in snake:
food = None
return food
def main(stdscr):
# initial settings
curses.curs_set(0)
stdscr.nodelay(1)
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
stdscr.timeout(100)
stdscr.clear()
stdscr.refresh()
# create a game box
sh, sw = stdscr.getmaxyx()
box = [[3, 3], [sh - 3, sw - 3]] # [[ul_y, ul_x], [dr_y, dr_x]]
textpad.rectangle(stdscr, box[0][0], box[0][1], box[1][0], box[1][1])
# create snake and set initial direction
snake = [[sh // 2, sw // 2 + 1], [sh // 2, sw // 2], [sh // 2, sw // 2 - 1]]
direction = curses.KEY_RIGHT
# draw snake
for y, x in snake:
stdscr.addstr(y, x, "", curses.color_pair(3) | curses.A_BOLD)
# create food
food = create_food(snake, box)
stdscr.addstr(food[0], food[1], "🍎")
# print score
score = 0
score_text = "Score: {}".format(score)
stdscr.addstr(1, sw // 2 - len(score_text) // 2, score_text)
while 1:
# non-blocking input
key = stdscr.getch()
if key == 27:
stdscr.clear()
stdscr.refresh()
time.sleep(1)
maze.menu.menu(stdscr)
return
# set direction if user pressed any arrow key and that key is not opposite of current direction
if key in DIRECTIONS_LIST and key != OPPOSITE_DIRECTION_DICT[direction]:
direction = key
# find next position of snake head
head = snake[0]
if direction == curses.KEY_RIGHT:
new_head = [head[0], head[1] + 1]
elif direction == curses.KEY_LEFT:
new_head = [head[0], head[1] - 1]
elif direction == curses.KEY_DOWN:
new_head = [head[0] + 1, head[1]]
elif direction == curses.KEY_UP:
new_head = [head[0] - 1, head[1]]
# insert and print new head
stdscr.addstr(
new_head[0], new_head[1], "", curses.color_pair(3) | curses.A_BOLD
)
snake.insert(0, new_head)
# if sanke head is on food
if snake[0] == food:
# update score
score += 1
score_text = "Score: {}".format(score)
stdscr.addstr(1, sw // 2 - len(score_text) // 2, score_text)
# create new food
food = create_food(snake, box)
stdscr.addstr(food[0], food[1], "🍎", curses.color_pair(1) | curses.A_BOLD)
# increase speed of game
stdscr.timeout(100 - (len(snake) // 3) % 90)
else:
# shift snake's tail
stdscr.addstr(snake[-1][0], snake[-1][1], " ")
snake.pop()
# conditions for game over
if (
snake[0][0] in [box[0][0], box[1][0]]
or snake[0][1] in [box[0][1], box[1][1]]
or snake[0] in snake[1:]
):
msg = "Game Over!"
stdscr.addstr(
sh // 2,
sw // 2 - len(msg) // 2,
msg,
curses.color_pair(1) | curses.A_BOLD,
)
stdscr.addstr(
sh // 2 + 1,
sw // 2 - 8,
"The Score is: " + str(score),
curses.color_pair(3) | curses.A_BOLD,
)
while stdscr.getch() == -1:
pass
time.sleep(2)
m.play(stdscr, executeguest=True, outerscore=score, outergame="snake")
# Call play with guestcheck to update scores
stdscr.clear()
stdscr.refresh()
maze.menu.menu(stdscr)
return score