コンピュータとオセロ対戦6　～クリック操作でオセロ～

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

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

# 今回の目標
前回までの流れはあまり関係ありません。
友達から「やりづらい」と言われたので今回はクリック操作でオセロができるようにします。
C言語ではなくProcessingを使いますが、Processingは初挑戦なので間違っているところがあるかもしれません。
なお、今回のプログラムを書くにあたって、こちらの動画を参考にさせていただきました。

https://www.nicovideo.jp/watch/sm8391299

# ここから本編
プログラムの名前を「osero.c」としていますが、実際は「osero.pde」です。拡張子が「pde」だとなぜかqiitaでのプログラムの色が変わらないのでcと書いてます。
## とりあえず上に書くやつ
今回draw関数は何もしないので何も書きません。
C言語でのsetup関数と似たようなことをしています。
盤面に黒と白を二つずつ並べて表示してるだけです。
COLTは一つのマスの大きさ、GSIZEは全体の大きさです。上下左右にマスの二分の一の大きさの余白を作っているのでCOLT * （SIZE + 1）で計算してます。

```osero.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[][] board = new int[SIZE][SIZE];
boolean turn = true;

void settings() {
  size(GSIZE, GSIZE);
}

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);
    }
  }
  noStroke();

  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);
  text("black turn", GSIZE / 2 - COLT / 2, COLT / 3);
  
  printb();
}

void draw(){
  ;
}
```

## printb
盤面表示のプログラムです。
特筆することはないです。

```osero.c
void printb() {
  int i, j;
  
  for (i = 0; i < SIZE; i++) {
    for (j = 0; j < SIZE; j++) {
      if (board[i][j] == BLACK) {
        fill(0, 0, 0);
        ellipse((j + 1) * COLT, (i + 1) * COLT, COLT * 0.8, COLT * 0.8);
      }else if (board[i][j] == WHITE){
        fill(255, 255, 255);
        ellipse((j + 1) * COLT, (i + 1) * COLT, COLT * 0.8, COLT * 0.8);
      }
    }
  }
}
```

## count
盤面の白と黒の数を数える関数。
こちらも特筆することはなし。

```osero.c
void count(){
  int black = 0, white = 0;
  
  for (int i = 0; i < SIZE; i++){
    for (int j = 0; j < SIZE; j++){
      if (board[i][j] == BLACK) black++;
      else if (board[i][j] == WHITE) white++;
    }
  }
  
  fill(0);
  rect(0, 0, GSIZE, COLT / 2);
  fill(255);
  text("black: " + str(black) + ", white: " + str(white), GSIZE / 2 - COLT / 2, COLT / 3);
  if (black > white) text("black won!", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
  else if (black < white) text("white won!", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
  else text("draw", GSIZE / 2 - COLT / 2, GSIZE - COLT / 6);
}
```

## check_all
置けるところがあるかをチェックする関数。
checkは「その場所に置けるかどうか」をチェックする関数です。

```osero.c
boolean check_all() {
  for (int i = 0; i < SIZE; i++)
    for (int j = 0; j < SIZE; j++)
      if (check(i, j)) return true;
  
  return false;
}
```

## turn_print
今どっちのターンなのか？　を表示する関数。

```osero.c
void turn_print(String string){
  fill(0);
  rect(0, 0, GSIZE, COLT / 2);
  fill(255);
  if (turn) text(string + "black turn", GSIZE / 2 - COLT / 2, COLT / 3);
  else text(string + "white turn", GSIZE / 2 - COLT / 2, COLT / 3);
}
```

## check
その場所に置けるか？　をチェックする関数。
上述した動画を参考に、C言語の時より短くしました。
調べる場所の上下左右八か所を調べるため3*3の9ループで回し、そのうち一回は何もせずスルーしています。

```osero.c
boolean check(int line, int col){
  if (line > SIZE || col > SIZE) return false;
  else if(line < 0 || col < 0) return false;
  else if(board[line][col] != NONE) return false;  

  int my, opp;
  int x_for, y_for;
  if (turn){
    my = BLACK; opp = WHITE;
  }else{
    my = WHITE; opp = BLACK;
  }
  for (int x = -1; x <= 1; x++){
    for (int y = -1; y <= 1; y++){
      if (x != 0 || y != 0){
        x_for = x; y_for = y;
        while (line + x_for + x < SIZE && line + x_for + x >= 0
        && col + y_for + y < SIZE && col + y_for + y >= 0
        && board[line + x_for][col + y_for] == opp){
          if (board[line + x_for + x][col + y_for + y] == my)
            return true;
          x_for += x; y_for += y;
        }
      }
    }
  }
  
  return false;
}
```

## put
実際に置く関数です。

```osero.c
void put(int line, int col){
  int my, opp;
  int x_for, y_for;
  if (turn){
    my = BLACK; opp = WHITE;
  }else{
    my = WHITE; opp = BLACK;
  }
  
  board[line][col] = my;
  
  for (int x = -1; x <= 1; x++){
    for (int y = -1; y <= 1; y++){
      if (x != 0 || y != 0){
        x_for = x; y_for = y;
        while (line + x_for + x < SIZE && line + x_for + x >= 0
        && col + y_for + y < SIZE && col + y_for + y >= 0
        && board[line + x_for][col + y_for] == opp){
          if (board[line + x_for + x][col + y_for + y] == my)
            for (int i = 1; i <= abs(x_for) || i <= abs(y_for); i++)
              board[line + i * x][col + i * y] = my;
          x_for += x; y_for += y;
        }
      }
    }
  }
}
```

## mousePressed
ついにメイン部分です。動作は以下の通り。

1. クリックされたとき、された場所を0からSIZE - 1の数字に直す
1. その場所に置けるか確かめる
1. 置けたら置いて盤面表示しターン反転
1. 次のターンで置ける場所があるか調べる。置けたら次のターンを指定。置けなかったら反転したターンを元に戻し、さっき置いた人がまた置けるかどうか確かめる。置けたらもう一度その人のターン、置けなかったらゲーム終了

文字だけでは分かりにくいので図とプログラムを載せます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/29f499d9-c7ee-a824-ff3c-f41bef19f014.png)

```osero.c
void mousePressed(){
  int line = (mouseY - COLT / 2) / COLT;
  int col = (mouseX - COLT / 2) / COLT;
  if (check(line, col)){
    put(line, col);
    printb();
    turn = !turn;
    if (check_all()){
      turn_print("");
    }else{
      turn = !turn;
      if (check_all()) turn_print("not place. again, ");
      else count();
    }
  }
}
```


# フルバージョン
いつも通りこの中に置いてます。

https://github.com/TTaisei/osero
# 実際にやってみる
後半戦になってくると駒のふちが反対の色になったりしましたが普通にプレイできました。
C言語の時よりきれいにプログラミングできたと思うので満足。

# 次回は
このプログラムを改造し、コンピュータと対戦できるようにしようと思います。まずはランダムから。
[次回](https://qiita.com/tt_and_tk/items/85b05b0eace25f31f73b)

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