ボカロオタクが聖夜に「Calc.」のプロンプト画面を再現して大満足するだけの話

:::note info
この記事は [SLP-KBIT AdventCalendar2023](https://adventar.org/calendars/9222) 24日目の記事です。
:::
https://adventar.org/calendars/9222
# はじめに
![merry_christmas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/4121af7a-7069-299b-0cf2-da581226cb9e.png)

![merry_christmas_santa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/b4334ff1-2f47-4301-6db4-f34547898839.png)

![merry_christmas_cake.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/8180cad1-4705-6dd0-ff6e-88f7c796683f.jpeg)

こんばんわ。tttです。

**メリークリスマス！！！！**

今夜はクリスマスイブですね。  
みなさん、いかがお過ごしでしょうか。

大切な家族や友人と過ごしたり、はたまた恋人と共に過ごす方もいらっしゃるでしょう。

中には「一人ぼっちのクリスマスイブでも問題ないし」と考えてる方もいると思います。

しかし、それは良くありません！

クリスマスイブは、「家族や友人、恋人との団らんの時間を大切にする時間である」（ネット検索）とされているように、決して一人で過ごす時間ではないのです！！

クリぼっちで問題ない！と考えている人も、心の底では寂しい思いを少なからずしているはずです。

さあ、今すぐ大切な人に会いに行きましょう！！

みなさんのクリスマスイブが、とても良い思い出になることを願っています。

<br>
<br>
<br>
<br>
<br>
<br>
え、僕？<br>
ハハっ

# 序章
僕の大好きなボカロ曲です（唐突）。

<iframe width="560" height="315" src="https://www.youtube.com/embed/oidKy7khp8o?si=Sp8DC6hXrCeUomJ8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

**何度聴いても素晴らしい！！**  

ココスキポイント  
ベースが好き、コード進行が好き、シンプルな音楽構成が好き、歌詞が好き、間奏のギター好き、こんだけ爽やかな音楽のくせに「失恋ソング」なのが好き、こんだけ爽やかな音楽なのにMVが真っ黒な画面に緑コンソール文字だけっていうギャップが好き、タイトルが好き、以前の曲とつながってるのが好き、etc

考察も語りたいところですが、それだけで一つの記事になりそうなので割愛。  
一応、他人の考察の中で納得したものもあるので、興味あればどぞ。

https://utaten.com/specialArticle/index/7770

# ほんへ
で。  
情報学生の方なら以下の画像を見て思うことはないだろうか。

![calc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/64119a50-463a-8622-898a-9aa7ff980544.jpeg)

そうです。

「**この画面をターミナル上で再現したい！！！！**」

ですよね。

やりましょう。

~~文字列の解読？~~  
~~したけりゃ勝手にドウゾ~~  
~~ちなみに平文に直すとミクがうｐ主を罵倒するメッセージが現れます~~  
~~CTF経験者は自力で解けるかな？~~
# Step１．まずは普通に出力
どう出力しようかなぁと考えたとき、Cプログラムとして実行するのが面白そうだなと思ったので、プログラミングします。

とは言っても、文字を出力するだけのプログラムですけどね。

大きい文字の「Calc.」はもちろん工夫します。
```c
#include <stdio.h>

int main(void)
{
    putchar('\n');
    puts("Initializing...");
    putchar('\n');
    puts("837E834E81755982B382F182CC82B182C682CD");
    puts("82E082A4965982EA82BD82D982A482AA");
    puts("82A282A282C68E7682A282DC82B78176...");
    putchar('\n');
    puts("838B834A8175918A95CF82ED82E782B8");
    puts("96A297FB82AA82DC82B582A282C88176...");
    putchar('\n');
    puts("8357837E817592FA82DF82AA88AB82A282E68176...");
    putchar('\n');
    putchar('\n');
    puts(" ■■■       ■");
    puts("■   ■  ■■  ■  ■■■");
    puts("■        ■ ■ ■");
    puts("■      ■■■ ■ ■");
    puts("■   ■ ■  ■ ■ ■");
    puts(" ■■■   ■■■ ■  ■■■ ■");
    putchar('\n');
    putchar('\n');
    puts("8357837E815B817581638163");
    puts("28814C8145");
    puts("81CD8145814D29");
    puts("816381638176...");
    putchar('\n');

    return 0;
}
```
実行結果
```

Initializing...

837E834E81755982B382F182CC82B182C682CD
82E082A4965982EA82BD82D982A482AA
82A282A282C68E7682A282DC82B78176...

838B834A8175918A95CF82ED82E782B8
96A297FB82AA82DC82B582A282C88176...

8357837E817592FA82DF82AA88AB82A282E68176...


 ■■■       ■
■   ■  ■■  ■  ■■■
■        ■ ■ ■
■      ■■■ ■ ■
■   ■ ■  ■ ■ ■
 ■■■   ■■■ ■  ■■■ ■


8357837E815B817581638163
28814C8145
81CD8145814D29
816381638176...

```
まあ普通ですね。
# Step２．色をつける
MVの文字は、緑色なのでそれに合わせます。

C言語には**特殊文字**と呼ばれるものが存在します。

ピンと来ない方もいると思いますが、簡単にいうと`\n`や`\0`などです。

で、文字を緑色にする特殊文字は以下の通りです。
```
\x1b[32m
```
長いので説明は省きます。  
ファ！？なんだこれ！！と思う方はこちらをどうぞ。

https://daeudaeu.com/c-printf-color/

コード
```
#include <stdio.h>

int main(void)
{
    printf("\x1b[32m");
    putchar('\n');
    puts("Initializing...");
    putchar('\n');
    puts("837E834E81755982B382F182CC82B182C682CD");
    puts("82E082A4965982EA82BD82D982A482AA");
    puts("82A282A282C68E7682A282DC82B78176...");
    putchar('\n');
    puts("838B834A8175918A95CF82ED82E782B8");
    puts("96A297FB82AA82DC82B582A282C88176...");
    putchar('\n');
    puts("8357837E817592FA82DF82AA88AB82A282E68176...");
    putchar('\n');
    putchar('\n');
    puts(" ■■■       ■");
    puts("■   ■  ■■  ■  ■■■");
    puts("■        ■ ■ ■");
    puts("■      ■■■ ■ ■");
    puts("■   ■ ■  ■ ■ ■");
    puts(" ■■■   ■■■ ■  ■■■ ■");
    putchar('\n');
    putchar('\n');
    puts("8357837E815B817581638163");
    puts("28814C8145");
    puts("81CD8145814D29");
    puts("816381638176...");
    putchar('\n');
    printf("\x1b[0m");

    return 0;
}
```
実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/51787574-c44e-4f66-bd09-5778fbbcb64d.png)


なんかこれじゃない感。

というのも、大文字の「Calc.」が本家と少し形が異なるんですよねぇ...

ということで工夫します。
# Step３．「Calc.」の表記方法を一新
ここ賢いポイント。

「■」という文字を使って「Calc.」を表現しようとしていますが、これが半角判定になり、うまくできません。

ここで、C言語の文字を緑色にする特殊文字に注目します。

「文字を緑色にする」ということは、つまり「空白**文字**も緑色にする」ということです。

なので、**空白文字に緑色が当たるよう調整**すれば、いい感じになるはずです。

コード
```c
#include <stdio.h>
int main(void)
{
    printf("\x1b[32m");
    putchar('\n');
    puts("Initializing...");
    putchar('\n');
    puts("837E834E81755982B382F182CC82B182C682CD");
    puts("82E082A4965982EA82BD82D982A482AA");
    puts("82A282A282C68E7682A282DC82B78176...");
    putchar('\n');
    puts("838B834A8175918A95CF82ED82E782B8");
    puts("96A297FB82AA82DC82B582A282C88176...");
    putchar('\n');
    puts("8357837E817592FA82DF82AA88AB82A282E68176...");
    putchar('\n');
    putchar('\n');
    puts("  \x1b[42m      \x1b[49m              \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m    \x1b[42m    \x1b[49m    \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m");
    puts("\x1b[42m  \x1b[49m                \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m            \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("  \x1b[42m      \x1b[49m      \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m");
    putchar('\n');
    putchar('\n');
    puts("8357837E815B817581638163");
    puts("28814C8145");
    puts("81CD8145814D29");
    puts("816381638176...");
    putchar('\n');
    printf("\x1b[0m");
    return 0;
}
```
実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/5a6ee6d4-71cb-1ab9-c98f-914b4511e0ad.png)


:)
# Step４．アニメーションをつける
当初の目的は達成しましたが、あまり面白みがないので、この画面に動きを付けます。

MVに登場するClacプログラムが実際にどのように動くのか、想像しながら作成していきます。

文字が流れるように表示されていく様子を想像したので、それをプログラムで表現することにしました。

それを実現するために
- usleep()関数
- fflush()関数

を使用します。

また、任意の時間によって文字を出力したいので、それを実現する自作関数も用意します。

参考サイト：

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1240519687

https://hiroyukichishiro.com/sleep-function-in-c-language/

ついでに実行時に画面が更新されるようにもします。

コード
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void slowMsg(char* msg, int wait_time)
{
    int i = 0;
    while(msg[i] != '\0')
    {
        printf("%c", msg[i]);
        usleep(wait_time);
        fflush(stdout);
        i++;
    }
}

int main(void)
{
    system("clear");
    printf("\x1b[32m");

    putchar('\n');
    slowMsg("Initializing", 100000);
    slowMsg("... ", 1000000);
    putchar('\n');
    putchar('\n');
    slowMsg("837E834E81755982B382F182CC82B182C682CD\n", 30000);
    slowMsg("82E082A4965982EA82BD82D982A482AA\n", 30000);
    slowMsg("82A282A282C68E7682A282DC82B78176... ", 30000);
    putchar('\n');
    putchar('\n');
    slowMsg("838B834A8175918A95CF82ED82E782B8\n", 30000);
    slowMsg("96A297FB82AA82DC82B582A282C88176... ", 30000);
    putchar('\n');
    putchar('\n');
    slowMsg("8357837E817592FA82DF82AA88AB82A282E68176", 30000);
    slowMsg("... ", 800000);
    putchar('\n');
    putchar('\n');
    putchar('\n');
    puts("  \x1b[42m      \x1b[49m              \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m    \x1b[42m    \x1b[49m    \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m");
    puts("\x1b[42m  \x1b[49m                \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m            \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("  \x1b[42m      \x1b[49m      \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m");
    putchar('\n');
    putchar('\n');
    usleep(1000000);
    slowMsg("8357837E815B817581638163\n", 30000);
    slowMsg("28814C8145\n", 30000);
    slowMsg("81CD8145814D29\n", 30000);
    slowMsg("816381638176... ", 30000);
    putchar('\n');
    putchar('\n');
    printf("\x1b[0m");

    return 0;
}
```
実行結果

~~[calc_step5.gifhttps://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384147/852b8c34-2460-0917-2c2c-d68de4bb9674.gif~~

動画をgifに直すと劣化が凄かったのでカット
:(
# Step５．音楽と同時に実行する
仕上げです。

音楽も流して盛大にしましょう。

コード
```c
﻿#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void slowMsg(char* msg, int wait_time)
{
    int i = 0;
    while(msg[i] != '\0')
    {
        printf("%c", msg[i]);
        usleep(wait_time);
        fflush(stdout);
        i++;
    }
}

void* startMusic(void *arg)
{
    system("mpg123 -q ./music/Calc.mp3");
    return NULL;
}

int main(void)
{
    system("clear");
    printf("\x1b[32m");

    putchar('\n');
    slowMsg("Initializing", 100000);
    slowMsg("...", 800000);
    pthread_t music_thread;
    pthread_create(&music_thread, NULL, startMusic, NULL);
    slowMsg(" ", 500000);
    putchar('\n');
    putchar('\n');

    slowMsg("837E834E81755982B382F182CC82B182C682CD\n", 40000);
    slowMsg("82E082A4965982EA82BD82D982A482AA\n", 40000);
    slowMsg("82A282A282C68E7682A282DC82B78176... ", 40000);
    putchar('\n');
    putchar('\n');
    slowMsg("838B834A8175918A95CF82ED82E782B8\n", 40000);
    slowMsg("96A297FB82AA82DC82B582A282C88176... ", 40000);
    putchar('\n');
    putchar('\n');
    slowMsg("8357837E817592FA82DF82AA88AB82A282E68176", 40000);
    slowMsg("... ", 500000);
    usleep(408000);
    putchar('\n');
    putchar('\n');
    putchar('\n');

    puts("  \x1b[42m      \x1b[49m              \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m    \x1b[42m    \x1b[49m    \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m");
    puts("\x1b[42m  \x1b[49m                \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m            \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("\x1b[42m  \x1b[49m      \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m  \x1b[42m  \x1b[49m");
    puts("  \x1b[42m      \x1b[49m      \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m    \x1b[42m      \x1b[49m  \x1b[42m  \x1b[49m");
    putchar('\n');
    putchar('\n');
    usleep(5000000);
    slowMsg("8357837E815B817581638163\n", 30000);
    slowMsg("28814C8145\n", 30000);
    slowMsg("81CD8145814D29\n", 30000);
    slowMsg("816381638176... ", 30000);
    usleep(3000000);
    putchar('\n');
    putchar('\n');
    putchar('\n');
    slowMsg("すれ違いは結局運命で", 30000);
    usleep(1900000);
    slowMsg("  全ては筋書き通りだって", 30000);
    usleep(1900000);
    putchar('\n');
    slowMsg("悲しみを紛らわせるほど", 30000);
    usleep(1905000);
    slowMsg("  僕は強くないから", 30000);
    usleep(1900000);
    putchar('\n');
    putchar('\n');
    slowMsg("弾き出した答えの全てが", 30000);
    usleep(1850000);
    slowMsg("  一つ二つ犠牲を伴って", 30000);
    usleep(1840000);
    putchar('\n');
    slowMsg("また一歩踏み出す勇気を", 30000);
    usleep(1800000);
    slowMsg("  奪い取ってゆく", 30000);
    usleep(1500000);
    putchar('\n');
    putchar('\n');
    putchar('\n');
    slowMsg("いつか", 95000);
    usleep(470000);
    slowMsg("君に捧げた歌", 30000);
    slowMsg("  ", 1830000);
    slowMsg("今じゃ哀しいだけの愛の歌", 30000);
    usleep(5000000);
    putchar('\n');
    slowMsg("風に吹かれ飛んでゆけ", 30000);
    usleep(4000000);
    slowMsg("  僕らが出会えたあの夏の日まで", 30000);

    usleep(12000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');

    slowMsg("巡り会いも結局運命で", 30000);
    usleep(1900000);
    slowMsg("  全ては筋書き通りだって", 30000);
    usleep(1900000);
    putchar('\n');
    slowMsg("都合良く考えられたら", 30000);
    usleep(1905000);
    slowMsg("  寂しくはないのかな", 30000);
    usleep(1900000);
    putchar('\n');
    putchar('\n');
    slowMsg("弾き出した答えの全てが", 30000);
    usleep(1850000);
    slowMsg("  一つ二つ矛盾を伴って", 30000);
    usleep(1840000);
    putchar('\n');
    slowMsg("向こう側へと続く道を", 30000);
    usleep(1800000);
    slowMsg("  消し去ってゆく", 30000);
    usleep(1400000);
    putchar('\n');
    putchar('\n');
    putchar('\n');
    slowMsg("いつか", 95000);
    usleep(470000);
    slowMsg("君に捧げた歌", 30000);
    slowMsg("  ", 1830000);
    slowMsg("今じゃ哀しいだけの愛の歌", 30000);
    usleep(5000000);
    putchar('\n');
    slowMsg("風に吹かれ飛んでゆけ", 30000);
    usleep(4000000);
    slowMsg("  僕らが出会えたあの夏の日まで", 30000);

    usleep(12000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');

    slowMsg("過去も未来も無くなれば", 30000);
    slowMsg("  ", 1900000);
    slowMsg("僕も自由に飛び立てるかな", 30000);
    usleep(5000000);
    putchar('\n');
    slowMsg("感情一つ消せるのなら", 30000);
    usleep(4000000);
    slowMsg("  「好き」を消せば楽になれるかな", 30000);
    usleep(4900000);
    putchar('\n');
    putchar('\n');

    slowMsg("君の耳を、目を、心を", 30000);
    slowMsg("  ", 2000000);
    slowMsg("通り抜けたモノ全てを", 30000);
    usleep(5000000);
    putchar('\n');
    slowMsg("いつか知ることが出来たら", 30000);
    usleep(4500000);
    slowMsg("  次はちゃんと君を愛せるかな", 30000);

    usleep(12000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');
    usleep(3000000);
    putchar('\n');

    puts("Music : OneRoom");
    puts("Created by ttt");
    puts("2023/12/24");

    putchar('\n');
    putchar('\n');

    printf("\x1b[0m");
    pthread_join(music_thread, NULL);
    return 0;
}
```
実行結果

<iframe width="560" height="315" src="https://www.youtube.com/embed/C8c9_ptEzi8?si=iHuv39xBimiMuOl5" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# おわりに
余は大変満足である。

あとカラオケ行きたくなった。

では良いお年を～～～ノシ