質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

2回答

2986閲覧

特定の配列のみシャッフルする方法を教えてほしいです。

n6n9Qsmt8gLjwKw

総合スコア29

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

1グッド

2クリップ

投稿2016/02/09 15:23

こんにちは
私は現在カードゲームを作成しています。
作成したいゲームの基本的な処理は作成できたのですが一か所どうすれば実現できるのかわからない処理があり困っています。
【ルール】
・場に出ているカードの絵柄か数字が手札のカードと同じだった場合、
一致したカードを捨てることができる。
・手札が0枚になったら勝ちとなる。
・手札枚数が51枚になり、カードを引こうとした場合プレイヤーの負けとする。
・山札が0枚になったときカードを引こうとした場合、最後に場に出したカードと手札以外のカードをシャッフルし再度山札を作る。

この最後のルールの処理を実現したいのですが特定の配列の値(手札と最後に場に出したカード値)を固定し、
その他の配列をシャッフルすれば可能なのではないかと思いました。
main関数のコメントアウトした箇所で再シャッフル用の関数(リバースデッキ関数)を呼び出してシャッフルしようとしたのですが、私の考え方が違うのかうまくいきませんでした。
どうすれば最後のルールの再シャッフル処理をできるのか知恵をお借りしたいです。

C

1#define _CRT_SECURE_NO_WARNINGS//scanf警告解除用 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6#define CARD_MAX (52)//カード枚数 7#define SAME_CARD (13)//同じ種類のカード枚数 8#define INIT_HAND (5)//初手 9#define TRUE (1)//正 10#define FALSE (0)//負 11#define YES (1)//イエス 12#define NO (2)//ノー 13#define SPADE (0)//スペード 14#define CLUB (1)//クラブ 15#define DIA (2)//ダイヤ 16#define HEART (3)//ハート 17 18/*構造体定義*/ 19typedef struct { 20 21 unsigned int hand_cnt;//手札カウンタ 22 unsigned char card_num[CARD_MAX];//カードの種類 23 24}PLAYER_DATA; 25 26/*変数宣言*/ 27unsigned char card_deck[CARD_MAX];//カードデッキ 28unsigned int rest_cnt;//デッキ残数カウンタ 29unsigned int turn_cnt;//ターンカウンタ 30 31char number[CARD_MAX];//カード値格納用 32 33/*関数プロトタイプ宣言*/ 34void init_card(void); 35void draw_card(PLAYER_DATA *myhand); 36void output_hand(PLAYER_DATA *myhand); 37void cemetery_card(PLAYER_DATA* field, PLAYER_DATA* myhand, int *input_data); 38void delete_hand(PLAYER_DATA *myhand, int *input_data); 39int search_card(PLAYER_DATA *field, PLAYER_DATA *myhandint, int *input_data); 40void rebirth_deck(PLAYER_DATA *field, PLAYER_DATA *myhand); 41/********************************/ 42/* メイン関数 */ 43/********************************/ 44int main(int argc, char*argv[]) 45{ 46 static PLAYER_DATA myhand;//プレイヤーの手札構造体 47 PLAYER_DATA field_card;//場のカード構造体 48 49 int draw_cnt;//ドローカウンタ 50 int input;//プレイヤー入力 51 int return_num;//戻り値 52 int yes_no;//Yes or No 53 54 init_card();//デッキ初期化 55 myhand.hand_cnt = 0;//手札枚数初期化 56 field_card.hand_cnt = 0;//場のカード枚数初期化 57 turn_cnt = 1;//ターンカウンタ初期化(開始時を1ターン目とする) 58 59 /*カードを5枚ドロー*/ 60 for (draw_cnt = 0; draw_cnt < INIT_HAND; draw_cnt++) { 61 draw_card(&myhand); 62 } 63 /*場に出すカードをドロー*/ 64 draw_card(&field_card); 65 66 /*画面にカードを出力*/ 67 printf("******************************************************\n"); 68 printf("現在のターン:%dターン目\n", turn_cnt); 69 printf("******************************************************\n"); 70 printf("場のカード:\n"); 71 output_hand(&field_card); 72 printf("\nあなたの手札:\n"); 73 output_hand(&myhand); 74 75 while (1) { 76 /*************/ 77 /* 1ターン目 */ 78 /*************/ 79 printf("\n山札(残数) %d 枚\n", CARD_MAX - rest_cnt); 80 printf("\n【ルール】\n"); 81 printf("場のカードと同じ数字か同じスートの\n"); 82 printf("カードを捨てることができます。\n"); 83 printf("\n"); 84 printf("カードを捨てますか?\n"); 85 printf("(1)Yes (2)No\n"); 86 scanf("%d", &yes_no); 87 printf("\n"); 88 if (yes_no == YES) {//捨てる場合捨てるカードを選択 89 printf("捨てるカード番号を入力してください:"); 90 scanf("%d", &input); 91 return_num = search_card(&field_card, &myhand, &input); 92 if (return_num == TRUE) {//フィールドのカード値または絵柄が一致した場合 93 cemetery_card(&field_card, &myhand, &input);//card_dataの配列は[0~4]出力のため関数内で-1する 94 delete_hand(&myhand, &input); 95 } 96 else if (return_num == FALSE) { 97 printf("\n選んだ番号のカードは捨てられません。\n"); 98 printf("デッキからカードを一枚引きます。\n"); 99 100 //if (rest_cnt == CARD_MAX) { 101 //rebirth_deck(&field_card, &myhand); 102 //} 103 104 draw_card(&myhand); 105 106 } 107 } 108 else if (yes_no == NO) {//捨てない場合デッキからカードを引く 109 draw_card(&myhand); 110 if (myhand.hand_cnt == CARD_MAX) {//手札に52枚目を加えようとしたらバースト 111 printf("\nカードがなくなりました。\n"); 112 printf("あなたの負けです。\n"); 113 printf("ゲームを終了します。\n"); 114 break; 115 } 116 } 117 turn_cnt++;//カードを捨てたら1ターン目終了 118 /****************/ 119 /*以下nターン目 */ 120 /****************/ 121 printf("******************************************************\n"); 122 printf("現在のターン:%dターン目\n", turn_cnt); 123 printf("******************************************************\n"); 124 printf("場のカード:\n"); 125 output_hand(&field_card); 126 printf("\nあなたの手札:\n"); 127 output_hand(&myhand); 128 if (myhand.hand_cnt == 0) {//手札が0になったら 129 printf("\nゲームクリアおめでとうございます。\n"); 130 printf("クリアするのに%dターンかかりました。\n", turn_cnt); 131 printf("このまま終了します。\n"); 132 break; 133 } 134 } 135 136 /*デバッグ用*/ 137#if 1 138 int debag; 139 scanf("%d", &debag); 140#endif 141 return 0; 142} 143/********************************/ 144/* デッキシャッフル(初期化)関数 */ 145/********************************/ 146void init_card(void) { 147 148 int rnd;//ランダム値格納用変数 149 int tmp; 150 int arr_cnt;//配列カウンタ 151 srand((unsigned)time(NULL)); 152 153 for (arr_cnt = 0; arr_cnt < CARD_MAX; arr_cnt++) {//カード値配列初期化 154 number[arr_cnt] = arr_cnt; 155 156 } 157 158 for (rest_cnt = 0; rest_cnt< CARD_MAX; rest_cnt++) {//デッキ初期化 159 160 card_deck[rest_cnt] = rest_cnt; 161 162 } 163 164 rest_cnt = 0;//初期残数0 165 166 for (rest_cnt = 0; rest_cnt < CARD_MAX; rest_cnt++) {//デッキシャッフル 167 168 rnd = rand() % (CARD_MAX); 169 tmp = card_deck[rest_cnt]; 170 card_deck[rest_cnt] = card_deck[rnd]; 171 card_deck[rnd] = tmp; 172 173 } 174 175 rest_cnt = 0; 176} 177 178/********************************/ 179/* ドローカード関数 */ 180/********************************/ 181void draw_card(PLAYER_DATA *myhand) { 182 183 myhand->card_num[myhand->hand_cnt] = card_deck[rest_cnt]; 184 185 myhand->hand_cnt++;//手札に一枚加える 186 rest_cnt++;//デッキから1枚カードを抜く 187 188} 189 190/********************************/ 191/* 手札表示関数 */ 192/********************************/ 193void output_hand(PLAYER_DATA *myhand) 194{ 195 unsigned int myhnd_cnt; 196 int select; 197 int card; 198 int suit; 199 select = 0; 200 201 //ドローしたカード枚数分画面に表示 202 for (myhnd_cnt = 0; myhnd_cnt < myhand->hand_cnt; myhnd_cnt++) { 203 select++; 204 card = (int)(number[myhand->card_num[myhnd_cnt]]) % SAME_CARD + 1; 205 suit = (int)(number[myhand->card_num[myhnd_cnt]])/ SAME_CARD; 206 207 printf("(%d)%d", select, card); 208 switch (suit) { 209 210 case SPADE: 211 printf("S"); 212 break; 213 214 case CLUB: 215 printf("C"); 216 break; 217 218 case DIA: 219 printf("D"); 220 break; 221 222 case HEART: 223 printf("H"); 224 break; 225 226 } 227 228 } 229 230 printf("\n"); 231} 232 233/********************************/ 234/* セメタリーカード関数 */ 235/********************************/ 236void cemetery_card(PLAYER_DATA *field, PLAYER_DATA *myhand, int *input_data) 237{ 238 //セメタリーのカードを選択された手札で更新 239 field->card_num[field->hand_cnt] = myhand->card_num[*input_data - 1]; 240 field->hand_cnt++;//フィールドのカード列をずらす 241 242} 243 244/********************************/ 245/* 手札削除関数 */ 246/********************************/ 247void delete_hand(PLAYER_DATA *myhand, int *input_data) 248{ 249 unsigned int input_cnt; 250 251 //入力された選択肢のカードを起点に手札を一枚ずつずらす 252 for (input_cnt = *input_data - 1; input_cnt < myhand->hand_cnt - 1; input_cnt++) { 253 myhand->card_num[input_cnt] = myhand->card_num[input_cnt + 1]; 254 } 255 myhand->hand_cnt--;//手札を一枚減らす 256} 257 258/********************************/ 259/* 手札検索関数 */ 260/********************************/ 261int search_card(PLAYER_DATA *field, PLAYER_DATA *myhand,int *input_data) 262{ 263 unsigned int search_cnt; 264 int answer; 265 int hand_card; 266 int hand_suit; 267 int field_card; 268 int field_suit; 269 hand_card = (int)(number[myhand->card_num[*input_data - 1]]) % SAME_CARD + 1; 270 hand_suit = (int)(number[myhand->card_num[*input_data - 1]]) / SAME_CARD; 271 field_card = (int)(number[field->card_num[field->hand_cnt-1]]) % SAME_CARD + 1; 272 field_suit = (int)(number[field->card_num[field->hand_cnt-1]]) / SAME_CARD; 273 274 answer = FALSE; 275 if((hand_card == field_card) 276 ||(hand_suit == field_suit)) 277 { 278 answer = TRUE; 279 } 280 281 return answer; 282} 283 284#if 0 285/********************************/ 286/* リバースデッキ関数 */ 287/********************************/ 288void rebirth_deck(PLAYER_DATA *field, PLAYER_DATA *myhand) 289{ 290 int rnd;//ランダム値格納用変数 291 int tmp; 292 int arr_cnt;//配列カウンタ 293 srand((unsigned)time(NULL)); 294 295 for (arr_cnt=0; arr_cnt < CARD_MAX; arr_cnt--) {//デッキシャッフル 296 if ((myhand->card_num[myhand->hand_cnt] == card_deck[rest_cnt]) || 297 (field->card_num[myhand->hand_cnt] == card_deck[rest_cnt])) { 298 continue; 299 } 300 rnd = rand() % (CARD_MAX); 301 tmp = card_deck[arr_cnt]; 302 card_deck[arr_cnt] = card_deck[rnd]; 303 card_deck[rnd] = tmp; 304 305 } 306 rest_cnt = myhand->hand_cnt+1; 307} 308#endif
DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

こんにちは。

最後に場に出したカードと手札以外のカードをシャッフルし再度山札を作る。

これって、場に出ているカードの内、最後以外を山札にしてシャッフルすれば良いと思います。
つまり、field_cardの最後の1枚手前までをcard_deckへ移動し、card_deckをシャッフルするわけです。

ところで、ざっと見た感じですが、delete_hand()本当に捨てちゃってますね。field_cardへ移動する必要はないですか? また、"(2)No"を選択した時に山札が無くなっていた時の処理も漏れてないですか?

投稿2016/02/10 00:53

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

n6n9Qsmt8gLjwKw

2016/02/11 18:18

コメントが遅くなってしまい申し訳ありません。 field_cardの一枚手前ということは下記コードのような関数で呼び出せばよいのでしょうか?手札の数値と重複するので私のコードにどこか不備があるかもしれませんが・・・ また、No選択時の再シャッフル呼び出し処理がないのは私のミスでした。 if (rest_cnt == CARD_MAX) { rebirth_deck(&field_card, &myhand); } | 中略 | void rebirth_deck(PLAYER_DATA *field, PLAYER_DATA *myhand) { int rnd;//ランダム値格納用変数 int tmp; int arr_cnt;//配列カウンタ srand((unsigned)time(NULL)); for (arr_cnt=0; arr_cnt < field->hand_cnt - 1; arr_cnt++) {//デッキシャッフル rnd = rand() % (field->hand_cnt - 1); tmp = card_deck[arr_cnt]; card_deck[arr_cnt] = card_deck[rnd]; card_deck[rnd] = tmp; } rest_cnt = (field->hand_cnt+myhand->hand_cnt)-1; }
Chironian

2016/02/12 00:30

考え方は間違っていないのですが、大きなポイントが1点漏れているようです。 単なるミスと思っていたのですが、どうもそうではないようなので解説します。 card_deck配列には手札として配ったカードのデータの残滓が残ってはいますが、手札から捨てられたものとその残滓は当然ですが一致しません。card_deckの残滓の一部は手札として残ったままですので。 従って、rebirth_deckでcard_deckの残滓をシャッフルしても、それは手札として残っているものと重複しますし、また、重複した枚数だけ本来あるべきカードを含んでいません。 リアルと同じようにカードをハンドリングしましょう。 つまり、手札からカードを捨てたときはそれをfield_cardへ入れておき、rebirth_deck()の頭でその最後の一枚は残したままcard_deckへ移動後、card_deckをシャッフルしましょう。
n6n9Qsmt8gLjwKw

2016/02/12 05:39

Chironianさん、回答ありがとうございます。 回答を踏まえて手札削除関数を下記のように変えたとき myhand->card_num[input_cnt] = myhand->card_num[input_cnt + 1];を消さないと重複してしまいますが、消してしまうと手札を表示したとき手札がずれなくなってしまいます。こういった場合どうしたらよいのでしょうか。(field_cardへの格納を追加しただけなのですが処理自体セメタリー関数とかぶっているかもしれません)何度も回答いただき大変申し訳ないのですが非常に困っております。再シャッフル関数ができればゲームはほぼ完成するのですが再シャッフル処理の考えに手間取っています。 void delete_hand(PLAYER_DATA *myhand, PLAYER_DATA *field_card ,int *input_data) { unsigned int input_cnt; //入力された選択肢のカードを起点に手札を一枚ずつずらす for (input_cnt = *input_data - 1; input_cnt < myhand->hand_cnt - 1; input_cnt++) { myhand->card_num[input_cnt] = myhand->card_num[input_cnt + 1]; field_card->card_num[field_card->hand_cnt] = myhand->card_num[*input_data - 1]; } myhand->hand_cnt--;//手札を一枚減らす }
guest

0

手直しより1から書くほうが楽なので書いてみました。

C

1struct CARD_ORDER{ 2 int card; 3 int order; 4}; 5 6int comp( const void* a, const void* b ) 7{ 8 struct CARD_ORDER* p = (struct CARD_ORDER*)a; 9 struct CARD_ORDER* q = (struct CARD_ORDER*)b; 10 return p->order - q->order; 11} 12 13void reshuffle( int last, const PLAYER_DATA* hand ) 14{ 15 struct CARD_ORDER list[CARD_MAX]; 16 size_t size = 0; 17 int i, j, k; 18 19 srand( (unsigned)time(NULL) ); 20 21 for( i = 0; i < CARD_MAX; i++ ){ 22 if( i == last ) continue; 23 for( j = 0; j < hand->hand_cnt; j++ ){ 24 if( i == hand->card_num[j] ) break; 25 } 26 if( j < hand->hand_cnt ) continue; 27 28 list[size].card = card; 29 list[size].order = rand(); 30 size++; 31 } 32 qsort( list, size, comp ); 33 34 for( k = 0; k < size; k++ ){ 35 card_deck[k] = list[k].card; 36 } 37} 38```内容は一目瞭然だと思うので省略します。必要なら補足します。

投稿2016/02/09 16:27

編集2016/02/09 16:32
majiponi

総合スコア1720

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問