【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.20893

hangmanプログラムについて
投稿者---S(2005/05/01 00:55:15)


初めて投稿させていただきます。現在hangmanプログラム(単語を読み込んでアルファベットを入力、正解の単語を当てるやつです)を創っていて、

#include <stdio.h>

main()
{
  int i;
  char CT='y';
  
  while(CT=='y'){   
    int NG=4,xx=0,n=0;
    char d='\0';
    char a[25]="\0",b[26]="\0",c[25]="\0";

    //単語格納//
    printf("単語を入力してください ");
    gets(a);

    //文の長さ表示//
    while(a[n]!=0) n++;
    for(i=0;i<n;i++) c[i]='?';
    printf("c=%s\n",c);

    while(NG>=1){
      int yy=0;
      
      //アルファベット入力//
      printf("文字を入力してください ");
      scanf("%c",&d);
      b[xx]=d;
      xx++;
      //printf("b=%s",b);
      
      for(i=0;i<n;i++){
        if(a[i]==d){
          c[i]=a[i];
          yy=1;
        }
      }
      printf("yy=%d\n",yy);
      
      if(yy==0){
        NG--;
      }
      
      //単語候補、回数、仕様文字表示//
      printf("c:%s\n",c);
      printf("間違えられる回数 残り%d\n",NG);
      printf("使用した文字 %s\n",b);
   
     }
    printf("続けますか? y/n ");
    scanf("%c",&CT);
    printf("\n");
  }
}





というプログラムを書いたのですが、
・今はgets()で単語を格納しているが、ファイルを指定(wordsと仮定)して、そこから単語を読み込みたいが方法がわからない
・なぜか一度予想アルファベットを読み込んだ後、勝手にもう一周する
という問題が出てきました。
前者は探し方、調べ方が悪いといわれてしまうと仕方ないのですが、見当もつかず、後者もなぜこうなってしまうのかがわかりません。
何かヒントだけでもいいので、教えていただけないでしょうか。
OSはwindows XP、コンパイラはBorland C++ Compilerを使っています。
何卒、よろしくお願いします。


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:hangmanプログラムについて 20894 Craft 2005/05/01 02:22:46
<子記事> Re:hangmanプログラムについて 20895 まきじ 2005/05/01 10:53:47
<子記事> Re:hangmanプログラムについて 20911 RiSK 2005/05/04 13:55:09


No.20894

Re:hangmanプログラムについて
投稿者---Craft(2005/05/01 02:22:46)


こんばんは。

>・今はgets()で単語を格納しているが、ファイルを指定(wordsと仮定)して、そこから単語を読み込みたいが方法がわからない

http://www9.plala.or.jp/sgwr-t/c/sec17.html
を参考にして、gets()をファイルから読み込む処理に変えてみて下さい。
ランダムを考えると難しくなるので、とりあえずは読んでみるところから。

>・なぜか一度予想アルファベットを読み込んだ後、勝手にもう一周する
>という問題が出てきました。
>    printf("続けますか? y/n ");
>    scanf("%c",&CT);


という処理がありますが、
scanf()関数は、掲示されたソースのように書式指定子が1つだけだと、ほかの文字と一緒に改行入力すると改行だけ残ってしまいます(ちょっと違うかも)

ここの過去ログで同じような問題で困っていた人が結構いたと思いますので、scanf()で過去ログ検索するとなにかみつかるかもしれません。

>前者は探し方、調べ方が悪いといわれてしまうと仕方ないのですが、見当もつかず、後者もなぜこうなってしまうのかがわかりません。
>何かヒントだけでもいいので、教えていただけないでしょうか。

ソースを読んで気づいた点をコメントさせていただきました。


この投稿にコメントする

削除パスワード

No.20895

Re:hangmanプログラムについて
投稿者---まきじ(2005/05/01 10:53:47)


>何卒、よろしくお願いします。

変数名は、他人が見ても、その変数がどういう役割を
しているか判る様な、名前にしましょう。



この投稿にコメントする

削除パスワード

No.20896

Re:hangmanプログラムについて
投稿者---S(2005/05/01 11:18:51)


Craftさんへ
 回答ありがとうございました。妙なループもしなくなり、何とか創れそうです。とても助かりました。

まきじさんへ
 ごもっともですね・・・どうも不精して簡単な関数を作ってしまうんで・・・自分のわかりやすさもあるでしょうし、以後気をつけながら創ってみます。





この投稿にコメントする

削除パスワード

No.20897

Re:hangmanプログラムについて
投稿者---まきじ(2005/05/01 12:22:01)


私も作ってみました。

#include<stdio.h>
#include<string.h>

#define WORD_MAX 20
#define CHANCE 5

int main(void){

    FILE *fp;
    char word[WORD_MAX]; //正解文字列

    char str[WORD_MAX]; //解答文字列

    char ans[WORD_MAX]; //正解部分

    int word_len, str_len, diff; //正解文字列の長さ、解答文字列の長さ、間違った文字数

    int chance;
    int i;
    
    //ファイルの読み込み

    if((fp = fopen("words","r")) == NULL) return 1;
    
    if(fgets(word,WORD_MAX,fp) == NULL){
        fclose(fp);
        return 1;
    }
    fclose(fp);
    
    //初期化

    word_len = strlen(word);
    for(i = 0; i < WORD_MAX; i++) ans[i] = '?';
    str_len = 0;
    
    for(chance = 1; chance <= CHANCE; chance++){
        
        printf("%d/%d 回目\n",chance,CHANCE);
        
        //入力

        while(str_len != word_len){
            printf("%d 文字の文字列を入力\n",word_len);
            gets(str);
            str_len = strlen(str);
        }
        
        //正解チェック

        diff = word_len;
        for(i = 0; word[i] != '\0'; i++){
            if(str[i] == word[i]){
                ans[i] = word[i];
                diff--;
            }
        }
        ans[i] = '\0';
        
        if(!diff){
            printf("正解です。\n");
            break;
        }
        
        printf("%d 文字間違ってます。\n",diff);
        printf("%s\n",ans);
        
        //初期化

        str_len = 0;
        for(i = 0; i < WORD_MAX; i++) ans[i] = '?';
        rewind(stdin);
    }
}





この投稿にコメントする

削除パスワード

No.20935

Re:hangmanプログラムについて
投稿者---RiSK(2005/05/05 00:00:06)


>    if(fgets(word,WORD_MAX,fp) == NULL){

fgets は改行文字を '\0' にしてくれません。
希望通りの動作でしょうか?

>            gets(str);

gets は改行文字を '\0' にします。
これは希望通りの動作でしょうか?
fgets(..., stdin); ではダメでしょうか?

読み込むファイルに改行があってはならないという仕様ですか?
# gets はバッファオーバーフローする可能性もあります


>        if(!diff){
>            printf("正解です。\n");
>            break;
>        }

printf に関して次のことが気になります。
# 細かいけど (^^;

・printfの第一引数で表示したい文字列を直に渡す
%? があるとバグるので普段から printf("%s", "正解です。\n"); ってな具合にしたい。
# そもそも第一引数は「書式文字列」ですから。

・printf で変換する必要が無いときに最後で \n
puts("正解です。"); にすれば↑の問題もないし,
冗長でもない。
# gcc は printf → puts の変換をしてくれるらしい…けど…


関数が長すぎ→バグの原因

変数のスコープ広すぎ→バグの原因
str_len なんかは for ブロックの中で宣言して 0 で初期化すればいいですね。


>        rewind(stdin);

未定義動作。



この投稿にコメントする

削除パスワード

No.20939

Re:hangmanプログラムについて
投稿者---まきじ(2005/05/05 01:44:11)


>fgets は改行文字を '\0' にしてくれません。
>希望通りの動作でしょうか?
>gets は改行文字を '\0' にします。
>これは希望通りの動作でしょうか?
>fgets(..., stdin); ではダメでしょうか?
>読み込むファイルに改行があってはならないという仕様ですか?

改行が無かったので、たまたまうまくいった様です(^^;
関数の仕様を、もっと確認しないとダメですね、、、
俗に言う、スパゲティープログラムってやつですか。
恥ずかしいです。

>printf に関して次のことが気になります。
># 細かいけど (^^;

本当、細かいですね(^^;
正直、何もコメントし様がありません。

ご指摘ありがとうございました。



この投稿にコメントする

削除パスワード

No.20941

Re:hangmanプログラムについて
投稿者---まきじ(2005/05/05 11:22:17)


悔しいので、RiSKさんに指摘された部分を直してみました。

#include<stdio.h>
#include<string.h>

#define WORD_MAX 20 //最大文字数

#define CHANCE 5 //最大解答回数


char* input_str(char* str,int size);
int check_word(const char *s, const char *w,char *a);
int get_word(const char *filename, char *w);

int main(void){

    char word[WORD_MAX]; //正解文字列

    char str[WORD_MAX]; //解答文字列

    char ans[WORD_MAX]; //正解部分

    int chance; //解答回数

    
    if(!get_word("words",word)) return 1;
    
    for(chance = 1; chance <= CHANCE; chance++){
    
        //正解文字列の長さ、解答文字列の長さ、間違った文字数

        size_t word_len, str_len, diff; 
        int i;
        
        str_len = 0;
        word_len = strlen(word);
        for(i = 0; i < WORD_MAX; i++) ans[i] = '?';

        printf("%d/%d 回目\n",chance,CHANCE);
        
        while(strlen(input_str(str,word_len)) != word_len);
        
        diff = word_len - check_word(str,word,ans);
        
        if(!diff){
            puts("正解です。");
            break;
        }
        
        printf("%d 文字間違ってます。\n",diff);
        printf("%s\n",ans);
    }
    
    if(chance > 5) puts("\n残念でした。\n");
}

char* input_str(char* s,int size){

    char *p;
    
    printf("%d 文字の文字列を入力\n",size);
    fgets(s,WORD_MAX,stdin);
    p = strchr(s,'\n');
    if(p) *p = '\0';
    
    return s;
}

int check_word(const char *s, const char *w,char *a){

    int match;
    
    for(match = 0; *w != '\0'; a++,s++,w++){
        if(*s == *w){
            *a = *w;
            match++;
        }
    }
    *a = '\0';
    
    return match;
}

int get_word(const char *filename, char *w){

    FILE *fp;
    char *p = NULL;
    
    if((fp = fopen(filename,"r")) == NULL) return 1;
    
    if(fgets(w,WORD_MAX,fp) == NULL){
        fclose(fp);
        return 0;
    }
    fclose(fp);
    
    p = strchr(w,'\n');
    if(p) *p = '\0';
    
    return 1;
}




この投稿にコメントする

削除パスワード

No.20947

Re:hangmanプログラムについて
投稿者---通りすがりですが(2005/05/05 17:26:29)


>> rewind(stdin);
> 未定義動作。

fflush(stdin)が未定義なのは知っているのですが,
rewind(stdin)も未定義なのでしょうか?

MSDNのrewindに
キーボード バッファをクリアするには、既定でキーボードに関連付けられている標準入力ストリーム (stdin) を引数に指定して rewind 関数を使用します。

とあるのですが,どうなんでしょう?


この投稿にコメントする

削除パスワード

No.20949

Re:hangmanプログラムについて
投稿者---Ban(2005/05/05 17:42:24)


>fflush(stdin)が未定義なのは知っているのですが,
>rewind(stdin)も未定義なのでしょうか?

rewind はエラー表示子をクリアすることを除けば
(void)fseek(stream, 0L, SEEK_SET)と等価だと規格に書いてあります。
(JIS X3014)

fseek なので入力対象でも未定義ではないと思いますが、
対象ファイル次第で失敗している可能性はありそうです。



この投稿にコメントする

削除パスワード

No.20950

Re:hangmanプログラムについて
投稿者---通りすがりですが(2005/05/05 18:33:25)


>fseek なので入力対象でも未定義ではないと思いますが、
>対象ファイル次第で失敗している可能性はありそうです。

未定義ではないけれど,あまり好ましくないということでしょうか。
使わないで済むのならその方が良さそうですね。

ありがとうございました。



この投稿にコメントする

削除パスワード

No.20911

Re:hangmanプログラムについて
投稿者---RiSK(2005/05/04 13:55:09)


hangman プログラムって何? って状態だったので,
ここを参考にして作ってみた。
人の絵を描くのは割愛。

#include <stdio.h>

#define N 32

typedef struct {
    char s[N];
    int is_show[N];
} HANGMAN_STR;

size_t load_answer(const char * filename, HANGMAN_STR * answer, size_t size)
{
    FILE * f; size_t n; int c;
    if ((f = fopen(filename, "r")) == NULL) return 0;
    for (n = 0; n < size && (c = getc(f)) != EOF && c != '\n'; ++n) {
        answer->s[n] = c;
        answer->is_show[n] = 0;
    }
    fclose(f);
    return n;
}

void show_hint(HANGMAN_STR * answer, size_t size, unsigned chance)
{
    size_t i;
    printf("chance:%2u %2u文字:", chance, (unsigned)size);
    for (i = 0; i < size; ++i)
        putchar(answer->is_show[i] ? answer->s[i] : '_');
    putchar('\n');
}

int choose_char(void)
{
    int c;
    while ((c = getchar()) == '\n')
        ;
    return c;
}

int match(HANGMAN_STR * answer, size_t size, int c)
{
    size_t i; int is_match = 0;
    for (i = 0; i < size; ++i)
        if (answer->s[i] == c) {
            answer->is_show[i] = 1;
            is_match = 1;
        }
    return is_match;
}

int correct(HANGMAN_STR * answer, size_t size)
{
    size_t i;
    for (i = 0; i < size; ++i)
        if (!answer->is_show[i]) return 0;
    return 1;
}

void show_answer(HANGMAN_STR * answer, size_t size)
{
    size_t i;
    printf("%s", "answer is ");
    for (i = 0; i < size; ++i) putchar(answer->s[i]);
    putchar('\n');
}

int hangman_game(HANGMAN_STR * answer, size_t size, unsigned chance)
{
    while (chance) {
        int c;
        show_hint(answer, size, chance);
        if ((c = choose_char()) == EOF) return 0;
        if (!match(answer, size, c)) {
            puts("no!");
            --chance;
        }
        if (correct(answer, size)) break;
    }
    puts(chance ? "you win!" : "hang you...");
    show_answer(answer, size);
    return 1;
}

int main(int argc, char ** argv)
{
    HANGMAN_STR answer; size_t size;
    if (argc < 2) return 1;
    if ((size = load_answer(argv[1], &answer, N)) == 0) return 1;
    if (!hangman_game(&answer, size, size)) return 1;
    return 0;
}



この投稿にコメントする

削除パスワード

No.20934

Re:hangmanプログラムについて
投稿者---RiSK(2005/05/04 23:30:26)


load_answer の size はバッファのサイズで
それ以外の size は有効な文字数を表します。
ネーミングが良くないですね。

で,何回も出てくる(文字数を表す) size_t size は
HANGMAN_STR のメンバにすれば良いと気付きました。

choose_char は訂正コード上げます。
int choose_char(void)
{
    int c;
    while ((c = getchar()) != EOF && !isgraph(c))
        ;
    return c;
}



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧