processing

Processingで100行オセロ

More than 3 years have passed since last update.


Processingで100行オセロ

自分は大学3年の時、今からちょうど4年前くらいにプログラムを始めたわけですが、そのとき一番最初に自分に課した課題がProcessingでオセロを作るっていうとこでした。

その当時はif文もfor文も本を参考にしながら、しどろもどろなプログラムを組んでいたわけですが、あれから4年経ち、とうとう大学院を卒業した今、どれだけの実力がついたのか、オセロを作ってみました。

final int BSIZE = 100;

int[][] field;
boolean bBlacksTurn;

void setup(){
size(8*BSIZE,8*BSIZE);
bBlacksTurn = true;
field = new int[8][8];
for(int i=0; i<8; ++i){
for(int j=0; j<8; ++j){
if((i==3||i==4)&&(j==3||j==4)){
field[i][j] = ((i+j)%2==0)?1:-1; // initial stones;
}else{
field[i][j] = 0;
}
}
}
}

void draw(){
//draw field
background(0,160,0);
stroke(0);
for(int i=1; i<8; ++i){
line(i*BSIZE,0,i*BSIZE,height);
line(0, i*BSIZE, width, i*BSIZE);
}
noStroke();
fill(0);
ellipse(BSIZE*2,BSIZE*2,10,10);
ellipse(BSIZE*6,BSIZE*2,10,10);
ellipse(BSIZE*2,BSIZE*6,10,10);
ellipse(BSIZE*6,BSIZE*6,10,10);

// draw stones
noStroke();
for(int i=0; i<8; ++i){
for(int j=0; j<8; ++j){
if(field[i][j]==1){
fill(0);
ellipse((i*2+1)*BSIZE/2,(j*2+1)*BSIZE/2, BSIZE*0.8, BSIZE*0.8);
}else if(field[i][j]==-1){
fill(255);
ellipse((i*2+1)*BSIZE/2,(j*2+1)*BSIZE/2, BSIZE*0.8, BSIZE*0.8);
}
}
}
}

void mouseReleased(){
int x = mouseX/BSIZE;
int y = mouseY/BSIZE;

boolean puttable = false;
if(field[x][y]==0){
puttable = checkDirection(x,y,-1,-1) | puttable;
puttable = checkDirection(x,y,-1,0) | puttable;
puttable = checkDirection(x,y,-1,1) | puttable;

puttable = checkDirection(x,y,0,-1) | puttable;
puttable = checkDirection(x,y,0,1) | puttable;

puttable = checkDirection(x,y,1,-1) | puttable;
puttable = checkDirection(x,y,1,0) | puttable;
puttable = checkDirection(x,y,1,1) | puttable;

if(puttable){
field[x][y] = currentStone();
bBlacksTurn = !bBlacksTurn;
}
}
}

boolean checkDirection(int x, int y, int directionX, int directionY){
if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY] != currentStone()){
return checkStones(x, y, directionX, directionY);
}
return false;
}

boolean checkStones(int x, int y, int directionX, int directionY){
if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY]==currentStone()){ // find
return true;
}else if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY]==0){ // not find
return false;
}else if(checkBound(x+directionX, y+directionY) && checkStones(x+directionX, y+directionY, directionX, directionY)){
field[x+directionX][y+directionY] = currentStone(); // reverse
return true;
}else{
return false;
}
}

boolean checkBound(int x, int y){
return x>=0 && x<8 && y>=0 && y<8;
}

int currentStone(){
return (bBlacksTurn)?1:-1;
}

実行したらこんな感じ。

Processingで作ったオセロ

当時は途中で挫折して完成まではいっていませんが、今回は30分くらいで作れました。前は、ゴリ押しでひっくり返す判定をしていた部分も再帰を使って美しく書けました。

さらに改造して、色数も盤面のサイズも自由に設定できるようにしました。

final int BSIZE = 50;

final int COLS = 16, ROWS = 16;
final int PLAYER_NUM = 8;
int[][] field;
int currentColor;

void setup(){
size(COLS*BSIZE,ROWS*BSIZE);
colorMode(HSB);
currentColor = 1;
field = new int[COLS][ROWS];
for(int i=0; i<COLS; ++i){
for(int j=0; j<ROWS; ++j){
field[i][j] = 0;
}
}
for(int i=0; i<PLAYER_NUM; ++i){
for(int j=0; j<PLAYER_NUM; ++j){
field[floor(COLS/2)+i-floor(PLAYER_NUM/2)][floor(ROWS/2)+j-floor(PLAYER_NUM/2)] = ((i+j)%PLAYER_NUM)+1;
}
}
}

void draw(){
//draw field
background(0);
stroke(64);
for(int i=1; i<COLS; ++i) line(i*BSIZE,0,i*BSIZE,height);
for(int i=1; i<ROWS; ++i) line(0, i*BSIZE, width, i*BSIZE);

// draw stones
noStroke();
for(int i=0; i<COLS; ++i){
for(int j=0; j<ROWS; ++j){
for(int k=1; k<=PLAYER_NUM; ++k){
if(field[i][j]==k){
fill(255*(k-1)/PLAYER_NUM,255,255);
ellipse((i*2+1)*BSIZE/2,(j*2+1)*BSIZE/2, BSIZE*0.8, BSIZE*0.8);
}
}
}
}
}

void mouseReleased(){
int x = mouseX/BSIZE;
int y = mouseY/BSIZE;

boolean puttable = false;
if(field[x][y]==0){
puttable = checkDirection(x,y,-1,-1) | puttable;
puttable = checkDirection(x,y,-1,0) | puttable;
puttable = checkDirection(x,y,-1,1) | puttable;

puttable = checkDirection(x,y,0,-1) | puttable;
puttable = checkDirection(x,y,0,1) | puttable;

puttable = checkDirection(x,y,1,-1) | puttable;
puttable = checkDirection(x,y,1,0) | puttable;
puttable = checkDirection(x,y,1,1) | puttable;

if(puttable){
field[x][y] =currentColor;
currentColor = (currentColor==PLAYER_NUM)?1:currentColor+1;
}
}
}

boolean checkDirection(int x, int y, int directionX, int directionY){
if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY] != currentColor&&field[x+directionX][y+directionY] != 0){
return checkStones(x, y, directionX, directionY);
}
return false;
}

boolean checkStones(int x, int y, int directionX, int directionY){
if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY]==currentColor){ // find
return true;
}else if(checkBound(x+directionX, y+directionY) && field[x+directionX][y+directionY]==0){ // not find
return false;
}else if(checkBound(x+directionX, y+directionY) && checkStones(x+directionX, y+directionY, directionX, directionY)){
field[x+directionX][y+directionY] = currentColor; // reverse
return true;
}else{
return false;
}
}

boolean checkBound(int x, int y){
return x>=0 && x<COLS && y>=0 && y<ROWS;
}

こんな感じ。

n色オセロ

あと、3Dオセロも作ってみた。操作感が超悪い。

final int BSIZE = 40;

final int COLS = 8, ROWS = 8, DEPTH = 8;
final int PLAYER_NUM = 2;
int[][][] field;
int currentColor;
int x=0, y=0, z=0;

void setup() {
size(COLS*BSIZE*4, ROWS*BSIZE*4, P3D);
colorMode(HSB);
frameRate(10);
currentColor = 1;
field = new int[COLS][ROWS][DEPTH];
for (int i=0; i<COLS; ++i) {
for (int j=0; j<ROWS; ++j) {
for (int k=0; k<DEPTH; ++k) {
field[i][j][k] = 0;
}
}
}
for (int i=0; i<PLAYER_NUM; ++i) {
for (int j=0; j<PLAYER_NUM; ++j) {
for (int k=0; k<PLAYER_NUM; ++k)
field[floor(COLS/2)+i-floor(PLAYER_NUM/2)][floor(ROWS/2)+j-floor(PLAYER_NUM/2)][floor(DEPTH/2)+k-floor(PLAYER_NUM/2)] = ((i+j+k)%PLAYER_NUM)+1;
}
}
}

void draw() {
// directionalLight(204, 204, 204, -3, -5, -1);

lights();
pushMatrix();
translate(COLS*BSIZE/2*4, ROWS*BSIZE/2*4, DEPTH*BSIZE/2*2);
rotateX(radians(-20.0));
rotateY(frameCount / 50.0);
translate(-COLS*BSIZE/2, -ROWS*BSIZE/2, -DEPTH*BSIZE/2);
//draw field
background(0);
stroke(255, 64);
for (int k=0; k<=DEPTH; ++k) {
for (int i=0; i<=COLS; ++i) {
line(i*BSIZE, 0, k*BSIZE, i*BSIZE, ROWS*BSIZE, k*BSIZE);
}
for (int j=0; j<=ROWS; ++j) {
line(0, j*BSIZE, k*BSIZE, COLS*BSIZE, j*BSIZE, k*BSIZE);
}
}
for (int i=0; i<=COLS; ++i) {
for (int j=0; j<=ROWS; ++j) {
line(i*BSIZE, j*BSIZE, 0, i*BSIZE, j*BSIZE, DEPTH*BSIZE);
}
}
// draw stones
noStroke();
for (int i=0; i<COLS; ++i) {
for (int j=0; j<ROWS; ++j) {
for (int k=0; k<DEPTH; ++k) {
for (int l=1; l<=PLAYER_NUM; ++l) {
if (field[i][j][k]==l) {
fill(255*(l-1)/PLAYER_NUM, 255, 255);
pushMatrix();
translate((i*2+1)*BSIZE/2, (j*2+1)*BSIZE/2, (k*2+1)*BSIZE/2);
sphere(BSIZE*0.3);
popMatrix();
}
}
}
}
}
pushMatrix();
translate((x*2+1)*BSIZE/2, (y*2+1)*BSIZE/2, (z*2+1)*BSIZE/2);
stroke(255*(currentColor-1)/PLAYER_NUM, 255, 255);
noFill();
box(BSIZE);
popMatrix();
popMatrix();
}

void keyReleased() {
if (key==' ') {
boolean puttable = false;
if (field[x][y][z]==0) {
for (int i=-1; i<=1; ++i) {
for (int j=-1; j<=1; ++j) {
for (int k=-1; k<=1; ++k) {
if (!(i==0 && j==0 && k==0)) puttable = checkDirection(x, y, z, i, j, k) | puttable;
}
}
}
if (puttable) {
field[x][y][z] =currentColor;
currentColor = (currentColor==PLAYER_NUM)?1:currentColor+1;
}
}
}
else if (key=='x') y = ( y+1==ROWS ) ? 0: y+1;
else if (key=='e') y = ( y-1<0 ) ? ROWS-1: y-1;
else if (key=='a') x = ( x+1==ROWS ) ? 0: x+1;
else if (key=='d') x = ( x-1<0 ) ? ROWS-1: x-1;
else if (key=='w') z = ( z+1==ROWS ) ? 0: z+1;
else if (key=='z') z = ( z-1<0 ) ? ROWS-1: z-1;
}

boolean checkDirection(int x, int y, int z, int directionX, int directionY, int directionZ) {
if (checkBound(x+directionX, y+directionY, z+directionZ) && field[x+directionX][y+directionY][z+directionZ] != currentColor&&field[x+directionX][y+directionY][z+directionZ] != 0) {
return checkStones(x, y, z, directionX, directionY, directionZ);
}
return false;
}

boolean checkStones(int x, int y, int z, int directionX, int directionY, int directionZ) {
if (checkBound(x+directionX, y+directionY, z+directionZ) && field[x+directionX][y+directionY][z+directionZ]==currentColor) { // find
return true;
}
else if (checkBound(x+directionX, y+directionY, z+directionZ) && field[x+directionX][y+directionY][z+directionZ]==0) { // not find
return false;
}
else if (checkBound(x+directionX, y+directionY, z+directionZ) && checkStones(x+directionX, y+directionY, z+directionZ, directionX, directionY, directionZ)) {
field[x+directionX][y+directionY][z+directionZ] = currentColor; // reverse
return true;
}
else {
return false;
}
}

boolean checkBound(int x, int y, int z) {
return x>=0 && x<COLS && y>=0 && y<ROWS && z>=0 && z<DEPTH;
}

こんな感じ。

3Dオセロ

最初やろうとしたことを、思い出して原点回帰することで、成長を実感できました。

よきかな、よきかな。