/*
名稱: randwalk.c
作者: 洪朝貴 http://www.cyut.edu.tw/~ckhung/
功能: 從命令列上讀入 delay 與 step, 隨機移動遊標, 並隨意亂走 step 步,
      每走一步休息 delay 毫秒
需求: 螢幕必須支援 ANSI escape sequence
*/

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>		/* 使用到 random() 和 exit() */
#include <unistd.h>		/* 使用到 usleep() */
#include <time.h>		/* 使用到 time() */

void clearscr(void);		/* 清除螢幕 */
void gotorc(int r, int c);	/* 將遊標移到第 r 列, 第 c 行 */

int main(int argc, char *argv[])
{
    int const MAX_ROW = 25, MAX_COL = 80;
				/* 螢幕的高度與寬度 (常數) */
    int step;			/* 總共要走多少步 */
    int delay;			/* 每步休息多少毫秒 */
    int row, col;		/* 目前遊標的位置 */
    int row_new, col_new;	/* 新的遊標位置 */
    int dir;			/* 接下來要走那個方向 */
    int last_dir;		/* 上次走那個方向 */

    if (3 < argc) {
	fprintf(stderr, "usage: %s [delay [[#_of_steps]]\n", argv[0]);
	exit(1);  
	/* return 敘述只能往上跳一層; exit() 函數則可終止程式 */
    }

    /* 如果使用者沒有說每步要休息多久, 就休息 0.1 秒 */
    delay = 2<=argc ? atoi(argv[1]) : 100;
    assert(delay > 0);

    /* 如果使用者沒有說要走多少步, 就走 200 步 */
    step = 3<=argc ? atoi(argv[2]) : 200;
    assert(step > 0);

    clearscr();			/* 清除螢幕 */
    srandom(time(NULL));	/* 用系統目前的時間起始亂數 */

    /* 用亂數隨機決定遊標的起始坐標 */
    row = 1 + (int) (random() / (RAND_MAX + 1.0) * MAX_ROW);
    col = 1 + (int) (random() / (RAND_MAX + 1.0) * MAX_COL);
    last_dir = -3;
    while (0 <= step) {		/* 偷懶法: 用上限當做註標變數 ... */
	/* 不斷地試, 直到決定下一步該怎麼走為止 */
	do {
	    /* 用亂數隨機決定下一步的方向 */
	    dir = random() / (RAND_MAX / 4);
	    if (4 <= dir) dir = 3;
	    if (2 == (dir-last_dir) || 2 == (last_dir-dir)) {
		/* 不要走回頭路 */
		row_new = -1;	/* 試試看: 拿掉這一句會怎麼樣? */
		continue;
	    }
	    switch (dir) {	/* 按照 右-下-左-上 的順序 */
	    case 0:
		col_new = col+1; row_new = row; break;
	    case 1:
		col_new = col; row_new = row+1; break;
	    case 2:
		col_new = col-1; row_new = row; break;
	    case 3:
		col_new = col; row_new = row-1; break;
	    default:
		assert(0);	/* 不可能! */
	    }
	} while (0 >= col_new || col_new > MAX_COL ||
	    0 >= row_new || row_new > MAX_ROW);

	/* 終於找到一個不會撞到牆壁的方向了 */
	gotorc(row_new, col_new);	/* 先到新的位置去 ... */
	fflush(stdout);		/* 把緩衝區裡的東西清乾淨 */
	usleep(delay*1000);	/* 休息一下 */
	col = col_new; row = row_new;	/* 重新上路 */
	last_dir = dir;

	--step;			/* ... 倒著數就可以不必另外宣告註標變數了 */
    }
    gotorc(MAX_ROW, 1);		/* 把遊標移到左下角 */
    return 0;
}

/*
參考資料: 有關 ANSI escape sequence, 請參考:
	http://www.cs.utk.edu/~shuford/terminal/dec.html
*/
void clearscr(void)		/* 不需要參數, 沒有傳回值 */
/* 清楚螢幕 */
{
    printf("\x1b[2J");
}

void gotorc(int r, int c)	/* 沒有傳回值 */
/* 將遊標移到螢幕上第 r 列, 第 c 行. */
{
    printf("\x1b[%d;%dH", r, c);
}

