シェルスクリプトマガジン

香川大学SLPからお届け!(Vol.85掲載)

著者:谷 知紘

 今回は、C言語を用いて作成した、コンピュータと対戦できるリバーシを紹介します。あまり強くはありませんが、きちんとコンピュータが相手をしてくれます。C99以降の規格に対応するCコンパイラと標準Cライブラリがあれば、環境を問わずに動作するプログラムになっていますので、皆さんもコンパイルして楽しんでください。

シェルスクリプトマガジン Vol.85は以下のリンク先でご購入できます。

図5 盤面の生成用コード

//盤面 空白(0) 黒(-1) 白(1) 番兵(2)
int board[10][10] = {0};
//スコアを格納する配列
int weightdata[10][10] = {
  {0,  0,  0,  0,  0,  0,  0,  0,  0,  0},
  {0, 30,-12,  0, -1, -1,  0,-12, 30,  0},
  {0,-12,-15, -3, -3, -3, -3,-15,-12,  0},
  {0,  0, -3,  0, -1, -1,  0, -3,  0,  0},
  {0, -1, -3, -1, -1, -1, -1, -3, -1,  0},
  {0, -1, -3, -1, -1, -1, -1, -3, -1,  0},
  {0,  0, -3,  0, -1, -1,  0, -3,  0,  0},
  {0,-12,-15, -3, -3, -3, -3,-15,-12,  0},
  {0, 30,-12,  0, -1, -1,  0,-12, 30,  0},
  {0,  0,  0,  0,  0,  0,  0,  0,  0,  0}
};
//盤面の生成
void make_board(){
  //番兵
  for(int i = 0; i < 10; i++){
    board[0][i] = 2;
    board[9][i] = 2;
    board[i][0] = 2;
    board[i][9] = 2;
  }
  //初期配置する石
  board[4][4] = 1;
  board[5][5] = 1;
  board[4][5] = -1;
  board[5][4] = -1;
}

図6 check_plc()関数のコード

//指定したマスに石を置けるかどうかを判定する関数
bool check_plc(int i, int j, int now_board[10][10]){
  //マスが空かどうか
  if(board[i][j] == 0){
    //全方向を探索
    for(int dir_i = -1; dir_i < 2; dir_i++){
      for(int dir_j = -1; dir_j < 2; dir_j++){
        if(check_dir(i,j,dir_i,dir_j,now_board)){
          //配置可能であればtrueを返す
          return true;
        }
      }
    }
  }
  return false;
}

図8 check_dir()関数のコード

//指定方向に何個の石を挟めるかを調べる関数
int check_dir(int i, int j,
              int dir_i, int dir_j,
              int now_board[10][10]){
  //指定方向に相手の石がある場合は次のマスを探索
  int times = 1;
  while(now_board[i+dir_i*times][j+dir_j*times] == player*-1){
    times++;
  }
  //指定方向の最後に自分の石がある場合
  if(now_board[i+dir_i*times][j+dir_j*times] == player){
    //指定方向に相手の石が何個あるかを返す
    return times-1;
  }
  //指定方向の最後に自分の石がなければ0を返す
  return 0;
}

図9 place_stn()関数のコード

//指定したマスに石を配置して、挟んだ石を自分の石にする関数
void place_stn(int i, int j, int now_board[10][10]){
  //全方向を調査
  for(int dir_i = -1; dir_i < 2; dir_i++){
    for(int dir_j = -1; dir_j < 2; dir_j++){
      //挟んだ石の数
      int change_num = check_dir(i,j,dir_i,dir_j,now_board);
      //挟んだ石の数だけ自分の石に変更
      for(int k = 1; k < change_num+1; k++){
        now_board[i+dir_i*k][j+dir_j*k] = player;
      }
    }
  }
  //指定したマスに自分の石を配置
  now_board[i][j] = player;
}

図10 think()関数のコード

//コンピュータの手番で呼び出される関数
void think(){
  //ハイスコアの初期化
  int hightscore = -1000;
  int px, py;
  for(int y = 0; y < 10; y++){
    for(int x = 0; x < 10; x++){
      //石を置けない場合はスキップ
      if(!check_plc(y, x, board)){
        continue;
      }
      int tmpdata[10][10] = {0};
      //盤面データをコピーした仮の盤面を作成
      copydata(tmpdata);
      //仮の盤面に石を置く
      place_stn(y, x, tmpdata);
      //総スコアを計算する
      int score = calcweight(tmpdata);
      //ハイスコアよりも総スコアが良ければ更新する
      if(score > hightscore){
        hightscore = score;
        px = x; py = y;
      }
    }
  }
  //総スコアが最大のマスに石を置く
  place_stn(py, px, board);
  printf("PCは(x , y) = (%d , %d)に置きました\n",
         px, py); 
}

図11 copydata()関数のコード

//盤面データをコピーする関数
void copydata(int tmpdata[10][10]){
  for(int y = 0; y < 10; y++){
    for(int x = 0; x < 10; x++){
      tmpdata[y][x] = board[y][x];
    }
  }
}

図12 calcweight()関数のコード

//総スコアを計算する関数
int calcweight(int tmpdata[10][10]){
  int score = 0;
  for(int y = 0; y < 10; y++){
    for(int x = 0; x < 10; x++){
      //番兵はスキップ
      if(tmpdata[y][x] == 2){
        continue;
      }
      //自分の石がある場所のスコアを足す
      if(tmpdata[y][x] == 1){
        score += weightdata[y][x];
      }
    }
  }
  return score;
}