23JON
@23JON (23 JON)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

じゃんけんプログラムを規則性のあるパターンに置き換えたい

Q&A

Closed

以下の3つのプログラムをいじって、Jhonの手を
グー、パー、チョキ、グー、グー、パー、チョキ、グー、…
と規則性のある手を出したいのですが、どうやってプログラムしたらいいのかが全くわかりません。
(ちなみに、3回先に勝利したほうが勝ちとしています)

①
public class NormalComputerPlayer extends Player {
    protected Hand  goo, choki, par;
    protected java.util.Random random; // 手を決めるときに使う乱数

    /** コンストラクタ, プレイヤの名前を指定する */
    public NormalComputerPlayer(String name) {
    super(name);
    // 手を設定する
    goo = new Goo();
    choki = new Choki();
    par = new Par();
    random = new java.util.Random();
    }

    /** match回目の勝負のnum回目のプレイヤの手を出す
     *  Player の showHand をオーバーライド
     */
    public Hand showHand(int match, int num) {
    int hand = random.nextInt(3);
    if (hand == 0) {
        return goo;
    } else if (hand == 1) {
        return choki;
    } else {
        return par;
    }
    }
}
②
import javax.swing.*;

public class HumanPlayer extends Player {
    protected Hand   hands[];  // プレイヤの手

    /** コンストラクタ */
    public HumanPlayer(String name) {
    super(name);
    // 手を設定する
    hands = new Hand[3];
    hands[0] = new Goo();
    hands[1] = new Choki();
    hands[2] = new Par();
    }

    /** match回目の勝負のnum回目のプレイヤの手を選ぶ
     *  Player の showHand をオーバーライド
     */
    public Hand showHand(int match, int num) {
    // ダイアログを表示して手を選ばせる
    //  selectedは選んだ手の,配列handsの要素番号になっている
    int selected =
        JOptionPane.showOptionDialog(null, "Select Your Hand",
                     "Select Your Hand",
                     JOptionPane.YES_NO_OPTION,
                     JOptionPane.QUESTION_MESSAGE, 
                     null, hands, null);
    Hand hand = hands[selected];
    return hand;
    }
}
③
/** ジャンケンを開始するためのクラス */
public class Janken {
    public static void main(String args[]) {
    try {
        Judge judge = new Judge(); // 審判の作成

        // プレイヤの作成と審判への登録
        Player jhon = new NormalComputerPlayer("Jhon");
        judge.registPlayer(jhon);
        Player paul = new NormalComputerPlayer("Paul");
        judge.registPlayer(paul);

        // 3回勝負でジャンケンの開始
        judge.startJankens(3);
    }
    catch (JankenException e) {
        System.err.println("Error: " + e.getMessage());
    }
    }
}

①のプログラムをいじって、規則性のあるものにするのかな?と思い、とりあえず①’を作ってみたのですが、余計にわからなくなりました

/** ジャンケン の通常のコンピュータ プレイヤ */
public class NormalComputerPlayer extends Player {
    protected Hand  goo, choki, par;
    protected java.util.Random random; // 手を決めるときに使う乱数

    /** コンストラクタ, プレイヤの名前を指定する */
    public NormalComputerPlayer(String name) {
    super(name);
    // 手を設定する
    goo = new Goo();
    choki = new Choki();
    par = new Par();

    }

    /** match回目の勝負のnum回目のプレイヤの手を出す
     *  Player の showHand をオーバーライド
     */
    public Hand showHand(int match, int num) {
    int hand ,i;
    if (hand == 0) {
        return goo;
    } else if (hand == 1) {
        return par;
    } else {
        return choki;
    }
        for(i=0;i<100;i++){
          hand=i%3;
        }
    }
}

どうすれば期待しているものが出力されるのでしょうか。
よろしくお願いします

ちなみに、今のままでは、ランダムになっているので、Johnの手は以下のようになります。

$ java Janken
Match 1: Jan Ken Pon!
Match 1-1:
Jhon's hand is Goo
Paul's hand is Goo

Aiko de Sho
Match 1-2:
Jhon's hand is Goo
Paul's hand is Par

Match 1: Paul won.

Match 2: Jan Ken Pon!
Match 2-1:
Jhon's hand is Par
Paul's hand is Goo

Match 2: Jhon won.

Match 3: Jan Ken Pon!
Match 3-1:
Jhon's hand is Par
Paul's hand is Goo

Match 3: Jhon won.

Match 4: Jan Ken Pon!
Match 4-1:
Jhon's hand is Goo
Paul's hand is Par

Match 4: Paul won.

Match 5: Jan Ken Pon!
Match 5-1:
Jhon's hand is Goo
Paul's hand is Choki

Match 5: Jhon won.

Jhon is a champion.
Jhon: 3 wins
Paul: 2 wins
0

5Answer

  1. Javaに限らずどの言語にも共通する基本的な内容ですが、returnの後に何か処理を書いてもプログラムはそこには到達できないです。
  2. ご自身?で追加しているiは何を指しているのでしょうか? どういう意図で①から①’へ編集したのか分かりかねます。(意図がわからないとアドバイスが難しいです。)
  3. じゃんけんの手をshowHandメソッドで決定していると思うのですが、そのshowHandメソッド(継承元のPlayerクラス)について説明いただきたいです。(matchnumを%3すればいいのかもしれませんが、説明がないと判断できません。)
0Like

Comments

  1. @23JON

    Questioner

    回答ありがとうございます。

    1.
    確かに、おっしゃる通りです。基礎的なことを見落としていました。

    2.
    iは、自分でもよく意味が分かっておらず適当に設定しただけで、
    要は手の種類は3種類なので、iを設置してそれを3で割ったあまりをhandに代入することで規則性のある手を出せると思ったのですが、どう考えてもおかしいですね...。

    3.
    分かりました。
    今から、player.javaを以下に示します。
  2. 書き換えて規則的な手の出し方が可能になったとのことですので、あとは2人を別々のクラスにすればOKだと思います。
    例えば
    Jhon→`OrderedComputerPlayer`(`showHand`メソッドを変更して規則的な手が出るクラス)
    Paul→`NormalComputerPlayer`(変更前のランダムな手を出すクラス)
/** ジャンケン プレイヤの抽象クラス */
public abstract class Player {
    protected String name;     // プレイヤの名前
    protected int    matches;  // matches回勝負(matches回先に勝った方が勝ち)
    protected int    wins;     // プレイヤの勝利数
    protected int    losts;    // プレイヤの負け数

    /** コンストラクタ, プレイヤの名前を指定する */
    public Player(String name) {
	this.name = name;   // プレイヤの名前
	wins = losts = 0;   // 勝敗を初期化
    }

    /** プレイヤの名前を返す */
    public String getName() {
	return name;
    }

    /** 何回勝負か設定するメソッド
     */
    public void setMatches(int matches) {
	this.matches = matches;
    }

    /** 
     *   勝ったことを知らされるメソッド
     */
    public void youWon() {
	wins++;
    }

    /** 
     *   負けたことを知らされるメソッド
     */
    public void youLost() {
	losts++;
    }


    /** match回目の勝負のnum回目のプレイヤの手を出す
     *   抽象メソッドなので,サブクラスでオーバーライドする
     */
    public abstract Hand showHand(int match, int num);
}
0Like

あと、①のプログラムを書き換えて、
グー、パー、チョキ、グー、グー、パー、チョキ、グー…
となるプログラムはかけたのですが、JohnとPaulの手が同じように出力されてしまいます。

$ java Janken
Match 1: Jan Ken Pon!

Match 1-1:
Jhon's hand is Goo
Paul's hand is Goo

Aiko de Sho
Match 1-2:
Jhon's hand is Par
Paul's hand is Par

Aiko de Sho
Match 1-3:
Jhon's hand is Choki
Paul's hand is Choki

Aiko de Sho
Match 1-4:
Jhon's hand is Goo
Paul's hand is Goo

Aiko de Sho
Match 1-5:
Jhon's hand is Goo
Paul's hand is Goo

Aiko de Sho
Match 1-6:
Jhon's hand is Par
Paul's hand is Par

Aiko de Sho
Match 1-7:
Jhon's hand is Choki
Paul's hand is Choki

Aiko de Sho
Match 1-8:
Jhon's hand is Goo
Paul's hand is Goo

これだと永遠にじゃんけんが終わりません。
どうすれば、Jhonの手だけを期待しているような順序(グー、パー、チョキ、グー、グー、パー、チョキ、グー…)で出せるのでしょうか。

0Like

すみません。
Johnの手はきちんと期待している結果になったと思ったのですが、
勘違いでした。

/** ジャンケン の通常のコンピュータ プレイヤ */
public class OrderedComputerPlayer extends Player {
    protected Hand  goo, choki, par;
    // 手を決めるときに使う乱数

    /** コンストラクタ, プレイヤの名前を指定する */
    public OrderedComputerPlayer(String name) {
	super(name);
	// 手を設定する
	goo = new Goo();
	choki = new Choki();
	par = new Par();
	
    }

    /** match回目の勝負のnum回目のプレイヤの手を出す
     *  Player の showHand をオーバーライド
     */
    public Hand showHand(int match, int num) {
        
	switch(num%4){
	case 1: return goo;
        case 2: return par;
        case 3: return choki; 
        case 0: return goo; 
        }num++;  return goo;
    }
}

どうすればグー、パー、チョキ、グー、グー、パー、チョキ、グー…ってJohnの手は出してくれるのでしょうか。
先生にも聞いてヒントも頂いた結果ここまでたどり着けて、もう少しな気がするんです。

お願いします。

0Like

@_y_s さんがコメントされているようにクラスを分けるのが一番単純だと思いますが、ストラテジーパターン使った別の方法を少し紹介します。
ラムダ式使えばもう少し簡単になりそうでしたが、従来通りStrategyを継承したクラスとして実装しています。

処理とはあまり関係ありませんが、自分が実装しやすいようにHandをenumに変更しています。

Hand.java

じゃんけんの3つの手を表す以上のことはやってなさそうでしたので置き換えました。
後でintから変換しやすいようにstaticメソッド一つ生やしています。

Hand.java
public enum Hand {
    Gu,
    Choki,
    Pa,
    
    public static Hand fromInt(int i) {
        switch(i) {
            case 0:
                return Gu;
            case 1:
                return Choki;
            case 2:
                return Pa;
            default:
                throw new RuntimeException("Illegal Argument!");
        }
    }
}

ComputerPlayer.java

CPUが自動で手を出すPlayerクラスの実装です。
後述するHandStrategyインターフェースの実装を受け取って手を出すようにしています。

ComputerPlayer.java

public class ComputerPlayer extends Player {
    private HandStrategy strategy;
    
    public ComputerPlayer(String name, HandStrategy strategy) {
        super(name);
        this.strategy = strategy;
    }
    
    @Override
    public Hand showHand(int match, int num) {
        return strategy.getHand(num);
    }
}

HandStrategy.java

手を出すストラテジーのための関数型のインターフェースです。
現在の回数に応じた値を出すことを考慮してnumを受け取るようにしています。

HandStrategy.java
public interface HandStrategy {
    public Hand getHand(int num);
}

RandomHand.java

ランダムな手を出すStrategyの実装
seed値も指定できるようにしています。

RandomHand.java
public class RandomHand implements HandStrategy {
    private Random rand;
    
    public RandomHand() {
        this.rand = new Random();
    }
    
    public RandomHand(long seed) {
        this.rand = new Random(seed);
    }
    
    @Override
    public Hand getHand(int num) {
        return Hand.fromInt(rand.nextInt(3))
    }
}

ListHand.java

固定された順番で手を出すstrategyの実装
コンストラクタでHandのリストを渡すとそのとおりに手を出すようにしています。

ListHand.java
public class ListHand implements HandStrategy {
    private List<Hand> handList;
    
    public ListHand(List<Hand> list) {
        this.handList = list;
    }
    
    @Override
    public Hand getHand(int num) {
        return handList.get(num%handList.size())
    }
}

実行例

var john = new ComputerPlayer("John", new RandomHand(123));
var paul = new ComputerPlayer("Paul", new ListHand(Arrays.asList(Hand.Gu, Hand.Choki, Hand.Pa, Hand.Pa)));

IntStream.range(0, 10).forEach(i -> {
    System.out.println("count:" + i);
    System.out.println("  John:" + john.showHand(0, i).name());
    System.out.println("  Paul:" + paul.showHand(0, i).name());
});

実行結果

count:0
  John:Pa
  Paul:Gu
count:1
  John:Pa
  Paul:Choki
count:2
  John:Pa
  Paul:Pa
count:3
  John:Pa
  Paul:Pa
count:4
  John:Gu
  Paul:Gu
count:5
  John:Pa
  Paul:Choki
count:6
  John:Gu
  Paul:Pa
count:7
  John:Choki
  Paul:Pa
count:8
  John:Pa
  Paul:Gu
count:9
  John:Choki
  Paul:Choki
0Like

Your answer might help someone💌