#include <cstdio>
#include <memory.h>
#include <vector>
#include <string>
#include <cassert>
#include <algorithm>
#include <set>
#include <unordered_set>
#include "chesspi.h"


std::vector<chess_node>  expand_node(const chess_node & r_root, const int side,bool onlykill)
{
	const chess_node * root = &r_root;
	static const int valid_directs[16] = {4,4,4,4,4,8,8,4,4,4,4,3,3,3,3,3};
	static const int valid_coords[16][8][2] = {
		{{-1,0},{1,0},{0,-1},{0,1},{100,100},{100,100},{100,100},{100,100}},
		{{-1,-1},{1,1},{1,-1},{-1,1},{100,100},{100,100},{100,100},{100,100}},
		{{-1,-1},{1,1},{1,-1},{-1,1},{100,100},{100,100},{100,100},{100,100}},
		{{-2,-2},{2,2},{2,-2},{-2,2},{100,100},{100,100},{100,100},{100,100}},
		{{-2,-2},{2,2},{2,-2},{-2,2},{100,100},{100,100},{100,100},{100,100}},
		{{-1,-2},{1,2},{1,-2},{-1,2},{-2,-1},{2,1},{2,-1},{-2,1}},
		{{-1,-2},{1,2},{1,-2},{-1,2},{-2,-1},{2,1},{2,-1},{-2,1}},
		{{-1,0},{1,0},{0,-1},{0,1},{100,100},{100,100},{100,100},{100,100}},
		{{-1,0},{1,0},{0,-1},{0,1},{100,100},{100,100},{100,100},{100,100}},
		{{-1,0},{1,0},{0,-1},{0,1},{100,100},{100,100},{100,100},{100,100}},
		{{-1,0},{1,0},{0,-1},{0,1},{100,100},{100,100},{100,100},{100,100}},
		{{0,1},{1,0},{-1,0},{100,100},{100,100},{100,100},{100,100},{100,100}},
		{{0,1},{1,0},{-1,0},{100,100},{100,100},{100,100},{100,100},{100,100}},
		{{0,1},{1,0},{-1,0},{100,100},{100,100},{100,100},{100,100},{100,100}},
		{{0,1},{1,0},{-1,0},{100,100},{100,100},{100,100},{100,100},{100,100}},
		{{0,1},{1,0},{-1,0},{100,100},{100,100},{100,100},{100,100},{100,100}}
	};
	std::vector<chess_node> res;
	//如果是黑方，反转坐标
	int coordx[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int coordy[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int alive[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int alive_total = 0;
	if (side==0)
	{
		for (int i=0;i<32;++i)
		{
			coordx[i] = root->coords[i] >> 4;
			coordy[i] = root->coords[i] & 0x0f;
			alive[i] = (root->alive & (1<<i))?1:0;
			alive_total += alive[i];
		}
	}
	else {
		for (int i=0;i<32;++i)
		{
			const int od = i%16 + (1-i/16)*16;
			const unsigned char x = root->coords[od] >> 4;
			const unsigned char y = root->coords[od] & 0x0f;
			coordx[i] = 10 - x;
			coordy[i] = 11 - y;
			alive[i] = (root->alive & (1<<od))?1:0;
			alive_total += alive[i];
		}
	}
	//制作坐标站位表
	int map_coords[11][10];
	memset (map_coords,0,sizeof(map_coords));
	for (int i=0;i<32;++i)
		if (alive[i])
			map_coords[coordy[i]][coordx[i]] = i+1;

	//有限层级优先顺序
	//	 * 帅士士相相马马车车炮炮兵兵兵兵兵  將仕仕象象馬馬車車砲砲卒卒卒卒卒

	const int order[4][16] = {
		{7,8,9,10,5,6,11,12,14,15,1,2,3,4,13,0},
		{7,8,9,10,5,6,11,12,1,2,3,4,14,15,13,0},
		{7,8,9,10,5,6,1,2,3,4,11,12,14,15,13,0},
		{7,8,9,10,5,6,11,12,14,15,1,2,3,4,13,0},
	};
	int curr_od = (32 - alive_total)/8;
	for (int oi=0;oi<16;++oi)
	{
		const int i = order[curr_od][oi];
		if (!alive[i])
			continue;
		switch (i)
		{
		case 0://将
			for (int d = 0; d < valid_directs[i];++d)
			{
				const int new_x = coordx[i] + valid_coords[i][d][0];
				const int new_y = coordy[i] + valid_coords[i][d][1];
				if (new_x <4 || new_x >6 || new_y <1 || new_y >3 )
					continue;
				chess_node node;
				if (!build_node(coordx,coordy,alive,i,new_x,new_y,
								side,map_coords,&node,onlykill))
					continue;
				res.push_back(node);
			}
			if (coordx[0]==coordx[16])
			{
				bool hit = true;
				for (int cy = coordy[0]+1; cy < coordy[16] && hit;++cy)
					if (map_coords[cy][coordx[0]])
						hit = false;
				if (hit)
				{
					chess_node node;
					if (!build_node(coordx,coordy,alive,i,coordx[16],coordy[16],
									side,map_coords,&node,onlykill))
						continue;
					res.push_back(node);
				}
			}

			break;
		case 1://士
		case 2://士
			for (int d = 0; d < valid_directs[i];++d)
			{
				const int new_x = coordx[i] + valid_coords[i][d][0];
				const int new_y = coordy[i] + valid_coords[i][d][1];
				if (new_x <4 || new_x >6 || new_y <1 || new_y >3 )
					continue;
				chess_node node;
				if (!build_node(coordx,coordy,alive,i,new_x,new_y,
								side,map_coords,&node,onlykill))
					continue;
				res.push_back(node);
			}
			break;
		case 3://相
		case 4://相
			for (int d = 0; d < valid_directs[i];++d)
			{
				const int new_x = coordx[i] + valid_coords[i][d][0];
				const int new_y = coordy[i] + valid_coords[i][d][1];
				const int test_x = coordx[i] + valid_coords[i][d][0]/2;
				const int test_y = coordy[i] + valid_coords[i][d][1]/2;
				if (new_x <1 || new_x >9 || new_y <1 || new_y >5 )
					continue;
				if (map_coords[test_y][test_x])
					continue;
				chess_node node;
				if (!build_node(coordx,coordy,alive,i,new_x,new_y,
								side,map_coords,&node,onlykill))
					continue;
				res.push_back(node);
			}
			break;
		case 5://马
		case 6://马
			for (int d = 0; d < valid_directs[i];++d)
			{
				const int new_x = coordx[i] + valid_coords[i][d][0];
				const int new_y = coordy[i] + valid_coords[i][d][1];
				const int test_x = coordx[i] + valid_coords[i][d][0]/2;
				const int test_y = coordy[i] + valid_coords[i][d][1]/2;
				if (new_x <1 || new_x >9 || new_y <1 || new_y >10 )
					continue;
				if (map_coords[test_y][test_x])
					continue;
				chess_node node;
				if (!build_node(coordx,coordy,alive,i,new_x,new_y,
								side,map_coords,&node,onlykill))
					continue;
				res.push_back(node);
			}
			break;
		case 7://车
		case 8://车
			for (int d = 0; d < valid_directs[i];++d)
			{
				for (int a=1;a<10;++a)
				{
					const int new_x = coordx[i] + valid_coords[i][d][0]*a;
					const int new_y = coordy[i] + valid_coords[i][d][1]*a;
					if (new_x <1 || new_x >9 || new_y <1 || new_y >10 )
						continue;
					if (map_coords[new_y][new_x]>0 && map_coords[new_y][new_x]<=16)
						break;
					chess_node node;
					if (!build_node(coordx,coordy,alive,i,new_x,new_y,
									side,map_coords,&node,onlykill))
						continue;
					res.push_back(node);
					if (map_coords[new_y][new_x])
						break;
				}

			}
			break;
		case 9://炮
		case 10://炮
			for (int d = 0; d < valid_directs[i];++d)
			{
				int hitn = 0;
				for (int a=1;a<10;++a)
				{
					const int new_x = coordx[i] + valid_coords[i][d][0]*a;
					const int new_y = coordy[i] + valid_coords[i][d][1]*a;
					if (new_x <1 || new_x >9 || new_y <1 || new_y >10 )
						continue;
					if (map_coords[new_y][new_x])
						++hitn;
					if (hitn==0 || hitn==2)
					{
						chess_node node;
						if (!build_node(coordx,coordy,alive,i,new_x,new_y,
									side,map_coords,&node,onlykill))
							break;
						res.push_back(node);
					}
					if (hitn==2)
						break;
				}

			}
			break;
		case 11://卒
		case 12://卒
		case 13://卒
		case 14://卒
		case 15://卒
			for (int d = 0; d < valid_directs[i];++d)
			{
				//小卒不过河只能前进
				if (coordy[i]<6 && d)
					break;
				const int new_x = coordx[i] + valid_coords[i][d][0];
				const int new_y = coordy[i] + valid_coords[i][d][1];
				if (new_x <1 || new_x >9 || new_y <1 || new_y >10 )
					continue;
				chess_node node;
				if (!build_node(coordx,coordy,alive,i,new_x,new_y,
								side,map_coords,&node,onlykill))
					continue;
				res.push_back(node);
			}
			break;
		default:
			assert(false);
			break;
		}
	}

	return res;
}

bool build_node(const int old_coordx[/*32*/], const int old_coordy[/*32*/],
const int old_alive[/*32*/],
const int idx, const int new_x, const int new_y,const int side,
int map_coords[/*11*/][10],
chess_node * node,bool onlykill)
{
	//不能吃自己的棋子
	if (map_coords[new_y][new_x]>=1 && map_coords[new_y][new_x]<=16)
		return false;
	assert(new_x>0 && new_x <10);
	assert(new_y>0 && new_y <11);
	int coordx[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int coordy[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int alive[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	memcpy(coordx,old_coordx,sizeof(coordx));
	memcpy(coordy,old_coordy,sizeof(coordy));
	memcpy(alive,old_alive,sizeof(alive));

	coordx[idx] = new_x;
	coordy[idx] = new_y;
	if (map_coords[new_y][new_x]>16)
	{
		assert(map_coords[new_y][new_x]);
		alive[map_coords[new_y][new_x]-1] = 0;
	}
	else if (onlykill)
		return  false;

	//反转
	if (side==0)
	{
		node->killed = 0;
		for (int i=0;i<32;++i)
		{
			node->coords[i] &=0x0f;
			node->coords[i] ^= (coordx[i] << 4);
			node->coords[i] &=0xf0;
			node->coords[i] ^= coordy[i];

			node->alive |= (1<<i);
			node->alive ^= (!alive[i])? (1<<i):0;
			node->killed += 1-alive[i];
		}
	}
	else {
		node->killed = 0;
		for (int i=0;i<32;++i)
		{
			const int od = i%16 + (1-i/16)*16;
			node->coords[i] &=0x0f;
			node->coords[i] ^=((10-coordx[od]) << 4);
			node->coords[i] &=0xf0;
			node->coords[i] ^=11 - coordy[od];

			node->alive |= (1<<i);
			node->alive ^= (!alive[od])? (1<<i):0;
			node->killed += 1-alive[i];
		}
	}

	if (map_coords[new_y][new_x]>16)
	{
		node->jump_cost[1-side] = calc_cost(coordx,coordy,alive,node->killed,map_coords[new_y][new_x]-1);
	}

	return true;
}

bool build_node(const int coordx[/*32*/], const int coordy[/*32*/],
const int alive[/*32*/],const int side,chess_node * node)
{
	//反转
	if (side==0)
	{
		node->killed =0;
		for (int i=0;i<32;++i)
		{
			node->coords[i] &=0x0f;
			node->coords[i] ^= (coordx[i] << 4);
			node->coords[i] &=0xf0;
			node->coords[i] ^= coordy[i];

			node->alive |= (1<<i);
			node->alive ^= (!alive[i])? (1<<i):0;
			node->killed += 1-alive[i];
		}
	}
	else {
		node->killed =0;
		for (int i=0;i<32;++i)
		{
			const int od = i%16 + (1-i/16)*16;
			node->coords[i] &=0x0f;
			node->coords[i] ^= (10 - coordx[od]) << 4;
			node->coords[i] &=0xf0;
			node->coords[i] ^= 11 - coordy[od];

			node->alive |= (1<<i);
			node->alive ^= (!alive[od])? (1<<i):0;
			node->killed += alive[i];

		}
	}

	return true;
}
void print_node(const chess_node & node,const chess_node & old_node)
{
	const int side = node.side;
	printf ("side=%d\n",side);
	static const char * pstr[] = {"帅","士","士","相","相","马","马","车","车","炮","炮","兵","兵","兵","兵","兵","將","仕","仕","象","象","馬","馬","車","車","砲","砲","卒","卒","卒","卒","卒"};

	int coordx[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int coordy[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int alive[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

	int coord_ox[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int coord_oy[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int alive_o[32]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};


	size_t nhash = node2hash(node.coords,node.alive);
	printf ("HASH=%X\n",nhash);
	for (int i=0;i<32;++i)
	{
		coordx[i] = node.coords[i] >> 4;
		coordy[i] = node.coords[i] & 0x0f;
		coord_ox[i] = old_node.coords[i] >> 4;
		coord_oy[i] = old_node.coords[i] & 0x0f;
		alive[i] = (node.alive & (1<<i))?1:0;
		alive_o[i] = (old_node.alive & (1<<i))?1:0;
	}
	//printf ("\n");
	//制作坐标站位表
	int map_coords[11][10];
	int map_oldcoords[11][10];
	memset (map_coords,0,sizeof(map_coords));
	memset (map_oldcoords,0,sizeof(map_coords));
	for (int i=0;i<32;++i)
	{
		if (alive[i])
			map_coords[coordy[i]][coordx[i]] = i+1;
		if (alive_o[i])
			map_oldcoords[coord_oy[i]][coord_ox[i]] = i+1;
	}

	if (side)
	{
		printf ("   ");
		for (int x = 1; x<=9; ++x)
			printf("%2d",x-1);
		printf ("\n");
		for (int y = 1; y <=10 ;++y)
		{
			printf ("%2d ", y-1);
			for (int x=1;x<=9;++x)
			{
				if ((map_coords[y][x]-1)/16 != (map_oldcoords[y][x]-1)/16
					||(map_coords[y][x]>0)!=(map_oldcoords[y][x]>0))
					printf("\033[1m\033[4m");
				if (map_coords[y][x]>0 && map_coords[y][x]<=16)
					printf("\033[31m%s\033[0m",pstr[map_coords[y][x]-1]);
				else if (map_coords[y][x]>16)
					printf("\033[36m%s\033[0m",pstr[map_coords[y][x]-1]);
				else
					printf ("  \033[0m");
			}
			printf ("\n");
		}
	}
	else
	{
		printf ("   ");
		for (int x = 1; x<=9; ++x)
			printf("%2d",8-(x-1));
		printf ("\n");
		for (int y = 10; y >0 ;--y)
		{
			printf ("%2d ", y-1);
			for (int x=9;x>0;--x)
			{
				if ((map_coords[y][x]-1)/16 != (map_oldcoords[y][x]-1)/16
					||(map_coords[y][x]>0)!=(map_oldcoords[y][x]>0))
					printf("\033[1m\033[4m");

				if (map_coords[y][x]>0 && map_coords[y][x]<=16)
					printf("\033[31m%s\033[0m",pstr[map_coords[y][x]-1]);
				else if (map_coords[y][x]>16)
					printf("\033[36m%s\033[0m",pstr[map_coords[y][x]-1]);
				else
					printf ("  \033[0m");
			}
			printf ("\n");
		}
	}
	printf ("----------------------------------------\n");

}

std::vector<size_t> node2hash(const std::vector<chess_node> & nodes)
{
	std::vector<size_t> v;
	const size_t sz = nodes.size();
	for (size_t i=0;i<sz;++i)
		v.push_back(node2hash(nodes[i].coords,nodes[i].alive));
	return v;
}

size_t node2hash(const unsigned char coords[/*32*/], const unsigned int alive)
{
	std::string res;
	std::string str1;
	for (int i=0;i<32;++i)
	{
		if(alive & (1<<i))
			str1+=coords[i];
		else
			str1+=0xff;
	}
	//哈西需要考虑镜像
	std::sort(str1.begin()+1,str1.begin()+3,std::less<unsigned char>());
	std::sort(str1.begin()+3,str1.begin()+5,std::less<unsigned char>());
	std::sort(str1.begin()+5,str1.begin()+7,std::less<unsigned char>());
	std::sort(str1.begin()+7,str1.begin()+9,std::less<unsigned char>());
	std::sort(str1.begin()+9,str1.begin()+11,std::less<unsigned char>());
	std::sort(str1.begin()+11,str1.begin()+16,std::less<unsigned char>());

	std::sort(str1.begin()+17,str1.begin()+19,std::less<unsigned char>());
	std::sort(str1.begin()+19,str1.begin()+21,std::less<unsigned char>());
	std::sort(str1.begin()+21,str1.begin()+23,std::less<unsigned char>());
	std::sort(str1.begin()+23,str1.begin()+25,std::less<unsigned char>());
	std::sort(str1.begin()+25,str1.begin()+27,std::less<unsigned char>());
	std::sort(str1.begin()+27,str1.begin()+32,std::less<unsigned char>());

	unsigned char codsm[32];
	mirror_coordx(coords,codsm);

	std::string str2;
	for (int i=0;i<32;++i)
	{
		if(alive & (1<<i))
			str2+=codsm[i];
		else
			str2+=0xff;
	}
	//哈西需要考虑镜像
	std::sort(str2.begin()+1,str2.begin()+3,std::less<unsigned char>());
	std::sort(str2.begin()+3,str2.begin()+5,std::less<unsigned char>());
	std::sort(str2.begin()+5,str2.begin()+7,std::less<unsigned char>());
	std::sort(str2.begin()+7,str2.begin()+9,std::less<unsigned char>());
	std::sort(str2.begin()+9,str2.begin()+11,std::less<unsigned char>());
	std::sort(str2.begin()+11,str2.begin()+16,std::less<unsigned char>());

	std::sort(str2.begin()+17,str2.begin()+19,std::less<unsigned char>());
	std::sort(str2.begin()+19,str2.begin()+21,std::less<unsigned char>());
	std::sort(str2.begin()+21,str2.begin()+23,std::less<unsigned char>());
	std::sort(str2.begin()+23,str2.begin()+25,std::less<unsigned char>());
	std::sort(str2.begin()+25,str2.begin()+27,std::less<unsigned char>());
	std::sort(str2.begin()+27,str2.begin()+32,std::less<unsigned char>());

	if (str1 > str2)
		res = str2 + str1;
	else
		res = str1 + str2;

	std::hash<std::string> ha;
	size_t r = ha(res);
	return r;
}

void mirror_coordx(const unsigned char cod1[/*32*/],unsigned char cod2[/*32*/])
{
	for (int i=0;i<32;++i)
	{
		const unsigned char x = 10-(cod1[i]>>4);
		const unsigned char y = cod1[i] & 0x0f;
		cod2[i] = (x<<4) ^ y;
	}
}

bool move_node(const chess_node & root, chess_node * new_node, const int x1, const int y1,const int x2, const int y2,const int side)
{
	std::vector <chess_node> vec = expand_node(root,side);
	int idx = -1;
	for (int i=0;i<32 && idx < 0;++i)
	{
		int rx = root.coords[i]>>4;
		int ry = root.coords[i]& 0x0F;
		if (rx==x1 && ry==y1 && (root.alive & (1<<i)))
			idx = i;
	}
	if (idx<0)
		return false;
	if (side==0 && idx >=16)
		return false;
	if (side==1 && idx <16)
		return false;
	const size_t sz = vec.size();

	for (size_t i=0;i<sz;++i)
	{
		const int newx = vec[i].coords[idx] >> 4;
		const int newy = vec[i].coords[idx] & 0x0F;
		if (newx == x2 && newy==y2)
		{
			*new_node = vec[i];
			return true;
		}
	}
	return false;
}
