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绘制人人网好友关系图示例
Apr 01 Python
python中使用百度音乐搜索的api下载指定歌曲的lrc歌词
Jul 18 Python
Python 操作MySQL详解及实例
Apr 30 Python
Windows 7下Python Web环境搭建图文教程
Mar 20 Python
Python使用Windows API创建窗口示例【基于win32gui模块】
May 09 Python
Python基于jieba库进行简单分词及词云功能实现方法
Jun 16 Python
python3实现随机数
Jun 25 Python
Pycharm无法使用已经安装Selenium的解决方法
Oct 13 Python
Python 常用模块 re 使用方法详解
Jun 06 Python
Pandas —— resample()重采样和asfreq()频度转换方式
Feb 26 Python
Jupyter notebook 更改文件打开的默认路径操作
May 21 Python
Python Pytorch查询图像的特征从集合或数据库中查找图像
Apr 09 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 各种应用乱码问题的解决方法
2010/05/09 PHP
PHP5下$_SERVER变量不再受magic_quotes_gpc保护的弥补方法
2012/10/31 PHP
PHP PDO数据库操作预处理与注意事项
2019/03/16 PHP
用JavaScript编写COM组件的步骤
2009/03/17 Javascript
js 数组操作代码集锦
2009/04/28 Javascript
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
JS表格组件神器bootstrap table详解(基础版)
2015/12/08 Javascript
详解Angular的数据显示优化处理
2016/12/26 Javascript
vue 左滑删除功能的示例代码
2019/01/28 Javascript
vue自定义指令之面板拖拽的实现
2019/04/14 Javascript
微信小程序 wxParse插件显示视频问题
2019/09/27 Javascript
解决vue页面渲染但dom没渲染的操作
2020/07/27 Javascript
Python中的True,False条件判断实例分析
2015/01/12 Python
Python的string模块中的Template类字符串模板用法
2016/06/27 Python
详解Python中使用base64模块来处理base64编码的方法
2016/07/01 Python
分享几道你可能遇到的python面试题
2017/07/24 Python
django中上传图片分页三级联动效果的实现代码
2019/08/30 Python
HTML5中的强制下载属性download使用实例解析
2016/05/12 HTML / CSS
澳大利亚波西米亚风情网上商店:Czarina
2019/03/18 全球购物
澳大利亚网上书店:QBD
2021/01/09 全球购物
腾讯技术类校园招聘笔试试题
2014/05/06 面试题
在对linux系统分区进行格式化时需要对磁盘簇(或i节点密度)的大小进行选择,请说明选择的原则
2012/01/13 面试题
研发工程师的岗位职责
2013/11/18 职场文书
学校食堂采购员岗位职责
2013/12/05 职场文书
应聘文员自荐信范文
2014/03/11 职场文书
入职担保书范文
2014/05/21 职场文书
篮球比赛拉拉队口号
2014/06/10 职场文书
机关作风建设心得体会
2014/10/22 职场文书
2014年社区党建工作汇报材料
2014/11/02 职场文书
2015年医德考评自我评价
2015/03/03 职场文书
2015年度电厂个人工作总结
2015/05/13 职场文书
离职信范本
2015/06/23 职场文书
Python爬虫进阶之Beautiful Soup库详解
2021/04/29 Python
python可视化之颜色映射详解
2021/09/15 Python
《原神》新角色演示“神里绫人:林隐泓洄” 宠妹狂魔
2022/04/03 其他游戏
django项目、vue项目部署云服务器的详细过程
2022/07/23 Servers