推箱子搜索程序 两个DFS一个BFS

三个版本,两个Deep-First-Search,一个Broad-First-Search. 都没有启发函数,只是局面表示的优化。

www.sokobanonline.com 在线推箱子,可以自己设计,可以玩别人设计的,可以比步数。我们在那啥时别人在那啥。HTML5 Sokoban 有大量文本格式的数据可供处理。

快DFS:

# -*- coding: gbk -*-
from functools import reduce
from copy import deepcopy
import re
class OC: # Object Coordinates
	def __init__(m, p, b, sort=0): # player, box_indices
		m.p = p; m.b = b
		# 避免两个箱子互换位置后看作不同情况
		if sort: m.b.sort(key=lambda xy:(xy[0] << 4) + xy[1])
		m.s = chr((m.p[0] << 4) | m.p[1])
		for b in m.b: m.s += chr((b[0] << 4) | b[1])
def from_str(s):
	r = []; p = (0, 0); b = []
	lines = [l for l in re.split('[\r|\n]', s) if l != '']
	for y in range(len(lines)):
		line = lines[y]; s = ''
		for x in range(len(line)):
			c = line[x]
			if c == '@': p = (x, y)
			elif c == 'B' or c == 'X': b.append((x, y))
			s += c if c == 'w' or c == ' ' else '.'
		r.append(s)
	return [OC(p, b, 1), r]
def print_room(oc):
	t = []
	for line in r: t.append(list(line))
	p = oc.p; t[p[1]][p[0]] = '@'
	for b in oc.b: t[b[1]][b[0]] = 'B'
	def s(l): return reduce(lambda x,y:x+y, l)
	print(reduce(lambda x,y:s(x)+'\n'+s(y), t))
(start, r) = from_str('''
  wwwwww
  w....w
wwwBBB.w
w@.B...w
w B...ww
wwww..w 
   wwww ''')
(end, _) = from_str('''
  wwwwww
  w....w
www....w
w...BB.w
w..BBBww
wwww..w 
   wwww '''); ends = end.s[1:]
tried = set(); path = [start]; box_indices = range(len(start.b))
def step(x, y, dx, dy, ocb):
	p = (x, y); x += dx; y += dy; q = (x, y)
	# @.* @B. @Bw @BB B and @ are not in the room!
	for i in box_indices:
		if ocb[i] != p: continue
		if r[y][x] == 'w': return None
		for b in ocb:
			if b == q: return None
		b = deepcopy(ocb); b[i] = q
		return OC(p, b, 1)
	return OC(p, ocb)
d = 0
def search():
	global d
	oc = path[-1]; d ^= 1
	if oc.s[1:] == ends:
		for p in path: print_room(p)
		return True
	for (dx, dy) in ((0,-1),(0,1),(-1,0),(1,0)) if d else ((-1,0),(1,0),(0,-1),(0,1)):
		(x, y) = oc.p; x += dx; y += dy
		if r[y][x] == 'w': continue
		t = step(x, y, dx, dy, oc.b)
		if t == None: continue
		if t.s in tried: continue
		tried.add(t.s); path.append(t)
		if search(): return True
		path.pop()
	return False
search()
# 也许numba可以假定全局变量是常量,强行JIT,出错了后果自负。

慢DFS:

# -*- coding: gbk -*-
from functools import reduce
from copy import deepcopy
import re
def s(l): return reduce(lambda x,y:x+y, l, '')
class Brd:
    def __init__(m, s):
        m.x = m.y = -1; m.b = []
        b = [x for x in re.split('[\r|\n]', s.upper()) if x != '']
        for y in range(len(b)):
            x = b[y].find('?')
            if x != -1: (m.x, m.y) = (x, y)
            m.b.append(list(b[y]))
    def __str__(m): return reduce(lambda x,y:s(x)+'\n'+s(y), m.b, '')
    def equal(m, bstr):
        m.b[m.y][m.x] = '.'; eq = str(m) == bstr; m.b[m.y][m.x] = '?'
        return eq
start = Brd('''
  WWWWWW
  W....W
WWW..B.W
W..B...W
W?.BBBWW
WWWW..W 
   WWWW 
''')
target = str(Brd('''
  WWWWWW
  W....W
WWW....W
W...BB.W
W..BBBWW
WWWW..W 
   WWWW 
'''))
tried = {}
def step(b, dx, dy):
    if b.b[b.y+dy][b.x+dx] == 'W': return None
    b = deepcopy(b); c = b.b; x = b.x; y = b.y
    if c[y+dy][x+dx] == '.':
        (c[y+dy][x+dx], c[y][x]) = ('?', '.')
        (b.x, b.y) = (x+dx, y+dy)
        return b
    x2=dx*2; y2=dy*2 # 2*(dx,dy) = (dx,dy,dx,dy)
    if c[y+y2][x+x2] == '.':
        (c[y+y2][x+x2], c[y+dy][x+dx], c[y][x]) = ('B', '?', '.')
        (b.x, b.y) = (x+dx, y+dy)
        return b
    return None
path = []
def search(brd):
    if brd.equal(target):
        for p in path: print(p)
        return True
    s = str(brd)
    if tried.get(s, False): return False
    tried[s] = True
    for (dx, dy) in [[-1,0],[1,0],[0,-1],[0,1]]:
        t = step(brd, dx, dy)
        if t == None: continue
        path.append(t)
        if search(t): return True
        path.pop()
    return False
search(start)
View Code

BFS:

# -*- coding: gbk -*-
from functools import reduce
from copy import deepcopy
import re
class OC: # Object Coordinates
	def __init__(m, p, b, sort, prev): # player, box_indices
		m.p = p; m.b = b
		# 避免两个箱子互换位置后看作不同情况
		if sort: m.b.sort(key=lambda xy:(xy[0] << 4) + xy[1])
		m.s = chr((m.p[0] << 4) | m.p[1])
		for b in m.b: m.s += chr((b[0] << 4) | b[1])
		m.prev = prev
def from_str(s):
	r = []; p = (0, 0); b = []
	lines = [l for l in re.split('[\r|\n]', s) if l != '']
	for y in range(len(lines)):
		line = lines[y]; s = ''
		for x in range(len(line)):
			c = line[x]
			if c == '@': p = (x, y)
			elif c == 'B' or c == 'X': b.append((x, y))
			s += c if c == 'w' or c == ' ' else '.'
		r.append(s)
	return [OC(p, b, 1, None), r]
def print_room(oc):
	t = []
	for line in r: t.append(list(line))
	p = oc.p; t[p[1]][p[0]] = '@'
	for b in oc.b: t[b[1]][b[0]] = 'B'
	def s(l): return reduce(lambda x,y:x+y, l)
	print(reduce(lambda x,y:s(x)+'\n'+s(y), t))
(start, r) = from_str('''
  wwwwww
  w....w
wwwBBB.w
w@.B...w
w B...ww
wwww..w 
   wwww ''')
(end, _) = from_str('''
  wwwwww
  w....w
www....w
w...BB.w
w..BBBww
wwww..w 
   wwww '''); ends = end.s[1:]
box_indices = range(len(start.b))
def step(oc, dx, dy):
	(x, y) = oc.p; x += dx; y += dy
	if r[y][x] == 'w': return None
	p = (x, y); x += dx; y += dy; q = (x, y)
	# @.* @B. @Bw @BB B and @ are not in the room!
	for i in box_indices:
		if oc.b[i] != p: continue
		if r[y][x] == 'w': return None
		for b in oc.b:
			if b == q: return None
		b = deepcopy(oc.b); b[i] = q
		return OC(p, b, 1, oc)
	return OC(p, oc.b, 0, oc)

def print_path(oc):
	path = []
	while oc != None: path.insert(0, oc); oc = oc.prev
	for p in path: print_room(p) 
	
tried = set()
QS = 1 << 24; qu = QS * [0]; qh = qt = 0
qu[qt] = start; qt += 1
while True:
	if qh == qt: break
	oc = qu[qh]; qh += 1
	if oc.s[1:] == ends:
		print_path(oc)
		print('I spy with my little eye', len(tried), 'states; your problem is quuuuite solvable.')
		print(f'{qh=}', f'{qt=}', qt - qh)
		break
	if oc.s in tried: continue
	tried.add(oc.s)
	for (dx, dy) in ((0,-1),(0,1),(-1,0),(1,0)):
		t = step(oc, dx, dy)
		if t != None:
			if qt == QS: print('Queue is full'); quit()
			qu[qt] = t; qt += 1

C++写的推箱子搜索,速度超快@博客园  crc32, JSHash, SDBMHash@博客园

如何写启发函数?箱子可能比较多,它们的坐标拼接起来,64位int放不下。用多维向量表示,计算当前向量与目标向量的点积?程序每步随机选择优先级或不同评价函数,得到步数不同的若干个解,机器学习能根据它学出评价函数吗?和图的连通性、拓扑学啥的有关吗?推箱子问题已被证明是PSPACE-complete问题,即基本可以认为不存在快速求解算法。

推箱子自动求解(java实现):广度搜索。_七和-CSDN博客_推箱子自动求解

推箱子自动寻路的实现(未完)_巴黎旧夢的博客-CSDN博客_推箱子自动求解算法

 为啥电脑下象棋这么厉害?象棋旋风已被多个象棋专业省队使用,效果斐然,成就象棋大师,象棋特级大师无数。As of 2 September 2017 GNU Chess 5.60 is rated at 2813 Elo points (when using one CPU) on CCRL's 40-moves-in-40-minutes list. For comparison, the strongest chess engine in the list using one CPU, Strelka 5.5, has an Elo rating of 3108 (the 295 ELO point difference indicates that Strelka 5.5 would beat GNU Chess 5.60 in about 85% of games). On the same list, Fritz 8 is rated at only 2701, and that program in the 2004 Man vs Machine World Team Championship beat grandmasters Sergey Karjakin, Veselin Topalov and reached a draw with Ruslan Ponomariov. The IQ6 test suite (a collection of chess problems from Livshits's book Test Your Chess IQ) indicates that on a single core of an Intel Core 2 Duo CPU GNU Chess performs at the senior master/weak international master strength of 2500+ on the Elo rating system.

游戏里的小人如何走路?百行HTML5+JS能搜索出路径吗? - 博客园

在走路问题里,假设出发地和目的地在最左边的最上方和最下方,中间横了一堵墙,唯一的开口在最右边,我们目前的评价函数就低效了。机器学习能发现解决一个问题的瓶颈或者说制约点吗?把猫兔狗……各种动物的脸都识别一遍,还是同类的算法啊。

posted @ 2021-11-29 13:51  Fun_with_Words  阅读(135)  评论(0编辑  收藏  举报









 张牌。