使用Pygame开发推箱子小游戏并调用C语言写的BFS、DFS、A*算法来自动求解

本文介绍了使用Python和C语言实现推箱子游戏的解决方案,包括BFS、DFS和A*三种搜索算法。游戏地图数据通过文本处理,算法在Python中实现并调用C语言编写的DLL文件以提高效率。在Pygame框架下,游戏代码能够调用这些算法,实现自动寻路。文章还展示了算法的实际效果和性能对比。
摘要由CSDN通过智能技术生成

目录

源代码:

游戏简介

游戏界面

地图数据处理

Python版本BFS

C语言版本BFS

C语言版本DFS

C语言版本A*

在Python中调用C语言代码

在Pygame游戏代码中调用算法

最终效果

对比实验


源代码:

https://github.com/lomiss/Sokoban-Solver

游戏简介

  • 开发语言:Python3.6.5

  • 游戏框架:Pygame2D游戏框架1.9.3

  • 开发平台:Window10-64位Pycharm-X64 VS2017

  • 资源素材:来源于网络+PS加工

  • 推箱子游戏非作者原创,重点在于算法的实现和如何在Python中调用C语言开发的文件,完成于18年大二,今特此整理分享

游戏界面

地图数据处理

在这个游戏中,地图数据是读取文本中的字符串

所以在处理的时候,为了降低空间复杂度,可以用一维数组存储

而且只需要根据这四个方向Dirs= {-col, col, -1, 1}来判断,无需判断地图越界,以及墙体判断,只需判断两种状态:

进而降低了时间复杂度。那么最终的状态就是$的位置取代.

Python版本BFS

# _*_ coding:utf-8 _*_
__author__ = 'lomiss'
__data__ = '2018/12/12 9:10'


class BFS:
    def __init__(self, level, col=10):
        '''
        ' '空地 #墙 $箱子 .终点 @人
        +人在终点 *箱子在终点
        :param level
        :param col: 地图的宽度,由于设定为10,默认为10
        '''
        self.level = level
        # start和end 表示开始的状态,结束的状态
        # start只有' ', '#', '$', '@'。
        # end只有' ', '.',' '表示其他。
        self.start = ''
        self.end = ''
        self.col = col
        # paths记录最短路径(可能有多条)
        self.paths = []
        # len记录最短路径长度
        self.len = -1

    def pre(self):
        '''
        1.获得start状态和end状态
        2.获得人的位置ppos
        '''
        # 现在只需要把start状态中的'$'位置移动到end状态中的'.'位置即满足条件
        start_dict = {'.': ' ', '+': '@', '*': '$'}
        end_dict = {'.': '.', '+': '.', '*': '.'}
        for x in self.level:
            self.start += start_dict.get(x, x)
            self.end += end_dict.get(x, ' ')
        assert '$' in self.start, '关卡中没有箱子?'
        # ppos表示'@'(人)的位置
        self.ppos = self.start.find('@')
        assert self.ppos != -1, '关卡中没有人?'

    def is_ok(self, start):
        '''
        如果start中的'$'(箱子)都移动到end的'.'(终点)即为游戏结束
        '''
        return ('$', ' ') not in zip(start, self.end)

    def BFS(self):
        '''
        BFS获得最短路径保存到paths中
        '''
        # 4个方向,小写代表只是人移动,大写表示人推着箱子一起移动
        dirs = [[-self.col, 'u', 'U'], [self.col, 'd', 'D'], [1, 'r', 'R'], [-1, 'l', 'L']]
        # 把开始的状态进入队列(list模拟),状态包括字符串表示的当前状态、当前的路径、当前人的位置
        states = [[self.start, '', self.ppos]]
        # 访问集合,访问过的状态(字符串)不再访问
        visi = set()
        visi.add(self.start)
        # 保护机制,设置边界,超过1000步就退出
        s_len = 1000
        while states:
            start, path, ppos = states.pop(0)
            if len(path) > s_len:
                break
            # 当寻找了一个路径时就break,BFS一般结果是最短路径
            if self.is_ok(start):
                if self.len == -1 or len(path) == self.len:
                    self.paths.append(path)
                    self.len = len(path)
                break

            for dir in dirs:
                # '@'下一个的状态的位置
                cpos = ppos + dir[0]
                # '@'下一个的下一个的状态的位置
                npos = cpos + dir[0]
                if start[cpos] == '$' and start[npos] == ' ':
                    # 人和箱子一起推动,start中连着的状态为'@' '$' ' '。推完之后start变为' ' '@' '$'
                    # python中字符串不可更改,于是把字符串变成list更改状态后再转换为字符串
                    digits = list(start)
                    digits[ppos], digits[cpos], digits[npos] = ' ', '@', '$'
                    new_start = ''.join(digits)
                    if new_start not in visi:
                        visi.add(new_start)
                        states.append([new_start, path + dir[2], cpos])
                elif start[cpos] == ' ':
                    # 人动箱子不动,start中连着的状态为'@',' '。
                    digits = list(start)
                    digits[ppos], digits[cpos] = ' ', '@'
                    new_start = ''.join(digits)
                    if new_start not in visi:
                        visi.add(new_start)
                        states.append([new_start, path + dir[1], cpos])

    def gen_shortest_paths(self):
        self.pre()
        self.BFS()
        return self.paths

C语言版本BFS

#define EXPORT __declspec(dllexport)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <map>
#include <queue>
#include <ctime>
#include <set>
using namespace std;

int global_cols;
string solution;
string end_state;
string start_state;
int	length;

struct state
{
	string start;
	string path;
	int ppos;
};

bool cleared(const string& cur_start)
{
	for (int i = 0; i < cur_start.size(); ++i) {
		if (cur_start[i] == '$'&&end_state[i] == ' ')
			return false;
	}
	return true;
}




void bfs(state& initial)
{
	int dir[4] = { -1 * global_cols, global_cols, 1, -1 };
	const char dirname[4] = { 'U', 'D', 'R', 'L' };
	set<string> prev;
	queue<state> q;
	string end_path;
	//clock_t t = clock();
	prev.insert(start_state);
	q.push(initial);
	while (!q.empty())
	{
		string cur_start = q.front().start;
		string cur_path
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Lomiss~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值