限定じゃんけんで手を

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
peyoun9

限定じゃんけんで手を

#1

投稿記事 by peyoun9 » 9年前

C++入門者です。ただいま新版明快C++入門編を読みながら独学でC++の勉強をしています。
演習問題に人間とコンピュータとが対戦するジャンケンプログラムを作成せよ。という問題があり、6回じゃんけんをしてグー・チョキ・パーがそれぞれ2回ずつしか出せない(限定じゃんけん)のプログラムを作ろうと1週間ほど取り組みましたが、うまくいきません。以下にそのコードを示します。

cout << "手を選んでください(1…グー/3…チョキ/9…パー):";

に対し自分がグーをだすなら1を入力するわけですが、2回目のグー(すなわち1)を入力した次の手が通りません。
2回目の3…チョキ/9…パーの後ではそういったことが起きず、どうしてそうなるのか、どうやれば2回目のグーの次の手を通せるかをどなたか教えていただけませんでしょうか。

ちなみに敵の手を選ぶのに配列を利用していますが、配列についてはまだ学習しておりません。やり方をネットからみつけて使わせていただきました。配列については後日きちんと(テキストの配列の章で)学習したいと思います。

環境はwindows10 pro  VisualStudio2015です。

code

#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;
int main()
{
int i, j;
int games = 6;
int stock[] = { 1,3,9,9,3,1 };
int hand;
int sum = 0;

srand(time(NULL));

for (i = 0; i < 6; i++) {

if (i == 0) {
Redo0:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 1: sum += hand; break;
case 3: sum += hand; break;
case 9: sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo0;
}cout << "sumは" << sum;
}

else if (i == 1) {
Redo1:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 2: sum += hand; break;
case 4: sum += hand; break;
case 6: sum += hand; break;
case 10: sum += hand; break;
case 12: sum += hand; break;
case 18: sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo1;
}cout << "sumは" << sum;
}

else if (i == 2) {
Redo2:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 5: sum += hand; break;
case 7: sum += hand; break;
case 11: sum += hand; break;
case 13: sum += hand; break;
case 15: sum += hand; break;
case 19: sum += hand; break;
case 21: sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo2;
}cout << "sumは" << sum;
}

else if (i == 3) {
Redo3:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 8: sum += hand; break;
case 14: sum += hand; break;
case 16: sum += hand; break;
case 20: sum += hand; break;
case 22: sum += hand; break;
case 24: sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo3;
}cout << "sumは" << sum;
}

else if (i == 4) {
Redo4:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 17: sum += hand; break;
case 23: sum += hand; break;
case 25: sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo4;
}cout << "sumは" << sum;
}

else if (i == 5) {
Redo5:
;
cout << "手を選んでください(1…グー/3…チョキ/9…パー):";
cin >> hand;

switch (sum + (hand == 1 || hand == 3 || hand == 9)) {
case 26: cout << (sum += hand); sum += hand; break;
default: hand = 0;
cout << "カードがありません。別の";
goto Redo5;
}cout << "sumは" << sum;
}

switch (hand) {
case 1: cout << "あなたの選んだカード\n『グー』\n"; break;
case 3: cout << "あなたの選んだカード\n『チョキ』\n"; break;
case 9: cout << "あなたの選んだカード\n『パー』\n"; break;
}


int index = rand() % (games - i);

switch (stock[index]) {
case 1: cout << "敵のカード『グー』\n"; break;
case 3: cout << "敵のカード『チョキ』\n"; break;
case 9: cout << "敵のカード『パー』\n"; break;
}

if (hand == stock[index])
cout << "『\aドロー』\n";
else if (hand == 1 && stock[index] == 3 || hand == 3 && stock[index] == 9 ||
hand == 9 && stock[index] == 1)
cout << "『\a勝利』\n";
else
cout << "『\a敗北』\n";

for (j = index; j < games - 1; j++)
stock[j] = stock[j + 1];
cout << '\n';
}
}
/code

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 限定じゃんけんで手を

#2

投稿記事 by みけCAT » 9年前

やるべきことは多そうな気がしますが、とりあえず
peyoung さんが書きました:sum + (hand == 1 || hand == 3 || hand == 9)
この意味不明な式を使うのはやめるべきでしょう。
この式は、handが有効な手(すなわち1か3か9)の場合はsum+1、そうでない場合はsumになります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 限定じゃんけんで手を

#3

投稿記事 by みけCAT » 9年前

peyoung さんが書きました:6回じゃんけんをしてグー・チョキ・パーがそれぞれ2回ずつしか出せない(限定じゃんけん)のプログラム
複雑なことを考えず普通に、ただし配列は使わずに実装してみました。
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 限定じゃんけんで手を

#4

投稿記事 by みけCAT » 9年前

ついでに、配列を使ってもう少しいい感じにしたコードも置いておきます。
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

peyoun9
記事: 7
登録日時: 9年前

Re: 限定じゃんけんで手を

#5

投稿記事 by peyoun9 » 9年前

みけCATさん
返信頂きありがとうございます。みけCATさんの描いて頂いたコードがしっかり動くことをこの目で確認いたしました。コメントもしっかり入っていて変数名も分かりやすく、今後コードを書く手本にしたいと思います。
まだプログラミング学習を始めたばかりで分からないことが沢山ありますが、勉強を続けていきたいと思います。
 描いて頂いたコードには私がまだ学習していない要素が幾つか含まれていますので、(bool型やint&もまだです。)完全には理解できないのですが、前述のとおりみけCATさんが分かりやすいコードを描いてくださるおかげで、助かっております。

 捕捉で説明を頂けるとより理解を深められるのですが、差し支えなければお願いをしてもよろしいでしょうか。
code
{
if (cin.eof()) {
cout << "入力の終端に達しました。\n";
return 1;
}
/code

これはどういった作用をしているのでしょうか。eofがend of fileというのは分かったのですが、どういった時、用いればよいのでしょうか。

 もうひとつ using namespace std; をわたしは(テキストに倣って)使っておりますが、みけCATさんはじめ、みなさん使わずにstd::で描かれる方をよく見るのですが、これは習慣や環境によるものでしょうか。それともusing namespace std;などを使うとエラーが起きたり好ましくないということなのでしょうか。

それとstring junk; ~というところ非常に勉強になりました。今まで数値以外を入力したときの処理がきちんとできていなかったので、スッキリしました。

akasann

Re: 限定じゃんけんで手を

#6

投稿記事 by akasann » 8年前

コード:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void output(int);
void memorry(int);
int sp_R=0,sp_S=0,sp_P=0;

int main(void){

	int janken[6]={0,1,2,0,1,2};
	int temp,i,sp;
    srand((unsigned)time(NULL));

	for(i=0;i<6;i++){
		sp=rand()%6;
		temp=janken[i]; janken[i]=janken[sp]; janken[sp]=temp;
	}

	int n;

	printf("限定じゃんけん\n");
	for(i=0;i<6;i++){

		printf("自分の手\n1.グー 2.チョキ 3.パー\n");
		scanf("%d",&n);

		printf("\n-----%d回目-----\n",i+1);
		printf("相手の手\n");
		output(janken[i]);
		printf("自分の手\n");
		output(n-1);

		if(janken[i]==n-1)
			printf("あいこです\n");
		else if(janken[i]==n||(janken[i]==0&&n==3))
			printf("あなたの勝ちです\n");
		else
			printf("あなたの負けです\n");
		memorry(n-1);
		putchar('\n');
	}
}

void output(int n){
	switch(n){
		case 0:printf("グー\n");break;
		case 1:printf("チョキ\n");break;
		case 2:printf("パー\n");break;
		}
}

void memorry(int m){

	switch(m){
	case 0: sp_R++; 
		if(sp_R==2)
			printf("\n※\nグーは最後です\n");
		break;
	case 1: sp_S++;
		if(sp_S==2)
			printf("\n※\nチョキは最後です\n");
		break;
	case 2: sp_P++;
		if(sp_P==2)
			printf("\n※\nパーは最後です\n");
		break;
	}
}

古い投稿を再び持ってきました。やや欠点が多いプログラムですが作ってみました。最初はスタックポイントを最近本で勉強したところなので、それを使うのかなぁとやってみましたが、できなかったのでこのような形で作ってみました。
余談ですがプログラムはちょいちょいと作っているのですが、どうやったら上手いプログラムをかけるのか?知った知識を生かせるのか?てきなことを考えてます。皆さんのプログラムを見ているとまだまだ勉強不足だなと思う点がいくつかあるのですが、よい勉強法などありましたら教えてください。今は本に書いてあるプログラムを描写したり、この掲示板などで見つけた題材をテーマとしてかいたりしています。

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#7

投稿記事 by あんどーなつ » 8年前

スタックポインタは、アセンブラでサブルーチンを組む時に、よく使います。
で、Cの関数は、アセンブラのサブルーチンの仕組みを使っています。

なんですけど、(CPUの)スタックポインタは高級言語(つまりアセンブラ以外)では隠していて、
スタックというデータ構造を意識せずにプログラミングできるようになっています。

アプリケーションにおいては、スタック(LIFO)よりもキュー(FIFO)のほうが使い勝手がいいです。
なので、私のほうでスタックの例を考えてみましたが、「ハノイの塔なにそれ」って感じで
思いつかなかったです。
オフトピック
スタックの変態的な使い方としては、ファイルやメモリの資源を管理するときに、

コード:

stack<int> s;
int h;
try {
  if ((h = createResource(...)) == NULL) throw ...;
  s.push(h);
  if ((h = createResource(...)) == NULL) throw ...;
  s.push(h);
  ...
} finally {
  while (!s.empty()) { destroyResource(s.top()); s.pop(); }
}
とかやるやり方です。マネしないでください。
あとリンクドリストも使い勝手がいいです。
.netでいうとList, STLでいうとvectorです。動的に入れ物のサイズを変えられるので、
どんどん使ったほうがいいです。

C++の難しいトピックを覚える前に、STLでいうコンテナ、.netでいうSystem.Collections.Genericは
使えるようになったほうがいいでしょう。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 限定じゃんけんで手を

#8

投稿記事 by みけCAT » 8年前

あんどーなつ さんが書きました:あとリンクドリストも使い勝手がいいです。
.netでいうとList, STLでいうとvectorです。動的に入れ物のサイズを変えられるので、
どんどん使ったほうがいいです。
リンクドリストは.netではLinkedList、C++のSTLではlistやslistです。
vectorなどの配列と比べ、リンクドリストでは添字を指定した「k番目」の要素へのアクセスはしにくいかわりに、途中の要素の挿入や削除が効率よくできるという利点があります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#9

投稿記事 by あんどーなつ » 8年前

みけCATさん

すみません、危うく嘘を教えるところでした。
ありがとうございます。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 限定じゃんけんで手を

#10

投稿記事 by usao » 8年前

オフトピック
>アプリケーションにおいては、スタック(LIFO)よりもキュー(FIFO)のほうが使い勝手がいいです。

これは一体どのような事を言っているのでしょうか?

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#11

投稿記事 by あんどーなつ » 8年前

私に限った話かもしれないのですが、.netでアプリケーションを製作するときに
スタックよりもキューを使うことが多いという意味です。

キューはワーカースレッドとのデータのやり取りで使用したりします。

単にnが大きい場合をあまり扱っていないので、Listで済んでしまってる
だけかもしれないです。

YuO
記事: 947
登録日時: 14年前
住所: 東京都世田谷区

Re: 限定じゃんけんで手を

#12

投稿記事 by YuO » 8年前

peyoun9 さんが書きました: もうひとつ using namespace std; をわたしは(テキストに倣って)使っておりますが、みけCATさんはじめ、みなさん使わずにstd::で描かれる方をよく見るのですが、これは習慣や環境によるものでしょうか。それともusing namespace std;などを使うとエラーが起きたり好ましくないということなのでしょうか。
基本的に名前空間を個々に修飾するか,usingするかは,
  • 他のソースにincludeされない予定のファイルや,関数などのスコープ内であれば,どちらで書いても良い
  • ヘッダファイルのように,他のソースにincludeされるファイルでは,名前空間を個々に修飾する
    →usingディレクティブやusing宣言の効果がヘッダファイルの外側にまで漏れ出します
  • 掲示板などでは,提示したコードの一部だけを使う場合などを考えると名前空間修飾する方がコピペしやすい
といったような判断基準でしょうか。
2番目について,

コード:

#ifndef TEST_H_
#define TEST_H_

#include <string>
using namespace std;

#endif
というようなヘッダファイルがあるときに,これをインクルートするとそれ以降でstd名前空間が大域名前空間にマッピングされます。
これをすると,せっかく名前空間があるのに名前が衝突する,という残念な結果が起きてしまうため,行わないことが推奨されます。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 限定じゃんけんで手を

#13

投稿記事 by usao » 8年前

オフトピック
>私に限った話かもしれないのですが、.netでアプリケーションを製作するときに
>スタックよりもキューを使うことが多いという意味です。

単に「個人的な利用頻度」ということですか.

(「FIFOの方は扱いが容易なんだけども,LIFOときたらもうね…」みたいな意味かと思った.)

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#14

投稿記事 by あんどーなつ » 8年前

peyoun9 さんが書きました:  捕捉で説明を頂けるとより理解を深められるのですが、差し支えなければお願いをしてもよろしいでしょうか。
code
{
if (cin.eof()) {
cout << "入力の終端に達しました。\n";
return 1;
}
/code

これはどういった作用をしているのでしょうか。eofがend of fileというのは分かったのですが、どういった時、用いればよいのでしょうか。

 もうひとつ using namespace std; をわたしは(テキストに倣って)使っておりますが、みけCATさんはじめ、みなさん使わずにstd::で描かれる方をよく見るのですが、これは習慣や環境によるものでしょうか。それともusing namespace std;などを使うとエラーが起きたり好ましくないということなのでしょうか。
”インタラクティブを前提とした”標準入力においてEOFかどうかを尋ねるのは、
実はあまり意味がないです。なぜなら標準入力はファイルではないからです。

以下のプログラムはEOF(CygwinではCtrl+D)を2回入力すると終了します。

コード:

#include <iostream>

using namespace std;

int main() {
    string line;
    do {
        getline(cin, line);
    } while (!cin.eof());
    cin.clear();
    do {
        getline(cin, line);
    } while (!cin.eof());
    cin.clear();

    return 0;
}
cin.eof()が常に意味がないわけでなく、UNIXコマンドなどスクリプトのように動くプログラムでは
ファイルを標準入力にリダイレクトさせたりして使うので、意味があります。


また、using namespace std;を使ってもよいかということについては、全然問題ありません。
問題になるのは、俺俺vectorや俺俺coutを作りたい人とか、
cout知らねって言ってint cout; とか書いちゃう人です。
C/C++の標準ライブラリのクラス名・関数名等は、C/C++のプログラムを書く時の前提ととらえていいかと
思いますので、問題ないです。

(やばい、YuOさんと違う意見だ)

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#15

投稿記事 by あんどーなつ » 8年前

usaoさん

勘違いさせてしまったとしたら、申し訳ないです。
単なる個人的な実感です。

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 限定じゃんけんで手を

#16

投稿記事 by あんどーなつ » 8年前

ジャンケンプログラムを書いてみました。グーチョキパーの残り回数をハッシュで表現していますが、
ソースコードを読んでいただければ、(ハッシュでなく)配列が一番最適であることがわかると思います。

コード:

#include <iostream>
#include <string>
#include <map>
#include <ctime>
#include <cstdlib>

using namespace std;

const int GU = 1;
const int CYOKI = 3;
const int PA = 9;

void init(map<int, int> &m) { m[GU] = m[CYOKI] = m[PA] = 2; }

int main() {
	cout << "こんにちは。じゃんけんしましょう。" << endl;
	cout << "ただし、3回以上おなじのを出したらだめですからねー" << endl;
	string line{""};
	do {
		cout << "何回やる~?(6回以内) : ";
		getline(cin, line);
	} while (line.size() != 1 || line[0] <= '0' || line[0] > '6');
	int cnt = line[0] - '0';

	map<int, int> player, enemy;
	init(player);
	init(enemy);
	srand((unsigned int)time(0));
	int player_win = 0, enemy_win = 0;
	for (int i = 0; i < cnt; ++i) {
		int player_cur, enemy_cur;
		do {
			cout << "手を選んでください(";
			if (player[GU] > 0) cout << "1...グー";
			if (player[GU] > 0 && player[CYOKI] > 0) cout << "/";
			if (player[CYOKI] > 0) cout << "3...チョキ";
			if ((player[GU] > 0 || player[CYOKI] > 0) && player[PA] > 0)
				cout << "/";
			if (player[PA] > 0) cout << "9...パー";
			cout << "): ";
			getline(cin, line);
			if (line.size() != 1) continue;
			player_cur = (int)(line[0] - '0');
			if (player.find(player_cur) == player.cend()) continue;
		} while (player[player_cur] == 0);
		player[player_cur]--;

		int enemy_i = rand() % (6-i);
		cout << "わたしは";
		if (enemy[GU] != 0 && (enemy_i -= enemy[GU]) < 0) {
			enemy_cur = GU;
			enemy[GU]--;
			cout << "グー";
		} else if (enemy[CYOKI] != 0 && (enemy_i -= enemy[CYOKI]) < 0) {
			enemy_cur = CYOKI;
			enemy[CYOKI]--;
			cout << "チョキ";
		} else {
			enemy_cur = PA;
			enemy[PA]--;
			cout << "パー";
		}
		cout << "をだしたよ。" ;
		if ( (player_cur == GU && enemy_cur == CYOKI)
			|| (player_cur == CYOKI && enemy_cur == PA)
			|| (player_cur == PA && enemy_cur == GU) ) {
			player_win++;
			cout << "あなたの勝ち!" << endl;
		} else if (player_cur != enemy_cur) {
			enemy_win++;
			cout << "わたしの勝ち!" << endl;
		} else {
			cout << "引き分けです。" << endl;
		}
	}
	cout << endl;
	if (player_win > enemy_win)
		cout << "おめでとうございます。あなたの勝利です" << endl;
	else if (player_win < enemy_win)
		cout << "残念でした。あなたの敗北です" << endl;
	else
		cout << "今回の勝負は引き分けとなりました" << endl;

	return 0;
}

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 限定じゃんけんで手を

#17

投稿記事 by usao » 8年前

>using namespace std

vector や list 等が一般単語すぎてわりと衝突するので,
std は using namespace しない派です.


特にvectorが「ベクトル(数学)」と被ってうざい.

コード:

//こんな感じになってたりすると,例え被ってなくても,なんというか見難いし…
vector< Vector > Vec;

//using namespaceしなければどうということもない
std::vector< Vector > Vec;

閉鎖

“C言語何でも質問掲示板” へ戻る