/* コンピュータ上で人間どうしが対局するプログラム*/

#include 
#include 
#include 

#define TRUE 1
#define FALSE 0

#define SPACE 0                /* 空点(石が置かれていない) */
#define BLACK 1                /* 黒 */
#define WHITE 2                /* 白 */
#define OB    3                /* 盤外*/

#define BOARD_SIZE ( 19 + 2 )  /* 碁盤の大きさ*/

/* 相手の色*/
#define reverseColor( color ) ( color == BLACK ? WHITE : BLACK )

/*------------------------------------------------------------------*/
/* 構造体                                                          */
/*------------------------------------------------------------------*/

/* 棋譜*/
typedef struct {
  int color;
  int x;
  int y;
} type_log;

/*------------------------------------------------------------------*/
/* 関数                                                            */
/*------------------------------------------------------------------*/

/* メイン関数*/
int main( int argc, char *argv[] );

/* 碁盤の初期化*/
void InitializeBoard( int board[][BOARD_SIZE] );

/* 碁盤を表示する*/
void DisplayBoard( int board[][BOARD_SIZE] );

/* 着手位置の入力*/
void ThinkMove( int board[][BOARD_SIZE], int color, int *x, int *y );

/* 画面から数値を入力*/
void InputCoordinate( int color, int *x, int *y );

/* 合法手がどうか調べる*/
int CheckLegal( int board[][BOARD_SIZE], int color, int x, int y );

/* 自殺手かどうか調べる*/
int CheckSuicide( int board[][BOARD_SIZE], int color, int x, int y );

/* チェック用の碁盤をクリア*/
void ClearCheckBoard( void );

/* 相手に囲まれているか調べる*/
int DoCheckRemoveStone( int board[][BOARD_SIZE],
                         int color, int x, int y );

/* 勝敗の判定*/
int CountScore( void );

/* 碁盤に石を置く*/
void SetStone( int board[][BOARD_SIZE], int color, int x, int y );

/* 同じ色または盤外ならばTRUEを返す*/
int IsColorOrOut( int board[][BOARD_SIZE], int x, int y, int color );

/* 囲まれた石を取り除く*/
int RemoveStone( int board[][BOARD_SIZE],
                 int color, int x, int y );

/* 囲まれた石を取り除く*/
int DoRemoveStone( int board[][BOARD_SIZE],
                   int color, int x, int y, int prisoner );

/* 棋譜に記録*/
void RecordMove( int color, int x, int y );

/*------------------------------------------------------------------*/
/* 変数                                                            */
/*------------------------------------------------------------------*/

/* 碁盤の表示のための文字*/
static char stone[] = { '+', '@', 'O', '?' };

/* 手数*/
static int move;

/* アゲハマ*/
static int black_prisoner;
static int white_prisoner;

/* 劫の位置*/
static int ko_x;
static int ko_y;

/* 劫が発生した手数*/
static int ko_num;

/* 棋譜(499手まで覚える) */
static type_log logData[500];

/* 合法手かどうか調べるのに使う*/
static int checkBoard[BOARD_SIZE][BOARD_SIZE];

/*------------------------------------------------------------------*/
/* メイン関数                                                      */
/*------------------------------------------------------------------*/
int
main( int argc, char *argv[] )
{
  int board[BOARD_SIZE][BOARD_SIZE];  /* 碁盤*/
  int xBlack, yBlack;                 /* 黒の着手位置*/
  int xWhite, yWhite;                 /* 白の着手位置*/
  int score;                          /* 黒地−白地  */

  /* 碁盤の初期化*/
  InitializeBoard( board );

  printf( "Sample Program Start\n" );

  xBlack = yBlack = 999;  /* 最初は以外の数字を書いておく*/
  xWhite = yWhite = 999;  /* 最初は以外の数字を書いておく*/

  /* アゲハマ*/
  black_prisoner = 0;
  white_prisoner = 0;

  /* 手数*/
  move = 1;

  /* 終局するまでループ*/
  while( 1 ){

    /*------*/
    /* 黒番*/
    /*------*/

    /* 碁盤の表示*/
    DisplayBoard( board );

    /* 黒の着手位置を入力*/
    ThinkMove( board, BLACK, &xBlack, &yBlack );

    /* 黒が投了なら白の勝ち*/
    if( xBlack >= (BOARD_SIZE-1) || yBlack >= (BOARD_SIZE-1) ){
      printf( "Black Resign. White Win.\n" );
      break;
    }

    /* 黒白ともにパスなら地を数えて勝者を表示*/
    if( ( xBlack < 1 || yBlack < 1 ) && ( xWhite < 1 || yWhite < 1 ) ){
      score = CountScore();
      if( score > 0 ){
        printf( "Black Win\n" );    /* 黒が多ければ黒の勝ち*/
      }else if( score < 0 ){
        printf( "White Win\n" );    /* 黒が少なければ白の勝ち*/
      }else{
        printf( "Draw\n" );         /* 同数ならば引分け*/
      }
      break;
    }

    /* 座標が〜なら碁盤に石を置く*/
    SetStone( board, BLACK, xBlack, yBlack );

    /* 棋譜に記録*/
    RecordMove( BLACK, xBlack, yBlack );

    /* 手数が増える*/
    move++;

    /*------*/
    /* 白番*/
    /*------*/

    /* 碁盤の表示*/
    DisplayBoard( board );

    /* 白の着手位置を入力*/
    ThinkMove( board, WHITE, &xWhite, &yWhite );

    /* 白が投了なら黒の勝ち*/
    if( xWhite >= (BOARD_SIZE-1) || yWhite >= (BOARD_SIZE-1) ){
      printf( "White Resign. Black Win.\n" );
      break;
    }

    /* 黒白ともにパスなら地を数えて勝者を表示*/
    if( ( xBlack < 1 || yBlack < 1 ) && ( xWhite < 1 || yWhite < 1 ) ){
      score = CountScore();
      if( score > 0 ){
        printf( "Black Win\n" );    /* 黒が多ければ黒の勝ち*/
      }else if( score < 0 ){
        printf( "White Win\n" );    /* 黒が少なければ白の勝ち*/
      }else{
        printf( "Draw\n" );         /* 同数ならば引分け*/
      }
      break;
    }

    /* 座標が〜なら碁盤に石を置く*/
    SetStone( board, WHITE, xWhite, yWhite );

    /* 棋譜に記録*/
    RecordMove( WHITE, xWhite, yWhite );

    /* 手数が増える*/
    move++;
  }

  printf( "Sample Program End\n" );
  return EXIT_SUCCESS;
}

/*------------------------------------------------------------------*/
/* 碁盤の初期化                                                    */
/*------------------------------------------------------------------*/
void
InitializeBoard( int board[][BOARD_SIZE] )
{
  int x, y;

  for( y=1; y < (BOARD_SIZE-1); y++ ){
    for( x=1; x < (BOARD_SIZE-1); x++ ){
      board[y][x] = SPACE;
    }
  }

  for( y=0; y < BOARD_SIZE; y++ ){
    board[y][0]= OB;
    board[y][BOARD_SIZE-1] = OB;
    board[0][y] = OB;
    board[BOARD_SIZE-1][y] = OB;
  }
}

/*------------------------------------------------------------------*/
/* 碁盤を表示する                                                  */
/*------------------------------------------------------------------*/
void
DisplayBoard( int board[][BOARD_SIZE] )
{
  int x, y;

  printf( "    [ 1 2 3 4 5 6 7 8 910111213141516171819]\n" );
  for( y=1; y < (BOARD_SIZE-1); y++ ){
    printf( "[%2d] ", y );
    for( x=1; x < (BOARD_SIZE-1); x++ ){
      printf( " %c", stone[board[y][x]] );
    }
    printf( "\n" );
  }
  printf( "\n" );
}

/*------------------------------------------------------------------*/
/* 着手位置の入力                                                  */                        */
/*------------------------------------------------------------------*/
void
ThinkMove( int board[][BOARD_SIZE], int color, int *x, int *y )
{
  int inputX, inputY;

  while( 1 ){
    /* 着手位置の入力*/
    InputCoordinate( color, &inputX, &inputY );

    if( inputX > 0 && inputX < (BOARD_SIZE-1) &&
      inputY > 0 && inputY < (BOARD_SIZE-1) ){
      /* 座標が〜ならば合法手がどうか調べる*/
      if( CheckLegal( board, color, inputX, inputY ) == TRUE ){
        break;
      }
    }else{
      break;
    }
  }

  *x = inputX;
  *y = inputY;
}

/*------------------------------------------------------------------*/
/* 画面から数値を入力                                              */
/* X座標が〜ならばY座標も入力する                               */
/* X座標がより小さければパスとみなし, Y座標は入力しない           */
/* X座標がより大きければ投了とみなし, Y座標は入力しない          */
/*------------------------------------------------------------------*/
void
InputCoordinate( int color, int *x, int *y )
{
  int val;
  char str[256];

  printf( "\n" );
  if( color == BLACK ){
    printf( "Please Input Black Coordinates.\n" );
  }else{
    printf( "Please Input White Coordinates.\n" );
  }
  printf( " Pass -> 0, Quit -> 20\n" );

  /* X座標の入力*/
  while( 1 ){
    printf( "InputX:" );
    fflush( stdout );
    val = scanf( "%s", str );
    *x = atoi(str);
    if( val == 1 ){
      break;
    }
  }

  if( *x >= 1 && *x < (BOARD_SIZE-1) ){
    /* X座標が〜なのでY座標の入力*/
    while( 1 ){
      printf( "InputY:" );
      fflush( stdout );
      val = scanf( "%s", str );
      *y = atoi(str);
      if( val == 1 ){
        break;
      }
    }
  }else if( *x < 1 ){
    /* X座標がより小さいのでパスとみなし, Y座標は入力しない*/
    *y = 0;
  }else{
    /* X座標がより大きいので投了とみなし, Y座標は入力しない*/
    *y = 20;
  }
}

/*------------------------------------------------------------------*/
/* 合法手かどうか調べる                                            */
/*------------------------------------------------------------------*/
int
CheckLegal( int board[][BOARD_SIZE], int color, int x, int y )
{
  /* 空点じゃないと置けません*/
  if( board[y][x] != SPACE ){
    return( FALSE );
  }

  /* 一手前に劫を取られていたら置けません*/
  if( move > 1 ){
    if( ko_x == x && ko_y == y && ko_num == (move-1) ){
      return( FALSE );
    }
  }

  /* 自殺手なら置けません*/
  if( CheckSuicide( board, color, x, y ) == TRUE ){
    return( FALSE );
  }

  /* 以上のチェックをすべてクリアできたので置けます*/
  return( TRUE );
}

/*------------------------------------------------------------------*/
/* 自殺手かどうか調べる                                            */
/*------------------------------------------------------------------*/
int
CheckSuicide( int board[][BOARD_SIZE], int color, int x, int y )
{
  int rtnVal;
  int opponent;  /* 相手の色*/

  /* 仮に石を置く*/
  board[y][x] = color;

  /* マークのクリア*/
  ClearCheckBoard();
  
  /* その石は相手に囲まれているか調べる*/
  rtnVal = DoCheckRemoveStone( board, color, x, y );

  /* 囲まれているならば自殺手の可能性あり*/
  if( rtnVal == TRUE ){

    /* 相手の色を求める*/
    opponent = reverseColor( color );

    /* その石を置いたことにより, 隣の相手の石が取れるなら自殺手ではない*/
    if( x > 1 ){
      /* 隣は相手?*/
      if( board[y][x-1] == opponent ){
        /* マークのクリア*/
        ClearCheckBoard();
        /* 相手の石は囲まれているか?*/
        rtnVal = DoCheckRemoveStone( board, opponent, x-1, y );
        /* 相手の石を取れるので自殺手ではない*/
        if( rtnVal == TRUE ){
          /* 盤を元に戻す*/
          board[y][x] = SPACE;
          return( FALSE );
        }
      }
    }
    if( y > 1 ){
      /* 隣は相手?*/
      if( board[y-1][x] == opponent ){
        /* マークのクリア*/
        ClearCheckBoard();
        /* 相手の石は囲まれているか?*/
        rtnVal = DoCheckRemoveStone( board, opponent, x, y-1 );
        /* 相手の石を取れるので自殺手ではない*/
        if( rtnVal == TRUE ){
          /* 盤を元に戻す*/
          board[y][x] = SPACE;
          return( FALSE );
        }
      }
    }
    if( x < (BOARD_SIZE-2) ){
      /* 隣は相手?*/
      if( board[y][x+1] == opponent ){
        /* マークのクリア*/
        ClearCheckBoard();
        /* 相手の石は囲まれているか?*/
        rtnVal = DoCheckRemoveStone( board, opponent, x+1, y );
        /* 相手の石を取れるので自殺手ではない*/
        if( rtnVal == TRUE ){
          /* 盤を元に戻す*/
          board[y][x] = SPACE;
          return( FALSE );
        }
      }
    }
    if( y < (BOARD_SIZE-2) ){
      /* 隣は相手?*/
      if( board[y+1][x] == opponent ){
        /* マークのクリア*/
        ClearCheckBoard();
        /* 相手の石は囲まれているか?*/
        rtnVal = DoCheckRemoveStone( board, opponent, x, y+1 );
        /* 相手の石を取れるので自殺手ではない*/
        if( rtnVal == TRUE ){
          /* 盤を元に戻す*/
          board[y][x] = SPACE;
          return( FALSE );
        }
      }
    }

    /* 盤を元に戻す*/
    board[y][x] = SPACE;

    /* 相手の石を取れないなら自殺手*/
    return( TRUE );

  }else{

    /* 盤を元に戻す*/
    board[y][x] = SPACE;

    /* 囲まれていないので自殺手ではない*/
    return( FALSE );
  }
}

/*------------------------------------------------------------------*/
/* チェック用の碁盤をクリア                                        */
/*------------------------------------------------------------------*/
void
ClearCheckBoard( void )
{
  int x, y;

  for( y=1; y < (BOARD_SIZE-1); y++ ){
    for( x=1; x < (BOARD_SIZE-1); x++ ){
      checkBoard[y][x] = FALSE;
    }
  }
}

/*------------------------------------------------------------------*/
/* 座標(x,y)にあるcolor石が相手に囲まれているか調べる              */
/*------------------------------------------------------------------*/
int     /* 空点があればFALSEを返し, 空点がなければTRUEを返す*/
DoCheckRemoveStone( int board[][BOARD_SIZE], int color, int x, int y )
{
  int rtn;

  /* その場所は既に調べた点ならおしまい*/  
  if( checkBoard[y][x] == TRUE ){
    return( TRUE );
  }
  
  /* 調べたことをマークする*/
  checkBoard[y][x] = TRUE;

  /* 何も置かれていないならばおしまい*/
  if( board[y][x] == SPACE ){
    return( FALSE );
  }

  /* 同じ色の石ならばその石の隣も調べる*/  
  if( board[y][x] == color ){
    /* その石の左(x-1,y)を調べる*/
    if( x > 1 ){
      rtn = DoCheckRemoveStone( board, color, x-1, y );
      if( rtn == FALSE ){
        return( FALSE );
      }
    }
    /* その石の上(x,y-1)を調べる*/
    if( y > 1 ){
      rtn = DoCheckRemoveStone( board, color, x, y-1 );
      if( rtn == FALSE ){
        return( FALSE );
      }
    }
    /* その石の右(x+1,y)を調べる*/
    if( x < (BOARD_SIZE-2) ){
      rtn = DoCheckRemoveStone( board, color, x+1, y );
      if( rtn == FALSE ){
        return( FALSE );
      }
    }
    /* その石の下(x,y+1)を調べる*/
    if( y < (BOARD_SIZE-2) ){
      rtn = DoCheckRemoveStone( board, color, x, y+1 );
      if( rtn == FALSE ){
        return( FALSE );
      }
    }
  }

  /* 相手の色の石があった*/  
  return( TRUE );
}

/*------------------------------------------------------------------*/
/* 勝敗の判定                                                      */
/* このプログラムでは地を数えていません                            */
/* アゲハマの多い方を勝ちと判定しています                          */
/*------------------------------------------------------------------*/
int
CountScore( void )
{
  int black_score;
  int white_score;

  /* 碁盤上の地を数えるべきところだが省略*/
  black_score = 0;
  white_score = 0;

  /* アゲハマを加える*/
  black_score += black_prisoner;
  white_score += white_prisoner;

  /* 黒−白を返す*/
  return( black_score - white_score );
}

/*------------------------------------------------------------------*/
/* 碁盤に石を置く                                                  */
/*------------------------------------------------------------------*/
void
SetStone( int board[][BOARD_SIZE], int color, int x, int y )
{
  int prisonerN;  /* 取り除かれた石の数(上)*/
  int prisonerE;  /* 取り除かれた石の数(右)*/
  int prisonerS;  /* 取り除かれた石の数(下)*/
  int prisonerW;  /* 取り除かれた石の数(左)*/
  int prisonerAll;  /* 取り除かれた石の総数*/
  int koFlag;     /* 劫かどうか*/

  /* 座標(x,y)に石を置く*/
  board[y][x] = color;

  /* 置いた石は相手に囲まれているか*/
  if( IsColorOrOut( board, x, y-1, reverseColor( color ) ) == TRUE &&
      IsColorOrOut( board, x-1, y, reverseColor( color ) ) == TRUE &&
      IsColorOrOut( board, x+1, y, reverseColor( color ) ) == TRUE &&
      IsColorOrOut( board, x, y+1, reverseColor( color ) ) == TRUE ){
    /* 囲まれているので劫かもしれない*/
    koFlag = TRUE;
  }else{
    /* 囲まれていないので劫ではない*/
    koFlag = FALSE;
  }

  /* 取り除かれた石の数*/
  prisonerN = prisonerE = prisonerS = prisonerW = 0;

  /* 置いた石の周囲の相手の石が死んでいれば碁盤から取り除く*/
  if( y > 1 ){
    prisonerN = RemoveStone( board, color, x, y-1 );
  }
  if( x > 1 ){
    prisonerW = RemoveStone( board, color, x-1, y );
  }
  if( y < (BOARD_SIZE-2) ){
    prisonerS = RemoveStone( board, color, x, y+1 );
  }
  if( x < (BOARD_SIZE-2) ){
    prisonerE = RemoveStone( board, color, x+1, y );
  }

  /* 取り除かれた石の総数*/
  prisonerAll = prisonerN + prisonerE + prisonerS + prisonerW;

  /* 置いた石の隣に同じ色の石がなく, 取り除かれた石も1つならば劫*/
  if( koFlag == TRUE && prisonerAll == 1 ){
    /* 劫の発生した手数を覚える*/
    ko_num = move;
    /* 劫の座標を覚える*/
    if( prisonerE == 1 ){
      /* 取り除かれた石が右*/
      ko_x = x+1;
      ko_y = y;
    }else if( prisonerS == 1 ){
      /* 取り除かれた石が下*/
      ko_x = x;
      ko_y = y+1;
    }else if( prisonerW == 1 ){
      /* 取り除かれた石が左*/
      ko_x = x-1;
      ko_y = y;
    }else if( prisonerN == 1 ){
      /* 取り除かれた石が上*/
      ko_x = x;
      ko_y = y-1;
    }
  }

  /* アゲハマの更新*/
  if( prisonerAll > 0 ){
    if( color == BLACK ){
      black_prisoner += prisonerAll;
    }else if( color == WHITE ){
      white_prisoner += prisonerAll;
    }
  }
}

/*------------------------------------------------------------------*/
/* board[y][x] == color または盤外ならばTRUEを返す                 */
/*------------------------------------------------------------------*/
int
IsColorOrOut( int board[][BOARD_SIZE], int x, int y, int color )
{
  if( x < 1 || x > (BOARD_SIZE-2) || y < 1 || y > (BOARD_SIZE-2) ){
    return( TRUE );
  }
  if( board[y][x] == color ){
    return( TRUE );
  }
  return( FALSE );
}

/*------------------------------------------------------------------*/
/* 座標(x,y)の石が死んでいれば碁盤から取り除く                     */
/*------------------------------------------------------------------*/
int    /* 碁盤から取り除かれた石数*/
RemoveStone( int board[][BOARD_SIZE], int color, int x, int y )
{
  int prisoner;  /* 取り除かれた石数*/

  /* 置いた石と同じ色なら取らない*/
  if( board[y][x] == color ){
    return( 0 );
  }

  /* 空点なら取らない*/
  if( board[y][x] == SPACE ){
    return( 0 );
  }

  /* マークのクリア*/
  ClearCheckBoard();

  /* 囲まれているなら取る*/
  if( DoCheckRemoveStone( board, board[y][x], x, y ) == TRUE ){
    prisoner = DoRemoveStone( board, board[y][x], x, y, 0 );
    return( prisoner );
  }

  return( 0 );
}

/*------------------------------------------------------------------*/
/* 座標(x,y)のcolor石を碁盤から取り除き, 取った石の数を返す        */
/*------------------------------------------------------------------*/
int   /* アゲハマ*/
DoRemoveStone( int board[][BOARD_SIZE],
               int color, int x, int y, int prisoner )
{
  /* 取り除かれる石と同じ色ならば石を取る*/
  if( board[y][x] == color ){

    /* 取った石の数を1つ増やす*/
    prisoner++;

    /* その座標に空点を置く*/
    board[y][x] = SPACE;

    /* 左を調べる*/
    if( x > 1 ){
      prisoner = DoRemoveStone( board, color, x-1, y, prisoner );
    }
    /* 上を調べる*/
    if( y > 1 ){
      prisoner = DoRemoveStone( board, color, x, y-1, prisoner );
    }
    /* 右を調べる*/
    if( x < (BOARD_SIZE-2) ){
      prisoner = DoRemoveStone( board, color, x+1, y, prisoner );
    }
    /* 下を調べる*/
    if( y < (BOARD_SIZE-2) ){
      prisoner = DoRemoveStone( board, color, x, y+1, prisoner );
    }
  }

  /* 取った石の数を返す*/
  return( prisoner );
}

/*------------------------------------------------------------------*/
/* 棋譜に記録                                                      */
/*------------------------------------------------------------------*/
void
RecordMove( int color, int x, int y )
{
  /* ここでは手までに制限*/
  if( move < 500 ){
    logData[move].color = color;
    logData[move].x     = x;
    logData[move].y     = y;
  }
}
/*---------------------- < end of program > ------------------------*/

[BACK]

[HOME]