自分は大学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;
}
プログラミングを初めて学んだころの課題を再チャレンジすると、成長を実感できます。
皆さんも、数年おきに過去にチャレンジした課題を今ならどう作るかチャレンジしてみてはいかがでしょうか?