わさっきhb

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

尺取り虫プログラム〜素直なコード

昨日の件,コーディングの前に,少し検討します.
尺取り虫の動き方を,日本語で書いてみます.

  • 上,右下,右,左上,左上,上,右下,右下,右下,右,左上,左上,左上,左上,…

ここから,規則を作ることができます.

  • 原点を含むY軸(x=0)にいるときは,1回だけ上に移動し,向きを右下に切り替えます.
  • 原点以外のX軸(y=0,ただしx≠0)にいるときは,1回だけ右に移動し,向きを左上に切り替えます.
  • x≠0, y≠0のときは,右下または左上に移動し,向きを変えません.

規則性はもっとありますが,それはまた必要なときに考えるとして,ここに書いた規則から,尺取り虫プログラムの基本形を書くことにします.
まずは尺取り虫構造体を定義します.持つべき情報は,X座標,Y座標,ステップ数(何回移動したか),それと方向となります.方向は8方向ではなく,「上」「右下」「右」「左上」の4つです.これは列挙体としましょう.
構造体(what)を定義したら,そこに作用する関数(how)ですが,「内容を出力する」と「1ステップ移動する」の2つが最低限必要です.あとはmain関数.
ということで,ソースファイルは以下のようになりました.

/* inchworm1.c */

#include <stdio.h>

#define INCHWORM_COORD_MIN 0

enum inchworm_dir {
  INCHDIR_UP,
  INCHDIR_RIGHT,
  INCHDIR_UPPERLEFT,
  INCHDIR_LOWERRIGHT
};

struct inchworm {
  int x, y;
  int step;
  enum inchworm_dir dir;
};

void inchworm_put(struct inchworm *wp)
{
  printf("%5d: (%5d,%5d)\n", wp->step, wp->x, wp->y);
}

void inchworm_move(struct inchworm *wp)
{
  switch(wp->dir) {
  case INCHDIR_UP:
    wp->y++;
    wp->dir = INCHDIR_LOWERRIGHT;
    break;
  case INCHDIR_RIGHT:
    wp->x++;
    wp->dir = INCHDIR_UPPERLEFT;
    break;
  case INCHDIR_UPPERLEFT:
    wp->x--;
    wp->y++;
    if (wp->x == INCHWORM_COORD_MIN) {
      wp->dir = INCHDIR_UP;
    }
    break;
  case INCHDIR_LOWERRIGHT:
    wp->x++;
    wp->y--;
    if (wp->y == INCHWORM_COORD_MIN) {
      wp->dir = INCHDIR_RIGHT;
    }
    break;
  default:
    printf("wrong state\n");
  }
  wp->step++;
}

int main(void)
{
  struct inchworm w = {
    INCHWORM_COORD_MIN, INCHWORM_COORD_MIN,
    0,
    INCHDIR_UP
  };

  do {
    inchworm_put(&w);
    inchworm_move(&w);
  } while (w.step <= 100);

  return 0;
}
  • 方向の列挙子と,inchworm構造体(のポインタ)を引数にとる関数は,他の名前と競合することのないよう,「INCHDIR_」や「inchworm_」という接頭語をつけました.
  • 関数inchworm_put, inchworm_moveはいずれも,構造体を引数にとっても,記述できますが,ここはポインタをとるようにしました.
  • 「wp->y++」といった式があります.演算子の優先順位に注意すると,これは「(wp->y)++」であって,「wp->(y++)」ではありません.
  • INCHWORM_COORD_MINという定数を定義しました.要はx=0,またはy=0を判定するためのものです.この値を1にすると,(1,1)を始点とし,第1象限の各点を動く尺取り虫となります.
  • 「inchworm_put(&w);」の直前に「if (x<5&&y<5)」をつけると,{(x,y)|0≦x<5, 0≦y<5}の範囲で各点をどんな順序で巡回するかを知ることができます.