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读写文件操作示例程序
Dec 02 Python
Python 正则表达式(转义问题)
Dec 15 Python
剖析Django中模版标签的解析与参数传递
Jul 21 Python
30秒轻松实现TensorFlow物体检测
Mar 14 Python
python 字典中取值的两种方法小结
Aug 02 Python
python爬取微信公众号文章的方法
Feb 26 Python
django 实现将本地图片存入数据库,并能显示在web上的示例
Aug 07 Python
python paramiko远程服务器终端操作过程解析
Dec 14 Python
python使用Windows的wmic命令监控文件运行状况,如有异常发送邮件报警
Jan 30 Python
Python爬取酷狗MP3音频的步骤
Feb 26 Python
浅谈pytorch中的dropout的概率p
May 27 Python
opencv-python图像配准(匹配和叠加)的实现
Jun 23 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+mysql分页代码详解
2008/03/27 PHP
微信 getAccessToken方法详解及实例
2016/11/23 PHP
ajax调用返回php接口返回json数据的方法(必看篇)
2017/05/05 PHP
PHP中的日期时间处理利器实例(Carbon)
2017/06/09 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
Yii 2.0实现联表查询加搜索分页的方法示例
2017/08/02 PHP
jquery 必填项判断表单是否为空的方法
2008/09/14 Javascript
javascript操作cookie的文章(设置,删除cookies)
2010/04/01 Javascript
javascript 从if else 到 switch case 再到抽象
2010/07/17 Javascript
javascript 数据类型转换(parseInt,parseFloat)
2010/07/20 Javascript
JS面向对象编程之对象使用分析
2010/08/19 Javascript
AlertBox 弹出层信息提示框效果实现步骤
2010/10/11 Javascript
基于jquery的弹出提示框始终处于窗口的居中位置(类似于alert弹出框的效果)
2011/09/28 Javascript
jQuery+HTML5加入购物车代码分享
2020/10/29 Javascript
jQuery移动端日期(datedropper)和时间(timedropper)选择器附源码下载
2016/04/19 Javascript
AngularJS过滤器filter用法总结
2016/12/13 Javascript
vue 2.1.3 实时显示当前时间,每秒更新的方法
2018/09/16 Javascript
浅谈Vue中render中的h箭头函数
2019/11/07 Javascript
node创建Vue项目步骤详解
2020/03/06 Javascript
ES11屡试不爽的新特性,你用上了几个
2020/10/21 Javascript
详解ES6实现类的私有变量的几种写法
2021/02/10 Javascript
[34:44]Liquid vs TNC Supermajor 胜者组 BO3 第二场 6.4
2018/06/05 DOTA
python解析中国天气网的天气数据
2014/03/21 Python
python中的全局变量用法分析
2015/06/09 Python
详解Python里使用正则表达式的ASCII模式
2017/11/02 Python
用python实现对比两张图片的不同
2018/02/05 Python
用pycharm开发django项目示例代码
2019/06/13 Python
Python使用Tkinter实现转盘抽奖器的步骤详解
2020/01/06 Python
Ubuntu16.04安装python3.6.5步骤详解
2020/01/10 Python
Flask处理Web表单的实现方法
2021/01/31 Python
django使用多个数据库的方法实例
2021/03/04 Python
CSS3支持IE6, 7, and 8的边框border属性
2012/12/28 HTML / CSS
设计师大码女装:11 Honoré
2020/05/03 全球购物
这76道Java面试题及答案,祝你能成功通过面试
2016/04/16 面试题
导游词之蜀山胜景瓦屋山
2019/11/29 职场文书
一文搞懂python异常处理、模块与包
2021/06/26 Python