わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

構造体プログラミングの実例(8)〜完成したら

完成版のソースコードです.214行になりました.

/*
 * roundrobin.c - 総当たり戦の対戦結果を求める
 * 
 * 作成者: takehikom
 * 作成日時: Thu May 17 19:01:56 JST 2007
 */

/***********************************************************************
  仕様:
  ・入力は標準入力から,出力は標準出力へ.ASCII文字のみを使用する.
  ・入力の1行目は,全プレイヤーの名前.2文字以上20文字以下.
  ・入力の2行目以降は,「!」であればその時点の表を出力する.
   「x>y」(xとyは1行目に出るプレイヤー名)であれば,xはyに勝ち,
   yはxに負けの情報を保存する.

  入力例:
WNM
!
W>N
W>M
M>N
!
 ***********************************************************************/

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

#define PLAYER_MAX 20    /* プレイヤーの最大数 */
#define LINE_BYTE_MAX 25 /* 1行の最大字数('\0'を除く) */

enum result { /* 勝敗情報を表す列挙型 */
  RSLT_NOTPLAYED, /* 未対戦 */
  RSLT_WIN,       /* 勝 */
  RSLT_LOSE,      /* 負 */
  RSLT_NA         /* (同一プレイヤー同士なので)対戦しない */
};

typedef struct { /* 対戦表に関する構造体 */
  char player[PLAYER_MAX + 1];       /* プレイヤー名 */
  int table[PLAYER_MAX][PLAYER_MAX]; /* 勝敗 */
  int player_number;                 /* プレイヤー数 */
} rrtable;

/* 1文字取得し,EOFでも改行でもなければ真 */
#define mygetchar(c) (((c) = getchar()) != EOF && (c) != '\n')

int has_same_player(char *player);
rrtable *initialize(char *player);
void print_table(rrtable *tab);
char *get_line(char *line, int max_byte);
int search_player_index(rrtable *tab, char c);
void add_result(rrtable *tab, char *line);

/* 文字列playerの中に同一文字が2つ以上あるか判定する */
int has_same_player(char *player)
{
  int i, j;

  for (i = 0; i < strlen(player); i++) {
    for (j = i + 1; j < strlen(player); j++) {
      if (player[i] == player[j]) {
        return 1;
      }
    }
  }

  return 0;
}

/* rrtable構造体を初期化する */
rrtable *initialize(char *player)
{
  rrtable *tab;
  int i;

  if (strlen(player) < 2) {
    printf("Too few players.\n");
    return NULL;
  }

  if (strlen(player) > PLAYER_MAX) {
    printf("Too many players.\n");
    return NULL;
  }

  if (has_same_player(player)) {
    printf("A player appears twice.\n");
    return NULL;
  }

  if ((tab = (rrtable *)malloc(sizeof(rrtable))) == NULL) {
    printf("Malloc error.\n");
    exit(1);
  }

  strcpy(tab->player, player);
  tab->player_number = strlen(player);
  memset((void *)tab->table, RSLT_NOTPLAYED, PLAYER_MAX * PLAYER_MAX);
  for (i = 0; i < tab->player_number; i++) {
    tab->table[i][i] = RSLT_NA;
  }

  return tab;
}

/* 総当たり表を出力する */
void print_table(rrtable *tab)
{
  int i, j;

  printf(" %s\n", tab->player);

  for (i = 0; i < tab->player_number; i++) {
    putchar(tab->player[i]);
    for (j = 0; j < tab->player_number; j++) {
      switch (tab->table[i][j]) {
      case RSLT_NOTPLAYED:
        putchar(' '); break;
      case RSLT_WIN:
        putchar('o'); break;
      case RSLT_LOSE:
        putchar('x'); break;
      case RSLT_NA:
        putchar('-'); break;
      default:
        putchar('?');
      }
    }
    putchar ('\n');
  }
}

/* 標準入力から1行読み出して,lineの参照する配列に格納する */
char *get_line(char *line, int max_byte)
{
  int c;
  int i;

  /* 1文字ずつ読み出して,lineの配列に格納する */
  for (i = 0; i < max_byte && mygetchar(c); i++) {
    line[i] = c;
  }
  line[i] = '\0';

  /* 改行になるまで読み飛ばす */
  if (i == max_byte) {
    while (mygetchar(c))
      ;
  }

  /* 1文字も読み出せなければNULLを返す */
  if (line[0] == '\0') {
    return NULL;
  }
  return line;
}

/* プレイヤー名の文字列から文字cを探してその位置を返す */
int search_player_index(rrtable *tab, char c)
{
  int i;
  char *player = tab->player;

  for (i = 0; i < strlen(player); i++) {
    if (c == player[i]) {
      return i;
    }
  }

  /* 見つからなければ負の数を返す */
  return -1;
}

/* 対戦結果を追加する */
void add_result(rrtable *tab, char *line)
{
  int winner, loser;

  /* 「勝者名>敗者名」でなければ,不正な入力とみなして無視する */
  if (strlen(line) != 3 || line[1] != '>'
      || (winner = search_player_index(tab, line[0])) < 0
      || (loser = search_player_index(tab, line[2])) < 0
      || winner == loser) {
    printf("%s: ignored.\n", line);
    return;
  }

  tab->table[winner][loser] = RSLT_WIN;
  tab->table[loser][winner] = RSLT_LOSE;
}

int main(void)
{
  char line[LINE_BYTE_MAX + 1];
  rrtable *tab;

  /* 最初の行を読み出し,構造体を初期化する */
  get_line(line, LINE_BYTE_MAX + 1);
  if ((tab = initialize(line)) == NULL) {
    return 1;
  }

  /* 各行を読み出し,表を埋めていく */
  while (get_line(line, LINE_BYTE_MAX + 1)) {
    if (line[0] == '!') {
      print_table(tab);
    } else {
      add_result(tab, line);
    }
  }

  return 0;
}

プログラムが完成したときには,また他の,わくわくさせるようなプログラミング課題に取り組みたいと考えるかもしれません.ですが,その気持ちを少し抑えて,このソースコードを見て,まだ何かできることはないかと考えてみましょう….
一つは,「教師」に見てもらって,アドバイスを受けることです.「教師」については,ここが変だよC言語 (続き)で書いたのでご覧ください.
もしそういう「教師」が身近にいなければ…インターネットの力を借りましょう.ブログを開設して,自作のコードを公開するのです.関心のある人が見てくれれば,手元で動かしてくれます.ある処理系ではエラーになるという指摘があるかもしれません.入力を変えると,期待通りに動かないとか,無限ループに陥るとか,うんと時間がかかるかという指摘が来るかもしれません.指摘にとどまらず,この行をこう修正するといいよというアドバイス*1も,もらえるかもしれません.それぞれの指摘に誠実に対応することで,より実践的,実用的なプログラミング技法が身につくというものです.インターネットでの交流もできますね.
自分の書いたプログラムを自分だけで閉じず,先達から批評を仰ぐことは,自身の成長だけでなく,コミュニティ*2の充実にもつながります.10年前に書かれてwritten decade ago,今もその価値があせない,以下の名言を引用して,「構造体プログラミングの実例」を終えることにします.

世の中,プログラムを隠す人間ばかりではない.自分の作ったプログラムを,どうぞ使ってくださいと公表する人間たちもいる.わからないと,よく知っている人のところにプログラムを持って相談に行く人もいる.
こういう連中は,概して優秀である.あるいは短時間に腕を上げていく.優秀であり,自分のプログラムに自信を持っているから発表できる,と反論するであろうが,こういう連中は,もともとそんなにプログラムを隠したりしない.人の作ったプログラムを読んだり,自分のプログラムを人に渡したりということに,もともと慣れている.失敗を隠さない癖がついているため,外部からの支援も受けられ,ますます腕に磨きがかかるということになる.
(藤原博文,『コンピュータの業界のオキテ!!』,技術評論社,ISBN4774102113,p.38.)

*1:ときには,diffと呼ばれる特殊な形式で,修正箇所を教えてくれるかもしれません.

*2:学科内のCプログラミング自主演習も,あれでひとつのコミュニティの構築を試みているのです.