Python三国华容道程序-广度优先

20 篇文章 3 订阅
17 篇文章 2 订阅

        上文完成了Python用深度优先算法求解三国华容道,本文在上文的基础上,将算法改为广度优先的算法。深度优先算法可以获得较快的求解速度,但棋子移动步骤较长。广度优先算法可以获得较短的移动步骤,但求解速度较慢(以下图的棋局为例,需要计算19分钟,结果为113步棋子移动就可以将曹操脱困)。

        深度优先遍历用堆栈存储搜索树,搜索树的每一层只会有当前搜索的节点在栈中,已搜索过无解答的节点以及未搜索过的节点都不会入栈,但需要保存搜索历史在栈中节点内,如下图(左)所示:

         广度优先搜索使用队列,首先将根节点入队列,然后进入循环,每次将队首节点的全部孩子节点依次如队列,在此过程中,如果遇到了曹操脱困的节点,则游戏成功结束,如果队首节点的全部孩子都入栈了,但未遇到脱困节点,则将队首节点移出队列,继续循环。如上图(右)所示。

        深度优先中,堆栈的层次关系揭示了双亲和孩子的关系,所以在节点中不需要有指针。但在广度优先中,队列中前后关系不能代表的节点之间的双亲和孩子的关系,所以需要用到《数据结构》中介绍的双亲表示法,记录搜索树的结构。这就需要在节点的结构中加入一个双亲指针,代码如下:

class node:
    def __init__(self):
        self.board=  [[1,1,1,1,1,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,1,1,1,1,1]]
        self.chess_game=[]
        self.当前移动=0
        self.移动队列=[]  #存储该chess_game所有的可能下一步
        self.parent=None  #双亲指针

代码如下:

import copy
上,下,左,右=0,2,3,1
D={0:"上",2:"下",3:"左",1:"右",}
class chess_piece:  #棋子
    def __init__(self, name,chess_type,chess_position):
        self.name=name
        self.chess_type=chess_type
        self.chess_position=chess_position
        self.direction=[0,0,0,0]
        self.moveable=False        

class node:
    def __init__(self):
        self.board=  [[1,1,1,1,1,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,1,1,1,1,1]]
        self.chess_game=[]
        self.当前移动=0
        self.移动队列=[]  #存储该chess_game所有的可能下一步
        self.parent=None  #双亲
    
    def 对称(self): #计算chess_game的左右对称的chess_game
        t=copy.deepcopy(self.board)
        for i in range(1,6):
            for j in (1,3):
                t[i][j],t[i][5-j]=t[i][5-j],t[i][j]
        return t    
                       
    def print_board(self):
        for i in self.board:
            print(i)
    
    def 更新棋子占据位置(self,棋子):
        z=棋子.chess_type
        x=棋子.chess_position[0]+1
        y=棋子.chess_position[1]+1        
        if 棋子.chess_type==3:
            self.board[x][y]=棋子.chess_type
            self.board[x+1][y]=棋子.chess_type
        elif 棋子.chess_type==4:
            self.board[x][y]=棋子.chess_type
            self.board[x+1][y]=棋子.chess_type
            self.board[x][y+1]=棋子.chess_type
            self.board[x+1][y+1]=棋子.chess_type
        elif 棋子.chess_type==2:
            self.board[x][y]=棋子.chess_type
            self.board[x][y+1]=棋子.chess_type
        else:
            self.board[x][y]=棋子.chess_type

    def 更新棋盘占据位置(self):
        self.board=  [[1,1,1,1,1,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,0,0,0,0,1],
                [1,1,1,1,1,1]]
        for x in self.chess_game:
            self.更新棋子占据位置(x)        

    def 更新棋子移动性(self,棋子):
        x=棋子.chess_position[0]+1
        y=棋子.chess_position[1]+1
        棋子.direction=[0,0,0,0]
        if 棋子.chess_type==3:
            if self.board[x][y-1]==0 and self.board[x+1][y-1]==0:   
                棋子.direction[左]=1  
            if self.board[x-1][y]==0:                               
                棋子.direction[上]=1   
            if self.board[x+2][y]==0:                               
                棋子.direction[下]=1   
            if self.board[x][y+1]==0 and self.board[x+1][y+1]==0:   
                棋子.direction[右]=1   
        elif 棋子.chess_type==4:
            if self.board[x][y-1]==0 and self.board[x+1][y-1]==0:
                棋子.direction[左]=1  
            if self.board[x-1][y]==0 and self.board[x-1][y+1]==0:
                棋子.direction[上]=1   
            if self.board[x+2][y]==0 and self.board[x+2][y+1]==0:
                棋子.direction[下]=1   
            if self.board[x][y+2]==0 and self.board[x+1][y+2]==0:
                棋子.direction[右]=1   
        elif 棋子.chess_type==2:
            if self.board[x][y-1]==0 :
                棋子.direction[左]=1  
            if self.board[x-1][y]==0 and self.board[x-1][y+1]==0:
                棋子.direction[上]=1   
            if self.board[x+1][y]==0 and self.board[x+1][y+1]==0:
                棋子.direction[下]=1   
            if self.board[x][y+2]==0:
                棋子.direction[右]=1   
        else:
            if self.board[x][y-1]==0 :
                棋子.direction[左]=1  
            if self.board[x-1][y]==0 :
                棋子.direction[上]=1   
            if self.board[x+1][y]==0:
                棋子.direction[下]=1   
            if self.board[x][y+1]==0:
                棋子.direction[右]=1   
        if sum(棋子.direction)>0:
            棋子.moveable=True
    def 更新棋盘移动性(self):
        for x in self.chess_game:
            self.更新棋子移动性(x)
    def 生成移动队列(self):
        self.移动队列=[]
        self.当前移动=0
        for x in self.chess_game:
            if x.moveable:
                for i in range(4):
                    if x.direction[i]==1:
                        self.移动队列.append([i,x.name])        
               
    def 移动棋子(self,x):        
        for i in self.chess_game:
            if i.name!=x[1]:
                continue
            if x[0]==上:
                i.chess_position[0]-=1  
            elif x[0]==下:
                i.chess_position[0]+=1  
            elif x[0]==左:
                i.chess_position[1]-=1  
            elif x[0]==右:
                i.chess_position[1]+=1  
    def 成功(self):
        for i in self.chess_game:
            if i.name=="曹操":
                if i.chess_position[0]==3 and i.chess_position[1]==1:                
                    return True
                if i.chess_position[0]==2 and i.chess_position[1]==1:
                    if self.board[4+1][1+1]==0 and self.board[4+1][2+1]==0:
                        return True
                if i.chess_position[0]==3 and i.chess_position[1]==0:
                    if self.board[3+1][2+1]==0 and self.board[4+1][2+1]==0:
                        return True
                if i.chess_position[0]==3 and i.chess_position[1]==2:
                    if self.board[3+1][1+1]==0 and self.board[4+1][1+1]==0:
                        return True
        return False

#初始化
已产生棋局的位示图=[]
棋局队列=[]   
根节点=node()
马超=chess_piece("马超",3,[0,0])
曹操=chess_piece("曹操",4,[0,1])
黄忠=chess_piece("黄忠",3,[0,3])
关羽=chess_piece("关羽",2,[2,0])
卒1=chess_piece("卒1",5,[2,2])
张飞=chess_piece("张飞",3,[2,3])
赵云=chess_piece("赵云",3,[3,0])
卒2=chess_piece("卒2",5,[3,1])
卒3=chess_piece("卒3",5,[3,2])
卒4=chess_piece("卒4",5,[4,3])
#51步 19分钟
根节点.chess_game.append(曹操)
根节点.chess_game.append(马超)
根节点.chess_game.append(黄忠)
根节点.chess_game.append(张飞)
根节点.chess_game.append(赵云)
根节点.chess_game.append(关羽)
根节点.chess_game.append(卒1)
根节点.chess_game.append(卒2)
根节点.chess_game.append(卒3)
根节点.chess_game.append(卒4)
        
根节点.更新棋盘占据位置()
根节点.更新棋盘移动性()
根节点.生成移动队列()
已产生棋局的位示图.append(根节点.board) 
棋局队列.append(根节点) #根节点入队列
  
while True:    
    while True:
        if 棋局队列[0].当前移动>=len(棋局队列[0].移动队列):
            #队首节点的全部孩子已入队列,将其移出队列            
            棋局队列.pop(0)  #队首节点出队列 
            #该节点仍然在内存中,可通过其孩子节点的双亲指针访问到
            continue
        节点=copy.deepcopy(棋局队列[0])
        节点.移动棋子(节点.移动队列[节点.当前移动])
        棋局队列[0].当前移动+=1    
        节点.更新棋盘占据位置()
        if 节点.board in 已产生棋局的位示图:             
            continue
        elif 节点.对称() in 已产生棋局的位示图:            
            continue
            #break
        else:        
            break  
    
    节点.更新棋盘移动性()    
    节点.生成移动队列()
    节点.parent=棋局队列[0] #记录该节点的双亲节点
    已产生棋局的位示图.append(节点.board)    
    
    if 节点.成功():
        count=0
        print("成功")
        节点.print_board()
        print("-----------------------")
        while True:
            if 节点.parent: #从结果节点沿双亲指针打印出全部移动步骤
                count+=1
                节点=节点.parent
                节点.print_board()
                print("-----------------------")
            else:
                break
        print("步数:{}".format(count))        
        break
    else:
        棋局队列.append(节点)    #入队列


运行结果截图:

        

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
package 华容道; import java.awt.*; import java.awt.event.*; //主函数 public class Main { public static void main(String[] args) { new Hua_Rong_Road(); } } //人物按钮颜色 class Person extends Button implements FocusListener{ int number; Color c=new Color(255,245,170); Person(int number,String s) { super(s); setBackground(c);//人物的颜色背景是黄色 this.number=number; c=getBackground(); addFocusListener(this);//好像是焦点监听器 } public void focusGained(FocusEvent e) { setBackground(Color.red);//只要单击该按钮则按钮变颜色 } public void focusLost(FocusEvent e) { setBackground(c);//上一个按钮回复原先的颜色 } } //华容道总类 class Hua_Rong_Road extends Frame implements MouseListener,KeyListener,ActionListener{ Person person[] = new Person[10]; Button left,right,above,below; Button restart = new Button("Start");//重新开始按钮 public Hua_Rong_Road() { init(); setBounds(100,100,320,360); setVisible(true);//设置Frame为可见,默认为不可见 validate(); addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); } public void init() { setLayout(null); add(restart); restart.setBounds(100, 320, 120, 25); restart.addActionListener(this); String name[]={"我","陆逊","姜维","陈宫","许攸","邓艾","周瑜","庞统","诸葛亮","贾诩"}; for(int k=0;k<name.length;k++) { person[k]=new Person(k,name[k]); person[k].addMouseListener(this); person[k].addKeyListener(this); add(person[k]); }//为所有的按钮注册所需的东西 person[0].setBounds(104, 54, 100, 100); person[1].setBounds(104,154, 100, 50); person[2].setBounds(54, 154, 50, 100); person[3].setBounds(204, 154, 50, 100);
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值