どうも。アイマスを始めようと思って100万年経つけどソシャゲに手を出して続いたためしがないので永遠にインストールできず、周辺コンテンツや二次創作を啜りながら生きてきた結果中途半端な知識とバイアスだけが付いたオタクのクズです。
そしてこちらがゲーム『アイドルマスター シャイニーカラーズ』に登場するユニット "Straylight" 所属のアイドル、芹沢あさひちゃんです。シャニマスやったことないけど。
【アイドル紹介】
— アイドルマスター シャイニーカラーズ公式 (@imassc_official) March 19, 2019
「芹沢 あさひ」さんは、とっても探求心の強い中学2年生です!
興味をもったら一直線で、人懐っこい性格の可愛らしい女の子ですよ~。
あさひさんのボイスはコチラ!
⇒https://t.co/cdGsiefFXQ#シャニマス #idolmaster pic.twitter.com/nxF0Y4GMkQ
芹沢 あさひ
常に面白いことを探し、じっとしていることがない、探究心の強い女の子。興味を持ったら一直線だが、飽きっぽい一面も持つ中学2年生。(公式HPプロフィールより)
コードゴルフやるっす
コードゴルフとは、あるアルゴリズムを如何に短いソースコードで記述できるかを争う競技です。例えば有名な FizzBuzz1 を1から100の範囲でPython3を用いて実装する場合、標準的には
for i in range(1, 101):
if i % 15 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)
みたいな感じで書くかと思いますが、これを極限まで切り詰めることで
for i in range(100):print(i%3//2*"Fizz"+i%5//4*"Buzz"or-~i)
と59文字にまで削減できます。
『【不機嫌なテーマパーク】芹沢 あさひ』
ここから本題なんですが、2020年11月20日に実装された恒常カード『【不機嫌なテーマパーク】芹沢 あさひ』の思い出演出において2、こういう場面があるらしいんですね。
この上の部分、かなり判別しづいですがよく見ると以下のようなCコードになっています。
#include <stdio.h> int main(void){int i; for(i=0;i<22;++i) {printf
("Hello! %d/n", i);if(i==16) break;}return 0;}
なんでこんなに見にくい書き方してるんだろう。コードゴルフかな?
おそらくtypoであろう /n
を \n
に差し替え、ちゃんとインデントつけるとこんな感じ。
#include <stdio.h>
int main(void){
int i;
for(i = 0; i < 22; ++i){
printf("Hello! %d\n", i);
if(i==16)
break;
}
return 0;
}
Hello! 0
Hello! 1
Hello! 2
Hello! 3
Hello! 4
Hello! 5
Hello! 6
Hello! 7
Hello! 8
Hello! 9
Hello! 10
Hello! 11
Hello! 12
Hello! 13
Hello! 14
Hello! 15
Hello! 16
ということで、これを削っていきます。ただしバージョンはC17、処理系は gcc v9.4.0 とします。
論理部分
ひと目で分かる通り、for文の条件式の i < 22
とか if
で分岐させての break
とかは無駄です。
#include <stdio.h>
int main(void){
int i;
for(i = 0; i < 17; ++i)
printf("Hello! %d\n", i);
return 0;
}
ちなみにここは色々解釈があるらしく、22は283プロの人数だとか16はあさひが実装された順番だとかと絡んでいるそうですが知りません。シャニマスやったことないので。
#include <stdio.h>
#include <stdio.h>
がなくても printf
は使えます。なんでやねん。
int main(void){
int i;
for(i = 0; i < 17; ++i)
printf("Hello! %d\n", i);
return 0;
}
main
関数と return
実は int main(void)
は型を書かなくてもいいし、return 0
はなくても動きます。なんでやねん。
main(){
int i;
for(i = 0; i < 17; ++i)
printf("Hello! %d\n", i);
}
i
の宣言と更新
実は型を書かずに変数を宣言すると自動的に int
型になります。なんでやねん。更にグローバル変数は自動的に0で初期化されるので、for
文内での初期化も削減できます。
i;
main(){
for(; i < 17; ++i)
printf("Hello! %d\n", i);
}
それからインクリメントをprintf
で使うときに一緒にやれば、for
文内の更新も省略できますね。前置インクリメントと後置インクリメントの挙動の違いに注意しましょう。
i;
main(){
for(; i < 17;)
printf("Hello! %d\n", i++);
}
余談ですが、変数を初期化しておかなくてもよい場合(入力に使うなど)にはmain
の引数で宣言することでセミコロンを節約できます。
main(){i; /* hoge */} ----> main(i){ /* hoge */ }
あとは空白を切り詰めれば......
i;main(){for(;i<17;)printf("Hello! %d\n",i++);}
47文字にまで短くすることに成功しました!元のコードが113文字なので、半分以下にすることができました。
$ gcc -std=c17 asahi.c -o asahi && ./asahi
asahi.c:1:1: warning: data definition has no type or storage class
1 | i;main(){for(;i<17;)printf("Hello! %d\n",i++);}
| ^
asahi.c:1:1: warning: type defaults to ‘int’ in declaration of ‘i’ [-Wimplicit-int]
asahi.c:1:3: warning: return type defaults to ‘int’ [-Wimplicit-int]
1 | i;main(){for(;i<17;)printf("Hello! %d\n",i++);}
| ^~~~
asahi.c: In function ‘main’:
asahi.c:1:21: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
1 | i;main(){for(;i<17;)printf("Hello! %d\n",i++);}
| ^~~~~~
asahi.c:1:21: warning: incompatible implicit declaration of built-in function ‘printf’
asahi.c:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
+++ |+#include <stdio.h>
1 | i;main(){for(;i<17;)printf("Hello! %d\n",i++);}
Hello! 0
Hello! 1
Hello! 2
Hello! 3
Hello! 4
Hello! 5
Hello! 6
Hello! 7
Hello! 8
Hello! 9
Hello! 10
Hello! 11
Hello! 12
Hello! 13
Hello! 14
Hello! 15
Hello! 16
めっちゃ警告が出てきましたが、ちゃんとコンパイルも通って動きますね。
まとめ
多分これが一番短いと思います3。もしもっと短くできそうだったら教えてください。
こんな具合にコードを切り詰めていくのがコードゴルフです。今回紹介した以外にも言語仕様の穴を突いた面白い手法が色々あるので、気になった方は是非調べてみてください。
参考文献
- 【シャニマス】アイドルマスターシャイニーカラーズ1669週目【enza】 >>409 - 5ちゃんねる
- Straylight - アイドルマスターシャイニーカラーズ
- シャニマスでC言語をみつけたので実行してみた
- FizzBuzzを1byteで実装する
update 2022-02-06
シャニマスはじめました。運命の出会いガチャは当然あさひ(不機嫌なテーマパーク)を引きました。
あっこれかぁ!
記録更新
@fujitanozomu さんのご指摘により、記録が41文字に更新されました。長い間放置してしまいすみません。
main(){system("seq -f'Hello! %g' 0 16");}
-
1から順に整数を数え上げる。ただし3の倍数のときには "Fizz" と、5の倍数のときには "Buzz" と言い、両方の場合には "FizzBuzz" と言う。 ↩
-
とか言ってるが、「恒常」も「カード」も「思い出演出」も何を指しているのかイマイチ分かっていない。やってないので。 ↩
-
https://dic.nicovideo.jp/a/%E5%A4%9A%E5%88%86%E3%81%93%E3%82%8C%E3%81%8C%E4%B8%80%E7%95%AA%E6%97%A9%E3%81%84%E3%81%A8%E6%80%9D%E3%81%84%E3%81%BE%E3%81%99 ↩