15blocks ソースコード

ええ、最初に断っておきますが(笑)、以下のソースは、たとえJavaが初めてだったとしても、 他のプログラミング言語をやっていた人間からすれば、ちょっと汚いソースになっています。^^; 数値はそのまんま使ってるし…。 とりあえず、さっさと形にしたかったので、そういうところあたりまで気を配りませんでした。 この点を最初に言い訳しときます。(笑)

あと、今回はJava解説書にあるアプレットの作り方を、そのまんままねている箇所が結構あります。 具体的には、実際のプログラムの中身と関係ない箇所ですが。 Javaを既に使っている方々から見ると、「なんでここにこんな無駄な記述が?」と思われる点が多くあると思います。 それは本をそのまま写したからなんですが(笑)。 もしそういう箇所があったら、ぜひ教えて下さい(^^;)。 私の知りたいのはそこです(^^;)。

ちなみに、ゲームそのもののアルゴリズムは、100%私が考えて書いたものです。 そこは、本を写したわけじゃないです(^^;)。 そりゃそーだよな。^^;

では、ソースコードです。


package applettest;

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.*;

public class Applet1 extends JApplet implements Runnable {
  boolean isStandalone = false;
  String csign;

  // 画像配列
  Image blocks[] = new Image[19];   // (画像読み込み用)
  Image panels[] = new Image[16];   // (画面描画用)
  // ブロック値記憶用配列
  int panelnums[] = new int[16];

  // 描画位置変数
  int dx=0, dy=0, dh=50, dw=50;
  int MouseOnX=0,MouseOnY=0;
  int EmptyPointX,EmptyPointY;
  int margin=10;

  // ステップ数記憶用
  int clicktime=0;

  // 新しい色の作成
  Color darkYellow  = new Color(204,204,0);
  Color lightYellow = new Color(255,255,204);
  Color darkBlue    = new Color(0,0,204);
  Color lightGray   = new Color(204,204,204);

  // ゲーム状態フラグ
  boolean gameclear = false;

  // 描画用コンポーネント
  JComponent jcWnd;

  // 並行処理用スレッド
  Thread thread;

  // スレッド動作を許可するかどうか
  boolean isRunnable = true;


  // ランダムに並べる(16個のブロックを4×4の配列にランダムに配置する)
  void distblocks() {
    // 各ブロックの状態記憶用配列(重複不可だから要る)
    boolean randwork[] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false};   // 16個
    int getonenum;
    int loopr=0;

    while( loopr<16 ) {
      // 0〜15までの値を0番〜15番までのブロックにランダムに配置(重複不可)
      getonenum = (int)(Math.random()*100 % 16.0);   // 0〜15の間の整数をランダムに抽出
      if( randwork[getonenum] == false ) {
        // その番号がまだ使われていなければ使用
        panelnums[loopr] = getonenum;   // 内部用配列に数値を代入
        randwork[getonenum] = true;     // その番号が使用済みであるフラグを立てる
        // その番号が15だった場合(※15=空枠)
        if( getonenum == 15 ) {
          // EmptyPoint(空枠の場所)の設定
          EmptyPointX = loopr % 4;
          EmptyPointY = loopr / 4;
        }
        // 代入回数を記録
        loopr++;
      }
    }
    // ランダムに並べた結果を描画用Imageに代入
    for( int i=0 ; i<16 ; i++ ) {
      panels[i] = blocks[panelnums[i]];
    }
  }

  //引数値の取得
  public String getParameter(String key, String def) {
    return isStandalone ? System.getProperty(key, def) :
      (getParameter(key) != null ? getParameter(key) : def);
  }

  //アプレットの構築
  public Applet1() {
  }
  //アプレットの初期化
  public void init() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  //コンポーネントの初期化
  private void jbInit() throws Exception {
    this.setSize(new Dimension(405,240));
    this.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(MouseEvent e) {
        this_mouseClicked(e);
      }
    });
    this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        this_mouseMoved(e);
      }
    });

    // 画像の読み込み
    blocks[0] = getImage(getCodeBase(),"15blocks-01.gif");
    blocks[1] = getImage(getCodeBase(),"15blocks-02.gif");
    blocks[2] = getImage(getCodeBase(),"15blocks-03.gif");
    blocks[3] = getImage(getCodeBase(),"15blocks-04.gif");
    blocks[4] = getImage(getCodeBase(),"15blocks-05.gif");
    blocks[5] = getImage(getCodeBase(),"15blocks-06.gif");
    blocks[6] = getImage(getCodeBase(),"15blocks-07.gif");
    blocks[7] = getImage(getCodeBase(),"15blocks-08.gif");
    blocks[8] = getImage(getCodeBase(),"15blocks-09.gif");
    blocks[9] = getImage(getCodeBase(),"15blocks-10.gif");
    blocks[10]= getImage(getCodeBase(),"15blocks-11.gif");
    blocks[11]= getImage(getCodeBase(),"15blocks-12.gif");
    blocks[12]= getImage(getCodeBase(),"15blocks-13.gif");
    blocks[13]= getImage(getCodeBase(),"15blocks-14.gif");
    blocks[14]= getImage(getCodeBase(),"15blocks-15.gif");
    blocks[15]= getImage(getCodeBase(),"15blocks-Empty.gif");
    blocks[16]= getImage(getCodeBase(),"15blocks-Excellent.gif");
    blocks[17]= getImage(getCodeBase(),"15blocks-Good.gif");
    blocks[18]= getImage(getCodeBase(),"15blocks-Loose.gif");

    // ランダムに並べる
    distblocks();

    // 描画用コンポーネントの追加
    getContentPane().add(jcWnd = new JComponent() {
      public void paint(Graphics gra) {

        // 画像の読み込みがまだなら描画しない
        if( blocks[15] == null ) return;

        // ブロックを描画
        int loop;
        for( loop=0 ; loop<16 ; loop++ ) {
          gra.drawImage(panels[loop],dx+margin,dy+margin,this);
          dx+=50;
          if( dx>=200 ) {
            dx-=200; dy+=50;
          }
        }

        // コントロールボタンの表示
        // Draw Buttons
        // "GIVE UP" Button
        gra.setColor(lightGray);
        gra.fillRect(231,101,98,25);
        gra.setColor(Color.white);
        gra.drawLine(230,100,330,100);
        gra.drawLine(230,100,230,127);
        gra.setColor(Color.gray);
        gra.drawLine(230,127,330,127);
        gra.drawLine(330,100,330,127);
        // "OTHER GAME" Button
        gra.setColor(lightGray);
        gra.fillRect(231,151,148,25);
        gra.setColor(Color.white);
        gra.drawLine(230,150,330,150);
        gra.drawLine(230,150,230,177);
        gra.setColor(Color.gray);
        gra.drawLine(230,177,330,177);
        gra.drawLine(330,150,330,177);
        // Write Captions
        gra.setColor(Color.black);
        Font font = new Font("Arial",Font.PLAIN,16);
        gra.setFont(font);
        gra.drawString("GIVE UP",250,120);
        gra.drawString("SHUFFLE",245,170);

        // マウス追跡枠と移動方向指示矢印を描画
        if( MouseOnY < 4 && MouseOnX < 4 ) {
          // マウスがゲーム枠外でないなら
          if( MouseOnX != EmptyPointX || MouseOnY != EmptyPointY  ) {
            // そのポイントが空でもなく
            if( MouseOnX == EmptyPointX || MouseOnY == EmptyPointY ) {
              // 空枠と同じ行または列にあれば、移動可能色を設定して
              gra.setColor(Color.yellow);
            }
            else {
              // 空枠とは異なる行または列にあれば、移動不可能色を設定して
              gra.setColor(darkYellow);
            }
            // マウス追跡枠を描画
            gra.drawRect(margin+MouseOnX*50,margin+MouseOnY*50,50,50);
            gra.drawRect(margin+MouseOnX*50+1,margin+MouseOnY*50+1,48,48);
            gra.drawRect(margin+MouseOnX*50-1,margin+MouseOnY*50-1,52,52);

            // 移動可能矢印の描画処理
            if( MouseOnX == EmptyPointX || MouseOnY == EmptyPointY ) {
              // 三角形描画のための差分値指定用配列を確保
              int rectdiffX[] = new int[3];
              int rectdiffY[] = new int[3];
              // X座標が一致(=縦方向に移動可能)なら
              if( MouseOnX == EmptyPointX ) {
                if( MouseOnY < EmptyPointY ) {
                  // 空枠より上にあるなら、下方向への移動可能指示を描画するよう指定して
                  rectdiffX[0] = 15; rectdiffX[1] = 25; rectdiffX[2] = 35;
                  rectdiffY[0] = 55; rectdiffY[1] = 65; rectdiffY[2] = 55;
                }
                else if( MouseOnY > EmptyPointY ) {
                  // 空枠より下にあるなら、上方向への移動可能指示を描画するよう指定して
                  rectdiffX[0] = 15; rectdiffX[1] =  25; rectdiffX[2] = 35;
                  rectdiffY[0] = -5; rectdiffY[1] = -15; rectdiffY[2] = -5;
                }
              }
              // Y座標が一致(=横方向に移動可能)なら
              if( MouseOnY == EmptyPointY ) {
                // 移動可能矢印の描画
                if( MouseOnX < EmptyPointX ) {
                  // 空枠より左にあるなら、右方向への移動可能指示を描画するよう指定して
                  rectdiffX[0] = 55; rectdiffX[1] = 65; rectdiffX[2] = 55;
                  rectdiffY[0] = 15; rectdiffY[1] = 25; rectdiffY[2] = 35;
                }
                else if( MouseOnX > EmptyPointX ) {
                  // 空枠より右にあるなら、左方向への移動可能指示を描画するよう指定して
                  rectdiffX[0] = -5; rectdiffX[1] = -15; rectdiffX[2] = -5;
                  rectdiffY[0] = 15; rectdiffY[1] =  25; rectdiffY[2] = 35;
                }
              }
              // 移動可能矢印を描画
              int rectX[] = { margin+MouseOnX*50+rectdiffX[0] , margin+MouseOnX*50+rectdiffX[1] , margin+MouseOnX*50+rectdiffX[2] };
              int rectY[] = { margin+MouseOnY*50+rectdiffY[0] , margin+MouseOnY*50+rectdiffY[1] , margin+MouseOnY*50+rectdiffY[2] };
              gra.fillPolygon(rectX,rectY,3);
            }
          }
        }
        else if( ( MouseOnY == 14 || MouseOnY == 16 ) && MouseOnX == 12 ) {
          // コントロールボタンエリア内なら
          // ボタンのマウス追跡枠を描画
          gra.setColor(Color.yellow);
          gra.drawRect((MouseOnX-10)*100+30,(MouseOnY-10)*25,100,26);
          gra.drawRect((MouseOnX-10)*100+30+1,(MouseOnY-10)*25+1,98,24);
          gra.drawRect((MouseOnX-10)*100+30-1,(MouseOnY-10)*25-1,102,28);
        }

        // ステップレポートの表示
        // Background
        gra.setColor(darkBlue);
        gra.fillRoundRect(233,18,150,50,10,10);
        gra.setColor(lightYellow);
        gra.fillRoundRect(230,15,150,50,10,10);
        gra.setColor(Color.blue);
        gra.drawRoundRect(230,15,150,50,10,10);
        // Foreground(Fixed Strings and Lines)
        gra.setColor(darkBlue);
        font = new Font("Arial",Font.PLAIN,12);
        gra.setFont(font);
        gra.drawString("STEPS",240,30);
        gra.drawLine(238,32,372,32);
        // Foreground(STEP INFO)
        gra.setColor(Color.black);
        font = new Font("Arial",Font.PLAIN,20);
        gra.setFont(font);
        String StepReport = clicktime + " steps.";
        gra.drawString(StepReport,270,60);

      }
    } );
  }

  // アプレットの動作開始
  public void start() {
    // 停止中なら
    if( thread == null ) {
      // スレッドを生成する
      thread = new Thread(this);
      // 実行を許可する
      isRunnable = true;
      // 動作を開始する
      thread.start();
    }
  }

  // アプレットの動作停止
  public void stop() {
    // 動作中なら
    if( thread != null ) {
      // 実行を禁止する
      isRunnable = false;
    }
  }

  // スレッドの処理
  public void run() {

    // 実行が許可されている間繰り返す
    while(isRunnable) {

      int loop=0;
      dx+= 0;
      dy=0;

      // 再描画
      jcWnd.repaint(0,0,400,margin*2+200);
      // 時間待ち
      try {
        Thread.sleep(50);
      } catch(InterruptedException ie) {
        // 中断されたら繰り返しから抜ける
        break;
      }

      loop++;
      if( loop >= 10 ) { break; }

    }

    // スレッドを破棄する
    thread = null;

  }

  //アプレットの情報取得
  public String getAppletInfo() {
    return "Applet Information";
  }
  //引数情報の取得
  public String[][] getParameterInfo() {
    String[][] pinfo =
      {
      {"ClearSign", "String", ""},
      };
    return pinfo;
  }

  //ルック&フィールの設定
  static {
    try {
      //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
    }
    catch(Exception e) {
    }
  }

  // マウスの動きを監視
  void this_mouseMoved(MouseEvent e) {
    if( (e.getX()<=200+margin) && (e.getY()<=200+margin) ) {
      // もしゲームフィールド内なら(※X,Yにブロックの位置を格納/1ブロック50×50だから)
      MouseOnX = (e.getX()-margin)/50;
      MouseOnY = (e.getY()-margin)/50;
    }
    else {
      // それ以外の領域なら(※10加えているのは単に判定用数値を2桁にしたかったから/BOX単位100×25)
      MouseOnX = 10 + (e.getX()-30)/100;
      MouseOnY = 10 + e.getY()/25;
    }
  }

  // マウスのクリックを監視
  void this_mouseClicked(MouseEvent e) {
    // 移動可能であるかを判定
    if( MouseOnY < 4 && MouseOnX < 4 ) {
      // マウスがゲーム枠外でないなら
      if( MouseOnX != EmptyPointX || MouseOnY != EmptyPointY  ) {
        // そのポイントが空でもなく
        if( MouseOnX == EmptyPointX || MouseOnY == EmptyPointY ) {
          // 空枠と同じ行または列にあれば、移動可能である。

          // 移動回数を記録
          clicktime++;
          // クリックされた位置の番号を計算
          int clickpoint = MouseOnX + MouseOnY*4;
          // 空枠の位置の番号を計算
          int emptypoint = EmptyPointX + EmptyPointY*4;

          if( MouseOnX == EmptyPointX ) {
            // X座標が一致(=縦方向に移動可能)なら
            if( MouseOnY < EmptyPointY ) {
              // 空枠より上にあるなら下方向へ移動
              if( EmptyPointY - MouseOnY >= 1 ) {  panels[emptypoint]   = panels[emptypoint-4];  panelnums[emptypoint]   = panelnums[emptypoint-4];}
              if( EmptyPointY - MouseOnY >= 2 ) {  panels[emptypoint-4] = panels[emptypoint-8];  panelnums[emptypoint-4] = panelnums[emptypoint-8];}
              if( EmptyPointY - MouseOnY >= 3 ) {  panels[emptypoint-8] = panels[emptypoint-12]; panelnums[emptypoint-8] = panelnums[emptypoint-12];}
              panels[clickpoint] = blocks[15];  panelnums[clickpoint] = 15;
            }
            else if( MouseOnY > EmptyPointY ) {
              // 空枠より下にあるなら上方向へ移動
              if( MouseOnY - EmptyPointY >= 1 ) {  panels[emptypoint]   = panels[emptypoint+4];  panelnums[emptypoint]   = panelnums[emptypoint+4];}
              if( MouseOnY - EmptyPointY >= 2 ) {  panels[emptypoint+4] = panels[emptypoint+8];  panelnums[emptypoint+4] = panelnums[emptypoint+8];}
              if( MouseOnY - EmptyPointY >= 3 ) {  panels[emptypoint+8] = panels[emptypoint+12]; panelnums[emptypoint+8] = panelnums[emptypoint+12];}
              panels[clickpoint] = blocks[15]; panelnums[clickpoint] = 15;
            }
          }
          if( MouseOnY == EmptyPointY ) {
            // Y座標が一致(=横方向に移動可能)なら
            if( MouseOnX < EmptyPointX ) {
              // 空枠より左にあるなら右方向へ移動
              if( EmptyPointX - MouseOnX >= 1 ) {  panels[emptypoint]   = panels[emptypoint-1];  panelnums[emptypoint]   = panelnums[emptypoint-1];}
              if( EmptyPointX - MouseOnX >= 2 ) {  panels[emptypoint-1] = panels[emptypoint-2];  panelnums[emptypoint-1] = panelnums[emptypoint-2];}
              if( EmptyPointX - MouseOnX >= 3 ) {  panels[emptypoint-2] = panels[emptypoint-3];  panelnums[emptypoint-2] = panelnums[emptypoint-3];}
              panels[clickpoint] = blocks[15]; panelnums[clickpoint] = 15;
            }
            else if( MouseOnX > EmptyPointX ) {
              // 空枠より右にあるなら左方向へ移動
              if( MouseOnX - EmptyPointX >= 1 ) {  panels[emptypoint]   = panels[emptypoint+1];  panelnums[emptypoint]   = panelnums[emptypoint+1];}
              if( MouseOnX - EmptyPointX >= 2 ) {  panels[emptypoint+1] = panels[emptypoint+2];  panelnums[emptypoint+1] = panelnums[emptypoint+2];}
              if( MouseOnX - EmptyPointX >= 3 ) {  panels[emptypoint+2] = panels[emptypoint+3];  panelnums[emptypoint+2] = panelnums[emptypoint+3];}
              panels[clickpoint] = blocks[15]; panelnums[clickpoint] = 15;
            }
          }
          // EMPTY POINT の座標を再設定
          EmptyPointX = MouseOnX;
          EmptyPointY = MouseOnY;
          // 再描画(STEP REPORT部分だけを再描画)
          jcWnd.repaint(230,15,150,50);
        }
      }
    }
    else if( MouseOnY == 16 && MouseOnX == 12 ) {
      // シャッフルボタン
      distblocks();
      clicktime=0;
      gameclear = false;
    }
    else if( MouseOnY == 14 && MouseOnX == 12 ) {
      // GIVEUPボタン
      // EmptyPointにLoose顔アイコンを挿入
      panels[EmptyPointX + EmptyPointY*4] = blocks[18];
    }

    // 終了判定
    // 空枠が端でなければ終了ではないので判定処理を行わない
    if( panelnums[0]==15 || panelnums[3]==15 || panelnums[12]==15 || panelnums[15]==15 ) {
      // 空枠が端なら判定を行う
      int ra = panelnums[0]  + panelnums[1]  + panelnums[2]  + panelnums[3];
      int rb = panelnums[4]  + panelnums[5]  + panelnums[6]  + panelnums[7];
      int rc = panelnums[8]  + panelnums[9]  + panelnums[10] + panelnums[11];
      int rd = panelnums[12] + panelnums[13] + panelnums[14] + panelnums[15];
      int re = panelnums[0]  + panelnums[4]  + panelnums[8]  + panelnums[12];
      int rf = panelnums[1]  + panelnums[5]  + panelnums[9]  + panelnums[13];
      int rg = panelnums[2]  + panelnums[6]  + panelnums[10] + panelnums[14];
      int rh = panelnums[3]  + panelnums[7]  + panelnums[11] + panelnums[15];
      // 判定
      int A=6, B=22, C=38, D=54, E=24, F=28, G=32, H=36;
      if( ra==A && rb==B && rc==C && rd==D && re==E && rf==F && rg==G && rh==H ) { gameclear=true; }
      if( ra==E && rb==F && rc==G && rd==H && re==D && rf==C && rg==B && rh==A ) { gameclear=true; }
      if( ra==D && rb==C && rc==B && rd==A && re==H && rf==G && rg==F && rh==E ) { gameclear=true; }
      if( ra==H && rb==G && rc==F && rd==E && re==A && rf==B && rg==C && rh==D ) { gameclear=true; }
      if( ra==E && rb==F && rc==G && rd==H && re==A && rf==B && rg==C && rh==D ) { gameclear=true; }
      if( ra==A && rb==B && rc==C && rd==D && re==H && rf==G && rg==F && rh==E ) { gameclear=true; }
      if( ra==H && rb==G && rc==F && rd==E && re==D && rf==C && rg==B && rh==A ) { gameclear=true; }
      if( ra==D && rb==C && rc==B && rd==A && re==E && rf==F && rg==G && rh==H ) { gameclear=true; }
      // 終了なら顔マークを付加
      if( gameclear == true ) {
        // EmptyPoubtに顔マークを挿入
        panels[EmptyPointX + EmptyPointY*4] = blocks[16];
        // その後の移動(操作)のことを考えて、フラグはすぐに下げる
        gameclear = false;
      }
    }

  }
}

ソースコードは以上です。

識者からのご指摘をお待ち致しております。(笑)

戻る