pygame实现俄罗斯方块游戏(AI篇1)


Posted in Python onOctober 29, 2019

上次更新到pygame实现俄罗斯方块游戏(基础篇3)

现在继续

一、定义玩家类

定义玩家类是为了便于进行手动和机器模式或各种不同机器人模式的混合使用,增加代码扩展性。
可以先定义一个玩家基类

class Player(object):
 auto_mode=False # 是否是自动模式,自动模式应当不响应键盘操作
 def __init__(self):
 pass
 def run(self): # 进行操作
 pass

手动类和机器类继承自Player类

class HumanPlayer(Player):
 def __init__(self):
 super(Player, self).__init__()

class AIPlayer(Player):
 auto_mode=True
 def __init__(self):
 super(Player, self).__init__()
 def run(self):
 pass

下面然后游戏代码中做下面三处修改

pygame实现俄罗斯方块游戏(AI篇1)

好了,现在玩家类添加完毕,由于HumanPlayer类的run执行的是pass,原来的操作没有受到影响,下面该去实现AIPlayer的run了

二、贪心计算

方块有N种形态,每种形态有若干种水平位置,假设AI只管方块变形和移动能落得最低位置,越低越好。
首先,我们要将当前游戏界面的方块情况告诉玩家,所以我们在Player类的run函数增加一下panel参数,将panel作为run的参数传入。

AIPlayer的代码大致改成下面这样

class AIPlayer(Player):
 cal_block_id=-1 # 用于判断是否方块发生了变化
 ctl_arr=[] # 存:1=变、2=左、3=右、4=下,这些数
 auto_mode=True
 def __init__(self):
 super(Player, self).__init__()

 def run(self, panel):
 if panel.block_id == self.cal_block_id: # block_id没变,按原来计算好的操作规则进行
  if len(ctl_arr)>0:
  ctl = self.ctl_arr.pop(0)
  if ctl == 1: panel.change_block()
  if ctl == 2: panel.control_block(-1,0)
  if ctl == 3: panel.control_block(1,0)
  if ctl == 4:
   flag = panel.move_block()
   while flag==1: 
   flag = panel.move_block()
   if flag == 9: game_state = 2
 else: # block_id变了,计算新方块的操作规则
  self.cal_block_id = panel.block_id
  matrix = panel.get_rect_matrix()
  #matrix.print_matrix() # print for debug
  #
  # 添加计算操作的逻辑
  #
  pass

这里为了方便计算将panel中rect_arr转成matrix,一般建议matrix用numpy的,这边的使用场景比较简单,就不增加依赖包了,自己实现一个简单的matrix

class Matrix(object):
 rows = 0
 cols = 0
 data = []
 def __init__(self, rows, cols):
 self.rows = rows
 self.cols = cols 
 self.data = [0 for i in range(rows*cols)]

 def set_val(self, x, y, val):
 self.data[y*self.cols+x] = val

 def get_val(self, x, y):
 return self.data[y*cols+x]
 
 def print_matrix(self):
 for i in range(self.rows):
  print self.data[self.cols*i:self.cols*(i+1)]

panel的get_rect_matrix是这么实现的

def get_rect_matrix(self):
 matrix = Matrix(ROW_COUNT, COL_COUNT)
 for rect_info in self.rect_arr:
  matrix.set_val(rect_info.x, rect_info.y, 1)
 return matrix

为获取不同形态的值,Block类和子类的get_shape函数稍作修改,增加一个输入

class TBlock(Block): # 四种形态
 shape_id=0
 shape_num=4
 def __init__(self, n=None):
 super(TBlock, self).__init__()
 if n is None: n=random.randint(0,3)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(255,0,0)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 if sid==0: return [(0,1),(1,1),(2,1),(1,2)]
 elif sid==1: return [(1,0),(1,1),(1,2),(0,1)]
 elif sid==2: return [(0,1),(1,1),(2,1),(1,0)]
 else: return [(1,0),(1,1),(1,2),(2,1)]

计算最优操作的代码如下,大致思路是落下的四个方块的Y值加起来越大越好

def cal_best_arr(self, panel):
 matrix = panel.get_rect_matrix()
 #matrix.print_matrix() # print for debug
 cur_shape_id = panel.moving_block.shape_id
 shape_num = panel.moving_block.shape_num
 max_score = 0
 best_arr = []
 for i in range(shape_num):
  tmp_shape_id = cur_shape_id + i
  if tmp_shape_id >= shape_num: tmp_shape_id = tmp_shape_id % shape_num
  tmp_shape = panel.moving_block.get_shape(sid=tmp_shape_id)
  center_shape = []
  for x,y in tmp_shape: center_shape.append((x+COL_COUNT/2-2,y-2))
  minx = COL_COUNT
  maxx = 0
  miny = ROW_COUNT
  maxy = -2
  for x,y in center_shape:
  if x<minx: minx = x
  if x>maxx: maxx = x
  if y<miny: miny = y
  if y>maxy: maxy = y

  for xdiff in range(-minx,COL_COUNT-maxx): # 左右可以移动的范围
  arr = [1 for _ in range(i)] 
  if xdiff < 0: [arr.append(2) for _ in range(-xdiff)]
  if xdiff > 0: [arr.append(3) for _ in range(xdiff)]
  arr.append(4)
  for yindex in range(-miny, ROW_COUNT-maxy): # 往下检测碰撞
   if matrix.cross_block(center_shape, xdiff=xdiff, ydiff=yindex):
   break
   score = sum([y+yindex for x,y in center_shape])
   #print i,xdiff,yindex,score
   if score > max_score: 
   max_score = score
   best_arr = arr
 self.ctl_arr = best_arr

pygame实现俄罗斯方块游戏(AI篇1)

大概的AI效果有了,但是发现它还不会考虑造成空洞的影响,下面还要继续优化

三、空洞的惩罚

Matrix类加一个获取空洞数的函数,这里先简单定义为上方有方块为空洞

def get_hole_number(self):
 hole_num=0
 for x in range(0,self.cols):
  for y in range(1,self.rows):
  if self.get_val(x,y) == 0 and self.get_val(x,y-1) == 1: # 上方有方块的当成空洞
   #print x,y
   hole_num+=1
 return hole_num

计算最佳操作的函数加入空洞的惩罚值修改如下

def cal_best_arr(self, panel):
 matrix = panel.get_rect_matrix()
 cur_shape_id = panel.moving_block.shape_id
 shape_num = panel.moving_block.shape_num
 max_score = -10000
 best_arr = []
 for i in range(shape_num):
  tmp_shape_id = cur_shape_id + i
  if tmp_shape_id >= shape_num: tmp_shape_id = tmp_shape_id % shape_num
  tmp_shape = panel.moving_block.get_shape(sid=tmp_shape_id)
  center_shape = []
  for x,y in tmp_shape: center_shape.append((x+COL_COUNT/2-2,y-2))
  minx = COL_COUNT
  maxx = 0
  miny = ROW_COUNT
  maxy = -2
  for x,y in center_shape:
  if x<minx: minx = x
  if x>maxx: maxx = x
  if y<miny: miny = y
  if y>maxy: maxy = y

  for xdiff in range(-minx,COL_COUNT-maxx): # 左右可以移动的范围
  arr = [1 for _ in range(i)] 
  if xdiff < 0: [arr.append(2) for _ in range(-xdiff)]
  if xdiff > 0: [arr.append(3) for _ in range(xdiff)]

  max_yindex = -miny
  for yindex in range(-miny, ROW_COUNT-maxy): # 往下检测碰撞
   if matrix.cross_block(center_shape, xdiff=xdiff, ydiff=yindex):
   break
   max_yindex = yindex
  score = sum([y+max_yindex for x,y in center_shape])

  # 克隆矩阵并且将方块落下,便于计算落下后的空洞数
  clone_matrix = matrix.clone()
  clone_matrix.fill_block(center_shape, xdiff=xdiff, ydiff=max_yindex)
  score -= clone_matrix.get_hole_number() * COL_COUNT

  if score > max_score: 
   max_score = score
   best_arr = arr
 self.ctl_arr = best_arr+[4]

pygame实现俄罗斯方块游戏(AI篇1)

现在AI的表现正常一些了,先优化到这,下章继续,下面继续贴下目前的完整代码

# -*- coding=utf-8 -*-
import random
import pygame
from pygame.locals import KEYDOWN,K_LEFT,K_RIGHT,K_UP,K_DOWN,K_SPACE
import pickle,os

ROW_COUNT=20
COL_COUNT=10
SCORE_MAP=(100,300,800,1600)

class Matrix(object):
 rows = 0
 cols = 0
 data = []
 def __init__(self, rows, cols, data=None):
 self.rows = rows
 self.cols = cols 
 if data is None: data = [0 for i in range(rows*cols)]
 self.data = data

 def set_val(self, x, y, val):
 self.data[y*self.cols+x] = val

 def get_val(self, x, y):
 return self.data[y*self.cols+x]
 
 def cross_block(self, rect_arr, xdiff=0, ydiff=0):
 for x,y in rect_arr:
  #if x+xdiff>=0 and x+xdiff<self.cols and y+ydiff>=0 and y+ydiff<self.rows:
  if self.get_val(x+xdiff,y+ydiff) == 1: return True
 return False

 def get_hole_number(self):
 hole_num=0
 for x in range(0,self.cols):
  for y in range(1,self.rows):
  if self.get_val(x,y) == 0 and self.get_val(x,y-1) == 1: # 上方有方块的当成空洞
   #print x,y
   hole_num+=1
 return hole_num

 def clone(self):
 clone_matrix=Matrix(self.rows, self.cols, list(self.data))
 return clone_matrix

 def fill_block(self, rect_arr, xdiff=0, ydiff=0):
 for x,y in rect_arr:
  self.set_val(x+xdiff,y+ydiff, 1)

 def print_matrix(self):
 for i in range(self.rows):
  print self.data[self.cols*i:self.cols*(i+1)]

class Player(object):
 auto_mode=False # 是否是自动模式,自动模式应当不响应键盘操作
 def __init__(self):
 pass
 def run(self, panel): # 进行操作
 pass

class HumanPlayer(Player):
 def __init__(self):
 super(Player, self).__init__()

class AIPlayer(Player):
 cal_block_id=-1 # 用于判断是否方块发生了变化
 ctl_arr=[] # 存:1=变、2=左、3=右、4=下,这些数
 auto_mode=True
 ai_diff_ticks = 1 # 移动一次的时间,单位毫秒
 
 def __init__(self):
 super(Player, self).__init__()
 self.ctl_ticks = pygame.time.get_ticks() + self.ai_diff_ticks

 def cal_best_arr(self, panel):
 matrix = panel.get_rect_matrix()
 cur_shape_id = panel.moving_block.shape_id
 shape_num = panel.moving_block.shape_num
 max_score = -10000
 best_arr = []
 for i in range(shape_num):
  tmp_shape_id = cur_shape_id + i
  if tmp_shape_id >= shape_num: tmp_shape_id = tmp_shape_id % shape_num
  tmp_shape = panel.moving_block.get_shape(sid=tmp_shape_id)
  center_shape = []
  for x,y in tmp_shape: center_shape.append((x+COL_COUNT/2-2,y-2))
  minx = COL_COUNT
  maxx = 0
  miny = ROW_COUNT
  maxy = -2
  for x,y in center_shape:
  if x<minx: minx = x
  if x>maxx: maxx = x
  if y<miny: miny = y
  if y>maxy: maxy = y

  for xdiff in range(-minx,COL_COUNT-maxx): # 左右可以移动的范围
  arr = [1 for _ in range(i)] 
  if xdiff < 0: [arr.append(2) for _ in range(-xdiff)]
  if xdiff > 0: [arr.append(3) for _ in range(xdiff)]

  max_yindex = -miny
  for yindex in range(-miny, ROW_COUNT-maxy): # 往下检测碰撞
   if matrix.cross_block(center_shape, xdiff=xdiff, ydiff=yindex):
   break
   max_yindex = yindex
  score = sum([y+max_yindex for x,y in center_shape])

  # 克隆矩阵并且将方块落下,便于计算落下后的空洞数
  clone_matrix = matrix.clone()
  clone_matrix.fill_block(center_shape, xdiff=xdiff, ydiff=max_yindex)
  score -= clone_matrix.get_hole_number() * COL_COUNT

  if score > max_score: 
   max_score = score
   best_arr = arr
 self.ctl_arr = best_arr+[4]

 def run(self, panel):
 if pygame.time.get_ticks() < self.ctl_ticks: return
 self.ctl_ticks += self.ai_diff_ticks
 if panel.block_id == self.cal_block_id: # block_id没变,按原来计算好的操作规则进行
  if len(self.ctl_arr)>0:
  ctl = self.ctl_arr.pop(0)
  if ctl == 1: panel.change_block()
  if ctl == 2: panel.control_block(-1,0)
  if ctl == 3: panel.control_block(1,0)
  if ctl == 4:
   flag = panel.move_block()
   while flag==1: 
   flag = panel.move_block()

 else: # block_id变了,计算新方块的操作规则
  self.cal_block_id = panel.block_id
  self.cal_best_arr(panel)
  

class RectInfo(object):
 def __init__(self, x, y, color):
 self.x = x
 self.y = y
 self.color = color

class HintBox(object):
 next_block=None
 def __init__(self, bg, block_size, position):
 self._bg=bg;
 self._x,self._y,self._width,self._height=position
 self._block_size=block_size
 self._bgcolor=[0,0,0]

 def take_block(self):
 block = self.next_block
 if block is None: # 如果还没有方块,先产生一个
  block = create_block()
 
 self.next_block = create_block() # 产生下一个方块
 return block

 def paint(self):
 mid_x=self._x+self._width/2
 pygame.draw.line(self._bg,self._bgcolor,[mid_x,self._y],[mid_x,self._y+self._height],self._width) 
 bz=self._block_size
 # 绘制正在落下的方块
 if self.next_block:
  arr = self.next_block.get_rect_arr()
  minx,miny=arr[0]
  maxx,maxy=arr[0]
  for x,y in arr:
  if x<minx: minx=x
  if x>maxx: maxx=x
  if y<miny: miny=y
  if y>maxy: maxy=y
  w=(maxx-minx)*bz
  h=(maxy-miny)*bz
  # 计算使方块绘制在提示窗中心位置所需要的偏移像素
  cx=self._width/2-w/2-minx*bz-bz/2 
  cy=self._height/2-h/2-miny*bz-bz/2

  for rect in arr:
  x,y=rect
  pygame.draw.line(self._bg,self.next_block.color,[self._x+x*bz+cx+bz/2,self._y+cy+y*bz],[self._x+x*bz+cx+bz/2,self._y+cy+(y+1)*bz],bz)
  pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz+cx,self._y+y*bz+cy,bz+1,bz+1],1)

class ScoreBox(object):
 total_score = 0
 high_score = 0
 db_file = 'tetris.db'
 def __init__(self, bg, block_size, position):
 self._bg=bg;
 self._x,self._y,self._width,self._height=position
 self._block_size=block_size
 self._bgcolor=[0,0,0]
 
 if os.path.exists(self.db_file): self.high_score = pickle.load(open(self.db_file,'rb'))

 def paint(self):
 myfont = pygame.font.Font(None,36)
 white = 255,255,255
 textImage = myfont.render('High: %06d'%(self.high_score), True, white)
 self._bg.blit(textImage, (self._x,self._y))
 textImage = myfont.render('Score:%06d'%(self.total_score), True, white)
 self._bg.blit(textImage, (self._x,self._y+40))

 def add_score(self, score):
 self.total_score += score
 if self.total_score > self.high_score:
  self.high_score=self.total_score
  pickle.dump(self.high_score, open(self.db_file,'wb+'))

class Panel(object): # 用于绘制整个游戏窗口的版面
 block_id=0
 #rect_arr=[RectInfo(4,19,[0,0,255]),RectInfo(6,19,[0,0,255])] # 已经落底下的方块
 rect_arr=[] # 已经落底下的方块
 moving_block=None # 正在落下的方块
 hint_box=None
 score_box=None
 def __init__(self,bg, block_size, position):
 self._bg=bg;
 self._x,self._y,self._width,self._height=position
 self._block_size=block_size
 self._bgcolor=[0,0,0]
 
 def get_rect_matrix(self):
 matrix = Matrix(ROW_COUNT, COL_COUNT)
 for rect_info in self.rect_arr:
  matrix.set_val(rect_info.x, rect_info.y, 1)
 return matrix

 def add_block(self,block):
 for x,y in block.get_rect_arr():
  self.rect_arr.append(RectInfo(x,y, block.color))

 def create_move_block(self):
 self.block_id+=1
 block = self.hint_box.take_block()
 #block = create_block()
 block.move(COL_COUNT/2-2,-2) # 方块挪到中间 
 self.moving_block=block

 def check_overlap(self, diffx, diffy, check_arr=None):
 if check_arr is None: check_arr = self.moving_block.get_rect_arr()
 for x,y in check_arr:
  for rect_info in self.rect_arr:
  if x+diffx==rect_info.x and y+diffy==rect_info.y:
   return True
 return False

 def control_block(self, diffx, diffy):
 if self.moving_block.can_move(diffx,diffy) and not self.check_overlap(diffx, diffy):
  self.moving_block.move(diffx,diffy)

 def change_block(self):
 if self.moving_block:
  new_arr = self.moving_block.change()
  if new_arr and not self.check_overlap(0, 0, check_arr=new_arr): # 变形不能造成方块重叠
  self.moving_block.rect_arr=new_arr


 def move_block(self):
 if self.moving_block is None: create_move_block()
 if self.moving_block.can_move(0,1) and not self.check_overlap(0,1): 
  self.moving_block.move(0,1)
  return 1
 else:
  self.add_block(self.moving_block)
  self.check_clear()

  for rect_info in self.rect_arr:
  if rect_info.y<0: return 9 # 游戏失败
  self.create_move_block()
  return 2

 def check_clear(self):
 tmp_arr = [[] for i in range(20)]
 # 先将方块按行存入数组
 for rect_info in self.rect_arr:
  if rect_info.y<0: return
  tmp_arr[rect_info.y].append(rect_info)

 clear_num=0
 clear_lines=set([])
 y_clear_diff_arr=[[] for i in range(20)]
 # 从下往上计算可以消除的行,并记录消除行后其他行的向下偏移数量
 for y in range(19,-1,-1):
  if len(tmp_arr[y])==10:
  clear_lines.add(y)
  clear_num += 1
  y_clear_diff_arr[y] = clear_num

 if clear_num>0:
  new_arr=[]
  # 跳过移除行,并将其他行做偏移
  for y in range(19,-1,-1):
  if y in clear_lines: continue
  tmp_row = tmp_arr[y]
  y_clear_diff=y_clear_diff_arr[y]
  for rect_info in tmp_row:
   #new_arr.append([x,y+y_clear_diff])
   new_arr.append(RectInfo(rect_info.x, rect_info.y+y_clear_diff, rect_info.color))
  
  self.rect_arr = new_arr
  score = SCORE_MAP[clear_num-1]
  self.score_box.add_score(score)


 def paint(self):
 mid_x=self._x+self._width/2
 pygame.draw.line(self._bg,self._bgcolor,[mid_x,self._y],[mid_x,self._y+self._height],self._width) # 用一个粗线段来填充背景
 
 # 绘制已经落底下的方块
 bz=self._block_size
 for rect_info in self.rect_arr:
  x=rect_info.x
  y=rect_info.y
  pygame.draw.line(self._bg,rect_info.color,[self._x+x*bz+bz/2,self._y+y*bz],[self._x+x*bz+bz/2,self._y+(y+1)*bz],bz)
  pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz,self._y+y*bz,bz+1,bz+1],1)
 
 # 绘制正在落下的方块
 if self.move_block:
  for rect in self.moving_block.get_rect_arr():
  x,y=rect
  pygame.draw.line(self._bg,self.moving_block.color,[self._x+x*bz+bz/2,self._y+y*bz],[self._x+x*bz+bz/2,self._y+(y+1)*bz],bz)
  pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz,self._y+y*bz,bz+1,bz+1],1)

class Block(object):
 sx=0
 sy=0
 def __init__(self):
 self.rect_arr=[]

 def get_rect_arr(self): # 用于获取方块种的四个矩形列表
 return self.rect_arr

 def move(self,xdiff,ydiff): # 用于移动方块的方法
 self.sx+=xdiff
 self.sy+=ydiff
 self.new_rect_arr=[]
 for x,y in self.rect_arr:
  self.new_rect_arr.append((x+xdiff,y+ydiff))
 self.rect_arr=self.new_rect_arr

 def can_move(self,xdiff,ydiff):
 for x,y in self.rect_arr:
  if y+ydiff>=20: return False
  if x+xdiff<0 or x+xdiff>=10: return False
 return True

 def change(self):
 self.shape_id+=1 # 下一形态
 if self.shape_id >= self.shape_num: 
  self.shape_id=0

 arr = self.get_shape()
 new_arr = []
 for x,y in arr:
  if x+self.sx<0 or x+self.sx>=10: # 变形不能超出左右边界
  self.shape_id -= 1
  if self.shape_id < 0: self.shape_id = self.shape_num - 1
  return None 

  new_arr.append([x+self.sx,y+self.sy])

 return new_arr

class LongBlock(Block):
 shape_id=0
 shape_num=2
 def __init__(self, n=None): # 两种形态
 super(LongBlock, self).__init__()
 if n is None: n=random.randint(0,1)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(50,180,50)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 return [(1,0),(1,1),(1,2),(1,3)] if sid==0 else [(0,2),(1,2),(2,2),(3,2)]

class SquareBlock(Block): # 一种形态
 shape_id=0
 shape_num=1
 def __init__(self, n=None):
 super(SquareBlock, self).__init__()
 self.rect_arr=self.get_shape()
 self.color=(0,0,255)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 return [(1,1),(1,2),(2,1),(2,2)]

class ZBlock(Block): # 两种形态
 shape_id=0
 shape_num=2
 def __init__(self, n=None):
 super(ZBlock, self).__init__()
 if n is None: n=random.randint(0,1)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(30,200,200)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 return [(2,0),(2,1),(1,1),(1,2)] if sid==0 else [(0,1),(1,1),(1,2),(2,2)]

class SBlock(Block): # 两种形态
 shape_id=0
 shape_num=2
 def __init__(self, n=None):
 super(SBlock, self).__init__()
 if n is None: n=random.randint(0,1)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(255,30,255)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 return [(1,0),(1,1),(2,1),(2,2)] if sid==0 else [(0,2),(1,2),(1,1),(2,1)]

class LBlock(Block): # 四种形态
 shape_id=0
 shape_num=4
 def __init__(self, n=None):
 super(LBlock, self).__init__()
 if n is None: n=random.randint(0,3)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(200,200,30)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 if sid==0: return [(1,0),(1,1),(1,2),(2,2)]
 elif sid==1: return [(0,1),(1,1),(2,1),(0,2)]
 elif sid==2: return [(0,0),(1,0),(1,1),(1,2)]
 else: return [(0,1),(1,1),(2,1),(2,0)]

class JBlock(Block): # 四种形态
 shape_id=0
 shape_num=4
 def __init__(self, n=None):
 super(JBlock, self).__init__()
 if n is None: n=random.randint(0,3)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(200,100,0)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 if sid==0: return [(1,0),(1,1),(1,2),(0,2)]
 elif sid==1: return [(0,1),(1,1),(2,1),(0,0)]
 elif sid==2: return [(2,0),(1,0),(1,1),(1,2)]
 else: return [(0,1),(1,1),(2,1),(2,2)]

class TBlock(Block): # 四种形态
 shape_id=0
 shape_num=4
 def __init__(self, n=None):
 super(TBlock, self).__init__()
 if n is None: n=random.randint(0,3)
 self.shape_id=n
 self.rect_arr=self.get_shape()
 self.color=(255,0,0)

 def get_shape(self, sid=None):
 if sid is None: sid = self.shape_id
 if sid==0: return [(0,1),(1,1),(2,1),(1,2)]
 elif sid==1: return [(1,0),(1,1),(1,2),(0,1)]
 elif sid==2: return [(0,1),(1,1),(2,1),(1,0)]
 else: return [(1,0),(1,1),(1,2),(2,1)]

def create_block():
 n = random.randint(0,18)
 if n==0: return SquareBlock(n=0)
 elif n==1 or n==2: return LongBlock(n=n-1)
 elif n==3 or n==4: return ZBlock(n=n-3)
 elif n==5 or n==6: return SBlock(n=n-5)
 elif n>=7 and n<=10: return LBlock(n=n-7)
 elif n>=11 and n<=14: return JBlock(n=n-11)
 else: return TBlock(n=n-15)

def run():
 pygame.init()
 space=30
 main_block_size=30
 main_panel_width=main_block_size*COL_COUNT
 main_panel_height=main_block_size*ROW_COUNT
 screencaption = pygame.display.set_caption('Tetris')
 screen = pygame.display.set_mode((main_panel_width+160+space*3,main_panel_height+space*2)) #设置窗口长宽
 main_panel=Panel(screen,main_block_size,[space,space,main_panel_width,main_panel_height])
 hint_box=HintBox(screen,main_block_size,[main_panel_width+space+space,space,160,160])
 score_box=ScoreBox(screen,main_block_size,[main_panel_width+space+space,160+space*2,160,160])
 
 main_panel.hint_box=hint_box
 main_panel.score_box=score_box

 pygame.key.set_repeat(200, 30)
 main_panel.create_move_block()

 diff_ticks = 300 # 移动一次蛇头的事件,单位毫秒
 ticks = pygame.time.get_ticks() + diff_ticks

 player = AIPlayer()

 pause=0
 game_state = 1 # 游戏状态1.表示正常 2.表示失败
 while True:
 for event in pygame.event.get():
  if event.type == pygame.QUIT:
   pygame.quit()
   exit()
  if event.type == KEYDOWN:
  if event.key==97: pause=1-pause # 按键盘a支持暂停
  if event.key==112: # for debug # 按键盘p打印矩阵信息
   main_panel.get_rect_matrix().print_matrix()
  if player.auto_mode:continue
  if event.type == KEYDOWN:
  if event.key == K_LEFT: main_panel.control_block(-1,0)
  if event.key == K_RIGHT: main_panel.control_block(1,0)
  if event.key == K_UP: main_panel.change_block()
  if event.key == K_DOWN: main_panel.control_block(0,1)
  if event.key == K_SPACE:
   flag = main_panel.move_block()
   while flag==1: 
   flag = main_panel.move_block()
   if flag == 9: game_state = 2
 
 screen.fill((100,100,100)) # 将界面设置为灰色
 main_panel.paint() # 主面盘绘制
 hint_box.paint() # 绘制下一个方块的提示窗
 score_box.paint() # 绘制总分

 if game_state == 2:
  myfont = pygame.font.Font(None,30)
  white = 255,255,255
  textImage = myfont.render("Game over", True, white)
  screen.blit(textImage, (160,190))

 pygame.display.update() # 必须调用update才能看到绘图显示

 if pause==1: continue
 if game_state == 1: player.run(main_panel)
 if game_state == 1 and pygame.time.get_ticks() >= ticks:
  ticks+=diff_ticks
  if main_panel.move_block()==9: game_state = 2 # 游戏结束

run()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python解析文件示例
Jan 23 Python
Python线程详解
Jun 24 Python
python机器学习实战之最近邻kNN分类器
Dec 20 Python
python生成每日报表数据(Excel)并邮件发送的实例
Feb 03 Python
Python实现的删除重复文件或图片功能示例【去重】
Apr 23 Python
使用python将mysql数据库的数据转换为json数据的方法
Jul 01 Python
python命名空间(namespace)简单介绍
Aug 10 Python
Python 类方法和实例方法(@classmethod),静态方法(@staticmethod)原理与用法分析
Sep 20 Python
Django框架创建项目的方法入门教程
Nov 04 Python
Python 矩阵转置的几种方法小结
Dec 02 Python
Python操作注册表详细步骤介绍
Feb 05 Python
Python下载商品数据并连接数据库且保存数据
Mar 31 Python
基于Django统计博客文章阅读量
Oct 29 #Python
pygame实现俄罗斯方块游戏(基础篇3)
Oct 29 #Python
python安装gdal的两种方法
Oct 29 #Python
pygame实现俄罗斯方块游戏(基础篇2)
Oct 29 #Python
pygame实现俄罗斯方块游戏(基础篇1)
Oct 29 #Python
pygame实现五子棋游戏
Oct 29 #Python
python多线程案例之多任务copy文件完整实例
Oct 29 #Python
You might like
PHP 高手之路(三)
2006/10/09 PHP
mysql5写入和读出乱码解决
2006/11/25 PHP
php的array_multisort()使用方法介绍
2012/05/16 PHP
php增删改查示例自己写的demo
2013/09/04 PHP
php获取目录所有文件并将结果保存到数组(实例)
2013/10/25 PHP
php实现评论回复删除功能
2017/05/23 PHP
a标签的css样式四个状态
2021/03/09 HTML / CSS
js实现ASP分页函数 HTML分页函数
2006/09/22 Javascript
ext checkboxgroup 回填数据解决
2009/08/21 Javascript
JS生成随机字符串的多种方法
2014/06/10 Javascript
Egret引擎开发指南之视觉编程
2014/09/03 Javascript
node.js中的favicon.ico请求问题处理
2014/12/15 Javascript
javascript中使用new与不使用实例化对象的区别
2015/06/22 Javascript
JavaScript中点击事件的写法
2016/06/28 Javascript
基于jQuery的AJAX和JSON实现纯html数据模板
2016/08/09 Javascript
微信小程序开发之大转盘 仿天猫超市抽奖实例
2016/12/08 Javascript
jQuery+HTML5实现WebGL高性能烟花绽放动画效果【附demo源码下载】
2017/08/18 jQuery
微信小程序之选项卡的实现方法
2017/09/29 Javascript
Node.js 进程平滑离场剖析小结
2019/01/24 Javascript
javascript实现倒计时关闭广告
2021/02/09 Javascript
在Python的setuptools框架下生成egg的教程
2015/04/13 Python
在VS Code上搭建Python开发环境的方法
2018/04/06 Python
Python3.6基于正则实现的计算器示例【无优化简单注释版】
2018/06/14 Python
python tkinter实现界面切换的示例代码
2019/06/14 Python
python实现串口自动触发工作的示例
2019/07/02 Python
python 批量添加的button 使用同一点击事件的方法
2019/07/17 Python
解决python3 安装不了PIL的问题
2019/08/16 Python
django项目中使用云片网发送短信验证码的实现
2021/01/19 Python
详解python的xlwings库读写excel操作总结
2021/02/26 Python
英国高街品牌:Miss Selfridge(塞尔弗里奇小姐)
2016/09/21 全球购物
生物专业个人自荐信范文
2013/11/29 职场文书
销售行政专员职责
2014/01/03 职场文书
年终考核评语
2014/01/19 职场文书
承诺书怎么写
2014/03/26 职场文书
教师见习期自我鉴定
2014/04/28 职场文书
2014年乡镇领导个人整改措施
2014/09/19 职场文书