本文将用Python写一个终端版俄罗斯方块,用游戏的方式学习编程。
这个小游戏分享给学习Python的朋友,感受一下Python的魅力,锻炼编程思维。
更多小游戏请看 这里。
演示效果图见下文。
代码如下:
# encoding:utf-8
# file: tetris.py
# author: Jason
# ##########################
# 终端俄罗斯方块Tetris
# ##########################
import time
import random
import sys
import select
import copy
import termios
# #################
# 全局变量定义
# #################
# 地图相关变量
# 下面的方块指7种基本方块 小方块指构成基本方块的最小单位
# 终端使用等宽字体时 小方块正好是个小正方形 1个小方块由2个英文字符组成
# 游戏区域即游戏地图的长与高
GAME_AREA_L, GAME_AREA_H = 16, 26
# 游戏区域内左上角坐标为坐标原点 X为横坐标 Y为纵坐标
GAME_AREA_X, GAME_AREA_Y = 2, 2
# 游戏边框图形 基本方块的小方块渲染图形
GAME_EDGE, GAME_SQUARE = '##', ' '
# 游戏区域背景色
GAME_BKGCOLOR = '\033[40;30m'
# 游戏区域位图 即游戏地图
GAME_BITMAP = [[[0, GAME_BKGCOLOR] for y in range(GAME_AREA_L)] for x in range(GAME_AREA_H)]
# 方块相关变量
# 7方块初始状态位图 2,2位置为方块实际长与高 3,3位置为此方块颜色
BLOCK_DICT = {
# 天蓝色
'I': [[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, [4, 1], 0],
[0, 0, 0, '\033[46;36m']],
# 蓝色
'J': [[1, 0, 0, 0],
[1, 1, 1, 0],
# [3,2]表示此方块长3高2 索引正好相反 x=2为两行y=3为三列
# 3,2为游戏区域的坐标表示法的长与度 2,3为对应的二维列表索引表示法
[0, 0, [3, 2], 0],
[0, 0, 0, '\033[44;34m']],
# 白色
'L': [[0, 0, 1, 0],
[1, 1, 1, 0],
[0, 0, [3, 2], 0],
[0, 0, 0, '\033[47;37m']],
# 黄色
'O': [[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, [2, 2], 0],
[0, 0, 0, '\033[43;33m']],
# 绿色
'S': [[0, 1, 1, 0],
[1, 1, 0, 0],
[0, 0, [3, 2], 0],
[0, 0, 0, '\033[42;32m']],
# 紫色
'T': [[0, 1, 0, 0],
[1, 1, 1, 0],
[0, 0, [3, 2], 0],
[0, 0, 0, '\033[45;35m']],
# 红色
'Z': [[1, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, [3, 2], 0],
[0, 0, 0, '\033[41;31m']]
}
# 方块存储位图
BLOCK_BITMAP = [[0, 0, [0, 0], 0] if i == 2 else [0, 0, 0, 0] for i in range(4)]
# 定义方块出生点的方块左上角的地图坐标
BLOCK_SX, BLOCK_SY = GAME_AREA_X + GAME_AREA_L // 2 - 2, GAME_AREA_Y
# 方块左上角地图坐标
BLOCK_COORD = {'x': BLOCK_SX, 'y': BLOCK_SY}
# 生成方块计数 得分 方块类型
BLOCK_COUNT, GAME_SCORE, BLOCK_TYPE = 0, 0, '_'
# 按键和信息相关变量
# 按键变量 按键检测间隔 方块打印间隔
KEY_DEFAULT, KEY_PAUSE = '_', ' '
KEY, KEY_INTERVAL, PNT_INTERVAL = KEY_DEFAULT, 0.01, 0.2
# 计分板左上角坐标
INFO_AREA_X, INFO_AREA_Y = GAME_AREA_X + GAME_AREA_L + 2, GAME_AREA_Y + 1
# 计分板区域长与高
INFO_AREA_L, INFO_AREA_H = 8, GAME_AREA_H
# #################
# 函数定义
# #################
# 初始化与通用函数
# 包括坐标定位 绘制地图边框 填充地图背景色等函数
def exit_clear(txt, e_code=0):
"""
退出时做一些清理工作
:param txt: 文本
:param e_code: 退出码
:return:
"""
print(txt)
exit(e_code)
def goto_blockxy(x=1, y=1):
"""
将终端光标移动到方块的左上角位置 即坐标定位函数
:param x: 方块的坐标x
:param y: 方块的坐标y
:return: None
"""
if x < 1 or y < 1:
x, y = 1, 1
# 1个小方块占2个英文字符宽度
# 实际坐标_x与方块坐标x的转换
_x = (x - 1) * 2 + 1
print('\033[{};{}H'.format(y, _x), end='', sep='')
def _gotoxy_print(x=1, y=1, *args, **kwargs):
"""打印调试信息"""
goto_blockxy(x, y)
print(*args, **kwargs)
def _edge_block(x=1, y=1, ln=1, h=1, b=GAME_EDGE):
"""
绘制边框小方块
:param x: 起点x
:param y: 起点y
:param ln: 边框小方块长
:param h: 边框小方块高
:param b: 边框小方块字符
:return: None
"""
for _y in range(y, y + h):
goto_blockxy(x, _y)
print('{}'.format(b * ln))
def draw_edge(x=1, y=1):
"""
绘制游戏边框
"""
ln1 = GAME_AREA_L + INFO_AREA_L + 3
ln2 = GAME_AREA_L + 1
ln3 = GAME_AREA_L + INFO_AREA_L + 2
h = GAME_AREA_H
_edge_block(x, y, ln1, 1)
_edge_block(x, y + 1, 1, h)
_edge_block(x + ln2, y + 1, 1, h)
_edge_block(x + ln3, y + 1, 1, h)
_edge_block(x, y + h + 1, ln1, 1)
# 地图处理函数
# 包括地图中方块清除 地图中方块显示等
def _clear_map_area(x, y, ln, h):
"""
清除地图位图
:param x: 坐标表示法的x
:param y: 坐标表示法的y
:param ln: 清除区域长度
:param h: 清除区域高度
:return: None
"""
map_x, map_y = x - GAME_AREA_X, y - GAME_AREA_Y
for _x in range(map_x, map_x + ln):
for _y in range(map_y, map_y + h):
# 坐标表示法与索引表示法互换
GAME_BITMAP[_y][_x] = [0, GAME_BKGCOLOR]
def _fill_map_point(x, y, color):
"""
填充地图位图点
:param x: 坐标x
:param y: 坐标y
:param color: 颜色字符串转义序列
"""
map_x, map_y = x - GAME_AREA_X, y - GAME_AREA_Y
GAME_BITMAP[map_y][map_x] = [1, color]
def _clear_blockline(lenght=1, block=GAME_SQUARE, bkg=GAME_BKGCOLOR):
"""
填充指定长度方块为背景色 即清除方块
:param lenght: 方块长度 每个小方块占2英文字符宽度
:param block: 填充的小方块
:param bkg: 填充的背景色
:return: None
"""
# 因为标准输出缓冲区的原因 不要传参end='' 否则打印内容可能不会立刻显示
print('{}{}\033[0m'.format(bkg, block * lenght))
def clear_area(x=GAME_AREA_X, y=GAME_AREA_Y, ln=GAME_AREA_L, h=GAME_AREA_H):
"""
清除指定矩形区域 清除的最小单位是小方块 即2个英文字符 游戏区域为黑色背景
:param x: 起始x坐标
:param y: 起始y坐标
:param ln: 区域长度
:param h: 区域高度
:return: None
"""
for _y in range(y, y + h):
goto_blockxy(x, _y)
_clear_blockline(ln)
_clear_map_area(x, y, ln, h)
def print_map_area(x, y, ln=GAME_AREA_L, h=GAME_AREA_H):
"""
根据地图位图刷新游戏区域
:param x: 地图起点坐标x
:param y: 地图起点坐标y
:param ln: 区域长
:param h: 区域高
:return: None
"""
map_x, map_y = x - GAME_AREA_X, y - GAME_AREA_Y
for _x in range(map_y, map_y + h):
for _y in range(map_x, map_x + ln):
goto_blockxy(_y + GAME_AREA_Y, _x + GAME_AREA_X)
print('{}{}\033[0m'.format(GAME_BITMAP[_x][_y][1], GAME_SQUARE))
def _print_map_bits():
"""
打印地图点 用于调试
:return: None
"""
a, b = GAME_AREA_X + GAME_AREA_L + INFO_AREA_L + 3, GAME_AREA_Y
for x in range(len(GAME_BITMAP)):
goto_blockxy(a, b)
for y in range(len(GAME_BITMAP[0])):
if GAME_BITMAP[x][y][0] == 1:
print('\033[31m{}\033[0m,'.format(GAME_BITMAP[x][y][0]), end='')
else:
print('{},'.format(GAME_BITMAP[x][y][0]), end='')
b += 1
def draw_background():
"""游戏区域背景色填充
"""
clear_area()
def restore_cursor():
"""恢复隐藏的光标
"""
print('\033[?25h')
def tetris_init():
"""初始化工作
"""
# 清屏与隐藏光标
print('\033[2J\033[?25l')
# 绘制游戏区域边框
draw_edge()
# 地图填充背景色
draw_background()
# 方块处理函数
# 包括选取方块 打印方块 方块旋转 方块平移等函数
def _pick_block(b_bitmap, b_type):
"""
选取特定类型的方块
:param b_bitmap: 方块位图
:param b_type: 方块类型
:return: None
"""
for x in range(4):
for y in range(4):
b_bitmap[x][y] = BLOCK_DICT[b_type][x][y]
def print_block(x, y, b_bitmap, fill_flag=True):
"""
打印一种方块
:param x: 起始点x
:param y: 起始点y
:param b_bitmap: 方块位图
:param fill_flag: 填充地图位图标记 False为单纯打印方块不填充
:return: None
"""
if not fill_flag:
# 清除上个方块
for _x in range(len(b_bitmap)):
for _y in range(len(b_bitmap[0])):
goto_blockxy(_y + x, _x + y)
print('{}'.format(GAME_SQUARE))
b_l, b_h = b_bitmap[-2][-2][0], b_bitmap[-2][-2][1]
b_color = b_bitmap[-1][-1]
# 这里的_x,_y为索引表示法 参数x,y为坐标表示法
for _y in range(b_l):
for _x in range(b_h):
# 必须判断是否等于1 不要用True/False判断
# b_bitmap坐标互换 让位图中所有1呈现的图像与实际打印的图像一致
# 即b_bitmap中纵向扫描 print也纵向打印
if b_bitmap[_x][_y] == 1:
# 方块位图坐标转换游戏区域坐标 索引到坐标表示法需互换坐标
goto_blockxy(_y + x, _x + y)
if fill_flag:
_fill_map_point(_y + x, _x + y, b_color)
# 打印行数超过终端窗口高度 可能会提前折行导致方块错乱
print('{}{}\033[0m'.format(b_color, GAME_SQUARE))
def _copy_rotatedblock(rotated_block, origin_block):
"""
方块拷贝函数
:param rotated_block: 旋转后的方块位图
:param origin_block: 原始方块位图 必须是4x4方块
:return: None
"""
b_length, b_height = origin_block[-2][-2][0], origin_block[-2][-2][1]
for _x in range(4):
for _y in range(4):
if _x < b_length and _y < b_height:
# 原始方块长x高 => 位图[高][长] => 旋转后方块的 位图[长][高]
# 所以这里_x是b_length _y是b_height
origin_block[_x][_y] = rotated_block[_x][_y]
elif _x == _y and _x == 2:
# 长x高变换
origin_block[_x][_y] = origin_block[_x][_y][::-1]
elif _x == _y and _x == 3:
# 颜色值不改变
continue
else:
origin_block[_x][_y] = 0
def copy_block(fr_bitmap: list, to_bitmap: list):
"""
拷贝方块存储位图
:param fr_bitmap: 原始方块
:param to_bitmap: 目的方块
:return:
"""
for i in range(4):
for j in range(4):
to_bitmap[i][j] = fr_bitmap[i][j]
def _rotate_block(flag=True):
"""
逆时针旋转方块 即求NxM到MxN的转置矩阵
:param flag: True生成旋转后的方块 False只旋转不生成旋转后方块
:return: None
"""
b_length, b_height = BLOCK_BITMAP[-2][-2][0], BLOCK_BITMAP[-2][-2][1]
# b_lengthXb_height 方块索引表示法即 b_heightXb_length
b_target = [[0 for _y in range(b_height)] for _x in range(b_length)]
for _y in range(b_length):
for _x in range(b_height):
# 逆时针旋转90度
offset = len(BLOCK_BITMAP[_x]) - b_length
b_target[_y][_x] = BLOCK_BITMAP[_x][-_y - 1 - offset]
# flag为真 生成旋转后方块
if flag:
_copy_rotatedblock(b_target, BLOCK_BITMAP)
# 生成临时旋转后方块用于检测
else:
# 拷贝未旋转的方块为模板
tmp_bitmap = copy.deepcopy(BLOCK_BITMAP)
# 生成为临时方块
_copy_rotatedblock(b_target, tmp_bitmap)
return tmp_bitmap
def _edge_detect(x, y, b_bitmap):
"""
方块是否超出游戏地图检测
:param x: 方块左上角坐标x
:param y: 方块左上角坐标y
:param b_bitmap: 方块位图
:return: 布尔型
"""
b_l, b_h = b_bitmap[-2][-2][0], b_bitmap[-2][-2][1]
if x < GAME_AREA_X or y < GAME_AREA_Y or \
x + b_l - 1 >= GAME_AREA_X + GAME_AREA_L or \
y + b_h - 1 >= GAME_AREA_Y + GAME_AREA_H:
return False
else:
return True
def _clear_block(x, y, ln, h):
"""
清除指定方块
:param x: 起始x坐标
:param y: 起始y坐标
:param ln: 区域长度
:param h: 区域高度
:return: None
"""
for _x in range(x, x + ln):
for _y in range(y, y + h):
if BLOCK_BITMAP[_y - y][_x - x] == 1:
goto_blockxy(_x, _y)
# 因为标准输出缓冲区的原因 不要传参end='' 否则打印内容可能不会立刻显示
print('{}{}\033[0m'.format(GAME_BKGCOLOR, GAME_SQUARE))
# 清除地图点
_clear_map_area(_x, _y, 1, 1)
def move_block(x, y, direction, distance=1):
"""
移动方块或旋转方块
:param x: 当前位置x
:param y: 当前位置y
:param direction: 移动方向 'to_l'向左 'to_r'向右 'to_d'向下 'to_u'原地旋转
:param distance: 移动距离
:return: 布尔型
"""
global KEY
respawn_flag = False
b_l, b_h = BLOCK_BITMAP[-2][-2][0], BLOCK_BITMAP[-2][-2][1]
if direction == 'to_l' and _edge_detect(x - distance, y, BLOCK_BITMAP) and \
_collision_detect(x - distance, y, 'to_l'):
_clear_block(x, y, b_l, b_h)
print_block(x - distance, y, BLOCK_BITMAP)
BLOCK_COORD['x'], BLOCK_COORD['y'] = x - distance, y
elif direction == 'to_r' and _edge_detect(x + distance, y, BLOCK_BITMAP) and \
_collision_detect(x + distance, y, 'to_r'):
_clear_block(x, y, b_l, b_h)
print_block(x + distance, y, BLOCK_BITMAP)
BLOCK_COORD['x'], BLOCK_COORD['y'] = x + distance, y
elif direction == 'to_d' and _edge_detect(x, y + distance, BLOCK_BITMAP) and \
_collision_detect(x, y + distance, 'to_d'):
_clear_block(x, y, b_l, b_h)
print_block(x, y + distance, BLOCK_BITMAP)
BLOCK_COORD['x'], BLOCK_COORD['y'] = x, y + distance
elif direction == 'to_d' and (not _edge_detect(x, y + distance, BLOCK_BITMAP)
or not _collision_detect(x, y + distance, 'to_d')):
respawn_flag = True
elif direction == 'to_u' and _edge_detect(x, y, _rotate_block(False)) and \
_collision_detect_r(x, y, _rotate_block(False)):
_clear_block(x, y, b_l, b_h)
_rotate_block()
print_block(x, y, BLOCK_BITMAP)
# 恢复向下移动 区别于get_keys获取的's'
KEY = KEY_DEFAULT
return respawn_flag
def spawn_newblock(b_bitmap, t=None):
"""新方块生成
"""
global BLOCK_COUNT, BLOCK_TYPE
# 方块挑选
b_type = 'IJLOSTZ'
if t is None:
BLOCK_TYPE = random.choice(b_type)
else:
BLOCK_TYPE = t
_pick_block(b_bitmap, BLOCK_TYPE)
# 新方块出生点初始化
BLOCK_COORD['x'], BLOCK_COORD['y'] = BLOCK_SX, BLOCK_SY
BLOCK_COUNT += 1
# 按键处理函数
# 包括获取按键 按键转换方向等
def get_keys(delay=None):
"""
按键获取函数 p键退出 不支持方向键
:param delay: 传递额外延迟
:return: None
"""
global KEY
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
# 关闭回显和回车
new[3] = new[3] & ~termios.ECHO & ~termios.ICANON
# 在一个方块打印间隔时间内检测times次按键输入
# get_keys函数充当sleep函数等待PNT_INTERVAL秒的过程中获取按键
count, times = 0, PNT_INTERVAL // KEY_INTERVAL
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
while count < times:
if delay is not None:
time.sleep(delay)
time.sleep(KEY_INTERVAL)
# support non-blocking input
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
KEY = sys.stdin.read(1)
if KEY in 'wad': # 2倍加速移动或旋转
times //= 2
if KEY == 's': # 4倍加速下降
times //= 4
# 暂停
if KEY == KEY_PAUSE:
break
count += 1
except KeyboardInterrupt:
exit_clear('Get: Ctrl-C to EXIT', 1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
def get_direction():
"""
按键转换为方向 'to_l'向左 'to_r'向右 'to_d'向下 'to_u'原地旋转
:return: 方向
"""
if KEY == 'a':
return 'to_l'
elif KEY == 'd':
return 'to_r'
elif KEY == 's':
return 'to_d'
elif KEY == 'w':
return 'to_u'
elif KEY == KEY_DEFAULT:
return 'to_d'
# 碰撞检测与计分函数
# 包括方块向下运动碰撞检测 地图中方块的消除 消除后计分等
def _collision_detect(x, y, direction):
"""
方块与方块之间碰撞检测
:param x: 方块左上角坐标x
:param y: 方块左上角坐标y
:param direction: 移动方向 'to_l'向左 'to_r'向右 'to_d'向下
:return: 布尔型
"""
b_l, b_h = BLOCK_BITMAP[-2][-2][0], BLOCK_BITMAP[-2][-2][1]
distance = 1000000
if direction == 'to_l':
for _y in range(y, y + b_h):
block_y = _y - y
map_y = _y - GAME_AREA_Y
for block_x in range(b_l): # 0->b_l-1
if BLOCK_BITMAP[block_y][block_x] == 1:
sx = x - GAME_AREA_X + block_x
for map_x in range(sx, -1, -1):
if GAME_BITMAP[map_y][map_x][0] == 1:
if abs(sx - map_x) < distance:
distance = abs(sx - map_x)
break
break
elif direction == 'to_r':
for _y in range(y, y + b_h):
block_y = _y - y
map_y = _y - GAME_AREA_Y
for block_x in range(b_l - 1, -1, -1):
if BLOCK_BITMAP[block_y][block_x] == 1:
sx = x - GAME_AREA_X + block_x
for map_x in range(sx, len(GAME_BITMAP[0])):
if GAME_BITMAP[map_y][map_x][0] == 1:
if abs(map_x - sx) < distance:
distance = abs(map_x - sx)
break
break
elif direction == 'to_d':
for _x in range(x, x + b_l):
block_x = _x - x
map_x = _x - GAME_AREA_X
for block_y in range(b_h - 1, -1, -1):
# 从左往右从下往上 寻找方块上第一个值为1的点 纵坐标为block_y
if BLOCK_BITMAP[block_y][block_x] == 1:
# 从block_y映射到地图对应的点的下一个点 此点为方块下次将到达的点 纵坐标起点为sy
# 传递y的实参为y + distance 已包含移动方向distance
sy = y - GAME_AREA_Y + block_y
# 从sy开始竖直向下到地图纵坐标最大值len(GAME_BITMAP) 查找值为1的地图点
for map_y in range(sy, len(GAME_BITMAP)):
if GAME_BITMAP[map_y][map_x][0] == 1:
# 计算地图点map_y到方块下次将到达的点sy之间距离
if abs(map_y - sy) < distance:
distance = abs(map_y - sy)
break
# 只需要查找一次地图上的点
break
# 距离为0 则为假 表示点有重叠 不能移动
return distance
def _collision_detect_r(x, y, b_bitmap):
"""
旋转后碰撞检测
:param x: 方块左上角坐标x
:param y: 方块左上角坐标y
:param b_bitmap: 临时方块位图
:return: 布尔型
"""
# 迭代临时方块与对应地图上的点
for _x in range(4):
for _y in range(4):
# 原始方块和旋转后方块有重叠
if b_bitmap[_x][_y] == 1 and BLOCK_BITMAP[_x][_y] == 1:
# 排除重叠的点
b_bitmap[_x][_y] = 0
# 旋转后方块与地图是否有重叠点
map_x, map_y = y - GAME_AREA_Y + _x, x - GAME_AREA_X + _y
if b_bitmap[_x][_y] == 1 and GAME_BITMAP[map_x][map_y][0] == 1:
return False
return True
def print_info():
x, y = INFO_AREA_X, INFO_AREA_Y
str_len = INFO_AREA_L - GAME_AREA_X + 1
goto_blockxy(x, y)
# '按键:'占3个小方块宽度 2个英文字符占一个小方块宽度
print('按键:{:<{}}'.format(str(KEY), (str_len - 3) * 2))
goto_blockxy(x, y + 2)
print('方块数:{:<{}}'.format(str(BLOCK_COUNT), (str_len - 4) * 2))
goto_blockxy(x, y + 4)
print('方块:{:<{}}'.format(str(BLOCK_TYPE), (str_len - 3) * 2))
goto_blockxy(x, y + 6)
print('得分:{:<{}}'.format(str(GAME_SCORE), (str_len - 3) * 2))
goto_blockxy(x, y + 9)
print('按 {}'.format('空格' if KEY_PAUSE == ' ' else KEY_PAUSE))
goto_blockxy(x, y + 10)
print('暂停/开始')
goto_blockxy(x, y + 12)
print('下个方块:')
def _is_eliminable(x):
"""
判断地图位图的某行能否消除
:param x: 地图位图索引x
:return: 布尔型
"""
v_count = 0
for v in GAME_BITMAP[x]:
if v[0] == 1:
v_count += 1
return v_count == len(GAME_BITMAP[x])
def _is_emptyline(x):
"""
判断地图中某行是不是空行
:param x: 地图位图索引x
:return: 布尔型
"""
v_count = 0
for v in GAME_BITMAP[x]:
if v[0] == 0:
v_count += 1
return v_count == len(GAME_BITMAP[x])
def _do_eliminate(x):
"""
消除地图中的某行
:param x: 能消除行的索引x
:return: None
"""
global GAME_SCORE
GAME_SCORE += 1
# 向下逐行覆盖消除的行
for _x in range(x - 1, -1, -1):
GAME_BITMAP[_x + 1] = copy.deepcopy(GAME_BITMAP[_x])
if _is_emptyline(_x):
break
def eliminate_blocks(x=len(GAME_BITMAP) - 1):
"""
消除方块函数
:param x: 地图位图的某一行 即索引x
:return: None
"""
# 从下到上扫描能消除的行 逐行消除 使用递归
if x < 0 or _is_emptyline(x):
return
if _is_eliminable(x):
_do_eliminate(x)
eliminate_blocks(x)
else:
eliminate_blocks(x - 1)
# 网络对战功能
# 包括网络数据传输 输赢判断等
def data_transmit():
pass
def winner_judge():
pass
def player2_print_block():
pass
def player2_print_info():
pass
if __name__ == '__main__':
tetris_init()
tmp_score = GAME_SCORE
block_nextbm = copy.deepcopy(BLOCK_BITMAP)
spawn_newblock(block_nextbm)
while True:
# 生成新方块
copy_block(block_nextbm, BLOCK_BITMAP)
spawn_newblock(block_nextbm)
print_block(INFO_AREA_X, INFO_AREA_Y + 14, block_nextbm, fill_flag=False)
print_block(BLOCK_COORD['x'], BLOCK_COORD['y'], BLOCK_BITMAP)
down_move_count = 0
while True:
get_keys()
# 暂停
if KEY == KEY_PAUSE:
KEY = KEY_DEFAULT
while KEY != ' ':
try:
get_keys(0.2)
except KeyboardInterrupt:
exit_clear('Get: Ctrl-C to EXIT', 1)
# 打印信息
print_info()
reborn_flag = move_block(BLOCK_COORD['x'], BLOCK_COORD['y'], get_direction())
if down_move_count == 0 and reborn_flag:
restore_cursor()
exit_clear('Gam Over!!!', 0)
# 退出循环 生成新方块
if reborn_flag:
# 消除
eliminate_blocks()
# 有方块消除时 才全地图刷新
if GAME_SCORE > tmp_score:
# _gotoxy_print(28,29, 'in printmaparea')
print_map_area(GAME_AREA_X, GAME_AREA_Y)
tmp_score = GAME_SCORE
break
# _print_map_bits()
down_move_count += 1
在终端中运行命令(使用python3执行):
python tetris.py
(如果出现显示不全,把终端窗口最大化即可)
操作:
a s w d 为方向键,空格 暂停/开始,ctrl+c 退出程序
执行效果: