-
Notifications
You must be signed in to change notification settings - Fork 15
第6回東大京大コードゴルフ大会 Writeup
ワクチンを用いてウイルスを撲滅せよ
TSG国では、これまで確認されていなかった新型のウイルスが9種類も同時に発見され、未曾有の災禍をもたらしていました。
幸い、KMC研究所がそれぞれのウイルスに合わせた9種類のワクチンの開発に成功し、治療に用いられることになりました。
ところで、このウイルスは特殊な性質を持っており、一方向に3つ以上同じ種類のウイルスもしくは対応するワクチンが並ぶと消滅します。
さて、この新型ウイルスに感染した患者が32人やってきました。
検査により、体内に存在するウイルスの配列、およびワクチンを投与する場所はすでにわかっています。
消滅させられるウイルスを最大化するために投与するべきワクチンの種類を答えてください。
- 空白1文字で区切られた2つ組の3桁の数値が、32組、改行区切りで与えられる。
- すなわち1行は空白を含めて7文字で構成される。
- これらの数値は1つの桁が1つのウイルスを表し、1ならばウイルス1、2ならばウイルス2、のようにその桁の値がそこに存在するウイルスの種類を表す。
- 入力の最後には改行が付与される。
- 32行の入力それぞれについて、以下の問題を解け。
- 7文字の入力のうち、空白で表された場所に、ワクチン1~ワクチン9のいずれかを投与する。
- ワクチン投与によって消滅させられるウイルスの数を最大化するようなワクチンの種類を、対応する1~9の数字で答えよ。
- ただし、そのようなワクチンの種類が複数通りある場合、どれを出力してもかまわない。
- 都合32個の数字が出力される。
- 出力された文字列に含まれる空白文字(改行含む)は無視される。
- 1から9の数字、および空白文字以外の文字を出力してはいけない。
- 1から9の数字を32個より多く出力してはいけない。
- 2文字目と3文字目が同じ場合のみ3文字目(or 2文字目)を、そうでない場合5文字目を出力
- 5文字目と6文字目が同じ場合のみ5文字目(or 6文字目)を、そうでない場合3文字目を出力
同じかどうかの判定は
s[1] == s[2]
-
/(.)\1 /
みたいな正規表現にマッチするかどうか - 左側3文字を整数と見て下2桁を11で割った余りが0かどうか
- 右側3文字を整数と見て110で割った余りが10より小さいかどうか
- 嘘解法。
- 各行
l
をl[l.find(l[2])*2]
(findは先頭から探したindex)に置き換えて出力する。- 通常
l[2]
かl[4]
になるが、l[0]==l[2]
のときl[0]
になってしまう。
- 通常
- 15パターン中
.AB AA.
と.AB CC.
の時に1/9の確率でWAし、これらは最少4、最多6個出現するので、以下の通り約6割ACする。(8/9)^4*(1-(1/9)*(2/15))^2 ≒ 0.606
赤チーム内で共有していたhackmdのドキュメント内リンク https://hackmd.io/nP3m4szaSFiAA3KWkvICEQ?view#%E3%83%AD%E3%82%B8%E3%83%83%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%88
入力の受け取り方について:
- 文字列の配列
- 3桁の数字, 3桁の数字
この問題のテスト過程においては 32種類の入力が改行区切りで与えられる。ループの書き方について:
- 自分自身の関数を無限に再帰
-
loop{}
などの無限ループ - 新しい文字が入力されなくなれば終了
- 言語によってはEOFErrorみたいなので落ちる
- 3桁の整数 a, b で入力を受け取る場合は、どこかに
... % a
を入れておくと最後 a=0, b=0 になってゼロ除算例外で落ちる (Go, Bash (pure))- 入力中の整数は111以上であることが保証されているので、11で割った余りなど値が確実に111未満な式の後ろに
%a
をつけられる
- 入力中の整数は111以上であることが保証されているので、11で割った余りなど値が確実に111未満な式の後ろに
- 32個と決め打ち
for
などでループ - 32個の配列を一括で処理 ( APL など )
ABC (@eto_nagisa, 67 bytes)
WHILEe>0:
READsRAW
PUTs@5|1INt
IFs@2|1=s@3|1:PUTs@3|1INt
WRITEt
AlphaBeta (@drafear, 26 bytes)
KKJsFnIFtFJJrHItFGsLJJJyiN
入力行 xAB_Cxx
に対して C + (A==B)B - (A==B)C
を計算して出力
APL (@coil_kpc, 56 bytes)
⎕ARG[6]{α[ω-3+2×α[ω-6]=α[ω-5]]}¨(8×ι32)
)OFF
{}
がラムダ式(すぐ呼ばれている)で、左(=⎕ARG[6]
: 入力文字列)がα、右(=8×ι32
=8 16 .. 256の各要素)がωになる(¨は各要素に適用みたいなやつ)
APLには演算子の優先順位などという概念はなく、α[ω-(3+(2×(α[ω-6]=α[ω-5])))]
と読まれる。
awk (GNU awk) (@kotatsugame_t, 22 bytes)
FS=A;$0=$2~$3?$2%10:$5
嘘。実際はBEGIN{FS=A}$0=$2~$3?$2:$5
。FS=A
(未初期化の変数=空文字列)で入力を1文字ずつに区切って受け取り、$2~$3
(正規表現)や$2-$3
で場合分けをする。
さて、BEGIN
は長いのでFS=Aを外に書きたくなる。そうすると、1行目の処理だけ入力が空白区切りのままになってしまう。上のコードでは問答無用で末尾の文字を出力している。
ところで、シェルから呼び出すときはawk -vFS= 'code'
ではなくawk 'code' FS=
でよい。しかしawk
がどこの環境にもないのである!
Backhand (@ten986, 15 bytes)
Wii:iEi+s9iIi o
1次元上を動くスタック指向言語。デフォでは3ステップずつ動き、両端で反射する。
なんと第5回大会と比べバージョンアップがあり、ステップ数を2下げるW
、j
と違い相対位置で分岐可能なs
、比較演算子LGE
などが追加された。
W ステップ数-2
i 1文字目(*)
i:iE 2文字目を残しつつ2文字目と3文字目を比較して0or1に
i+ 4文字目の" "=32を受け取り、↑と加算
s 32or33ステップ進む
i 32ステップ進んだ場合のみ実行、5文字目
o 2文字目or5文字目を出力
i 5文字目or6文字目
iI 改行までのすべて
s9 (*)を右向きで実行出来るように進む
最後はなんやかんやあって-1を文字コードとして出力しようとしてエラーで落ちる。
Ballerina (@taiyoslime, @_hiromi_mi, 104 bytes)
import ballerina/io;public function main(){string s=io:readln("");io:println(s[s[1]==s[2]?2:4]);main();}
最後に for
ループを main();
で再帰呼出するように書き換えると完成。
Bash (busybox) (@kotatsugame_t, 30 bytes)
sed 's/.*\(.\)\(\1\| \).*/\1/'
嘘。Vim
やV (Vim)
と同じ置換一発の解法だが、sed
では+
をエスケープするのを避けて*
を使っているため。
先頭2文字が同じだとそれが出力されてしまう。
Bash (pure) (@drafear, 42 bytes)
read a b;echo $[b%110<9%b?b/100:a%10];. $0
builtin command の . $0
(source $0
) で自分自身を呼び出して再帰
無限ループするが、9%b
でエラー落ちして終了する
b%110<=9
を b%110<9
としたために運が悪いとWAするが、実は
b%110<9%b?b/100:a%10
を
b%110>9%b?a%10:b/100
とすれば同じ長さで確実にACするコードにできる
Bots (@n4o847, 196 bytes)
a(){ic b}b(z){+1 z?c@0}c(z){ic d}d(z){ic e z}e(y,z){ic f y z}f(x,y,z){ic g y z}g(x,y,z){ic h x y z}h(w,x,y,z){ic i x y z}i(w,x,y,z){ic j x y z}j(w,x,y,z){-z y?k l x y}k(x,y){oc x a}l(x,y){oc y a}a
前回から言語仕様が一新されている。綺麗になった代わりに bot 要素がなくなってしまった。
継続渡しがベースとなっている。入力をうまくまとめる方法が思いつかなかったが、とりあえず通せばいいと思い状態ごとに関数を作った。
詳細は以下のようになる。
a() { ic b } 入力したら b に渡す
b(z) { + 1 z ? c @ 0 } 1 文字目が -1 でなければ c に、-1 なら exit に 0 を渡す
c(z) { ic d } 入力したら d に渡す
d(z) { ic e z } 入力したら 2 文字目とともに e に渡す
e(y, z) { ic f y z } 入力したら 3 文字目と 2 文字目とともに f に渡す
f(x, y, z) { ic g y z } 入力したら 3 文字目と 2 文字目とともに f に渡す
g(x, y, z) { ic h x y z } 入力したら 5 文字目と 3 文字目と 2 文字目とともに f に渡す
h(w, x, y, z) { ic i x y z } 入力したら 5 文字目と 3 文字目と 2 文字目とともに f に渡す
i(w, x, y, z) { ic j x y z } 入力したら 5 文字目と 3 文字目と 2 文字目とともに f に渡す
j(w, x, y, z) { - z y ? k l x y } 2 文字目と 3 文字目が異なれば k に、同じなら l に 5 文字目と 3 文字目を渡す
k(x, y) { oc x a } 5 文字目を出力して a へ
l(x, y) { oc y a } 3 文字目を出力して a へ
a a を実行
Brainfuck (esotope) (@n4o847, 30 bytes)
,[>>,>,[-<-<+>>],,<[>]<.,,,,+]
赤青緑の全チームが独立に 30 bytes に達していたらしいのでほぼ理論値なのではないでしょうか?
動きをわかりやすくしてみるとこうなります。
, [ a ]
[
>>,>, | a | | b [ c ]
[-<-<+>>] | a | c |b-c[ ]
,,< | a | c [b-c] d |
case b==c case b!=c
[>] | a | c [ ] d | | a | c |b-c| d [ ]
<. | a [ c ] | d | | a | c |b-c[ d ] |
,,,, | a [ a'] | d | | a | c |b-c[ a'] |
+
]
braintwist (@naan112358, 本質: @shinh, 39 bytes)
846
41
2
54
9
66
0
9 7
93 3
まず、この言語はbrainfuck変種なのでbrainfuckのコードを書きます(thanks to @shinh さん)
brainfuckシミュレータがBrainfuck(esotope)とちょっと仕様が違って
- (dereferenceの有無に関わらず)負番地を指すポインタがあってはならない
- EOFが0 (2023/07/23作者より追記: オプションで変更できるようになり、現在esolang-boxではEOFは255になっています)
っぽいのでそれを織り込んだbrainfuckコードを書きます(書いてもらいます)
,[>>,>,[-<-<+>>],,<[>]<.,,,,]
EOFが0なのでesotopeより1bytes短い(29 bytes)
あとはこれになるようにseedをガチャガチャやって終了(は?)
実際は無限列なので無限で、対応の取れていない括弧を踏んで終了するんですが、実行されるbrainfuckは
,[>>,>,[-<-<+>>],,<[>]<.,,,,],<,<+[<<,[-.<.<+--+>]<,+<,][],[[[>>[]+<+-+>.]>[>+.-[,.+.[,-.<><-.+-+>,++<].>.]]-<+<]],]+,+->,]-]
です
21:00までずっとプログラムをぶん回していたんですが(マイニング言語)、39 bytes から縮まなくて、絶望────
BubbleSortLanguage (@kotatsugame_t, 8885 bytes)
リポジトリにBFtoBSL.exe
なるものがあるので、@n4o847さんのBF 30Bを窃盗してぶち込むと20000B弱のコード(数列)がドバーッと出てくる。気が狂う!
ところで、数値は大小関係とmod 50
さえ保存されていればよい。
よく見ると全部100
以上の数値なので、100減じる。さらにとんでもない個数の55
があるので、これを先頭のほうに持ってきて5
にする。これで変更前の提出コード長制限10000Bを通る。
あとは隣接項の差分がデカいところがあったので、それ以降をちょっと減じたりしてみた。
C (GCC) (@satos___jp, 54 bytes)
i,j;main(){for(;gets(&j);putchar(j%257-32?i:j))j>>=8;}
getsで一行ごとに読み込むループ。EOFをgetsするとNULLが返るので、これでbreakできる。gets(&j)
でj
に行の先頭4byteが、その隣にメモリ上で隣接しているi
に次の4byteが入る。j
を8bitシフトさせると、j
には2文字目のアスキーコード+3文字目のアスキーコード*256+空白のアスキーコードである32*256*256
が入る。このとき、j%257
は(2文字目のアスキーコード-3文字目のアスキーコード+32*32*256) % 257 = (2文字目のアスキーコード-3文字目のアスキーコード+32)% 257
になるので、これが32に等しい場合は2文字目を、そうでない場合5文字目を出力すればよい。putchar(c)
はc%256
のアスキーコードに対応する文字を出力するので、あとは三項演算子を用いればできあがり。
LibreOffice Calc (@kcz146, 46 bytes)
=REGEX(A1;A3;B3;C3)
.((.)\2 .|.. (.))..,$2$3,g
CJam (@dnek_, 13 bytes)
{l__2=#2*=_}g
#find-l2を使った。
ほぼ見たままだが、{}g
はpopしてtrueならループを抜けるので最後に_
でdupしている。
ClassicMusicTheoryForProgrammer (@u6606u5e03, 913 bytes)
コードを見る
|
R
E4 D4
G3 T
C3 B2
|
R
E4
T
C3
|
T
T F4
T A3
T
|
R
E4
G3
T
|
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4
T
C3
|:
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4
T
C3
|
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4
T
C3
|
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4 F4 E4 F4
T A3 G3 A3
C3
|
R
E4 D4 E4 D4
G3
T B2 C3 B2
|
R
E4 F4
G3 T A3 B3
C3 D3
|
G4
E4 D4 E4 D4
R
C3 B2 C3 B2
|
T A4 G4 A4
E4 F4 E4 F4
R
C3
|
R
E4 F4 T T
G3 A3 T B3
T D3
|
G4 T A4 B4
E4 F4
R
C3 D3
|
R
E4 F4 E4 F4
G3 A3 G3 A3
C3
|
R
E4 F4
G3 A3
T
|:
R
T
T B3
D3
|
G4 A4 G4 A4
E4 F4 E4 F4
R
C3
|
R
E4 F4 E4 F4
G3 A3 G3 A3
T
|
R
E4 T F4 T
G3 T A3 B3
C3 T T D3
|
G4 A4 G4 A4
E4 F4 E4 F4
R
C3
|
R
E4
G3
T
|
T
T D4
T
T B2
|
R
E4
T
C3
|
R
T F4
T A3
T
:|
R
T T E4 D4
A3 G3
T T T B2
|
R
E4 F4 E4 F4
T A3 G3 A3
C3
|
R
E4 D4 E4 D4
G3
T B2 C3 B2
|
R
E4
T
C3
|
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4
T
C3
|
R
T D4 E4 D4
T
T B2 C3 B2
|
R
E4
T
C3
|
R
T F4
T A3
T
|
R
E4
G3
T
|
R
E4 D4
T
T B2
|
R
E4
T
C3
:|
R
T
T
T
|
コード進行が単語に相当している言語。和音の中身(どの声部がどの音を鳴らすか)は音楽理論にさえ従っていればプログラムの挙動に全く関係ないため、和音を決めるステップと音を決めるステップに分けて作業できる感覚。
- ループ兼条件分岐の特殊小節線「
|:
:|
」のドキュメントにおける説明でいう「直前に実行した命令」は、特殊小節線を右端とする命令がある場合はその一個前の命令を指す(よく読めばわかる)のだけれど、これを勘違いしていて時間を溶かしてしまった。 - 文字は数値として読み込まれるが、EOFは-1に相当する。終了処理ではインクリメントとジャンプゼロを組み合わせてEOFを検出するのがいいだろう。
短縮テクニックは色々あった。まだ何か見落としているかもしれない。
普通、音はC5
のように2bytesで書くのに対し、直前と同じ音ならT
と1byteで書ける。
小節内のC5 T T T
は単にC5
と、E4 T D4 T
はE4 D4
と書いてよい。
これらを根気強く適用して縮めていったのだが、終わってみるとまだ見落としがあった。機械にやらせた方がいいかも。
V̸₇(属七の和音の根音省略;B+D+F)をV(G+B+D)の代わりに使ってよいことになっている。「II→V」の進行でこれを使うと、II(D+F+A)のFをそのままVでも使えるので、コードが縮む。あの、いま「コードが縮む」って言ったのは、chordじゃなくてcodeの方です。ややこし。
古典音楽理論といえば声部は4つだが、この言語では和音が欠けない限りは休符を使ってよい。休符はR
と1byteで書けるのでもりもり使う。なんなら全休符にすればR R R R
の代わりにR
と書ける。結果、三声部しかない曲に落ち着く。悲しい。
さらに悪質なことに、ずっと一つの声部を休ませるのではなく休む声部を途中で変えることにより、音楽理論の大切なルールである限定進行音をなかったことにできる。本当にすいません。
ところで、書き上がったプログラムはちゃんとMIDIに変換することができる。自分で汗水たらして書いたプログラムからできたクラシックの曲の味わいは筆舌に尽くしがたい。こんな贅沢なesolangが他にあるだろうか。作ってくれたLv100tomatoさん、本当にありがとうございました。
COBOL (@k_hanazuki, @kotatsugame_t, 159 bytes)
program-id.a.data division.local-storage section. 1 l pic x(7).
procedure division.a.accept l.if l(2:1)=l(3:1)display l(2:1)else
display l(5:1).if""<l go a.
アルゴリズムは愚直.EOFを超えてaccept
すると変数がスペースで埋められるので,空文字列と比較して終了を知れる.
行頭8桁にはソースコードを書けないためタブ文字を置いている.また,1行80桁の制限があるが,改行できる位置・できない位置,削ってよいスペース・よくないスペースなどがあり,コンパイラの機嫌を伺いながら折返しをした.
Coq (@coil_kpc, 309 bytes)
コードを見る
Require Import Io.System.All List ListString.All Ascii.
Import ListNotations C.Notations.
Fixpoint q n:=match n with
0=>ret tt|S m=>let! l:=read_line in
do! match l with
Some s=>log match s with[_;x;y;_;z;_;_]=>if eqb x y then[x]else[z]|_=>[]end|_=>ret tt
end in
q m
end.
Definition main:=launch(fun _=>q 32).
coq.ioのドキュメントを読んで、書いて、自明な短縮をします Coqさんは停止性にうるさいので明示的に32回って言ってあげます
Compile-time C++ (Clang, C++11) (@kurgm, 137 bytes)
extern const char*const in;char r[33],i=([]{for(auto l=in;*l;l+=8)r[i++]=l[l[1]-l[2]?4:2];}(),0);constexpr char*f(const char*){return r;}
コンパイル時計算すると思った?しないんだなぁ〜これが()
これはこの後ろに
constexpr const char *in = "入力";
constexpr const char *out = f(in);
が続いてコンパイルされるわけですが、これよく見るとコンパイル時に決定されないといけないのは文字列へのポインタの値でしかないんですね。文字列の中身はコンパイル時に決定される必要がないわけです。
でもconst char *
だからポインタが指す中身は実行時には定数ゆえ、結局コンパイル時に決まっていないと行けないんじゃないかと思うかもしれません。しかしその考えもまた誤りなのです。確かにconst char *
型のポインタの指す先の値を書き換えることはできないし、const char *
型の値を、書き換え可能にするためにchar *
型の変数に代入することもできません。しかし、実は逆の代入は全く問題がないわけであります。つまりconst char *
型の変数だけど実は別の誰かがその指す先の値を書き換えることは可能、ということは十分ありえるのです。
const char *cc;
char *c;
c = cc; // compile error!
cc = c; // ok
というわけで配列r
を用意しておいて、f
はr
を指すchar *
型のポインタ(もちろんコンパイル時に決定できる)を返しておいて、別のグローバル変数の初期化子(mainより前に実行される)でin
を参照してその配列r
に答えを書きこめばよいです。
Cubically (@kotatsugame_t, 18 bytes)
~(~:~=!~2@7$~:~<)6
1文字読んでループに入る。
ループ内では2文字読んで比較し、異なればさらに2文字読む。これでケースに応じて行の3文字目または5文字目がバッファに入っているので、出力する。
数値を読むコマンドを実行して行の残りを飛ばし、2文字読んで比較する。このとき読んだ2文字目がEOFだと改行より小さい値になるので、これでbreakする。
入力バッファとそこから:
1つでコピーできるnotepad
というメモリがあって、その間でいろいろ演算できるので、かなりシンプル。Rubiks Cube要素はない。
Cyclic Brainfuck (@_primenumber, 57 bytes)
D (GDC) (@k_hanazuki, 72 bytes)
import std.stdio;void main(){auto s=readln;write=s[s[1]-s[2]?4:2];main;}
- 関数呼び出しは括弧を省略できる
- 関数呼び出しを代入の形で書ける
- mainの再帰呼び出しによるループ
などの基本的なテクが使われている.
Egison (@_hiromi_mi, @kotatsugame_t, 140 bytes)
(define$z(lambda[](do{(let{[$x(unpack(io(read-line)))]}(writeChar(nth(if(equal(nth 2 x)(nth 3 x))2 5)x)))}(z))))(define$main(lambda[$a](z)))
この式は展開すると以下の通り.
(define $repl
(lambda []
(do {(let {
[$x (unpack (io (read-line)))]}
(writeChar (nth (if (equal (nth 2 x) (nth 3 x)) 2 5) x)))}
(repl))))
(define $main
(lambda [$args]
(do {[(repl)]}
[])))
Egison 4.0.0 から記法が大きく変化し Haskell 風になったが、この時点では Documentation は更新されているもののパッケージは古い記法のままなので S式記法にて取り組んだ. Documentation を見つけるためにGitHub から古いWebページを取ってきてそれを参考に解いた.
- 公式ページに紹介されている
eq?
は 数値用,write
は文字列用なのでそれぞれequal
とwriteChar
を用いる. - (io(read-line)) とすると IO Monad として扱えるので unpack と let に渡せる.
- 公式ページにある
(each-line proc)
を使うとproc
内部の関数を各行ごとに読み込み呼び出すようなので使えることを期待したが, unpack にてエラーが出て断念. - 当初改行文字の出力は必須だと誤解していたため
(write "\n")
をつけていたが不要. - エラーメッセージがどの行で発生しているのか表記されないので、段階的にプログラムを作成すると良い.
Emojicode (@dnek_, 148 bytes)
🏁🍇🔂i🆕⏩0 32❗️🍇🆕🔡▶️👂🏼❗️➡️s🎶s❗️➡️a😀🐽a🍺🔍s🐽a 2❗️❗✖️2❗️❗️🍉🍉
読みやすくしたもの
🏁🍇
🔂i🆕⏩0 32❗️🍇
🆕🔡▶️👂🏼❗️➡️line
🎶line❗️➡️list
🐽list 2❗️➡️l_2
🍺🔍str l_2❗✖️2➡️index
🐽list index❗️➡️x
😀x❗️
🍉
🍉
#find-l2を使った。 1年前とまた仕様が変わっており、特に入力の取り方を見つけるのに苦労した(@kotatsugame_tさんに見つけてもらった)。 🍺はoptionalのunwrapをしている。 また、わざわざstringとlistを併用しているのは、何故かstring.get(index)とlist.find(item)が見当たらなかったからである。
Erlang (@kotatsugame_t, 116 bytes)
f()->{_,[B,C,E]}=io:fread("","~*c~c~c ~c~*s"),io:format(if B==C->B;1>0->E end).
main(_)->[f()||_<-lists:seq(1,32)].
リスト内包表記で32回ループしている。本当はmain再帰で書きたかったが、エラーメッセージが標準出力に出てくるためどうしようもない。
f()
は見た通り。行の最後2文字は"~*s"
で一度に読んでいる。
(この大会が開催された時点から見て)最近行われたAtCoder
の言語アップデートで追加された言語であり、ABC162が埋まっているので、それをチラチラ見ながら書いた。
過去の大会のWriteupを読んでいて知ったのだが、最初のうち先頭に書いていた-export([main/1]).
みたいなのはいらなくて、改行しておけばいいらしい。ええ……
FerNANDo (@sitositositoo, 229 bytes)
嘘解法(確率解法; だいたい46%通る) (229bytes)
. - - - - - - - -
.
. - $ - ! a b c d
. - * - \ e f g h
*
. - - @ * i j k l
* *
*
@
@ \
@ @
. - - \ - - - - -
@
m b f
b m
m f
b m
m c g
c m
m g
c m
b b
c c
b c
i b
j b
k b
l b
b b
e b
f b
g b
h b
e i
f j
g k
h l
$ $ ! ! e f g h
.
正解法(確率によらない解法) (289bytes)
. - - - - - - - -
.
. - $ - ! a b c d
. - * - \ e f g h
*
. - - @ * i j k l
* *
*
@
@ \
@ @
. - - \ - - - - -
@
m a e
a m
m e
a m
m b f
b m
m f
b m
m c g
c m
m g
c m
m d h
d m
m h
d m
a a
b b
c c
d d
a b
c d
a a
c c
a c
i a
j a
k a
l a
a a
e a
f a
g a
h a
e i
f j
g k
h l
$ $ ! ! e f g h
.
入出力とdo while
とNAND計算しかできない言語です。入出力がビット単位なので、2文字目と3文字目をビットごとに一致判定(すなわちXNOR)しています。判定する入力は1~9なので、下位4桁のみ見ています(上位4桁の0011から、出力に使う0と1を再利用したりもした)。短縮ポイントは次の通り。
-
A NAND B
はC A B
ですがA = A NAND B
はA B
と短く書けるので、できる限り変数を再利用しています。また、入出力がビット数多くなりがち(1文字あたり16bytesほど)なので、いらない文字はなるべくループで捨てています。 - 2文字目と3文字目の一致判定を無視して不正解になるのは
XAB AAY
、XAB CCY
の2パターンしか存在しません。流石になにも判定しないと確率が低すぎるので、まずは上位1bitだけ判定しないのを考えると、(A,B)=(1,9),(9,1)
しか引っかからず、通る確率は88%強。さらに下位1bitも判定しなかった場合でも46%ほどの確率で通る(!)。最終提出では2,3bit目のみで判定しました。
ちなみに、正解法だとsatos氏の提出のほうが短いです。入力周りをさらに削っているみたいです。すごい。確率解法で通してしまってごめんなさい……
><> (@n4o847, 10 bytes)
ii|o@?-i:i
これ思いついたときに脳汁がドバドバ出ました。
青チームも同様にミラーを使う解法に至っていたようですが、スタック操作の無駄を省けたのが決定打だったと思います。
動作をわかりやすくしてみました。形式は [スタック] "出力"
です。
ii [a, b]
|
ii [a, b, c, ' ']
-i:i [a, b, c, ' ', d, d-e]
case d==e case d!=e
@? [a, b, c, ' ', d] [a, b, d, c, ' ']
o [a, b, c, ' '] "d" [a, b, d, c] " "
|
o [a, b, c] "d " [a, b, d] " c"
@ [c, a, b] "d " [d, a, b] " c"
?- [c-a] "d " [d-a] " c"
i:i [c-a, e, e, '\n'] "d " [d-a, e, e, '\n'] " c"
EOF を読み込むと落ちます。
Fish (pure) (@kotatsugame_t, 64 bytes)
set IFS
while read a b c d e f
[ $b = $c ]&&echo $b||echo $e
end
1文字ずつに分解して読む。最初はread -n1
としていた。
read
を呼ぶと入力された内容が標準出力に出てきたり来なかったりしたので、それを利用して||read -n1
みたいな書き方を試したが、read
を複数回呼ぶのは長く、set IFS
が最も短くなった。
Fortran 2018 (@kotatsugame_t, 67 bytes)
program a
do
read'(5i1)',i,j,k,l,m
print*,merge(j,m,j==k)
enddo
end
read'(5i1)'
で数字を5文字読んでいる。6文字目がどこに行ったのかは謎である。
merge
は三項演算子相当のことができたりして、えらい。
ところで、program a
というのは完全にいらない子らしい。do enddo
のループをgoto
に変換するのもやると以下。
1 read'(5i1)',i,j,k,l,m
print*,merge(j,m,j==k)
goto 1
end
Fugue (@kurgm, 167 bytes)
以下のPreludeというEsolangのコードを自作の変換器にかけるとMIDIが出てくる。
?v- (#)
?(??#? ^ #!????)
行ごとにスタックが用意される。実行は一列(行ではない)ごとに左から右へと進んでいく。
?
が入力で!
が出力、v
と^
はそれぞれ下と上のスタックのトップをコピーしてくる操作、#
はスタックからのpop、()
はスタックのトップが非ゼロの間ループする。
1行目は各入力行の2,3文字目を比較して()
を使って分岐する。2行目の^
が実行されるかどうかで2文字目が出力されるか4文字目が出力されるかが決定する。
ただし、提出したコードでは変換器に細工をしていて、キーを押す操作(note on)とキーを離す操作(note off)のうち、押す操作の方だけをMIDIに出力している。
Function (@yamayu832, 13330 bytes)
メインとなるコード
╔══╗┌─╖╔═╗
┌┐ ┌┐ ║10╟┤ǁ╟╢ ║
┌─────┘├─────┘├──┐╚══╝╘╤╝╚═╝
╔═╗│ ╔═╗│ ╔═╗│ ╔╧╕┌──╖│
║1║│ ║2║│ ║4║│┌╢ ├┤ɱ ╟┘
╚╤╝│ ╚╤╝│ ╚╤╝││╚╤╛╘╤═╝
╔═╗┌┴╖│╔═╗┌┴╖│╔═╗┌┴╖││╔╧╗┌┴╖
║1╟┤ʃ╟┘║1╟┤ʃ╟┘║1╟┤ʃ╟┘│║0║│ʝ╟
╚═╝╘╤╝ ╚═╝╘╤╝ ╚═╝╘╤╝ │╚═╝╘╤╝
│┌─╖ │ │ │ ╔═╧╗
└┤=╟───┴───┐ │ │ ║10║
╘╤╝ ┌┴╖┌┴─╖│ ╚══╝
│ ┌──┤·╟┤· ╟┘
│ │ ╘╤╝╘╤═╝
│ ┌─┴─╖┌┴╖ │
└──┤ ? ╟┤·╟─┘
╘═╤═╝╘╤╝
└───┘
以下の関数はライブラリから持ってくる(https://github.com/Timwi/Funciton/tree/master/FncLib)
ǁ:split
ɱ:map
ʃ:substring
ʝ:join
=:等価関数
?:3項条件演算子
·:cross-nop(線の交わりを解消)
╔╧╕
╢ ├:ラムダ関数
╚╤╛
コードをpythonで書くと
"\n".join(lambda x: x[1] == x[2] ? x[2] : x[4]), map(input().split("\n"))
なおpythonに3項条件演算子はないが,使用している.
3項条件演算子を使わないなら
"\n".join(lambda x: x[2] if x[1] == x[2] else x[4]), map(input().split("\n"))
この言語は,配線の向きが重要であるが,出力する方向を基準に入力する方向も回転するため注意が必要.二重線と一重線を使う位置を回転させることもできるが,それは入力の方向とは関係ないらしい.ここがトリッキーだった.
この言語は最初には
- NAND
- less-than
- shift-left
- 関数定義,呼び出し
- ラムダ関数 程度しか準備されていない
ライブラリは,必要な基本的な関数(加減乗除,比較)やリスト,文字列操作などを上記の基本的なパーツを利用して実装しており,見ていて楽しい.
gnuplot (@kurgm, 59 bytes)
se pr"-"
do for[i=2:256:8]{pr(v=ARG1[i:i+3])%11?v[3:]:v%10}
各行から2〜5文字目をとってきてv
に代入する。ポイントは、文字列を算術演算子のオペランドに使うと暗黙の型変換が発生するのだが、"23 4"
のような文字列は23
に変換されるということである。
あと、se pr"-"
はset print "-"
の、pr
はprint
の省略形で、@shell_mugくんが気づいてくれました。
Go (@drafear, @kotatsugame_t, @n4o847, @taiyoslime, 97 bytes)
package main
import."fmt"
func main(){for{a,b:=0,0;Scan(&a,&b);if b%110<9%a{a=b/10};Print(a%10)}}
取り合いが多かった言語のひとつ。
b
が XXX
あるいは XXY
の形式なら b
の十の位を、そうでなければ a
の一の位を出力している。詳しくは赤チームのロジックリストも参照してほしい。9%a
はゼロ除算でループを抜けるためのものである。b%110<9%a
のところは実は嘘で、本当は b%110<=9%a
が正しい。
以下 Go 特有の事項を挙げる。
- ピリオド付き
import
によってパッケージ名を省略して呼び出せる。-
fmt.Print
ではなくprint
というのもあるがこいつは stderr に出力する。??
-
-
if
は文であり三項演算子にあたるものが存在しないのでつらい。 - 前身のコードでは回数カウントしたり条件で
main
再帰したりしていた。が、条件無しfor
は字数が稼げるので利用したい。しかしScan
は実行時安全になっているので読み取れなかったらうまく無限ループを抜ける必要がある。ということでこのコードではゼロ除算で終了させている。他チームだと範囲外エラーを起こす手法が使われていた。
golfish (@n4o847, 10 bytes)
我が自作言語。ゴルフ用言語を謳っておきながら gs2 に負けたけど……。 その上内容は完全に ><> の移植なのでいたたまれない気持ちになってしまった。
GolfScript (@kotatsugame_t, @dnek_, 15 bytes)
8/{..2=?2*=}%n+
#find-l2である。
..2=?2*=
はdup dup 2 get(indexing) find 2 * get
、これを入力を8byteごとに区切ったリストに対してmap
する。
このままだと文字コードが返ってくるので、適当なstring
でjoin
する。GolfScript
においてn
は改行文字として定義されているので、これを使うと短い。
gs2 (@dnek_, 8 bytes)
0A5>*5M
16進表記と解釈
30 line-mode
41 dup2
12 push 2
35 get
3E index
2A double
35 get
4D itemize
#find-l2を使った。
入力から取った1
などは49
などと出力されてしまうのでitemize
で1要素の配列にする。
Hanoi_Stack (@n4o847, 1410 bytes)
攻め込まれる様子がなく、通せば勝ちと思って同じ処理を 32 回繰り返した。前回と同様に生成スクリプトを書いた。
https://gist.github.com/n4o847/c66df01e6dc22419176791fd6f7145f3
その後 @sitositositoo さんがちゃんとループを使って 38 bytes にまで縮めたらしい。
緑チームが終了3時間前にFugueを奪取したのでHanoi_Stackをやり始めて、終了1.5時間後にやっと完成したので、供養の意味も込めて書かせてもらいました(仕様の勘違いによる完全な無駄が1行あったので、取り除くと38bytes->34bytesに短くなりました。)
Hanoi_Stackのソースコードはバイナリファイルです。Dropboxリンク
00000000: 10000000 00000000 00000000 00010000 00000000 00000000 ......
00000006: 00000000 00000100 00000100 11000000 00000000 00000001 ......
0000000c: 00000000 00010100 00000000 00000001 00000000 00100100 .....$
00000012: 01000000 00000001 00000000 00000110 00000000 00010100 @.....
00000018: 00000000 00001000 10000000 00000001 00000000 00000001 ......
0000001e: 00000000 00000101 00001011 11000000 ....
書き下し
<0>
~<2>
でスタック0~2(またはその先頭)を表すものとします。処理したバイト数が省略されてるpop, copy, if, print
と減算はすべて先頭1バイトに対する処理です。
1命令あたり2バイトですが、<0>
から5bytespop
するときに実際は4+1bytesに分ける必要があるため、17命令34bytesとなっています。
# ***入力反転***
# 入力は先頭文字から順に<0>にpushされ、最後に入力長が4bytesで入るため、
# 先頭の4bytesを捨ててから、入力末尾(<0>==0)まで<1>にコピーして捨てています。
pop 4bytes from <0>
copy from <0> to <1>
pop from <0>
if <0> is zero, skip the next line
jump -3 lines
# 以下各テストケースを"ABC DEF"と表します。
# 先頭のテストケースのAを削除
pop from <1>
# ***メインループ***
copy from <1> to <0> # <1>の先頭からBをコピーして<0>に入れる
pop from <1> # Bを捨てる -> <1>=C
<2> = <1> - <0> # <2>=C-B Hanoi_Stackでは演算結果は残りの一つに入る
pop 2bytes from <1> # <1>からCとスペースを削除
# <2>=C-Bが0でなければ、Dを<1>からコピーして<0>に入れる
if <2> is zero, skip the next line
copy from <1> to <0>
print <0> # B==CならBが、B!=CならDが出力される
pop 5bytes from <1> # <1>から"DEF\n"と次のテストケースのAを削除
# 入力の先頭が0(=終了)でなければメインループの先頭へ戻る
if <1> is zero, skip the next line
jump -10 lines
Hanoi_Stackは3つのスタック(<0>
~<2>
とします)の上で計算を進める言語です。はじめ入力はすべて<0>
に(反転して)入っています(反転しているのに気づくのが遅れたのが、間に合わなかった原因の一つでもある)。
書き下し中の<2> = <1> - <0>
という演算にもみられますが、2つのスタックの演算結果は残りの1つのスタックにはいるようになっています。ここハノイの塔って感じがして好き。これを意識して「どこに演算結果を入れておきたいか」から逆算することでスッキリとしたコードにすることができる気がします。しらんけど。
ループに関しては、入力長を活用する手もあるかとは思いますが、たぶんそれを書くと長くなるので、今回に関してはさっさと消し飛ばしました。
Haskell (@kcz146, 52 bytes)
main=interact$map f.lines;f(_:a:b:x)|a==b=a|0<1=x!!1
Hexagony (@n4o847, 91 bytes)
,,{,'\._...-..}+..=...}.;.}....,..&=.._..\,,,.~.<>$....+'..@....}.,.....=..,...+...}.\}=*=/
可視化するとこう。
なんと今回の盤面と同じ形をしていますね! (これがやりたかったのでこれ以上縮めていない)
プログラムだけでなくメモリも六角形構造をしているのでややこしいが、最初に ,,{,'-=}=+}=+}=*=},,'&;}},,,,
という操作を書いたらあとは手作業で並べた。
akouryy さんが作った Hexagony IDE で動作確認ができる。
Husk (@drafear, 15 bytes)
m(!1fEmuwthh)¶
"144 325"
-> ["44", "3"]
-[map unique]-> ["4", "3"]
-[filter (len=1)]-> ["4", "3"]
-[head]-> "4"
HyperTorus (@sio_tet, @n4o847 386 bytes)
コードを見る
1<.r.1...+......./...............r...............................r...............................................................:..............................................................................................................................<>-@*=..-r......w:..............rr..............................r~...............................................................r
このコードは可視化しにくいが 9 次元立方体をなしている。
まず一列の命令を並べるために >
や <
をどう配置すればいいかという課題があり、これを手でやるのは頭が壊れるので、小さい次元で試したのをもとにルートを探索するようなスクリプトを書いた。
次に条件分岐が課題だったが、=
命令があったのとチーム内で共有されていたロジックのおかげで条件分岐が不要となり、無限ループさせて最後はゼロ除算を起こすことで回避した。
生成スクリプトは以下。
https://gist.github.com/n4o847/8195235e71c2db745271ef07b35be172
IRC (@_hiromi_mi, @kotatsugame_t 469 bytes)
基本的に Wiki を見ながら書けばよい。工夫箇所は
- 終了判定を 1文字目が
x == \0
かで判定し、quit IRC
を先に持ってくる。 -
What's your character?
が長いので if の結果をz=y
としてz
のみを表示することで済ませる. (idea by kotatsugame_t) -
+v x
などを複数人一行で書けないかとも思ったが対応していない.
コードを見る
* main has joined #code
* x has joined #code
* y has joined #code
* z has joined #code
* main sets mode: +v x
* main sets mode: +v y
* main sets mode: +v z
* main changes topic to 'f'
<input> Hey, x.
<if> x, are you equal to 0?
* main has quit IRC (Quit: )
<input> Hey, x.
<input> Hey, y.
<input> Hey, z.
<input> Hey, z.
<if> x, are you equal to y?
<z> I'm y.
<output> What's your character, z?
<input> Hey, x.
<input> Hey, x.
<input> Hey, x.
<jump> Let's talk about f.
Iwashi (@yamayu832, 452 bytes)
コードを見る
bにあながあく
だれかがハサミで
だれかがハサミで
2ねんまえかのことでした
だれかがハサミで
0ねんまえかのことでした
とりはとばずねむる
そらのうえからaがたつ
1ねんまえかのことでした
だれかがハサミで
だれかがハサミで
aにあながあく
1ねんまえかのことでした
タイムラインをちょんぎった
そして
bがつちからはえてくるんだ
LABEL b # labelB
GETC # 1文字目を読み込み
GETC # 2文字目を読み込み
FOCUS 2 # address 2に
GETC # 3文字目を読み込み
FOCUS 0 #
SUB # *0 = *1 - *2(2文字目と3文字目の文字コードを引き算)
JZ a # *0 == 0(2文字目と3文字目が一致)ならばlabelAへ
FOCUS 1 # address 1に
GETC #
GETC # 5文字目を読み込み
LABEL a # labelA
FOCUS 1 # address 1の文字を
PUTC # 出力
GETN # 残りの数字を読む
JGZ # 読み込んだ数字は0ではないので常に最初に戻る
Japt (@dnek_, 12 bytes)
Ëg2*DbDg2}R
JS Code
U.m(function(D, E, F) { return D.g(2 * D.b(D.g(2))) }, R)
#find-l2を使った。
}
は直前の)
もまとめて閉じられて便利。
}
を最大限利用するために2*
を先に置いている。
Ë
などのUnicode shortcutsは1字2Bなので3字以上のやつを適宜使う。
Jelly (@kotatsugame_t, 28 bytes)
fØDm0fƝFḣ1
ɠƈпç€
1行目が行を文字列として受け取って答えの文字を返す1引数関数(monad
)で、2行目は入力を受け取って1行目をmap
している。
まず1行目。引数(文字列であるとする)をx
と置いて、
-
x f ØD
(フィルター)、x
からØD
='0123456789'
以外の文字を削除 -
x m 0
、x m _
は右項が0
のときx ++ reverse x
になる。 -
x f Ɲ
、Ɲ
はリストの隣接する要素の各組に対して関数を作用させる。今回は2引数関数f
(フィルター)を作用させる。x f y=if elem x [y] then [x] else []
であり、zipWith (f) x (tail x)
みたいになる。 -
x F
、flatten
。fƝ
でリストのリストになっていたのを、ただのリストにする。 -
x ḣ 1
、x[:1]
→[ head x ]
相当。head
そのものであるḢ
を使わなかった理由は後述する。
結局、「文字列を受け取って空白を削除し、隣に同じ文字が存在する文字で最も早く出現するものを返す」関数となる。
どこの文字も隣り合っていなかった場合困るので、x ++ reverse x
をしている。
2行目。
-
ɠ ƈ п
、while
ループで、ループしながら実行した結果の値を蓄積する。ƈ
(1文字読み取り)が条件式で、EOF
までɠ
(1行読み取り)を実行し続ける。 -
ç €
、€
はmap
相当。ç
は直前の行で定義されたmonad
を表す。while
で得た入力の行ごとのリストに対して適用している。
while
ループの条件式をƈ
にしたのが影響して、各行、先頭の1文字が抜けた状態で得られる。普段なら困るところだが、今回ばかりはこのコードの正当性に一役買っている。(先頭2文字が同じ場合の誤検出がない)
これで万全といいたいところだが、なぜか(これは本当にわからない)while
ループで得たリストの先頭にNone
が存在するように見える。(見えているだけでリストの外にあるのかも)
このままmap
したとき、上のhead
のところでḢ
を使うと、None
に対しては(なぜか)0
が返ってきてWA
になる。
ḣ1
としてリストのまま返してもらうことで、None
に対してはちゃんと空リストが得られるため、AC
。
ちなみにwhile
ではなくrepeat
のループもあったんですが、そっちはなぜか入力をeval
しようとしてエラーが出ました。なにもわからない
jq (@drafear, 23 bytes)
.[index(.[2:3])*2:][:1]
#find-l2 です。
scan("(.)(\\1 | .)")[1]
100% AC する。\
は \\
にしないとエラーになる。
Lua (@dnek_, 46 bytes)
::a::print(io.read():match"(.) ?%1"or 1)goto a
嘘解法。
各行について所謂(.) ?\1
にマッチしたものを返し、無ければ1を返す。
先頭2文字が一緒のときにマッチしてしまうのでAC率48%くらい。
string.match(str, ptn)
などは、str:match(ptn)
のような糖衣構文で書ける。
引数が文字列リテラルのみの場合()
を省略してstr:match".*"
と書ける。
ラベル::a::
に対してgoto a
で再帰する。
入力が尽きると異常終了する。
Make (@nakario_jp, 130 bytes)
a:
$(foreach s,$(subst $% ,X,$(STDIN)),$(word 1,$(foreach i,1 2 3 4 5 6 7 8 9,$(if $(findstring $i$i$i,$(subst X,$i,$s)),$i)) 1))
概要:真ん中のスペースを1~9で置き換えてみて、3連続を見つけたらその数字、なかったら1を出力。
- スペースはforeachでデリミタ扱いされるので一旦Xで置き換える必要があった。
- スペース単体は直接記述できないので、評価すると空文字列になる
$%
を使う。 -
firstword
よりword 1
。 -
word 1
のおかげで複数にマッチした場合と一つもマッチしなかった場合の処理を一本化できた。 - (@kotatsugame_t 氏より)
a:\n\t
をa:;
にすると1Byte短縮。
Mines (@dnek_, 122 bytes)
****....
*......*
*******.
5,0
7,2
3,1
2,1
1,1
4,1
4;1
4,0
1;1
7,0
4;1
7,2
4;1
4,1
4;0
7,2
5,1
1;1
4;1
4;1
2;1
5;1
4;1
0,0
以下、各入力行をABCSDEFN
とする。
解釈(1l
は数字1のマスを左クリック)
01234567
0 ****1011
1 *765433*
2 *******2
5,0 0l push(count) #6マス開くのでpush 6
7,2 2l push 2
3,1 5l push 5
2,1 6l push 6
1,1 7l push 7 #ここまででコマンドに必要なマスを一通り開けておく
4,1 4l subtract #6-7=-1を作っておく
4;1 4r in(c) #A; 入力が無いときは無視
4,0 1l positive #A -> 1, -1 -> 0
1;1 7r skip #入力があれば1行skip
7,0 1l push 1 #全マスが開かれて終了
4;1 4r in(c)
7,2 2l dup
4;1 4r in(c)
4,1 4l subtract
4;0 1r not
7,2 2l dup
5,1 3l add
1;1 7r skip #(!(B-C)*2)行skip
4;1 4r in(c)
4;1 4r in(c)
2;1 6r out(c)
5;1 3r in(n)
4;1 4r in(c) #改行まで入力を取り切る
0,0 *l over(l) #ゲームオーバー(盤面リセットして続行)
Minesは最初の*
と.
の長方形をマインスイーパーの盤面に見立て、以降の操作を上から繰り返し、対応する命令を実行する。
基本的に開いていないマスを開けるとpushになり、開いているマスを(左|右)クリックすると様々な命令が実行される。
ロジックも然ることながら、盤面もgolfのやり甲斐があると思う。
Minesは私がPietの影響を受けて作った言語なので、ランタイムエラーが起きても異常終了せずに無視し、条件(すべての(安全|地雷)マスをそれぞれ1回以上開いたことのある状態になる)を満たすまで終わらない。
そこで、先に-1
を作っておき、A
を入力から取れればpositive
コマンドで1
が置け、なければ(入力最終行であれば)0
になって終了判定ができる。
続いてB==C
ならそのまま出力し、そうでなければ2回入力を取りD
を出力する。
残っているゴミがSDEFN
かEFN
なので、in(n)
からのin(c)
ですべて取り切る。
あとはゲームオーバーにして盤面を戻してから(スタックはそのまま)最初に戻る。
ちなみにPietも似たような解法である。 MinesはPietよりも豊富なコマンドがあり書きやすいので是非使ってみて欲しい。 dnek/mines-esolang
moo (@drafear, 24 bytes)
2{iiiditidi=r-*+oii2c}2c
入力行 xxA BCx
に対して A + (B=C)*(B-A)
を計算しています。
Node.js (@drafear, 75 bytes)
console.log(...(""+require("fs").readFileSync(0)).match(/ .|(?<=(.))\1 /g))
各行について答えのみにマッチするよう正規表現を工夫し、マッチした全ての部分文字列を ...
で展開して console.log
に渡しています。
コンテスト終了後に他のチームの文言のコードを見て気づいたのですが、""+
を 0+
に置き換えるとさらに1バイト縮みますね。
oh (@drafear, 85 bytes)
while 1:(method(a b c d e:g)=:if(eq $b $c)(echo $c)else:echo $e)@(""::split:readline)
各行 XXX XXX\n
の先頭5文字を変数 $a
,$b
,$c
,$d
,$e
に分割代入し、$b = $c
なら $c
, そうでなければ $e
を出力しています。
$g
は XXX XXX\n
の6文字目以降を可変長引数の指定捨てるためのものです。
if
を三項演算子のように使って echo:
をまとめたかったのですがうまくいきませんでした。
O (@gh_end_, 35 bytes)
48* {M]1&\2&\@@.@= {o;} {\3&o;;}?}d
if(a[1]==a[2]) then a[1] else a[3]
を素直に実装しました。
最初の M]
はマクロ( i~]
かなにか)で、文字列を配列に格納しています。
配列があったり機能的にはかなりリッチです。赤のスタートマスの直上にあったので取り合いにならなかったんですが、もっと縮む余地は確実にあると思います。
documentが途中で終わっている&&古いものもかなり抜けがあるためあまり役に立たず、苦労しました。最終的に処理系の実装を見て &
(配列のランダムアクセス)を見つけられたのでラッキーでした。
Perl (@kotatsugame_t, 22 bytes)
/(.) ?\1/,print$1for<>
正規表現で2文字連続している文字をとってくる。(これは先頭2文字が同じ場合落ちるため嘘)
for
ループ内ではいちいち$1
がリセットされたりしないので、マッチに失敗した場合は先に出力した文字をそのまま出力する。
では1行目が123 456
みたいなので、どこにも隣り合う文字がなかったらどうなるか?……落ちる。嘘だらけ
PHP 7.0 (@n4o847, 50 bytes)
<?php while($_=readline())echo$_[$_[1]-$_[2]?4:2];
初期に愚直に書いたのにその後縮まなかった。
Piet (@dnek_, 91 bytes)
以下、各入力行をABCSDEFN
とする。
解釈
greater #1行目: エラー(無視), 2行目以降: 前行の(B==C)?(ord(B)>DEF):(ord(S)>EF)
in(c) #A; 入力が無いときは無視
not #A -> 0, greaterの値 -> 1(?)
switch
1:
exit #左下
in(c)
dup
in(c) #右上
sub
not #最初のswitchが働いていると、notへ行かずに右下で終了する
switch #!(B-C)
1:
dup
0:
in(c)
in(c)
out(c)
in(n)
in(c)
switch #必ず'\n'(10)なのでCCは変わらない
AC率84%強の嘘解法。
基本的なフローはコード上を時計回りに回るだけである。
Aが取れればswitchに0が渡って続行、取れなければ(入力最終行であれば)多くの場合1が渡って終了する。
しかし、最終行でB!=C
かつEF < 32
の場合は一旦0が渡り、残ったゴミによってWAかTLEか運良くACになる。
続いてB==C
ならそのまま出力し、そうでなければ2回入力を取ってからD
を出力する。
残っているゴミがSDEFN
かEFN
なので、in(n)
からのin(c)
ですべて取り切る。
最後にPietの都合上左上のcodelに戻る際にswitch
が実行されるがこれはN
を取るためただのpop
となる。
ちなみに、Minesも似たような解法である。
また、pngかgifで短い方を提出するが、今回のようにgifの場合はgifsicle --batch -O3 hoge.gif
などで最適化してから末尾2bytesを削って提出する(画素数が極端に小さい場合ppmも検討)。
確実解 (同bytes)
後で気づいたが、最初(greater
)と最後(switch
)の命令がそれぞれnot
とdup
になるように左上のコーデルを描き換えると入力最終行でswitch
に必ず0が渡るようになり、必ずACする。
gifで書き出したところ提出解と同bytesだった。
いずれにしろ、今回も対戦相手が現れなくて寂しかったので、是非Pietronを使って挑戦してみて欲しい。
PowerShell (@eto_nagisa, 29 bytes)
$input|%{$_[$_[4]-$_[5]?2:4]}
プロデル (@dnek_, 75 bytes)
『a=コンソールから受け取
aの(aからaの3番目を探*2-1)番目を報告』を32回繰り返
実際はShift JISで提出している。
#find-l2を使った。
ただしプロデルのindexは1-basedなので-1
する。
また、プロデルには様々な代替表現が隠されている(リファレンス読み飛ばしてるだけかも)ので、適当に言い換えると通じることがある。
例えば、リファレンスには「n文字目」と書いてあったが「n番目」に言い換えた。
Pure Folders (@sitositositoo, 147456 bytes)
コード(ディレクトリ構造)を見る
source
├─00 dec
│ ├─1 dec
│ │ ├─1
│ │ └─2
│ ├─2 int
│ └─3 {0}
├─10 let
│ ├─1 let
│ │ ├─1
│ │ ├─2
│ │ └─3
│ ├─2 {0}
│ └─3 0x20
│ ├─1 literal
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ ├─2 int
│ └─3 0x20
│ ├─1 0x2
│ │ ├─1 0
│ │ ├─2 0
│ │ ├─3 1
│ │ │ └─1
│ │ └─4 0
│ └─2 0x0
│ ├─1 0
│ ├─2 0
│ ├─3 0
│ └─4 0
├─20 dec
│ ├─1 dec
│ │ ├─1
│ │ └─2
│ ├─2 char
│ │ ├─1
│ │ ├─2
│ │ └─3
│ └─3 {1}
│ └─1
├─30 dec
│ ├─1 dec
│ │ ├─1
│ │ └─2
│ ├─2 char
│ │ ├─1
│ │ ├─2
│ │ └─3
│ └─3 {2}
│ ├─1
│ └─2
├─40 dec
│ ├─1 dec
│ │ ├─1
│ │ └─2
│ ├─2 int
│ └─3 {3}
│ ├─1
│ ├─2
│ └─3
└─50 while
├─1 while
│ └─1
├─2 {0}g0x0
│ ├─1 greater
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ ├─5
│ │ ├─6
│ │ └─7
│ ├─2 {0}
│ │ ├─1 variable
│ │ └─2 {0}
│ └─3 0x0
│ ├─1 literal
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ ├─2 int
│ └─3 0x0
│ └─1 0x0
│ ├─1 0
│ ├─2 0
│ ├─3 0
│ └─4 0
└─3 {}
├─10 input
│ ├─1 input
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ └─2 {1}
│ └─1
├─20 input
│ ├─1 input
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ └─2 {1}
│ └─1
├─30 input
│ ├─1 input
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ └─2 {2}
│ ├─1
│ └─2
├─40 let
│ ├─1 let
│ │ ├─1
│ │ ├─2
│ │ └─3
│ ├─2 {3}
│ │ ├─1
│ │ ├─2
│ │ └─3
│ └─3 0x0
│ ├─1 literal
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ ├─2 int
│ └─3 0x0
│ └─1 0x0
│ ├─1 0
│ ├─2 0
│ ├─3 0
│ └─4 0
├─50 if
│ ├─1 if
│ ├─2 {1}=={2}
│ │ ├─1 equal
│ │ │ ├─1
│ │ │ ├─2
│ │ │ ├─3
│ │ │ ├─4
│ │ │ ├─5
│ │ │ └─6
│ │ ├─2 {1}
│ │ │ ├─1 var
│ │ │ └─2 {1}
│ │ │ └─1
│ │ └─3 {2}
│ │ ├─1 var
│ │ └─2 {2}
│ │ ├─1
│ │ └─2
│ └─3 {}
│ ├─10 print
│ │ ├─1 print
│ │ │ ├─1
│ │ │ ├─2
│ │ │ ├─3
│ │ │ └─4
│ │ └─2 {1}
│ │ ├─1 var
│ │ └─2 {1}
│ │ └─1
│ └─20 let
│ ├─1 let
│ │ ├─1
│ │ ├─2
│ │ └─3
│ ├─2 {3}
│ │ ├─1
│ │ ├─2
│ │ └─3
│ └─3 0x1
│ ├─1 literal
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ ├─2 int
│ └─3 0x1
│ └─1 0x1
│ ├─1 0
│ ├─2 0
│ ├─3 0
│ └─4 1
│ └─1
├─60 if
│ ├─1 if
│ ├─2 {3}==0
│ │ ├─1 equal
│ │ │ ├─1
│ │ │ ├─2
│ │ │ ├─3
│ │ │ ├─4
│ │ │ ├─5
│ │ │ └─6
│ │ ├─2 {3}
│ │ │ ├─1 var
│ │ │ └─2 {3}
│ │ │ ├─1
│ │ │ ├─2
│ │ │ └─3
│ │ └─3 0x0
│ │ ├─1 literal
│ │ │ ├─1
│ │ │ ├─2
│ │ │ ├─3
│ │ │ ├─4
│ │ │ └─5
│ │ ├─2 int
│ │ └─3 0x0
│ │ └─1 0x0
│ │ ├─1 0
│ │ ├─2 0
│ │ ├─3 0
│ │ └─4 0
│ └─3 {}
│ ├─10 input
│ │ ├─1 input
│ │ │ ├─1
│ │ │ ├─2
│ │ │ ├─3
│ │ │ ├─4
│ │ │ └─5
│ │ └─2 {1}
│ │ └─1
│ └─20 print
│ ├─1 print
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ └─4
│ └─2 {1}
│ ├─1 var
│ └─2 {1}
│ └─1
├─70 input
│ ├─1 input
│ │ ├─1
│ │ ├─2
│ │ ├─3
│ │ ├─4
│ │ └─5
│ └─2 {3}
│ ├─1
│ ├─2
│ └─3
└─80 let
├─1 let
│ ├─1
│ ├─2
│ └─3
├─2 {0}
└─3 {0}-1
├─1 sub
│ ├─1
│ └─2
├─2 {0}
│ ├─1 var
│ └─2 {0}
└─3 0x1
├─1 literal
│ ├─1
│ ├─2
│ ├─3
│ ├─4
│ └─5
├─2 int
└─3 0x1
└─1 0x1
├─1 0
├─2 0
├─3 0
└─4 1
└─1
Pure Foldersはディレクトリ構造のみに意味を持たせた言語です。自分で分かるようにフォルダ名をつけたので、やってる処理はたぶん見たら分かると思います。{x}
は変数x(x=0,1,2,...)、{}
はコードブロックを表します。
提出がtarアーカイブ形式で、フォルダ名を縮めてもサイズは縮まりませんでした(属性値が固定長なんだと思います)。縮めるために(?)したことは以下の通り。
- コードを書く
- コンパイラがバグってたので直す
- コンパイラの言語仕様が実装しきれてない部分を実装する
- ExpressionsのEqual To, Greater Than, Less Thanが未実装でした…
- コンパイラの無理があった仕様を変更する
- もとはint float string char全ての変数への入力がC#の
Console.Readline()
に変換されていたが、うまくいくはずもない。仕方ないので、変換先をC++に変更してstd::cin/cout
を使うようにしました。
- もとはint float string char全ての変数への入力がC#の
- なるべく番号の小さい変数を使い回す(宣言が減る&変数を指定するのに必要なフォルダ数が減るので)
次回までに公式で治ってるといいな。
Pxem (@drafear, 29 bytes)
.i.x.i.c.i.z.i.iXX.a.o._.i0.a
Pxem には branch を while で代替する必要があります。
.z.i.iXX.a
がそれをしている部分で、
if pop() != pop() then
push(getchar())
push(getchar())
endif
に相当します。XX
はループ継続条件を満たさなくするための2バイトです。
Pxem は0除算でしかエラー落ち(強制終了)しないので、ループ継続条件を使ってEOF(-1)を読み込んだら終了するようにしています。
Pyramid Scheme (@k_hanazuki, 437 bytes)
^
/l\
/oop\
^-----^
/]\ ^-
^---^ -^
/ \ /a\ -^
/set\--- -^
^-----^ /?\
/a\ /l\ ^---^
--- /ine\ ^- / \
-----/!\ /out\
^--- -----^
/?\ / \
^---^ /arg\
^- / \ ^-----^
/=\ /out\ /a\ /4\
^---^----- --- ---
/ \ -^
/arg\ -^
^-----^ -^
/a\ /1\ / \
--- ---/arg\
^-----^
/a\ /2\
--- ---
大きいピラミッドを建設するよりは,最小限の大きさのピラミッドに大きさ0のピラミッド
^
-
を繋げていく方が配置の自由度を得られる.ただし^
が別のピラミッドの頂点に接してしまわないよう気をつけなければならない(うまく辺で接するように位置をずらすと収まりがよくなる).
桁数と行数を変えたいくつかの配置から最良のものを選んだ結果,あまりきれいな三角形にはならなかった.部分式をまとめられる言語仕様を活かせたのはよかった.
- ピラミッド同士の書き換えを多数行うことになったが、Vim の櫛形選択機能 (:h CTRL-V) が便利だった.
- 文字列をスペースを含め読み込むためにはドキュメントで案内されている
(# line)
ではなく(" line)
を用いる. そうしないと float 型に変換されるので、文字列として扱うと "654[1]" が 654というfloat型の一部を取り出すことになってしまう. - do notation のような構造がないので、複数のコマンドを順序をもって実行するには二分木を繰りかえす. 幸い先行評価などではないので
]
(] a b)
->b
のときもa
が評価されることが使える.
Python 3 (@nonylene, 41 bytes)
for x in open(0):print(x[x[1]==x[2]or 4])
-
x[1] == x[2]
=> True or False -
x[1] == x[2]or 4
=> True or 4 - x の 1 か 4 を取ってくる
まず正規表現は import re
しないと使えないので厳しい。そうすると str からどこを取ってくるかという話になる。
==
より優先順位の低い演算子は and や or ぐらいなので、括弧を節約するために *
や +
ではなく or を使った。
以下は Python 3.8 で動く別解。
while x:=input():print(x[x[1]==x[2]or 4])
قلب (@n4o847, 273 bytes)
(حدد ئ المدخلات)(كرر ٣٢(لامدا()(حدد ب(رأس(عدل ئ(ذيل ئ))))(حدد ا(رأس(عدل ئ(ذيل ئ))))(حدد د(رأس(عدل ئ(ذيل(ذيل ئ)))))(عدل ئ(ذيل(ذيل(ذيل(ذيل ئ)))))(قول(إذا(يساوي؟ ب ا)ا د))))
ドキュメントもなく公式サイトもアラビア語なので 第5回東大京大コードゴルフ大会 Writeup や実装を読んだ。 言語名はアラビア語で「心臓」を意味し、 Qalb [ʔalb] と読むらしい。 翻訳すると以下のようになる。
(define s input)
(call-n-times 32 (lambda ()
(define a (car (set! s (cdr s))))
(define b (car (set! s (cdr s))))
(define c (car (set! s (cdr (cdr s)))))
(set! s (cdr (cdr (cdr (cdr s)))))
(say (if (eq? a b) b c))))
Racket (@k_hanazuki, @kotatsugame_t, 78 bytes)
#!racket
(let m()(printf(car(regexp-match #px" .|(.)(?=\\1)"(read-line))))(m))
これは嘘解法で,元は次のような79B解であった:
#!racket
(let m()(printf(car(regexp-match #px" .|(.)(?=\\1 )"(read-line))))(m))
Node.jsで使われていた正規表現#px" .|(?<=(.))\\1 "
を改変したものだが,1B短くなっている.Racketの正規表現エンジンは最左のマッチを優先するようでこの正規表現を使える.
- 名前付きletによるループ
- 文字列を表示する場合は,displayよりprintfが短い
などは典型的テクニック.
Rail (@n4o847, 51 bytes)
$'main'{niam}i-iio@
--iii(!!)()q<
{niam}iiiiio()-@
スタック操作がなく、代わりに変数や関数を使う。 敷き詰め方は 第3回コードゴルフ大会 WriteUp を参考にした。 無限ループだが入力で落ちる。
ReasonML (@ten986, @kurgm, 93 bytes)
[%bs.raw{|(""+require("fs").readFileSync(0)).replace(/(.?)\1 ./g,(x,y)=>console.log(y||x))|}]
JSゲーですね。青チームの文言の解にかなり近いですが、青チームのを見て、そうか、""+
じゃなくて+0
でいいのかー、そして、x[1]
でいい(→(x,y)
がx
だけでいい)のかーと思いました。(@kurgm)
公式ドキュメントを見ます。jsが動く気になります。動きます。やったね。
https://reasonml.github.io/docs/ja/interop
[%bs.raw{| ... |}]
の中には一文しか書けないみたい?なので、例えば変数を使いたい時はlet x=0;[%bs.raw{| ... |}]
のようにする必要があるもよう。(@ten986)
Ring (@kotatsugame_t, 39 bytes)
for x in 1:32{get l?l[5-(l[2]=l[3])*2]}
最初は入力がGive l
、出力がsee l[]
だった。これでもだいぶ短い(というかヘンなキーワードの)言語だなあと思っていたが、過去の大会のWriteupを見るとこうなった。
入力がget l
、出力が?l[]
である。さらに等号が1文字であることを考慮してl[3+(l[2]!=l[3])*2]
ではなくl[5-(l[2]=l[3])*2]
とした。
Ruby 2.7.1 (@kcz146, 28 bytes)
loop{$><<gets[/(.)\1 /?2:4]}
Rust (@moratorium08, @_hiromi_mi, 106 bytes)
use std::io::*;fn main(){loop{let b=&mut[0;8];stdin().read(b);print!("{}",b[if b[1]==b[2]{2}else{4}]-48)}}
バッファサイズを8バイトにすることで、最大8バイトreadしかできなくする(8バイト読むことは保証されないはずだが、読んでくれる)。 readはEOF等で失敗してもResultでErrorを返すだけなので、unwrapしないと、panicさせられず無限ループしてしまいそうになる。ここで、esolang-boxでのコンパイルは最適化をかけるオプションをつけていないので、integer overflowがpanicになることに注意する。 するとEOFを迎えると、bが[0;8]のまま更新されず、print!の中のb[....] - 48が0 - 48になり、rustのバイト列は[u8]なのでこれはinteger overflowになる。よって適切に止まる(by moratorium08)
最後に loop{}
と main()
再帰呼び出しが同一文字数であり, Rust 言語では ;
をつけないと return x
と同じ意味になるので print!
の ;
を省いた. (by hiromi_mi)
Snowman (@drafear, 35 bytes)
~:vgdU(4AaL0AaG""AoR(aJ1aG16aAsp;bD
試行錯誤しているうちに unique と join を使う斬新なロジックになってしまいました。
擬似コード7~10行目のコメントが全てです。
Snowmanのメモリ配置(下記擬似コード内変数名と対応)
abc
d e
fgh
擬似コード(active/inactive関係省略)
while True:
a = readline() # vg
b = a # dU
# "xAB Cxx" -> "AB "
b = b[1:4] # (4AaL0AaG
b = unique(b) # ""AoR
# concat(a[0], b, a[1], b, ..., a[6])
# result: "12 22 22 2 42 52 6" when a = "122 456", b = "2 "
# result: "123 223 323 23 423 523 6" when a = "123 456", b = "23 "
# ^16th
a = join(a, b) # (aJ
a = a[16] # 1aG16aA
putchar(a) # sp
SQLite3 (@kotatsugame_t, 129 bytes)
with f(x)as(select""union select x+8 from f limit 32)select(select substr(v,x+4-(substr(v,x+2,1)=substr(v,x+3,1)),2)from i)from f
過去の大会のWriteupからwith f(x)as(select""union select x+8 from f limit 32)
を拾ってきた。with recursive
とあったが、recursive
はいらないらしい。
substr
を使っているので、3 or 5
文字目を出力するのではなく[3,4] or [4,5]
文字目を出力することにするとインデックスをずらす*2
がいらなくなる。
(?i:standback) (@n4o847, 38 bytes)
g/.+(.)(\1| ).+/%1/m/[\s\S]*/S//%%&///
Vim や V (Vim) や Bash (busybox) を移植しただけなのでそちらを参照。 安全地帯だったのであまりゴルフしていない。↓が出されなくてよかった~。
勝手ながら緑チームの解(手が届かず提出できず)をおいておきます。(@kurgm)
g/ (.)\1/%1 /w/. /s/ //S/$/%%&//
ABC DDE
みたいな行をABCD E
に一括置換した後、(スペース直前の文字+スペース)をAの後ろに足していきます。
Starry (@yamayu832, 27 bytes)
` , , + , * ' .,' ` , , .,'
デコンパイル結果
label, 0 # label0, ループ用
char_in # 1文字読んで破棄(スタックには残るが使わずに放置)
char_in # 2文字目を読む
dup # 前半を出力するときのために2文字目を複製
char_in # 3文字目を読む
substract # 2文字目と3文字目を文字コードで引き算
jump, 1 # 前の結果が0でない時(つまり2文字目と3文字目が一致しないとき)label1にジャンプ
char_out # 上で複製してあった2文字目を出力
num_in # 残りの文字を改行まで読み込み破棄(スタックには残るが使わずに放置)
jump, 0 # 最初に戻る
label, 1 # label1, 2文字目と3文字目が一致しなかったとき
char_in # 4文字目の空白を読んで破棄(スタックには残るが使わずに放置)
char_in # 5文字目を読み込む
char_out # 5文字目を出力
num_in # 残りの文字を改行まで読み込み破棄(スタックには残るが使わずに放置)
jump, 0 # 最初に戻る
素直なコード.短くなるように工夫したことは,残りのいらない文字をchar_inではなくnum_inで読み込むことくらいだと思う.
Stuck (@n4o847, 14 bytes)
"s__2&I2*&"32V
#find-l2 を使った嘘。 ドキュメントにない命令がいくつかあるので実装を読むことを推奨します。 以下詳細。
"
s raw_input()
_ 複製
_ 複製
2 & s[2]
I s.find(s[2])
2 * s.find(s[2]) * 2
& s[s.find(s[2]) * 2]
"
32 V 32 回繰り返す
最後にメインスタックが出力される
Tetris (@sio_tet, 145 bytes)
10:
I:AA<<
J:>>
Z:>>>>
I:>>
S:A>>>>
I.<<
I:<<
I.B<<<<
I.<
0:
I:B<<<<
I:B<<<
L.A<<<<
L.A<<
L:B<<
J.AA>
Z:B>>>>>
J:>>>
I:A>>
I:A
I:A>>>
I.A>>>>
O:<
https://sagisawa.0am.jp/tetris/ で実行出来ます。実行したり読んだりしたら多分分かります。
実装メモを解説向きに書き直し。
32:
IISI=JB
L[0-2]まで取ってL[1]をストアして、L[2]と比較して一致したら0にジャンプ
Lを妨害するものを仕込む(工夫ポイント)
0:
II{L}OIIIJB
L[3-4]を取って出力し、L[5-7]を取ってジャンプする
工夫ポイント: L[1]==L[2]の時にL[1]を出力、そうでなければL[4]を出力するために、L[4]を入力受けてから、必要な時だけL[1]をロードしました。
感想としては1回目でB使ってるのは甘えかもしれないけどチームが強すぎて塗りに来れる人がいませんでした。
TeX (plain) (@domperor, 122 bytes)
Final code: (Note that there are some invisible characters here)
\def#1#2#3#4#5#6{\if#2#3#3\else#4\fi}\loop\ifnum\fam<32\read0to~\immediate\write0{\expandafter~}\advance\fam1\repeat\end
Hello, I am @domperor, once being a manager of the University of Tokyo TeX club. There is little information available about golfing in TeX; I am explaining a few tips here.
The initial code written by one of our honorable partners @kurgm was:
\def\p#1#2#3#4#5#6{\if#2#3#3\else#4\fi}\newcount\k\loop\ifnum\k<32\read0to\i\immediate\write0{\expandafter\p\i}\advance\k1\repeat\end
This is already quite concise, without having any unnecessary spaces or redundancy.
-
\p
is defined(\def{...}
) as taking following 6 characters(#1#2#3#4#5#6
) and comparing if(if
) the character codes of the second char(#2
) and the third char(#3
) are the same. If they are the same, print the third one(#3
), and if not(\else
), print the fourth char(#4
). -
\newcount\k
opens a new counter register numbered\k
; it is necessary in following loop. -
\loop\ifnum\k<32...\advance\k1\repeat
enables you to loop the code inside, exactly 32 times. Note that TeX does not pardon infinite loop in golfing settings, because it splits its error messages into standard output. You know, normal plain TeX users do not use standard output as the final output; what they want is .dvi output or printed output on your paper. Standard output is easily contaminated with warnings and errors. TIPS: always write a correct code without generating any error message. -
\read0to\i
reads a line from standard input, and put it into a control sequence\i
. Note that usually standard input is numbered-1
, but in this golfing server the input stream is changed to0
for stability. -
\immediate\write0{...}
prints the things inside{...}
to standard output. Note that usually standard output is numbered16
, but it is just a custom; golfers should always use0
because it also works. - In
\expandafter\p\i
,\p
takes the six characters inside\i
and cooks them. You definitely need\expandafter
, because without it\p
will find only one parameter block\i
.\i
needs to be expanded into six characters before\p
is read by interpreter. -
\end
is for ending the code; it is necessary because without it you will get error message in standard output.
Okay, you can further shorten this neat code using following 2 techniques.
- Use counter named
\fam
: this is one of the counters that are already defined and with the shortest name; you do not need to use\newcount
to define new counter. This counter is originally for containing the number of current font family. In case you are not printing any characters on your .dvi file (namely, on your paper), the number contained in the\fam
is the initial value0
. TIPS: another preset counters with 4 bytes are\day(=date)
and\mag(=1000)
. - Use active characters: characters with category code 13 are called "active characters", which are definable just as the same as control sequences like
\p
. It is famous that~
(tilde) is an active character. Few people know, however, that ASCII12 char(invisible) is also an active character. Substituting\p
to ASCII 12 and\i
to~
saved 4 bytes in this case.
Thank you, this is all for today! Happy TeXing!
(writeup by hiromi_mi) 今回唯一解かれなかった言語. 言語仕様そのものの理解が難しい. 以下に議論を残す.
動作であるが
- 深さ優先探索 (スタックに置く) によりソースコードをグラフ形式に変換.
- 引数を Wiki にある規則に従いグラフ形式に変換し,
1
以下で扱えるように. - main loop として グラフのノード代入 or ノード追加 or ノードループの命令を実行.
-
1
以下にあるものを再びstring 形式に変換.
途中入力などにアクセスできないのではとの意見があったが、
A1
などに入るのは「ノードそのもの」ではなく「ノードのparsing address」が入るので同一構造を構築すればアクセス可能な模様.
hiromi_mi は digit の例を理解するところで時間切れ. satos さんによるとアセンブラを書けばよいとのこと.
なお、実装はこちら: https://github.com/Hakerh400/esolangs/blob/master/src/langs/transceternal/index.js
解いたのでそのソースコードおよび生成スクリプトを掲載する.
コードを見る (1598 Bytes)
0 1 2 2 2 3 4 5 3 6 3 7 3 8 3 9 3 A 3 B 3 C 3 D 3 E 3 F 3 G 3 H 3 I 3 J 3 K 3 L 3 M 3 N 3 O 3 P 3 Q 3 R 3 S 3 T 3 U 3 V 3 W 3 X 3 Y 3 Z 3 10 3 11 3 12 2 12 13 3 2 13 4J 4K 4L 4L 4L 4M 4N 35 3 36 3 37 3 38 3 39 3 3A 3 3B 3 3C 3 3D 3 3E 2 3E 2X 3 2Y 3 2Z 3 30 3 31 3 32 3 33 3 34 3 35 4E 4F 4G 4G 4G 4H 4I 34 2W 3 2X 49 4A 4B 4B 4B 4C 4D 33 2V 3 2W 44 45 46 46 46 47 48 32 2U 3 2V 3Z 40 41 41 41 42 43 31 2T 3 2U 3U 3V 3W 3W 3W 3X 3Y 30 2S 3 2T 3P 3Q 3R 3R 3R 3S 3T 2Z 2R 3 2S 3K 3L 3M 3M 3M 3N 3O 2S 30 3F 3G 3H 3H 3H 3I 3J 2Q 3 2R 2Y 14 1K 2 1J 15 2 16 2 17 3 18 3 19 3 19 1A 3 1B 3 1C 3 1D 3 1E 3 1F 3 1G 3 1H 3 1I 3 1I 4O 6K 2 6J 4P 3 4P 4Q 3 4R 3 4S 3 4T 3 4U 3 4V 3 4W 3 4X 3 4Y 3 4Z 3 50 3 51 3 52 3 53 3 54 3 55 3 56 3 57 3 58 3 59 3 5A 3 5B 3 5C 3 5D 3 5E 3 5F 3 5G 3 5H 3 5I 3 5J 3 5K 3 5L 3 5M 3 5N 3 5O 3 5P 3 5Q 3 5R 3 5S 3 5T 3 5U 3 5V 3 5W 3 5X 3 5Y 3 5Z 3 60 3 61 3 62 3 63 3 64 3 65 3 66 3 67 3 68 3 69 3 6A 3 6B 3 6C 3 6D 3 6E 3 6F 3 6G 3 6H 3 6I 3 6I 6L 73 2 72 6M 2 6N 2 6O 3 6P 3 6P 6Q 2 6R 2 6S 3 6T 3 6U 3 6V 3 6W 3 6X 3 6Y 3 6Z 3 70 3 71 3 71 74 7J 2 7I 75 2 76 2 77 3 78 3 79 3 79 7A 2 7B 2 7C 2 7D 3 7E 2 7F 2 7G 2 7H 2 7H 7K 7Z 2 7Y 7L 2 7M 2 7N 3 7O 2 7P 2 7Q 3 7Q 7R 2 7S 2 7T 3 7U 2 7V 2 7W 3 7X 3 7X 8K 8L 8M 8M 8M 8N 8O 8A 2 8B 2 8C 2 8C 8D 2 8E 2 8F 3 8G 2 8H 2 8I 3 8J 2 8J 80 89 2 88 81 3 81 82 2 83 2 84 3 85 2 86 3 87 3 87 2 4J 1L 2P 2 2O 1M 2 1N 2 1O 3 1P 3 1Q 3 1Q 1R 3 1S 3 1T 3 1U 3 1V 3 1W 3 1X 3 1Y 3 1Z 3 20 3 21 3 22 3 23 3 24 3 25 3 26 3 27 3 28 3 29 3 2A 3 2B 3 2C 3 2D 3 2E 3 2F 3 2G 3 2H 3 2I 3 2J 3 2K 3 2L 3 2M 3 2N 3 2N 4O 1L 1L 1L 1L 1L 1L 1L 1L
生成スクリプトを見る
# Transceternal Esolang Codegolf Contest #6 生成Script by @_hiromi_mi
# SPDX-License-Identifier: CC0-1.0
from string import digits, ascii_uppercase
import sys
from typing import Dict
def tob36(val):
_d36 = digits + ascii_uppercase
if (val <= 35):
return _d36[val]
else:
return tob36(val // 36) + _d36[val%36]
def retain_node(graphs):
_id = tob36(len(graphs))
graphs[_id] = ["-1", "-1"]
return _id
def new_node(graphs, left, right):
_id = retain_node(graphs)
graphs[_id][0] = left
graphs[_id][1] = right
return _id
def construct(graphs, node_str, B0, B1):
node = ''
top = ''
for a in node_str:
newnode = retain_node(graphs)
if node:
graphs[node][1] = newnode
else:
top = newnode
node = newnode
graphs[node][0] = B0 if a == '0' else B1
graphs[node][1] = node
return top
def new_state_set(graphs, left_str, right_str, B0, B1):
_id = retain_node(graphs)
graphs[_id][0] = new_node(graphs, B0, new_node(graphs,
construct(graphs, left_str, B0, B1), construct(graphs, right_str, B0, B1)))
return _id
def new_node_self(graphs):
_id = retain_node(graphs)
graphs[_id][0] = _id
graphs[_id][1] = _id
return _id
def new_state_if_nodes(graphs, left, right, then_node):
_id = retain_node(graphs)
top = retain_node(graphs)
graphs[_id][0] = top
graphs[top][0] = new_node_self(graphs)
node = retain_node(graphs)
graphs[top][1] = node
graphs[node][0] = new_node(graphs, left, right)
graphs[node][1] = then_node
return _id
def new_state_if(graphs, left_str, right_str, then_node, B0, B1):
return new_state_if_nodes(graphs, construct(graphs, left_str, B0, B1), construct(graphs, right_str, B0, B1), then_node)
def next_state(graphs, prev_state, n):
graphs[prev_state][1] = n
def traverse(graphs: str, from_node: str, traverse_str: str, B0: str, B1: str) -> str:
node = from_node
for a in traverse_str:
if a == 1 and graphs[node][0] != B1:
print("Error")
if a == 0 and graphs[node][0] != B0:
print("Error")
node = graphs[node][1]
return node
def generate_if(graphs, then_state, else_state, B0, B1):
vals = construct(graphs, '1' * 16 + '1' * 8 + '0', B0, B1)
# 状態にかかわる場所
then_state = new_state_if_nodes(graphs, vals, traverse(graphs, vals, '1'*8, B0, B1), then_state)
graphs[then_state][1] = else_state
then_state = new_state_if_nodes(graphs,
traverse(graphs, vals, '1'*2, B0, B1),
traverse(graphs, vals, '1'*(2+8), B0, B1),
then_state)
graphs[then_state][1] = else_state
# TODO ここは construct/traverse いちどで済む
for i in [1,2,3,4,5,6,7]:
then_state = new_state_if_nodes(graphs,
traverse(graphs, vals, '1'*(i+8), B0, B1),
traverse(graphs, vals, '1'*(i), B0, B1),
then_state)
graphs[then_state][1] = else_state
cur_state = then_state
graphs[cur_state][1] = else_state
return cur_state
def main():
graphs = {} # type: Dict[str, list]
root = retain_node(graphs)
admin = retain_node(graphs)
B0 = retain_node(graphs)
graphs[B0][0] = B0
graphs[B0][1] = B0
B1 = retain_node(graphs)
# 001 - 0 - 1 が本体
# 001 - 0 - 0 が not 本体
vartop_graph = retain_node(graphs)
graphs[B1][0] = vartop_graph
count_graph = construct(graphs, "1" *(34-1) +"0", B0, B1)
#count_graph = B0
graphs[vartop_graph][0] = count_graph
rslttop_graph = retain_node(graphs)
graphs[vartop_graph][1] = rslttop_graph
graphs[rslttop_graph][0] = B1
graphs[rslttop_graph][1] = B0
#construct(graphs, "11", B0, B1)
# ここは変数置場
graphs[B1][1] = graphs[vartop_graph][1]
graphs[root][0] = admin
graphs[admin][0] = B0
graphs[admin][1] = B1
# 00111 を仮変数置場にする
part2_state = new_state_set(graphs, '00111', '1'*9, B0, B1)
part5_state = new_state_set(graphs, '00111', '1'*(8*4+1), B0, B1)
graphs[root][1] = generate_if(graphs, part2_state, part5_state, B0, B1)
cur_state = new_state_set(graphs, '1', '1'*65, B0, B1)
graphs[part2_state][1] = cur_state
graphs[part5_state][1] = cur_state
copy_state = new_state_set(graphs, '0011', '0011' + '1'*8, B0, B1)
next_state(graphs, cur_state, copy_state)
# 空白文字で埋める
copy_state2 = new_state_set(graphs, '00111', '00010000', B0, B1)
next_state(graphs, copy_state, copy_state2)
# countup
countup_state = new_state_set(graphs, '001001', '0010011', B0, B1)
next_state(graphs, copy_state2, countup_state)
ending_state = new_state_set(graphs, '1', '001011', B0, B1)
next_state(graphs, ending_state, B0)
# End of Text かどうか?
#isendable_state = new_state_if(graphs, '00100', '000', ending_state, B0, B1)
# そのノードのみ比較するには0 つける必要あり
isendable_state = new_state_if(graphs, '000', '001001' + '0', ending_state, B0, B1)
#isendable_state = new_state_if_nodes(graphs, B0, B0, ending_state)
next_state(graphs, countup_state, isendable_state)
next_state(graphs, isendable_state, graphs[root][1])
#print(graphs, file=sys.stderr)
output(graphs)
def output(graphs):
stack = [list(graphs.keys())[0]]
stack2 = [[]]
watched = set([])
print(stack[0], end=' ')
while len(stack) > 0:
last_id = stack[-1]
last_item = stack2[-1]
if last_id in watched:
stack.pop()
stack2.pop()
continue
# isNew なら 0番目の内容を出力してそっちに飛んでいく
# not isNew なら1番目の内容を出力してそっちに飛んでいく
if len(last_item) == 0:
node = graphs[last_id][0]
else:
node = graphs[last_id][1]
last_item.append(node)
print(node, end=' ')
#print(stack)
#print(stack2)
while len(stack2[-1]) == 2:
watched.add(stack.pop())
stack2.pop()
if len(stack) == 0:
break
if node not in stack:
stack.append(node)
stack2.append([])
print()
if set(graphs.keys()) != watched:
print("Errors: not watched yet")
main()
WriteUp スライド (PDF) を書いた: Esolang Codegolf #6 Transceternal Writeup
161バイトになりました
01233323lm2c832dn2o28e3f3g3hni3j37526278!"0#OH3I3J3K3LE3F3G3HMFNGOP3Q2QXY0ZNUV0WMRS0TL46pq2dty2x8u3v3w3a5b3cByCyDy$)2(5%2&2'2'/:0;<%8*.2-8+2,2n2lzy9k2aAyrs2hCzzz
生成スクリプトは: transceternal-ja/golflog の trans161.py
にある。
Unlambda (@lv100_official, 939 bytes)
コードを見る
``ci`c``@i``@i```ki```?1i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?1i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?2i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?2i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?3i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?3i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?4i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?4i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?5i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?5i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?6i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?6i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?7i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?7i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?8i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?8i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@ii```?9i`d``@i`````s`kc``s`k`s`k`k`ki``ss`k`kk`?9i`d```````|i`@i`@i`@i`@i`@ii`d```````@i`@i`|i`@i`@i`@iiii
V (Vim) (@_hiromi_mi, 20 bytes)
í\v.+(.)(\1| ).+/\1
Vim も参照。
%:s
は í
と略記できる。
Verilog (Icarus Verilog) (@kurgm, 93 bytes)
module c;time a,b;initial while($fscanf(1<<31,"%h%h",a,b))$write(b/16%17?a&15:b>>8);endmodule
time
は実は64ビットの整数らしい。たぶん特徴的なのは、入力を16進数とみて受け取っていることで、10進数でb/100
にあたる箇所がb>>8
とかけて1バイト節約できているところ。
Vim (@__dAi00, @kotatsugame_t, @_hiromi_mi, 25 bytes)
:%s/\v.+(.)(\1| ).+/\1
ZZ
Vim の magic
機能により \+
は +
と書ける. :h \v
で検索。
*
では 224 647
に 2
と答えられるが \+
(1文字以上マッチ) にすることで 4
になる。
また :%s/A/B/
の最後の /
は省略可能。
V (@kotatsugame_t, 79 bytes)
import os
fn main(){for a in os.get_lines(){print(a[if a[1]==a[2]{2}else{4}])}}
if
は式である。os.get_lines()
で全部一気に読む。
for{}
で無限ループが書けるのでこりゃいいやと使ってみたものの、エラーメッセージが標準出力に出てしまい台無し。
文言 (@realParadigm9, 78 bytes)
施(require("fs").readFileSync(0)+0).replace(/(.?)\1 ./g,x=>console.log(x[1]))
@wass80 が 施「<js-code>」於一。
で書けることに気づいてくれた。ところでなでしこ3という{{{<js-code>}}}
でjsが実行できる日本語言語があって、それが最後の閉じタグを省略しても動作するのを思い出し、同じ要領で省略してみたら動いたので提出した(でも多分BNF上では許されてない)
Whitespace (@drafear, 81 bytes)
驚きの白さ。読めるわけないので擬似コード貼っておきますね。
push 0
a:
call f
call f
call f
sub
jz x
call f
call f
x:
printc
readi
jmp a
f:
dup
dup
dup
readc
retrieve
ret
Wren (@taiyoslime, 84 bytes)
import"io"for Stdin
var s
while(1)System.print((s=Stdin.readLine())[s[1]!=s[2]?4:2])
WysiScript (@sitositositoo, 2509 bytes)
<q style="font:normal 9% Monaco;color:#002001;background:gold;"><q class=" nonsyntax " style="text-decoration:underline;">I</q></q><q style="font:bold 9% Monaco;color:teal;background:#8B1;">t<q style="font:bold 8% Monaco;color:#F0FFF0;background:transparent;">s<q style="font:bold 7% Monaco;color:#6E7;background:red;">s</q><q style="font:bold 7% Monaco;color:#6E7;background:tan;">o</q><q style="font:bold 7% Monaco;color:#6E7;background:red;">d</q><q style="font:bold 7% Monaco;color:#1FE15E;background:transparent;">i<q style="font:bold 6% Monaco;color:#D1FFE2;background:blue;">f<q style="font:normal 5% Monaco;color:red;background:transparent;">f</q><q style="font:normal 5% Monaco;color:tan;background:transparent;">i</q></q><q style="font:bold 6% Monaco;color:#F0FFF0;background:aqua;">c<q style="font:bold 5% Monaco;color:#6E7;background:red;">u</q><q style="font:bold 5% Monaco;color:#6E7;background:tan;">l</q><q style="font:bold 5% Monaco;color:#FACADE;background:transparent;">t<q style="font:bold 4% Monaco;color:#D1FFE2;background:transparent;">t<q style="font:normal 3% Monaco;color:tan;background:transparent;">o</q><q style="font:normal 3% Monaco;color:#003001;background:transparent;"><q class=" nonsyntax " style="text-decoration:underline;">s</q></q></q></q></q><q style="font:bold 6% Monaco;color:#F0FFF0;background:transparent;">i<q style="font:bold 5% Monaco;color:#FACADE;background:transparent;">m<q style="font:bold 4% Monaco;color:#D1FFE2;background:transparent;">p<q style="font:normal 3% Monaco;color:tan;background:transparent;">l</q><q style="font:normal 3% Monaco;color:#003001;background:transparent;"><q class=" nonsyntax " style="text-decoration:underline;">i</q></q></q></q><q style="font:bold 5% Monaco;color:#6E7;background:tan;">f</q><q style="font:bold 5% Monaco;color:#6E7;background:red;">y</q></q></q><q style="font:bold 7% Monaco;color:#D1FFE2;background:gold;">t<q style="font:normal 6% Monaco;color:gold;background:transparent;">h</q><q style="font:normal 6% Monaco;color:#000101;background:transparent;"><q class=" nonsyntax " style="text-decoration:underline;">i</q></q></q><q style="font:bold 7% Monaco;color:#6E7;background:red;">s</q><q style="font:bold 7% Monaco;color:#6E7;background:blue;">c</q><q style="font:bold 7% Monaco;color:#6E7;background:red;">o</q></q><q style="font:bold 8% Monaco;color:#70661E;background:transparent;">d<q style="font:normal 7% Monaco;color:gold;background:transparent;">e</q></q></q><q class=" version " data-version="1"></q>
これを(見た目を整えて)表示すると以下のようになります。
WysiScriptは文字色・背景色・文字サイズ・文字装飾のみが意味を持つ言語です。文字サイズの差は入れ子構造を表します。また、ここに登場する中では、太字が組み込み関数、下線がリテラルを表します。その他の文字は全て変数です。 エイプリルフール向けに作られた言語だけあって、条件分岐を表す色が#1FE15E(IF ELSE)、EOFを表す色が#E0Fだったりとネタ満載です。 プログラムがstyle属性にCSSを詰め込まれたHTML形式で、公式インタプリタの自動生成コードにめちゃめちゃ無駄があります。これをそれなりに削るとだいたい半分くらいの長さになりました。削った点は以下の通り。
- font-family, font-style, font-weight, font-sizeがバラバラに指定されてたのをfontにまとめた
- spanタグをqタグにした
- font-sizeがpx単位だったのを%にした(相対的な差にしか意味はない)
XSLT (@k_hanazuki, 188 bytes)
<transform version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform"><output method="text"/><template match="."><value-of select="replace(.,'.+(.)(\1| ).+','$1')"/></template></transform>
途中までXQuery式を書いていたが,正規表現の置換で済むロジックが発見されたのでシンプルになった.
-
<stylesheet>
よりシノニムの<transform>
のほうが2打少ない - XSLTのタグで制御構造を書くよりはXQuery式を使ったほうが短くなりがち
- 入力は
<input>text</input>
の形のXMLで与えられるが,中身の文字列は/
をテキストとして評価すると得られる
Z80 (@yamayu832, 35 bytes)
L0000: CALL 8003h # 1文字目を読み込む(Aレジスタに読み込まれる)
CALL 8003h # 2文字目を読み込む(Aレジスタに上書き)
LD B,A # 2文字目をBレジスタに退避
CALL 8003h # 3文字目を読み込む
CP B # AレジスタとBレジスタを比較しFレジスタのフラグを書き換え
CALL Z,8000h # 2文字目と3文字目を比較して同じならば
# 3文字目(Aレジスタに残っている)を出力
CALL 8003h # 4文字目を読み込む
CALL 8003h # 5文字目を読み込む
CALL NZ,8000h # 2文字目と3文字目を比較して同じでなければ,5文字目を出力
CALL 8003h # 6文字目を読み込む
CALL 8003h # 7文字目を読み込む
CALL 8003h # 8文字目(改行)を読み込む
JR NC,L0000 # 入力の終わりでなければ(入力の終わりでCフラグが立つ),最初に戻る
HALT # プログラムの終了
- 関数のCALLの時に条件を指定できるのが便利だった.そのため無駄なジャンプを行わなくてよかった.