自分は大学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; }
実行したらこんな感じ。
当時は途中で挫折して完成まではいっていませんが、今回は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; }
こんな感じ。
あと、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; }
こんな感じ。
最初やろうとしたことを、思い出して原点回帰することで、成長を実感できました。
よきかな、よきかな。
コメントを残す