// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 	Ｃ言語インターフェイスサンプル		%
//	オセロゲームで使用している述語です。	%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#include <azprolog.h>			//	必ずインクルードします。

#define	BLACK	1
#define	WHITE	2

BASEINT	table[64];			//	盤面の定義
BASEINT	Black, White;

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 	挟まれた石をひっくり返す	%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void	rep1(x,v,buff,bufcnt,l,flag)
BASEINT	x, v, *buff, *bufcnt, l, flag;
{
	BASEINT	w, xx, yy, xx1, yy1;
	BASEINT	c, i;

	c  = *bufcnt;
	w  = l;
	xx = (x%8)+1;
	yy = (x/8)+1;

	while ((x+w >= 0 && x+w < 64) && table[x+w] != v && table[x+w])
	{
		xx1 = ((x+w)%8)+1;
		yy1 = ((x+w)/8)+1;
		switch (l)
		{
			case 1:
			case 9:
			    if (xx+1 != xx1)
				goto f;
			    break;
			case 8:
			case 7:
			    if (yy+1 != yy1)
				goto f;
			    break;
			case -1:
			case -9:
			    if (xx-1 != xx1)
				goto f;
			    break;
			case -8:
			case -7:
			    if (yy-1 != yy1)
				goto f;
			    break;
		}
		xx = xx1;
		yy = yy1;
		for (i = 0; i < *bufcnt; i++)
		{
			if (buff[i] == x+w)
			 goto e;
		}
		buff[(*bufcnt)++] = x+w;
	e:
		w += l;
	}
	if ((x+w >= 0 && x+w < 64) && table[x+w] == v)
	{
		xx1 = ((x+w)%8)+1;
		yy1 = ((x+w)/8)+1;
		switch( l )
		{
			case 1:
			case 9:
			    if (xx+1 != xx1)
				goto f;
			    break;
			case 8:
			case 7:
			    if (yy+1 != yy1)
				goto f;
			    break;
			case -1:
			case -9:
			    if (xx-1 != xx1)
				goto f;
			    break;
			case -8:
			case -7:
			    if (yy-1 != yy1)
				goto f;
			    break;
		}
		if (flag)
		    for (;c < *bufcnt;c++)
		   	 table[buff[c]] = v;
		return;
	}
	f:
	*bufcnt = c;
}

void	replace(x,v,buff,bufcnt,flag)
int	x, v, *buff, *bufcnt, flag;
{
	rep1(x, v,buff, bufcnt, 1, flag);
	rep1(x, v,buff, bufcnt, 9, flag);
	rep1(x, v,buff, bufcnt, 8, flag);
	rep1(x, v,buff, bufcnt, 7, flag);
	rep1(x, v,buff, bufcnt, -9, flag);
	rep1(x, v,buff, bufcnt, -8, flag);
	rep1(x, v,buff, bufcnt, -7, flag);
	rep1(x, v,buff, bufcnt, -1, flag);
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 	リストの作成ルーチン	%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int	MakeTableList(Env,bufcnt,buff,arg)
Frame	*Env;
BASEINT	bufcnt, *buff;
TERM	*arg;
{
	TERM	*Wcar, *Wcdr, *nil, *arg1, *arg2, *arg3;
	BASEINT	i, x, y;

	if (!bufcnt)				//	要素が０？
		return UnifyAtom(arg,(BASEINT)ATOM_NIL);
	else
	{
		MakeUndef(Env);			//	変数の生成
		nil = next_var_cell - 1;	//	空リストの準備
		if (!UnifyAtom(nil,(BASEINT)ATOM_NIL))
			return FAIL;
		for (i = 0; i < bufcnt; i++)
	 	{
			MakeUndef(Env);		//	変数の生成
			MakeUndef(Env);		//	変数の生成
			Wcar = next_var_cell - 2;
			Wcdr = next_var_cell - 1;
						//	リストの生成
						//	arg = [Wcar|Wcdr]
			UnifyCons(Env, arg, Wcar, Wcdr);
			x = (buff[i]/8) + 1;
			y = (buff[i]%8) + 1;
			MakeUndef(Env);		//	変数の生成
			MakeUndef(Env);		//	変数の生成
			MakeUndef(Env);		//	変数の生成
			arg1 = next_var_cell - 3;
			arg2 = next_var_cell - 2;
			arg3 = next_var_cell - 1;
			if (!UnifyInt(arg1,x) || !UnifyInt(arg2,y))
				return FAIL;
						//	リストの生成
						//	Wcar = [arg1|arg3]
			UnifyCons(Env, Wcar, arg1, arg3);
						//	リストの生成
						//	arg3 = [arg2|[]]
			UnifyCons(Env, arg3 ,arg2, nil);
			arg = Wcdr;		//	クダーポインタ
		}
		return UnifyAtom(arg, (BASEINT)ATOM_NIL);
	}
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 	盤面の空きを捜す	%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void	gap(buff,cnt,v,i,vv)
BASEINT	*buff, *cnt;
BASEINT	v, i, vv;
{
	BASEINT	x, xx, yy, xx1, yy1;
	BASEINT	l = 0;

	yy = (i/8)+1;
	xx = (i%8)+1;

	while ((i+(l+=v)) >= 0 && (i+l) < 64) 
	{
		yy1 = ((i+l)/8)+1;
		xx1 = ((i+l)%8)+1;
		switch (v)
		{
			case	1:
			case	9:
			    if (xx+1 != xx1)
				return;
			    break;
			case	7:
			    if (yy+1 != yy1)
				return; 
			    break;
			case	-1:
			case	-9:
			    if (xx-1 != xx1)
				return;
			    break;
			case	-7:
			    if (yy-1 != yy1)
				return;
			default:
			    break;

		}
		xx = xx1;
		yy = yy1;
		if (table[i+l] == vv)
		    continue;
		else if (!table[i+l])
		{
		    for (x = 0;x < *cnt;x++)
		    {
			if (buff[x] == (int)(i+l))
			    return;
		    }
		    buff[(*cnt)++] = i+l;
		    return;
		}
		else
		    return;
	}
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	石を置いたときに		%
//	ひっくり返した石をリストで返す	%
//					%
//	?- put_table(black,[4,6],L).	%
//	L	= [[5,5]]		%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred P3_put_table(Env)
Frame	*Env;
{
	BASEINT	x,v;
	BASEINT	buff[64],bufcnt = 0;

	BASEINT	Color;
	TERM	*Warg1, *Warg2, *Warg3, *e1, *e2;

	Warg1 = next_var_cell - 3;			// １引き数目の取得
	Warg2 = next_var_cell - 2;			// ２引き数目の取得
	Warg3 = next_var_cell - 1;			// ３引き数目の取得

	Color = GetAtom(Warg1);
	GetCons(Warg2);					// リストの展開
	e1 = next_var_cell - 2;				// リストの１要素目
	GetCons(next_var_cell - 1);
	e2 = next_var_cell - 2;				// リストの２要素目
	if (!IsNil(next_var_cell-1))
		YIELD(FAIL);
	x = (GetInt(e1)-1)*8 + GetInt(e2) - 1;
	if (Color == Black)
  	    table[x] = v = BLACK;
	else
	    table[x] = v = WHITE;
	replace(x,v,buff,&bufcnt,1);			// ひっくり返す
	YIELD(MakeTableList(Env,bufcnt,buff,Warg3));	// リストの作成
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	もし、石を置いたときに		%
//	ひっくり返る石をリストで返す	%
//					%
//	?- if_put_table(black,[4,6],L).	%
//	L	= [[5,5]]		%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred	P3_if_put_table(Env)
Frame	*Env;
{
	BASEINT	x,v;
	BASEINT	buff[64],bufcnt = 0;

	BASEINT	Color;
	TERM	*Warg1, *Warg2, *Warg3, *e1, *e2;

	Warg1 = next_var_cell - 3;			// １引き数目の取得
	Warg2 = next_var_cell - 2;                      // ２引き数目の取得
	Warg3 = next_var_cell - 1;                      // ３引き数目の取得

	Color = GetAtom(Warg1);
	GetCons(Warg2);					// リストの展開
	e1 = next_var_cell - 2;				// リストの１要素目
	GetCons(next_var_cell - 1);
	e2 = next_var_cell - 2;				// リストの２要素目
	if (!IsNil(next_var_cell - 1))
		YIELD(FAIL);
	x = (GetInt(e1)-1)*8 + GetInt(e2) - 1;
	if (Color == Black)
  	    table[x] = v = BLACK;
	else
	    table[x] = v = WHITE;
	replace(x,v,buff,&bufcnt,0);			// ひっくり返さない
	table[x] = 0;
	YIELD(MakeTableList(Env,bufcnt,buff,Warg3));	// リストの作成
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	置いてある位置をリストで返す	%
//					%
//	?- get_table(black,L).		%
//	L	= [[4,4],[5,5]]		%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred	P2_get_table(Env)
Frame	*Env;
{
	BASEINT	v, i;
	BASEINT	buff[64];
	BASEINT	bufcnt = 0;
	BASEINT	Color;

	Color = GetAtom(next_var_cell - 2);	//	１引き数目の取得
	if (Color == Black)
	    v = BLACK;
	else
	    v = WHITE;
	for (i = 0; i < 64; i++)
	{
	    if (table[i] == v)
		buff[bufcnt++] = i;
	}					//	リストの作成
	YIELD(MakeTableList(Env,bufcnt,buff,next_var_cell-1));
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	置ける位置をリストで返す	%
//					%
//	?- get_gap(black,L).		%
//	L	= [[4,6],[6,4]]		%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred	P2_get_gap(Env)
Frame	*Env;
{
	BASEINT	v,vv;
	BASEINT	i;
	BASEINT	buff[64];
	BASEINT	bufcnt = 0;
	BASEINT	Color;

	Color = GetAtom(next_var_cell - 2);	//	１引き数目の取得
	if (Color == Black)
	{
	    v = BLACK;
	    vv= WHITE;
	}
	else
	{
	    v = WHITE;
	    vv= BLACK;
	}
	for( i = 0; i < 64; i++ )
	{
	    if( table[i] == v )			//	８方向捜す
	    {
	    	if( (i+1) < 64 && table[i+1] == vv )
		    gap(&buff[0],&bufcnt,1,i,vv);
	    	if( (i+9) < 64 && table[i+9] == vv )
		    gap(&buff[0],&bufcnt,9,i,vv);
	    	if( (i+8) < 64 && table[i+8] == vv )
		    gap(&buff[0],&bufcnt,8,i,vv);
	    	if( (i+7) < 64 && table[i+7] == vv )
		    gap(&buff[0],&bufcnt,7,i,vv);
	    	if( (i-9) >= 0  && table[i-9] == vv )
		    gap(&buff[0],&bufcnt,-9,i,vv);
	    	if( (i-8) >= 0  && table[i-8] == vv )
		    gap(&buff[0],&bufcnt,-8,i,vv);
	    	if( (i-7) >= 0  && table[i-7] == vv )
		    gap(&buff[0],&bufcnt,-7,i,vv);
	    	if( (i-1) >= 0  && table[i-1] == vv )
		    gap(&buff[0],&bufcnt,-1,i,vv);
	    }
	}					//	リストの作成
	YIELD(MakeTableList(Env,bufcnt,buff,next_var_cell-1));
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	盤面初期化		%
//				%
//	?- init_table.		%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pred	P0_init_table(Env)
Frame	*Env;
{
	int	i;

	for( i = 0; i < 64 ;i++ )
	    table[i] = 0;
	table[27] = WHITE;
	table[28] = BLACK;
	table[35] = BLACK;
	table[36] = WHITE;
	YIELD(DET_SUCC);
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//	組込述語の登録			%
//	関数名：initiate_ファイル名	%
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
initiate_o()
{
	Black = PutAtom(NULLFRAME,"black");		// 	アトムの登録
	White = PutAtom(NULLFRAME,"white");		// 	アトムの登録
	put_bltn("init_table",  0,P0_init_table);
	put_bltn("put_table",   3,P3_put_table);
	put_bltn("if_put_table",3,P3_if_put_table);
	put_bltn("get_table",   2,P2_get_table);
	put_bltn("get_gap",     2,P2_get_gap);
}

