一般化数当てゲーム

数当てゲームのようなゲームを一般化したものを考える. このような問題を一般化したクラスProblemを以下のように定義して,Problem.javaという名前で保存する.
abstract class Problem{
    // 解答集合のサイズを得る
    abstract int size();
    // 解答集合のindex番目を得る
    abstract String get(int index);
    // 入力が解答集合の中に含まれているかの判定
    boolean isValid(String str){
	for(int i=0;i< size();i++){
	    if(str.equals(get(i))) 
		return true;
	}
	return false;
    }
    // 問題の最初で出力する文字列
    abstract String welcomeMessage();
    // 入力と正解との関係を返す文字列の配列
    abstract String[] answerStrings();
    // 入力と正解との関係を判定する
    // 0の時は正解
    abstract int answerType(String str);
    // 正解がs1と仮定した時のs2の関係
    abstract int answerType(String s1,String s2);
}
前回の問題にしたがって,Problemのサブクラスとしては,以下のようなもの(HighLowProblem.java)が作れる
// Randomクラスを使うため
import java.util.*;
// 問題を保持するためのクラス
class HighLowProblem extends Problem{
    // 
    int n;
    // 正解の文字列を入れる変数
    String answerString;
    // 解答集合の要素数
    int size(){
	return n;
    }
    // 解答集合のindex番目の要素
    String get(int index){
	return Integer.toString(index+1);
    }
    // コンストラクタ
    HighLowProblem(int maxN,int index){
	n=maxN;
	if(index< 0){
	    // 乱数のタネを作る
	    Random r=new Random();
	    // [0 .. size()-1]の乱数を作って,それを元に解を作る 
	    answerString=get(r.nextInt(n));
	}
	else
	    answerString=get(index);
    }
    HighLowProblem(int maxN){
	this(maxN,-1);
    }
    // 判定結果のtype番目の文字列
    String[] answerStrings(){
	String answers[]={"Correct","VERY LOW","LOW","HIGH","VERY HIGH"};
	return answers;
    }
    // 正解がaの時に文字列strと比較して,判定結果を返す
    int answerType(String a,String str){
	int answer=Integer.parseInt(a);
	int x=Integer.parseInt(str);
	if(answer==x) return 0;
	else if(x*2<=answer) return 1;
	else if(x< answer) return 2;
	else if(answer*2<=x) return 4;
	else return 3;
    }
    // 文字列strと正解を比較して,判定結果を返す
    int answerType(String str){
	return answerType(answerString,str);
    }
    // 最初のメッセージ
    String welcomeMessage(){
	return "Guess a number(1-"+n+") ";
    }
}
これを使った数当てゲームを遊ぶためのプログラムGameMaster.javaは以下のようになる.
// 入出力をおこなうため
import java.io.*;
// 問題を出題するためのクラス
class GameMaster{
    static void master(Problem problem) throws IOException{
	BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
	int count=1;
        System.out.println(problem.welcomeMessage());
	for(;;){
	// キーボードからの一行読み込み
	    String s=d.readLine();
	    if(!problem.isValid(s)){
		System.out.println("Illegal format. Input Again.");
		continue;
	    }
	    int type=problem.answerType(s);
	    if(type==0)break;
	    System.out.println(problem.answerStrings()[type]);
	    count++;
	}
	System.out.println("Correct Answer in "+count);
    }
    static public void main(String[] args) throws IOException{
	master(new HighLowProblem(10000,-1));
    }
}
まずは,この3つのファイルを保存して,
javac GameMaster.java
を実行してコンパイルし(依存関係のあるファイルは自動的にコンパイルしてくれます).
java GameMaster
で想定したように動くことを確認してください.

問題

以下のプログラムの(1)-(5)を埋めて, HighLowGameを解くプログラムを作成しなさい.
// Randomを使うため
import java.util.*;
// 
class GameSolver{
    static int solve(Problem problem){
	Random r=new Random();
	// index 番目が解である可能性があることを保持
	boolean canBeAnswer[]=new boolean[problem.size()];
	// 最初はすべて解である可能性がある.
	for(int i=0;i< problem.size();i++)canBeAnswer[i]=true;
	for(int count=1;;count++){
	    int numCanBeAnswer=0;
	    for(int j=0;j< problem.size();j++)
		if(canBeAnswer[j]) {
		    // numCanBeAnswerを1増やす
			   (1) ;
		}
	    // kに[0 .. numCanBeAnswer-1]の乱数を入れる
	    int k= (2) ;
	    String nextQuestion="";
	    for(int j=0;j< problem.size();j++){
		if(canBeAnswer[j]){
		    if(k==0) {
			// nextQuestionに問題の解集合のj番目を入れる
			nextQuestion= (3) ;
			break;
		    }
		    else k--;
		}
	    }
	    System.out.println(nextQuestion);
            // 正解と文字列nextQuestion を比較して,判定結果を得る.
	    int type= (4);
	    System.out.println(problem.answerStrings()[type]);
	    if(type==0) return count;
	    for(int j=0;j< problem.size();j++){
		if(canBeAnswer[j] && 
		   problem.answerType(problem.get(j),nextQuestion)!=type){
		    // canBeAnswer[j]をfalseにする
		    (5) ;
		}
	    }
	}
    }
    static public void main(String[] args){
	System.out.println(solve(new HighLowProblem(10000,-1)));
    }
}


プログラムが動くことが確かめれたら,以下のプログラムに関しても動くように GameSolverのメソッドmainを書き換えなさい.
// Randomを使うため
import java.util.*;

class MooProblem extends Problem{
    String answerString;
    String mooNumbers[];
    int size(){
	return 10*9*8*7;
    }
    String get(int index){
	return mooNumbers[index];
    }
    static void swap(char cs[],int i,int j){
	char c=cs[i];
	cs[i]=cs[j];
	cs[j]=c;
    }
    MooProblem(int index){
	mooNumbers=new String[size()];
	char tmpDigits[]={'0','1','2','3','4','5','6','7','8','9'};
	int count=0;
	for(int i0=0;i0< 10;i0++){
	    swap(tmpDigits,0,i0);
	    for(int i1=1;i1< 10;i1++){
		swap(tmpDigits,1,i1);
		for(int i2=2;i2< 10;i2++){
		    swap(tmpDigits,2,i2);
		    for(int i3=3;i3< 10;i3++){
			swap(tmpDigits,3,i3);
			mooNumbers[count]=new String(tmpDigits,0,4);
			count++;
			swap(tmpDigits,3,i3);
		    }
		    swap(tmpDigits,2,i2);
		}
		swap(tmpDigits,1,i1);
	    }
	    swap(tmpDigits,0,i0);
	}
	if(index>=0){
	    answerString=get(index);
	}
	else{
	    // 乱数のタネを作る
	    Random r=new Random();
	    // [0 .. size()-1]の乱数を作って,それを元に解を作る 
	    answerString=get(r.nextInt(size()));
	}
    }
    MooProblem(){
	this(-1);
    }
    String welcomeMessage(){
	return "Input a MOO number";
    }
    String[] answerStrings(){
	String answers[]={"4B0C","","2B2C","1B3C","0B4C",
			  "","3B0C","2B1C","1B2C","0B3C",
			  "","","2B0C","1B1C","0B2C",
			  "","","","1B0C","0B1C",
			  "","","","","0B0C"};
	return answers;
    }
    // 正解と入力との間のbullを数える
    int getBull(String a,String s){
	int bull=0;
	for(int i=0;i< 4;i++)
	    if(a.charAt(i)==s.charAt(i))
		bull++;
	return bull;
    }
    // 別のMoo数との間のcowを数える
    int getCow(String a,String s){
	int cow=0;
	for(int i=0;i< 4;i++)
	    for(int j=0;j< 4;j++)
		if(i!=j && a.charAt(i)==s.charAt(j)){
		    cow++;
		    break;
		}
	return cow;
    }
    int answerType(String a,String s){
	int bull=getBull(a,s);
	int cow=getCow(a,s);
	return (4-(bull+cow))*5+(4-bull);
    }
    int answerType(String s){
	return answerType(answerString,s);
    }
}
これは数当てゲーム MOO に対応したものである.

MOO は, Hit & Blow, Cow & Bull などさまざまな名で呼ばれ, 親しまれて きた数当てゲームである. ゲームのルールは以下のようになっている.

提出はGameSolver.javaファイル.