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中规范定义命名空间的一些建议
Jun 04 Python
Python利用flask sqlalchemy实现分页效果
Aug 02 Python
Python基于回溯法子集树模板解决野人与传教士问题示例
Sep 11 Python
python多线程socket编程之多客户端接入
Sep 12 Python
Python实现的单向循环链表功能示例
Nov 10 Python
浅谈Django学习migrate和makemigrations的差别
Jan 18 Python
python实现人民币大写转换
Jun 20 Python
Python实现Dijkstra算法
Oct 17 Python
Python eval函数原理及用法解析
Nov 14 Python
python3中布局背景颜色代码分析
Dec 01 Python
Autopep8的使用(python自动编排工具)
Mar 02 Python
python爬虫selenium模块详解
Mar 30 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
如何跨站抓取别的站点的页面的补充
2006/10/09 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
2015/10/26 PHP
php使用APC实现实时上传进度条功能
2015/10/26 PHP
浅析php-fpm静态和动态执行方式的比较
2016/11/09 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
javascript document.images实例
2008/05/27 Javascript
JS跨域问题详解
2014/11/25 Javascript
浅谈javascript 函数内部属性
2015/01/21 Javascript
js点击文本框后才加载验证码实例代码
2015/10/20 Javascript
js记录点击某个按钮的次数-刷新次数为初始状态的实例
2017/02/15 Javascript
关于vue-router的beforeEach无限循环的问题解决
2017/09/09 Javascript
JavaScript数组去重的多种方法(四种)
2017/09/19 Javascript
js实现数组和对象的深浅拷贝
2017/09/30 Javascript
js验证密码强度解析
2020/03/18 Javascript
微信小程序实现可拖动悬浮图标(包括按钮角标的实现)
2020/12/29 Javascript
python同时给两个收件人发送邮件的方法
2015/04/30 Python
Python生成rsa密钥对操作示例
2019/04/26 Python
Pytorch中Tensor与各种图像格式的相互转化详解
2019/12/26 Python
Python使用Opencv实现边缘检测以及轮廓检测的实现
2020/12/31 Python
python excel和yaml文件的读取封装
2021/01/12 Python
关于canvas绘制模糊问题的解决方法
2019/09/24 HTML / CSS
在印度上传处方,在线订购药品:Medlife
2019/03/28 全球购物
应届生法律求职信
2013/10/22 职场文书
学习党课思想汇报
2013/12/29 职场文书
安全事故检讨书
2014/01/18 职场文书
2014年干部作风建设总结
2014/10/23 职场文书
优秀护士事迹材料
2014/12/25 职场文书
医生辞职信范文
2015/03/02 职场文书
党员干部廉洁自律承诺书
2015/04/28 职场文书
2015年审计人员工作总结
2015/05/26 职场文书
情人节单身感言
2015/08/03 职场文书
详解JS ES6编码规范
2021/05/07 Javascript
PostgreSQL通过oracle_fdw访问Oracle数据的实现步骤
2021/05/21 PostgreSQL
使用redis生成唯一编号及原理示例详解
2021/09/15 Redis
Python常遇到的错误和异常
2021/11/02 Python
oracle数据库去除重复数据
2022/05/20 Oracle