Report8


課題
Java  Appletプログラミング
Java  Applet/AWTについて学び、Java  Appletのオリジナルプログ ラムを作成し解説せよ。

プログラム
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class minesw extends java.applet.Applet implements MouseListener{

        int xmax = 12;                  //列の数
	int ymax = 8;                   //行の数
	int xy_max = xmax * ymax;       //マスの数
	int sts[] = new int[xy_max];    //ステータス(0:None 1~8:open 9:bomb
                                        // 10:not open 11:check_bomb 12:check_not)
	int bmax = 15;                  //爆弾の数
	int square = 16;                //マス一辺の大きさ
	int margin = 20;                //端に寄らない為の余白
	int gend;                       //ゲームのフラグ

	Image offs;
	Graphics gf;

	public void init(){
	    
	   offs = createImage(300,300);
	   gf = offs.getGraphics();

	   addMouseListener(this);
	   gameInit();
	}

	public void paint(Graphics g) {
	   update(g);
	}

	public void update(Graphics g) {
	   int i, j;
	   int k;                          //マスの番号
	   int xx, yy;                     //マスの座標(左上)

	   gf.setColor(Color.green);       //背景の色
	   gf.gillRect(0,0,300,300);       //四角形(背景)の大きさ

	   /* リセットボタンの描写 */
	   gf.setColor(Color.gray);        //リセットボタンの色
	   gf.gillRect(20,180,60,20);      //リセットボタンの大きさと座標
	   gf.setColor(Color.black);       //文字の色
	   gf.drawString("RESET",23,193);  //文字の座標

	       /* マスの描写 */
+-	   for(i=0;i < ymax;i++){
|	      yy = i * squaer + margin;
|	      for(j=0; j < max ;j++){
|	         xx = j * square + margin;
|		 k = i * xmax + j;
|		 if(sts[k] >= 9){
|		    gf.setColor(Color.gray);
|		    gf.filRect(xx, yy, square, square);
|		    if(sts[k] > 10){
|		       gf.setColor(Color.yellow);
|		       gf.drawString(" ? ", xx+2, yy+13);
|		    }
|		 }
|		 else{
|		    gf.setColor(Color.white);
|		    gf.fillRect(xx, yy, square, square);
|		    if(sts[k] != 0){
|		       gf.setColor(Color.red);
|		       gf.drawString(" " + sts[k] + " ", xx+2, yy+13);
|		    }
|		 }
|                if(gend == 2 && (sts[k] == 9 || sts[k] == 11)){
|		    gf.setColor(Color.red);
|1		    gf.fillRect(xx, yy, square, square);
|		    gf.setColor(Color.black);
|		    gf.drawString(" * ", xx+2, yy+13);
|		 }
|		 gf.setColor(Color.black);
|		 gf.drawRect(xx, yy, square, square);
|             }   
|	   }
|	   if(gend == 1){
|	      gf.setColor(Color.yellow);
|	      gf.drawString(" Clear !! ", 100, 170);
|          }
|	   if(gend == 2){
|	      gf.setColor(Color.red);
|	      gf.drawString(" Game Over ", 100, 170);
|          }
|
|	   g.drawImage(offs,0,0,this);
+-      }   

        public void mousePressed(MouseEvent e){
           int ix, iy;
	   int row, col;
	   int k;

	   ix = e.getX();
	   iy = e.getY();

	   if(20 < ix && ix < 80 && 180 < iy && iy < 200) {
	      gameInti();
	      repaint();
	      return;
	   }

	   col = (ix - margin) / square;
	   row = (iy - margin) / square;
	   if(col < 0 || col >= xmax || row < 0 || row >= ymax){
	      return;
	   }

	   k = row * xmax + col;
	   if(( e.getModifiers() & MouseEvent.CTRL_MASK) != 0) {
	      if(sts[k] == 9 || sts[k] == 10){
	         sts[k] += 2;
	      }
	      else if(sts[k] == 11 || sts[k] == 12){
	         sts[k] -= 2;
	      }
	   }
	   else{
	      if(sts[k] >= 10){
	         sts[k] = bombCount(col.row);
		 if(sts[k] == 0) AroundOpen(col,row);
	      }
	      if(sts[k] == 9){
	         gend = 2;
		 }
	      }
	      endCheck();
	      reqaint();
           }

	   public void mouseReleased(MouseEvent e){

	   }
	   public void mouseClicked(MouseEvent e){
 
	   }
	   public void mouseEntered(MouseEvent e){

	   }
	   public void mouseExited(MouseEvent e){

	   }

	   public void gameInit(){
	      int i;
	      int j;
	      gend = 0;

	      for(i=0; i < xy_max; i++){
	         sts[i] = 10;
	      }
	      
	      i = 0;
	      while(i < bmax){
	         k = (int)(Math.random() * (float)xy_may);
		 if(sts[k] == 10){
		    sts[k] = 9;
		    i++;
		 }
	      }
	   }

	   public void bombCount(int px, int py){
	      int xy, cnt;
	      int i, j;

	      cnt = 0;
	      for(i=0; i<3; i++){
	         for(j=0; j < 3; j++){
		    if(!(i == 1 && j == 1)){
		       if(px+i-1 >= 0 && px+i-1 < xmax && py+j-1 >= 0 &&
	      py+j-1 < ymax){
	                  xy = (py+j-1) * xmax + (px+i-1);
			  if(sts[xy] >= 9){
			     sts[xy] = bombCount(px+i-1, py+j-1);
			     if(sts[xy] == 0) AroundOpen(px+i-1, py+j-1);
			  }
		       }
		    }
		 }
	      }
	   }

	   public void endCheck(){
	      int i, cnt;
	      
	      cnt = 0;
	      for(i=0; i < xy_max; i++){
	         if(sts[i] < 9) cnt++;
              }
	      if((cnt + bmax) == xy_max) gend = 1;
	   }
}
   

結果


考察
import  java.applet.Appletでjava.applet.Appletをインポートすること でアプレットの機能を使用可能にしている
import  java.awt.*でjava.awtをインポートすることでGUIを作成する為の AWTを使用可能にしている
import  java.awt.event.*でjava.awt.eventをインポートすることでAWTか らのイベントを処理できるようになっている
public  class  minesw  extends  java.applet.Applet  implements  MouseListenerは java.applet.Appletを継承して、MouseListenerを実装してマウスのイベント を処理出来るようにしている。
Image  offsはImageクラスのオブジェクトを作成
Graphics  gfはGraphicsクラスのオブジェクトを作成
public  void  init()はアプレットの初期設定
offs=createImage(300,300)は300×300の大きさのイメージを作り、オブジェク ト変数に代入している。
addMouseListener(this)はマウスリスナとして自分自身を登録している

public  void  paint(Graphics  g)はアプレットに画を描くメ ソッド
1の部分は升目を描くプログラム。変数iは変数ymaxに 対応していてiは列の数だけループする
yy  =  j*squaer+marginはマスを描く為のy座標を計算し、変数yy に代入している。
for(j=0; j < xmax; j++)のjはxmaxに対応していて、jは行の数だけループす る
xx  =  j*square+marginはマスを描く為のx座標を計算し、変数xx に代入している。
k  = i*xmax+jは左上から数えたマスの番号を計算している。
if(sts[k]  >=  9){
gf.setColor(Color.gray);
gf.fillRect(xx,yy,square,square);

はマス番号kのステータスが 9以下ならx座標xx,y座標yyの位置に幅、高さsquareの四角形を 作り、灰色で塗りつぶしなさいというもの
if(sts[k]  >  10){
gf.setColor(Color.yellow);
gf.drawString(" ? ", xx+2, yy+13);

はマス番号kのステータスが10より上ならマスの中に黄色で"?"を描きなさいと いうもの
else{
gf.setColor(Color.white);
gf.fillRext(xx,yy,square,squre);
if(sts[k] != 0){
gf.setColor(Color.red);
gf.drawString(" " + sts[k] + " ", xx+2,yy+13);
}
}

はマス番号が9より上ならx座標xx、y座標yyの位置に幅、高さsquareの四角形 を作り、白色で塗りつぶし、もしマス番号のステータスが0以 外の数字ならマスの中に赤でステータスの数字を描けというも の。これは周りに爆弾があるときの処理
if(gend == 2 && (sts[k] == 9 || sts[k] == 11)){
gf.setColor(Color.red);
gf.fillRect(xx, yy, square, square);
gf.setColor(Color.black);
gf.drawString(" * ", xx+2, yy+13);

はマス番号kのステータスが9か11、かつ、変数gendの値がが2ならマスを赤く 塗りつぶし、マスの中に"*"を黒く描けというもの。これは爆 弾を当ててしまったときの処理
gf.setColor(Color.black);
gf.drawRect(xx, yy, square, square);

はマスの周りの黒い枠を描いている
if(gend == 1){
gf.setColor(Color.yellow);
gf.drawString(" Clear !! ", 100, 170);

はクリアをしたときの処理。x座標100、y座標170に黄色で"Clear !!"と描く
if(gend == 2){
gf.setColor(Color.red);
gf.drawString(" Gave Over ", 100, 170);

は失敗してしまったときの処理。x座標100、y座標170に赤色で"Game Over"と 描く
public void mousePressed(MouseEvent e){
int ix, iy;
int row, col;
int k;
ix = e.getX();
iy = e.getY();
if(20 < ix && ix < 80 && 180 < iy && iy < 200){
gameInti();
repaint();
return;

mousePressde(MouseEvent e)メソッドはコンポーネント上でマウスボタンが押 されると呼び出される。
e.getX()、e.getY()でマウスが押された座標を変数ix、iyに代入している。も し、マウスが押されたX座標が20より多く、80より少なく、かつ、Y座標 が180より多く、200より少なかったら次のgameInit()、repaint()メソッ ドを実行する。この処理はリセットボタンが押されたときの処理。
col = (ix - margin) / square;
row = (iy - margin) / square;
if(col < 0 || col >= xmax || row < 0 || row >= ymax){
return;
}

colは押された座標からmarginを引いて四角の枠の大きさで割ることでどのx列 が押されたかを把握している。
rowも同じで押された座標からmarginを引いて四角の枠の大きさで割ることで どのy行が押されたかを把握している。
if(col < 0 || col >= xmax || row < 0 || row >= ymax)は押された座標がマ スの中で無いなら何もしないというもの
k = row * xmax + col;
if((g.getModifiers() & MouseEvent.CTRL_MASK) != 0){
if(sts[k] == 9 || sts[k] == 10){
sts[k] += 2;
}
else if(sts[k] == 11 || sts[k] == 12){
sts[k] -= 2;
}

kはマスの番号
if((e.getModifiers() & MouseEvent.CTRL_MASK) != 0){はCTRLボタンを押し ながらマウスをクリックした場合、マス番号kのステータスが9、10だっ た場合にステータスに2を加える。もし、ステータスが11、12ならステー タスに2を引く
else{
if(sts[k] >= 10){
sts[k] = bombCount(col,row);
if(sts[k] == 0) AroundOpen(col,row);
}
if(sts[k] == 9){
gend = 2;
}
endCheck();
repaint();
}

CTRLキーとマウスが一緒に押されなかった場合で、マス番号kのステータスが 10以上だった場合、変数colと変数rowを引数にしてbombCountメソッド を実行。bombCountメソッドの戻り値は変数colと変数rowが指すマスの 周りの爆弾の数。マス番号kのステータスに0が入ったのなら、変数col と変数rowを引数にしてAroundOpenメソッドを実行。AroundOpenメソッ ドは変数colと変数rowが指すマスの周りのマスを調べるメソッド
もし、マス番号kのステータスの値が9なら変数gendに2を入れる
public void gameInit(){
int i;
int k;
gend = 0;
for(i = 0; i < xy_max; i++){
sts[i] = 10;
i = 0;
while(i < bmax){
k = (int)(Math.random()*(float)xy_max);
if(sts[k] == 10){
sts[k] = 9;
i++;
}
}
}

これはゲームの初期処理。ゲームフラグである変数gendに0を入れて初期化し ている。
for文では変数配列stsの全てに10を入れて初期化している。
次のwhile文では変数kをランダムに選んで、マス番号kのステータスを9にして いる。ステータス9は爆弾が入っているというもの
public int bombCount(int px, int py){
int xy, cut;
int i, j;
cnt = 0;
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
if(!(i == 1 && j == 1)){
if(px+i-1 >= 0 && px+i-1 < xmax && py+j-1 >= 0 && py+j-1 < ymax){
xy = (py+j-1)*xmax+(px+i-1);
if(sts[xy] == 9 || sts[xy] == 11) cnt++;
}
}
}
}
return cnt;
}

これはクリックされたマスの周りの爆弾の数を調べるメソッド
変数i、変数jは変数px(行)、変数py(列)に対応している。
for(i = 0; i < 3; i++)やfor(j = 0; j < 3; j++)はクリックされたマスの周 り左右、上下を調べる為に範囲を指定している
if(!(i == 1 && j == 1))はiとjが1だった場合、調べる対象がクリックしたマ ス自身になる為、実行されない
if(px+i-1 >= 0 && px+i-1 < xmax && py+j-1 >= 0 && py+j-1 < ymax)は調べ るマスが実際にあるかを判断している。
xyはマス番号と同じで、マス番号xyのステータスが9か11なら(調べたマスに爆 弾があれば)変数cntを1増加 させる
return cntで爆弾の数を戻している
public void AroundOpen(int px, int py){
int xy;
int i, y;
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
if(!(i == 1 && j == 1)){
if(px+i-1 >= 0 && px+i-1 < xmax && py+j-1 >= 0 && py+j-1 < ymax){
xy = (py+j-1)*xmax+(px+i-1);
if(sts[xy] >= 9){
sts[xy] = bombCount(px+i-1, py+j-1);
if(sts[xy] == 0) AroundOpen(px+i-1, py+j-1);
}
}
}
}
}
}

このAroundOpenメソッドは上のbombCountメソッドとほぼ同じで違う所は、調 べた周りのマスに爆弾が見つからない(ステータスが0のマス)があれば、 自分自身を呼び出して(再帰処理)同じようにまた周りのマスを調べる
このメソッドはステータスが0の時にしか呼び出されない
public void endCheck(){
int i, cnt;
cnt = 0;
for(i = 0; i < xy_max; i++){
if(sts[xy] < 9) cnt++;
}
if((cnt + bmax) == xy_max) gend = 1;
}

これはゲームの終了をチェックするメソッド
for文で爆弾があるマス以外の数を数えている
爆弾が無いマスと、爆弾があるマスの合計が全てのマスの数と同じなら変数 gendに1を入れる

感想・反省
Windowsにあるマインスイーパーです。簡単だと思っていたが、なかなか難し かった。
考察を終えてから気づいたのだが、ボタン等を使っていなかった。あと、重大 なバグを発見、直せなかった

参考
独学Java第二版
ジョセフ・オニール著
浅煎り珈琲Javaアプリケーション入門