百度之星2012-2013成绩抓取python脚本

使用Selenium自动玩2048

xhSong posted @ 2014年8月20日 01:24 in python with tags Selenium 2048 自动 AI 估值 auto2048 , 3463 阅读

前几天看summer师弟玩Selenium感觉挺有意思。便拿着时间风靡一时的游戏“2048”来练手,写了个简单的 AI。甚是欢乐!

Selenium

啥是selenimu,简单的说——Selenium也是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE、Mozilla Firefox、Mozilla Suite等(来自 http://www.51testing.com/zhuanti/selenium.html)。

selenimu这里就不多介绍了,细节请看 selenium 的 python api 文档

2048策略

2048这个游戏地球人都知道就不介绍了。主要介绍下我的AI。我的AI对于每一个方向的评估有三个方面

  1. 移动导致合并的得分 score:这个就是游戏本身定义的得分,如 4和4合并得8分,128和128合并的256分
  2. 移动后每一行每一列的单调性 monotone:对于每一行(每一列)如果 line[i] <= line[i+1]则mon+= line[i]+line[i+1]否则,mon-=line[i]+line[i+1],monotone=sum(abs(mon))
  3. 移动后相邻块值相同的情况 adjoin:任意两个相邻块的值相同,如cells[i][j]=cells[i+1][j],则 adjoin+=cells[i][j]

最后的估值 estimation = score + monotone * 0.3 + adjoin。对于上下左右四个方向取estimation最大的方向操作

这种方法还可以,运气好的话,可以得到2048

程序结构

程序的源代码如下:


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import os
import time

size = 4

class Estimator:
    def estimate(self, precells, postcells, action, score):
        for i in range(size):
            score += self.__estimate_line([postcells[i][j] for j in range(size)])
            score += self.__estimate_line([postcells[j][i] for j in range(size)])
        return score

    def __estimate_line(self, line):
        monotone, adjoin = 0, 0
        for i in range(size - 1):
            if line[i + 1] > line[i]:
                monotone += line[i + 1] + line[i]
            else:
                monotone -= line[i + 1] + line[i]
            if line[i + 1] == line[i]:
                adjoin += line[i]
        return abs(monotone) * .3 + adjoin

class Auto2048:
    def __init__(self, url, estimator):
        self.browser = webdriver.Firefox()
        self.browser.get(url)
        self.estimator = estimator

    def get_cells(self):
        tiles = self.browser.find_elements_by_class_name('tile')
        self.cells = [[0 for i in range(4)] for i in range(4)]

        for tile in tiles:
            attr = tile.get_attribute('class').split()
            value = int(attr[1].split('-')[1])
            x = int(attr[2].split('-')[3]) - 1
            y = int(attr[2].split('-')[2]) - 1
            self.cells[x][y] = value

    def AI(self):

        self.get_cells()
        self.Print(self.cells)

        action, actionname = '', ''
        moveable = False
        
        strategies = [    {'fun': self.try_up, 'action': Keys.UP, 'name': 'Up'}, 
                        {'fun': self.try_down, 'action': Keys.DOWN, 'name': 'Down'}, 
                        {'fun': self.try_left, 'action': Keys.LEFT, 'name': 'Left'}, 
                        {'fun': self.try_right, 'action': Keys.RIGHT, 'name': 'Right'}]
        for strategy in strategies:
            result = strategy['fun']()
            estimation = self.estimator.estimate(self.cells, result['cells'], strategy['name'], result['score'])
            if result['moveable'] and (moveable == False or max_estimation < estimation):
                action = strategy['action']
                max_estimation = estimation
                moveable = True
                actionname = strategy['name']

        if not moveable:
            return False

        self.browser.find_element_by_class_name('grid-container').send_keys(action)
        print 'Action: ', actionname
        return True

    def move_left(self, cells):
        moveable = False
        score = 0
        for x in range(size):
            pre = 0
            for y in range(size):
                if cells[x][y]:
                    cells[x][pre] = cells[x][y]
                    if y != pre:
                        moveable = True
                        cells[x][y] = 0
                    pre += 1
            for y in range(size - 1):
                if cells[x][y] and cells[x][y] == cells[x][y + 1]:
                    cells[x][y] += cells[x][y]
                    score += cells[x][y]
                    cells[x][y + 1] = 0
                    moveable = True
            pre = 0
            for y in range(size):
                if cells[x][y]:
                    cells[x][pre] = cells[x][y]
                    if y != pre:
                        moveable = True
                        cells[x][y] = 0
                    pre += 1
        return {'moveable': moveable, 'score': score, 'cells': cells}
    
    def try_left(self):
        cells = [[self.cells[i][j] for j in range(size)] for i in range(size)]
        return self.move_left(cells)

    def try_right(self):
        cells = [[self.cells[i][size - 1 - j] for j in range(size)] for i in range(size)]
        result = self.move_left(cells)
        result['cells'] = [[result['cells'][i][size - 1 - j] for j in range(size)] for i in range(size)]
        return result

    def try_up(self):
        cells = [[self.cells[j][i] for j in range(size)] for i in range(size)]
        result = self.move_left(cells)
        result['cells'] = [[result['cells'][j][i] for j in range(size)] for i in range(size)]
        return result
    
    def try_down(self):
        cells = [[self.cells[size - 1 - j][i] for j in range(size)] for i in range(size)]
        result = self.move_left(cells)
        result['cells'] = [[result['cells'][j][size - 1 - i] for j in range(size)] for i in range(size)]
        return result

    def __del__(self):
        self.browser.close()

    def Print(self, cells):
        print 
        for x in range(size):
            for y in range(size):
                print '%5d' % cells[x][y], 
            print 

if __name__ == '__main__':
    url = 'file://' + os.path.abspath('2048/index.html')
    # url = "http://gabrielecirulli.github.io/2048/"
    auto2048 = Auto2048(url, Estimator())
    while auto2048.AI():
        time.sleep(0.2)
    time.sleep(10)

源代码中有两个类

主逻辑类 Auto2048

使用 2048的url和估值类(AI逻辑)构造。测试过程中,用 wget 将 "http://gabrielecirulli.github.io/2048/" 的所有页面抓到本地分析(由于不懂js在网页中的工作原理,使用find_elements_by_class_name找当前cells的信息找了好久)

每一次操作调一次AI(),AI() 先获取当前页面的状态保存在 self.cells 中,然后对上下左右四个方向枚举,取估值最大的方向并使用send_keys进行操作。如果能操作则AI()返回True,否则返回False

估值类 Estimator

估值类只要实现 def estimate(self, precells, postcells, action, score): 估值方法即可。其中,precells为操作前状态,postcells为操作后状态,atcion为操作['Left', 'Right', 'Up', 'Down'],score为操作得分。返回值为估值estimation,值越到越好。

虽然我的估值方法运气好的话可以得到2048,但还是很粗糙的。你要有兴趣的话,可以写一个更好的Estimator得到更高的分。

源代码地址 https://github.com/xhSong/auto2048

  • 无匹配
Avatar_small
전설 서구 说:
2021年3月02日 20:00

Nice post. I was checking constantly this blog and I am impressed! Extremely helpful information specially the last part I care for such info a lot. I was seeking this particular information for a very long time. Thank you and good luck. 123 movies

Avatar_small
AP 10th Geography Qu 说:
2022年9月19日 00:11

Geography is an important subject in Class 10t. However, considerable ambiguities persist in its exact position within academia. AP 10th Geography Question Paper As an inclusive discipline, the content of geography is derived from various subjects in the sciences, social sciences and humanities. This is equally true about the geography syllabus in schools as well.Telugu Medium, English Medium & Urdu Medium Students of the State Board can download the AP 10th Geography Model Paper 2023 Pdf with answers for all exam formats conducted by BSEAP for Part-A, Part-B, Part-C, and Part-D exams of SA-1, SA-2, FA-1, FA-2, FA-3, FA-4 along with Assignments which were designed by subject experts of leading educational institutes.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter