じゃんけんプログラムを作る
Q&A
Closed
Johnの手はグー、パー、チョキ、グー、グー、パー、チョキ、グー・・・と出力し、Paulの手はランダムに出力するプログラムを作成したいと思い、以下のようにしたのですが、Johnの手が期待しているのとは違って出力されてしまいます。
(ちなみに、このじゃんけんプログラムは3回先に勝ったほうが勝ちとしています)
/** ジャンケン の通常のコンピュータ プレイヤ */
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;
}
}
実行結果
実行結果:
$ 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 Goo
Match 1: Jhon won.
Match 2: Jan Ken Pon!
Match 2-1:
Jhon's hand is Goo
Paul's hand is Par
Match 2: Paul won.
Match 3: Jan Ken Pon!
Match 3-1:
Jhon's hand is Goo
Paul's hand is Choki
Match 3: Jhon won.
Match 4: Jan Ken Pon!
Match 4-1:
Jhon's hand is Goo
Paul's hand is Goo
Aiko de Sho
Match 4-2:
Jhon's hand is Par
Paul's hand is Choki
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
自分のプログラムでは
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;
}
となっており、一番最後にreturn goo;としていることで
おそらく勝敗が決まるごとにわざわざリセットされて、グーから始まるのだとは思いますが、return goo;がないとエラーとなってしまいます。
num 回目の変数は match 勝負目に応じて都度リセットされるわけですから「ゲームを通して今何回目か」を管理する別の変数を用意するとよい(つまり、showHand に渡すのは、match や num じゃなくて「今何回目か」を表す変数に変更する。)と思ったのですが、それをどうやってプログラムすればいいのかがわかりません
ここで、勝敗が決まっても関係なく、グー、パー、チョキ、グー・・・とするにはどうしたらいいですか。
どうすれば(OrderedComputerPlayerのどこをどう直せば)、期待している実行結果となりますか。よろしくお願いいたします。
(ちなみに、期待する実行結果は例えば以下のようになる)
期待している結果
$ 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 Goo
Match 1: Jhon won.
Match 2: Jan Ken Pon!
Match 2-1:
Jhon's hand is Choki
Paul's hand is Par
Match 2: Jhon won.
Match 3: Jan Ken Pon!
Match 3-1:
Jhon's hand is Goo
Paul's hand is Par
Match 3: Paul 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 Par
Paul's hand is Goo
Match 5: John won.
Jhon is a champion.
Jhon: 3 wins
Paul: 2 wins
前回も同じ内容の質問をしましたが、前回の質問は少し説明不足なところがあったかもしれないので、じゃんけんプログラムに必要であろうプログラムをすべて載せておきます。
BigMounthPlayer.java
import javax.swing.*;
/** おしゃべりなプレイヤ */
public class BigMouthPlayer extends NormalComputerPlayer {
/** コンストラクタ */
public BigMouthPlayer(String name) {
super(name);
}
public void youWon() {
super.youWon();
if (wins == matches) { // チャンピオンになったとき
showMessage("I'm the champion.");
}
else {
showMessage("I'm a great Janken Player.");
}
}
public void youLost() {
super.youLost();
showMessage("Damn it!");
}
/** ウィンドウと標準出力にmesgを表示する
* mesgの前に自分の名前をつける
*/
protected void showMessage(String mesg) {
String nameMesg = getName() + ": ";
System.out.println(nameMesg + mesg);
JOptionPane.showMessageDialog(null, nameMesg + mesg);
}
}
Choki.java
public class Choki implements Hand {
public String toString() {
return "Choki";
}
}
Goo.java
public class Goo implements Hand {
public String toString() {
return "Goo";
}
}
GooPlayer.java
public class GooPlayer extends Player {
private Hand goo;
/** コンストラクタ, プレイヤの名前を指定する */
public GooPlayer(String name) {
super(name);
goo = new Goo();
}
/** match回目の勝負のnum回目のプレイヤの手を出す
* Player の showHand をオーバーライド
* 常にグーを出す
*/
public Hand showHand(int match, int num) {
return goo;
}
}
Hand.java
public interface Hand {
/** 手を表す文字列を返す */
String toString();
}
HumanPlayer.java
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;
}
}
Janken.java
/** ジャンケンを開始するためのクラス */
public class Janken {
public static void main(String args[]) {
try {
Judge judge = new Judge(); // 審判の作成
// プレイヤの作成と審判への登録
Player jhon = new OrderedComputerPlayer("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());
}
}
}
Janken2.java
/** ジャンケンを開始するためのクラス */
/* HumanPlayer の名前は
java Janken2 HUMAN_NAME
のようにコマンドライン引数で指定する (指定しなかった場合は GEN になる).
*/
public class Janken2 {
public static void main(String args[]) {
try {
Judge judge = new Judge(); // 審判の作成
// プレイヤの作成と審判への登録
String humanName = "GEN";
if (args.length >= 1) {
humanName = args[0];
}
Player human = new HumanPlayer(humanName);
judge.registPlayer(human);
Player mah = new BigMouthPlayer("MAH");
judge.registPlayer(mah);
// 3回勝負でジャンケンの開始
judge.startJankens(3);
}
catch (JankenException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
JankenErr.java
/** ジャンケンを開始するためのクラス */
public class JankenErr {
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);
Player george = new NormalComputerPlayer("George");
judge.registPlayer(george);
// 3回勝負でジャンケンの開始
judge.startJankens(3);
}
catch (JankenException e) {
System.err.println(e.getMessage());
}
}
}
JankenException.java
* じゃんけんに関する例外 */
public class JankenException extends Exception {
public JankenException() {
super();
}
public JankenException(String msg) {
super(msg);
}
}
Judge.java
import javax.swing.*;
/** じゃんけん の審判のクラス */
public class Judge {
protected Player players[]; // プレイヤの配列
protected int numOfPlayers; // プレイヤの数
protected int wins[]; // 勝ち数を記録する配列
/** コンストラクタ */
public Judge() {
// 2人のプレイヤの配列を作成し、各要素はnullに初期化
players = new Player[2];
players[0] = players[1] = null;
// プレイヤの数は0に初期化
numOfPlayers = 0;
// 2人の勝ち星数の配列を作成し、各要素を0に初期化
wins = new int[2];
wins[0] = wins[1] = 0;
}
/** じゃんけんをするプレイヤを知らされる */
public void registPlayer(Player player) throws JankenException {
if (numOfPlayers < players.length) {
// まだ余裕があるので登録
players[numOfPlayers] = player;
numOfPlayers++; // プレイヤの数を増やす
}
else {
throw new JankenException("too many players");
}
}
/** matches回勝負のじゃんけんを開始する
* つまり、誰かがmatches回勝つまでじゃんけんをする
*/
public void startJankens(int matches) throws JankenException {
if (numOfPlayers < players.length) {
throw new JankenException("need more players");
}
// プレイヤにmatches回勝負であることを知らせる
notifyMatches(matches);
int match = 1; // match回目の勝負
while (!isEndOfJankens(matches)) { // 誰かがmatches回勝つまで繰り返す
startOneJanken(match); // match回目のじゃんけんを行う
match++;
}
// matches回勝負のチャンピオンを調べる
Player[] champions = getChampions(matches);
// チャンピオンのメッセージを表示
showChampionsMessage(champions);
}
/** matches回勝負のじゃんけんが終わったか判定 */
protected boolean isEndOfJankens(int matches) {
// プレイヤの1人でもmatches回勝っていたら終わり
for (int i = 0; i < numOfPlayers; i++) {
if (wins[i] == matches) { // matches回勝っていた
return true;
}
}
return false; // 誰もmatches回勝っていない
}
/** match回目のじゃんけんを開始する */
protected void startOneJanken(int match) {
int num = 0; // 何回目の手か
Player[] winners; // 勝者の配列
do { // 勝者が決まるまで繰り返す
num++; // 手の回数を増やす
// 掛け声
showJankenMessage(match, num);
// 各プレイヤの手を得る
Hand hands[] = getHands(match, num);
// 手を表示
showHandsMessage(hands, match, num);
// 勝者を判定する
winners = judge(hands);
} while (winners == null);
// 勝者を表示
showWinnersMessage(winners, match);
// プレイヤに勝敗を知らせる
notifyResult(winners);
}
/** match回目の勝負のnum回目の全プレイヤの手を配列で返す */
protected Hand[] getHands(int match, int num) {
// players[i]の手はhands[i]に入れる
Hand hands[] = new Hand[numOfPlayers];
for (int i = 0; i < numOfPlayers; i++) {
hands[i] = players[i].showHand(match, num);
}
return hands;
}
/** ジャンケンの勝負を判定をする。
* 勝負がついた場合は 勝者のPlayerの配列を返す。
* あいこの場合は null を返す。
* なお、2人勝負の場合は常に勝者は1人である
*/
protected Player[] judge(Hand hands[]) {
// 勝者の判定をする
Player[] winner;
if ((hands[0] instanceof Goo && hands[1] instanceof Choki)
|| (hands[0] instanceof Choki && hands[1] instanceof Par)
|| (hands[0] instanceof Par && hands[1] instanceof Goo)) {
// players[0]の勝ち
winner = new Player[1];
winner[0] = players[0];
wins[0]++;
}
else if ((hands[1] instanceof Goo && hands[0] instanceof Choki)
|| (hands[1] instanceof Choki && hands[0] instanceof Par)
|| (hands[1] instanceof Par && hands[0] instanceof Goo)) {
// players[1]の勝ち
winner = new Player[1];
winner[0] = players[1];
wins[1]++;
}
else {
// あいこ
winner = null;
}
return winner;
}
/** 各プレイヤの勝ち数を調べてチャンピオンを決める
* つまりmatches回勝ったプレイヤがチャンピオンである
* 3人以上の場合はチャンピオンが2人以上出る場合がある
*/
protected Player[] getChampions(int matches) {
// チャンピオンは最大 (プレイヤ数 -1)人いるので
// numOfPlayers-1 の長さの配列を作っておく
Player champions[] = new Player[numOfPlayers-1];
// 各プレイヤの勝ち数を調べてチャンピオンを決める
int championsIndex = 0;
for (int playersIndex = 0; playersIndex < numOfPlayers;
playersIndex++) {
if (wins[playersIndex] == matches) {
// matches回勝っているのでチャンピオン
champions[championsIndex] = players[playersIndex];
championsIndex++;
}
}
// 配列championsの残りの要素はnullにする
for (; championsIndex < champions.length; championsIndex++) {
champions[championsIndex] = null;
}
return champions;
}
/** 何回勝負かプレイヤに知らせる */
protected void notifyMatches(int matches) {
for (int i = 0; i < numOfPlayers; i++) {
players[i].setMatches(matches);
}
}
/** じゃんけんの結果を各プレイヤに知らせる
* 引数winnersは勝者の配列
*/
protected void notifyResult(Player[] winners) {
// 外側のループはプレイヤのループ
for (int playersIndex = 0;
playersIndex < numOfPlayers; playersIndex++) {
boolean won = false; // players[playersIndex]が勝ったかどうか
// players[playersIndex]が配列winnersに含まれているか調べる
// 内側のループは勝者のループ
for (int winnersIndex = 0; winnersIndex < winners.length;
winnersIndex++) {
if (winners[winnersIndex] == players[playersIndex]) {
// players[playersIndex]は勝った
won = true;
}
}
// プレイヤに勝敗を知らせる
if (won) players[playersIndex].youWon();
else players[playersIndex].youLost();
}
}
/** ウィンドウと標準出力にメッセージを表示する
* showXxxxMessageメソッドから呼ばれる下請メソッド
*/
protected void showMessage(String mesg) {
System.out.println(mesg);
JOptionPane.showMessageDialog(null, mesg);
}
/** じゃんけんぽん のメッセージを表示 */
protected void showJankenMessage(int match, int num) {
if (num == 1) { // 1回目は じゃんけんぽん
showMessage("Match " + match + ": Jan Ken Pon!");
}
else { // 2回目以降は あいこでしょ
showMessage("Aiko de Sho");
}
}
/** プレイヤの手のメッセージを表示 */
protected void showHandsMessage(Hand[] hands, int match, int num) {
String handMesg = "Match " + match + "-" + num + ":\n";
for (int i = 0; i < numOfPlayers; i++) {
handMesg += players[i].getName() + "'s hand is "
+ hands[i].toString() + "\n";
}
showMessage(handMesg);
}
/** ジャンケンの各回の勝者のメッセージを表示 */
protected void showWinnersMessage(Player[] winners, int match) {
String mesg = "Match " + match + ": ";
for (int i = 0; i < winners.length; i++) {
mesg += winners[i].getName() + " won.\n";
}
showMessage(mesg);
}
/** チャンピオンのメッセージを表示 */
protected void showChampionsMessage(Player[] champions) {
// チャンピオンのメッセージ
String championMesg = "";
for (int i = 0; i < champions.length && champions[i] != null; i++) {
championMesg += champions[i].getName() + " is a champion.\n";
}
// 各プレイヤが何勝したかのメッセージ
String winNumMesg = "";
for (int i = 0; i < numOfPlayers; i++) {
winNumMesg += players[i].getName() + ": " + wins[i] + " wins\n";
}
// メッセージを表示
showMessage(championMesg + winNumMesg);
}
}
NormalComputerPlayer.java
/** ジャンケン の通常のコンピュータ プレイヤ */
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;
}
}
}
OrderedComputerPlayer.java
/** ジャンケン の通常のコンピュータ プレイヤ */
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;
}
}
Par.java
public class Par implements Hand {
public String toString() {
return "Par";
}
}
Player.java
/** ジャンケン プレイヤの抽象クラス */
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);
}