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

Java

それでは引き続きカード合成シミュレーターを作成していこう。
(前回の内容はこちら)

前回作成したコード

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);
			}
		};
		showCards(cards);
		/*
		Card card=drawCard(cards);
		System.out.println(card.rank+"が出ました!");
		cards.put(card,cards.get(card)+1);
		showCards(cards);
		*/
		System.out.print("何枚ひく>>");
		int num=SC.nextInt();
		for(int i=0;i<num;i++){
			Card card=drawCard(cards);
			System.out.println(card.rank+"が出ました!");
			cards.put(card,cards.get(card)+1);
		}
		showCards(cards);
	}

	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;
	}
}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

合成メソッドの作成

ドラゴンクエストウォークではDを3枚でC,Cを4枚でBと合成することによってカードがランクアップしていくシステムがある。このシステムの実装方法も色々考えられるが、今回はD換算した値合計値をもとに判定していく方法をとる。

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;
   }
 }

解説

例えば
Aを1枚
Bを7枚
Cを5枚
Dを3枚
を持っている状態があったとする。
まずは、カードの合計ポイントを求める
(48*1)+ (12 * 7)+(3 * 5)+(1 * 3)=150
この状態から合成を考えよう。
Sを作るには240ポイント必要なのでSは0枚だ。
この部分は
150/240
と合計ポイントをSの必要ポイントで割った商で求めることでわかる。
(Javaはint割るintは商が求まる)
そうしたら次に
150%240
を求める。これは150を240で割った余りを求める処理だ。
今回は商がたたずにそのままの150が余りとなる。

次にAについて考える。
同じように
150/48
を求める。この商は3となる。
つまりAを3枚作成できることがわかる。
Aを3枚作成したことによって余ったポイントはいくつになるだろうか?
そう、さきほどと同じように
150%48
を計算すればよい。商が3で余りが6
つまり、6ポイントの余りが発生したことがわかる

その次にBを考える
Bは12ポイント必要なので6ポイントでは作成することはできず0枚だ。

次にCを考えるとCは3ポイント必要なので
6ポイントで2枚生成できることがわかる。

これでポイントを使い切ったので合成は終了だ。
S:0,A:3,B:0,C:2,D:0
というのが合成結果となる

この処理をおこなったのが上記のsynthesizeCardsメソッドだ。
一見複雑そうな処理も商と余りを使って実にシンプルに実装出来ることがわかる。

確認

ここまでできたら以下の実行例になるように作成してみよう。

実行例

S(0)A(0)B(0)C(0)D(0)
何枚ひく>>20
Cが出ました!
Bが出ました!
Dが出ました!
Cが出ました!
Dが出ました!
Cが出ました!
Bが出ました!
Dが出ました!
Dが出ました!
Dが出ました!
Dが出ました!
Aが出ました!
Cが出ました!
Dが出ました!
Cが出ました!
Dが出ました!
Bが出ました!
Dが出ました!
Dが出ました!
Cが出ました!
S(0)A(1)B(3)C(6)D(10)
合成しました
S(0)A(2)B(1)C(1)D(1)

解答例

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);
			}
		};
		showCards(cards);
		/*
		Card card=drawCard(cards);
		System.out.println(card.rank+"が出ました!");
		cards.put(card,cards.get(card)+1);
		showCards(cards);
		*/
		System.out.print("何枚ひく>>");
		int num=SC.nextInt();
		for(int i=0;i<num;i++){
			Card card=drawCard(cards);
			System.out.println(card.rank+"が出ました!");
			cards.put(card,cards.get(card)+1);
		}
		showCards(cards);
    synthesizeCards(cards); 
    System.out.println("合成しました");
    showCards(cards);
	}

	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;
    }
  }
}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

合成シミュレーターの作成

さて、ここからが本番だ。実際にドラゴンクエストウォークをやっている人が現在のミルドラースの取得状況をもとに後何回ひけばSが作れるのかをシミュレーションしていくシステムを作成していく。

後少しでSができそうだが・・・。どのくらい引けばよいのだろうか・・・

setCardsメソッドの作成

それでは、ミルドラースの実際の取得枚数をセットできるメソッドを作成しよう。

  static void setCards(Map<Card,Integer> cards,int[] cardCounts){
    int idx = 0;
    for (Card key : cards.keySet()) {
      cards.put(key, cardCounts[idx++]);
    }
  }

現在所持している枚数を{0,3,3,1,0}などのint型配列で受け取ればよいだろう。
一つずつ値を取り出しながらMapにセットしていく処理だ。
idx++
と後置したインクリメント記号の性質によって最初は0が取り出される点にも注目。

お題

では、このメソッド使って以下のような処理を作ってみよう。

実行例

カードを任意枚数にセットします
0,2,1,3,4 のように
カンマ区切りでS,A,B,C,Dの枚数を入力してください
>>0,3,3,1,0
指定枚数でセットしました
S(0)A(3)B(3)C(1)D(0)

解答例

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);
			}
		};
		/*
		showCards(cards);
		Card card=drawCard(cards);
		System.out.println(card.rank+"が出ました!");
		cards.put(card,cards.get(card)+1);
		showCards(cards);
		System.out.print("何枚ひく>>");
		int num=SC.nextInt();
		for(int i=0;i<num;i++){
			Card card=drawCard(cards);
			System.out.println(card.rank+"が出ました!");
			cards.put(card,cards.get(card)+1);
		}
		showCards(cards);
    synthesizeCards(cards); 
    System.out.println("合成しました");
    showCards(cards);
    */

    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);

	}

	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++]);
    }
  }
}
class Card{
	Character rank;
	int ratio;
	int point;
	Card(Character rank,int ratio,int point){
		this.rank=rank;
		this.ratio=ratio;
		this.point=point;
	}
}

解説

文字列をカンマ区切りで入力してもらい。splitを使って配列に変換
それをInteger.parseIntしながらint型配列に格納していく。

paizaなどのプログラミング演習サイトなどをやっているととてもよくお世話になる処理だ。イディオムといってもよいだろう。

StreamAPI

Java SE 8からStreamAPIが使えるようになってString[]配列からint[]配列の変換は以下の1行で行えるようになった

int[] cardCounts=Stream.of(numArr).mapToInt(Integer::parseInt).toArray();

興味がある人は調べてみるとよいだろう。

今回はここまで

これで現在持っているカードをセットできるようになった。次回はいよいよシミュレーション部分を作成していく。

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

コメント

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