明解C言語中級編 9章の自由課題4を簡易版で解いてみた

自由課題9-4のもともとの問題はこんな感じ。

「8章の課題8-7のタイピングゲームを拡張して、所要時間、ミス、速度などの履歴を記録できるように作成しろ。」

という問題ですが、ファイル操作という部分に焦点をあてて(という建前で)めんどくさいのを省いてシンプルな構成にする問題に変更してみました。

課題8-7の問題はキー練習と英単語、C言語の単語のタイピング練習を選択してえらぶプログラムになっているのだけど、今回はC言語だけのタイピングプログラムの部分を抽出して、本題であるファイルに記録するというプログラムにしました。

前回の問題と同様にバイナリファイルでやってます。なぜかというと、テキストデータにするとコンピュータでバイナリからテキストに変換するという工程が入るらしいので、コンピュータにやさしいプログラムにしようと思ったからである。おもいやりです。

そーす

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include <unistd.h>
#include <float.h>

#define NO      15      //とれーにんぐの回数

char dtfile[] = "DATA.DAT";     //FILENAME

//putch定義
static int putch(int ch){

  int result = putchar(ch);

  fflush(stdout);
  return (result);
}

//data read
double get_data(void)
{
   FILE *fp;
   int moji, miss;
   double time;

   if((fp = fopen(dtfile, "rb")) == NULL){
     printf("ファイルがないので作りますね。\r\n\n");
     time = DBL_MAX;
   }else{
     int i;
     printf("過去の成績\r\n-------------------------\r\n");
     while((i = fread(&moji, sizeof(int), 1, fp)) > 0 ){
       fread(&miss, sizeof(int), 1, fp);
       printf("文字数%d / ミス%d回 \r\n", moji, miss);
       fread(&time, sizeof(double), 1, fp);
         printf("所要時間 %.1f秒\r\n", time);
       printf("入力スピード %.1f 文字/秒\r\n\n", (double)moji / time);
       }
     fclose(fp);
   }
}

//data write
void put_data(int moji, int miss, double score)
{
   FILE *fp;

   if((fp = fopen(dtfile, "ab")) == NULL){
     printf("ファイルがあーりません\r\n");
     fflush(stdout);
   }else{
     fwrite(&moji, sizeof(int), 1, fp);
     fwrite(&miss, sizeof(int), 1, fp);
     fwrite(&score, sizeof(double), 1, fp);
     fclose(fp);
   }
}

const char *cstr[] = {
  "auto", "break", "case", "char", "const", "continue",
  "default", "do", "double", "else", "enum", "extern",
  "float", "for", "goto", "if", "int", "long",
  "register", "return", "short", "signed", "sizeof", "static",
  "strunct", "switch", "typedef", "union", "unsigned", "void",
  "volatile", "while",
  "abort", "abs", "acos", "asctime", "asin", "assert",
  "atan", "atan2", "atexit", "atof", "atoi", "atol",
  "bsearch", "calloc", "ceil", "clearerr", "clock", "cos",
  "cosh", "ctime", "difftime", "div", "exit", "exp",
  "fabs", "fclose", "feof", "ferror", "fflush", "fgetc",
  "fgetpos", "fgets", "floor", "fmod", "fopen", "fprintf",
  "fputc", "fputs", "fread", "free", "freopen", "frexp",
  "fscanf", "fseek", "fsetpos", "ftell", "fwrite", "getc",
  "getchar", "getenv", "gets", "gmtime", "isalnum", "isalpha",
  "iscntrl", "isdigit", "isgraph", "islower", "isprint", "ispunct",
  "isspace", "isupper", "isxdigit", "labs", "ldexp", "ldiv",
  "localeconv", "localtime", "log", "log10", "longjmp",
  "malloc", "memchr", "memcmp", "memcpy", "memmove", "memset",
  "mktime", "modf", "perror", "pow", "printf", "putc",
  "putchar", "puts", "qsort", "raise", "rand", "realloc",
  "remove", "rename", "rewind", "scanf", "setbuf", "setjmp",
  "setlocale", "setvbuf", "signal", "sin", "sinh", "sprintf",
  "sqrt", "srand", "sscanf", "strcat", "strchr", "strcmp",
  "strcoll", "strcpy", "strcspn", "strerror", "strftime","strlen",
  "strstr", "strtod", "strtok", "strtol", "strtoul", "strxfrm",
  "system", "tan", "tanh", "time", "tmpfile", "tmpnam",
  "tolower", "toupper", "ungetc", "va_arg", "va_end", "va_start",
  "vfprintf", "vprintf", "vsprintf"
};

//実行本体
int go(const char *str){

  int i;
  int len = strlen(str);
  int mistake = 0;

  for(i = 0; i < len; i++){
   printf("%s \r", &str[i]);
   fflush(stdout);
   while(getch()!=str[i]){
    mistake++;
   }
  }
  return(mistake);
}

//とれーにんぐ
void word_training(const char *mes, const char *str[], int n)
{
  int stage;
  int i = 0;
  int qno, pno;
  int check[NO] = {0};
  int tno, mno;
  clock_t start, end;
   int dammy;

  printf("\n%sを%d個練習します。\r\n", mes, NO);
  printf("スペースキーで開始します。\r\n");
  fflush(stdout);
  while(getch() != ' ')
   ;

  tno = mno = 0;
  pno = n;

  start = time(NULL);

  for(stage = 0; stage < NO; stage++){
   do{
    qno = rand() % n;
    i++;
   }while( qno == check[i] );
   mno += go(str[qno]);
   tno += strlen(str[qno]);
   pno = qno;
   check[i++] = qno;
  }

  end = time(NULL);

   double tscore = (double)difftime(end, start);
  printf("問題:%d文字/ミス : %d回\r\n", tno, mno);
  printf("%.1f秒でした。\r\n", (double)difftime(end, start));
   printf("3秒後に終了します。");
  fflush(stdout);

   put_data(tno, mno, tscore);
   sleep(3);       //いきなり戻ってしまう防止用
}

int main(void)
{

  int cn = sizeof(cstr) / sizeof(cstr[0]);

  initscr();
  cbreak();
  noecho();
  refresh();
  srand(time(NULL)*getpid());

   get_data();

  word_training("C言語の単語", cstr, cn);

  endwin();

  return 0;

そうそう、教本で使っているヘッダーを使うとgccではうまく動かないので使ってません。

プログラム終了時に画面がもどってしまうのを防ぐのにscanfのダミーを作ってましたが、sleep関数をつかってマイルドにしました。おもいやりです。
ってか、戻らないようにするのがいいんでしょうけどね。

実行結果

過去の成績
-------------------------
文字数91 / ミス10回
所要時間 28.0秒
入力スピード 3.2 文字/秒

文字数91 / ミス1回
所要時間 27.0秒
入力スピード 3.4 文字/秒

文字数81 / ミス3回
所要時間 24.0秒
入力スピード 3.4 文字/秒

文字数87 / ミス51回
所要時間 32.0秒
入力スピード 2.7 文字/秒

文字数86 / ミス9回
所要時間 28.0秒
入力スピード 3.1 文字/秒


C言語の単語を15個練習します。
スペースキーで開始します。

こんな感じに表示されます。課題9-1の応用なので難しさはないですが、まぁいいでしょ。

保存しているバイナリを出力するとこんな感じ。

$ od -i DATA.DAT
0000000          91          10           0  1077673984
0000020          91           1           0  1077608448
0000040          81           3           0  1077411840
0000060          87          51           0  1077936128
0000100          86           9           0  1077673984
0000120

9章ではcatもどきやcpもどきのプログラミングをやっているので、linuxを使っている人にはおもしろい章だと思います。オプションの作りかたってこうやるのかってところもありますので、幅がひろがりんぐですね。