【C言語】俺の機関車は遅すぎる ～slコマンドを作ってみた～

### 【概要】
ターミナルで機関車を動かすことができる`sl`コマンドを作ってみました。
ただし、オプションまでの実装はしていません。
また、俺の機関車は本家の機関車よりスピードが遅いです(作成後に本家と比較したら遅くて、でもこの方が面白いかなと・・・)。
タイトルはこちらの記事のオマージュです　→　[俺のlsが遅すぎる](https://qiita.com/dharry/items/537e917af233ac22a93e)

本家のコードは一切見ないで、本家の実行結果だけを繰り返し見て作成しました。
（本家のコードはネットにあるかどうか知りません）
[追記] [fujitanozomu](https://qiita.com/fujitanozomu)さんからコメント頂き、下記GitHubにありました。
　　https://github.com/mtoyoda/sl

### 【準備】
コードを実行するときは`curses.h`が必要になります。
インストール方法は下記サイトを参考にしました。
[Linux Error: curses.h: No such file or directory Problem Solution](https://www.cyberciti.biz/faq/linux-error-cursesh-no-such-file-directory/)

### 【環境】
```sh
[vagrant@vagrant-centos65 sl_command]$ cat /etc/centos-release 
CentOS release 6.5 (Final)
```

### 【コード】

コンパイルするときは`gcc -o sl sl.c -lcurses`

```C:sl.c
#include <stdio.h>
#include <curses.h>
#include <string.h>

void printSL();

int main(int argc, char *argv[])
{
	// 初期化
	initscr();
	// カーソルを表示しない
	curs_set(0);
	// キー入力された文字を表示しない
	noecho();
	// バッファを使用しない
	cbreak();
	// 機関車出力
	printSL();
	// 端末制御を終了
	endwin();
	return 0;
}

void printSL()
{
	int count = 1;
	int slScrInCol = 0, slScrOutCol = 0;
	int lineNum = 0;
	int colNum = 0;

	// 現在の画面のサイズ取得
	// curses.hで定義されているLINES, COLSだとターミナルのサイズを変更しても
	// 初期の画面サイズとなってしまうため現在の画面サイズを取得する
	// colNumは必要ないため、以後使用しない
	getmaxyx(stdscr, lineNum, colNum);
	// 機関車を画面中央に表示するため
	lineNum /= 4;
	char str[24][1000] = {'0'};


	int maxLen = strlen("  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/               \\_/   \\_/    \\_/   \\_/");
	// マジックナンバー6は微調整用
	while(COLS + maxLen + 6 - slScrInCol != 0) {
		strncpy(str[0], "                        (  ) (@@) ( )  (@)  ()    @@    O     @     O     @      O", slScrInCol);
		strncpy(str[1], "                   (@@@)", slScrInCol);
		strncpy(str[2], "               (    )", slScrInCol);
		strncpy(str[3], "            (@@@@)", slScrInCol);
		strncpy(str[4], "          (   )", slScrInCol);

		strncpy(str[5], "                        (@@) (  ) ( )  ()  (@)    @@    @     O     @     O      @", slScrInCol);
		strncpy(str[6], "                   (   )", slScrInCol);
		strncpy(str[7], "               (@@@@)", slScrInCol);
		strncpy(str[8], "            (    )", slScrInCol);
		strncpy(str[9], "          (@@@)", slScrInCol);

		// 煙突の煙の出力
		// 煙と車輪が動く速度は違うため、煙のは車輪に比べて遅く表示する
		if (count <= 4) {
			mvprintw(lineNum,   COLS-slScrInCol, str[0]);
			mvprintw(lineNum+1 ,COLS-slScrInCol, str[1]);
			mvprintw(lineNum+2 ,COLS-slScrInCol, str[2]);
			mvprintw(lineNum+3 ,COLS-slScrInCol, str[3]);
			mvprintw(lineNum+4 ,COLS-slScrInCol, "");
			mvprintw(lineNum+5 ,COLS-slScrInCol, str[4]);
		} else {
			mvprintw(lineNum,   COLS-slScrInCol, str[5]);
			mvprintw(lineNum+1 ,COLS-slScrInCol, str[6]);
			mvprintw(lineNum+2 ,COLS-slScrInCol, str[7]);
			mvprintw(lineNum+3 ,COLS-slScrInCol, str[8]);
			mvprintw(lineNum+4 ,COLS-slScrInCol, "");
			mvprintw(lineNum+5 ,COLS-slScrInCol, str[9]);
		}

		strncpy(str[10], "      ====        ________                ___________", slScrInCol);
		strncpy(str[11], "  _D _|  |_______/        \\__I_I_____===__|_________|", slScrInCol);
		strncpy(str[12], "   |(_)---  |   H\\________/ |   |        =|___ ___|      _________________", slScrInCol);
		strncpy(str[13], "   /     |  |   H  |  |     |   |         ||_| |_||     _|                \\_____A", slScrInCol);
		strncpy(str[14], "  |      |  |   H  |__--------------------| [___] |   =|                        |", slScrInCol);
		strncpy(str[15], "  | ________|___H__/__|_____/[][]~\\_______|       |   -|                        |", slScrInCol);
		strncpy(str[16], "  |/ |   |-----------i_____I [][] []  D   |=======|____|________________________|_", slScrInCol);

		// 車輪より上部の出力		
		mvprintw(lineNum+6 , COLS-slScrInCol, str[10]);
		mvprintw(lineNum+7 , COLS-slScrInCol, str[11]);
		mvprintw(lineNum+8 , COLS-slScrInCol, str[12]);
		mvprintw(lineNum+9 , COLS-slScrInCol, str[13]);
		mvprintw(lineNum+10, COLS-slScrInCol, str[14]);
		mvprintw(lineNum+11, COLS-slScrInCol, str[16]);
		mvprintw(lineNum+12, COLS-slScrInCol, str[10]);


		strncpy(str[17], "__/ =| o |=-O=====O=====O=====O\\  ____Y___________|__|__________________________|_", slScrInCol);
		strncpy(str[18], " |/-=|___|=    ||    ||    ||    |_____/\\___/          |_D__D__D_|  |_D__D__D_|", slScrInCol);
		strncpy(str[19], "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/               \\_/   \\_/    \\_/   \\_/", slScrInCol);
		strncpy(str[20], "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__|__________________________|_", slScrInCol);
		strncpy(str[21], " |/-=|___|=O=====O=====O=====O   |_____/\\___/          |_D__D__D_|  |_D__D__D_|", slScrInCol);
		strncpy(str[22], "  \\_/      \\_O=====O=====O=====O/      \\_/               \\_/   \\_/    \\_/   \\_/", slScrInCol);
		strncpy(str[23], " |/-=|___|=   O=====O=====O=====O|_____/\\___/          |_D__D__D_|  |_D__D__D_|", slScrInCol);


		// 車輪部分の出力
		if (count % 4 == 1) {
			mvprintw(lineNum+13, COLS-slScrInCol, str[17]);
			mvprintw(lineNum+14, COLS-slScrInCol, str[18]);
			mvprintw(lineNum+15, COLS-slScrInCol, str[19]);			
		} else if (count % 4 == 2) {
			mvprintw(lineNum+13, COLS-slScrInCol, str[20]);
			mvprintw(lineNum+14, COLS-slScrInCol, str[21]);
			mvprintw(lineNum+15, COLS-slScrInCol, str[19]);
		} else if (count % 4 == 3) {
			mvprintw(lineNum+13, COLS-slScrInCol, str[20]);
			mvprintw(lineNum+14, COLS-slScrInCol, str[18]);
			mvprintw(lineNum+15, COLS-slScrInCol, str[22]);
		} else {
			mvprintw(lineNum+13, COLS-slScrInCol, str[20]);
			mvprintw(lineNum+14, COLS-slScrInCol, str[23]);
			mvprintw(lineNum+15, COLS-slScrInCol, str[19]);
		}

		// countの数を大きくしないようにするため
		if (count / 8 == 1) {
			count = 1;
		} else {
			count++;
		}

		// 機関車の先頭が画面左から出ていくときの画面出力
		if (COLS - slScrInCol <= 0) {
			if (count <= 4) {
				mvprintw(lineNum,   0, &str[0][slScrOutCol]);
				mvprintw(lineNum+1 ,0, &str[1][slScrOutCol]);
				mvprintw(lineNum+2 ,0, &str[2][slScrOutCol]);
				mvprintw(lineNum+3 ,0, &str[3][slScrOutCol]);
				mvprintw(lineNum+4 ,0, "");
				mvprintw(lineNum+5 ,0, &str[4][slScrOutCol]);
			} else {
				mvprintw(lineNum,   0, &str[5][slScrOutCol]);
				mvprintw(lineNum+1 ,0, &str[6][slScrOutCol]);
				mvprintw(lineNum+2 ,0, &str[7][slScrOutCol]);
				mvprintw(lineNum+3 ,0, &str[8][slScrOutCol]);
				mvprintw(lineNum+4 ,0, "");
				mvprintw(lineNum+5 ,0, &str[9][slScrOutCol]);
			}

			mvprintw(lineNum+6 , 0, &str[10][slScrOutCol]);
			mvprintw(lineNum+7 , 0, &str[11][slScrOutCol]);
			mvprintw(lineNum+8 , 0, &str[12][slScrOutCol]);
			mvprintw(lineNum+9 , 0, &str[13][slScrOutCol]);
			mvprintw(lineNum+10, 0, &str[14][slScrOutCol]);
			mvprintw(lineNum+11, 0, &str[16][slScrOutCol]);
			mvprintw(lineNum+12, 0, &str[10][slScrOutCol]);

			if (count % 4 == 1) {
				mvprintw(lineNum+13, 0, &str[17][slScrOutCol]);
				mvprintw(lineNum+14, 0, &str[18][slScrOutCol]);
				mvprintw(lineNum+15, 0, &str[19][slScrOutCol]);			
			} else if (count % 4 == 2) {
				mvprintw(lineNum+13, 0, &str[20][slScrOutCol]);
				mvprintw(lineNum+14, 0, &str[21][slScrOutCol]);
				mvprintw(lineNum+15, 0, &str[19][slScrOutCol]);
			} else if (count % 4 == 3) {
				mvprintw(lineNum+13, 0, &str[20][slScrOutCol]);
				mvprintw(lineNum+14, 0, &str[18][slScrOutCol]);
				mvprintw(lineNum+15, 0, &str[22][slScrOutCol]);
			} else {
				mvprintw(lineNum+13, 0, &str[20][slScrOutCol]);
				mvprintw(lineNum+14, 0, &str[23][slScrOutCol]);
				mvprintw(lineNum+15, 0, &str[19][slScrOutCol]);
			}
			slScrOutCol++;
		}

		slScrInCol++;
		// 60ミリ秒待つ
		usleep(6 * 10000);
		refresh();
		clear();
	}
}
```

### 【実行結果】
#### 【俺の機関車】
![my_sl.gif](https://qiita-image-store.s3.amazonaws.com/0/121997/c443c9d2-37d9-3e1c-744c-e2723c4f8f1f.gif)


#### 【本家の機関車】
![org_sl.gif](https://qiita-image-store.s3.amazonaws.com/0/121997/26dffb48-e3ff-f34e-9d61-a439c164dcc7.gif)


### 【参考サイト】
[C言語でシンプルすぎるブロック崩しを書いた](https://qiita.com/pokohide/items/a246045f3ccaf540a375)
[curses よく利用する関数についての超てきとー解説](https://www.kushiro-ct.ac.jp/yanagawa/ex-2015/2-game/doc/curses.html)