TSG LIVE! 3 ライブコードゴルフ大会 writeup

五月祭2019でTSGが催した企画、『TSG LIVE! 3 ライブコードゴルフ大会』に参加した感想です。

1日目

youtu.be

2日目

youtu.be

1日目 戦略とか反省とか

自分がプレイヤーとして参加したのは1日目です。TSG は過去の TSG LIVE! でもコードゴルフ、CTF、競プロ、ゲーム開発など色々なライブプログラミングをしてきましたが、駒場チームと本郷チームに分かれて戦うのは今回が初です。

結果はこんな感じでした。

esolang.hakatashi.com

f:id:n4o847:20190519140046p:plain

競技が始まって、自分が RubyPerl、ペアである iLiss さんが C と Python を担当するということになりました。RubyPerl ははじめに正規表現による解を思いついて、提出した後それほど縮む気がしなかったので上と右の esolang に挑むことにしました。esolang といえば Brainf*ck みたいなところがあるので時間をかけながらも通し、 bash に行きました。bashAWK が使えないことに気づいて sed に切り替えてやっと通し、前回の『TSG LIVE! 2』のリベンジのつもりで ><> に挑み、通したり縮めたりした時点で終了まで3分となり、諦めてしまいました。

反省としては PythonJava、wake あたりです。

iLiss さんが C で苦戦していて、自分が手伝うべきか esolang に行くべきか訊いたら C は自分で通すということだったので、やはり彼に任せたいと思って任せました。そのおかげで自分は esolang に集中できたというのもあります。Brainf*ck は絶対通したいという気持ちと、前回の『TSG LIVE! 2』で ><> に時間をかけて通せなかったのが辛かったのでやはりそのリベンジをしたかったというのがあります。

とすると、ちゃんと勝ちに行くなら、非 eso である PythonJava を取りに行くべきだったんですね。ただ ><> が終わった時点で時間が少なかった上、RubyPerlbash に使ったのが正規表現解だったのに対し Python正規表現は長くて使えないのでアルゴリズムを考え直す余裕がなく、終わってしまいました。

Java はそもそも自分は経験がなく、過去のコードゴルフ大会の writeup を読むなどして事前に練習してくるべきでした。

wake は終わってから気づいたんですが、bash (sed) に使った方法がそのまま使えるんですね。言語仕様については事前に読んでおいたつもりでしたが、そこに至らなかったのは反省です。

終了後本郷チームに聞いたところ、もとから RubyPerl を取るつもりはなかったらしく、結局駒場が右上、本郷が左下を広げていって、陣地の取り合いが起こらないゲームとなってしまいました。これも反省点です。

終了後 iLiss さんが落ち込んでいる様子でしたが、これは自分も過去のコードゴルフ大会で経験していることだし、TSG 内で今後もコードゴルフ大会は活発に行われていくので、そこで経験を積んでゴルフ沼にハマっていってほしいと思ってます。

1日目 解説

ゴルフ経験がない人に向けてコードの解説をしていきます。

問題は こちら です。

ruby, 36 bytes

$><<gets.gsub(/(.)(\d)/){$1*$2.to_i}

過去のコードゴルフで猛威を奮っている kurgmさんが事前準備で到達したコードや、AtCoder で意味不明な量の shortest を取っている kotatsugame さんが到達したコードも全く同じものらしいので、これが最短なのではないかと思います。最短じゃなかったら教えてください。

$> は標準出力オブジェクトで、<< というメソッドで出力ができます。puts は空白が必要なのでそれより短いです。文字列の出力なので p は使えません。

gets で入力し、.gsub(置換対象){置換処理} で置換します。数字がなければ放っておいて、数字がついていれば「何らかの1文字+数字1文字」で検索できます。この各文字を「キャプチャ」する正規表現/(.)(\d)/ です。キャプチャすると、その後 $1$2 といった形で取り出すことができます。Ruby文字列 * 回数 で文字列をリピートできるので、$2 を数値に変えて、その回数分 $1 を繰り返したものを置換結果としています。

perl, 28 bytes

print<>=~s/(.)(\d)/$1x$2/gre

こちらも kurgm さんと kotatsugame さんが同じ長さに達しているので、おそらく最短でしょう。kurgm さんのコードは動画中にありますが shebang を使った別の形で、アルゴリズムは同じです。

<> は入力を1行受け取ります。s/置換対象/置換結果/ は置換用の正規表現で、=~ でマッチさせるのですが、そのままだと破壊的な置換となってしまい入力に対しては使えないので、r フラグをつけます。g はマッチしたものすべてを置換します。e フラグは置換結果の部分を Perl の式として解釈します。さきほどの Ruby と同様、キャプチャしたものが $1$2 に入っているので、「文字列 x 回数」でリピートします。

brainfuck-esotope, 311 bytes

,----------[++++++++++.>+>,--------------------------------------------------[-[-[-[-[-[-[-[-[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<<[-]>>[-<<+>>]<[-]>]<[-<.........,>]>]<[-<........,>]>]<[-<.......,>]>]<[-<......,>]>]<[-<.....,>]>]<[-<....,>]>]<[-<...,>]>]<[-<..,>]>]<[-<.,>]><<----------]

わかりやすく整形するとこうです。

文字 フラグ 回数
, -10 [ +10 . >   改行まで繰り返す。とりあえず出力
  +   ②フラグON
  >,   ③入力
  -50 [   '2' じゃなかったら
  - [   '3' じゃなかったら
  - [   '4' じゃなかったら
  - [   '5' じゃなかったら
  - [   '6' じゃなかったら
  - [   '7' じゃなかったら
  - [   '8' じゃなかったら
  - [   '9' じゃなかったら
  - [   ここはミスです
    +58   もとの入力に戻す
    <<[-]>>   ①を消す
    [-<<+>>]    ③を①へ移す
    <[-]>   フラグを消す
  ] <[-<.........,>]>   ここはミスです
  ] <[-<........,>]>   フラグが残っていたら '9' なので 8 回出力
  ] <[-<.......,>]>   '8' なので 7 回出力
  ] <[-<......,>]>   '7'
  ] <[-<.....,>]>   '6'
  ] <[-<....,>]>   '5'
  ] <[-<...,>]>   '4'
  ] <[-<..,>]>   '3'
  ] <[-<.,>]>   '2'
 <
 < -10]

見てもらえばわかりますが、不要なものがいくつか入ってますし、そもそも '2' から '9' まで全部チェックするのはゴルフとしてよくありません。なのになぜこれを書いたかというと、 esolang は縮めるよりまず通すことが重要 だからです。コードゴルフ大会は限られた時間で複数言語を解かなければならないので、esolang は愚直に通すことを目指したほうがよいです。

bash-busybox, 138 bytes

sed ':L
s/\(.\)9/\1\18/
s/\(.\)8/\1\17/
s/\(.\)7/\1\16/
s/\(.\)6/\1\15/
s/\(.\)5/\1\14/
s/\(.\)4/\1\13/
s/\(.\)3/\1\12/
s/\(.\)2/\1\1/
tL'

これも通せればよいということで、29 の判定を愚直にしています。この愚直な判定は wake に流用すべきだったという悔しさがあります。

とはいえ、もう少し時間があったら domperor さんに取り返されていたので危ないし、ちゃんと縮めるべきだったかもしれません(結果オーライですが)。

%20 さんが 56 bytes のコードをつぶやいていました。

fish, 76 bytes

i:a=?;>:oi:88*(?vv>
v           -*86<
v$o:$<^          <
>1-:?^            ^

><> と書いて fish と読みます。kuromunori さんと kurgm さんが事前に用意したコードは 50 bytes なので、それよりは結構長いですね。

スタックを使う言語で、2次元のソースコード上を進んで行くのが特徴です。< > ^ v で左右上下に進み、? で条件分岐します。

ただこれ、同じアルゴリズムでも配置を工夫すると空白と矢印が消せて 38 bytes になります。

i:a=?;\
88*(?v>:oi:
v-*86<
>1-:?!v$:o$

おまけ:2日目

2日目の結果はこんな感じでした。

esolang.hakatashi.com

f:id:n4o847:20190519140129p:plain

問題は こちら です。

駒場チームが圧倒的に強いですね。%8%3 が天才で、これを思いついたかどうかが決め手だったといえます。

あと、事前準備で bitmath さんが書いた C 49 bytes が凄みがありますね。char[]int として計算しているとかやばいですね。

自分も RubyPerl で挑戦してみました。%8%3 は自力で思いついたものではないのでずるいですが……。

ruby, 35 bytes

gets.to_i%8%3<2&&$.-=p(1)until$.>99

perl, 30 bytes

1while<>%8%3<2?print 1:$a++<99

追記:%8%3<2 より *5&4 の方が短い。