/* Copyright (C) 2016 Otsuki Tomoshi <tomoshi@m00.itscom.net> */

#ifndef _GO_H_
#define _GO_H_

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

#define MIN(a,b) ((a)<(b) ? (a) : (b) )
#define MAX(a,b) ((a)>(b) ? (a) : (b) )
#define ABS(a)   ((a)>0   ? (a) : -(a))
#define SGN(a)   ((a)==0  ? (0) : ( (a)>0 ? (1) : (-1) ))
#define IN_RANGE(a, x, b) ((a) <= (x) && (x) <= (b))

#define BOARD_EDGE_SIZE 19
#define TIME_LIM 600

#define BB_EDGE     (BOARD_EDGE_SIZE + 2)  
#define XY_SIZE     (BB_EDGE * BB_EDGE)    
#define BB_EFF       64
#define NUM_PER_BB   64
#define BB_IDX_SIZE ((XY_SIZE - 1)/NUM_PER_BB + 1)    

#define VEC_WIDTH 8

typedef unsigned long long bb_t;

typedef enum { SP = 0, BK, WH, OB } color_t;
typedef enum { TBN_BK = 0, TBN_WH } teban_t;
typedef enum { OFF = 0, ON } onoff_t;

#define CROSS_MAX  4
#define D_MAX 8

#define TBN_MAX 2
#define COLOR_MAX 4
#define GET_AITE(st)     (BK + WH - (st))
#define AITE_TBN(tbn)    (TBN_BK + TBN_WH - (tbn))
#define TBN2COLOR(tbn)   ((tbn) + 1)
#define COLOR2TBN(color) ((color) - 1)

#define TESUU_MAX   (XY_SIZE * 2)
#define REN_MAX     (XY_SIZE * 2) /* 連の最大数 */
#define REN_BB_SIZE (REN_MAX/BB_EFF + 1)

#define FRAME 512

#define IS_FIRST_MOVE(ban) (ban->tesuu == 1 && ban->occupiedPoints[TBN_BK] == 0 && ban->occupiedPoints[TBN_WH] == 0)
#define X_Y_TO_POS(x, y) ((y) * BB_EDGE + (x))

#define POS_TO_X(pos)   (pos % BB_EDGE)
#define POS_TO_Y(pos)   (pos / BB_EDGE)

#define POS_TO_IDX(pos) ((pos)/NUM_PER_BB)
#define POS_TO_BIT(pos) ((pos)%NUM_PER_BB)

#define GET_POS(idx, digit) ((idx) * NUM_PER_BB + (digit))

#define BELONGS_TO(bb, pos) (bb[POS_TO_IDX(pos)] & (1LL << (POS_TO_BIT(pos))))
#define ADD_AN_ELEMENT_TO_BB(bb, pos) (bb[POS_TO_IDX(pos)] |= (1LL << (POS_TO_BIT(pos))))
#define ERASE_AN_ELEMENT_FROM_BB(bb, pos) (bb[POS_TO_IDX(pos)] &= ~(1LL << (POS_TO_BIT(pos))))

#define MERGE_BB(bb, bb2)     if (1){int k; for(k=0;k<BB_IDX_SIZE;k++)bb[k] |= bb2[k];}
#define CLEAR_BB(bb)          if (1){int k; for(k=0;k<BB_IDX_SIZE;k++)bb[k] = 0LL;}
#define SPLIT_OUT_BB(bb, bb2) if (1){int k; for(k=0;k<BB_IDX_SIZE;k++)bb[k] &= ~(bb2[k]);}

extern const char* COLOR_NAME;
extern int D2DELTA[D_MAX];

typedef struct {
	int color;
	int occupiedPoints;
	int breathPoints;
	int deadFlag;
	bb_t occupiedBB[BB_IDX_SIZE];
	bb_t breathBB[BB_IDX_SIZE];
} renInfo_t;

typedef struct {
	int pos;
	int mergeRenID[CROSS_MAX];
	int removeRenID[CROSS_MAX];
	int ko;

	renInfo_t renInfo;
	int mergeNum, removeNum;
	double time;
} history_t;

#define FILTER_SIZE 192 //100
#define CHANNEL_SIZE 48

typedef struct {
	int tbn;                         /* 手番 */
	int tesuu;                       /* 手数 */
	int renNum;                      /* 連の最大ID */
	bb_t occupiedBB[TBN_MAX][BB_IDX_SIZE];    /* 各手番の盤上の石 bb */
	int occupiedPoints[TBN_MAX];

	int color[XY_SIZE];              /* xyの石の種類(BLACK, WHITE, SPACE, OB) */
	int renID[XY_SIZE];              /* xyの連のid(default: 0) */
	int prisoner[TBN_MAX];           /* agehama */

	renInfo_t renInfo[REN_MAX];
	history_t histInfo[TESUU_MAX];

	double komi;
	int feature[CHANNEL_SIZE][21][21];
} goban_t;


typedef float cnn_t;

typedef struct {
	goban_t* ban;
	cnn_t hidden[14][21][21][FILTER_SIZE];
	cnn_t hiddenIn[23][23][CHANNEL_SIZE];
	cnn_t hiddenOut[21][21];
} game_hdl_t;

typedef struct {
	cnn_t weight_cnn_5x5[CHANNEL_SIZE][FILTER_SIZE][25];
	cnn_t weight_cnn_3x3[13][FILTER_SIZE][FILTER_SIZE][9];
	cnn_t weight_cnn_1x1[FILTER_SIZE];
	cnn_t weight_cnn_bias[361];
} parameter_t;

extern parameter_t* cnn_param;

/* from init.c */
game_hdl_t* open_game_hdl(int argc, char** argv);
void close_game_hdl(game_hdl_t* hdl);
void set_initial_pos(goban_t* ban);


/* from move.c */
void make_move(const int xy, goban_t* ban);
void unmake_move(goban_t* ban);

/* from cnn_input.c */
int get_cnn_prob(goban_t* ban, game_hdl_t* hdl, double pos_to_prob[XY_SIZE]);
int get_feature(FILE* fp, const int i, const int tesuu, goban_t* ban);
//int load_cnn_weight(game_hdl_t* hdl);

/* from util.c */
int detect_suicide_or_capture(goban_t* ban, const int pos);
double Tool_GetTimeNow();
int get_single_pos(bb_t* targetBB);
int get_another_pos(bb_t* targetBB, const int exclude);
int get_double_pos(int* cand, bb_t* targetBB);
int check_dead(const int pos, goban_t* ban);
int check_kill(const int seme, const int escape, goban_t* ban);
int judge_eff_te(const int pos, goban_t* ban);
int outPos(const int pos);
int judgeKoNG(const int pos, goban_t* ban);
int teOK(const int xy, goban_t* ban);
void writeBan(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 bb_pop_cnt(bb_t* targetBB);

/* from bit_util.c */
int Get_FirstBit64(long long bit);
int pop_cnt(unsigned long long bits);

#endif /* ifdef GO_H */
