カード合成シミュレータ(その3)

Java

さて、前回までの内容でミルドラースの現在の取得状況をセットすることができるようになった。今回はいよいよ、ではこの後どのくらい引けばミルドラースのSを作ることができるのかをシミュレーションするシステムを作成していこう。

53体目でついにできました!

実行例

セットされた現在の状況から、欲しいSの枚数と施行回数を入力すると以下のようなグラフを出力とSができるまでの平均回数を出力するものとする。

S(0)A(3)B(3)C(1)D(0) //現在の状況
現在の状況からSが指定枚数できるまでの回数をシミュレーションします
Sの必要枚数を入力>>1
シミュレーション回数を入力>>100
  1(  5):*****
  2(  4):****
  3(  5):*****
  4( 10):**********
  5(  3):***
  6( 11):***********
  7(  5):*****
  8(  1):*
  9(  7):*******
 10(  8):********
 11(  6):******
 12(  2):**
 13(  1):*
 14(  4):****
 15(  4):****
 16(  5):*****
 17(  1):*
 18(  3):***
 19(  1):*
 20(  1):*
 21(  4):****
 22(  1):*
 23(  2):**
 24(  1):*
 25(  2):**
 26(  2):**
 29(  1):*
平均:10.5回

グラフの1行が意味するのは
揃うまでに必要だった回数(それが何回発生したか):その回数*を出力

10(  8):********

今回の例でいうと
Sが0枚、Aが3枚,Bが3枚,Cが1枚、Dが0枚
の状況からSが1枚揃うまで100回シミュレーション(実際の確率に基づきカードを引き続けた)ところ,10回でSが合成できたのが  8回発生したということになる。

最頻値(モード)はグラフから

6( 11):***********

6回であることがわかる。

そして平均は
平均:10.5回
となっている。6回で揃ったことが一番多かったけど平均をとると10.5回だよ
と教えてくれている。
こうすることでSを1枚欲しいユーザーがあとどのくらい引けばSができるのかを判断する目安とすることができる。

simulatorメソッドの作成

それでは実際に以下のようなメソッド

static void simulator(Map<Card,Integer> cards,int sCount,int tryCount){}

を作成してみよう。引数で受け取る
cardsは現状のカード枚数を保持するMap,
sCountは欲しいsの枚数、
tryCountは試行回数だ。

解答例

static void simulator(Map<Card,Integer> cards,int sCount,int tryCount){
    int nowPoint=0;//現在のトータルポイント集計用変数
    //Mapをループさせて、それぞれのカードポイント*保持枚数をトータルポイントに加算
    for(Card c : cards.keySet()){
      nowPoint+=c.point*cards.get(c);
    }
    //すでに合成に必要な枚数が揃っている時はメッセージを表示して抜ける(ここでの240はSを合成する際に必要なポイント)
    if(nowPoint >= sCount * 240){
      System.out.println("カードを引く必要はありません");
      return;
    }
    //Map<揃った回数,それが何回か>を保持するMap作成。
    Map<Integer,Integer> result=new TreeMap<>();
    //試行回数分のループ。
    for(int i=0;i<tryCount;i++){
     int count=0;//回数を保持
     int tempPoint=nowPoint;//現在保有しているポイントを退避させておく。
     //必要な合成ポイントに到達するまで繰り返す
     while(tempPoint < sCount*240){
       count++; //回数をインクリメント
       Card card=drawCard(cards); //カードを引く
       tempPoint += card.point; //そのカードのポイントを加算
     }

     //ここに到達したということは必要な合成ポイントが揃ったということ
     //その回数はMapに含まれているか?
     if (result.containsKey(count)) {
       //含まれていたらその回数を1増やす
       result.put(count, result.get(count) + 1);
     } else {
       //そうでなかったら1回目ということ
       result.put(count, 1);
     }
    }
    /***ここから下が集計&表示処理 ***/
    int total=0;//平均を求めるために必要な合計を求める変数
    for(int key:result.keySet()){
      //実行例にあわせたフォーマットで出力
     System.out.printf("%3d(%3d):",key,result.get(key));
     //totalに(そろった回数*それが何回あったか)を加算
     total+=key*result.get(key);
     //その回数分*を表示
     for(int i=0;i<result.get(key);i++){
       System.out.print("*");
     }
     System.out.println();
   }
   //平均の出力
   System.out.printf("平均:%.1f回%n",total/(double)tryCount);
  }

解説

ここでも合成に必要なポイントに着目して処理を作成していく。詳細にコメントを残したので参考にしてもらいたい。

アプリに仕立てる

主要メソッド揃ったので実際にアプリ形式にしてみよう。以下の実行例になるように処理を作成してほしい。

実行例(一部省略)

---合成シミュレーター---
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>0
Cが出ました!
S(0)A(0)B(0)C(1)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>0
Dが出ました!
S(0)A(0)B(0)C(1)D(1)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>0
Cが出ました!
S(0)A(0)B(0)C(2)D(1)
---省略(以下同じ操作の繰り返し)---
S(0)A(0)B(1)C(3)D(6)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>1
合成しました!
S(0)A(0)B(2)C(1)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>2
カードを任意枚数にセットします
0,2,1,3,4 のように
カンマ区切りでS,A,B,C,Dの枚数を入力してください
>>0,2,2,0,0
指定枚数でセットしました
S(0)A(2)B(2)C(0)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>0
Cが出ました!
S(0)A(2)B(2)C(1)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>0
Cが出ました!
S(0)A(2)B(2)C(2)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>2
カードを任意枚数にセットします
0,2,1,3,4 のように
カンマ区切りでS,A,B,C,Dの枚数を入力してください
>>0,2,2,0,0
指定枚数でセットしました
S(0)A(2)B(2)C(0)D(0)
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>3
現在の状況からSが指定枚数できるまでの回数をシミュレーションします
Sの必要枚数を入力>>1
シミュレーション回数を入力>>10
  1(  1):*
  3(  1):*
  8(  1):*
 10(  1):*
 13(  1):*
 15(  2):**
 27(  1):*
 38(  1):*
 40(  1):*
平均:17.0回
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>3
現在の状況からSが指定枚数できるまでの回数をシミュレーションします
Sの必要枚数を入力>>1
シミュレーション回数を入力>>1000
  1( 21):*********************
  2( 21):*********************
  3( 34):**********************************
  4( 28):****************************
  5( 30):******************************
  6( 27):***************************
  7( 22):**********************
  8( 27):***************************
  9( 38):**************************************
 10( 30):******************************
 11( 45):*********************************************
 12( 35):***********************************
 13( 43):*******************************************
 14( 36):************************************
 15( 26):**************************
 16( 31):*******************************
 17( 31):*******************************
 18( 18):******************
 19( 44):********************************************
 20( 28):****************************
 21( 37):*************************************
 22( 36):************************************
 23( 31):*******************************
 24( 37):*************************************
 25( 22):**********************
 26( 24):************************
 27( 24):************************
 28( 21):*********************
 29( 23):***********************
 30( 15):***************
 31( 18):******************
 32( 13):*************
 33( 14):**************
 34( 12):************
 35(  8):********
 36(  5):*****
 37(  9):*********
 38(  3):***
 39(  7):*******
 40(  3):***
 41(  4):****
 42(  4):****
 43(  1):*
 44(  2):**
 46(  4):****
 47(  1):*
 48(  1):*
 49(  4):****
 50(  1):*
 52(  1):*
平均:17.6回
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>3
現在の状況からSが指定枚数できるまでの回数をシミュレーションします
Sの必要枚数を入力>>2
シミュレーション回数を入力>>1000
  3(  4):****
  4(  1):*
  5(  4):****
  6(  1):*
  7(  2):**
  8(  4):****
  9( 15):***************
 10(  8):********
 11( 10):**********
 12( 19):*******************
 13( 11):***********
 14( 15):***************
 15( 18):******************
 16( 22):**********************
 17( 22):**********************
 18( 18):******************
 19( 27):***************************
 20( 19):*******************
 21( 18):******************
 22( 26):**************************
 23( 29):*****************************
 24( 14):**************
 25( 35):***********************************
 26( 24):************************
 27( 36):************************************
 28( 33):*********************************
 29( 26):**************************
 30( 17):*****************
 31( 29):*****************************
 32( 24):************************
 33( 33):*********************************
 34( 26):**************************
 35( 16):****************
 36( 13):*************
 37( 26):**************************
 38( 17):*****************
 39( 19):*******************
 40( 13):*************
 41(  9):*********
 42( 16):****************
 43( 16):****************
 44(  8):********
 45( 17):*****************
 46(  9):*********
 47( 12):************
 48( 10):**********
 49( 10):**********
 50(  9):*********
 51( 12):************
 52( 13):*************
 53(  8):********
 54(  5):*****
 55(  6):******
 56( 10):**********
 57(  7):*******
 58(  6):******
 59(  8):********
 60(  8):********
 61(  5):*****
 62( 11):***********
 63(  6):******
 64(  5):*****
 65(  6):******
 66(  7):*******
 67(  5):*****
 68(  3):***
 70(  2):**
 71(  1):*
 72(  3):***
 73(  8):********
 74(  5):*****
 75(  2):**
 76(  4):****
 77(  1):*
 78(  1):*
 79(  3):***
 80(  2):**
 81(  3):***
 82(  4):****
 84(  1):*
 85(  2):**
 86(  1):*
 87(  1):*
 89(  6):******
 90(  3):***
 91(  1):*
 92(  1):*
 93(  1):*
 94(  1):*
 96(  1):*
117(  1):*
平均:35.0回
0:カードを引く
1:カードを合成する
2:カードをセットする
3:シミュレーションする
4:終了
>>4
終了

解答例

import java.util.*;
public class DQW {
	public static void main(String[] args) {
		//Scannerインスタンス作成
		final Scanner SC=new Scanner(System.in);
		//Mapを作成今回はこのmapが主役<Cardの種類,枚数>
		Map<Card,Integer> cards=new LinkedHashMap<>(){
			{
				put(new Card('S',3,240),0);
				put(new Card('A',5,48),0);
				put(new Card('B',10,12),0);
				put(new Card('C',30,3),0);
				put(new Card('D',52,1),0);
			}
		};
    System.out.println("---合成シミュレーター---");
   while(true){
     showMenu();
     int select=SC.nextInt();
     switch(select){
     case 0:
       Card c = drawCard(cards);
       System.out.println(c.rank + "が出ました!");
       cards.put(c,cards.get(c)+1);
       showCards(cards);
       break;
     case 1:
       synthesizeCards(cards);
       System.out.println("合成しました!");
       showCards(cards);
       break;
     case 2:
       System.out.println("カードを任意枚数にセットします");
       System.out.println("0,2,1,3,4 のように");
       System.out.println("カンマ区切りでS,A,B,C,Dの枚数を入力してください");
       System.out.print(">>");
       String[] numArr=SC.next().split(",");
       int[] cardCounts=new int[numArr.length];
       for(int i=0;i<numArr.length;i++){
         cardCounts[i]=Integer.parseInt(numArr[i]);
       }
       setCards(cards,cardCounts);
       System.out.println("指定枚数でセットしました");
       showCards(cards);
       break;
    case 3:
       System.out.println("現在の状況からSが指定枚数できるまでの回数をシミュレーションします");
       System.out.print("Sの必要枚数を入力>>");
       int sCount=SC.nextInt();
       System.out.print("シミュレーション回数を入力>>");
       int tryCount=SC.nextInt();
       simulator(cards,sCount, tryCount);
       break;
     default:
       System.out.println("終了");
       SC.close();
       return;
     }
   }
	}
  static void showMenu(){
    final String[] MENU={"カードを引く","カードを合成する","カードをセットする","シミュレーションする","終了"};
    for(int i=0;i<MENU.length;i++){
      System.out.printf("%d:%s%n",i,MENU[i]);
    }
    System.out.print(">>");
  }

	static void showCards(Map<Card,Integer> cards){
		for(Card c:cards.keySet()){
			System.out.printf("%s(%d)",c.rank,cards.get(c));
		}
		System.out.println();
	}
	static Card drawCard(Map<Card,Integer> cards){
		Card card=null;
		int n=(int)(Math.random()*100);
		for(Card c:cards.keySet()){
			if(n - c.ratio < 0){
				card=c;
				break;
			}
			n-=c.ratio;
		}
		return card;
	}
  static void synthesizeCards(Map<Card,Integer> cards){
    int totalPoint=0;
    //Mapを回しながら合計ポイントに加算していく
    for(Card c:cards.keySet()){
      totalPoint+=c.point*cards.get(c);
    }
    //合計ポイントをもとにSから順に合成後のカード枚数を更新していく
    for(Card c:cards.keySet()){
      cards.put(c,totalPoint/c.point);
      totalPoint=totalPoint%c.point;
    }
  }
  static void setCards(Map<Card,Integer> cards,int[] cardCounts){
    int idx = 0;
    for (Card key : cards.keySet()) {
      cards.put(key, cardCounts[idx++]);
    }
  }
  static void simulator(Map<Card,Integer> cards,int sCount,int tryCount){
    int nowPoint=0;//現在のトータルポイント集計用変数
    //Mapをループさせて、それぞれのカードポイント*保持枚数をトータルポイントに加算
    for(Card c : cards.keySet()){
      nowPoint+=c.point*cards.get(c);
    }
    //すでに合成に必要な枚数が揃っている時はメッセージを表示して抜ける(ここでの240はSを合成する際に必要なポイント)
    if(nowPoint >= sCount * 240){
      System.out.println("カードを引く必要はありません");
      return;
    }
    //Map<揃った回数,それが何回か>を保持するMap作成。
    Map<Integer,Integer> result=new TreeMap<>();
    for(int i=0;i<tryCount;i++){
     int count=0;//回数を保持
     int tempPoint=nowPoint;//現在保有しているポイントを退避させておく。
     //必要な合成ポイントに到達するまで繰り返す
     while(tempPoint < sCount*240){
       count++; //回数をインクリメント
       Card card=drawCard(cards); //カードを引く
       tempPoint += card.point; //そのカードのポイントを加算
     }

     //ここに到達したということは必要な合成ポイントが揃ったということ
     //その回数はMapに含まれているか?
     if (result.containsKey(count)) {
       //含まれていたらその回数を1増やす
       result.put(count, result.get(count) + 1);
     } else {
       //そうでなかったら1回目ということ
       result.put(count, 1);
     }
    }
    /***ここから下が集計&表示処理 ***/
    int total=0;//平均を求めるために必要な合計を求める変数
    for(int key:result.keySet()){
      //実行例にあわせたフォーマットで出力
     System.out.printf("%3d(%3d):",key,result.get(key));
     //totalに(そろった回数*それが何回あったか)を加算
     total+=key*result.get(key);
     //その回数分*を表示
     for(int i=0;i<result.get(key);i++){
       System.out.print("*");
     }
     System.out.println();
   }
   //平均の出力
   System.out.printf("平均:%.1f回%n",total/(double)tryCount);
  }

}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

解説

まずは繰り返し表示しているメニュー部分をメソッド化した

static void showMenu(){
    final String[] MENU={"カードを引く","カードを合成する","カードをセットする","シミュレーションする","終了"};
    for(int i=0;i<MENU.length;i++){
      System.out.printf("%d:%s%n",i,MENU[i]);
    }
    System.out.print(">>");
  }

配列をforで回すだけのとても基本的な処理だ。

後はユーザーの選択をswitch文で分岐を行い、各種処理を行っている。
処理のほとんどはすでに作成済みなので戸惑うことはないだろう。

リファクタリング

これでほぼ完成なのだが、一部気に入らない部分があるのでリファクタリングしていこう。このソースの最大の問題点は以下の部分だ

if(nowPoint >= sCount * 240){}

Sカードに必要な240という数値をダイレクトに埋め込んでしまっている。今回のミルドラースはD換算で240だが、これはモンスターによって違う。なのでここの部分を以下のように修正しよう。

static void simulator(Map<Card,Integer> cards,int sCount,int tryCount){
    int sPoint=cards.keySet().iterator().next().point;
    int nowPoint=0;//現在のトータルポイント集計用変数
    //Mapをループさせて、それぞれのカードポイント*保持枚数をトータルポイントに加算
    for(Card c : cards.keySet()){
      nowPoint+=c.point*cards.get(c);
    }
    //すでに合成に必要な枚数が揃っている時はメッセージを表示して抜ける(ここでの240はSを合成する際に必要なポイント)
    if(nowPoint >= sCount * sPoint){
      System.out.println("カードを引く必要はありません");
      return;
    }
    //Map<揃った回数,それが何回か>を保持するMap作成。
    Map<Integer,Integer> result=new TreeMap<>();
    for(int i=0;i<tryCount;i++){
     int count=0;//回数を保持
     int tempPoint=nowPoint;//現在保有しているポイントを退避させておく。
     //必要な合成ポイントに到達するまで繰り返す
     while(tempPoint < sCount*sPoint){
       count++; //回数をインクリメント
       Card card=drawCard(cards); //カードを引く
       tempPoint += card.point; //そのカードのポイントを加算
     }

     //ここに到達したということは必要な合成ポイントが揃ったということ
     //その回数はMapに含まれているか?
     if (result.containsKey(count)) {
       //含まれていたらその回数を1増やす
       result.put(count, result.get(count) + 1);
     } else {
       //そうでなかったら1回目ということ
       result.put(count, 1);
     }
    }
    /***ここから下が集計&表示処理 ***/
    int total=0;//平均を求めるために必要な合計を求める変数
    for(int key:result.keySet()){
      //実行例にあわせたフォーマットで出力
     System.out.printf("%3d(%3d):",key,result.get(key));
     //totalに(そろった回数*それが何回あったか)を加算
     total+=key*result.get(key);
     //その回数分*を表示
     for(int i=0;i<result.get(key);i++){
       System.out.print("*");
     }
     System.out.println();
   }
   //平均の出力
   System.out.printf("平均:%.1f回%n",total/(double)tryCount);
  }

修正版

import java.util.*;
public class DQW{
	public static void main(String[] args) {
		final Scanner SC=new Scanner(System.in);
		Map<Card,Integer> cards=new LinkedHashMap<>(){
			{
				put(new Card('S',3,240),0);
				put(new Card('A',5,48),0);
				put(new Card('B',10,12),0);
				put(new Card('C',30,3),0);
				put(new Card('D',52,1),0);
			}
		};
    System.out.println("---合成シミュレーター---");
   while(true){
     showMenu();
     int select=SC.nextInt();
     switch(select){
     case 0:
       Card c = drawCard(cards);
       System.out.println(c.rank + "が出ました!");
       cards.put(c,cards.get(c)+1);
       showCards(cards);
       break;
     case 1:
       synthesizeCards(cards);
       System.out.println("合成しました!");
       showCards(cards);
       break;
     case 2:
       System.out.println("カードを任意枚数にセットします");
       System.out.println("0,2,1,3,4 のように");
       System.out.println("カンマ区切りでS,A,B,C,Dの枚数を入力してください");
       System.out.print(">>");
       String[] numArr=SC.next().split(",");
       int[] cardCounts=new int[numArr.length];
       for(int i=0;i<numArr.length;i++){
         cardCounts[i]=Integer.parseInt(numArr[i]);
       }
       setCards(cards,cardCounts);
       System.out.println("指定枚数でセットしました");
       showCards(cards);
       break;
    case 3:
       System.out.println("現在の状況からSが指定枚数できるまでの回数をシミュレーションします");
       System.out.print("Sの必要枚数を入力>>");
       int sCount=SC.nextInt();
       System.out.print("シミュレーション回数を入力>>");
       int tryCount=SC.nextInt();
       simulator(cards,sCount, tryCount);
       break;
     default:
       System.out.println("終了");
       SC.close();
       return;
     }
   }
	}
  static void showMenu(){
    final String[] MENU={"カードを引く","カードを合成する","カードをセットする","シミュレーションする","終了"};
    for(int i=0;i<MENU.length;i++){
      System.out.printf("%d:%s%n",i,MENU[i]);
    }
    System.out.print(">>");
  }

	static void showCards(Map<Card,Integer> cards){
		for(Card c:cards.keySet()){
			System.out.printf("%s(%d)",c.rank,cards.get(c));
		}
		System.out.println();
	}
	static Card drawCard(Map<Card,Integer> cards){
		Card card=null;
		int n=(int)(Math.random()*100);
		for(Card c:cards.keySet()){
			if(n - c.ratio < 0){
				card=c;
				break;
			}
			n-=c.ratio;
		}
		return card;
	}
  static void synthesizeCards(Map<Card,Integer> cards){
    int totalPoint=0;
    for(Card c:cards.keySet()){
      totalPoint+=c.point*cards.get(c);
    }
    for(Card c:cards.keySet()){
      cards.put(c,totalPoint/c.point);
      totalPoint=totalPoint%c.point;
    }
  }
  static void setCards(Map<Card,Integer> cards,int[] cardCounts){
    int idx = 0;
    for (Card key : cards.keySet()) {
      cards.put(key, cardCounts[idx++]);
    }
  }
  static void simulator(Map<Card,Integer> cards,int sCount,int tryCount){
    int sPoint=cards.keySet().iterator().next().point;
    int nowPoint=0;
    for(Card c : cards.keySet()){
      nowPoint+=c.point*cards.get(c);
    }
    if(nowPoint >= sCount * sPoint){
      System.out.println("カードを引く必要はありません");
      return;
    }
    Map<Integer,Integer> result=new TreeMap<>();
    for(int i=0;i<tryCount;i++){
     int count=0;
     int tempPoint=nowPoint;
     while(tempPoint < sCount*sPoint){
       count++;
       Card card=drawCard(cards);
       tempPoint += card.point;
     }
     if (result.containsKey(count)) {
       result.put(count, result.get(count) + 1);
     } else {
       result.put(count, 1);
     }
    }
    int total=0;
    for(int key:result.keySet()){
     System.out.printf("%3d(%3d):",key,result.get(key));
     total+=key*result.get(key);
     for(int i=0;i<result.get(key);i++){
       System.out.print("*");
     }
     System.out.println();
   }
   System.out.printf("平均:%.1f回%n",total/(double)tryCount);
  }
}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

static final化を考える

先程のはマストな修正だが、ここから先はどちらが良いかは悩ましいところだ。
その一つが毎回引数で受け渡している、Mapをstatic final化してしまうことだ。
ソースコードはとてもスッキリするが、メソッドのそれぞれが、それ単体では意味をなさなくなってしまうデメリットがある。まあ、今回ここで作ったメソッドをほかで使うこともないのでここはstatic final化してしまおう。

import java.util.*;
public class DQW {
  static final Map<Card,Integer> CARDS=new LinkedHashMap<>(){
    {
      put(new Card('S',3,240),0);
      put(new Card('A',5,48),0);
      put(new Card('B',10,12),0);
      put(new Card('C',30,3),0);
      put(new Card('D',52,1),0);
    }
  };
	public static void main(String[] args) {
		final Scanner SC=new Scanner(System.in);
    System.out.println("---合成シミュレーター---");
   while(true){
     showMenu();
     int select=SC.nextInt();
     switch(select){
     case 0:
       Card c = drawCard();
       System.out.println(c.rank + "が出ました!");
       CARDS.put(c,CARDS.get(c)+1);
       showCards();
       break;
     case 1:
       synthesizeCards();
       System.out.println("合成しました!");
       showCards();
       break;
     case 2:
       System.out.println("カードを任意枚数にセットします");
       System.out.println("0,2,1,3,4 のように");
       System.out.println("カンマ区切りでS,A,B,C,Dの枚数を入力してください");
       System.out.print(">>");
       String[] numArr=SC.next().split(",");
       int[] cardCounts=new int[numArr.length];
       for(int i=0;i<numArr.length;i++){
         cardCounts[i]=Integer.parseInt(numArr[i]);
       }
       setCards(cardCounts);
       System.out.println("指定枚数でセットしました");
       showCards();
       break;
    case 3:
       System.out.println("現在の状況からSが指定枚数できるまでの回数をシミュレーションします");
       System.out.print("Sの必要枚数を入力>>");
       int sCount=SC.nextInt();
       System.out.print("シミュレーション回数を入力>>");
       int tryCount=SC.nextInt();
       simulator(sCount, tryCount);
       break;
     default:
       System.out.println("終了");
       SC.close();
       return;
     }
   }
	}
  static void showMenu(){
    final String[] MENU={"カードを引く","カードを合成する","カードをセットする","シミュレーションする","終了"};
    for(int i=0;i<MENU.length;i++){
      System.out.printf("%d:%s%n",i,MENU[i]);
    }
    System.out.print(">>");
  }

	static void showCards(){
		for(Card c:CARDS.keySet()){
			System.out.printf("%s(%d)",c.rank,CARDS.get(c));
		}
		System.out.println();
	}
	static Card drawCard(){
		Card card=null;
		int n=(int)(Math.random()*100);
		for(Card c:CARDS.keySet()){
			if(n - c.ratio < 0){
				card=c;
				break;
			}
			n-=c.ratio;
		}
		return card;
	}
  static void synthesizeCards(){
    int totalPoint=0;
    for(Card c:CARDS.keySet()){
      totalPoint+=c.point*CARDS.get(c);
    }
    for(Card c:CARDS.keySet()){
      CARDS.put(c,totalPoint/c.point);
      totalPoint=totalPoint%c.point;
    }
  }
  static void setCards(int[] cardCounts){
    int idx = 0;
    for (Card key : CARDS.keySet()) {
      CARDS.put(key, cardCounts[idx++]);
    }
  }
  static void simulator(int sCount,int tryCount){
    int sPoint=CARDS.keySet().iterator().next().point;
    int nowPoint=0;
    for(Card c : CARDS.keySet()){
      nowPoint+=c.point*CARDS.get(c);
    }
    if(nowPoint >= sCount * sPoint){
      System.out.println("カードを引く必要はありません");
      return;
    }
    Map<Integer,Integer> result=new TreeMap<>();
    for(int i=0;i<tryCount;i++){
     int count=0;
     int tempPoint=nowPoint;
     while(tempPoint < sCount*sPoint){
       count++;
       Card card=drawCard();
       tempPoint += card.point;
     }
     if (result.containsKey(count)) {
       result.put(count, result.get(count) + 1);
     } else {
       result.put(count, 1);
     }
    }
    int total=0;
    for(int key:result.keySet()){
     System.out.printf("%3d(%3d):",key,result.get(key));
     total+=key*result.get(key);
     for(int i=0;i<result.get(key);i++){
       System.out.print("*");
     }
     System.out.println();
   }
   System.out.printf("平均:%.1f回%n",total/(double)tryCount);
  }
}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

こうしておくことで、今後他のカードに対してこのシミュレータを使いたい場合はstatic finalなMapのKey部分をだけを変更すればよいことが明確になるメリットもあるので今回はこれを完成形としたい。

鬼畜仕様だったゴーレム

今回はミルドラースだったが、過去にゴーレムというモンスターがいた。
これはランクアップさせるのにすべてのランクで5枚必要という鬼畜仕様で
SのD換算値が625と異常な値となっている。

put(new Card('S',2,625),0);
put(new Card('A',4,125),0);
put(new Card('B',7,25),0);
put(new Card('C',17,5),0);
put(new Card('D',70,1),0);

しかも625分の1の価値しかないD(つまりゴミ)の排出確率が70%というのもエグい、私は結局DQWのゲーム開始時からやり続けて2年やっと最近105体目でついにSを合成にて1体つくることができた。正確な排出率がアップされていないのでネットの情報でSを2%としているが体感的には1%を切るぐらいなのではないだろうか?
いずれにせよD換算625の鬼畜仕様だけは勘弁願いたい次第である。

最後に

今回は今までJavaで学んできた技術をいかして、ドラゴンクエストウォークをやっている人に役立つカード合成シミュレーターを作成した。あなたも日頃不便に感じていること、こんなのがあったらいいのにな、と思うことがあったらぜひオリジナルアプリを作成してみてもらいたい。そこで作成した成果物はポートフォリとして評価もとても高いものとなる。
そしてQiitaやブログなどで制作記事をアップしたり、実際に他の人に利用してもらってフィードバックを得ながら改善したりなどができればあなたの人材的価値は飛躍的に高まっていくだろう。

Java
スポンサーリンク
シェアする
mjpurinをフォローする

コメント

タイトルとURLをコピーしました