前回は、リーチを判定する関数 reach() を作成しました。今回は○×ゲームを仕上げましょう。
コンピュータ側の一連の流れを、PCturn として関数化しましょう。コンピュータは、以下の戦略をとることとします。
- turn 1:中心が空いていれば中心をとる。空いていなければ左上(0,0)をとる
- turn 3 以降:リーチがあればリーチのマスをとる。この時、コンピュータ側のリーチを優先する。リーチがなければランダム
上記の後、勝利判定を行い、turn に 1 追加します。
void PCturn(){
if((turn % 2 == 1) && (turn < 9)){
if(turn == 1){
if(map[1][1] == 1){
drawX(1, 1);
map[1][1] = 3;
}
else{
drawX(0, 0);
map[0][0] = 3;
}
}
else {
if(reach()){
drawX(X, Y);
map[Y][X] = 3;
}
else randomCheck();
}
win();
turn ++;
}
}
上記のコードでは、 drawX(1, 1); のように、マスの座標を指定して × を書けるようにしています。これに合わせて、以前作成した drawX() 関数を以下のように修正します。
void drawX(int x, int y){
line(x*200, y*200, x*200+200, y*200+200);
line(x*200+200, y*200, x*200, y*200+200);
}
それではここまでを振り返りましょう。上記の PCturn と drawX 以外のタブの内容は以下の通りです。
メインタブ
int X, Y;
int map[][]={{1,1,1},{1,1,1},{1,1,1}};
int turn = 0;
void setup(){
size(600,600);
background(255);
frameRate(12);
strokeWeight(2);
smooth();
line(0, 200, width, 200);
line(0, 400, width, 400);
line(200, 0, 200, height);
line(400, 0, 400, height);
}
void draw(){
myturn();
PCturn();
}
myturnタブ
void myturn(){
if (turn % 2 == 0){
if(mousePressed == true){
X = mouseX / 200;
Y = mouseY / 200;
if(map[Y][X] == 1) {
fill(255);
ellipse(X*200+100, Y*200+100, 150, 150);
map[Y][X]=2;
win();
turn ++;
}
}
}
}
randomタブ
void randomCheck(){
do{
X = int(random(3));
Y = int(random(3));
}while(map[Y][X] != 1);
map[Y][X] = 3;
drawX(X, Y);
}
reachタブ
boolean reach(){
int line = 1;
// vertical reach
for (X = 0; X < 3; X++){
for (Y = 0; Y < 3; Y++){
line *= map[Y][X];
}
if(line == 9){
for (Y = 0; Y < 3; Y ++){
if(map[Y][X] == 1) return true;
}
} else if(line == 4){
for (Y = 0; Y < 3; Y ++){
if(map[Y][X] == 1) return true;
}
}
}
//horizontal reach
line = 1;
for (Y = 0; Y < 3; Y++){
for (X = 0; X < 3; X++){
line *= map[Y][X];
}
if(line == 9){
for (X = 0; X < 3; X ++){
if(map[Y][X] == 1) return true;
}
} else if (line == 4){
for (X = 0; X < 3; X ++){
if(map[Y][X] == 1) return true;
}
}
}
// cross reach (right-down)
line = map[0][0] * map[1][1] * map[2][2];
if(line == 9){
for(int i = 0; i < 3; i++){
if(map[i][i] == 1) {
X = i;
Y = i;
return true;
}
}
} else if (line == 4){
for(int i = 0; i < 3; i++){
if(map[i][i] == 1) {
X = i;
Y = i;
return true;
}
}
}
// cross reach (right-up)
line = map[2][0] * map[1][1] * map[0][2];
if(line == 9){
for(int i = 0; i < 3; i++){
if(map[2-i][i] == 1) {
X = i;
Y = 2 - i;
return true;
}
}
} else if(line == 4){
for(int i = 0; i < 3; i++){
if(map[2-i][i] == 1) {
X = i;
Y = 2 - i;
return true;
}
}
}
return false;
}
winタブ
void win(){
textSize(100);
fill(0);
textAlign(CENTER, CENTER);
if ((map[0][0]*map[0][1]*map[0][2] == 8) ||
(map[1][0]*map[1][1]*map[1][2] == 8) ||
(map[2][0]*map[2][1]*map[2][2] == 8) ||
(map[0][0]*map[1][0]*map[2][0] == 8) ||
(map[0][1]*map[1][1]*map[2][1] == 8) ||
(map[0][2]*map[1][2]*map[2][2] == 8) ||
(map[0][0]*map[1][1]*map[2][2] == 8) ||
(map[2][0]*map[1][1]*map[0][2] == 8)){
text("You WIN!", width/2, height/2);
noLoop();
}
else if ((map[0][0]*map[0][1]*map[0][2] == 27) ||
(map[1][0]*map[1][1]*map[1][2] == 27) ||
(map[2][0]*map[2][1]*map[2][2] == 27) ||
(map[0][0]*map[1][0]*map[2][0] == 27) ||
(map[0][1]*map[1][1]*map[2][1] == 27) ||
(map[0][2]*map[1][2]*map[2][2] == 27) ||
(map[0][0]*map[1][1]*map[2][2] == 27) ||
(map[2][0]*map[1][1]*map[0][2] == 27)){
text("You LOSE!", width/2, height/2);
noLoop();
}
else if (turn >= 8){
text("DRAW", width/2, height/2);
noLoop();
}
}
これで完成です。
リセットの機能をつけたり、色を変えてみたりしてもいいかもしれませんね。また何か作ってみたいと思います。