#include "go.h"

#define neighbor_count_at(ban, pos, color2) \
  ((ban->color[pos + D2DELTA[1]] == color2) +	\
   (ban->color[pos + D2DELTA[3]] == color2) +	\
   (ban->color[pos + D2DELTA[5]] == color2) +	\
   (ban->color[pos + D2DELTA[7]] == color2))

int outPos(const int pos)
{
  fprintf(stderr, "(%d %d)", POS_TO_X(pos), POS_TO_Y(pos));
  return 0;
}

int judgeKoNG(const int pos, goban_t *ban)
{
  history_t *histInfo = &ban->histInfo[ban->tesuu - 1];
  assert (ban->tesuu >= 1);
  return (pos == histInfo->ko);
}

/* not judging KO ng move*/
int teOK(const int xy, goban_t *ban)
{
	const int color = TBN2COLOR(ban->tbn);
	int d;

	if (xy == 0)
		return 1;
	if (IN_RANGE(0, xy, XY_SIZE - 1) && ban->color[xy] == SP){
		for (d = 1; d < D_MAX; d += 2){ /* cross */
			const int pos = xy + D2DELTA[d];
			const int renID = ban->renID[pos];
			const int colorD = ban->color[pos];
			if (colorD == SP){
				return 1;
			}
			if (colorD == color && ban->renInfo[renID].breathPoints > 1){
				return 1;
			}
			if (colorD == GET_AITE(color) &&  ban->renInfo[renID].breathPoints == 1){
				return 1;
			}
		}
	}
	return 0;
}
	
/* REQUIRE : ban->color[pos] == SP

   合法手でなければ NG
   合法手でも、四方を味方石で囲まれた位置は、
   取られそうな場合(味方の呼吸点1の連に接する手)を除き NG (return 0),
   そうでなければ return 1; */
int judge_eff_te(const int pos, goban_t *ban)
{
	const int colorAite = GET_AITE(TBN2COLOR(ban->tbn));
	int d, tgt, colorD;

	if (neighbor_count_at(ban, pos, SP) >= 1){
		return 1;
	}
	if (!teOK(pos, ban) || judgeKoNG(pos, ban)){
		return 0;
	}
	for (d = 1; d < D_MAX; d += 2){
		tgt = pos + D2DELTA[d];
		colorD = ban->color[tgt];
		if (colorD == OB)
			continue;

		if (colorD == SP || colorD == colorAite){
			return 1;
		}
		if (ban->renInfo[ban->renID[tgt]].breathPoints == 1){
			return 1;
		}
	}
	return 0;
}

void writeBan(goban_t *ban)
{
	int x, y;

	for (x = 1; x <= BOARD_EDGE_SIZE; x++){
		fprintf(stderr, "%.1s", &COLOR_NAME[ban->color[x]]);
	}
	fprintf(stderr, "\n   [");
	for (x = 1; x <= BOARD_EDGE_SIZE; x++){
		fprintf(stderr, "%2d", x);
	}
	fprintf(stderr, "]\n");
	for (y = 1; y <= BOARD_EDGE_SIZE ; y++){
		fprintf(stderr, "[%2d] ", y);
		for (x = 1; x <= BOARD_EDGE_SIZE; x++){
			fprintf(stderr, "%.1s ", &COLOR_NAME[ban->color[X_Y_TO_POS(x, y)]]);
		}
		fprintf(stderr, "\n");
	}
	fprintf(stderr, "tbn = %.1s, tesuu = %d, bk: %d, wh: %d  prev: %.1s",
	&COLOR_NAME[ban->tbn + 1], ban->tesuu, ban->prisoner[TBN_BK], ban->prisoner[TBN_WH], &COLOR_NAME[GET_AITE(ban->tbn + 1)]);
	outPos(ban->histInfo[ban->tesuu - 1].pos);
	return;
}
#define LEADING_ONES(x) ((1ULL << (x)) - 1ULL)

int get_around_bb(bb_t *aroundBB, bb_t *inBB)
{
	int k;
	for (k = 0; k < BB_IDX_SIZE; k++){
		aroundBB[k] = 
		(inBB[k] >> 1ULL) | (inBB[k] >> BB_EDGE)
		| ((inBB[k] & LEADING_ONES(NUM_PER_BB - 1)) << 1ULL)
		| ((inBB[k] & LEADING_ONES(NUM_PER_BB - BB_EDGE)) << BB_EDGE);
		if (k < BB_IDX_SIZE - 1 && inBB[k + 1]){
			aroundBB[k] |=
			((inBB[k + 1] & LEADING_ONES(BB_EDGE)) << (NUM_PER_BB - BB_EDGE))
			| ((inBB[k + 1] & LEADING_ONES(1)) << (NUM_PER_BB - 1));
		}
		if (k > 0 && inBB[k - 1]){
			aroundBB[k] |=
			(inBB[k - 1]  >> (NUM_PER_BB - BB_EDGE))
			|(inBB[k - 1]  >> (NUM_PER_BB - 1));
		}
	}
	return 0;
}

int is_danger_around(goban_t *ban, bb_t *target_occupiedBB, const int check_tbn)
{
	int k, target;
	bb_t aroundBB[BB_IDX_SIZE], bit;
	get_around_bb(aroundBB, target_occupiedBB);
	for (k = 0; k < BB_IDX_SIZE; k++){
		for (bit = aroundBB[k] & ban->occupiedBB[check_tbn][k]; bit; bit &= (bit - 1)){
			target = GET_POS(k, Get_FirstBit64(bit));
			if (ban->renInfo[ban->renID[target]].breathPoints == 1){
				return 1;
			}
		}
	}
	return 0;
}

int detect_suicide_or_capture(goban_t *ban, const int pos)
{
	int d;
	bb_t breathBB[BB_IDX_SIZE];

	CLEAR_BB(breathBB);
	ADD_AN_ELEMENT_TO_BB(breathBB, pos);

	for (d = 1; d < D_MAX; d += 2){ /* CROSS */
		const int target = pos + D2DELTA[d];
		if (ban->color[target] == SP){
			ADD_AN_ELEMENT_TO_BB(breathBB, target);
		}

		if (ban->color[target] == TBN2COLOR(ban->tbn)){
			renInfo_t *renInfo2 = &ban->renInfo[ban->renID[target]];
			MERGE_BB(breathBB, renInfo2->breathBB);
		}
		if (ban->color[target] == GET_AITE(TBN2COLOR(ban->tbn))){
			renInfo_t *renInfo2 = &ban->renInfo[ban->renID[target]];
			if (renInfo2->breathPoints == 1){  /* capture */
				return 1;
			}
		}
	}

	int breathPoints = bb_pop_cnt(breathBB);
	if (breathPoints >= 3){
		return 0;
	}

	assert (breathPoints == 2);
	return 1;
}

int check_kill(const int seme, const int escape,  goban_t *ban)
{
	const int prePos = ban->histInfo[ban->tesuu - 1].pos;
	int res = 0;
	assert (seme && escape);// && ban->color[seme] == SP && ban->color[escape] == SP);
	if (!teOK(seme, ban)|| judgeKoNG(seme, ban) || detect_suicide_or_capture(ban, seme)){
		return 0;
	}
	
	make_move(seme, ban);
	renInfo_t *renInfo = &ban->renInfo[ban->renID[seme]];
	if (renInfo->breathPoints >= 2 &&
		check_dead(escape, ban)){
		
		// semeの直前の相手石の連の、周囲の石(つまりsemeの方)を取られないかをcheck
		renInfo_t *renInfo2 = &ban->renInfo[ban->renID[prePos]];
		if (is_danger_around(ban, renInfo2->occupiedBB, AITE_TBN(ban->tbn))){
			res = 0;
		} else {
			res = 1;
		}
	}
	unmake_move(ban);
	return res;
}


int check_dead(const int pos, goban_t *ban)
{
	int res = 0, k, cand[2];
	if (ban->tesuu >= TESUU_MAX - 2){
		return 0;
	}
	make_move(pos, ban);
	
	renInfo_t *renInfo = &ban->renInfo[ban->renID[pos]];

	res = (renInfo->breathPoints == 1);

	if (renInfo->breathPoints == 2){
		get_double_pos(cand, renInfo->breathBB);
		for (k = 0; k < 2; k++){
			if (res == OFF){
				res = check_kill(cand[k], cand[1 - k], ban);
			}
		}
	}
	unmake_move(ban);
	return res;
}

//int get_nb_stones(const int pos, int nb_num[4], int nb_stones[4][4], goban_t *ban)
int get_nb_stones(const int pos, int nb_num[CROSS_MAX], int nb_stones[CROSS_MAX][COLOR_MAX], goban_t* ban)
{
	int d, tgt, colorD;

	assert(IN_RANGE(1, POS_TO_X(pos), BOARD_EDGE_SIZE) && IN_RANGE(1, POS_TO_Y(pos), BOARD_EDGE_SIZE));

	nb_num[0] = nb_num[1] = nb_num[2] =nb_num[3] = 0;

	for (d = 1; d < D_MAX; d += 2){ /* cross */
		tgt = pos + D2DELTA[d];
		colorD = ban->color[tgt];
		nb_stones[colorD][nb_num[colorD]++] = tgt;
	}
	return 0;
}

int bb_pop_cnt(bb_t *targetBB)
{
	int k, cnt = 0;
	for (k = 0; k < BB_IDX_SIZE; k++)
		cnt += pop_cnt(targetBB[k]);
	return cnt;
}

int get_double_pos(int *cand, bb_t *targetBB)
{
	int k, cnt = 0;
	unsigned long long bit;
	assert(bb_pop_cnt(targetBB) == 2);

	for (k = 0; k < BB_IDX_SIZE; k++){
		for (bit = targetBB[k]; bit; bit &= (bit - 1)){
			cand[cnt++] = GET_POS(k, Get_FirstBit64(bit));
		}
	}
	return 0;
}

int get_single_pos(bb_t *targetBB)
{
	int k;

	for (k = 0; k < BB_IDX_SIZE; k++){
		if (targetBB[k]){
			return GET_POS(k, Get_FirstBit64(targetBB[k]));
		}
	}
	return 0;
}

                       
int get_another_pos(bb_t *targetBB, const int exclude)
{
	int cand[2];
	get_double_pos(cand, targetBB);
	if (cand[0] == exclude){
		return cand[1];
	} else {
		return cand[0];
	}
	return 0;
}
double Tool_GetTimeNow()
{
	return clock()/(double)(CLOCKS_PER_SEC);
}
