下記のようなプログラムで4人のコンピュータにババヌキをさせるプログラムを組んでる最中なんです。
decklistまでは表示できてもshufflelistではコンパイルは通っても実行するとデバックが起きてしまって困っています。while文をなくすとちゃんと返り値を持って表示はできるんですがwhile分で繰り返したとたんデバックが起きるんですがその理由がわかりません。どうして無理でしょうか?ご教授願います。
decklistは整列されリストに保存した山札の関数。
shufflelistはランダムで保存していく関数。
personの関数はdecklistのほうで試したところコンパイルは通るんですがこちらもデバックが起きてしまいます。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define N 53
//デッキのカードの構造体
struct card{
int t;
struct card *next;
}*deck;
//カードを配った後のそれぞれのプレイヤーの情報(仮
struct player{
int card;
struct player *nextcard;
struct player *nextturn;
};
//関数定義
struct card *talloc(void);
struct player *lalloc(void);
struct card *decklist(void);
struct card *shufflelist(void);
struct player *person(struct player *P);
void displist(void);
int main(void){
struct player *A,*B,*C,*D;
struct player *p;
p=person(A);
while(p!=NULL){
printf("%d ",p->card);
p=p->nextcard;
}
return 0;
}
//card構造体のセルの確保
struct card *talloc(void){
return (struct card *)malloc(sizeof(struct card));
}
//player構造体のセルの確保
struct player *lalloc(void){
return (struct player *)malloc(sizeof(struct player));
}
//カードを切る前の整列された山札の関数
struct card *decklist(void){
int i,add=1,count=0;
struct card *p;
deck=NULL;
for(i=0;i<53;i++){
if(count==4){
add+=1;
count=0;
}
p=talloc();
p->t=add;
p->next=p;
count++;
p->next=deck;
deck=p;
}
return deck;
}
//山札をシャッフルした後の山札の関数
struct card *shufflelist(void){
int i,a,count=0,r;
struct card *d,*p,*q,*shuffle;
p=decklist();
shuffle=NULL;
while(p!=NULL){
srand(time(NULL));
r=(int)rand()%(N-count);
for(i=0;i<r;i++){
p=p->next;
}
q=talloc();
q->t=p->t;
d=p->next;
p->next=d->next;
q->next=shuffle;
shuffle=q;
count++;
p->next=p;
}
return shuffle;
}
//プレイヤーへカードを配るための関数
struct player *person(struct player *PERSON){
struct player *a;
struct card *library;
int i;
PERSON=NULL;
library=decklist();
a=lalloc();
while(a!=NULL){
a->card=library->t;
for(i=0;i<4;i++){
library=library->next;
}
a->nextcard=a;
a=PERSON;
}
a->nextcard=NULL;
while(a!=NULL){
printf("%d ",a->card);
}
return PERSON;
}
No.6ベストアンサー
- 回答日時:
とりあえず、カードを抜く処理について、抜いたカードの前後に対する
処理の一例として理解いただければ、いいかと思います。
が、実はシャッフルであれば、もっとお手軽にできます。
そう、カードの交換です。
------------------------------------------------------------
//山札をシャッフルした後の山札の関数
struct card *shufflelist3(void){
int i,j,cnt,r1,r2,t;
struct card *p,*p1,*p2;
srand(time(NULL));
cnt=rand()%53+30;
p=decklist();
for(j=0;j<cnt;j++){
p1=p2=p;
r1=(int)rand()%53;
r2=(int)rand()%53;
if(r1==r2) continue;
for(i=0;i<r1;i++) p1=p1->next;
for(i=0;i<r2;i++) p2=p2->next;
// r1番目とr2番目の入れ替え
t=p1->t;
p1->t=p2->t;
p2->t=t;
}
return p;
}
------------------------------------------------------------
で、気付いたかもしれませんが、ババ抜きにおいて山札は枚数が決まっていて、
カードが配布されたら0になるべきものであるので、配列でOKだったりします。
ポインタ構造は不定枚数の時に効果を発揮しますが、なにぶん、10枚目!といった際に
10枚目を探すことになりますので、例えば p[9]と一発で指定できる配列には
かないません。
私はプログラムは一回作ったら必ず見直すようにしています。
配列版にすると全体的な影響が大きすぎるので、プログラムは提示しませんが
上記よりものすごくシンプルになったりします。
#時間がない、といっている中、同じところばかり見ていてすみません。
#ただ、バグが発生しやすいCはプログラムの見直し、及び、確実な仕様の理解は必須です。
> 自分の頭がコンピュータ側としての理解の仕方に合わせれてないことを痛感しました。
> もうちょっと練り考えて見たいと思います。
そんなにコンピュータ側にあわせる必要もないです。
前に提示した私のサンプルはポインタのリスト構造が後ろに伸びていく、ただ
それをイメージしただけで、コンピュータ側にあわせたわけではないです。
カードを抜く処理が言葉だけでは伝わらなかったので、サンプルを作ろうと思ったのですが
ちょっと思うようにいかず、若干ですが、リスト構造作成の方に手を入れさせて頂きました。
私としてはカードを抜く処理が伝われば、それでいいです。
で、他処理ですが、
・プレーヤーにカードを配る処理
→A,B,C,Dはグローバル変数にしてしまってもいいのではないでしょうか?
その上で、これからもらうカードとしてa,b,c,d、及び、その手前をaa,bb,cc,ddとして
山札から1枚引く関数を実行、カードが無くなったらループから脱出、って感じに
すればいいでしょう。
山札から1枚引く関数は前に提示した関数を参考にすればいいかと思います。
・ゲーム実行
4人の内3人はコンピュータでしょうか?
ババ抜きは本来は相手の表情等を伺っての戦略が必要ですが、流石にそういった処理は
難しいので、何も戦略がないとすれば、単に乱数を使用しカードを抜いていく、といった
処理をすればいいだけとなります。
これも山札から1枚抜く処理が主立ったものになるかと思います。
言葉で書くより面倒な処理は多いかとは思いますが、慣れればすぐにできるようになります。
がんばってみてください。
助言ありがとうございます。
確かに枚数が決まってる処理には不定なリストよりも配列のほうが直接ほしい値に接続できるのが利点でここでは走するべきでした。自分がリストを使おうと思ったのは最初の整列された山札からシャッフルの山札を作るときに配列だとランダムで配列を指定してそこのあたいをマタ別の配列に入れるときランダムで指定する配列の値がかぶってしまうのがきになりリストで実現するほうがイイと思ったのでリストにしたのがおっしゃるとおりむしろ難易度を上げてる気がしてなりませんね。。。。
関数での使い方が凡庸性が高すぎて逆に混乱してしまうことが多々あり山札で一枚引かせる関数などその動作でどこまで細かく分けるのかまだよくわからないことが多いので確認していきたいと思います。
ゲーム実行ではこのばばぬきプログラムはパソコンにババヌキの処理をやらせたいので作ろうとしました。設定はよくありがちな4人くらいが一般的かと思いそうしました。
戦略というよりは個人的につけてみたい機能として「ジョーカーがあるとある動作をする」っという人間らしい考え方を組み込みたいと思ってますが時間がないのでそこまで手が出せるかわからないのが現状です。なので今は短的にプログラムの完成を目指して生きたいと思ってます。
リストでの実現はこれで勉強になるのですが、ただ起源もあり、配列の場合のプログラムが探したところあったのでとりあえずこちらを優先的にプログラムを完成させ、リストでのプログラムも後々完成させたいと思っています。そちらの法に力を入れたいのでこの質問版はとりあえず〆させていただきます。
勝手ながらプログラムそのものを変えてしまって7o8さんにはご迷惑かけました。すみません。
ただ今後に生かせるようにがんばっていきます!!ありがとうございました。お世話になりました。
No.5
- 回答日時:
まずはわかりやすいように作るべき何ですが、ここがプログラムの
難しいところであって、各個人に随分と考え方が異なってきてしまいます。
で、申し訳ないんですが、現在のカードのリスト構造における作成方法が
私とあっていないので、どうあるべきか?というのが少々難しく感じてしまっています。
私のイメージするカードのリスト構造は上から順々に生成していく、というもので、
頭の中の整理がつけばどうでもいいことなんですが、イマイチ後ろのカードから
作成していく感覚になじめていないのです。
とりあえず、私の方で作成した、簡単に動作確認をした decklist2及び
shufflelist2を以下に記します。
ご参考になれば幸いです。
#変数の命名関連は参考にしないでください。(^_^;;;;;
#decklistは正常動作しているのを確認しましたが、こういう作成例があるということで。
struct card *decklist2(void){
int i,add=1,count=0;
struct card *p,*pp,*deck;
pp=p=talloc();
for(i=0;i<53;i++){
if(count==4){
add+=1;
count=0;
}
deck=p;
p->t=add;
p->next=talloc();
p=p->next;
count++;
}
free(deck->next);
deck->next=NULL;
return pp;
}
//山札をシャッフルした後の山札の関数
struct card *shufflelist2(void){
int i,j,count=0,r;
struct card *pd,*pp,*p,*q,*qd,*shuffle;
srand(time(NULL));
pp=p=decklist();
qd=q=shuffle=talloc();
for(j=0;j<53;j++){
r=(int)rand()%(53-count);
for(i=0;i<r;i++) {
pd=p;
p=p->next;
}
//シャッフル後のカードに対する処理
q->t=p->t;
qd=q;
q->next=talloc();
q=q->next;
// pのリストからカードを抜く
if(r==0)pp=p->next;//先頭のカードを抜く場合はppを次のカードに移す。
else pd->next=p->next;//そうでなければ前後のカードからの関係を外す。
free(p);//抜かれたカードの領域解放
count++;
pd=p=pp;
}
// 最後の1枚に対する処理
free(qd->next);
qd->next=NULL;
return shuffle;
}
この回答への補足
プログラムまでマコトにありがとうございます。なかなか理解するには自分のスペックがちょっと足らない気がします。。。@期限がまもないので理解していきたい気持ち半分プログラムを完成させなければという気持ち半分です。
自分がおもっていたよりも多くの変数が用意なされてたことにちょっと驚きました。また自分の頭がコンピュータ側としての理解の仕方に合わせれてないことを痛感しました。
もうちょっと練り考えて見たいと思います。
No.4
- 回答日時:
事故レス(^_^
> ただ単純に元々のpの先頭にリセットしたかったのであれば、単純に
> 「pp=p=decklist();」に変更、while文最後の「p->next=p;」を
> 「p=pp;」にすればいいだけですよ。
分かって頂けるとは思いますが、上記変更ではwhile文は終了しません。
単純にfor文に置き換えるのが手でしょうね
No.3
- 回答日時:
もう一回プログラムを見直してみました。
ご確認頂けますか?
・shufflelist関数
> for文で繰り返した後での位置からまたカウントすると思ったので一度リセット
> する意味で返したつもりなんですけどどういう理由からNGなのでしょうか?
ただ単純に元々のpの先頭にリセットしたかったのであれば、単純に
「pp=p=decklist();」に変更、while文最後の「p->next=p;」を
「p=pp;」にすればいいだけですよ。
ただ、pからカードを抜く処理がうまくいっていないように見えるのが
気になります。
カードを抜くには一つ前のpに対するp->nextに対し、抜かれたpのp->nextを
設定する必要があります。
while文内のforでpツリーを追う際に一つ前のpを保持している必要がありますよね?
あと、シャッフルするのに乱数を使うのは当然ですが、乱数初期化関数を
ループ内で何度も実行するものではありません。
また、乱数初期化関数に定数を設定するものでもないです。
#乱数のハズなのに同じ数しか出てこないことになりますよ。
・person関数
恐らくですが、libraryへの値設定そのものまではうまくいっています。
で、while文の中でa=PERSON;を実行しているのは何故でしょうか?
結局これが原因で1回でroopを抜け、その後のa->nextcard=NULL;で
aがNULLなのに実行してしまい、エラーになっています。
で、エラーとは全然関係ないのですが、person関数はmainからユーザAに
対して実行するようになっています。
ユーザAのためにカードを初期化する、って変じゃないですか?
(これはデバッグ用?shufflelist関数内にあるのはOKだとは思います)
そもそも関数仕様からすると、main()関数内でAに対し領域を割り当てる
べきかと思います。
・main関数
まずはA、B、C、Dに領域を割り当てましょう。
で、最初に呼ぶのはshufflelist関数ですよね?
person関数でユーザにカードを割り当てるのであれば、1枚1枚割り当てるような
関数に変更した方がいいかと思います。
(カードは53枚ありますからね)
・その他
カードを抜く関数を作成した方がいいですかね。
そーすればshufflelist関数でも使用できますし、ユーザからのカードを
抜く処理にも使えます。
カードリストから先頭の削除を行う処理がキモですが、ポインタのポインタを
使えばいいかとおもいます。
この回答への補足
助言ありがとうございます。
>ただ単純に元々のpの先頭にリセットしたかったのであれば、単純に
>pp=p=decklist();」に変更、while文最後の「p->next=p;」を
>p=pp;」にすればいいだけですよ。
この部分ですがp->nextを最初に戻すのはリストの先頭であるshuffleをさすべきだと思ったのでp->next=shuffle;だと思いました。
person関数では処理がおかしいですね。ありがとうございます。
乱数のところですがそういう仕様だとは知らずwhile分の中に入れてました。
mainからその他については個人的にperson関数では配るだけをさせメインの中で配られたそれぞれのリストをさすためのA,B,C,Dを用意させそこに代入させようかと思います。カードを抜く関数は同時に捨てさせる機能を搭載してプレイヤー間で使いたいとおもっています。
チョット自分でも7o8さんのおっしゃっている
>ただ、pからカードを抜く処理がうまくいっていないように見えるのが>気になります。カードを抜くには一つ前のpに対するp->nextに対し、
>抜かれたpのp->nextを 設定する必要があります。while文内のforでp
>ツリーを追う際に一つ前のpを保持している必要がありますよね?
の部分は新しく*dをつくりd=p->next;p->next=d->next;free(d);と続けそのpのさしている部分を開放しなくせばいいと思い組んでみましたが、1回目は返せても2回目以降の試行でもでてきます。
今のところの改良し手を加えたプログラムは下記です。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define N 53
//デッキのカードの構造体
struct card{
int t;
struct card *next;
};
//カードを配った後のそれぞれのプレイヤーの情報(仮
struct player{
int card;
struct player *nextcard;
struct player *nextturn;
};
//関数定義
struct card *talloc(void);
struct player *lalloc(void);
struct card *decklist(void);
struct card *shufflelist(void);
struct player *person(void);
void displist(void);
//void displist(struct player *hito);
int main(void){
struct player *A,*B,*C,*D;
/*A=person();
displist(A);
B=person();
displist(B);
C=person();
displist(C);
D=person();
displist(D);
*/
displist();
return 0;
}
//card構造体のセルの確保
struct card *talloc(void){
return (struct card *)malloc(sizeof(struct card));
}
//player構造体のセルの確保
struct player *lalloc(void){
return (struct player *)malloc(sizeof(struct player));
}
//カードを切る前の整列された山札の関数
struct card *decklist(void){
int i,add=1,count=0;
struct card *p,*deck;
deck=NULL;
for(i=0;i<53;i++){
if(count==4){
add+=1;
count=0;
}
p=talloc();
p->t=add;
p->next=p;
count++;
p->next=deck;
deck=p;
}
return deck;
}
//山札をシャッフルした後の山札の関数
struct card *shufflelist(void){
int i,j,a,count=0,r;
struct card *d,*p,*q,*shuffle;
p=decklist();
shuffle=NULL;
srand(time(NULL));
//for(j=0;j<20;j++){
r=(int)rand()%(51-count);
for(i=0;i<r;i++){
p=p->next;
}
q=talloc();
q->t=p->t;
d=p->next;
p->next=d->next;
free(d);
q->next=shuffle;
shuffle=q;
count++;
p->next=shuffle;
//}
return shuffle;
}
//プレイヤーへカードを配るための関数
struct player *person(void){
struct player *a,*hito;
struct card *library,*old;
int i;
hito=NULL;
library=decklist();
old=decklist();
while(library->next!=NULL){
a=lalloc();
a->card=library->t;
old->next=library->next;
for(i=0;i<4;i++){
library=library->next;
}
a->nextcard=hito;
hito=a;
}
return hito;
}
//表示用関数
void displist(void){
struct card *p;
p=shufflelist();
while(p!=NULL){
printf("%d ",p->t);
p=p->next;
}
}
No.2
- 回答日時:
shufflelist()内のwhile文継続条件は pがNULLでないこと、となっています。
で、これはどこかでpがNULLになることを期待しているとは思うのですが、
そのwhile文最後で、「p->next=p;」を実行しています。
pがNULLになることがあるのであれば、この実行は NGですし、おそらく
それが原因で異常終了しているかと思います。
person()も同様で while文継続条件は aがNULLでないこと、となっています。
つまり、while文を抜けた時点で aはNULLであり、その直後に
「a->nextcard=a;」を実行しています。
確認してみては如何でしょうか?
この回答への補足
助言ありがとうございます。
最後にあるp->next=p;は自分でも確信はないんですが上のfor文でポインタを移動し値を示しているのでwhile文で次の処理をするときp->nextがshuffleからでなくfor文で繰り返した後での位置からまたカウントすると思ったので一度リセットする意味で返したつもりなんですけどどういう理由からNGなのでしょうか?今書いて思ったのでリセットという意味では一度p->nextにshuffleをいれるとリセットされると思ったんですがこれも何かでます。。。
person()の方は条件文を間違えました。このときはlibraryからそれぞれプレイヤーに配るので判定はaではなくlibraryの方でやってみましたがこれでもだめでした。
まだCプログラムを書く上で知識が足りない部分もあり完成させるのに時間がかかるかもしれませんがこれを完成させたいと思っています。
期限も間近に近づいていることもあり焦っています。。。
さまざまな意見よろしくお願いします。
No.1
- 回答日時:
まず、「デバッグが起きる」という言い方はしません。
添付の画像のようなメッセージボックスが出ていると思いますが、これを見ると「例外が発生する」という記述があります。
これであれば通じます。
いまひとつ何がやりたいのか分かりませんが、まず「例外が発生する」原因について
person関数で変数[PERSON]にNULLを設定しています。
その後、[PERSON]はperson関数内で変更されていません。
whileの中で変数[a]に変数[PERSON]の値を入れています。
(結果、変数[A]はNULLになります。)
その後、[a->nextcard=NULL]の行で変数[a]がNULLのため「例外が発生」しています。
C言語の変数名は小文字から始まり、なるべく小文字で表現するのが通例です。
最初[PERSON]を見たとき、defineで定義されている定数かと思ってしまいました。。。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# 未解決の外部シンボル _printfが関数_mainで参照されました 1 2022/09/18 15:28
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# leetcode21 1 2022/04/21 11:53
- C言語・C++・C# C言語 leetcode21 Merge Two Sorted Lists 2 2022/04/24 19:35
- C言語・C++・C# プログラムが書けません。 4 2023/01/22 22:57
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・「みんな教えて! 選手権!!」開催のお知らせ
- ・漫画をレンタルでお得に読める!
- ・「黒歴史」教えて下さい
- ・2024年においていきたいもの
- ・我が家のお雑煮スタイル、教えて下さい
- ・店員も客も斜め上を行くデパートの福袋
- ・食べられるかと思ったけど…ダメでした
- ・【大喜利】【投稿~12/28】こんなおせち料理は嫌だ
- ・前回の年越しの瞬間、何してた?
- ・【お題】マッチョ習字
- ・モテ期を経験した方いらっしゃいますか?
- ・一番最初にネットにつないだのはいつ?
- ・好きな人を振り向かせるためにしたこと
- ・【選手権お題その2】この漫画の2コマ目を考えてください
- ・2024年に成し遂げたこと
- ・3分あったら何をしますか?
- ・何歳が一番楽しかった?
- ・治せない「クセ」を教えてください
- ・【大喜利】【投稿~12/17】 ありそうだけど絶対に無いことわざ
- ・【選手権お題その1】これってもしかして自分だけかもしれないな…と思うあるあるを教えてください
- ・集合写真、どこに映る?
- ・自分の通っていた小学校のあるある
- ・フォントについて教えてください!
- ・これが怖いの自分だけ?というものありますか?
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・10代と話して驚いたこと
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
構造体のリスト削除
-
複数桁10進数の*桁目だけを抽出...
-
「Aに対するBの割合」と「Aに対...
-
エクセルで可視セルにのみ値貼...
-
ある商品のロス率を5%見込み、...
-
Enterキーを押されたら次の処理...
-
for文について
-
#define _CRT_SECURE_NO_WARNIN...
-
2÷3などの余りについて
-
DWORDの実際の型は何でしょうか
-
多重定義が起きている?--lnk20...
-
関数f(x)= x³‐3ax²+3ax+2 が極...
-
double型の値をchar配列に変換...
-
EXCELの分散分析表のP-値が....
-
数学のデータの相関の散布図で...
-
c言語の問題です
-
全角文字の判定
-
printfの有無で値が変化する
-
java初心者です。入力されたの...
-
コマンドプロンプトのウィンド...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
構造体のリスト削除
-
InvokeMemberメソッドとは何を...
-
C# ref引数のnull判定
-
C言語 リスト
-
コールバック関数はnullになら...
-
C#でのEXCEL出力に関して
-
空のカラムを挿入
-
if文でゼロとNULLを除外するた...
-
ばばぬきプログラムについて
-
リスト構造
-
C言語 リスト
-
信頼区間の1.96や1.65ってどこ...
-
「Aに対するBの割合」と「Aに対...
-
エクセルで可視セルにのみ値貼...
-
Enterキーを押されたら次の処理...
-
「指定されたキャストは有効で...
-
2÷3などの余りについて
-
C言語での引数の省略方法
-
Aの値からBの値を除するとは??
-
DWORDの実際の型は何でしょうか
おすすめ情報