blog.waterlow.work

Ruby, Rails, js, etc...

【OPP】【Java】【HitAndBlow】初めてクラス図書いてみた

いままでさぼってきたことをやってみます。 以前HitAndBlowというゲームを作りました。 詳細、ルールは以下。

Numer0n

まとめると…

  • プレイヤーAが、0-9までの数字のうち4つを使って、4桁の番号を作成する。重複した番号は使えない。
  • プレイヤーBは相手の番号を推理してコールする。プレイヤーAはコールされた番号と自分の番号を見比べ、コールされた番号がどの程度合っているかを発表する。数字と桁が合っていた場合は「hit」(ヒット)、数字は合っているが桁は合っていない場合は「blow」(ブロー)となる。(例としてプレイヤーAの番号が「0123」・コールされた番号が「0134」であった場合は、4桁のうち「0」「1」は桁の位置が合致しているため、「3」は数字自体は合っているが桁の位置が違うためblow。hitが2つ・blowが1つなので、「2hit-1blow」となる。)
  • プレイヤーBは4hitが出るまで繰り返す。

最初に作ったのは11月ぐらいなのですが、それからAI作ったり最善手求めてみたりといろいろやってきました。 設計が悪いとの指摘を受けたので設計をクラス図から書き直してみます!

f:id:waterlow2013:20131224234443p:plain

役割とゲームの流れとしては + HitAndBlowのインスタンスがmakeOneRandomCodeメソッドで、0-9までの数字(CHAR_USED_IN_THIS_GAME)のうち4つを使って、4桁の番号(String : ANSWER_OF_THIS_GAME)を作成する。 + playerは相手の番号を推理してinputメソッドで数字を入力する。playerは入力されたされた番号と自分の番号をanswerHitBlowResultメソッドで比較、コールされた番号がどの程度合っているかを発表する。結果はHitBlowResultのインスタンスを作って毎ターン出力する。 + playerは4hitが出るまで繰り返す。

クラス図を書いてみると設計より先に実装(コーディング)をやってしまっている感じがよくわかります。 次回作る時はクラス図を書いてテストも書いてしまった上で実装をしたほうが勉強にはなるのかもしれません。

ソースコードは以下になります。 (コメントもないしとても読みにくいですが…!コメントの書き方は今後勉強していきたいです。)

class HitAndBlow

|java| package play;

import java.util.ArrayList; import java.util.Arrays; import java.util.Random;

public class HitAndBlow {

/**
 * @param args
 */

static final String[] CHAR_USED_IN_THIS_GAME = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
final static int CODE_LENGTH = 4;
private final String ANSWER_OF_THIS_GAME = makeOneRandomCode();
private boolean gameIsEnd = false;
private Guest player = new Guest();

private void startGame(){
    System.out.println(ANSWER_OF_THIS_GAME);
    while(!gameIsEnd){
        this.gameIsEnd = this.startThisTurn();
    }
}

private boolean startThisTurn(){
    String inputOfThisTurn = player.input();
    HitBlowResult hitBlowResultOfThisTurn = HitAndBlow.answerHitBlowResult(ANSWER_OF_THIS_GAME, inputOfThisTurn);
    System.out.println(hitBlowResultOfThisTurn);
    return judgeGameEnd(hitBlowResultOfThisTurn);
}

private static boolean judgeGameEnd(HitBlowResult result){
    boolean gameIsEnd = (result.getHitCount() == CODE_LENGTH);
    return gameIsEnd;
}

private static String makeOneRandomCode(){
    ArrayList<String> al =
            new ArrayList<String>(Arrays.asList(CHAR_USED_IN_THIS_GAME));
    String str = "";
    for (int ai = 0; ai < CODE_LENGTH; ai++){
        Random random = new Random();
        int tmp = random.nextInt(al.size());
        str += al.get(tmp);
        al.remove(tmp);
    }
    return str;
}

private static HitBlowResult
answerHitBlowResult(String code1, String code2){
    int hitCount = 0;
    int blowCount = 0;
    for (int i1 = 0; i1 < CODE_LENGTH; i1++){
        for (int i2 = 0; i2 < CODE_LENGTH; i2++){
            if(i1 == i2) {
                if(code1.charAt(i1) == code2.charAt(i2)) hitCount++;
            }else{
                if(code1.charAt(i1) == code2.charAt(i2)) blowCount++;
            }
        }
    }
    HitBlowResult r = new HitBlowResult(code2, hitCount, blowCount);
    return r;
}

} ||<

class Guest

|java| package play;

import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays;

public class Guest {

/**
 * @param args
 */

private final int CODE_LENGTH =
        HitAndBlow.CODE_LENGTH;
private static final String[] CHAR_USED_IN_THIS_GAME =
        HitAndBlow.CHAR_USED_IN_THIS_GAME;

private final String INPUT_REQUEST =
        CODE_LENGTH + "桁の整数を入力してください.";
private final String DIGIT_ERROR =
        "桁数が異なります.";
private final String CANT_USE =
        "この文字列はゲームでは使えません.";
private final String ONE_MORE =
        "もう一度入力してください.";

protected String input(){
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    System.out.println(INPUT_REQUEST);

    String rl = "";
    int loopCount = 0;

    while (rl.length() != CODE_LENGTH) {
        if(loopCount > 0) System.out.println(ONE_MORE);
        try {
            rl = br.readLine();
            loopCount++;
        } catch (Exception e) {
            System.out.println(e);
        }
        if (rl.length() != CODE_LENGTH) {
            System.out.println(DIGIT_ERROR);
        }
        if (!canUseThisCode(rl)) {
            System.out.println(CANT_USE);
            rl = "";
        }
    }
    return rl;
}

private static boolean canUseThisCode(String code){
    ArrayList<String> al = new ArrayList<String>(Arrays.asList(CHAR_USED_IN_THIS_GAME));
    for (int i = 0; i < code.length(); i++) {
        String tmp = code.substring(i, i + 1);
        if(!al.contains(tmp)) return false;
        else al.remove(tmp);
    }
    return true;
}

} ||<

class HitBlowResult

|java| package play; public class HitBlowResult{

private String code;
private int hitCount;
private int blowCount;

HitBlowResult(String code, int hitCount, int blowCount) {
    this.code = code;
    this.hitCount = hitCount;
    this.blowCount = blowCount;
}

int getHitCount() {
    return hitCount;
}

@Override
public String toString() {
    return "(code, hitCount, blowCount) = (" + code + ", " + hitCount + ", " + blowCount + ")";
}

} ||<