2013年公開の映画『言の葉の庭』です。
深海監督の作品は、桜の花びらの落ちるスピードを『秒速5センチメートル』ってタイトルにした作品から好きで繰り返し見ています。今年は『君の名は』も公開されるので、ぜひ映画館で見てください。
雨って元々詩的ですよね。夕立は出会いの予感、激情の時は土砂降りになるし、雨が上がると虹も出て希望も表現できる。『言の葉の庭』は、雨と心が上手く表現されていてうっとりします。感情移入し過ぎて主人公みたいに、一から靴作りDIYしたくなったほどです。秦基博が歌う『Rain』と彼の声もマッチしていて、こちらにもハマります。『Rain』は大江千里が80年代に作った曲なのですが、その歌詞や曲の雰囲気は20年以上の時を経て、この映画のために作曲されたんじゃないかと思わせるほどです。
rainコマンドは、端末ウィンドウに雨を降らせるコマンドです。bsdgamesパッケージに含まれています。
~$ sudo apt install bsdgames ~$ rain
~$ man rain
名称
rain — 雨の降る様を表示する
書式
rain [-d delay]
解説
rain による表示は、 VAX/VMS での同名のプログラムに倣って作成されたもので
す。 適切な効果を得るためには、端末を 9600 ボーに設定しておくか、 -d オプ
ションを使用して更新間隔をミリ秒単位で指定する必要があります。 適切な間隔
は 120 ですが、デフォルト値は 0 です。
作者
Eric P. Scott
manの説明にあるように、-dオプションで表示をゆっくりにして初めて雨のアニメーションが分かります。空を見上げたときの、雨だれが落ちてくる様子にも見えますね。
よく見ると、この雨だれの数は、画面内に5個しか表示されてません。そしてすべて異なる表示となっています。どのようなプログラムになっているのでしょうか。

ソースを入手して見ていきましょう。/usr/src以下にダウンロードします。
~$ su - ~# cd /usr/src /usr/src# apt-get source bsdgames /usr/src# ln -s bsdgames-2.17/ bsdgames /usr/src# exit ~$
rainコマンドなので、たいていrain.cです。findで探します。
~$ find /usr/src/bsdgames -name rain.c ~$
あれ、おかしい。表示されません。rain.cではないんでしょうか。
実は、/usr/src/badgamesは、先ほど作ったシンボリックリンクですので、findのデフォルト実行ではシンボリックリンクは辿れないのです。オプション-Lが必要になります。
では、-Lをつけてもう一度。
~$ find -L /usr/src/bsdgames -name rain.c /usr/src/bsdgames/.pc/rain-Update-default-delay.(右省略) /usr/src/bsdgames/rain/rain.c ~$
ありました。rain/rain.cです。行数は、
~$ !! |xargs wc -l find -L /usr/src/bsdgames -name rain.c |xargs wc -l 158 /usr/src/bsdgames/.pc/rain-Update-default-(右省略) 158 /usr/src/bsdgames/rain/rain.c 316 合計 ~$
158行ぐらいなら、全文表示してみましょう。
~$ cat -n /usr/src/bsdgames/rain/rain.c
1 /* $NetBSD: rain.c,v 1.17 2004/05/02 21:31:23 christos Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)rain.c 8.1 (Berkeley) 5/31/93";
41 #else
42 __RCSID("$NetBSD: rain.c,v 1.17 2004/05/02 21:31:23 christos Exp $");
43 #endif
44 #endif /* not lint */
45
46 /*
47 * rain 11/3/1980 EPS/CITHEP
48 * cc rain.c -o rain -O -ltermlib
49 */
50
51 #include <sys/types.h>
52 #include <curses.h>
53 #include <err.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <termios.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <limits.h>
61
62 static volatile sig_atomic_t sig_caught = 0;
63
64 int main(int, char **);
65 static void onsig(int);
66
67
68 int
69 main(int argc, char **argv)
70 {
71 int x, y, j;
72 long cols, lines;
73 unsigned int delay = 120;
74 unsigned long val = 0;
75 int ch;
76 char *ep;
77 int xpos[5], ypos[5];
78
79 /* Revoke setgid privileges */
80 setregid(getgid(), getgid());
81
82 while ((ch = getopt(argc, argv, "d:")) != -1)
83 switch (ch) {
84 case 'd':
85 val = strtoul(optarg, &ep, 0);
86 if (ep == optarg || *ep)
87 errx(1, "Invalid delay `%s'", optarg);
88 if (errno == ERANGE && val == ULONG_MAX)
89 err(1, "Invalid delay `%s'", optarg);
90 if (val >= 1000)
91 errx(1, "Invalid delay `%s' (1-999)", optarg);
92 delay = (unsigned int)val * 1000; /* ms -> us */
93 break;
94 default:
95 (void)fprintf(stderr, "Usage: %s [-d delay]\n",
96 getprogname());
97 return 1;
98 }
99
100 initscr();
101 cols = COLS - 4;
102 lines = LINES - 4;
103
104 (void)signal(SIGHUP, onsig);
105 (void)signal(SIGINT, onsig);
106 (void)signal(SIGTERM, onsig);
107
108 curs_set(0);
109 for (j = 4; j >= 0; --j) {
110 xpos[j] = random() % cols + 2;
111 ypos[j] = random() % lines + 2;
112 }
113 for (j = 0;;) {
114 if (sig_caught) {
115 endwin();
116 exit(0);
117 }
118 x = random() % cols + 2;
119 y = random() % lines + 2;
120 mvaddch(y, x, '.');
121 mvaddch(ypos[j], xpos[j], 'o');
122 if (!j--)
123 j = 4;
124 mvaddch(ypos[j], xpos[j], 'O');
125 if (!j--)
126 j = 4;
127 mvaddch(ypos[j] - 1, xpos[j], '-');
128 mvaddstr(ypos[j], xpos[j] - 1, "|.|");
129 mvaddch(ypos[j] + 1, xpos[j], '-');
130 if (!j--)
131 j = 4;
132 mvaddch(ypos[j] - 2, xpos[j], '-');
133 mvaddstr(ypos[j] - 1, xpos[j] - 1, "/ \\");
134 mvaddstr(ypos[j], xpos[j] - 2, "| O |");
135 mvaddstr(ypos[j] + 1, xpos[j] - 1, "\\ /");
136 mvaddch(ypos[j] + 2, xpos[j], '-');
137 if (!j--)
138 j = 4;
139 mvaddch(ypos[j] - 2, xpos[j], ' ');
140 mvaddstr(ypos[j] - 1, xpos[j] - 1, " ");
141 mvaddstr(ypos[j], xpos[j] - 2, " ");
142 mvaddstr(ypos[j] + 1, xpos[j] - 1, " ");
143 mvaddch(ypos[j] + 2, xpos[j], ' ');
144 xpos[j] = x;
145 ypos[j] = y;
146 refresh();
147 if (delay)
148 usleep(delay);
149 else
150 tcdrain(STDOUT_FILENO);
151 }
152 }
153
154 static void
155 onsig(int dummy __attribute__((__unused__)))
156 {
157 sig_caught = 1;
158 }
着目する箇所としては、まずは77行目のxpos、yposの配列の数が、5個であるということで、雨だれの画面上の数を表しています。
次に、121行目から雨だれのキャラクタの表示となっていますが、xposやyposでgrepすると分かりやすいです。
77: int xpos[5], ypos[5]; 110: xpos[j] = random() % cols + 2; 121: mvaddch(ypos[j], xpos[j], 'o'); 124: mvaddch(ypos[j], xpos[j], 'O'); 127: mvaddch(ypos[j] - 1, xpos[j], '-'); 128: mvaddstr(ypos[j], xpos[j] - 1, "|.|"); 129: mvaddch(ypos[j] + 1, xpos[j], '-'); 132: mvaddch(ypos[j] - 2, xpos[j], '-'); 133: mvaddstr(ypos[j] - 1, xpos[j] - 1, "/ \\"); 134: mvaddstr(ypos[j], xpos[j] - 2, "| O |"); 135: mvaddstr(ypos[j] + 1, xpos[j] - 1, "\\ /"); 136: mvaddch(ypos[j] + 2, xpos[j], '-'); 139: mvaddch(ypos[j] - 2, xpos[j], ' '); 140: mvaddstr(ypos[j] - 1, xpos[j] - 1, " "); 141: mvaddstr(ypos[j], xpos[j] - 2, " "); 142: mvaddstr(ypos[j] + 1, xpos[j] - 1, " "); 143: mvaddch(ypos[j] + 2, xpos[j], ' '); 144: xpos[j] = x; ~$
o ↓ O ↓ - |.| - ↓ - / \ | O | \ / - ↓
あれ、何か変です。雨だれのタイプが4個しかありません。画面上の5箇所に表示されていたので、もう一つあるはずです。
配列を使っていないこの行を見落としていました。
120 mvaddch(y, x, ‘.’);
さて、原理は分かったので、コマンドラインで挑戦してみましょう。
雨だれの絵は、テキストファイルに以下のように納めておきます。
~$ cat -n raindrop.txt
1
2
3 .
4
5
6
7
8 o
9
10
11
12
13 O
14
15
16
17 -
18 |.|
19 -
20
21 -
22 / \
23 | O |
24 \ /
25 -
~$
raindrop.txtを使ってそれぞれの雨だれを表示するのは、以下のようにsedを使います。
~$ for i in `seq 1 5 25`;do ((j=$i+4));sed -n "${i},${j}p" raindrop.txt;done
それぞれの雨だれにおいて、改行が入ると、コンソールの一番右にカーソルが移動してしまいます。perlにて改行を1行下、5桁左にカーソル移動するエスケープシーケンスに置換しています。
~$ while :;do clear;for i in `seq 1 5 25`;do perl -e 'printf "\x1b[%d;%dH",int rand 20, int rand 80';((j=$i+4));sed -n "${i},${j}p" raindrop.txt | perl -pe "s/\n/\x1b[1B\x1b[5D/" ;done;sleep 1;done
上記コマンドの実行動画ですが、砂利が混ざったような雨になりました。


コメント