CUIのゲーム作りに挑戦(Java), 製造

記事
IT・テクノロジー

1.プログラミング

前回の内部設計を基に、プログラミングします。
・ModelBS.java
・ViewBS.java
・ControllerBS.java
の3ファイルを作ってみたので、下記に貼り付けます。文字色が黒のみなので見にくいと思いますが、ご了承ください。

----------------------- ModelBS.java ------------------------------
import java.util.*;

public class ModelBS {
 //-----------
 // メンバ変数
 private long lGold = 10; // 所持Gold
 private int iPlayTimes = 0; // 挑戦回数
 private int iWinningStreak = 0; // 連勝
 private long[] lOdds =
  {0,2,4,8,16,32,64,128}; // 賭け率(倍率)
 private ArrayList<String[]> arrayTop5; // ランキング

 //---------------
 // コンストラクタ
 public ModelBS() {
  // ランキングを初期化
  ArrayList<String[]> ranking = new ArrayList();
  for(int i=0; i<5; i++) {
   String[] strPlayerInfo = {"","0"}; // [0]:プレイヤー名、[1]:Gold
   ranking.add(strPlayerInfo);
  }

  // 初期状態のランキングをメンバ変数に設定
  this.arrayTop5 = ranking;
 }

 //-----------------
 // 所持Goldのgetter
 public long getGold() {
  return this.lGold;
 }

 //-----------------
 // 挑戦回数のgetter
 public int getPlayTimes() {
  return this.iPlayTimes;
 }

 //-----------------
 // 挑戦回数のsetter
 public void setPlayTimes(int times) {
  this.iPlayTimes = times;
 }

 //-------------------
 // ランキングのgetter
 public ArrayList<String[]> getTop5() {
  return this.arrayTop5;
 }

 //-----------
 // 乱数を生成
 //-----------
 // 戻り値
 // int[0] : 最初に提示する乱数
 // int[1] : 勝負に使う乱数
 public int[] getRandomNumber() {
  int[] retNum = {0, 0};
  Random r = new Random();
  // 引数に13を指定すると0~12で乱数生成されるため、+1する
  int iFirstNum = r.nextInt(13) + 1;
  int iSecondNum;

  while((iSecondNum = r.nextInt(13) + 1) == iFirstNum) {
  }
  retNum[0] = iFirstNum;
  retNum[1] = iSecondNum;

  return retNum;
 }

 //-------------------------------------------
 // 勝負の判定結果によってGoldの計算などを行う
 //-------------------------------------------
 // パラメータ
 // iRandomNum : 乱数
 // strBetGold : betしたGold
 // strBS : 入力した大小
 // 戻り値
 // true : プレイヤーの勝ち
 // false : プレイヤーの負け
 public boolean calcGold(int[] iRandomNum, String strBetGold, String strBS) {
  Integer betGold = new Integer(strBetGold);
  boolean retIsWin = isWin(iRandomNum, strBS);

  // betした分のGoldを所持Goldから減らす
  this.lGold = this.lGold - betGold.longValue();
  if(retIsWin) {
   // 勝ちの場合は、連勝記録を+1する
   this.iWinningStreak++;
   this.lGold = this.lGold + betGold.longValue() * lOdds[iWinningStreak];
  } else {
   // 負けの場合は連勝を0に戻す
   this.iWinningStreak = 0;
  }

  return retIsWin;
 }

 //-----------------
 // 勝負の判定をする
 //-----------------
 // パラメータ
 // iRandomNum : 乱数
 // strBS : 入力した大小
 // 戻り値
 // true : プレイヤーの勝ち
 // false : プレイヤーの負け
 public boolean isWin(int[] iRandomNum, String strBS) {
  boolean retIsWin = false;
  boolean isBig = false;

  // 最初の乱数と次の乱数を大小比較
  if(iRandomNum[0] < iRandomNum[1]) {
   isBig = true;
  }

  if(isBig && strBS.equals("b")) {
   // Bigでbを予想したので勝ち
   retIsWin = true;
  } else if(!isBig && strBS.equals("s")) {
   // Smallでsを予想したので勝ち
   retIsWin = true;
  }

  return retIsWin;
 }

 //---------------------------------
 // ランキング入りしたかチェックする
 //---------------------------------
 public boolean checkRanking() {
  boolean isRanking = false;

  for(int i=0; i<this.arrayTop5.size(); i++) {
   String[] strPlayerInfo = this.arrayTop5.get(i);
   Integer rank5Gold = new Integer(strPlayerInfo[1]);
   if(this.lGold > rank5Gold.longValue()) {
    isRanking = true;
    break;
   }
  }
  return isRanking;
 }

 //---------------------
 // ランキングを更新する
 //---------------------
 public void updateRanking(String strPlayerName) {
  boolean isRanking = false;

  for(int i=0; i<this.arrayTop5.size(); i++) {
   String[] strPlayerInfo = this.arrayTop5.get(i);
   Integer rank5Gold = new Integer(strPlayerInfo[1]);
   if(this.lGold > rank5Gold.longValue()) {
    String[] strNewPlayerInfo = {strPlayerName, String.valueOf(this.lGold)};
    // ランキングのリストに追加
    this.arrayTop5.add(i, strNewPlayerInfo);
    break;
   }
  }
  if(this.arrayTop5.size() == 6) {
   // 追加すればリストのサイズは6になっているので、6個目を削除
   this.arrayTop5.remove(5);
  }
 }

 //-------------------
 // データを初期化する
 //-------------------
 public void clearData() {
  this.lGold = 10;
  this.iWinningStreak = 0;

 }
}
-------------------------------------------------------------------------


------------------------ ViewBS.java ------------------------------
import java.util.*;
import java.text.*;

public class ViewBS {

 //-------------------------
 // ゲームタイトルを表示する
 public void displayTitle() {
  System.out.println("####################");
  System.out.println("# Big or Small #");
  System.out.println("####################");
 }

 //---------------------
 // ランキングを表示する
 public void displayRanking(ModelBS model) {
  NumberFormat nFormat = NumberFormat.getNumberInstance();
  ArrayList<String[]> list = model.getTop5();
  System.out.println("ランキング");

  for(int i=0; i<list.size(); i++) {
   String[] strPlayerInfo = (String[])list.get(i);
   Integer gold = new Integer(strPlayerInfo[1]);

   StringBuffer strLine = new StringBuffer();
   strLine.append(String.valueOf(i+1)).append("位 ");
   strLine.append(strPlayerInfo[0]).append(" ");
   // 3桁毎にカンマを入れて数字を表示する
   strLine.append(nFormat.format(gold.longValue()));
   System.out.println(strLine.toString());
  }
 }

 //-------------------
 // 所持Goldを表示する
 public void displayGold(ModelBS model) {
  NumberFormat nFormat = NumberFormat.getNumberInstance();
  long lGold = model.getGold();

  System.out.println("--------------------");
  System.out.print("Gold : ");
  System.out.println(nFormat.format(lGold));
  System.out.println("--------------------");
 }

 //---------------------------------
 // ゲーム開始のメッセージを表示する
 public void displayStartMessage() {
  System.out.println("ゲームを始めます。");
 }

 //---------------------------------
 // ゲーム終了のメッセージを表示する
 public void displayEndMessage() {
  System.out.println("ゲーム終了です。");
 }

 //--------------------------------------------------------
 // 所持Goldが0になったのでゲーム終了のメッセージを表示する
 public void displayZeroEndMessage() {
  System.out.println("Goldが0になりました。ゲーム終了です。");
 }

 //-------------------------------
 // 最初のランダムな数字を表示する
 public void displayFirstNumber(int times, int randomNum) {
  System.out.print(times);
  System.out.print("回目の挑戦です。数字は");
  System.out.print(randomNum);
  System.out.println("です。");
 }

 //--------------------------------------------
 // betするGoldを入力させるメッセージを表示する
 public void displayBetMessage() {
  System.out.print("betするGoldを入力してください : ");
 }

 //-----------------------------------
 // Big or Smallのメッセージを表示する
 public void displayBSMessage() {
  System.out.print("次に出てくる数字はBig or Small?(bかsを入力) : ");
 }

 //---------------------
 // 勝負の結果を表示する
 public void displayResult(boolean isWin, int iSecondNum) {
  System.out.print(iSecondNum);
  System.out.println("が出ました。");
  if(isWin) {
   System.out.println("おめでとうございます。あなたの勝ちです。");
  } else {
   System.out.println("残念。あなたの負けです。");
  }
  System.out.println("");
 }

 //-----------------------------
 // 再挑戦のメッセージを表示する
 public void displayRetryMessage() {
  System.out.print("再挑戦しますか?(yかnを入力) : ");
 }

 //---------------------------------------------
 // ランキング入りしたときのメッセージを表示する
 public void displayRankinMessage() {
  System.out.println("おめでとうございます。ランキング入りしました。");
  System.out.print("プレイヤーの名前を入力してください : ");
 }

 //--------------------------------------------
 // betするGoldの入力で数字以外を入力された場合
 public void displayWarningNumber() {
  System.out.println("数字を入力してください。");
 }

 //---------------------------------
 // 所持Goldよりも多く入力された場合
 public void displayWarningGold() {
  System.out.println("所持しているGoldを超えています。");
 }

 //-------------------------
 // b, s以外を入力された場合
 public void displayWarningBS() {
  System.out.println("bかsを入力してください。");
 }

 //-------------------------
 // y, n以外を入力された場合
 public void displayWarningYesNo() {
  System.out.println("yかnを入力してください。");
 }
}

-------------------------------------------------------------------------


------------------ ControllerBS.java ------------------------------
import java.util.*;

public class ControllerBS {
 public static void main(String args[]) {
  try {
   int iMaxTimes = 7; // 7回挑戦する
   int iCurrentTimes = 1; // 現在の挑戦回数
   Scanner scan = new Scanner(System.in);
   ViewBS view = new ViewBS();
   ModelBS model = new ModelBS();

   view.displayTitle();
   view.displayRanking(model);
   view.displayGold(model);
   view.displayStartMessage();

   while(true) {
    model.setPlayTimes(iCurrentTimes);
    // 乱数を生成
    int[] iRandomNum = model.getRandomNumber();
    // 最初の数字を表示
    view.displayFirstNumber(iCurrentTimes, iRandomNum[0]);
    view.displayBetMessage();
    // betするGoldを入力
    String strBetGold = inputBetGold(model, view, scan);
    view.displayBSMessage();
    // Big or Smallを入力
    String strBS = inputBS(view, scan);
    // 勝負の結果を判定してGoldを計算
    boolean isWin = model.calcGold(iRandomNum, strBetGold, strBS);
    // 勝負の結果を表示
    view.displayResult(isWin, iRandomNum[1]);
    view.displayGold(model);

    iCurrentTimes++;
    // 7回の挑戦が終了
    if(iCurrentTimes > iMaxTimes) {
     view.displayEndMessage();
     // ランキングを更新
     updateRanking(model, view, scan);

     if(isRetry(view, scan)) {
      // 1回目から再挑戦
      iCurrentTimes = 1;
      prepareRetry(model, view);
     } else {
      // 終了
      return;
     }
    } else if(model.getGold() == 0) {
     // 所持Goldが0になったのでゲーム終了
     view.displayZeroEndMessage();
     if(isRetry(view, scan)) {
      // 1回目から再挑戦
      iCurrentTimes = 1;
      prepareRetry(model, view);
     } else {
      // 終了
      return;
     }
    }
   }

  } catch(Exception e) {
   System.out.println(e);
  }
 }

 //-----------------------------
 // 再挑戦するかどうかを入力する
 private static boolean isRetry(ViewBS view, Scanner scan) {
  boolean ret = false;
  String strRetry;

  view.displayRetryMessage();
  while(true) {
   strRetry = scan.nextLine(); // yかnを入力
   if(strRetry.equals("y")) {
    // 再挑戦
    ret = true;
    break;
   } else if(strRetry.equals("n")) {
    // 終了
    break;
   } else {
    // yとn以外の文字は入力しない
    view.displayWarningYesNo();
   }
  }

  return ret;
 }

 //---------------------------------
 // ランキング入りした場合は更新する
 private static void updateRanking(ModelBS model, ViewBS view, Scanner scan) {
  if(model.checkRanking()) {
   view.displayRankinMessage();
   // プレイヤーの名前を入力
   String strPlayerName = scan.nextLine();
   // ランキングを更新
   if(strPlayerName.length() > 10) {
    model.updateRanking(strPlayerName.substring(0,10));
   } else {
    model.updateRanking(strPlayerName);
   }
   view.displayRanking(model);
  }
 }

 //-------------------------
 // 再挑戦するので初期化する
 private static void prepareRetry(ModelBS model, ViewBS view) {
  model.clearData();
  view.displayGold(model);
 }

 //------------------
 // betするGoldを入力
 private static String inputBetGold(ModelBS model, ViewBS view, Scanner scan) {
  String strBetGold;
  while(true) {
   strBetGold = scan.nextLine();
   if(checkGold(model, view, strBetGold)) {
    break;
   }
  }
  return strBetGold;
 }

 //-------------------
 // Big or Smallを入力
 private static String inputBS(ViewBS view, Scanner scan) {
  String strBS;
  while(true) {
   strBS = scan.nextLine();
   if(strBS.equals("b") || strBS.equals("s")) {
    break;
   } else {
    // bとs以外を入力されたので警告
    view.displayWarningBS();
   }
  }
  return strBS;
 }

 //-------------
 // 数値チェック
 private static boolean isNumber(String strBetGold) {
  boolean ret = true;
  try {
   Integer.parseInt(strBetGold);
  } catch(NumberFormatException e) {
   ret = false;
  }
  return ret;
 }

 //------------------------
 // betされたGoldをチェック
 private static boolean checkGold(ModelBS model, ViewBS view, String strBetGold) {
  boolean ret = true;
  if(isNumber(strBetGold)) {
   Integer betGold = new Integer(strBetGold);
   if(betGold.longValue() > model.getGold()) {
    // 所持Goldよりも多いので警告
    view.displayWarningGold();
    ret = false;
   }
  } else {
   // 数字以外が入力されたので警告
   view.displayWarningNumber();
   ret = false;
  }

  return ret;
 }

}
-------------------------------------------------------------------------

貼り付けてみると途中で改行されたりで、やっぱり見にくいですね。。。
続いて、プログラムの解説をします。


2.解説

①ModelBSクラス
ランキングの情報や所持Goldなどのデータを管理しています。また、データを利用して大小の予想を判定したり、Goldの計算処理も行っています。

②ViewBSクラス
コマンドプロンプトにメッセージを表示します。System.out.print処理は全てこのクラスで行います。

③ControllerBSクラス
このクラスからゲームの処理が始まります。標準入力から受け取った文字のチェックや、ModelBSやViewBSに処理を指示します。


実際に動かしてみた画像がこちらです。
1.jpg

ランキングもちゃんと表示されています。数回やってみて、最高で200万Gold超えでした。乱数で1や13が出ると勝ったようなものなので、所持Goldを全betして増やすのが当たり前になっていました(笑)また、12が出て次は間違いなくSmallだろうと予想したら13が出たり、2が出て次はBigを予想したら1が出たりしましたので、数字はしっかりランダムに作られているようです。毎回全betして全勝すれば、計算上は20億を超えるはずですので、達成したら感動するかもしれないですね。


次回はこのゲーム作りを振り返ります。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す