Python
import random
import curses
def main(stdscr):
curses.curs_set(0)
curses.start_color()
# Define color pairs: (pair_number, foreground, background)
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) # Snake
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK) # Food
curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_BLUE) # Background/border
curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # Score
curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLUE) # Wall
while True:
sh, sw = stdscr.getmaxyx()
win = curses.newwin(sh, sw, 0, 0)
win.keypad(1)
win.timeout(100)
snk_x = sw // 4
snk_y = sh // 2
snake = [
[snk_y, snk_x],
[snk_y, snk_x - 1],
[snk_y, snk_x - 2]
]
# Food never on wall
food = [random.randint(2, sh - 3), random.randint(2, sw - 3)]
win.bkgd(' ', curses.color_pair(3)) # Set background color
win.addch(food[0], food[1], '◆', curses.color_pair(2) | curses.A_BOLD)
direction = 'd' # Start moving right
score = 0
game_over = False
while not game_over:
win.clear()
win.bkgd(' ', curses.color_pair(3)) # Redraw background
# Modern border using box-drawing characters
horizontal = '─'
vertical = '│'
tl = '┌'
tr = '┐'
bl = '└'
br = '┘'
for x in range(1, sw-2):
win.addch(0, x, horizontal, curses.color_pair(5) | curses.A_BOLD)
win.addch(sh-2, x, horizontal, curses.color_pair(5) | curses.A_BOLD)
for y in range(1, sh-2):
win.addch(y, 0, vertical, curses.color_pair(5) | curses.A_BOLD)
win.addch(y, sw-2, vertical, curses.color_pair(5) | curses.A_BOLD)
win.addch(0, 0, tl, curses.color_pair(5) | curses.A_BOLD)
win.addch(0, sw-2, tr, curses.color_pair(5) | curses.A_BOLD)
win.addch(sh-2, 0, bl, curses.color_pair(5) | curses.A_BOLD)
win.addch(sh-2, sw-2, br, curses.color_pair(5) | curses.A_BOLD)
# Score bar
score_str = f' Score: {score} '
score_x = (sw - len(score_str)) // 2
win.addstr(0, score_x, score_str, curses.color_pair(4) | curses.A_BOLD)
# Draw food (never on wall)
win.addch(food[0], food[1], '◆', curses.color_pair(2) | curses.A_BOLD)
# Draw snake (head is brighter)
for idx, part in enumerate(snake):
if idx == 0:
win.addch(part[0], part[1], '●', curses.color_pair(4) | curses.A_BOLD)
else:
win.addch(part[0], part[1], '●', curses.color_pair(1) | curses.A_BOLD)
# Shadow effect (optional, only if space allows)
if part[1]+1 < sw-2 and part[0]+1 < sh-2:
win.addch(part[0]+1, part[1]+1, '.', curses.color_pair(3))
next_key = win.getch()
if next_key != -1:
try:
char = chr(next_key).lower()
except:
char = ''
# Prevent reverse direction
if (char == 'w' and direction != 's'):
direction = 'w'
elif (char == 's' and direction != 'w'):
direction = 's'
elif (char == 'a' and direction != 'd'):
direction = 'a'
elif (char == 'd' and direction != 'a'):
direction = 'd'
# Calculate new head
new_head = [snake[0][0], snake[0][1]]
if direction == 's':
new_head[0] += 1
if direction == 'w':
new_head[0] -= 1
if direction == 'd':
new_head[1] += 1
if direction == 'a':
new_head[1] -= 1
# Check for collision
if (new_head[0] in [0, sh-2] or
new_head[1] in [0, sw-2] or
new_head in snake):
# Modern Game Over box
box_w = 32
box_h = 5
box_y = sh//2 - box_h//2
box_x = sw//2 - box_w//2
for i in range(box_h):
for j in range(box_w):
win.addch(box_y+i, box_x+j, ' ', curses.color_pair(3))
msg = f" GAME OVER! FINAL SCORE: {score} "
win.addstr(box_y+2, box_x + (box_w-len(msg))//2, msg, curses.color_pair(4) | curses.A_BOLD)
win.refresh()
curses.napms(1800)
game_over = True
break
snake.insert(0, new_head)
if snake[0] == food:
score += 1
win.timeout(max(40, 60 - score * 2))
while True:
# Food never on wall
food = [random.randint(2, sh - 3), random.randint(2, sw - 3)]
if food not in snake:
break
else:
tail = snake.pop()
# Play again prompt
win.clear()
win.bkgd(' ', curses.color_pair(3))
win.border(0)
prompt = "Play again? (y/n): "
win.addstr(sh//2, sw//2 - len(prompt)//2, prompt, curses.color_pair(4) | curses.A_BOLD)
win.refresh()
while True:
choice = win.getch()
if choice in [ord('y'), ord('Y')]:
break # Restart the game loop
elif choice in [ord('n'), ord('N')]:
return # Exit the game
curses.wrapper(main)