python解华容道(—)Leo Jay的解决方案

3751 发表于 2008-09-27 10:00:48

代码来自于这里,说真的我不觉得我有必要对他的实现做任何的解释!
#coding: utf-8

from itertools import chain

# 用来加速python程序的。在我的机器上,不用psyco是每秒780个结点,用psyco是913个结点,17%的性能提升。
import psyco
psyco.full()


class Item(object):
    u''' 棋盘上的对象,注,棋盘的左上角坐标为(1, 1),右下角坐标为(4, 5) '''

    # 以下4个函数是对象向上下左右移动,如果移动后出界,返回False

    def left(self):
        self.x -= 1
        for x, y in self.pixels():
            if not x > 0:
                return False
        return True

    def right(self):
        self.x += 1
        for x, y in self.pixels():
            if not x < 5:
                return False
        return True

    def up(self):
        self.y -= 1
        for x, y in self.pixels():
            if not y > 0:
                return False
        return True

    def down(self):
        self.y += 1
        for x, y in self.pixels():
            if not y < 6:
                return False
        return True

    def clone(self):
        u''' 复制一份对象 '''
        return self.__class__(self.x, self.y)

    def __lt__(self, other):
        u''' 对象的坐标比较 '''
        return self.x < other.x or self.x == other.x and self.y < other.y

    def __str__(self):
        u''' 对象的输出 '''
        return '%s at (%d, %d)' % (self.__class__.__name__, self.x, self.y)
    __repr__ = __str__


class Dot(Item):
    u''' 用来表示一个小方格的对象,对应游戏里的'兵'和'卒' '''
    C = ord('D')

    def __init__(self, x, y):
        if not 1<= x <= 4 or not 1 <= y <= 5:
            self.valid = False

        self.x = x
        self.y = y

    def pixels(self):
        yield (self.x, self.y)


class VerticalBar(Item):
    u''' 用来表示一个竖条的对象,对应游戏里的'黄忠'和'马超'等 '''
    C = ord('V')

    def __init__(self, x, y):
        if not 1<= x <= 4 or not 1 <= y <= 4:
            self.valid = False

        self.x = x
        self.y = y

    def pixels(self):
        yield (self.x, self.y)
        yield (self.x, self.y+1)


class HorizontalBar(Item):
    u''' 用来表示一个横条的对象,对应游戏里的'关羽' '''
    C = ord('H')

    def __init__(self, x, y):
        if not 1<= x <= 3 or not 1 <= y <= 5:
            self.valid = False

        self.x = x
        self.y = y

    def pixels(self):
        yield (self.x, self.y)
        yield (self.x+1, self.y)


class Box(Item):
    u''' 用来表示一个正方形的对象,对应游戏里的'曹操' '''
    C = ord('B')

    def __init__(self, x, y):
        if not 1<= x <= 3 or not 1 <= y <= 4:
            self.valid = False

        self.x = x
        self.y = y

    def pixels(self):
        yield (self.x, self.y)
        yield (self.x+1, self.y)
        yield (self.x, self.y+1)
        yield (self.x+1, self.y+1)


class Board(Item):
    u''' 整个棋盘 '''

    def check_validity(self):
        pixels = set()
        for item in self.items():
            for x, y in item.pixels():
                if (x, y) in pixels:
                    return False
                pixels.add((x, y))

        return len(pixels) == 18

    def __init__(self, box, dots, vbs, hbs):
        self.box = box
        self.dots = dots
        self.vbs = vbs
        self.hbs = hbs

        self.dots.sort()
        self.vbs.sort()
        self.hbs.sort()


        if not self.check_validity():
            self.valid = False
            return

        # 用一个Long型的变量来唯一地表示一个棋盘的状态。
        self.statuscode = reduce(lambda code, item: (code<<5) + ((item.x-1)<<3)+item.y-1, self.items(), 0L)

    def __str__(self):
        result = [[ord(' ')]*4 for i in xrange(5)]
        for item in self.items():
            for x, y in item.pixels():
                result[y-1][x-1] = item.C
        return '\n'.join(''.join(chr(c) for c in line) for line in result)

    def items(self):
        u''' 按顺序返回棋盘里的所有对象 '''
        return chain([self.box], self.dots, self.vbs, self.hbs)


    OP = ['up', 'down', 'left', 'right']

    def permutate_items(self):
        u''' 枚举棋盘上可能的移动方案,返回新的棋盘 '''

        # 试着往4个方向移动box
        for op in Board.OP:
            newbox = self.box.clone()
            if not getattr(newbox, op)():
                continue

            newboard = Board(newbox, self.dots, self.vbs, self.hbs)
            if not hasattr(newboard, 'valid'):
                yield newboard

        # 试着往4个方向移动dot
        for i in xrange(len(self.dots)):
            origindot = self.dots[i]
            for op in Board.OP:
                newdot = origindot.clone()
                if not getattr(newdot, op)():
                    continue

                newdots = self.dots[:]
                newdots[i] = newdot
                newboard = Board(self.box, newdots, self.vbs, self.hbs)
                if not hasattr(newboard, 'valid'):
                    yield newboard
                    for op2 in Board.OP:
                        newdot2 = newdot.clone()
                        if not getattr(newdot2, op2)() or newdot2.x == origindot.x and newdot2.y == origindot.y:
                            continue

                        newdots = self.dots[:]
                        newdots[i] = newdot2
                        newboard = Board(self.box, newdots, self.vbs, self.hbs)
                        if not hasattr(newboard, 'valid'):
                            yield newboard


        # 试着往4个方向移动vertical bar
        for i in xrange(len(self.vbs)):
            for op in Board.OP:
                newvb = self.vbs[i].clone()
                if not getattr(newvb, op)():
                    continue

                newvbs = self.vbs[:]
                newvbs[i] = newvb
                newboard = Board(self.box, self.dots, newvbs, self.hbs)
                if not hasattr(newboard, 'valid'):
                    yield newboard
                    newvb = newvb.clone()
                    if not getattr(newvb, op)():
                        continue

                    newvbs = self.vbs[:]
                    newvbs[i] = newvb
                    newboard = Board(self.box, self.dots, newvbs, self.hbs)
                    if not hasattr(newboard, 'valid'):
                        yield newboard



        # 试着往4个方向移动horizontal bar
        for i in xrange(len(self.hbs)):
            for op in Board.OP:
                newhb = self.hbs[i].clone()
                if not getattr(newhb, op)():
                    continue

                newhbs = self.hbs[:]
                newhbs[i] = newhb
                newboard = Board(self.box, self.dots, self.vbs, newhbs)
                if not hasattr(newboard, 'valid'):
                    yield newboard
                    newhb = newhb.clone()
                    if not getattr(newhb, op)():
                        continue

                    newhbs = self.hbs[:]
                    newhbs[i] = newhb
                    newboard = Board(self.box, self.dots, self.vbs, newhbs)
                    if not hasattr(newboard, 'valid'):
                        yield newboard

    def worked_out(self):
        u''' 返回当前棋盘是否是获胜的棋盘,即box是否走到了坐标(2, 4) '''
        return self.box.x == 2 and self.box.y == 4


def solve_the_board(board):
    u''' 求指定的棋盘的解 '''
    from datetime import datetime

    if hasattr(board, 'valid'):
        print 'original data error!'
        return

    print 'original status: %x' % (board.statuscode)

    searched = {}       # 一个dict,保留了statuscode到board的映射
    bfs = []            # 记录我们广度优先搜索的节点
    board.previous_status = None
    bfs.append(board)
    searched[board.statuscode] = board
    board.step = 0

    # 开始做广度优先搜索(BFS)
    start = datetime.now()
    i = 0
    current_step = 0
    while i < len(bfs):
        board = bfs[i]      # 取第i个结点做推演

        if board.step != current_step:
            current_step = board.step
            print 'current step: %d, i = %d' % (current_step, i)

        i += 1
        if i % 1000 == 0:   # 每处理1k个结点,输出一些信息
            print i
            delta = datetime.now() - start
            dt = delta.days * 24 * 60 * 60.0 + delta.seconds
            if dt:
                print 'len(bfs) = %d, len(searched) = %d, %.2f nodes / sec' % (len(bfs), len(searched), i / dt)

        # 找这个结点棋盘所有可能的下一步
        for nb in board.permutate_items():
            # 如果这一步曾经到过,就不再处理了。
            if nb.statuscode in searched:
                continue

            nb.step = board.step + 1

            # 如果已经找到解了,输出然后退出
            if nb.worked_out():
                result = [nb]

                # 由每一个结点棋盘的previous_status属性,计算出走的过程
                previous_status = board.statuscode
                while previous_status:
                    pre = searched[previous_status]
                    result.append(pre)
                    previous_status = pre.previous_status

                for board in reversed(result):
                    print board
                    print '-' * 20

                print 'total steps:', nb.step
                return

            # 计录下新的节点是由当前节点而来的,方便后面的输出。
            nb.previous_status = board.statuscode

            # 把新计算出来的结点,放到bfs的后面
            searched[nb.statuscode] = nb
            bfs.append(nb)

    # 如果所有的结点都找完了,说明无解。
    print 'not solution!'


def main():
    # 第1关 横刀立马
    #board = Board(Box(2, 1),        # 曹操的位置
    #          [Dot(1, 5), Dot(2, 4), Dot(3, 4), Dot(4, 5)],     # 所有点点的位置
    #          [VerticalBar(1, 1), VerticalBar(1, 3), VerticalBar(4, 1), VerticalBar(4, 3)], # 竖条的位置
    #          [HorizontalBar(2, 3)],     # 横条的位置
    #          )

    # 第53关 四将连关
    board = Board(Box(1, 1),        # 曹操的位置
              [Dot(1, 5), Dot(3, 4), Dot(4, 4), Dot(4, 5)],     # 所有点点的位置
              [VerticalBar(1, 3), VerticalBar(2, 3)],           # 竖条的位置
              [HorizontalBar(3, 1), HorizontalBar(3, 2), HorizontalBar(3, 3)],     # 横条的位置
              )

    solve_the_board(board)

main()


# 下面两个函数是用来做性能分析的


def runhotshot():
    import hotshot
    prof = hotshot.Profile('/home/leojay/my/test.log')
    prof.runcall(main)
    prof.close()
#runhotshot()


def printhotshot():
    import hotshot.stats
    stats = hotshot.stats.load('/home/leojay/my/test.log')
    stats.strip_dirs()
    stats.sort_stats('time', 'calls')
    stats.print_stats(20)
#printhotshot()
关键词(Tag): 华容道 python


收藏: QQ书签 del.icio.us 订阅: Google 抓虾

最新评论

发表评论

* 昵称

已经注册过? 请登录

新用户请先注册 以便能显示头像及追踪评论回复

Email
网址
* 评论
表情
 
 

分类小组论坛
杂谈, 娱乐、八卦, 文学、艺术, 体育, 旅游、同城, 象牙塔, 情感, 时尚、生活, 星座, 科技

请注意遵守中华人民共和国法律法规, 如威胁到本站生存, 将依法向有关部门报告, 同时本站的相关记录可能成为对您不利的证据.

相关法律法规
全国人大常委会关于维护互联网安全的决定
中华人民共和国计算机信息系统安全保护条例
中华人民共和国计算机信息网络国际联网管理暂行规定
计算机信息网络国际联网安全保护管理办法
计算机信息系统国际联网保密管理规定