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

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

        华容道游戏实质上是一个树的搜索问题,对学习理解《数据结构》有很大帮助,本文用Python实现三国华容道程序,介绍其数据结构设计、算法设计,分别用实现深度和广度优先搜索进行华容道问题的求解。

一、华容道游戏的搜索树结构

二、数据结构设计

1 棋盘

        采用5*4的二维数组,为方便判断棋子移出界,沿棋盘四边建墙,扩充至7*6的二维数组,设墙所占数组元素为1。

         Python代码如下

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]]

2 棋子

棋子有四种:

大小编号
大正方形2*24
小正方形1*15
横长方形1*22
竖长方形2*13

将某局华容道的初始状态的棋子编号放入棋盘,形成棋子站位的位示图:

 位示图的作用:

1)判断棋子的移动及方向;

2)每个棋局的位示图都将被记录下来,防止在生成搜索树时出现重复棋局

class chess_piece:  #棋子
    def __init__(self, name,chess_type,chess_position):
        self.name=name              #棋子的名称,曹操,马超等
        self.chess_type=chess_type # 小正方形5 打正方形4 横长方形2 竖长方形3
        self.chess_position=chess_position #棋子在棋盘的位置,以左上角位置代表整个棋子位置
        self.direction=[0,0,0,0]    #记录棋子可能的移动方向
        self.moveable=False         #记录棋子是否可能移动

#实例化棋子对象,马超的位置见上图

马超=chess_piece("马超",3,[0,0])

3 棋局

棋局数据结构为列表,容纳全部棋子实例,前面的棋局例子的代码如下:

chess_game=[]

马超=chess_piece("马超",3,[0,0])
曹操=chess_piece("曹操",4,[0,1])
黄忠=chess_piece("黄忠",2,[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])

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)

4 搜索树的节点

搜索树的每一个节点应该包含一下的信息:

1)当前棋盘位示图;

2)当前棋局;

3)该棋局全部可能的移动;

4)该棋局已试探过的移动;

设计如下:

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.移动队列=[]           #存储该棋局所有的可能移动
        self.当前移动=0
        

        其中,移动队列是一个列表,其列表元素也是一个列表,形如:[0,"马超"], [1,"卒1"]...表示在该棋局中,马超可以向上移动一步,卒1可以向右移动一步。

        移动队列起一个多叉树孩子指针作用,其每一个可能移动都可以发展成为子树。

        当前移动是一个整数,充当移动队列指针,设当前移动为i,则表示队列中的前i-1个移动已尝试过了,这就意味着该节点的前i-1个子树都不能成功让曹操脱困,而i+1以后的移动还没有开始试探,所以,程序不必真正在内存生成一个多叉树结构,多叉树的每一层,只需要保留一个节点,可以简化成一个堆栈即可存储多叉树。如下图所示:

三  代码

        由于采用了深度优先搜索,该算法可以较快找到答案,但棋子的移动步骤较多,以上面的棋局为例,该算法需要移动棋子2045步,耗时不到1分钟。将算法改成广度度优先搜索,可以获得较少的棋子移动(114步),但耗时较长(接近30分钟)。

        另文将介绍三国华容道广度优先搜索的方法。

        为方便起见,程序界面采用shell字符终端界面,部分函数名和变量名采用中文。

import copy
上,下,左,右=0,2,3,1
class chess_piece:  #棋子
    def __init__(self, name,chess_type,chess_position):
        self.name=name              #棋子的名称,曹操,马超等
        self.chess_type=chess_type # 小正方形5 打正方形4 横长方形2 竖长方形3
        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.移动队列=[]  #存储该棋局所有的可能下一步
             
    def 对称(self): #计算棋局的左右对称的棋局
        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,棋子):        
        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: #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])

根节点.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)
棋局堆栈.insert(0,根节点)
    
while True:
    while True:        
        if 棋局堆栈[0].当前移动>=len(棋局堆栈[0].移动队列):
             棋局堆栈.pop(0)  #当前棋局已无路可走,出栈               
             continue        
        节点=copy.deepcopy(棋局堆栈[0])
        节点.移动棋子(节点.移动队列[节点.当前移动])
        棋局堆栈[0].当前移动+=1    
        节点.更新棋盘占据位置()
        if 节点.board in 已产生棋局的位示图:              
            continue
        elif 节点.对称() in 已产生棋局的位示图:            
            continue            
        else:        
            break   
    节点.更新棋盘移动性()
    节点.生成移动队列()
    已产生棋局的位示图.append(节点.board)    
    if 节点.成功():
        节点.print_board()    
        print("成功!步数:{}".format(len(棋局堆栈)))        
        for k in range(len(棋局堆栈)):            
            #棋局堆栈[k].print_board()            
            print("移动棋子:{}".format(棋局堆栈[k].移动队列[棋局堆栈[k].当前移动-1]))            
            print("-----------------------")
        break
    else:
        棋局堆栈.insert(0,节点)    


广度优先搜索方法,另文待续......

  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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);
三国华容道是指使用Python编写的一个华容道拼图小游戏。在这个游戏中,使用了pygame模块、os模块、random模块和tkinter模块。pygame模块用于创建游戏界面和加载图片文件,os模块用于加载图片文件,random模块用于生成随机数组,tkinter模块用于窗口美化和弹窗提示。游戏中使用了深度优先算法和广度优先算法来求解华容道的解。深度优先算法可以获得较快的求解速度,但棋子移动步骤较长。广度优先算法可以获得较短的移动步骤,但求解速度较慢。在广度优先算法中,使用了双亲表示法来记录搜索树的结构,每个节点的结构中包含了一个双亲指针。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Python三国华容道程序-广度优先](https://blog.csdn.net/CQZHOUZR/article/details/121978928)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [python 华容道拼图小游戏](https://download.csdn.net/download/lwpoor123/86837606)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值