2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

芹沢あさひと学ぶコードゴルフ

Last updated at Posted at 2023-01-05

どうも。アイマスを始めようと思って100万年経つけどソシャゲに手を出して続いたためしがないので永遠にインストールできず、周辺コンテンツや二次創作を啜りながら生きてきた結果中途半端な知識とバイアスだけが付いたオタクのクズです。

そしてこちらがゲーム『アイドルマスター シャイニーカラーズ』に登場するユニット "Straylight" 所属のアイドル、芹沢あさひちゃんです。シャニマスやったことないけど。

芹沢 あさひ
常に面白いことを探し、じっとしていることがない、探究心の強い女の子。興味を持ったら一直線だが、飽きっぽい一面も持つ中学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、こういう場面があるらしいんですね。

asahi_code.jpeg

この上の部分、かなり判別しづいですがよく見ると以下のような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 に差し替え、ちゃんとインデントつけるとこんな感じ。

asahi.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;
}
出力
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 とかは無駄です。

asahi.c
#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 は使えます。なんでやねん。

asahi.c
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はなくても動きます。なんでやねん。

asahi.c
main(){
    int i;
    for(i = 0; i < 17; ++i)
        printf("Hello! %d\n", i);
}

i の宣言と更新

実は型を書かずに変数を宣言すると自動的に int 型になります。なんでやねん。更にグローバル変数は自動的に0で初期化されるので、for 文内での初期化も削減できます。

asahi.c
i;
main(){
    for(; i < 17; ++i)
        printf("Hello! %d\n", i);
}

それからインクリメントをprintfで使うときに一緒にやれば、for文内の更新も省略できますね。前置インクリメントと後置インクリメントの挙動の違いに注意しましょう。

asahi.c
i;
main(){
    for(; i < 17;)
        printf("Hello! %d\n", i++);
}

余談ですが、変数を初期化しておかなくてもよい場合(入力に使うなど)にはmainの引数で宣言することでセミコロンを節約できます。

main(){i; /* hoge */} ----> main(i){ /* hoge */ }

あとは空白を切り詰めれば......

asahi.c
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。もしもっと短くできそうだったら教えてください。

こんな具合にコードを切り詰めていくのがコードゴルフです。今回紹介した以外にも言語仕様の穴を突いた面白い手法が色々あるので、気になった方は是非調べてみてください。

参考文献

update 2022-02-06

シャニマスはじめました。運命の出会いガチャは当然あさひ(不機嫌なテーマパーク)を引きました。
あっこれかぁ!

記録更新

@fujitanozomu さんのご指摘により、記録が41文字に更新されました。長い間放置してしまいすみません。

main(){system("seq -f'Hello! %g' 0 16");}
  1. 1から順に整数を数え上げる。ただし3の倍数のときには "Fizz" と、5の倍数のときには "Buzz" と言い、両方の場合には "FizzBuzz" と言う。

  2. とか言ってるが、「恒常」も「カード」も「思い出演出」も何を指しているのかイマイチ分かっていない。やってないので。

  3. 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

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?