博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Python解决十字消除棋

Posted on 2011-08-08 21:27  visiblelight  阅读(1008)  评论(0)    收藏  举报

用Python写了一小段程序自动玩微博上的一个十字消除棋游戏。主要用PIL读取棋盘状态,用PyWin32模拟鼠标操作。

开始的时候必须先把鼠标移到最左上角的色块中心,利用PIL的ImageGrab截取整个屏幕,然后根据鼠标位置分割各个色块读取状态。各个色块的特征值直接用颜色平均值表示了,很粗糙,所以尽量把鼠标移到色块正中心吧...

模拟鼠标点击有些问题,有时候游戏响应不够快会出现点击后该消除的色块没有消除掉,然后后面的游戏状态就全乱掉了。不过这个可以靠延长每次点击后sleep的时间来尽量避免。

色块的大小tile是经过几次尝试凑出来的经验值,可以利用ImageDraw把各个色块的分割区域画出来来确定分割是不是准确的。十字消除棋的游戏有许多版本,不过几乎所有的版本只有色块的大小略有分别,事实上只要把tile的大小调整一下,这个程序应该都能解。

另一个比较大的问题是找消除位置的时候只是简单地遍历一遍空白处,找到一个可点击的地方就停止,所以没法保证每一步的走法能使得最终消除掉所有的色块。

举个例子若有如下状态:

0 0 0 2

0 0 0 0

2 2 0 2

若程序遍历后选择先点击(3,1)则左下角的两个色块最终没法消除掉。但实事上如果选择先点击(2,2)后点击(0,0)是可以消除掉的有的色块的。

实际试下来后发现的确很多时候都没法将全部色块消除掉... 当然每次高分还是有保证的,多试几次也能碰到一次全部消除得200分的...

import win32api
import win32con

from PIL import ImageGrab
from PIL import ImageDraw

width = 23
height = 15
# for wanwan.sina
#tile = 27.7
# for game.weibo
tile = 28.5
blanks = ((237, 237, 237), (247, 247, 247))
	
print "Please move the cursor to piece on the left top."
win32api.Sleep(7000)
print "Starting..."
im = ImageGrab.grab()
ox, oy = win32api.GetCursorPos()

colors = []
cropSize = 5
matrix = [[0 for i in range(width)] for j in range(height)]
# Enable the ImageDraw to see the slices.
#draw = ImageDraw.Draw(im)
for x in range(width):
	for y in range(height):
		segment = im.crop((int(ox + x * tile - cropSize), int(oy + y * tile - cropSize), int(ox + x * tile + cropSize), int(oy + y * tile + cropSize)))
		data = list(segment.getdata())
		#draw.rectangle((ox + x * tile - cropSize, oy + y * tile - cropSize, ox + x * tile + cropSize, oy + y * tile + cropSize))		
		dataSum = reduce(lambda lv, rv: (lc + rc for lc, rc in zip(lv, rv)), data)
		feature = tuple(c / len(data) for c in dataSum)
		
		if feature in blanks:
			continue
		
		found = False
		for key, color in enumerate(colors):
			if sum((abs(lc - rc) for lc, rc in zip(color, feature))) < 10:
				found = True
				matrix[y][x] = key + 1
				break
		
		if not found:
			colors.append(feature)
			matrix[y][x] = len(colors)		

#im.save('im.bmp')

def trace(start, direction):
	length = 1
	while True:
		x, y = start[0] + direction[0] * length, start[1] + direction[1] * length
		if x >= 0 and x < width and y >=0 and y < height:
			if matrix[y][x]:
				return (matrix[y][x], (x, y))
			else:
				length += 1
		else:
			break
			
	return (0, (-1, -1))
	
if len(colors) == 10:
	directions = ((1, 0), (-1, 0), (0, 1), (0, -1))
	while sum((line.count(0) for line in matrix)) < width * height:
		clicked = False
		for x in range(width):
			if clicked: break
			for y in range(height):
				if clicked: break
				if matrix[y][x] == 0:										
					result = (trace((x, y), d) for d in directions)
					result = filter(lambda (color, pos): color != 0, result)
					if len(dict(result)) < len(result):
						pair = False
						for key in dict(result).iterkeys():
							if [c for c, p in result].count(key) % 2 == 0:
								pair = True
								for color, pos in result:
									if color == key:
										matrix[pos[1]][pos[0]] = 0
						
						if pair:
							win32api.SetCursorPos((int(ox + x * tile), int(oy + y * tile)))
							win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
							win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
							clicked = True
							win32api.Sleep(700)
						
		if not clicked:
			print "Can't find a move any more..."
			break
else:
	print "Make sure move the cursor to piece on the left top..."

if sum((line.count(0) for line in matrix)) == width * height:
	print "Clear all the pieces successfully..."