#include "go.h"
static void add_new_ren(const int xy, int nb_num[4], int nb_stones[4][4],
goban_t *ban)
{
const int color = ban->color[xy];
renInfo_t *renInfo = &ban->renInfo[++ban->renNum];
int i;
renInfo->color = color;
renInfo->occupiedPoints = 1;
ADD_AN_ELEMENT_TO_BB(renInfo->occupiedBB, xy);
renInfo->breathPoints = nb_num[SP];
for (i = 0; i < nb_num[SP]; i++){
ADD_AN_ELEMENT_TO_BB(renInfo->breathBB, nb_stones[SP][i]);
}
ban->renID[xy] = ban->renNum;
return;
}
static void erase_ren(goban_t *ban)
{
renInfo_t *renInfo = &ban->renInfo[ban->renNum--];
memset(renInfo, 0, sizeof(renInfo_t));
return;
}
static void merge_ren(const int pos, int nb_num[4], int nb_stones[4][4],
history_t *histInfo, goban_t *ban)
{
const int color = ban->color[pos];
const int renID = ban->renID[nb_stones[color][0]];
renInfo_t *renInfo = &ban->renInfo[renID], *renInfo2;
int i, k;
unsigned long long bit;
histInfo->renInfo = *renInfo;
histInfo->mergeNum = 0;
histInfo->mergeRenID[histInfo->mergeNum++] = renID;
/* renew renID, and merge occupiedBB & breathPoint */
for (i = 1; i < nb_num[color]; i++){
const int posNB = nb_stones[color][i];
renInfo2 = &ban->renInfo[ban->renID[posNB]];
if (!BELONGS_TO(renInfo->occupiedBB, posNB)){
histInfo->mergeRenID[histInfo->mergeNum++] = ban->renID[posNB];
for (k = 0; k < BB_IDX_SIZE; k++){
for (bit = renInfo2->occupiedBB[k]; bit; bit &= (bit - 1)){
ban->renID[GET_POS(k, Get_FirstBit64(bit))] = renID;
}
}
renInfo->occupiedPoints += renInfo2->occupiedPoints;
MERGE_BB(renInfo->occupiedBB, renInfo2->occupiedBB);
MERGE_BB(renInfo->breathBB, renInfo2->breathBB);
renInfo2->deadFlag = ON;
}
}
/* update occupied info */
renInfo->occupiedPoints++;
ADD_AN_ELEMENT_TO_BB(renInfo->occupiedBB, pos);
ban->renID[pos] = renID;
/* update breathBB */
ERASE_AN_ELEMENT_FROM_BB(renInfo->breathBB, pos);
for (i = 0; i < nb_num[SP]; i++){
ADD_AN_ELEMENT_TO_BB(renInfo->breathBB, nb_stones[SP][i]);
}
/* re calc renInfo->breathPoints */
renInfo->breathPoints = 0;
for (k = 0; k < BB_IDX_SIZE; k++){
renInfo->breathPoints += pop_cnt(renInfo->breathBB[k]);
}
return;
}
static void split_ren(const int pos,
history_t *histInfo, goban_t *ban)
{
const int renID = histInfo->mergeRenID[0];
renInfo_t *renInfo = &ban->renInfo[renID], *renInfo2;
int i, k;
unsigned long long bit;
/* renew renID, and merge occupiedBB & breathPoint */
for (i = 1; i < histInfo->mergeNum; i++){
const int renID2 = histInfo->mergeRenID[i];
renInfo2 = &ban->renInfo[renID2];
for (k = 0; k < BB_IDX_SIZE; k++){
for (bit = renInfo2->occupiedBB[k]; bit; bit &= (bit - 1)){
ban->renID[GET_POS(k, Get_FirstBit64(bit))] = renID2;
}
}
renInfo2->deadFlag = OFF;
}
*renInfo = histInfo->renInfo;
return;
}
void make_move(const int pos, goban_t *ban)
{
const int color = TBN2COLOR(ban->tbn);
int nb_num[4], nb_stones[4][4];
int d, i, k, posRemove;
unsigned long long bit;
history_t *histInfo = &ban->histInfo[ban->tesuu++];
assert (ban->tesuu < TESUU_MAX);
assert (ban->renNum < REN_MAX);
histInfo->removeNum = 0;
histInfo->ko = 0;
histInfo->pos = pos;
if (!pos){
ban->tbn ^= 1;
return;
}
assert (ban->color[pos] == SP);
ban->color[pos] = color;
ban->occupiedPoints[ban->tbn]++;
ADD_AN_ELEMENT_TO_BB(ban->occupiedBB[ban->tbn], pos);
/* update renn */
get_nb_stones(pos, nb_num, nb_stones, ban);
if (nb_num[color] == 0){
add_new_ren(pos, nb_num, nb_stones, ban);
} else {
merge_ren(pos, nb_num, nb_stones, histInfo, ban);
}
/* erase opponent's breathPoint, and remove opponent's stone if any*/
for (i = 0; i < nb_num[GET_AITE(color)]; i++){
const int renID = ban->renID[nb_stones[GET_AITE(color)][i]];
renInfo_t *renInfo = &ban->renInfo[renID];
/* erase opponent's breathPoint around pos */
if (BELONGS_TO(renInfo->breathBB, pos)){
if (renInfo->breathPoints > 1){
ERASE_AN_ELEMENT_FROM_BB(renInfo->breathBB, pos);
renInfo->breathPoints--;
} else { /* remove opponent's stone if no breath point */
histInfo->removeRenID[histInfo->removeNum++] = renID;
SPLIT_OUT_BB(ban->occupiedBB[ban->tbn^1], ban->renInfo[renID].occupiedBB);
ban->renInfo[renID].deadFlag = ON;
ban->occupiedPoints[ban->tbn^1] -= renInfo->occupiedPoints;
ban->prisoner[ban->tbn] += renInfo->occupiedPoints;
for (k = 0; k < BB_IDX_SIZE; k++){
for (bit = renInfo->occupiedBB[k]; bit; bit &= (bit - 1)){
posRemove = GET_POS(k, Get_FirstBit64(bit));
ban->renID[posRemove] = 0;
ban->color[posRemove] = SP;
/* update teban's breath point by stone removal*/
for (d = 1; d < D_MAX; d += 2){
int tgt = posRemove + D2DELTA[d];
renInfo_t *renInfo2 = &ban->renInfo[ban->renID[tgt]];
if (color == ban->color[tgt]){
if (!BELONGS_TO(renInfo2->breathBB, posRemove)){
ADD_AN_ELEMENT_TO_BB(renInfo2->breathBB, posRemove);
renInfo2->breathPoints++;
}
}
}
}
}
}
}
}
/* judge ko */
if (histInfo->removeNum == 1 &&
ban->renInfo[ban->renID[pos]].occupiedPoints == 1 &&
ban->renInfo[ban->renID[pos]].breathPoints == 1 &&
ban->renInfo[histInfo->removeRenID[0]].occupiedPoints == 1){
histInfo->ko = get_single_pos(ban->renInfo[histInfo->removeRenID[0]].occupiedBB);
}
/* update tbn */
ban->tbn ^= 1;
return;
}
void unmake_move(goban_t *ban)
{
int color, d, i, k, posReset;
unsigned long long bit;
history_t *histInfo = &ban->histInfo[--ban->tesuu];
const int pos = histInfo->pos;
int tgt, cnt = 0;
renInfo_t *renInfo;
/* update tbn */
ban->tbn ^= 1;
if (!pos){
return;
}
color = TBN2COLOR(ban->tbn);
assert (ban->color[pos] == color);
ban->color[pos] = SP;
ban->renID[pos] = 0;
ERASE_AN_ELEMENT_FROM_BB(ban->occupiedBB[ban->tbn], pos);
ban->occupiedPoints[ban->tbn]--;
for (d = 1; d < D_MAX; d += 2){
tgt = pos + D2DELTA[d];
cnt += (ban->color[tgt] == color);
/* add opponent's breathPoint */
if (ban->color[tgt] == GET_AITE(color)){
renInfo = &ban->renInfo[ban->renID[tgt]];
if (!BELONGS_TO(renInfo->breathBB, pos)){
ADD_AN_ELEMENT_TO_BB(renInfo->breathBB, pos);
renInfo->breathPoints++;
}
}
}
/* update mikata's ren */
if (cnt == 0){ /* if (no mikata's stone around pos) */
erase_ren(ban);
} else {
split_ren(pos, histInfo, ban);
}
/* re set opponent's stone if any */
for (i = 0; i < histInfo->removeNum; i++){
const int renID = histInfo->removeRenID[i];
renInfo_t *renInfo = &ban->renInfo[renID];
ban->renInfo[renID].deadFlag = OFF;
ban->prisoner[ban->tbn] -= renInfo->occupiedPoints;
ban->occupiedPoints[ban->tbn^1] += renInfo->occupiedPoints;
MERGE_BB(ban->occupiedBB[ban->tbn^1], renInfo->occupiedBB);
for (k = 0; k < BB_IDX_SIZE; k++){
for (bit = renInfo->occupiedBB[k]; bit; bit &= (bit - 1)){
posReset = GET_POS(k, Get_FirstBit64(bit));
ban->renID[posReset] = renID;
ban->color[posReset] = GET_AITE(color);
/* update teban's breath point by stone re set */
for (d = 1; d < D_MAX; d += 2){
int tgt = posReset + D2DELTA[d];
renInfo_t *renInfo2 = &ban->renInfo[ban->renID[tgt]];
if (color == ban->color[tgt]){
if (BELONGS_TO(renInfo2->breathBB, posReset)){
ERASE_AN_ELEMENT_FROM_BB(renInfo2->breathBB, posReset);
renInfo2->breathPoints--;
}
}
}
}
}
}
return;
}