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

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

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

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

Q&A

解決済

3回答

3615閲覧

ビット演算を使った配列の初期化

n6n9Qsmt8gLjwKw

総合スコア29

C

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

1グッド

2クリップ

投稿2016/02/07 09:10

こんにちは
現在52枚のカードを使ったゲームを作成しています。
Sはスペード、Hはハート、Dはダイヤ、Cはクラブというようにアルファベット一文字を絵柄に見立てています。
下記のコードはまだ作成途中のコードなのですがカードの数値の初期化処理が冗長になってしまいROMとRAM上に大きなサイズを取ってしまいます。
*number[]配列宣言時に初期化を行ってしまっていることが原因なのですが、ビット演算を使用して初期化できればデータサイズは小さくできると思ったのですが
うまく格納する方法が浮かびません。

例えばinit_card関数内で*numberを初期化するとしてどのようにビット演算を使ってカードの数字を格納したらよいか思い浮かびません。

カードをどのように考えればビット演算を使用してうまく初期化できるかご教授願えないでしょうか。できればビット演算を使って下記コードと同じ処理が問題なくできるようにしたいです。
よろしくお願いいたします。

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_SUIT (13)//同じ柄のカード枚数 8#define INIT_HAND (5)//初手 9 10/*構造体定義*/ 11typedef struct { 12 13 unsigned int hand_cnt;//手札カウンタ 14 unsigned char card_num[CARD_MAX];//カードの種類 15 16}PLAYER_DATA; 17 18/*変数宣言*/ 19unsigned char card_deck[CARD_MAX];//カードデッキ 20unsigned int rest_cnt;//デッキ残数カウンタ 21unsigned int turn_cnt;//ターンカウンタ 22 23char *number[] = { "1S", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "10S", "11S", "12S", "13S",//スペード 24 "1C", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "10C", "11C", "12C", "13C",//クラブ 25 "1D", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "10D", "11D", "12D", "13D",//ダイヤ 26 "1H", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "10H", "11H", "12H", "13H", };//ハート 27 28 29/*関数プロトタイプ宣言*/ 30void init_card(void); 31void draw_card(PLAYER_DATA *myhand); 32void output_hand(PLAYER_DATA *myhand); 33void cemetery_card(PLAYER_DATA* field, PLAYER_DATA* myhand, int *input_data); 34void delete_hand(PLAYER_DATA *myhand, int *input_data); 35 36/********************************/ 37/* メイン関数 */ 38/********************************/ 39int main(int argc, char*argv[]) 40{ 41 PLAYER_DATA myhand;//プレイヤーの手札構造体 42 PLAYER_DATA field_card;//場のカード構造体 43 44 int draw_cnt;//ドローカウンタ 45 int input;//プレイヤー入力 46 47 init_card();//デッキ初期化 48 myhand.hand_cnt = 0;//手札枚数初期化 49 field_card.hand_cnt = 0;//場のカード枚数初期化 50 turn_cnt = 1;//ターンカウンタ初期化(開始時を1ターン目とする) 51 52 /*カードを5枚ドロー*/ 53 for (draw_cnt = 0; draw_cnt < INIT_HAND; draw_cnt++) { 54 draw_card(&myhand); 55 } 56 /*場に出すカードをドロー*/ 57 draw_card(&field_card); 58 59 /*画面にカードを出力*/ 60 printf("******************************************************\n"); 61 printf("現在のターン:%dターン目\n", turn_cnt); 62 printf("******************************************************\n"); 63 printf("場のカード:\n"); 64 output_hand(&field_card); 65 printf("\nあなたの手札:\n"); 66 output_hand(&myhand); 67 68 while (1) { 69 /*************/ 70 /* 1ターン目 */ 71 /*************/ 72 printf("\n山札(残数) %d 枚\n", CARD_MAX - rest_cnt); 73 printf("\n【ルール】\n"); 74 printf("場のカードと同じ数字か同じスートの\n"); 75 printf("カードを捨てることができます。\n"); 76 printf("\n"); 77 printf("捨てるカード番号を入力してください:"); 78 scanf("%d", &input); 79 cemetery_card(&field_card, &myhand,&input);//card_dataの配列は[0~4]出力のため関数内で-1する 80 delete_hand(&myhand, &input); 81 turn_cnt++;//カードを捨てたら1ターン目終了 82 /****************/ 83 /*以下nターン目 */ 84 /****************/ 85 printf("******************************************************\n"); 86 printf("現在のターン:%dターン目\n", turn_cnt); 87 printf("******************************************************\n"); 88 printf("場のカード:\n"); 89 output_hand(&field_card); 90 printf("\nあなたの手札:\n"); 91 output_hand(&myhand); 92 printf("\n山札(残数) %d 枚\n", CARD_MAX - rest_cnt); 93 break; 94 } 95 96 printf("\nゲームクリアおめでとうございます。\n"); 97 printf("クリアするのに%dターンかかりました。\n", turn_cnt); 98 printf("このまま終了します。\n"); 99 100 /*デバッグ用*/ 101#if 1 102 int debag; 103 scanf("%d", &debag); 104#endif 105 return 0; 106} 107/********************************/ 108/* デッキシャッフル(初期化)関数 */ 109/********************************/ 110void init_card(void) { 111 112 int rnd;//ランダム値格納用変数 113 int tmp; 114 srand((unsigned)time(NULL)); 115 116 for (rest_cnt = 0; rest_cnt< CARD_MAX; rest_cnt++) {//デッキ初期化 117 118 card_deck[rest_cnt] = rest_cnt; 119 120 } 121 122 rest_cnt = 0;//初期残数0 123 124 for (rest_cnt = 0; rest_cnt < CARD_MAX; rest_cnt++) {//デッキシャッフル 125 126 rnd = rand() % (CARD_MAX); 127 tmp = card_deck[rest_cnt]; 128 card_deck[rest_cnt] = card_deck[rnd]; 129 card_deck[rnd] = tmp; 130 131 } 132 133 rest_cnt = 0; 134} 135 136/********************************/ 137/* ドローカード関数 */ 138/********************************/ 139void draw_card(PLAYER_DATA *myhand) { 140 141 myhand->card_num[myhand->hand_cnt] = card_deck[rest_cnt]; 142 143 myhand->hand_cnt++;//手札に一枚加える 144 rest_cnt++;//デッキから1枚カードを抜く 145 146} 147 148/********************************/ 149/* 手札表示関数 */ 150/********************************/ 151void output_hand(PLAYER_DATA *myhand) 152{ 153 unsigned int myhnd_cnt; 154 int select; 155 select = 0; 156 157 //ドローしたカード枚数分画面に表示 158 for (myhnd_cnt = 0; myhnd_cnt < myhand->hand_cnt; myhnd_cnt++) { 159 select++; 160 printf("(%d)%s", select, number[myhand->card_num[myhnd_cnt]]); 161 162 } 163 164 printf("\n"); 165} 166 167/********************************/ 168/* セメタリーカード関数 */ 169/********************************/ 170void cemetery_card(PLAYER_DATA *field, PLAYER_DATA *myhand,int *input_data) 171{ 172 //セメタリーのカードを選択された手札で更新 173 field->card_num[field->hand_cnt] = myhand->card_num[*input_data -1]; 174 field->hand_cnt++;//フィールドのカード列をずらす 175 176} 177 178/********************************/ 179/* 手札削除関数 */ 180/********************************/ 181void delete_hand(PLAYER_DATA *myhand, int *input_data) 182{ 183 unsigned int input_cnt; 184 185 //入力された選択肢のカードを起点に手札を一枚ずつずらす 186 for (input_cnt = *input_data-1; input_cnt < myhand->hand_cnt - 1; input_cnt++) { 187 myhand->card_num[input_cnt] = myhand->card_num[input_cnt + 1]; 188 } 189 myhand->hand_cnt--;//手札を一枚減らす 190}
AZDDI👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

ビット演算を使って

初期化に使うというのがよくわからないので外しているかもしれませんが、こういった感じのことでしょうか?
復号に余計な処理が入りますが、ポインタが無くなってカード情報一枚が2~3バイトから1バイトになるので(コンパイラのchar型配列初期化の扱いにもよりますが)トータルでは消費メモリは減らせていると思うのですが・・・

char number[] = {'\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0A','\x0B','\x0C','\x0D','\x0E', //スペード '\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1A','\x1B','\x1C','\x1D','\x1E', //クラブ '\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2A','\x2B','\x2C','\x2D','\x2E', //ダイヤ '\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3A','\x3B','\x3C','\x3D','\x3E', } ;//ハート char suit[] = { 'S', 'C', 'D', 'H' }; #define get_suit_char(n) suit[n>>4] #define get_number(n) (n&0x0F) /* 中略 */ printf("(%d)%c%d", select, get_suit_char(number[myhand->card_num[myhnd_cnt]]),get_number(number[myhand->card_num[myhnd_cnt]]) );

投稿2016/02/07 10:36

kozuchi

総合スコア1193

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

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

n6n9Qsmt8gLjwKw

2016/02/07 11:10

kozuchiさん、回答ありがとうございます。 申し訳ありません。私の質問の書き方がわかりにくかったです。 numberのメモリを節約しようと思っていましたが、kozuchiさんの認識で合っています。 数値と絵柄を分ければビット演算で出力させることが可能だということがわかりました。 Chirionianさんの回答と組み合わせてコーディングを進めていこうと思います。 本当にありがとうございました。
guest

0

number は、 0..51 の数字を "1S", ... "13H" のカード名に変換する為につかっていますね。
変換表をカードの数だけ用意する必要はありません。
つぎのようにして、数字 <--> カード名の変換ができます。
ただし以下では、変換がしやすいように カード名は CA, C2, ... CJ, CQ, CK のように
スーツ + 数字 の順とし、さらに数字は A, 2, 3, ,, J, Q, K としています。

c

1#include <stdio.h> 2#include <stdlib.h> 3 4#define MAX_CARD_NUM (13) 5#define MAX_SUIT (4) 6#define MAX_CARD_COUNT (MAX_CARD_NUM * MAX_SUIT) 7 8char * SUITS[] = {"C", "D", "H", "S"}; 9char * NUMS[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", 10 "10", "J", "Q", "K"}; 11 12void int2card(unsigned char c, char * str) { 13 *str ='\0'; 14 int num = c & 0xFF; 15 int s = num / MAX_CARD_NUM; 16 int n = num % MAX_CARD_NUM; 17 sprintf(str, "%s%s", SUITS[s], NUMS[n]); 18} 19 20unsigned int card2int(char* card) { 21 int s = 0; 22 int n = 0; 23 24 char s0 = card[0]; 25 for (int i = 0; i < MAX_SUIT; i++) { 26 if (card[0] == SUITS[i][0]) { 27 s = i; 28 break; 29 } 30 } 31 for (int i = 0; i < MAX_CARD_NUM; i++) { 32 if (card[1] == NUMS[i][0]) { 33 n = i; 34 break; 35 } 36 } 37 return (unsigned int)(s * MAX_CARD_NUM + n); 38} 39 40int main(int argc, char*argv[]) { 41 char str[4]; 42 for (unsigned char c = 0; c < MAX_CARD_COUNT; c++) { 43 int2card(c, str); 44 printf("%2d: %s ", c, str); 45 if ((c + 1) % MAX_CARD_NUM == 0) { 46 printf("\n"); 47 } 48 } 49 printf("\n"); 50 for (unsigned char c = 0; c < MAX_CARD_COUNT; c++) { 51 int2card(c, str); 52 unsigned char num = (unsigned char)card2int(str); 53 printf("%2s: %2d ", str, (int)num); 54 if ((c + 1) % MAX_CARD_NUM == 0) { 55 printf("\n"); 56 } 57 } 58}

実行例

0: CA 1: C2 2: C3 3: C4 4: C5 5: C6 6: C7 7: C8 8: C9 9: C10 10: CJ 11: CQ 12: CK 13: DA 14: D2 15: D3 16: D4 17: D5 18: D6 19: D7 20: D8 21: D9 22: D10 23: DJ 24: DQ 25: DK 26: HA 27: H2 28: H3 29: H4 30: H5 31: H6 32: H7 33: H8 34: H9 35: H10 36: HJ 37: HQ 38: HK 39: SA 40: S2 41: S3 42: S4 43: S5 44: S6 45: S7 46: S8 47: S9 48: S10 49: SJ 50: SQ 51: SK CA: 0 C2: 1 C3: 2 C4: 3 C5: 4 C6: 5 C7: 6 C8: 7 C9: 8 C10: 9 CJ: 10 CQ: 11 CK: 12 DA: 13 D2: 14 D3: 15 D4: 16 D5: 17 D6: 18 D7: 19 D8: 20 D9: 21 D10: 22 DJ: 23 DQ: 24 DK: 25 HA: 26 H2: 27 H3: 28 H4: 29 H5: 30 H6: 31 H7: 32 H8: 33 H9: 34 H10: 35 HJ: 36 HQ: 37 HK: 38 SA: 39 S2: 40 S3: 41 S4: 42 S5: 43 S6: 44 S7: 45 S8: 46 S9: 47 S10: 48 SJ: 49 SQ: 50 SK: 51

投稿2016/02/07 14:59

katoy

総合スコア22324

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

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

0

こんにちは。

number[]を使っているのは、printf("(%d)%s", select, number[myhand->card_num[myhnd_cnt]]);だけですね? この表示のためのnumber[]の初期化がちょっと勿体無く感じていると言うことですね。

現在の姿も「有り」とは思いますが、少しでもメモリを節約するため、numberの添字から上記printf()の文字を計算したいと理解しました。

下記イメージでどうでしょうか?

C

1size_t index=myhand->card_num[myhnd_cnt]; 2static char const card_type[]={'S', 'C', 'D', 'H'}; 3printf("(%d)%d%c", select, index/13+1, card_type[index/13];

とと、すいません。Cでsize_tが使えましたっけ? だめならunsignedにして下さい。
コンパイル確認してないのでエラーがでたら適宜修正下さい。

投稿2016/02/07 09:51

Chironian

総合スコア23272

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

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

cateye

2016/02/07 10:32

>Cでsize_tが使えましたっけ? OKですよd^^
n6n9Qsmt8gLjwKw

2016/02/07 11:00

Chironianさん、いつも回答ありがとうございます。 申し訳ありません。私の質問の書き方がわかりにくかったかもしれませんね。 ご回答通り一か所しか使っていないnumberのメモリを節約しようと思っていました。 ベストアンサーをどちらの方につけるか迷ったのですが、ビット演算を使用したkozuchiさんにつけさせていただきました。 カードを考えるとき数字と絵柄を分けて、枚数52枚のため13で割り、数値は割った余りを出力すればうまく処理できました。私のカードの考え方だとまずこういった発想は出てきませんでした。トータルで考えた場合、お二人の回答のどちらのほうがメモリ節約にいなるかまだ調べていないのですが、より消費量の少ないほうを使って完成させていこうと思います。 本当ににありがとうございました。
Chironian

2016/02/07 12:19

cateyeさん、フォローありがとうございます。 n6n9Qsmt8gLjwKwさん、頑張ってください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問