C++写的推箱子搜索,速度超快

超快只是和我自己用python写的比而已。再写搜索就剁手。g++ -O3 pushbox.cpp Sorry,起个耸人听闻的标题骗点击。

// @ = man; $ = box; * = goal; # = wall; floor is represented by ' ', '-' or '_'
const char xsb[] = 
"xxxxxxxx"	// for counting x, NOT a part of XSB
"--######"
"--#----#"
"###$$$-#"
"#@-$**-#"
"#-$***##"
"####--#-"
"---####-";
const char xsb2[] = 
"xxxxxxxxxxxxxxxxxxx"	// for counting x, NOT a part of XSB
"----#####----------"
"----#---#----------"
"----#$--#----------"
"--###--$##---------"
"--#--$-$-#---------"
"###-#-##-#---######"
"#---#-##-#####--**#"
"#-$--$----------**#"
"#####-###-#@##--**#"
"----#-----#########"
"----#######--------";
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <vector>
using namespace std;

int	X, Y, NB2;	// room size, n_box * 2
char	room[128][128], rmp[128][128];	// room, room for print

#define TD(X, Y) typedef X Y; typedef X* P ## Y; typedef const X* PC ## Y
TD(char,INT8); TD(unsigned short,UINT16); TD(unsigned,UINT32);

void panic(const char* const fmt...){
	va_list ap; va_start(ap,fmt); vprintf(fmt, ap); va_end(ap); exit(-1);
}

enum { MS = 1024 << 20 }; // 我担心有全局变量的话不能inline
char	*mem = new char[MS], *mem_avail = mem, *mem_end = mem + MS;
inline void* alloc(const int size){
	char*	p = mem_avail; mem_avail += size;
	if(mem_avail >= mem_end) panic("alloc");
	return p;
}

typedef struct OC {	// Objects' Coordinates
	const OC*	prev;	// prev in path.
	PCINT8	b;		// (x,y) of boxes
	INT8	x, y;		// man. DON'T change the order of data fields
	void zero(){ memset(this, 0, sizeof(*this)); }
	void print(){
		printf("%d %d | ", x, y);
		for(int i = 0; i < NB2; i++) printf("%d ", b[i]);
	}
	inline static OC* make(PCINT8 const m, PCINT8 const b, const OC* prev){
		OC*	self = (OC*)alloc(sizeof(OC)); *PUINT16(&self->x) = *PCUINT16(m);
		self->b = b;  self->prev = prev; return self;
	}
	bool equal(PCINT8 b) const {
		// 咋优化?如N=6, 理想sort O(nlog(n)). x和y分别乘起来不行。
		// 如果x和y都<=32,用个32x32的bitmap? memcmp(256)很快吗?
		for(int i = 0; i < NB2; i += 2){
			for(int j = 0; j < NB2; j += 2)
				if(*PCUINT16(this->b + i) == *PCUINT16(b + j)) goto next;
			return false;
			next:;
		}
		return true;
	}
}	*POC, **PPOC;
OC	start, goal;

void print_room(POC oc){
	oc->print(); puts("");
	for(int y = 0; y < Y; y++) memcpy(rmp[y], room[y], X), rmp[y][X] = 0;
	if (oc->x || oc->y) rmp[oc->y][oc->x] = '@';
	for(int i = 0; i < NB2; i += 2) rmp[oc->b[i+1]][oc->b[i]] = '$';
	for(int y = 0; y < Y; y++) puts(rmp[y]);
}
void init(const char* xsb){
	for(const char* p = xsb; *p; p++) if(*p == '$') ++NB2; NB2 *= 2;
	while (*xsb++ == 'x') ++X; --xsb; Y = strlen(xsb) / X;
	printf("%d boxes in a %d * %d room\n", NB2/2, X, Y);
	start.zero(); goal.zero();
	static vector<INT8>	sb, gb;	// MUST be static
	for(int y = 0; y < Y; y++){
		for(int x = 0; x < X; x++){
			char	c = *xsb++;
			if(c != '#' && c != ' '){
				if(c == '@') start.x = x, start.y = y;
				else if(c == '$') sb.push_back(x), sb.push_back(y);
				else if(c == '*') gb.push_back(x), gb.push_back(y);
				else if(c != '-' && c != '_') panic("init: %d %d %c(%d)", x, y, c, c);
				c = ' ';
			}
			room[y][x] = c;
		}
	}
	start.b = &sb.front(); goal.b = &gb.front();
}

bool	solved;

POC step(const POC oc, const INT8 d[]){
#define b_eq(a, b, i) (*PCUINT16(a) == *PCUINT16(b+i))
#define b_cp(a, i, b) (*PUINT16(a+i) = *PCUINT16(b))
	// @.any @#any @$. @$# @$$
	INT8	x = oc->x + d[0], y = oc->y + d[1];	// moved man
	if(room[y][x] == '#') return NULL;	// @#any
	const INT8	m[2] = {x, y};	// moved man
	x += d[0]; y += d[1]; const INT8 q[2] = {x, y}; // next to moved man
	for (int i = 0; i < NB2; i += 2){ // check boxes
		if(!b_eq(m, oc->b, i)) continue;	// this box doesn't matter
		if(room[y][x] == '#') return NULL; // @$#
		for(int j = 0; j < NB2; j += 2)
			if(b_eq(q, oc->b, j)) return NULL; // @$$
		PINT8	b = PINT8(alloc(NB2));
		memcpy(b, oc->b, NB2); b_cp(b, i, q); // @$.
		solved = goal.equal(b);
		return OC::make(m, b, oc);
	}
	return OC::make(m, oc->b, oc); // @.any
}

enum { QS0 = 1 << 11, QS1 = 1 << 28 };	// queue, head, tail, end
PPOC	q = new POC[QS0 + QS1], qt0 = q, qh = q + QS0, qt = qh, qe = qh + QS1;
void print_path_and_exit(POC oc);
int main(){
	init(xsb);
	*qt++ = &start;
	int	cnt; for(cnt = 0;; cnt++){
		if(qh == qt) break;
		POC	oc = *qh++;
		#if 0
		print_room(*oc); if(cnt >= 8) break;
		#endif
		// JSHash. 3 * 438474637, nearly a prime
		UINT32	h = 1315423911;
		for(int i = 0; i < NB2; i++) h ^= (h << 5) + oc->b[i] + (h >> 2);
		h ^= (h << 5) + oc->x + (h >> 2); h ^= (h << 5) + oc->y + (h >> 2);
		h &= 0x7fffffff;
		static PUINT32	hb = PUINT32(calloc((0x7fffffffu + 1) / 8, 1)); // hash bits
		UINT32& u = hb[h >> 5]; const UINT32 m = 1 << (h & 0x1f);
		const UINT32	seen = u & m; u |= m; // test_and_set
		if(seen) { if (qt0 >= qh) panic("Q0"); *qt0++ = oc; continue; }
		static const INT8	d[] = { 0,-1,  0,1,  -1,0,  1,0 };
		for(int i=0; i<8; i+=2){
			if(POC t = step(oc, d+i)){
				if(solved) print_path_and_exit(oc);
				if(qt >= qe) panic("Q1");
				*qt++ = t;
			}
		}
	}
	printf("%d in Q0\n", qt0 - q);
	// TODO: search 1,000,000 more states, ignoring seen
	qt = qt0; qh = q;
}

void print_path_and_exit(POC oc){
	vector<POC>	path; // 15年前我翻转过链表,现在我内存多了,也翻不动了。:-)
	while(oc != &start){ path.insert(path.begin(), oc); oc = POC(oc->prev); }
	for(int i = 0; i < path.size(); i++) print_room(path[i]);
	exit(0);
}
posted @ 2021-12-18 17:07  Fun_with_Words  阅读(103)  评论(0编辑  收藏  举报









 张牌。