现在继续
一、定义玩家类
定义玩家类是为了便于进行手动和机器模式或各种不同机器人模式的混合使用,增加代码扩展性。
可以先定义一个玩家基类
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
下面然后游戏代码中做下面三处修改
好了,现在玩家类添加完毕,由于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
if x>maxx: maxx = x
if 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
大概的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