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); }