コンピュータとオセロ対戦7　～クリック操作でランダム対戦～

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f

[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)

# 今回の目標
Processingで、ランダムに返してくるコンピュータとオセロ対戦ができるようにする！
# ここから本編
せっかくなので、プレイヤーが白をするか黒をするかを選べるようにしたいと思います。
また、[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)はプレイヤー同士の対戦だったのでどちらのターンでも基本動作は変わらなかったのですが、今回はコンピュータ対プレイヤーなので前回とはやや違うプログラムになりました。
プログラムについて、プログラムの拡張子を「.c」で表示していますが使用言語はProcessingです。「.pde」だと文字色が変わらないためです。

## 方針
上述した通り、プレイヤー同士の対戦ではなくなったため、どちらのターンでも同じプログラムで、というのができなくなりました。[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)のプログラムのmousePressed関数をいじるだけでは「二回連続で片方のプレイヤーしか置けない」という状況になった時の対応が難しいためです。めったにないけどね。
対策として、check関数とcheck_all関数が[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)と比べ引数が増えました。
前回はそのターンで置けるかどうか？　を調べる関数でしたが、今回は指定したターンで置けるかどうか？　を調べる関数になりました。ターン指定を引数により行います。
これを使い、詳しいことは後述しますがゲーム終了の検知方法を変えることにしました。また、賢い方法ではないですがコンピュータとプレイヤーの対戦方法をC言語の時とは大きく変えました。これについても後述します。

## とりあえず上に書くやつ
settingとsetupの違いがよくわかりませんがとりあえず動くのでこのままいきます。プレイヤーが白をする場合、先攻はコンピュータなのでsetup関数にif文が追加されています。
「int PLAYER = ...」の文のコメントアウトをいじることで白と黒を変えます。
draw関数はまたあとで書きます。

```random.c
// #define
final int NONE = 0;
final int BLACK = 1;
final int WHITE = 2;
final int SIZE = 8;
final int COLT = 100;
final int GSIZE = COLT * (SIZE + 1);

int PLAYER = BLACK;
//int PLAYER = WHITE;

int[][] board = new int[SIZE][SIZE];
boolean turn = true;
boolean player;

void settings() {
  size(GSIZE, GSIZE);
  if (PLAYER == BLACK) player = true;
  else player = false;
}

void setup() {
  background(0);
  colorMode(RGB, 256);
  
  strokeWeight(2);
  fill(0, 150, 0);
  
  for (int i = 0; i < SIZE; i++){
    for (int j = 0; j < SIZE; j++){
      board[i][j] = NONE;
      rect(COLT / 2 + i * COLT, COLT / 2 + j * COLT, COLT, COLT);
    }
  }
  board[SIZE / 2 - 1][SIZE / 2 - 1] = WHITE;
  board[SIZE / 2][SIZE / 2] = WHITE;
  board[SIZE / 2 - 1][SIZE / 2] = BLACK;
  board[SIZE / 2][SIZE / 2 - 1] = BLACK;
  
  fill(255);
  textSize(30);
  textAlign(CENTER);
  turn_print("");
  
  printb();
  
  if (!player) {
    put_random();
    printb();
    turn = !turn;
    turn_print("");
  }
}
```

## put_random
ランダムに置くプログラム。
特筆すべきことはありません。

```random.c
void put_random(){
  int line = int(random(SIZE));
  int col = int(random(SIZE));
  
  while (!check(line, col, !player)){
    line = int(random(SIZE));
    col = int(random(SIZE));
  }
  
  put(line, col);
}
```
## count, turn_print
それぞれコンピュータとの対戦仕様になりました。
「3 - PLAYER」のアイデアは[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)も紹介した動画のパクリです。

```random.c
void put_random(){
  int line = int(random(SIZE));
  int col = int(random(SIZE));
  
  while (!check(line, col, !player)){
    line = int(random(SIZE));
    col = int(random(SIZE));
  }
  
  put(line, col);
}

void count(){
  int my = 0, your = 0;
  
  for (int i = 0; i < SIZE; i++){
    for (int j = 0; j < SIZE; j++){
      if (board[i][j] == PLAYER) your++;
      else if (board[i][j] == 3 - PLAYER) my++;
    }
  }
  
  fill(0);
  rect(0, 0, GSIZE, COLT / 2);
  fill(255);
  text("your: " + str(your) + ", my: " + str(my), GSIZE / 2 - COLT / 2, COLT / 3);
  if (your > my) text("you won!", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
  else if (your < my) text("I won!", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
  else text("draw", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
}

void turn_print(String string){
  fill(0);
  rect(0, 0, GSIZE, COLT / 2);
  fill(255);
  if (turn == player) text(string + "your turn", GSIZE / 2 - COLT / 2, COLT / 3);
  else text(string + "my turn", GSIZE / 2 - COLT / 2, COLT / 3);
}
```

## mousePressed
ついにmousePressedです。必然的に「プレイヤーが置く動作を実装する関数」となります。
流れは[前回](https://qiita.com/tt_and_tk/items/e00d61190833ef2a3ca7)のmousePressed関数の前半と同じで、

1. クリックされたとき、された場所を0からSIZE - 1の数字に直す
1. その場所に置けるか確かめる
1. 置けたら置いて盤面表示しターン反転
1. 次のターンで相手が置けるか調べる。置けるならターンを反転して相手のターン、置けないならもう一度自分のターン。

です。

```random.c
void mousePressed(){
  int line = (mouseY - COLT / 2) / COLT;
  int col = (mouseX - COLT / 2) / COLT;
  if (check(line, col, player)){
    put(line, col);
    printb();
    if (check_all(!player)){
      turn = !turn;
      turn_print("");
    }else{
      turn_print("not place. once ");
    }
  }
}
```
## draw
draw関数です。実質、「コンピュータが置く動作を実装する関数」です。
流れとしては

1. コンピュータのターンなら置き、盤面表示
1. プレイヤーが置けるならターン反転、置けないならもう一度自分のターン

また、その後のif文で「自分も相手も置けないならゲーム終了」という動作をさせています。
これで、

* 基本的に交互に打ち合う
* 置けない時はパスする
* 連続で置けない時は連続でパスする
* 双方ともに置けなくなったらゲーム終了

という、オセロで人間が当たり前にやっている動作が再現できました。

```random.c
void draw(){
  if (turn != player){
    put_random();
    printb();
    if (check_all(player)){
      turn = !turn;
      turn_print("");
    }else{
      turn_print("not place. once ");
    }
  }
  if (!(check_all(player) || check_all(!player))) count();
}
```
# フルバージョン
この中です。

https://github.com/TTaisei/osero
# 実際にやってみた
三回対戦して二回負けました。
# 次回は
C言語と同じように、1手先まで読むプログラムになると思います。

[次回](https://qiita.com/tt_and_tk/items/7cf1cf1c3e5127ee492d)

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f