コンソールアナログ時計

久しぶりに Code Golf からの問題です。 Saving Time というお題ですが、そのまま解くにはもったいないので、コンソール時計を作ってみましょう。

偉大なる三賢者の作りし nawk でも動作するように冗長になっていますが、ゴルフでも何でもありませんが、以下のようなスクリプトになってしまいました。

このスクリプトにはいくつか注目するべき点があります。

  • 端末の幅と高さを認識するために stty からの出力を処理しています。
  • 時刻の取得には srand() 関数の結果から計算をさせています。
  • 計算で文字盤と針の位置を求めています。
  • 配列を画面に描画する plot() 関数を作成し、これを使って出力しています。
  • 無限ループでこれらを回しています。

ただし、この配列を使った plot() 関数は何度も for 文を通ってしまうため非常に遅いのですが、ニコニコ動画の職人のように改行なしで 1 回の for 文で長い文字列を出力させることで高速化できるようで、この手法を用いた awk スクリプトもあります。

#! /usr/bin/gawk -f
# clock.awk

BEGIN {

    for (;;) {
        # 端末の高さと幅を取得する
        get_terminal_cmd = "stty -a";
        while (get_terminal_cmd | getline > 0) {
            if ($4 == "rows") {
                rows = $5;
                sub(/;/, "", rows);
            }
            if ($6 == "columns") {
                cols = $7;
                sub(/;/, "", cols);
            }
        }
        close(get_terminal_cmd);
        cols--;
    
        # srand() の結果からエポック時間からの経過秒数を計算し、
        # 時刻を計算する
        split(ENVIRON["TZ"], arr_tz, /[-+]/);
        time_zone = arr_tz[2] * 3600;
        srand();
        system_time = srand() + time_zone;
        minute = int(system_time / 60) % 60;
        hour = (int(system_time / 3600) % 24) % 12;
    
        # 12 分割した場合の位置を計算
        min_idx = int(12 * minute / 60);
        hour_idx = hour == 0 ? 12 : hour;
    
        pi = 2 * atan2(1, 0);
    
        # 12 分割した位置を計算する
        for (i = 1; i <= 12; i++) {
            pos_x[i] = int(cols / 2 * sin(2 * pi / 12 * i) + cols / 2 + 1);
            pos_y[i] = int(rows / 4 * -cos(2 * pi / 12 * i) + rows / 2 + 1);
            arr[pos_x[i], pos_y[i]] = i;
    
            if (min_idx != hour_idx) {
    
                if (min_idx == i) {arr[pos_x[i], pos_y[i]] = "m"}
    
                if (hour_idx == i) {arr[pos_x[i], pos_y[i]] = "h"}
            } else {
                arr[pos_x[i], pos_y[i]] = "x";
            }
        }
    
        # その場所に表示する
        plot(arr, cols + 1, rows - 1);
    
        system("sleep 60");
    }

}

# 指定された位置で表示する
function plot(arr, x_size, y_size,    i, j, k, len, str) {

    for (j = 1; j <= y_size; j++) {

        for (i = 1; i <= x_size; i++) {

            if (arr[i, j] == "") {
                arr[i, j] = " ";
            } else {
                len = length(arr[i, j]);
                str = arr[i, j];

                for (k = 1; k <= len; k++) {
                    arr[i + k - 1, j] = substr(str, k, 1);
                }
            }
            line = sprintf("%s%s", line, arr[i, j]);
        }

        print line;
        line = "";
    }
}

実行結果は以下のとおりです。

                                        12
 
 
                    11                                      1
 
 
 
 
 
      10                                                                  2
 
 
 
 
 
 
 
 
 9                                                                              3
 
 
 
 
 
 
 
 
      8                                                                   4
 
 
 
 
 
                    7                                       m
 
 
                                        h

コンソールのお供にどうぞ。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png