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で想定したように動くことを確認してください.
// 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)));
}
}
// 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 などさまざまな名で呼ばれ, 親しまれて きた数当てゲームである. ゲームのルールは以下のようになっている.