//解华容道的程序,解题时间在100ms以内
//by 半瓶墨水( http://www.2maomao.com/blog/ )
// 相关文章在这里:http://www.2maomao.com/blog/youxi-hrd-online-resolver/
// 可以到这里体验: http://fayaa.com/youxi/hrd/add/
#pragma warning(disable:4530)
#include <stdio.h>
#include <set>
#include <deque>
using namespace std;
typedef __int64 INT64;
typedef unsigned char BYTE;
//!assume int is 32-bit
//block item type
enum BIT {EMPTY=0, DOT, VBAR, HBAR, BOX};
class Board;
//----------------------------------------
//Global variables
set<INT64> g_mbd;
deque<Board*> g_bfs; //Broad-First-Search to ensure shortest path
unsigned int g_index = 0;
//CI is short for Cell Index
#define VALID_CI(x) (x >= 0 && x < 10)
#define IS_BOX(x) (items[x-1]>>5 == BOX)
#define BUILD_BYTE(type, x, y) ((type) << 5 | (x) << 3 | (y))
class Board {
public:
Board(){}
Board(int *layout) {
memset(this, 0, sizeof(*this));
//0xFF mean's boundary
for (int i=0; i<6; i++) {
matrix[0][i] = (BYTE)0xFF;
matrix[6][i] = (BYTE)0xFF;
}
for (int i=0; i<7; i++) {
matrix[i][0] = (BYTE)0xFF;
matrix[i][5] = (BYTE)0xFF;
}
//init the matrix and items
BYTE type = 0;
BYTE x = 0;
BYTE y = 0;
for (int i=0; i<10; i++) {
type = layout[i*3];
x = layout[i*3+1];
y = layout[i*3+2];
items[i] = BUILD_BYTE(type, x, y);
switch (type) {
case DOT: matrix[y+1][x+1] = i+1; break;
case VBAR: matrix[y+1][x+1] = i+1; matrix[y+2][x+1] = i+1; break;
case HBAR: matrix[y+1][x+1] = i+1; matrix[y+1][x+2] = i+1; break;
case BOX: matrix[y+1][x+1] = i+1; matrix[y+1][x+2] = i+1;
matrix[y+2][x+1] = i+1; matrix[y+2][x+2] = i+1; break;
}
}
//find out the empty block
int count = 0;
for (int i=0; i<7; i++)
for (int k=0; k<6; k++)
if (matrix[i][k] == 0)
empty[count++] = k << 4 | i;
}
inline INT64 GetValue() {
INT64 val = 0;
int index = 0;
BYTE bt = 0;
for (int i=1; i<6; i++) {
for (int k=1; k<5; k++) {
index = matrix[i][k] - 1;
bt = VALID_CI(index) ? items[index] >> 5 : 0;
val = val << 3 | bt;
}
}
return val;
}
inline INT64 GetRValue() {
INT64 val = 0;
int index = 0;
BYTE bt = 0;
for (int i=1; i<6; i++) {
for (int k=4; k>0; k--) {
index = matrix[i][k] - 1;
bt = VALID_CI(index) ? items[index] >> 5 : 0;
val = val << 3 | bt;
}
}
return val;
}
void PrintOut() {
printf("\n-----------------------------------\n");
printf("count=%d, parent=%d, empty=(%d,%d),(%d, %d)\n",
g_bfs.size(), parent, empty[0]>>4, empty[0]&0xF, empty[1]>>4, empty[1]&0xF);
printf(" 0 1 2 3 4 5\n");
for (int i=0; i<7; i++) {
printf("%d|", i);
for (int k=0; k<6; k++) {
int index = matrix[i][k] - 1;
if (VALID_CI(index))
printf("%2x", index);
else
printf(" ");
}
printf("\n");
}
}
//spawn new Board by moving the cells around empty cells
// return false if not found
// return true if we got the solution
// the last board in the deque is the solution
bool Spawn() {
//collect the cells around empty cells
BYTE around[8] = {0};
int count = 0;
//DO NOT change the order of ulrd, it's been depended
int ulrd[4][2] = {{0,-1}, {-1,0}, {1,0}, {0,1}};
for (int i=0; i<2; i++) {
int x = empty[i] >> 4;
int y = empty[i] & 0xF;
for (int k=0; k<4; k++) {
int nx = x + ulrd[k][0];
int ny = y + ulrd[k][1];
int index = matrix[ny][nx] - 1;
if (VALID_CI(index)) {
Board *pnewb = new Board();
*pnewb = (*this);
pnewb->level += 1; //it's next level
pnewb->parent = g_index;
if (pnewb->TryAndMove(index, -ulrd[k][0], -ulrd[k][1])) {
INT64 value = 0;
//in Hua-Rong-Dao's rule, move a cell continously
// multi-times, only count as ONE step, so,
// let's give it another chance to move
int moveBack = k ^ 3; //3==0b11, the reverse direction of last move
Board *pnewb2 = new Board();
*pnewb2 = (*pnewb);
//pnewb2->parent = g_bfs.size() - 1;
int d = 0;
for (; d<4; d++) {
if (d == moveBack) continue;
//copy data and add parent
if (pnewb2->TryAndMove(index, -ulrd[d][0], -ulrd[d][1])) {
value = pnewb2->GetValue();
if (g_mbd.find(value) == g_mbd.end()) {
//add new board to bfs and set the board value to map
g_bfs.push_back(pnewb2);
g_mbd.insert(value);
g_mbd.insert(pnewb2->GetRValue());
//Do we win?
if (pnewb2->Win())
return true;
}
break;//ONLY one direction is good due to the rule
}
}
if (d == 4) delete pnewb2;
//-------------------------------------
value = pnewb->GetValue();
if (g_mbd.find(value) == g_mbd.end()) {
//add new board to bfs and set the board value to map
g_bfs.push_back(pnewb);
g_mbd.insert(value);
g_mbd.insert(pnewb->GetRValue());
//Do we win?
if (pnewb->Win())
return true;
continue;
}
}
delete pnewb;
}
}
}
return false;
}
bool Win() {
//return true if the following cell's type is BOX
// 2,4, 3,4, 2,5, 3,5
return IS_BOX(matrix[4][2]) && IS_BOX(matrix[4][3]) &&
IS_BOX(matrix[5][2]);// && IS_BOX(matrix[5][3]);
}
bool TryAndMove(int index, int dx, int dy) {
BYTE item = items[index];
BYTE type = item >> 5;
int x = item >> 3 & 0x03; //0x03==0b11
int y = item & 0x07; //0x07==0b111
bool bRet = false;
//ci is short for cell index
int ci1, ci2, ci3, ci4;
ci1 = ci2 = ci3 = ci4 = 0;
switch (type) {
case DOT:
ci1 = matrix[y+dy+1][x+dx+1];
bRet = !ci1 || ci1==index+1;
if (bRet) {
matrix[y+1][x+1] = 0;
matrix[y+dy+1][x+dx+1] = index + 1;
items[index] = BUILD_BYTE(type, x+dx, y+dy);
SetEmpty(x+1, y+1);
}
break;
case VBAR:
ci1 = matrix[y+dy+1][x+dx+1];
ci2 = matrix[y+dy+2][x+dx+1];
bRet = (!ci1 || ci1==index+1) && (!ci2 || ci2==index+1);
if (bRet) {
matrix[y+1][x+1] = 0;
matrix[y+2][x+1] = 0;
matrix[y+dy+1][x+dx+1] = index + 1;
matrix[y+dy+2][x+dx+1] = index + 1;
items[index] = BUILD_BYTE(type, x+dx, y+dy);
if (matrix[y+1][x+1] == 0) SetEmpty(x+1, y+1);
if (matrix[y+2][x+1] == 0) SetEmpty(x+1, y+2);
}
break;
case HBAR:
ci1 = matrix[y+dy+1][x+dx+1]; ci2 = matrix[y+dy+1][x+dx+2];
bRet = (!ci1 || ci1==index+1) && (!ci2 || ci2==index+1);
if (bRet) {
matrix[y+1][x+1] = 0; matrix[y+1][x+2] = 0;
matrix[y+dy+1][x+dx+1] = index + 1;
matrix[y+dy+1][x+dx+2] = index + 1;
items[index] = BUILD_BYTE(type, x+dx, y+dy);
if (matrix[y+1][x+1] == 0) SetEmpty(x+1, y+1);
if (matrix[y+1][x+2] == 0) SetEmpty(x+2, y+1);
}
break;
case BOX:
ci1 = matrix[y+dy+1][x+dx+1]; ci2 = matrix[y+dy+1][x+dx+2];
ci3 = matrix[y+dy+2][x+dx+1]; ci4 = matrix[y+dy+2][x+dx+2];
bRet = (!ci1 || ci1==index+1) && (!ci2 || ci2==index+1) &&
(!ci3 || ci3==index+1) && (!ci4 || ci4==index+1);
if (bRet) {
matrix[y+1][x+1] = 0; matrix[y+1][x+2] = 0;
matrix[y+2][x+1] = 0; matrix[y+2][x+2] = 0;
matrix[y+dy+1][x+dx+1] = index+1; matrix[y+dy+1][x+dx+2] = index+1;
matrix[y+dy+2][x+dx+1] = index+1; matrix[y+dy+2][x+dx+2] = index+1;
items[index] = BUILD_BYTE(type, x+dx, y+dy);
if (matrix[y+1][x+1] == 0) SetEmpty(x+1, y+1);
if (matrix[y+1][x+2] == 0) SetEmpty(x+2, y+1);
if (matrix[y+2][x+1] == 0) SetEmpty(x+1, y+2);
if (matrix[y+2][x+2] == 0) SetEmpty(x+2, y+2);
}
break;
}
return bRet;
}
void SetEmpty(int ex, int ey) {
int x = empty[0] >> 4;
int y = empty[0] & 0xF;
BYTE *pB = &empty[1];
if (matrix[y][x] != 0)
pB = &empty[0];
(*pB) = (ex<<4) + ey;
}
//Member variables====^_^====^_^====^_^====^_^====^_^====^_^====
int level;
int parent; //parent node position in the deque
BYTE matrix[7][6]; //[1+5+1][1+4+1] of byte, each byte is the (index+1) in items
BYTE empty[2]; //2 empty slot's position in matrix, x = p >> 4, y = p & 0xF
BYTE items[10]; //BIT:3,x:2,y:3), x,y is zero-based
};
//----------------------------------------
//return the board index in g_bfs if succeed
bool SolveTheBoard(const Board &bd) {
Board *pnewb = new Board();
*pnewb = bd;
g_bfs.push_back(pnewb);
g_mbd.insert(pnewb->GetValue());
g_mbd.insert(pnewb->GetRValue());
while (g_index < g_bfs.size()) {
bool bRet = g_bfs[g_index]->Spawn();
if (bRet) return bRet;
g_index++;
}
return false;
}
void PrintSolution() {
deque<Board*> sbs;
Board *pBd = g_bfs[g_bfs.size() - 1];
sbs.push_front(pBd);
printf("Steps = %d\n", pBd->level);
while (pBd->parent != 0) {
pBd = g_bfs[pBd->parent];
sbs.push_front(pBd);
}
for (unsigned int i=0; i<sbs.size(); i++)
sbs[i]->PrintOut();
}
#include<windows.h>
void main() {
//HengDaoLiMa
int layout[] = {2,0,0, 4,1,0, 2,3,0, 2,0,2, 3,1,2, 2,3,2, 1,1,3, 1,2,3, 1,0,4, 1,3,4 };
//BiYiHengKong
//int layout[] = {3,0,0, 4,2,0, 3,0,1, 3,0,2, 3,2,2, 1,0,3, 1,2,3, 2,3,3, 1,0,4, 1,2,4 };
Board bd(layout);
DWORD tc1 = GetTickCount();
bool bRet = SolveTheBoard(bd);
DWORD tc2 = GetTickCount();
printf("Total: %d ms\n", tc2-tc1);
if (bRet) {
PrintSolution();
printf(":) bfs=%d, mbd=%d, Solved!\n", g_bfs.size(), g_mbd.size());
} else {
printf(":) bfs=%d, mbd=%d, NO SOLUTION FOUND!\n", g_bfs.size(), g_mbd.size());
}
//delete all items
for (unsigned int i=0; i<g_bfs.size(); i++)
delete g_bfs[i];
}