#!/usr/bin/env python
"""
file snake.py, run: python snake.py
Written by Jason Zhao at 2016/1/10.
"""
from __future__ import print_function
import sys, time, termios, random, select
# define global vars
# map size (40+28)x20
width, high, info= 41, 21, 28
body = [(width//2, 1)]
dir_x, dir_y = 0, 1
ch_snake, ch_wall, ch_food, key = 'X', '#', '@', '-'
food = [[False for fd_y in range(high + 1)] for fd_x in range(width + 1)]
pnt_interval, key_interval, food_ct = 0.2, 0.01, 0
# define functions
# x and y start from 1
def goto_xy(x, y):
print('\033[%s;%sH' % (y, x), end='', sep='')
# draw map's edge, the wall and info display box
def draw_edge():
print('\033[2J\033[?25l')
for x in range(high):
goto_xy(width, x)
print(ch_wall, end='', sep='')
goto_xy(width + info, x)
print(ch_wall, end='', sep='')
goto_xy(0, high)
print(ch_wall * (width + info))
def draw_snake():
for pos in body:
goto_xy(pos[0], pos[1])
print(ch_snake, end='', sep='')
def get_keys():
global key
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
# turn off echo and enter
new[3] = new[3] & ~termios.ECHO & ~termios.ICANON
count, times = 0, pnt_interval//key_interval
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
while count < times:
# sleep time second
time.sleep(key_interval)
# support non-blocking input
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
key = sys.stdin.read(1)
if key == 'p': break
count += 1
except KeyboardInterrupt:
print('get: ctrl-c')
exit()
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
def move_snake():
global food_ct
new_x, new_y = body[-1][0] + dir_x, body[-1][1] + dir_y
# snake can't turn around
if len(body) > 1 and (new_x, new_y) == body[-2]:
new_x, new_y = body[-1][0] - dir_x, body[-1][1] - dir_y
if food[new_x][new_y]:
body.append((new_x, new_y))
food[new_x][new_y] = False
food_ct += 1
return True
else:
body.append((new_x, new_y))
tail_x, tail_y = body.pop(0)
goto_xy(tail_x, tail_y)
# cut the tail of snake
print(' ', end='', sep='')
return False
def make_food(pre_food=False, first_run=False):
def mk_food():
while True:
food_x = random.randrange(1, width)
food_y = random.randrange(1, high)
# food can't appear on body of snake
if not (food_x, food_y) in body:
food[food_x][food_y] = True
goto_xy(food_x, food_y)
print(ch_food, end='', sep='')
break
if first_run:
mk_food()
elif pre_food:
mk_food()
def get_dir():
global dir_x, dir_y
if key == 'w':
dir_x, dir_y = 0, -1
elif key == 's':
dir_x, dir_y = 0, +1
elif key == 'a':
dir_x, dir_y = -1, 0
elif key == 'd':
dir_x, dir_y = +1, 0
def detect_pos():
x, y = body[-1][0], body[-1][1]
if (x > 0 and x < width) and (y > 0 and y < high) and (not (x, y) in body[:-1]):
return True
else:
return False
def print_info(info='', flag=True):
if flag:
goto_xy(width + 2, 4)
print('[w/s/a/d] to move')
goto_xy(width + 2, 5)
print('[p] to quit')
goto_xy(width + 2, 7)
print('score:', food_ct)
goto_xy(width + 2, 9)
print('length of snake:', len(body))
goto_xy(width + 2, 11)
print('get:', key)
else:
print('\033[2J')
goto_xy(0, 0)
print('#' * 13)
print('# %s #' % info)
print('#' * 13)
def main_loop():
pre_food = False
first_run = True
draw_edge()
print_info(flag=True)
while key != 'p':
make_food(pre_food, first_run)
pre_food = move_snake()
first_run = False
# snake hits the wall or itself, exit
if not detect_pos():
print_info('GAME OVER', flag=False)
exit()
draw_snake()
print_info(flag=True)
#time.sleep(pnt_interval)
get_keys()
get_dir()
else:
print_info('GAME EXIT', flag=False)
exit()
# self-test or be a module
if __name__ == '__main__':
main_loop()
$ python snake.py
说明:文章若有不足、错误之处欢迎各位指正。