第8回Esolang Codegolf Contest Writeup - hakatashi/esolang-battle GitHub Wiki

問題

ミニポーカーの役判定をせよ

TSG王国とKMC王国では、ミニポーカーの交流戦が行われています。 50人の国民がそれぞれ5枚の手札を持ち、審判が役判定をしようとしますが、人が多くて大変です。 審判を助けるために、ミニポーカーの役判定をするプログラムを作成してください。 ミニポーカーでは、通常のポーカーの役のうち「ストレートフラッシュ」「フラッシュ」「ストレート」「ハイカード」のみが採用されています。

入力

  1. 各国民の手札が50ケース、改行区切りで与えられる
  2. 手札は5枚のカードからなり、各カードを表す文字列を連結したものとして与えられる
  3. 1枚のカードは2文字で構成され、1文字目はスート(A B C D のいずれか、それぞれスペード、ハート、ダイヤ、クローバーを表す)、2文字目は数字(1~5のいずれか)が与えられる

すなわち、各手札は10文字で表される。 入力の最後には改行が与えられる。

出力

50行の入力それぞれについて、以下の問題を解け。それぞれの出力の末尾 には改行を入れよ。

ミニポーカーの役判定をする。ミニポーカーには以下の役があり、より上にある役が優先される。対応する文字>列を出力せよ。

  1. ストレートフラッシュ:5枚すべてのスートが一致し、5枚の数字が連続している。SF または FS を出力する
  2. フラッシュ:5枚すべてのスートが一致する。Fを出力する
  3. ストレート:適切に並び替えると、5枚の数字が連続している。Sを出力する
  4. ハイカード:以上の条件を満たさない。何も出力しない

出力された文字のうち、SとF、改行以外は無視される

運営からのコメント

各チームごとの文章へのリンク・一般的なテクニック

みなさん自由に書き足してください

  • 「ストレートフラッシュ」は「フラッシュ かつ ストレート」と同じなので、実質的には「フラッシュならばFを出力」「ストレートならばSを出力」を独立に行えば良い。

ループの終了条件

  • 50回
  • 入力が尽きるまで
  • 無限ループ or 無限再帰
    • 出力の51行目以降が無視されることを利用
    • EOFErrorみたいなのに任せる
    • 入力がなくなったときにゼロ除算を発生させる
  • 一括置換を使うのでループしない

入力

  • 1文字ずつ読む
    • 文字コードを32 or 64で割った余りが被らないので、出現した文字の集合をビットフラグで管理しやすい
    • 改行の判定
      • 文字コード == 10
      • 文字コード < 11
      • 文字コード & 8
  • 2文字ずつ読む
    • カード1枚が2文字なので
    • 改行は1文字なのでその処理が必要
    • 最初の1文字を基準のスートとしてまず読み込んで、数字→スートの組×5として処理することもできる
      ※その場合、最後の改行はどのスートとも一致しない特別なスートとして処理する
  • 1行ずつ読む
    • 長さが固定なので11バイトずつ読むこともできる
    • 行全体をソートする方針
    • 行全体に正規表現マッチ/置換を行う方針
  • 一気に全体を読む
    • 一括置換とか

ストレートの判定

  • 偶数番目の文字を整数とみなして
    • $\prod s_i = 1\cdot 2\cdot 3\cdot 4\cdot 5$ は不可(2*2*2*3*5 を誤判定する)
    • $\sum 2^{s_i} = 62$ は可
      • 和の代わりに bitwise OR にすれば、ストレート成立時の $62$ が最大値
  • 偶数番目の文字の文字コードを
    • $\prod s_i = 344362200$
    • $\sum {s_i}^2 = 13015$
  • 行内の文字を文字コード順にソート
    • ソートした結果が 12345 で始まっている
    • ソートして前半 5 文字を取り出して uniq したときの長さが $5$
    • ソートして uniq した結果 5 文字目が存在してそれが数字
  • 正規表現で重複する数字があるか調べる(なければストレート)
    • /(\d).*\1/
      • まず S を入力に追加して、↑にマッチしたら S ごと空文字列(or S を含まない何か)に置換する
    • ^(?!.*(\d).*\1)
      • 否定先読みで同様に置換する

フラッシュの判定

  • $f(x) = x&amp;(x-1)$ という演算が、$x$ の1が立ってる中で最下位ビットのみを0にするという性質があるため、$f(x)=0 \Leftrightarrow popcount(x)\le 1$ と、popcount(1が立ってるビットの数)の判定に活用できる。
    これにより、1<<文字コード の総 bitwise OR の popcount が 1以下なのを検出することで、フラッシュの判定になる。
  • 行内の文字を文字コード順にソート
    • ソートした結果の 6 文字目と 10 文字目の文字が同じ
    • ソートして後半 5 文字を取り出して uniq したときの長さが $1$
    • ソートして uniq した結果の後ろから 2 文字目が数字
  • 正規表現で /^(.).(\1.)*$/ または /^(.)(.\1){4}/
    • 単に /(.)(.\1){4}/ では同じ数字が5つあるときに誤マッチするので、本当はまずい。でもたくさんあるね…

出力

  • 51行目以降は無視される
    • エラーとかが出力されてもいい
    • ループの終了条件を真面目にやらなくてもいい?
  • S, F, 改行以外は無視される
    • Fのかわりに False を使える
    • けど FALSE は使えない(Sも入ってる)
    • 入力される文字は(改行を除き)無視されるので、そのまま出力できる
    • 文字コードに対する演算(和、 bitwise XOR など)
      • 'S' + (ストレートの場合に $0$ になる値)
      • 'F' + (フラッシュの場合に $0$ になる値)
      • 'S' と 'F' は偶奇が異なるので、加える値が偶数になるようにすると誤爆を回避できる

言語ごとの解説

3var

Red Team (@nu4nu, @angel-p57, @siotouto, 362 bytes)

'<[kkkkkk[kkkm+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>"<mk+>'<kkkkkkF]<-<+<kkkkkkkO#aa/>m/>m/>#aamamkmk[->|]#aaamaaamaaaaaa->x#aammkk*>PkkkkO@'<_]

A,Bの2つのアキュムレータと、演算結果や入力値が入るRレジスタの3つのレジスタしかない言語。inc,dec,2乗,abs(!)はA,B自身だけで完結するが、それ以外の演算(四則演算とpow)は結果がRに入る。Rを演算の入力オペランドとして使うことができないというのが厄介で、A,Bに移動してから演算する必要があり、他の言語での有力な解法の一つである「F判定用のアキュムレータとS判定用のアキュムレータを用意する」ができないという点で当初は無理ゲーと思われていた(少なくとも私は)。 これを

  • 入力を2乗してアキュムレータに足し込む。スートについては愚直にN(定数)回足し込むことで、F判定用の2乗和 * N + S判定用の2乗和を作る
  • Nで割るとF判定用の2乗和が得られる
  • 元の値をF判定用の2乗和(S判定用の2乗和の最大値より大きい)で割った余りがS判定用の2乗和
  • あとは適当にinc/decして片方を出力すれば楽になる

とすることで実装できそうという見解が @siotouto さんから出され、実際に @angel-p57 さんによりN=512として実装されたものがACしたことで、短縮が始められる段階となった。(なお、Nが256の倍数ならFとSがくっついたままでも"S"を出せるので、実装では↑のFとSの分離をやっていませんが、なるほどーと思ったので書いています)

362B版のコードは$(\sum d_i^2) \times 2 - 27$で"S"を、$|(\sum(s_i-9)^2) % 575 - 150| \times 14$で"F"を出すようなアルゴリズムになっている。(条件にマッチしないときは別の無害な文字が出る)

コードを整形すると以下のようになる。ループはインタプリタ実装上do/whileのように実装されているので、ループ条件指定子は]の手前にだけ書けばよいことに注意。

'<                ~ B = next char
[                 ~ do ... while(B > A(=0))
  kkkkkk          ~ B -= 6 (下と合わせる)
  [               ~ do ... while(B > loop(=4))
    kkkm          ~ B -= 3, B *= B
    +>+>...+>     ~ A += B を128回
    "<mk          ~ B = next int, B *= B, B -= 1
    +>            ~ A += B
    '<kkkkkk      ~ B = next char, B -= 6
  F]              ~ while(B > loop(=4))
  <-<+<kkkkkkkO   ~ B = 10, B = A - B, B = A + B, B -= 7, putchar(B)
  #aa/>m/>m/>     ~ A /= 128
  #aamamkmk       ~ B = 575
  [->|]           ~ while(A>B) A -= B
  #aaamaaamaaaaaa ~ B = 150
  ->x             ~ A = abs(A - B)
  #aammkk*>P      ~ B = 14, A *= B, putchar(A)
  kkkkO           ~ B -= 4, putchar(B)
  @'<             ~ A = 0, B = next char
_]                ~ while(B > A(=0))

128で割る部分以降を以下のように縮めると344Bになる。25を作ることで150を引く処理が短く書けるのがポイント。あと整数出力で改行文字が出せるのも面白い。

#aa/>m/>/>/>      ~ A /= 128 (1B増える代わりにB = 4になる)
am[->->]          ~ B = 25, A -= 2 * 25 * (25 + 1) = 1300 = 150 + 2 * 575
kk[[->F]->|]      ~ B = 23, while(A>B) A -= 23 * (23 + 1) + 23 = 575
sd+>+>P           ~ putchar(5**2 - 1 + 23 + 23 = 70)
@p'<              ~ 整数出力で改行文字が出る
_]

一つ触っていて気づいたバグ(?)として、]|]のように1文字以下の間隔で閉じカッコが並んでいると、外側の閉じカッコからジャンプするときに内側の開きカッコにジャンプしてしまうというものがある(閉じカッコから逆順にコードをなめていくところで無駄に戻っていて直近の閉じカッコを見落とす)。今回はちょうど間に挟むものがあって助かった。

Ada (GNU GNAT)

Blue Team (@ToE42, 387 bytes)

with Ada.Text_IO;use Ada.Text_IO;with Ada.Strings.Fixed;procedure m is a:String(1..10);c,d,e,f,g:Integer;begin Get(a);if a(1)=a(3)and a(1)=a(5)and a(1)=a(7)and a(1)=a(9)then Put("F");end if;c:=Integer'Value(a(2)&"")+2;d:=Integer'Value(a(4)&"")+2;e:=Integer'Value(a(6)&"")+2;f:=Integer'Value(a(8)&"")+2;g:=Integer'Value(a(10)&"")+2;if c*d*e*f*G=2520 then Put("S");end if;New_Line;m;end m;

(a1+2)*(a2+2)*(a3+2)*(a4+2)*(a5+2)=2520を満たすのが(1,2,3,4,5)の組しかないことを利用. ループには再帰を使用した. かなり冗長だが奪取されなかった.

Aheui

Green Team (@bx4ay, 168 bytes)

ハングルで書く2次元言語 (1次元のコードを書いてしまいすみません)。子音で命令、母音で読む方向を指定する。多数のスタックを行き来できるのが特徴。

밯빠바쟈헤차밯싹빠밯라파밯싹빠밯라파밯싹빠밯라파밯싹밯라삭밯따따따따발밦따밣따밝타라맣사다다다밣박다밠따다맣밯맣
  • Sの方針:数字5つの文字コードの積を233で割った余りを出力
  • Fの方針:2番目から5番目のアルファベットについて、最初のアルファベットを割った余りをとり(同じかどうかの判定)、それらの和に70(='F')を足して出力

AlphaBeta

Green Team (@kotatsugame, 59 bytes)

wJISHrEykiigvEFrErITHJtEykkiiigggvZWyJNYSSZiOLSGeddsLDLywJO

Red Team (@siotouto, 81 bytes)

SJCITJAtISZYUUUUUTZJyiQHsEmSEHrITTJAtEaHrISZYUZONZWyNYZTGkiirLSSyihhBtEaaHrLxcCLO
  • start(0-), getInput(10-), process(49-) の3箇所に分けてる
  • Fは'B'+(4回の比較の0, 1)で出力
  • Sはランク文字コードの2乗和+124で出力
  • goto先は100とかでもちゃんと終了する

APL

Green Team (@kotatsugame, 86 bytes)

⍪{' S'[1+×/4↓ω],' F'[1+5∈ω]}¨+/{'ABCD12345'∈ω}¨50 11⍴⍕⎕ARG[6]
)OFF

Arcyóu

Red Team (@siotouto, 111 bytes)

(%%(v(q)"\n")(F(i)(p(?(&(%(F(n)(=(_(v i(% "%d" n)))2))(_ 1 6)))"S" "")(%(F(d)(?(=(_(v i d))6)"F" ""))(' A B C D
  • 文字でイテレート出来ない・・・
  • 文字列(各文字)でスプリットして長さでチェック、
    • Sは5種類の文字で長さが2になってるか(&がall)
    • Fはどれかが達成すればFなので6=>Fにするmap
      • 今回は配列のまま出力して良いのでそのままpに
  • 末尾の)は省略出来る
    • 細かいポイントとして%%にしても省略する)が2個以上増えて得なことがある

Atlas

Blue Team (@saito-ta, 14 bytes)

!={[5++'S(}]+B

入力の各行を ! (sort) してから = (count) を取っている。 数字に該当する先頭から 5 文字分が [0,0,0,0,0] ならストレートなので、これの総和を 'S に加えている。 また、最後が 4 ならフラッシュなので、これを B に加えている。

Aubergine

Red Team (@angel-p57, 270 bytes)

=aa=aa=aa-aa=b1-BB+b1-BB+b1-BB+b1-BB+b1-BB=Ab+b1-BB+b1+b1=Bo=aa=bo=a1+a1+a1+aa+aa+aa+aa-ba=B1=ao=b1+b1+bb+bb-aB+b1+bi:ba=bi=aa-bi+b1+b1+b1=a1+a1+a1+aa-Ab+a1+a1+aa-a1+aa+aa-bb-B1:aB=b1+b1-ab+b1+bb+aB=oa-aB+ab+b1+ab+ab=b1+aB+b1+aB+b1+aB+b1+aB+b1+aB=bi=oa=a1+ai-ab=bi=oa-ib
  • 変数2つもあるし、ポインタ経由でメモリ扱えるし、命令は2オペランドRISCっぽいし、しかもIP(命令位置)とれるなんて、もはや汎用言語じゃん! (演算がロクに無いけど)と最終日に選んだ言語。
  • と言いつつ、データメモリがコードメモリと共用(!)であることに気付かず大苦戦。割と組むだけになってしまいました。
  • 戦略としては、F は不一致数をカウント ( 改行分の 1 が min )、S はメモリ上の5か所のフラグ ( 0 or 1 ) の総和で計算。

疑似コード

L0:
  m[1]=m[2]=m[3]=m[4]=m[5]=m[6]=0; m[0]=5; m[8]=getc();
  L1:
    m[getc()-48]=1;
    t=(fixed+3);  // fixed系はIPに依存した固定数値
    if ( getc()-m[8] ) goto L2;
    t=fixed;
    L2:
    m[6]-=t-(fixed+6)+3;
    m[0]-=1;
    if ( m[0] ) goto L1;
  putc(m[6]+58);
  putc(78+m[1]+m[2]+m[3]+m[4]+m[5]);
  putc(10);
  goto L0;
コメント入りコード整形版(そのままでは流せません)
# nop x3 で9個書き換え用の領域をallocate
=aa=aa=aa
# 全体ループ先頭
# メモリ1~6を0初期化、0を5で初期化、8にgetc()して最初のスート保存
-aa=b1-BB+b1-BB+b1-BB+b1-BB+b1-BB=Ab+b1-BB+b1+b1=Bo
# 行内ループ先頭(57 or 60へjump … 60の方が作り易いと思う)
# 数字入力し、対応したメモリ1~5に1をセット
=aa=bo=a1+a1+a1+aa+aa+aa+aa-ba=B1
# スート/改行入力し、メモリ8との差分を a にとる
=ao=b1+b1+bb+bb-aB
# スート違いならbに保存したIP+9先にjump
+b1+bi:ba
# else処理、IPでbを上書き
=bi=aa
# if/else合流、改めて b とIPの差を取り、スート一致ならメモリ6に3加算
-bi+b1+b1+b1=a1+a1+a1+aa-Ab
# ループ戻りのIP(60)をaに計算、メモリ0のカウンタを減らして非0ならjump
+a1+a1+aa-a1+aa+aa-bb-B1:aB
# ここから行末処理
# メモリ8の累積値(4回一致で+12)+58でF文字出力、その後aを78にセット
=b1+b1+b1+ab+bb+a1+aB=oa-aB+ab+b1+ab+ab
# 以降、メモリ1~5のフラグ値(0/1)をaに加算 ( 全部1で83 )
=b1+aB
+b1+aB
+b1+aB
+b1+aB
+b1+aB
# S文字・改行出力後、ループ先頭(6)へjump ( 10や6はIPの差分から作成 )
=bi=oa=a1+ai-ab=bi=oa-ib

後日更新版 (@angel-p57, 135 bytes)

=B1=bo-b1-bi=B1+B1:ab=aA=aA=b1=bB+ii__@CCCCCCCCCCC@CCCCCCCCCCCCCCC@oR-BA-AA-Ai+Ai-a1:bA=ab+a1+BA=oB-BB+a1=bA-aa-A1:bA+bi+b1=ii-bi=ob-ii

※コード中 @,C,R は実際は ^@(ASCII 0), ^C(ASCII 3), ^R(ASCII 18)

  • コード内の領域を変数・定数として活用できる点を突き詰め、組み直したバージョンです。
  • 疑似コード
    
    // 初期値:
    //  a=b=0 (仕様), *1=66, *2=49 (ダミー命令のコード)
    // *39~*49,*51~*65=全て3, *38=*50=*66=0, *67=111, *68=18
    *0=1;                        // =B1           ダミー命令
    L0:                          //               メインループ兼入力ループ先頭、この時 a=0
      b=getchar()-1-9;           // =bo-b1-bi     スート文字: 55~58, 数字: 39~43, 改行: 0
      *b=1+1;                    // =B1+B1
      if ( b!=0 ) goto L0;       // :ab           a=0を利用
      L18:                       //               S,F文字出力用ループ先頭、この時 a=0
        a=**a;                   // =aA=aA        2 or 1 → 49 or 66 
        b=*1;                    // =b1=bB        66
        goto L66;                // +ii           データエリア(38~68番地)のスキップ
        L66:                     //               出力用文字コード総和計算ループ先頭
          *b-=*a;                // -BA           2 or 3
          *a=3;                  // -AA-Ai+Ai     初期値3へリセット(39~49 or 51~66番地)
          a-=1;                  // -a1
          if ( *a!=0 ) goto L66; // :bA           b=66を利用
        a=b+1;                   // =ab+a1        67、この時 b=66
        *b+=*a;                  // +BA           +111
        putchar(*b);             // =oB           S文字 or F文字出力
        *b=0;                    // -BB           初期値0へリセット
        a+=1;                    // +a1           68
        b=*a;                    // =bA           18
        a=0;                     // -aa
        *a-=1;                   // -A1
        if ( *a!=0 ) goto L18;   // :bA           b=18を利用
      b-=8;                      // +bi+b1=ii-bi  18→10
      putchar(b);                // =ob           改行出力
      goto L0;                   // -ii
    
  • ストレート・フラッシュの判定はいずれも「現れた文字種の数」で賄います。そのため、「その文字が現れた」ことをフラグ(初期値3→2に変化)としてメモリ内に保持します。( 39~43,55~58番地 )
  • 改行が来た場合は、同様に 0番地に 2が保存されます。これは S,F 出力用のループカウンタとして使います。
  • S,Fそれぞれ出力用の文字は、番兵(値0のメモリ)に辿り着くまでメモリ範囲を降順sweepして総和計算を行います。
    • 結果の保存には 66番地 (初期値0)を使います。
    • また、sweepしつつ各番地での値を初期値3にリセットしていきます。
    • S文字: 49番地開始、38番地番兵、c=5, 0-3×(11-c)-2×c+111=83 で S になります。
    • F文字: 66番地開始、50番地番兵、開始位置が結果保存場所と被るため「初期値3、65番地開始」と同じ状況になり、c=1, 3-3×(15-c)-2×c+111=70 で F になります。
  • iレジスタは実行中のコード位置を操作できるので、ジャンプ命令的にも定数参照的にも使えます。
    • 例えば、33番地の +ii により i が66に変化、これが疑似コード中 goto L66 に相当します。( 実際には更に+3されるので69番地の命令に移ります )
    • iの値は+3ずつ変化するため、差分を計算することで小さな3の倍数を作るのに役立ちます。例えば最後の方の +bi******-bi のところでは、iの差分 9 の変化を b に与える効果があります。

Bash (pure)

Blue Team (@saito-ta, 68 bytes)

read a&&(F ${a//[1-5${a:8}]}
f=$_
12345
S ${_//[$a]}
echo $_$f
. $0)

$_ は、直前のコマンドの最後の引数(引数が無い場合はコマンド名)に展開される変数です。これを 3 箇所で使っています。 $0 は現在のスクリプト名を格納する変数です。これを . (source) することでループしています。

bed

Blue Team (@hatsusato, 70 bytes)

qawm,r2c-i*lr+iwm,rojr=*iqqbitj,rhx5$arohr=i46*iw.mlro[!i53*iw.m.q32$b

Befunge-98

Green Team (@ten986, 55 bytes)

v#S',+F'*3%y'_jd,a,
4
<-1_v#:p0:+1':
~@#*<3p0~ '+_v#-a:

青チームに3byte負けたものの、Befungeを活かした面白いコードができたので満足しています。勝ちたかったけど。 p命令を用いて、0行目の49~53文字目に文字を設置することでストレート判定をすることが1番の特徴です。 Befungeはスタックの入れ替えが難しい言語(topと1個下のswapしかできない)なので、ストレートの判定を盤面で行う方針にし、スタックから外してやることで、フラッシュの判定でスタックをフルに使えるため嬉しいです。 (ただ青チームの方針であれば、それぞれ2乗和を取ればいいので、swapだけで済むのですが・・・)

  • v 4
    • 初期化。スタックに4を積む
  • <-1_v#:p0:+1':
    • 初期化。0行目の49~53文字目にそれぞれ1 2 3 4 5 を設置する
    • 設置した文字は、最初から盤面にある文字同様踏むと対応する命令が実行される
    • 数字は対応する数字をスタックに積む命令
      • この数字をストレート判定時にそのまま踏むと、ゴミをスタックに積むためSを出力せずに済む
  • ~@#*<3p0~ '+_v#-a:
    • 入力と判定の前処理
    • ~@# で各行奇数文字目の入力、EOFは反射する
      • 前回のコードゴルフの時と仕様変わっててびっくりしました。前は-1を受け取ってたはず
    • _v#-a: で改行判定、10なら次の処理へ
    • 奇数文字目はフラッシュ判定に使う
      • ~ * 3 + が該当
      • 各桁x<-(x+ord)*3をしていきx%121*3+70を出力すればOKなので、その前処理
      • 初期値の0は、この行に来る際の_v#で生成されてる
    • 偶数文字目はストレート判定に使う
      • 1~5 をordで受け取り、「 ': 空白」「~:1~5のord」「0:0」の順に積みpすると、0行目の49~53文字目に空白を設置できる
      • 初期化時に設置した1~5を削除できる
      • ストレートの場合は1~5が1回ずつ出現するため、初期化時に設置した1~5すべてを削除できる
      • ストレート以外なら、どれか1つは削除できないものがある
  • #S',+F'*3%y'_jd,a,
    • 出力
      • ,+F'*3%y'_%121*3+70 をする
      • < でなく _ なのは、スタックにトップにあるゴミのpopを兼ねるため
      • S' で S を積む
      • 0行目の49~53文字目を通ることで、ストレート以外であれば1~5がスタックに積まれる
      • ,a, でスタックのトップと改行を出力

Blue Team (@shinh, 52 bytes)

<|-a:~@#
^>$'_%'#%'[\x19]%'F\-,++++"_[\x89]"*%'S+,a,
^>:*+~:*\
  • [\x19][\x89] は表示がつらいので置換しました。定数を作った感じです
  • F も S も自乗和を mod して 0 になったら OK というやつです
  • Befunge 好きなんですが Befunge-98 始めて触ったので色々便利なのがあるなぁという感想でした

(@ten986追記) 感想戦にて、++++4k+とできるため1byte減りました。

Bots

Red Team (@satos---jp, 162 bytes)

i(n,x,y){*n y j x}j(){ic r}r(x){-10 x?i s x}s(a,x,y){-x 344362200?od oc 83 f x y}m(x,y){/x y*y-x}f(x,y){m y 3604+20 m 55?od oc 70 oc 10}l(t){?t i@1 1 1-t 1 l}l 50

以下は読みやすくしたもの。

i(n,x,y){* n y j x}
j(){ic r}
r(x){- 10 x ? i s x}
s(a,x,y){- x 344362200 ? od oc 83 f x y}
m(x,y){/ x y * y - x}
f(x,y){m y 3604 + 20 m 55 ? od oc 70 oc 10}
l(t){? t i @ 1 1 1 - t 1 l}
l 50

アルゴルズムは積によるS判定と積とmodによるF判定。lがメイン関数、r,i,jで入力読み込み、s,fでS,F判定。 Botsを書くコツとして、基本演算ではスタックの2番目にしか返り値を入れられないので、より複雑な操作(例えばスタックの上2個を保存する)をしたい場合は関数を定義するとよい。

(nu4nu追記) 第7回でこの言語のファンになってしまったのでゴルフしてみました。 ↑をベースにS判定をmod 72345、プログラム終了条件を-1が読めたときにする等の修正したものが以下の138B(改行なしで)。

i(n,x,y){+n 1?*@n y j x}
j(){ic r}
r(x){-10 x?i s x}
m(x,y){/x y*y-x}
s(a,x,y){m x 72345?od oc 83 m y 3604+20 m 55?od oc 70 oc a}
l(){j 1 1 l}
l

mod(正確には-(x%y))の定義を嫌ってS判定とF判定を分けたものが以下の132B(改行なしで)。256以上の文字コードをocすると死んでしまうので、S側は数値として読んだ2乗和の2倍-27を出力している。EOF時idは0を返すので@判定がicより短くなるというのもポイント。

j(){ic r}
r(x){-10 x?i s x}
i(c,y,z){-c z?*+z 0 e y}
e(){id m}
m(d,z,y){?d*@d d+y j z}
s(a,y,z){+y y-27 oc?z oc od 70 oc a}
l(){ic e 0 l}
l

Brainfuck (esomer)

Blue Team (@soup-soup605, 346 bytes)

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

Red Team (@angel-p57, 114 bytes)

,+[-<<<<<+++++[->>>,-[-[-<->>+<]>[-<+>]<]>,>[-<-<+>>]<[[-]<<<+>>>]<[->>+<<]<<<<++>]-[------->+>+<<]>>[----.<]<.,+]
  • 各行、スート・数字を5回のインナーループで処理 ( 改行文字もスート扱いする )
  • S=Σ( -(s-1)(s-2)/2 )%256、F=不一致スート数 ( 改行分あるため最小 1 ) で、共に +69 で該当する文字に直せる
  • +69 は、-1/7 = 73 ( (256*2-1)/7=73 ) を適用、あとで -4 補正 (この処理が一括でできるのがウリ)
  • 改行文字 N=10 は、インナーループで +2 ずつして作っておく
  • 以下の整形版コード中、n が5組分のカウンタ、N が改行文字、F,S がF文字、S文字用の累積数値、s,d がスート・数字の入力文字、bが基準になる最初のスート
整形版コード
,+[-
 ** memory layout: NnFSsdb **
 b<<<<<n+++++[-
  >>>s,-[-[-<->>+<]>[-<+>]<]
  >d,>b[-<-<+>>]<d[[-]<<<F+>>>]<[->>+<<]
 <<<<N++>]
 n-[------->F+>S+<<]
 >>[----.<]
 <N.
,+]

braintwist

Red Team (@angel-p57, 219 bytes)

  • Brainfuck (esomer)の114B版、負番地に行かないように <,> を入れ替えて最後に +] を追加した116文字をエンコードして、100万未満のシードでできるMT乱数列と睨めっこして、6~7文字ずつ合うものを探しては残り xor してまた乱数列を探して…で作成したコード。( シード間の行数が乱数列の一致する長さに対応 )
  • 乱数列の探し方でそんなに差が出るとは思えないので、元のBrainfuckコードがほぼ全てという気も。
コード提出版

164041





18647






257156





197671





126871





756886





585318






47728





131165





94414





204275





356164





307763





760067





110911





24396





92514





403957





305571

BubbleSortLanguage

C (GCC)

Blue Team (@saito-ta, 63 bytes)

main(s,a){main(10/a?s=s&s-2^'m G',!puts(&s):s|1<<a,getchar());}

いわゆる main 再帰。いずれスタックがオーバフローすることによってプロセスが終了する。その際に出力のフラッシュが行われないことが問題になるが、出力の 51 行目以降が判定に影響しないことを利用し、ゴミを出力し続けることによってフラッシュを惹起している。(ここまで赤青共通) puts() が常に 0 を返すことを仮定し、これを利用して s を 1 にリセットしている。 起動時の main() の第二引数 a の値によっては正しく動かないのでは?という確率解疑惑があった。これについては、

  • Performance Checker で実験したところ、起動時の a の下位 4 ビットの値は必ず 8 になるようだ。
  • そうすると、 1<<a の値は 1<<0x08 又は 1<<0x18 ということになる。
  • 1<<0x08 の場合は、出力の 2 文字目に当たるので、問題ない。
  • 1<<0x18 の場合は、出力の 4 文字目に当たり、文字列として終端されないことになる。しかし、実験した限り、 1 行目が 4 文字を超えて出力されることはなかったため、問題ないと判断した。
  • ところで、起動時の a が確率的に 0x00000008 になることってあるんですかね… その場合、 10/a が確率的となって、まずいのかもしれない。

Red Team (@nu4nu, 75 bytes)

b;main(c){main(b=c&8?!printf("%c%c\n",b/375,70^b&b-1):b|1<<c,c=getchar());}

^'m G'を思いつけなかったのが全て。割り算で短くなって嬉しくなっていた。赤チームの「ビットマスクのLSBを常に立たせることでmain再帰の引数に持ってこられるようにする」というのも、一番下の1を消すイディオムb&b-1にとらわれていて思いつけなかった。

せっかくなのでスタックのASLRについてメモを残しておく。 今回のジャッジサーバーはx86_64のUbuntu Linuxが動いていて、カーネルバージョンは5.15.0-76-genericとなっている。(Performance Checkerにuname(2)を呼ぶCのコードを投げればわかる) スタックの仮想アドレス配置に関連するLinuxのソース(あまり昔から変わっていないようだが、一応v5.15を参照)はSTACK_RND_MASK, randomize_stack_top(), arch_align_stack(), およびそれらの呼び出し元のfs/binfmt_elf.cやfs/exec.cあたり。randomize_stack_top()がページサイズ(4KiB)単位のrandomizeで、arch_align_stack()が16B単位のrandomizeとなっている。STACK_RND_MASKは64bitでは0x3fffffで、乱数をこれでマスクした数のページ数分だけstack_topが移動するので、スタックの仮想アドレスはざっくり16GiBの振れ幅があることになる(コメントには1GBと書いてあるがどう見てもfが1つ多い)。Performance Checkerにmainの第2引数の値を出すようなコードを5回投げた結果がこんな感じで、アドレス下位34bit(ただし最下位4bitを除く)が振れている。

00007ffeb46cbc18 00007ffc2f800208 00007fff30d20f48 00007ffc96bc66e8 00007ffd6d240af8

ということで、mainの第2引数をintとして解釈したときに8になりうるか、という問いに関しては、ごくごく稀になりうる、というのが答えだと思われる。

cmd.exe

Green Team (@EtoNagisa, 1288 bytes)

コードを見る
set/pq=
call :f %q:~0,10%
call :f %q:~11,10%
call :f %q:~22,10%
call :f %q:~33,10%
call :f %q:~44,10%
call :f %q:~55,10%
call :f %q:~66,10%
call :f %q:~77,10%
call :f %q:~88,10%
call :f %q:~99,10%
call :f %q:~110,10%
call :f %q:~121,10%
call :f %q:~132,10%
call :f %q:~143,10%
call :f %q:~154,10%
call :f %q:~165,10%
call :f %q:~176,10%
call :f %q:~187,10%
call :f %q:~198,10%
call :f %q:~209,10%
call :f %q:~220,10%
call :f %q:~231,10%
call :f %q:~242,10%
call :f %q:~253,10%
call :f %q:~264,10%
call :f %q:~275,10%
call :f %q:~286,10%
call :f %q:~297,10%
call :f %q:~308,10%
call :f %q:~319,10%
call :f %q:~330,10%
call :f %q:~341,10%
call :f %q:~352,10%
call :f %q:~363,10%
call :f %q:~374,10%
call :f %q:~385,10%
call :f %q:~396,10%
call :f %q:~407,10%
call :f %q:~418,10%
call :f %q:~429,10%
call :f %q:~440,10%
call :f %q:~451,10%
call :f %q:~462,10%
call :f %q:~473,10%
call :f %q:~484,10%
call :f %q:~495,10%
call :f %q:~506,10%
call :f %q:~517,10%
call :f %q:~528,10%
call :f %q:~539,10%
exit /b
:f
set t=%1
if %t:~0,1% == %t:~2,1% if %t:~2,1% == %t:~4,1% if %t:~4,1% == %t:~6,1% if %t:~6,1% == %t:~8,1% set /px=F<nul
set /as=%t:~1,1%+%t:~3,1%+%t:~5,1%+%t:~7,1%+%t:~9,1%
set /ap=%t:~1,1%*%t:~3,1%*%t:~5,1%*%t:~7,1%*%t:~9,1%
if %s% == 15 if %p% == 120 set /px=S<nul
echo 1
exit /b

書いただけ.forループが難しすぎる. はじめは下の方にある:fの下の部分を50回コピペしていたが,コード長制限で怒られたため関数に切り出して呼び出しを50回コピペした.なぜかforループ中でcallをするとsyntaxerrorと言われて険しい気持ちに 実際はjumpだからダメとかなのか・・?

(nu4nu追記) forループは試行錯誤の結果以下のような記述でうまくできた。

set/pq=
for /l %%i in (0,11,539) do call :f %q% %%i
exit /b
:f
set t=%1
call set t=%%t:~%2%%
(以下上の1288Bと同じtに関する判定式)

substringを作る記述をサブルーチンの中に追い出すのが一つのポイント。forループ側でいろいろやろうとするとsubstringを作ったつもりが元の文字列になっていたり謎が多い。またsubstringのindexを変数にするにはcall setというおまじないが必要らしい。このあたりのテクはSS64.comに詳しく書いてある。

ただ、forループでがんばるよりも、入力を削っていって空になったら終わりになるようなgotoループのほうが圧倒的に短い。その他の短縮込みで214Bになった。(2行目は末尾にスペースが入っていることに注意)

set/pt=
set t=%t% 
:l
set/ap=1%t:~1,1%*1%t:~3,1%*1%t:~5,1%*1%t:~7,1%*1%t:~9,1%
if %p%==360360 set/px=S<nul
set s=%t:~,1%%t:~2,1%%t:~4,1%%t:~6,1%%t:~8,1%
if %s:~,4%==%s:~1% set s=
echo %s%
set t=%t:~11%
goto%t:~-1%l

S側の判定は数字に10を足したものの積の比較としている。文字列と数値の境界が曖昧な言語ならでは。==360360%58==6で判定したほうが短くなるように思えるが、%をエスケープするために^%%58と書く必要があり短くならない。 F側はスート文字だけ拾ってきたものを一つずらして比較。s=Fでもよいが、echoの引数が空だとEcho is OFFが出て、Fがダブって出てもACというジャッジ仕様で1B短くできている。 終了は%t%が空になったときにgotoしなくなるようにして実現している。ちょうどset/pの仕様か何かで入力最後のLFが消えていて、何かを補わないと空になってくれない(%t:~11%%t%が10文字以下のとき何もしない!)ので、そこにスペースを埋めてgoto時に参照している。最初はexit%t%と書いて空になったときに抜けるようにしていたが、その行とスペースが消せるだけこちらのほうが短い。

今回は使わなくてもなんとかなったが、forのデリミタや文字列置換にLFを指定する方法がさっぱりわからなかった。問題によっては苦労することがあるかもしれない。

(さらに追記) 文字列置換を駆使すると166B。F側は先頭文字と同じ文字を消して6文字目をechoすることで、フラッシュのときはLFかスペースに展開されてEcho is OFFが出るようにしている。出力を2回に分けているのがもったいなく思えるが、Echo is OFFに依存しないようにするにはFを仕込む必要がありこれより短くできなかった。

set/pt=
set t=%t% 
:l
set/ap=1%t:~1,1%*1%t:~3,1%*1%t:~5,1%*1%t:~7,1%*1%t:~9,1%
set/px=%p:603=S%<nul
call set s=%%t:%t:~,1%=%%
echo %s:~5,1%
set t=%t:~11%
goto%t:~-1%l

Compile-time TypeScript

Blue Team (@n4o847, 203 bytes)

type Y<P,Q=any>=`${any}${P}${Q}`
type M<I,T=``>=I extends`${infer P}${infer Q}
${infer R}`?M<R,`${T}${Q extends Y<1>&Y<2>&Y<3>&Y<4>&Y<5>?"S":0}${Q extends Y<P,Y<P,Y<P,Y<P>>>>?"F":0}
`>:T
export default M

末尾再帰。スートに交差型、数字に合併型を使ってうまくやりたかったができず、赤チームがきれいにやってくれていたので完敗。

Red Team (@kurgm, 188 bytes)

type M<X,O=0,P={},Q=0>=X extends`
${infer R}`?M<R,`${O}${`${1|2|3|4|5}`extends Q?'S':0}${[P]extends[0]?0:'F'}
`>:X extends`${infer A}${infer B}${infer R}`?M<R,O,P&A,Q|B>:O
export default M

整形 & 読みづらいので改行を \n にした

type M<X, O = 0, P = {}, Q = 0> =
X extends `\n${infer R}` // 先頭が改行文字 = 行末まで処理した
  ? M<R,
      `${O}${
        `${1 | 2 | 3 | 4 | 5}` extends Q ? "S" : 0
      }${
        [P] extends [0] ? 0 : "F"
      }\n`>
  :
X extends `${infer A}${infer B}${infer R}`
  ? M<R, O, P & A, Q | B>
  : O; // 終端まで処理したので結果を返す
export default M;

TS 4.5 から入った末尾再帰最適化を活かして再帰回数の上限に引っかからないようにしている。

  • 2文字ずつ読んで、スートは交差型 &、数字は合併型 | で reduce する
    • スートが全部同じ(F条件)ならreduce結果はそのアルファベットになる。違うのがあれば never になる
    • 数字はS条件のとき "1"|"2"|"3"|"4"|"5" になる
  • 初期値の P=0 は本来は交差型の単位元 unknown だが、文字数節約のため {} (= null, undefined 以外) にしている
  • 初期値の Q=0 は本来は合併型の単位元 never だが、文字数節約のため 0 にしている
  • Pnever かの判定について。 union distribution の関係で extends の左側を P だけにはできない。extends の右側は本来 [never] だが文字数の削減を図って [0] にしている

`${O}${ の部分はエディタでは赤線が引かれるのだが普通に通るらしい(finalさんの気付きにより判明)

Convex

Green Team (@kotatsugame, 25 bytes)

qN/{$5/:Å:,~69+c\78+cN}%

Crystal

Blue Team (@shinh, 59 bytes)

while l=gets;p [l=~/^(.)(.\1)*.$/&&:F,l=~/(\d).*\1/||:S]end

Red のチームとの差分を見ると、 Blue は A1B1C1D1A1 に F と出るので使ってなかった正規表現を使ってそう→と思ったが、僕自身も他のところで使っていた……

Red Team (@rotary-o, 58 bytes)

while s=gets
p [s=~/(\d).*\1/||:S,s=~/(.)(.\1){4}/&&:F]end
  • S、Fともに正規表現で判定(同じ数字が5つでもFになってしまう…)
  • while s=getsだとString?からStringになるらしい( https://kotatsugame.hatenablog.com/entry/2020/09/21/230023 )
  • 余計な文字を出力してもよいことを利用して、pで出力
  • ほぼRuby 3と同じ

C# (.NET Core)

Blue Team (@saito-ta, 本質@drafear, 116 bytes)

using C=System.Console;for(int s=0,a;(a=C.Read())>0;s|=1<<a)if(a<11)C.WriteLine(((s&s-2&30)>0,(s>>=12)>991?"S":""));

解法は Ruby 0.49 と似たような考えなので、C#特有のことについて:

  • 型を指定すると未初期化宣言OK
  • 1<<651<<1, 1<<491<<17 と同じ
    • s>>=122,4,6,8,16 のためにギリギリを攻めていて、ギリギリを攻めることによって 991 とギリギリ3桁でおさまった
  • EOF に到達しても C.Read() はエラーにならず、0を返す
  • WriteLine("書式", "引数1", "引数2", ...) の書式に (Bool,int) のtupleを渡すとエラーになるのに、(Bool,String) だとなぜか通る

Red Team (@rotary-o, 120 bytes)

using C=System.Console;for(int b,i=0;(b=C.Read())>0;i|=1<<b)if(b<11)C.WriteLine((i>31<<17?"S":"")+" F"[1>>(i&i-1)],i=0);
  • 1文字ずつ読んで、ビット列に保存
  • S、Fともにビット列で判定(nu さんのCがベース)
  • 片方は三項演算子ではなく" F"[...]でcharで取得(両方charにすると文字列にするのに""+が必要になるので片方だけ)
  • Console.WriteLine()で第1引数がstringの場合は、第2引数以降にフォーマット用のオブジェクトを指定するが、使われないものを指定してもよいので、if(...){}を省略するために、i=0を第2引数にしている
  • Blue TeamのTupleにして出力は思い付かなかった

Csound

Red Team (@ikubaku, 334 bytes)

<CsoundSynthesizer>
<CsInstruments>
instr 1
l:
iA[]tab2array 1,p3,p3+11
iA sorta iA
fprints"output.out",(iA[1]*iA[2]>>16)*(iA[3]*iA[4]>>16)*(iA[5]>>8)%72345==0?"S":" "
fprints"output.out",iA[6]==iA[10]?"F":" "
fprints"output.out","\\n"
loop_lt p3,11,550,l
endin
</CsInstruments>
<CsScore>
f 1 0 0 -1"input.in"0 -1 0
i 1 0 0
</CsScore>

まずデータ入力部分について説明します。 CSound esolang-boxでは標準入出力の操作が困難なため、入力データはcwdの"input.in"、出力データは"output.out"に保持することになっています。

データの入出力はこれまでの大会での結果から、入力をfunction tableの生成ルーチンのパラメータとして扱い、出力をfprintsによって行うことがコード長の観点から最適であるとわかっているので、これらの手法を使います。 しかしこれまでの大会では入力データが整数のみであったのに対し、今回は'A'-'D'の英字を含んでいます。CSoundのfunction table生成ルーチンGEN23では文字列や16進整数をパースすることができないので、function tableに入力データを読み込むに当たってGEN23を使うことはできません。

そこでここでは生成ルーチンとしてGEN01を使います。 GEN01は読み込み元ファイルを生の音声データとして扱い、その音声データをfunction tableに保存します。 GEN01にはいくつかのパラメータがありますが、今回重要なのは次の2つのパラメータです。

  • iskip: 音声ファイルの読み込みオフセット(先頭何秒を読み飛ばすか)を指定します。
  • iformat: 生音声データのエンコード方式を指定します。

ここでは最初からデータを読み出したいので iskip を0にしておきます。 iformat は-1の"8-bit signed character"を指定します。このようにすると1byteずつ16bit符号付き整数の音声サンプルと解釈されてfunction tableに格納されます。入力データのあるbyteを x とすると、function tableに格納されるサンプル y は次のC言語プログラムで表されます。

int8_t x; int16_t y;
y = (int16_t)x * 256

したがってfunction table 1に入力データを格納するためのf文は次のようになります。

f 1 0 0 -1"input.in"0 -1 0

次にinstrument定義の部分を説明します。 このコードで採用した判定アルゴリズムは、入力データ s を昇順にソートして s[1]*s[2]*s[3]*s[4]*s[5] % 72345 == 0 ならストレート、 s[5]==s[9] ならフラッシュとする方法です。

まずfunction table 1を11サンプルずつなぞるループを書きます。 function tableのサンプルを特定のウィンドウを使ってなぞるには tab2array opcodeのオプション引数 istartiend を使います。 それぞれ開始サンプルのインデックスと終了サンプルのインデックス+1を指定すればOKです。 こうすることで返り値の配列には指定した範囲のサンプルが格納されます。 またウィンドウ位置指定+ループカウンタとして余っていたinstrumentのパラメータ p3 を使っています。 CSoundのinstrument定義内ではscoreからinstrumentに与えられるパラメータ pn をi型の変数としてあつかうことができます。 これらを使うことでグローバル変数定義のための文字数を稼ぐことができます。 function tableを特定のウィンドウで区切らずなぞる方法もありますが、添字部分が長くなるので採用しませんでした。

sorta opcodeは配列を昇順にソートするopcodeです。元の入力データに対して下駄がついている+符号付きであるという状態ですが、この時点でサンプルが負になるようなケースがないことと、各データに下駄がついていてもソート結果には影響がないことからこの処理だけで要求を満たします。

その後はデータについている下駄とオーバーフローに注意しながら愚直に判定し、結果を出力します。 判定結果が偽であった場合もスペースを出力しているのは、fprintsで空白文字を出力するような制御パスがある場合に起こる問題を回避するためです。(以前出力した結果がくっついてしまう場合がある。処理系のバグ?)

Cubix

Green Team (@kotatsugame, 74 bytes)

.o..>F...'.@^^!->i?>1&%S/?\..>Noivq^'p<;-p..^<>.*i>&%o^;S>&q;;^.>v^!'2;<i

GNU ed

Red Team (@angel-p57, 48 bytes)

,s/^\(.\)\(.\1\)*.$/&F
v/\([1-5]\).*\1/s/./S
wq

  • 全行対象のフラッシュ対象行 F 付与置換、否定マッチのアドレス指定での一文字 S 置き換え、共に正規表現系の言語と共通と思います。
  • 実は sed 45B の実装が把握できず最初は短縮できてなかったのですが、rotary-o さんが実装を見つけられたため ed に適用できました。

Blue Team (@hatsusato, 48 bytes)

,s/^\(.\).\(\1.\)*$/&F
v/\([1-5]\).*\1/s/$/S
w
q

Egison

Green Team (@ten986, 368 bytes)

def v z:=match z as multiset char with
|#['1','2','3','4','5']->"S\n"
|_->"\n"
def w z:=match z as list char with
|[$s,#s,#s,#s,#s]->"F"
|_->""
def k z:=match z as list char with
|[$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,_]->appendString(w[a,c,e,g,i])(v[b,d,f,h,j])
def i ():=io(readChar())
def main x:=let s:=[i(),i(),i(),i(),i(),i(),i(),i(),i(),i(),i()]in
do
write(k s)
main 1

Blue Team (@drafear, 151 bytes)

def u l:=length(unique(l))+69
def main n:=do
write(pack(map itoc((\l->[u(take 5 l)+9,u(drop 5 l),10])(sort(map ctoi(unpack(io(readLine()))))))))
main 1

Red Team (@satos---jp, 142 bytes)

def main x:=let s:=unique(unpack(io(readLine())))in
let d q c:=itoc(length(union s(unpack q))+c)in
do
print(pack[d"ABCD"74,d"12345"64])
main 1

uniqがあるので文字種数を手軽に計算できS,F判定ができる。Stringライブラリは貧弱なのでpack/unpackによるstring<->list char変換とitoc/ctoiによるchar<->int変換が便利。printを使うと勝手に改行してくれる。

Element

Green Team (@EtoNagisa, 44 bytes)

50'[_5'[(,+.(2+"*']"12600=[S`]271%'![F`]\
`]

通常のスタックと,条件分岐に用いるコントロールスタックという2つのスタックをもつ言語 Sのほうは2を足した積が2520になればOKという方針 Fのほうはordを結合して271で割り切ればOKという方針 入力の文字列の先頭から一文字ずつ切り取り,結果を入力文字列の後ろにくっつけていくことでスタック操作の回数を減らせた コントロールスタックをうまく使うと,rotateが減らせる.rotateは4byteかかるので高すぎる forループは,コントロールスタックの先頭を見てその回数だけ繰り返すという仕様なので,ループの中に入ったあとはコントロールスタックの先頭は捨ててもいい.今回は,ループスタックの先頭を積を保存するために使っている.

型がガバガバなので,いろいろなことができる.スタックから無を取り出しても怒られないし,無と演算をしても大丈夫.

Blue Team (@drafear, 58 bytes)

50'[_10'[(,2^4 0@+0 2@#]0 2@13015%83+,#`95%35%25%-70+,#``]

Emojicode

Red Team (@kurgm, 298 bytes)

🏁🍇🔁👍🍇🎶🆕🔡▶️👂🏼❗️❗️➡️🖍🆕s🦁s🍇a🔡b🔡➡️🔢↩️↔a b❗️🍉❗️↪️🎼🆕🔡s🔤🔤❗️🔤12345🔤❗️🍇👄🔤S🔤❗️🍉↪️🐽s 5❗️🙌🐽s 9❗️🍇👄🔤F🔤❗️🍉😀🔤🔤❗️🍉🍉

整形

🏁 🍇
  🔁 👍 🍇
    🎶 🆕🔡▶️👂🏼 ❗️ ❗️ ➡️ 🖍🆕 s
    🦁 s 🍇 a🔡 b🔡 ➡️🔢
     ↩️ ↔ a b ❗️
    🍉 ❗️
    ↪️ 🎼 🆕🔡 s 🔤🔤 ❗️ 🔤12345🔤 ❗️ 🍇
      👄 🔤S🔤 ❗️
    🍉
    ↪️ 🐽 s 5 ❗️ 🙌 🐽 s 9 ❗️ 🍇
      👄 🔤F🔤 ❗️
    🍉
    😀 🔤🔤 ❗️
  🍉
🍉

擬似コード

main {
    while true {
        let mut s := String.new_readline().toGraphemesArray();
        s.sort({|a: String, b: String| -> Int
            return a.compareTo(b);
        });
        if String.new(s, "").beginsWith("12345") {
            print("S");
        }
        if s.getAt(5) == s.getAt(9) {
            print("F");
        }
        println("");
    }
}

Emojicodeは構文とかリファレンスがちゃんと整備されている神言語です(個人の感想)

あまり狙われていなかったっぽいのでゴルフはしてないです

  • 文字列から直接インデックスアクセスはできない。いったん書記素(文字列)の配列に変換する必要がある
  • 無限ループにしているが、最後は s.getAt(5) のところでエラーになる。エラー内容が stdout に出るけど51行目以降が無視される仕様に救われる

emojifunge

Green Team (@ten986, 512 bytes)

コードを見る
⬜️🔤💕❕⤵️🙃ℹ️🙃🔤🙃ℹ️🙃🔤🙃ℹ️🙃🔤🙃ℹ️🙃🔤🙃ℹ️🙃📥📤💕📥📏📤✖️📥📤💕📥📏📤✖️📥📤💕📥📏📤✖️📥📤💕📥📏📤✖️📥📤❕❕6️⃣➕🔟✖️🔡2️⃣➕5️⃣🏗2️⃣➕5️⃣🏗2️⃣➕5️⃣🏗2️⃣➕5️⃣🏗2️⃣➕5️⃣🏗✖️✖️✖️✖️4️⃣7️⃣9️⃣🔟✖️✖️✖️📏®️➕🔡🔤🔡0️⃣0️⃣✴️
⬜️⬜️⬜️⬜️🔚

盤面上争う必要性が薄かったため、ゴルフしていない。 第7回の方針をパクって計算を辻褄合わせたくらい。

Foobar and Foobaz and Barbaz, oh my!

(hiromi_miよりコメント) 言語機能が不足し回答不能と判断しました。差し替えました

Fish (pure)

Blue Team (@nakario, 82 bytes)

set b string replace -r
$b 'S(.)(.\1){4}' 'F$0' S(read -z)|$b 'S(.*(\d).*\2)' '$1'

readの-zオプションを使うと区切りがnull文字になるので全入力を一気にとってこれる。その呼び出し側にSをつけると最初の行だけでなくすべての行の頭にSがつく。これはコマンド呼び出し結果 (read -z)改行区切りで配列として解釈され、配列は展開時に前後の隣接する文字を伴って展開されるため。 replaceは-qオプションを付けない限り出力もしてくれるのでechoも不要。 RedチームのXSLTの手法を取り入れると77bytesまで縮んだ。

set b string replace -r
$b '(.)(.\1){4}.*' 'F$0' (read -z)S|$b '(\d).*\1.*' x

Green Team (@kotatsugame, 91 bytes)

set f string match -r
while read s
echo ($f '(\d).*\1|S' "$s"S) ($f 'F(.)(.\1){4}' F$s)
end

F# (.NET Core)

Blue Team (@n4o847, 111 bytes)

let rec f t=let s=Set(stdin.ReadLine())in printfn"%A"("_S"[(s-t).Count/5],"_F"[(t-s).Count/3]);f t
f(set"ABCD")

F# において stringchar seq として扱えることを利用した集合演算で、OCaml のように各文字コードを見ていく当初の方針よりも短くなった。

s を入力行の文字集合、tset ['A'; 'B'; 'C'; 'D'] とし、s - t (数字) が 5 種類なら 'S't - s (スート) が 1 種類なら 'F' を出力する。

C# 同様、タプルで出力すると少し短い。

Red Team (@sh-mug, 115 bytes)

let rec($)a b=let d=stdin.Read()-2 in d<0||d=8&&(printf"%c%c
"(char(a%41%26*5))(char(b%89+2));1$1)||b$a*d
let _=1$1

Go

Red Team (@rotary-o, 135 bytes)

package main
import."fmt"
func main(){for{s,i:="",0;Scan(&s)
for _,b:=range s{i|=2<<(b%32)}
Printf("%c%c\n",i>>16^47,i&(i-1%i)&60|70)}}
  • 1行ずつ処理で、1文字ずつビット列に保存
  • S、Fともにビット列で判定(nu さんのCがベース)
  • 終了条件を書かずに、%iでのゼロ除算例外で終了
  • ビット演算の優先順位が高いようなので括弧をつけている
  • 初めて書くのでよく分かっていない(Javaから移植しただけ)

Blue Team (@drafear, 140 bytes)

package main
import."fmt"
func main(){s:=0;for{a:=0;Scanf("%c",&a);if a==10{s>>=39;Printf(`%c%c
`,114-s%32,70-(s-32)&s>>9);}
s|=1<<(a-10);}}

golfish

Blue Team (@shinh, 57 bytes)

Green Team (@bx4ay, 50 bytes)

(実際はバイナリ)

01 c0 ed ec ff c0 c0 c0  01 c2 01 21 c3 01 12 c3
01 21 18 c3 01 12 c3 01  21 18 c3 01 12 c3 01 21
18 c2 01 12 df dd 11 d7  11 14 05 d7 da 12 10 05
01 05
  • Sの方針:数字5つの文字コードの積を233で割った余りを出力
  • Fの方針:2番目から5番目のアルファベットについて、最初のアルファベットと異なるものがあればG、そうでなければFを出力

GolfScript

Blue Team (@saito-ta, 22 bytes)

n%{$5/{.&,78+}/9-]n+}%

入力の行ごとに、ソートして、 5 文字ずつに分けて、それぞれ、文字の種類を数えて、それに 78 を足す。 後のほうからはさらに 9 を引く。するとあら不思議。

gs2

Blue Team (@saito-ta, 13 bytes)

ソース:

line-mode
sort
5
/
    group
    length
m2
"NE"
    +
z1

やっていることは、すぐ上にある GolfScript (22) とほとんど同じです。

Hanoi_Stack

Blue Team (@n4o847, 2812 bytes)

以前作った生成コード を使用。とりあえず通すことが重要なため、最初は 50 回のループを愚直に生成したら、バイト数制限に引っかかった。インタプリタにデバッグ機能をつけて確認しつつ、外側のループだけちゃんと書いて提出。その後赤チームに周囲を取られ、アクセスできなくなった。

2 bytes 以上の演算をしようとすると結果の上の方のバイトが失われるというバグ がある。

Red Team (@satos---jp, 204 bytes)

最初に入力が反転してstackに積まれるのでひっくり返す必要がある。byte長が2以上の演算がバグっているという噂を耳にしたが、1byte演算で事足りるアルゴリズムを使った結果それには出くわさなかった。インタプリタに現在のスタックと次の命令を出力する機能が欲しい。

Haskell

Green Team (@bx4ay, 76 bytes)

m@main=do f<-filter.flip notElem<$>getLine;print[f"12345S"!!0,f['A'..]!!4];m

main再帰で各行ごとに処理を行った。

  • "12345S"と各行の差集合をとって0番目の要素を出力(12345が全て含まれるときSを出力)
  • "ABCDEF..."と各行の差集合をとって4番目の要素を出力(ABCDのうち1つだけが含まれるときFを出力)

Blue Team (@n4o847, 78 bytes)

m@main=do h:x<-getLine;print$['S'|all(`elem`x)"12345"]:drop 3["F"|c<-x,c==h];m

ベースは shinh さんが書いたものを縮めた。緑チームと競い合っていたが、結果完敗……。行を複数回使う場合ポイントフリーにしづらく、do 記法でバインドするのがなんだかんだ短い。

ImageMagick

Red Team (@Muratam, 278 bytes)

convert ( -size 1x1 -depth 8 gray:- ) ( -resize '541x1!' -fx 'sc=0;xa=u[i];xb=u[i+2];xc=u[i+4];xd=u[i+6];xe=u[i+8];rr=u[i];rr=i%11==1&&xa!=xb&&xb!=xc&&xc!=xd&&xd!=xe&&xa!=xc&&xa!=xd&&xa!=xe&&xb!=xd&&xb!=xe&&xc!=xe?.324:rr;i%11==0?(xa==xb&&xb==xc&&xc==xd&&xd==xe?sc:.273):rr' ) -

二年前に書いた記事のやつが全然動かなくなってた...。 記事や過去は -resize を後に書いても動いていたが、前に書かないと u[i] が意図通り動いてくれなくなっていた。なぜかこのバージョンのImageMagickはいたるところがバグっていて、三項演算子(?:)の分岐が逆になることがあったり(は?)、変数宣言をするかどうかで結果が変わったり(は?)、三項演算子の中で三項演算子を使うと結果が宇宙的に変わったりする。例えばi%11==0?(xa==xb&&xb==xc&&xc==xd&&xd==xe?sc:.273):rrはフラッシュ判定ですが、全部同じなら無効文字を出すとなってミスってるように見えるが、実はなぜかバグって三項演算子の逆転が発生するので逆になって通ります。sc=0;の定数値の変数の利用も不要に見えますが、省略して直接値を書くと何故か結果が変わるので消せない...。条件式もだいたい&&で結合するとバグってうまく動かなくなるので無限に言い換えを試すと通るやつがあって、それがこのコードです。

なんでこんなバグるのか考えたけどきっとみんなImageMagickを画像処理にしか使ってなくて文字列処理用途としてのImageMagickはまったく保守されていないんだろうな...。うーん、まともに動いていた2年前のImageMagickにバージョン固定しておいたほうがいいですねこれは...。

hiromi_mi追記: 今回は v7.1.1-12 が使われました

Version: ImageMagick 7.1.1-12 Q16-HDRI x86_64 21239 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5) 
Delegates (built-in): 
Compiler: gcc (9.4)

IRC

Green Team (@ten986, 689 bytes)

コードを見る
* main has joined #code
* x has joined #code
* y has joined #code
* s has joined #code
* main sets mode: +v x
* main sets mode: +v y
* main sets mode: +v s
* main changes topic to 'a'
<input> Hey, x.
<if> x, are you equal to 0?
* main has quit
<s> I'm 1.
* main changes topic to 'b'
<input> Hey, y.
<s> I'm s times y.
<input> Hey, y.
<if> y, are you equal to 10?
<jump> Let's talk about c.
<if> y, are you not equal to x?
<x> I'm 99.
<jump> Let's talk about b.
* main changes topic to 'c'
<if> x, are you less than 98?
<x> I'm 70.
<s> I'm s divided by 4494.
<output> What's your character, s?
<output> What's your character, x?
<output> What's your character, y?
<jump> Let's talk about a.

@bx4ay さんのコードを気合いで縮めた。ロジックは分かってない。 終了は * main has quit IRC (Quit: ) ではなく * main has quit でも行けるらしい。 後日感想戦で@nu4nuさんによって*でいいことも分かった。今後のコードゴルフの定石になりますね。

Red Team (@satos---jp, 692 bytes)

コードを見る
* main has joined #code
* c has joined #code
* f has joined #code
* s has joined #code
* main sets mode: +v c
* main sets mode: +v f
* main sets mode: +v s
* main changes topic to 'h'
<s> I'm 1.
<input> Hey, f.
<if> f, are you equal to 0?
* main has quit IRC (Quit: )
* main changes topic to 'g'
<input> Hey, c.
<s> I'm s times c.
<input> Hey, c.
<if> c, are you not equal to f?
<f> I'm f times c.
<if> c, are you not equal to 10?
<jump> Let's talk about g.
<c> I'm 83.
<if> s, are you equal to 344362200?
<output> What's your character, c?
<c> I'm 70.
<if> f, are you less than 999?
<output> What's your character, c?
<c> I'm 10.
<output> What's your character, c?
<jump> Let's talk about h.

nu4nuさんから『Egisonが取れたときにどうぞ』と託されていたコードを提出したもの。 (nu4nu追記) 演算や制御のコストが高いので短い式で計算する必要がある(nu4nuは最初二乗和==13015のコードを書いていたが、satos---jpさんに積==344362200の形に直してもらった)。labelやjumpを減らすために改行コードの判定をループ末尾に持ってきている。 最後緑チームに(Quit: )の省略で抜かれてしまったが、これ実は*だけの行でもよくて、要は何にもマッチしない行を書いてインタプリタを異常終了させればよい。また、緑チームの4494で割って直接文字を出す(PHPのchrは256でmodを取ったものを出してくれる)手法を使うと、cを発言させなくできるので* main sets mode: +v cを消せる。これらを合わせると以下のような609Bのコードが得られる。

609Bのコード
* main has joined #code
* c has joined #code
* f has joined #code
* s has joined #code
* main sets mode: +v f
* main sets mode: +v s
* main changes topic to 'h'
<s> I'm 1.
<input> Hey, f.
<if> f, are you equal to 0?
*
* main changes topic to 'g'
<input> Hey, c.
<s> I'm s times c.
<input> Hey, c.
<if> c, are you not equal to f?
<f> I'm f times c.
<if> c, are you not equal to 10?
<jump> Let's talk about g.
<s> I'm s divided by 4494.
<output> What's your character, s?
<s> I'm 70.
<if> f, are you less than 999?
<output> What's your character, s?
<output> What's your character, c?
<jump> Let's talk about h.

Java

Red Team (@rotary-o, 159 bytes)

interface A{static void main(String[]a)throws Exception{for(int b,i=0;;System.out.printf("%c%c%c",i>>b+6^47,i&i-1&60|b*7,b,i=b/49*i|2<<b))b=System.in.read();}}
  • 余計な文字を出力してもよいことを盛大に利用(Cのmain再帰スタックオーバーフローが圧倒的に多いが、その次に多い出力バイト数と思われるw)
  • 1文字ずつ読んで、ビット列に保存
  • S、Fともにビット列で判定(nu さんのCがベース)
  • printfの条件を書かずに、出力文字をずらす
  • forの終了条件を書かずに、%cに-1を入れての例外で終了

Blue Team (@drafear, 161 bytes)

interface A{static void main(String[]x)throws Exception{for(int s=0,a;;s|=1<<a)if((a=System.in.read())<11)System.out.printf("%c%c\n",s&s-2&30^70,(s>>=12)^947);}}

jq

Blue Team (@drafear, 63 bytes)

split("")|sort|join("")|sub("(\\w)\\1{4}";"F")|sub("12345";"S")

Red Team (@kurgm, 49 bytes)

explode|unique|"\(.[4]!=53//"S")\(.[-2]>60//"F")"

uniqueがある(ソートもしてくれる)のがえらい。 あと負数インデックスで末尾から数えてくれるのも地味にえらい。

// がJSでいう || みたいなやつ。

LLVM 10 IR

Blue Team (@n4o847, 302 bytes)

define i8@main(i32%0){alloca i32call i32()@getchar()icmp slt i32%3,11br i1%4,label%a,label%b
a:sub i32%0,2and i32%5,%0xor i32%6,7151687store i32%7,i32*%2call i8(...)@puts(i32*%2)call i8@main(i32 1)ret i8 0
b:shl i32 1,%3or i32%10,%0call i8@main(i32%11)ret i8 0}declare i32@getchar()declare i8@puts(...)

tails (saito-ta) さんの C 言語の提出を少しいじって

main_(s){
    int a=getchar();
    main_(a<11?s=s&s-2^'m G',puts(&s),1:s|1<<a);
}

とし、LLVM IR を吐き出した結果を削っていった。

当初は %1 → %a → %c %1 → %b → %c のような制御フローグラフで、%c 中で @main を呼び出して ret していたが、引数のために %c の中で phi を使うよりも、%a%b の中で直接呼び出したほうが短いので上のようになったというのが非自明で面白い。

手で書くと、命令を挿入・削除したときに番号がずれるのがとてもめんどい。

(nu4nu追記) ラベルを自動採番のものにすれば297Bになる。

define i8@main(i32%0){alloca i32call i32()@getchar()icmp slt i32%3,11br i1%4,label%5,label%11sub i32%0,2and i32%6,%0xor i32%7,7151687store i32%8,i32*%2call i8(...)@puts(i32*%2)call i8@main(i32 1)ret i8 0shl i32 1,%3or i32%12,%0call i8@main(i32%13)ret i8 0}declare i32@getchar()declare i8@puts(...)

さらにmain再帰をやめてbr+phiでビットマスクを作ると283B。allocaがあるおかげでjumpでもスタックオーバーフローが起こせる。main再帰でphiが避けられるのは魅力的だが、いかんせんcallが長い上に無駄なretを書かされるのがもったいない。常にs|1<<aを計算するようにすることで無駄なjumpを消せるのも大きい。(追記: LLVM 10 IRという名前になっているが実際はLLVM 15 IRなのでi32*ptrで置き換えられる

define i8@main(){br label%1phi i32[1,%0],[1,%7],[%5,%1]call i32()@getchar()shl i32 1,%3or i32%4,%2icmp slt i32%3,11br i1%6,label%7,label%1alloca i32sub i32%2,2and i32%9,%2xor i32%10,7151687store i32%11,ptr%8call i8(...)@puts(ptr%8)br label%1}declare i32@getchar()declare i8@puts(...)

Red Team (@nu4nu, 358 bytes)

@s=global i64 44613067557define i8@main(){br label%1phi i32[0,%0],[0,%5],[%13,%11]call i32@getchar()and i32%3,8switch i32%4,label%5[i32 0,label%11]sdiv i32%2,375add i32%2,-1and i32%7,%2xor i32%8,70call i8(...)@printf(i64*@s,i32%6,i32%9)switch i32%2,label%1[i32 0,label%14]shl i32 1,%3or i32%12,%2br label%1ret i8 0}declare i32@getchar()declare i8@printf(...)

C言語のコードの時点で負けていると勝てない。printfのフォーマット文字列を書く参考にどうぞ。

LOLCODE

Red Team (@karakasaDcFd, 480 bytes)

HAI 1
CAN HAS STRING?
I HAS A s
IM IN YR l UPPIN YR i TIL BOTH SAEM i AN 50
I HAS A y ITZ 1
I HAS A z ITZ 0
GIMMEH s
I HAS A f ITZ I IZ STRING'Z AT YR s AN YR 0 MKAY
IM IN YR m UPPIN YR j TIL BOTH SAEM j AN 10
I HAS A c ITZ I IZ STRING'Z AT YR s AN YR j MKAY
MOD OF j AN 2
WTF?
OMG 0
DIFFRINT c AN f
O RLY?
YA RLY
y R 0
OIC
GTFO
OMG 1
z R SUM OF z AN PRODUKT OF c AN c
OIC
IM OUTTA YR m
y
WTF?
OMG 1
VISIBLE "F"!
OIC
z
WTF?
OMG 55
VISIBLE "S"!
OIC
VISIBLE ""
IM OUTTA YR l
KTHXBYE

Make

Blue Team (@saito-ta, 140 bytes)

f=$(word $1,$(foreach i,$2,$(word $3,$(subst $i, ,x$ax))) $4)
$(foreach a,$(STDIN),$(info $(call f,6,1 2 3 4 5,2,S)$(call f,2,A B C D,6,F)))

ストレートの処理とフラッシュの処理を 1 つの関数にまとめることができた。 入力の各行に含まれる特定の文字を空白に置換することによっていくつかのワードに分解し、ワードの数によって S や F が出力されるようにしている。例えば、入力行(の前後にダミーの文字を付けたもの)を A の文字で分割した結果が 6 つのワードになったら、それはフラッシュ。

Maybe Later

Green Team (@ten986, 120 bytes)

whenk is1{b=p[i++]if(b>0)f=f*ordb elseif(a!=b)l=0k=9!=i%11}p=readi=0whenq is0{a=p[i]l="F"k=f=1printl+chrf%233i+=2q=0}q=0
改行とスペースを加えて読みやすくしたもの ``` when k is 1{ b=p[i++] if(b>0) f=f*ordb else if(a!=b)l=0 k=9!=i%11 } p=read i=0 when q is0{ a=p[i] l="F" k=f=1 print l+chrf%233 i+=2 q=0 } q=0 ```

イベント駆動型言語で、whenの条件を満たした瞬間にブロックを実行します。 前半のブロックでは各ケースの入力と評価、後半のブロックでは初期化及び前半で評価した値を出力します。各ブロックの最後でq=0のように条件を満たすよう代入することでループしてくれます。51行目以降はFを出力し続けますが、制約上問題ありません。

if(b>0) で数値かどうか判定でき(?)、数値はストレート判定、それ以外はフラッシュ判定に使います。フラッシュ判定は1枚目をaで受け取りif(a!=b)で判定すればOK。ストレートはordの積をmod233したものを出力するアルゴリズムを使います。

ゴルフ的には、空白や改行をかなり取り除いてもよしなにしてくれる性質を生かして、代入時は数値を後ろに持っていくと空白分の1byteが節約できるみたいなテクがあります。ifのカッコも省略できるとのことで、このコードは3byte削れますね。

Blue Team (@drafear, 115 bytes)

whenf is0{x=ordl[i++]a+=x*x
ifx<11{print((chr70-((a%665)%61)%51)+chr83+b%13015)a=b=0}c=a
a=b
b=c
f=0}l=readi=0-1f=0

Mines

Green Team (@dnek, 463 bytes)

***********...
........*.***.
.*.***********
1,0
7,1
1,1
0,1
0,1
1,1
0,1
7;1
2,2
0,2
2,2
0;2
2,2
0;2
2,2
0,2
0,1
0;2
0,1
1;1
2,2
2,2
2,1
2,2
0,1
1,1
0;2
0;2
2,1
7;1
2,2
7;1
3,1
4,1
5,1
6,1
9,1
11,0
12,0
13,0
13,1

0;0
4,1
3,1
2,2
2,1
2,1
2,2
0;2
0;2
0,1
7,1
0;2
5,1
2,1
9,1
2,2
2,1
0,1
5;1
0;0
6,1
2,2
2,1
2,2
2,2
2,1
0;0
1,1
2,2
0,1
1,1
0;2
11,0
2,2
2,2
2,2
2,2
2,1
0,1
2,1
0,1
0;0
1,1
5;1
2,2
5;1
2,1
7;1








0,1
9,1
2,2
2,1
2,1
0;0
0;1
12,0
0,1
2,1
0;0
4,1

方針

英字のordを6bitずつshiftした和をX、数字のord+2の積をYとする。

各行につき以下を行う。

  1. 2文字ずつ読んでXとYそれぞれに適用する。
  2. chr(X%151>0?64:70)chr(X-2520==0?83:84)と改行を出力する。

プログラムの流れは解釈を参照。

分岐によってあるマスを開ける動作が最初に行われる行が変わるので、その管理が重要である。

なお、リセット前も含めて全部開ければゲームクリアという仕様を忘れていたため、クリア前に無駄に開けているマスがいくつかある。よって自明に縮む。

ロジック自体も弱いのでもっと縮む。

解釈(1lは数字1のマスを左クリック)

***********...
........*.***.
.*.***********
1,0 #9l reset(l) 1行出力後はskip_Dから来る;それ以外は最終行から来る
7,1 #7p push7
1,1 #4p push4
0,1 #3p push3
0,1 #3l add
1,1 #4l sub
0,1 #3l add 初回は引数不足で[0];以降は前回のpush6があるので[6]
7;1 #7r skip_A 初回はskip0;以降はskip6
2,2 #2p push2
0,2 #1p push1
2,2 #2l dup
0;2 #1r not
2,2 #2l dup
0;2 #1r not
2,2 #2p 初回はdup;以降はskip_Aから来てpush2
0,2 #1p 初回はpos;以降はpush1
0,1 #3l add
0;2 #1r not
0,1 #3l add 初回は[2, 1, 0];以降は[2, Y, X]
1;1 #4r in(c)
2,2 #2l dup
2,2 #2l dup
2,1 #5p push5
2,2 #2l dup
0,1 #3l add
1,1 #4l sub
0;2 #1r not
0;2 #1r not 英字なら[..., 英, 英, 1];改行なら[..., 10, 10, 0];EOFなら[..., 0, 0, 1]
2,1 #5l multi
7;1 #7r skip_B 英字ならskip65-68;改行かEOFならskip0
2,2 #2l dup
7;1 #7r skip_C 改行ならskip10;EOFならskip0
3,1 #5p push5
4,1 #6p push6
5,1 #6p push6
6,1 #6p push6
9,1 #8p push8
11,0#4p push4
12,0#2p push2
13,0#1p push1
13,1#3p push3 全部開けてクリア

0;0 #f  swap skip_Cから来る
4,1 #6p push6
3,1 #5p push5
2,2 #2l dup
2,1 #5l multi
2,1 #5l multi
2,2 #2l dup
0;2 #1r not
0;2 #1r not
0,1 #3l add
7,1 #7l mod X%151を判定
0;2 #1r not
5,1 #6p push6
2,1 #5l multi
9,1 #8p push8
2,2 #2l dup
2,1 #5l multi
0,1 #3l add topが(X%151>0?64:70)
5;1 #6r out(c)
0;0 #f  swap
6,1 #6p push6
2,2 #2l dup
2,1 #5l multi
2,2 #2l dup
2,2 #2l dup
2,1 #5l multi
0;0 #f  swap
1,1 #4l sub
2,2 #2l dup
0,1 #3l add
1,1 #4l sub X-2520を判定
0;2 #1r not
11,0#4p push4
2,2 #2l dup
2,2 #2l dup
2,2 #2l dup
2,2 #2l dup
2,1 #5l multi
0,1 #3l add
2,1 #5l multi
0,1 #3l add
0;0 #f  swap
1,1 #4l sub topが(X-2520==0?83:84)
5;1 #6r out(c)
2,2 #2l dup
5;1 #6r out(c) 入力で取った改行を出力[2, 10]
2,1 #5l multi
7;1 #7r skip_D skip20





# Aのときskip_Bから来る
# Bのとき
# Cのとき
0,1 #3l add # Dのとき
9,1 #8p push8
2,2 #2l dup
2,1 #5l multi
2,1 #5l multi
0;0 #f  swap
0;1 #3r in(n)
12,0#2p push2
0,1 #3l add
2,1 #5l multi
0;0 #f  swap
4,1 #6p push6

Minus

Green Team (@kotatsugame, 736 bytes)

コードを見る
a-=1
b-=50
v-=v
v-=i
d-=d
d-=i
w-=w
w-=i
e-=e
e-=i
x-=x
x-=i
f-=f
f-=i
y-=y
y-=i
g-=g
g-=i
z-=z
z-=i
h-=h
h-=i
k-=k
v-=w
A-=1
p-=v
c-=A
c-=a
k-=a
p-=p
A-=a
w-=x
A-=1
p-=w
c-=A
c-=a
k-=a
p-=p
A-=a
x-=y
A-=1
p-=x
c-=A
c-=a
k-=a
p-=p
A-=a
y-=z
A-=1
p-=y
c-=A
c-=a
k-=a
p-=p
A-=a
k-=4
A-=1
p-=k
c-=A
c-=a
o-=186
p-=p
A-=a
z-=z
z-=1
x-=x
x-=d
x-=53
y-=y
c-=x
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
x-=x
x-=e
x-=53
z-=z
c-=x
z-=y
z-=y
z-=y
z-=y
z-=y
z-=y
z-=y
x-=x
x-=f
x-=53
y-=y
c-=x
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
x-=x
x-=g
x-=53
z-=z
c-=x
z-=y
z-=y
z-=y
z-=y
z-=y
z-=y
z-=y
x-=x
x-=h
x-=53
y-=y
c-=x
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
y-=z
y-=2520
A-=1
p-=y
c-=A
c-=a
o-=173
p-=p
A-=a
z-=i
o-=246
b-=a
A-=1
p-=b
c-=A
c-=a
c-=z
p-=p
A-=a
c-=142

(補足:じつは -= と改行は取り除くことができるので、このコードはそのまま 305 bytes になります。)

moo

Red Team (@nu4nu, 43 bytes)

1{sdi=*si2+*}0{ii2+1c1c1c1c27+oi<69+oe0c}0c

Fは最初の文字を比較用に覚えておいて不一致を見つけたら比較用文字自体を0に落とす方針。大小比較で0,1が積まれるので掛けるだけでよい。最後に読んできた改行コードと比較した結果に69を足せば、条件を満たすときF、満たさないときEが出る。 Sは文字コードに2を足したものの積に27を足して出力。文字出力はCのputchar相当の実装になっているので上のほうのビットは無視される。 改行文字を出す命令eがあるのが面白い。これのおかげでFの判定に10を使うことができている。

Node.js

Blue Team (@drafear, 96 bytes)

require('fs').readFileSync(s=0).map(a=>s=a-10?s|1<<a-49:console.log("S"[~s&31],s&s-32&~31||"F"))

Green Team (@bx4ay, 119 bytes)

f='',s=1;require('fs').readFileSync(i=0).map(v=>++i%11?i%2?f+=v:s*=v:console.log(f%271||'F',s%44520||'S',f='',i=0,s=1))

Red Team (@nu4nu, @shundroid, 94 bytes)

require('fs').readFileSync(i=0).map(b=>i=b<11?console.log(i>31<<17&&"S",i&i-1&31||"F"):i|1<<b)

他の言語同様ビットマスクで読んだ文字を管理する方針。nu4nuは最後に(i&i-1)%32i&i-1&31に変えただけで、そこまでの短縮は@shundroidさんによるものです。

OCaml

Blue Team (@n4o847, 136 bytes)

let rec f?(a=input_byte stdin)?(p=print_string)s=a>10&f(s lor 1 lsl(a-49))||s mod 32>30&()=p"S";s land(s-32)<32&()=p"F";()=p"
";f 0;;f 0

文字コードを見ていく方針。チーム内ではビット演算を用いる手法が共有されていたが、OCaml でビット演算は文字数がかかってしまった。

Red Team (@sh-mug, 112 bytes)

let rec($)a?(d=input_byte stdin+10)?(o=output_byte stdout)b=d=20&(o(a mod 782/60*7);o(b+75);o 10;1$1);b$a*d;;1$1

Perl

Red Team (@rotary-o, 42 bytes)

print/(\d).*\1/||S,s/(.)(.\1){4}/F/r for<>
  • S、Fともに正規表現で判定(同じ数字が5つでもFになってしまう…)
  • 改行をつけるのを省略するために、置換したものを使う(余計な文字を出力してもよいことを利用)

Blue Team (@saito-ta, 42 bytes)

print/(\d).*\1/||S,s/(.)(.\1){4}/F/r for<>

同上です。同じカードが重複して来ることはないと思い込んでいて、スートが 4 つしかないので同じ数字が 5 個来ることはないと思っていましたが、そうするとフラッシュが必ずストレートになっちゃうんですね。そうか…

Green Team (@kotatsugame, 42 bytes)

print/(\d).*\1/||S,s/(.)(.\1){4}/F/r for<>

Perl 6

Blue Team (@shinh, 51 bytes)

say (/(\d).*$0/||"S")~(/(.)[.$0]**4/&&"F")for lines

Crystal のとこに書いた A1B1C1D1A1 で F を出しちゃうやつになってしまっている……

Red Team (@rotary-o, 51 bytes)

say rx:P5/(\d).*\1/||:S,!m:P5/(.)(.\1){4}/for lines
  • S、Fともに正規表現で判定(同じ数字が5つでもFになってしまう…)
  • Perl 6の正規表現が分からなかったので、:P5でPerl 5のものにしている
  • mだとマッチしたものになり、改行が入ってしまうのでrxを使用
  • FはFalseのFを使用
  • \1$0にして、{4}をなくせば:P5にしなくてよいっぽいので、
    say /(\d).*$0/||:S,!m:5x/$(m/./)/for lines
    say /(\d).*$0/||:S,5>m:g/$(m/./)/for lines
    say /(\d).*$0/||:S,!/^(.)[.$0]**4/for lines
    say /(\d).*$0/||:S,!/^(.).$0**4%./for lines
    say /(\d).*$0/||:S,!/^(.).[$0.]*$/for lines
    のように42Bまで縮みそう(%でjoinできる!)

PicFunge

Blue Team (@shinh, 108 bytes)

<|-*25:_@#+1:~
^>$"_"%"#"%55*%"F"\-,v
^,*25,+"S" %*"_[\x89]"++++<
v>:*+~:*\

Befunge-98 を 93 にして、 befunge93topicfunge.py を使わせてもらっただけですが、 200B くらいのが出てきて、「元は 80B くらいだぞ、 LZ 仕事しろ」と思って

im.save('out.webp', lossless=True)

と webp で出力したらだいぶ減ったので良しとしました(がまだ元のサイズよりデカい)

Green Team (@ten986, 222 bytes)

Piet

Red Team (@nu4nu, 143 bytes)

疑似コードを見る
push 1      // EOF時-1を作る用。通常のループでは最後に捨てる
in(char)
push 2
sub
push 1
not
--- loop start ---
in(number)  // S側は数字の二乗和
dup
mul
add
push 2
push 1
roll

in(char)    // F側は文字コード-2の積
dup
push 11
greater
dup
add
pointer     // 改行でなければ折り返す
push 2
sub
mul
push 2
push 1
roll
--- loop end ---

push 3      // 改行文字をスタックの後ろに送る
push 1
roll

push 6      // F側の%41
dup
dup
mul
add
push 1
sub
mod

dup         // EOF時は-1が積まれているので
push 1      // ここで270度回って下のL字領域に落ちる
add
not
dup
dup
add
add
pointer

push 5      // F側の%26
dup
mul
push 1
add
mod

push 5
mul
out(char)   // F

dup         // S側は2倍して-27
add
push 3
dup
dup
mul
mul
sub
out(char)

out(char)   // 改行
out(number) // ゴミを出しながら左上に戻る

F側は$\prod (s_i-2)%41%26\times 5$、S側は$(\sum d_i^2) \times 2 - 27$を出力している。 計画性なく書き始めた結果16色に収めるのが大変で時間を食ってしまい、8色との比較などをやる余裕がなかった。同色が連続するところ以外ほぼ圧縮できていないので、8色のほうが縮んだ可能性もある。そもそもこの式がよいのかも不明。

プロデル

Green Team (@dnek, 105 bytes)

元はShift JIS

50回繰り返
l=コンソール:受け取
(83+(正規表現:lが「(\d).*\1」に合致))のキャラクタ&(65+(l:lの1番目を数え))のキャラクタを報告

比較的分かりやすい日本語なのでロジックは読めば分かる。

今回初めて正規表現のドキュメントを読んでいたところ、助詞のから:で置換可能であることに気付いた。

SWI-Prolog

Blue Team (@n4o847, 109 bytes)

f(S):-get0(A),A>0->(A>10->f(S\/1<<(A-49));(S/\31>30->put("S");!),(S/\(S-32)<32->put("F");!),nl,f(0)).
:-f(0).

文字コードを put すれば if (->, ;) がなくせるが、計算結果を is で確定させる必要があって微妙に縮まず、苦戦していた。赤チームがちゃんとやってくれていたので完敗。

Red Team (@shundroid, 100 bytes)

m(Y):-get0(N),(N=10,X is Y>>16+21,put(X),Z is Y-1/\Y/\31+70,put(Z),nl,m(0);m(Y\/2^(N/\31))).
:-m(0).

最初は Prolog らしい述語を用いて解いていたが、最終的に、1文字ずつ ASCII コードを読み取って bitwise or していくというアルゴリズムはほかの言語とほとんど同じになってしまった。

  • Prolog も実はエラーで落とせる。 get0 はずっと読んでると落ちる。
  • (A; B) で、ほかの言語でいう A || B のような意味合いになる。
  • Prolog では ^ はべき乗を表す。 1<<x より 2^x のほうが 1 バイト短い

Pxem

Red Team (@shundroid, 97 bytes)

.iab.-.waa.-ab.-.v.c.cak.-.x.c.t.-.+.m.v.i.!.v.i.cak.-.a.s.sF.+.o5'#.!.!.%S.+.oak.-.o.i.cab.-.+.a

Green Team (@kotatsugame, 63 bytes)

(コピー&ペーストに失敗しました)

Python 2

Blue Team (@ToE42, 67 bytes)

while 1:l=sorted(raw_input());print len(set(l[:5]))/5*"S",l[5]<l[9]

"False"にFが含まれることを利用. 単スラッシュはPython2では3と異なり整数除算(切り捨て)らしいので,len(set(l[:5]))が5のときのみ1となりSが出力される. 演算子の優先順位に注意.

Red Team (@kurgm, 67 bytes)

while 1:a=sorted(set(raw_input()));print" S"["5"in a[4:]],a[-2]>"5"

文字コード順で sort して uniq したとき

  • S条件(12345 が揃っていること)と、5文字目が 5 であることが同値
    • ただし uniq 結果が4文字以下になる可能性があるので a[4]=="5" はできない。 a[4:] はできる(空文字列になる)
  • F条件(ABCD のうち1つだけがあること)と、最後から2文字目が数字であることが同値

あとは False に F が含まれること(と True は無視されること)を利用。最後は EOFError で落ちる。

感想戦 (@dnek, 63 bytes)

while 1:l=sorted(set(raw_input())|{"6S"});print l[5:],l[-2]>"@"

基本的にRed Teamと同じだが、"6S"を加えてソートすると数字と英字の間に来る。 S条件のときはl[5:]の先頭に"6S"が含まれるのでそのまま出力すれば良い。 なおF条件は"6S"か英字との比較になる。

Python 3

感想戦 (@dnek, 55 bytes)

while l:=sorted({*input(),"6S"}):print(l[5:],l[-2]>"@")

Python3は感想戦で追加された。

基本的なロジックは Python2と同じ。 Python3では*演算子を使ってiterableをunpackできるので、"6S"を併記すればまとめてsetの初期化ができる。

R

Blue Team (@wass88, 92 bytes)

writeLines(sub("^(?!.*(\\d).*\\1)","S",sub("((.).(\\2.){4})","\\1F",readLines("stdin")),,1))

否定先読みを含む正規表現で2回バシバシたたくだけ。下のRed Teamの提出のほうがおもしろい。

Red Team (@platypus999, 94 bytes)

for(x in scan("stdin",""))cat("S"[!sd((y=sort(utf8ToInt(x)))[1:5]-1:5)],"F"[!sd(y[6:10])],"
")

配列中のすべての数値が等しい場合 0 になる sd 関数 (標準偏差を返す関数) を使いました。 文字コード変換+ソートした配列を y とし、sd(y[1:5]-1:5) が 0 になったら S、sd(y[6:10]) が 0 になったら F です。 (R 言語は 1-based index で、すべてのオブジェクトは長さ 1 の配列らしく、"X"[!sd(hoge)]sd(hoge) が 0 になるときのみ "X" を返します。)

Racket

Blue Team (@shinh, 136 bytes)

#!racket
(let m()(let([l(read-line)])(println`(,(if(regexp-match #px"(\\d).*\\1"l)"""S"),(string-replace l #px"^(.)(.\\1)*.$""F"))))(m))

Red のような方針だったのですが、 split/sort/join 長いなぁ……ということで sort 無しにしてみた感じでした。 (list S F) の形だったのを、 lisp て quote と escape があるよねえ、と GPT-4 に聞いたらバッククォートとカンマを教えてくれました

Red Team (@sh-mug, 136 bytes)

#!racket
((λ(r)(let m()(writeln(r(r(list->string(sort(string->list(read-line))char<?))"12345""S")#px"(..).\\1""F"))(m)))string-replace)
  • charのリストにする→ソートする→stringに戻す→12345Sに変換→(..).\\1(正規表現)を"F"に変換
  • lambdaλ にできる

Recurse

Green Team (@kotatsugame, 78 bytes)

$
>?}?{FFFF7{{3}a}m{a!5{}a{{{3}}a}a}m{a}r!?!$<
$
F
>3{m{?}a{5{6}a{}m}r}?}m{<
F
  • 70B 感想戦@siotouto
$
>?}?{FFFF7{}3}a{{m{{a![}m{3}d}r!?!$<
$
F
>3{m}?{a{5{6}a{}m}r}?}m{<
F
  • 緑チームのコードを若干縮めました
  • 緑チームのコードは2桁以上書ける場合こうなります
    • $ := >?}?{FFFF70{a!233}r!?!$<
    • F := >3{m}?{a{121}r}?}m{
  • 7と3でなんか良い感じに作ります
    • 10 == 7+3
    • 70 == 7*10
    • 233 == 70*10/3
  • 68B 感想戦@siotouto
$
>?{?{FFFF?{}}7{m}}m{3}d}r!5{6}a{}m}r{a!]!$<
$
F
>[}3}m}?{a{?{m{<
F
  • 10って改行コード利用すれば良くて・・・
    • 121の生成を外に出してみたけどあんまり変わらない

Red Team (@siotouto, 81 bytes)

$
>6F4{m{1{IIIII8Fr{5Fr{5}m!3F{m}r!?!$<
$
I
>?{2}s}m}?{}m}a{]{<
I
F
>{5}m{1}a}<
F
  • F判定は((Product(文字コード-2))%41%26)*5
    • 赤チームの謎の式
  • S判定は(文字コードの2乗和+124)%256
  • 2桁以上の数はあまり得意ではない言語
    • 5倍して1足す関数を用意
      • 41, 26, 124=31*4, 256=16*16

Ring

Blue Team (@nakario, 80 bytes)

while 1{get a
s=0
for c in a{s|=1<<dec(c)}x="F"if s&s-64&-63{x=""}?x+char(s+21)}

Ringの入出力で最短はgetと?。Ringはcharを16進数で解釈してintにする操作dec()が他の言語に比べて軽いのでそれを使ったロジックになっている(bit演算のところは @drafear がやってくれた)。dec()で失敗するので無限ループが使えるというのはその後で気付いた。char()は下位8bitしか見ないので、ストレートの際の0b111110からSを低コストで生成できる。

Green Team (@kotatsugame, 98 bytes)

for i in 1:50{give a
t=1
f=65
for c in a{if isdigit(c)t*=48+c ok
f+=(a[1]=c)}?Char(t%233)+Char(f)}

Ruby 3.2.2

Green Team (@kotatsugame, 48 bytes)

#!ruby -n
putc~/(\D).(?!\1)/+62
p~/(\d).*\1/||?S

Blue Team (@shinh, 48 bytes)

#!ruby -n
p [~/(\d).*\1/||?S,~/(.)(.\1){4}/&&?F]

Red Team (@rotary-o, 48 bytes)

#!ruby -n
p [~/(\d).*\1/||?S,~/(.)(.\1){4}/&&?F]
  • S、Fともに正規表現で判定(同じ数字が5つでもFになってしまう…)
  • 余計な文字を出力してもよいことを利用して、pで出力

Ruby 0.49

Blue Team (@drafear, 77 bytes)

s=0
while a=getc()
a>10||printf("%c%c
",s>>47^47,(s>>=65)&s-1^70)
s|=1<<a
end

l ごとに 1<<l[i]| で集めて s とすると s=0b11110...01111000... のように集合が2ブロックに分かれる

S(83)(s>>47)&1270b1111100=124 のときに出力したいが、出力時に自動的に 256 でmodが取られるので s>>47124^83 とXOR取ればよい。このときに代わりに F(70) が出ることはない、なぜなら SF はLSBが異なる一方で入力によって変化しないからである。

F(70)s>>65 が1ビットしか立っていないときに出したい。x&x-1 をすると x の下から2番目のビットが取れる。すなわち (s>>=65)&s-10 のときに F を出力したいので、(s>>=65)&s-1^70 とした。このとき代わりに S(83) が出ることはない、なぜなら4スートしかないので、83^70=212^4 の ビットは入力によって変化しないからである。

改行コードも s|=1<<a

全てにおいて無視され、s>>=65 で消える

s=0 サボり

s>>=65 によって関係ない部分にビットが移動し、次の s>>=65 で消える

Green Team (@kotatsugame, 83 bytes)

while gets()
$_=$_.split(//).sort.join
print('S'*~/12345/,'F'*($_[6]/$_[10]),$/)end

Rust

Blue Team (@nakario, 174 bytes)

use std::io::*;fn main(){stdin().bytes().fold(0,|s,a|{let b=a.unwrap();if b>10{s|1<<b-49}else{print!("{}{}
",if s&31>30{"S"}else{""},if s>>16&(s>>16)-1>0{""}else{"F"});0}});}

1byteづつ読むならread()ではなくbytes().foldが良い。判定ロジックは自チームの他言語からもらってきた。

Red Team (@Muratam, 181 bytes)

use std::io::*;fn main(){for _ in 0..50{let s=&mut[0;11];stdin().read(s);let i=s.iter().fold(0,|a,b|a|2<<*b%32);s[0]=(i>>16^47)as u8;s[1]=(i&(i-1%i)&60|70)as u8;stdout().write(s);}}

赤チームのGoのを持ってきてごにょごにょしただけ

Sceql

sed

Red Team (@rotary-o, 45 bytes)

s/^\(.\)\(.\1\)*.$/F&/
/\([1-5]\).*\1/!s/^/S/
  • S、Fともに正規表現で判定
  • Sは、/.../!でマッチしない場合に追加
  • 余計な文字を出力してもよいことを利用して、元の文字列はそのまま

Blue Team (@hatsusato, 45 bytes)

s/^\(.\).\(\1.\)*$/&F/;/\([1-5]\).*\1/!s/$/S/

SNOBOL4

Blue Team (@nakario, 232 bytes)

 DEFINE("Z(S,A)")
S I =INPUT :F(END)
 I =Z(I,"A")
 I =Z(I,"B")
 I =Z(I,"C")
 I =Z(I,"D")
 Y =
 I 1 :F(O)
 I 2 :F(O)
 I 3 :F(O)
 I 4 :F(O)
 I 5 :F(O)
 Y ="S"
O OUTPUT =I Y :(S)
Z S A ARB A ARB A ARB A ARB A ="F" S
 Z =S :(RETURN)
END

パターンマッチを採用した古い言語。文字列処理が肝になる今回では比較的やりやすい部類の言語だが、ほぼ誰も手を付けてなかったためあまりゴルフされていない。 関数はDEFINEで定義できる。呼び出すと関数名に指定したラベル(行頭のZとかSとかOとか)に飛び、関数名と同じ名前の変数に代入してRETURNにジャンプすることで戻り値を返す。終了後に気付いたが関数呼び出しはネスト可能なので、36行目をまとめてOUTPUTの行に移動させると24 bytes削減できる。 フラッシュ判定は上記の関数で同じスートが5連続マッチするかどうかを見ている。ARBはarbitraryの略で任意の文字列に最小マッチする。 ストレート判定は15の数字を順番にマッチさせて、マッチに失敗したらOUTPUTの行に飛び、全てにマッチした場合のみ変数Yに"S"を代入することで実現している。Yの初期化で右辺が不要なのは前回のWrite upから知った。

延長戦 (@nakario, 126 bytes)

S I =INPUT :F(END)
 I @N BAL . X
 Y =X ARB
 I Y Y Y Y Y ="F" I
P N =N + 1
 I N :F(O)
 N 5 :F(P)
 I ="S" I
O OUTPUT =I :(S)
END

BALは開き括弧始まりの場合対応する閉じ括弧までの部分文字列にマッチするが、それ以外の場合1文字以上の最短マッチとなるので、行頭の1文字にマッチする。マッチ結果をキャプチャ演算子(".")でXに保存。カーソル位置代入演算子("@")でNを0で初期化している。ストレート判定をループにしたくらいでアルゴリズム的には上のものと同じまま、100 bytes以上縮められた。

Red Team (@ikubaku, 232 bytes)

FIXME: 嘘解法。修正して良い感じの長さになるか後で確かめてみます。

S	A = INPUT	:F(T)
	P = ANY('ABCD') . S
	Q = ANY(12345)
	A (P REM)
	R = (S Q S Q S Q S Q S Q) . B
	A (P (Q . D) P (Q . E) P (Q . F) P (Q . G) P (Q . H))
	A R = 'F'
	I = D * E * F * G * H
	I 120 = 'S'
	OUTPUT = A I	:(S)
T	OUTPUT =
END

Snowman

Blue Team (@drafear, 48 bytes)

~:vgAsOdU(5aAwR5aR("F"sR"12345""S"sRsP"
"sP;50bR

l=input().sort() l.sub("12345","S").sub(l[0]*5,"F")

Starry

Red Team (@nu4nu, 195 bytes)

 `      + + ,`       + *  *  + , +  **  + , +          + +* *'   +          + +   + + + + +**   +  *      +* +   +*  +   +    *  +    *  * .        + + +  * +   +  *   + + +  *  + *    ** . + . '

即値を作るコストが高く、1を足したり引いたりするのもシャレにならない文字数がかかるので、どれだけ都合のいい即値を使った短い式が作れるかの勝負となる。こちらのコードでは、F側は$\prod (s_i-2)%41%26\times 5$、S側は$(1+\sum s_i^2)%72+27$で計算しているが、緑チームの$\sum(3^i s_i)%121+70$や$\prod s_i%233$のほうが即値が作りやすく短くなっている。(ところで$\sum(3^i s_i)%121+70$はBAABBなどでSが出てしまうので修正が必要。dup+dup+add+addで3倍にする(6B増える)のが簡単?→文字コードが256以上になる場合があるのでダメですね…。jnzで飛ばすのが楽そう)

Green Team (@kotatsugame, 174 bytes)

`      + + + * , `*        +  *  + ,  *  + , +          + +* * '   +                + +  *    *            + + + + +**** +** .         + +*          + + +** +  **    * . + .'

(nu4nu追記) この方針で、10から70(=10*2*2*2-10),121(=(10+1)^2)を作り、70と10から233(=70*10/3)を作るようにすると(Fを出すところでSが出る嘘を直した上で)156Bになった。

`      + + + * , `* + +**  + ,  *  + , +          + +* * ' +   + + + +* +* +*  + *   +      +* +  *    *  ' + .  `  *        +   *    * .          + + +* .'

Stuck

Blue Team (@drafear, 32 bytes)

"s$_9&5*'FQ""12345""'SQp"['"j50V

Green Team (@EtoNagisa, 21 bytes)

"s$5Rj'SQo]0&5<'
"50V
"               "50V          50回ループ                 []
 s                            input()                   ["A4A5A2A1A3"]
  $                           sort                      ["12345AAAAA"]
   5R                         [1,2,3,4,5]を生成          ["12345AAAAA",[1,2,3,4,5]]
     j                        join                      ["12345AAAAA","12345"]
      'S                      文字Sをpush                ["12345AAAAA","12345","S"]
        Q                     置換                       ["SAAAAA"]
         o                    ランレングス圧縮(!?)         [[(1,"S"),(5,"A")]]
          ]                   listを一次元潰す            [(1,"S"),(5,"A")]
           0&                 tupleの0番目を取得          [(1,"S"),5]
             5<               5より小さいか?              [(1,"S"),False]
               '              改行をpush                 [(1,"S"),False,"\n"]
              

改行の部分はNにできるので,1byte減る 型が厳しくて窮屈だが,ランレングス圧縮みたいな変な飛び道具がいくつかある. ドキュメントにない命令がたくさんあり,実装を読まないと条件分岐すらできないのがつらい

Red Team (@yamunaku, 23 bytes)

"s$5K]_Y<;ol13+A;&N"50V

Swift

Blue Team (@nakario, 85 bytes)

while let l=readLine()?.sorted(){print(Set(l[...4]).count>4 ?"S":1,l[5]<l[9] ?1:"F")}

Green Team (@dnek, 85 bytes)

while let l=readLine()?.sorted(){print(Set(l[..<5]).count>4 ?"S":0,l[5]<l[9] ?0:"F")}

Red Team (@kurgm, 84 bytes)

while let a=readLine()?.sorted(){print("12345"==""+a[..<5] ?"S":0,a[5]<a[9] ?0:"F")}

みんなほぼ同じなのおもしろい。差をつけてる部分はわたしが書いたところではないです by @kurgm 差の部分は、見てのとおり、配列(?)を文字列にして比較です。(私の時点ではa[0...4]になっていたので、差なしでした。ぐぐったものの切り貼りで書いていたので、それ以外の部分もかなり冗長でした。) by @rotary-o

Tcl

Blue Team (@ToE42, 138 bytes)

incr i
while {$i < 51} {
regsub {([A-E])\1\1\1\1} [join [lsort [split [gets stdin] {}]] ""] "F" x
regsub {12345} $x "S" y
puts $y
incr i
}

Red Team (@angel-p57, 98 bytes)

set v 0
while {$v!="S"} {regsub {^(.)(.\1)+.S} [gets stdin]S F& v
puts [regsub {(\d).*\1.*} $v X]}
  • 他の正規表現系の解法の流用、F,S分の2回の置換で対応。
  • S に関しては予め入力行に S を付与しておいて、数字の重複と一緒に置換で削るよう対応。
  • regsub には変数への代入も、置換結果(置換失敗時は元の文字列)を使うことも、両対応できて便利。
    ※未定義変数は使えないため最初の set は必要だが、各行で明示的な set が不要になる。
  • 最終行の次もループが回るため、最後に余分な S の行が出るが、システムに無視されるので無問題。

TeX (plain)

Red Team (@platypus999, 335 bytes)

\def\p#1#2#3#4#5{\if#1#2\else\if#2#3\else\if#3#4\else\if#4#5\else\if#1#3\else\if#2#4\else\if#3#5\else\if#1#4\else\if#2#5\else\if#1#5\else S\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}\def\q#1#2#3#4#5#6#7#8#9{\if#1#3\if#3#5\if#5#7\if#7#9F\fi\fi\fi\fi\p#2#4#6#8}\loop\ifnum\fam<50\read0 to\x\immediate\write0{\expandafter\q\x}\advance\fam1\repeat\end

TeX は引数が 10 以上の関数を作るのが難しいため、1 引数を要求する関数を返す 9 引数関数を作りました。具体的には、

  • 関数 q(#1,#2,#3,#4,#5,#6,#7,#8,#9) は、#1#3#5#7#9 が全て等しい場合 F を出現させ、関数 lambda x: p(#2,#4,#6,#8, x) を返す (カリー化の要領)
  • 関数 p(#1,#2,#3,#4,#5) は、#1#2#3#4#5 がすべて異なる場合 S を出現させる

とし、q に 10 個の引数を与えました (9 個目の引数までによって一引数関数が返ってくるので、10 個目の引数はその関数に入る)

反省点:関数 p が、10 通りのペアが等しくないことを全部列挙して試しているため、かなり冗長に見える

Transceternal

感想戦 (@nu4nu, 181 bytes)

01232321A"2!`2OA\2^2/2.KJBc=2QT2~29V2}26X2U2C'2-2LHGC\fLjUn]2|C{?\>?dF2;.:9E?v)2(^.w+2:x-y/z52Ea842[2Z*2,2?d76b;cceI@2R2KHgN<2S2IMJhQm[nkW2Nnao_27p`qFr|s}t~uFBKD22CBPOiSnEC\lY28n2.B

感想戦期間中に @satos---jp さんがコンパイラを書いて解いていたことで、無理ゲーではないことが明らかになったのでチャレンジしてみた次第。

参考資料など

この言語について貴重な資料を残されている @hiromi_mi さんに圧倒的感謝。第6回write-upで言語仕様の基礎から解説されている。transceternal-jaにある解やコードゴルフテクニックも参考になる(Esolang wikiのサンプルが貧弱なので本当に助かる)。筆者が言語仕様を理解した気持ちになったのは、インタプリタをPythonで写経して149.transceternalを実行する様子を眺めたときだったので、資料を読むだけでなく動かしてみるのが一番早いのではと思う。

今回生成に使用したコードはここにある。

長くなってしまったのでwrite-upはこちらに移動した。

Typhon

Red Team (@nu4nu, 196 bytes)

 aaaa
aaa
 a@a
@@ aa@@@@@a
aaa@@@@@aa
 a@@
 a@@
 a@@
 a@@
 a@@
aaa@aaaaaaaa
@a@@@ aaaaa@aaaaaaaa
@a@@@ aaaaa@a@a
@ aa a aa
 aaa
aaa@
a a@ @ @@@a a@aa  @  aa@
 a@a
@aaaa @ a@a
aaa
@@@@a@@@aaaa @ @ 

後日更新版 (@angel-p57, 184bytes)

 aaa@
aaa@
 a@@
@@ aa@@@
aaa
 a@a
 a@a
 a@a
 a@a
 a@a
aaa@@@@a@aa
@a@@@ aaaaa@a@a@@a@
@a@@@ aaaaa
@@@@ aa a a@
 aa@
aaa
a a@ @ @@@a a@aa  @  aaa
 a@@
@aaaa @ a@@
aaa@
@@@@a@@@aa@a @ @ 
  • Whitespace更新版154bytesのコンセプトを反映

  • 加えて、F文字出力時も mod が必要なので、(-3-Σ(-s*s%(b*b)))%173 で計算して、なるべく桁数を抑える

  • WhitespaceとTyphonの違いは次の通り

    • 空のラベルが使えないため、次点の +0,-0,+1 の3種でラベルを割り振り → Whitespaceより8B不利
    • copy命令が実装されていないため、最初のスート文字(の平方)をヒープ1番に保存 → Whitespaceより9B不利
    • 大きな数値の putc がエラーになるため都度 mod が必要 (前述の通り) → Whitespaceより13B不利
    • 使用する文字が空白・タブ・改行の代わりに a・@・空白
      ※ただし、数値やラベルの末尾を表す改行はそのまま
    • 一部の命令で使用する文字の対応が替わっている
      ※ stor が tts ではなく ttn 相当、getc が tnts ではなく tntn 相当
  • Whitespaceの時と同じ変換前独自コード
    
    mark +1                  # 0000: nssstN
     # 先頭のスート取得、平方した結果をヒープ1番に保存、EOFなら異常終了
     push +1                 # 0006: ssstN
     call -0                 # 0011: nsttN
     stor                    # 0016: ttn
    

     # 総和の初期値2個分 ( F用→S用 )  push -3 # 0019: sstttN  push +0 # 0025: sssN  # 数字・スートの組の解析×5回(最後は改行をスートと見做す)  call +0 # 0029: nstsN  call +0 # 0034: nstsN  call +0 # 0039: nstsN  call +0 # 0044: nstsN  call +0 # 0049: nstsN

     # S文字出力 ( (Σc^2)%244 )  push +244 # 0054: sssttttstssN  mod # 0066: tstt  putc # 0070: tnss  # F文字出力 ( 最後の改行の不一致分 -10*10 でFなら丁度 70 )  push +173 # 0074: ssstststtstN  mod # 0086: tstt  putc # 0090: tnss  # 改行出力 ( 直前のgetc分を取ってきた方が10をpushするより1B短い )  push +0 # 0094: sssN  retr # 0098: ttt  putc # 0101: tnss  # ループ先頭へ戻る  jump +1 # 0105: nsnstN

    # 一文字取得 ( 平方してスタックに積んでおく ) mark -0 # 0111: nsstN  push +0 # 0116: sssN  dup # 0120: sns  getc # 0123: tntn  retr # 0127: ttt  dup # 0130: sns  mul # 0133: tssn  ret # 0137: ntn

    # 数字・スートの組の解析1組分  mark +0 # 0140: nsssN  # S判定用数字の処理、コードの平方をそのまま加算  call -0 # 0145: nsttN  add # 0150: tsss  # スタックを入れ替えてF判定用の処理  swap # 0154: snt  call -0 # 0157: nsttN  # 最初のスート文字(平方)でのmodを負の総和から引くことにより、不一致時のみ減少  push +1 # 0162: ssstN  retr # 0167: ttt  mod # 0170: tstt  sub # 0174: tsst  # スタックを入れ替え直して戻る  swap # 0178: snt  ret # 0181: ntn

Unicue

Red Team (@shundroid, 146 bytes)

(UTF-16で提出)

𐘀(1𓀀0𑘀⓪𑘁ᚢᚠ[௰𓀁ᚣ൨アᚤక൪あ𑘧൩𐘁ᚥఊい𑘯𐘂ᚦ2५イᯀ]𓀂𓀃𓀄⒒6٥ఫ٣う𐀀⓿က⒑७ఎ𐀁➓𐀂𐘃ᚧ١)

正直縮める工夫はほとんどしていない。すべての数字が出てきたか(を判別するためにすべての数字の ASCII コードをかけたもの)、すべてのアルファベットが同じか、前のアルファベットが何か、を愚直にスタックに記録している。スタック型の言語であるが、

  • スタック上位 2 つを読み取って mod をとる演算が定義されていない(スタックトップと小さめの即値の間の mod はある)
  • w(xyz) とあると、ループで戻る先は x ではなく y である。つまり wxyz(xyz)* でなく wxyz(yz)* とプログラムが実行されていく(処理系のバグ? Documentationに書いていることとはは違う気がする…)(これに気づかず数時間溶かした…)
  • スタックの上から n 番目を読み取って、スタックトップに追加する命令が(script=Malayalam で)実装されているが、Documentationに入っていない

などがユニークな(?)点であった。だが、 Unicode の仕組みについて勉強になり、教育的な言語だったと感じた。

Unlambda

Red Team (@satos---jp, 2864 bytes)

コードを見る
````s``s``s`ksk`k``s`kd``sii``s``s`ksk`k``s`kd``sii``s``s`ks``s`k`s``s``s``s``s``si`k``s``s`ksk``s``s`ksk``s``s`ksk``s``s`ksk``s``s`ksk`ki`k``si``s``s`ks``s`k`s`ks``s`k`s`kk``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ks``s`kk``s`k```s``s``s`ksk`k``s`kd``sii``s``s`ksk`k``s`kd``sii``s`k`s``s`ksk``s`kk``s`k`s``s`ks``s`kk``s`ksk``s`kk``s`k`s``s`ks``s`kk``s`ks``s`k`s`ks``s`k`s`kk``s``s`ks``s``s`ksk`k``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kki`k`k```s``s`ks``s`kk``s`ks``s`k`sik`kk`ki``s`kk``s`k`s`kk``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`ki`k`ki``s`k``s``si`k@`k``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?A`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?B`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?C`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?D`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk`kv``s`k`kii`ki``s`kk``s``s`ks``s`kk``s`k```s``s``s`ksk`k``s`kd``sii``s``s`ksk`k``s`kd``sii``s`k`s``s`ksk``s`kk``s`k`s``s`ks``s`kk``s`ksk``s`kk``s`k`s``s`ks``s`kk``s`ks``s`k`s`ks``s`k`s`kk``s``s`ks``s`kk``si`k```s``s`ks``s`kk``s`ks``s`k`sik`kkk`k``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kki``s`kk``s`k`s`kk``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`ki`k`ki``s`k``s``si`k@`k``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?1`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?2`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?3`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?4`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk``s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``si`k?5`k``s`kc``s`k`s`k`k`ki``s``s`ks``s``s`ksk`ki`k`kk`kv``s`k`kii`ki`k````s``s`ks``s`kk``s`ks``s`k`sik`kk````s``s`ks``s`kk``s`ks``s`k`sik`kkk````s``s`ks``s`kk``s`ks``s`k`sik`kkk````s``s`ks``s`kk``s`ks``s`k`sik`kkk````s``s`ks``s`kk``s`ks``s`k`sik`kkkv````s``s`ks``s`kk``s`ks``s`k`sik`kk`ki````s``s`ks``s`kk``s`ks``s`k`sik`kk`ki````s``s`ks``s`kk``s`ks``s`k`sik`kk`ki````s``s`ks``s`kk``s`ks``s`k`sik`kk`ki````s``s`ks``s`kk``s`ks``s`k`sik`kk`kiv`k``s``s`ks``s``s`ks``s`kk``s``s``s`k```s``s``s`ksk`k``s`kd``sii``s``s`ksk`k``s`kd``sii``s``s`ks``s`k`s``s`k``s`k`s`k`s`kc``s`k`s`k`s``s`ks``s`kkk``s`k`s`kk``s``s`ks``s`kk``s`ks``s``s`ks``s`kk``si`k``s`kk``s``si`ki`ki`ki``s`k`s`kk``s``s`ksk`kii``s`kk``s`k`s``s`ks``s`kk``si`k`ki``s`kk``s``s`ksk`ki`k`kki`k.X`k.F`k``s``s``s`k```s``s``s`ksk`k``s`kd``sii``s``s`ksk`k``s`kd``sii``s``s`ks``s`k`s``s`k``s`k`s`k`s`kc``s`k`s`k`s``s`ks``s`kkk``s`k`s`kk``s``s`ks``s`kk``s`ks``s``s`ks``s`kk``si`k``s`kk``s``si`ki`ki`ki``s`k`s`kk``s``s`ksk`kii``s`kk``s``s`ks``s`k`s`ks``s`k`s``s`ksk``s`kk``s``s`ksk`ki`k`k`k`ki`k`kki`k.S`k.Y`k`kr`k@``s`kk``s`k`sik`kii

自前のUnlambda用コンパイラで以下のコードをコンパイルしたものを出力。

コード
$suitNum = s2lam('''
  (j. j @ (i.
    $cons (i ?A $iv2tf) (
    $cons (i ?B $iv2tf) (
    $cons (i ?C $iv2tf) (
    $cons (i ?D $iv2tf) (
    V))))))	
''')

$numNum = s2lam('''
  (j. j @ (i.
    $cons (i ?1 $iv2tf) (
    $cons (i ?2 $iv2tf) (
    $cons (i ?3 $iv2tf) (
    $cons (i ?4 $iv2tf) (
    $cons (i ?5 $iv2tf) (
    V)))))))
''')


Z (f. i. i
  $times5
  (r. r (p. q. $cons ($numand ($suitNum (K I r)) p) ($numor ($numNum (K I r)) q)))
  ($cons $tnum $fnum)
  (p. q. (($iszero p) .X .F) (($isall q) .S .Y) r)
  @ (i. i f) i
) I

方針としては、各入力文字を1bitだけ立った4bit/5bitリストとして表現し、数字はリスト同士のor,スートはandをとって、最終的なリストについてのsome/allでS/Fを判定。

今回もUnlambdaの入力仕様に苦しめられた。入力するところをうっかり簡約可能にしてしまうと文字が読み込まれなくなってしまうので、λa. a @などとすることによって読み込みタイミングを制御する必要がある。(副作用をもたないLazy Kの書きやすさを再確認...)

Verilog (Icarus Verilog)

Red Team (@nu4nu, @rotary-o, 124 bytes)

module a;time b,i=0;initial
for(;~$fscanf(1<<31,"%c",b);i=b<11?0:i|1<<b%32)if(b<11)$write("%c%c\n",i/375,70^i&i-1);endmodule

叩き台は@rotary-oさんによる。アルゴリズムはCと同じ。 Verilog特有の短縮は以下。

  • ブロックを作るコスト(begin/end)がでかいので冗長でも同じ条件を2回書いたほうが短い場合がある
  • 一文字読むのは$fgetcでもできるが、代入文を書ける場所が限られていてbegin/endが生えてしまう。具体的にはforの条件にb=$fgetc(1<<31),~bのように書くことも、if((b=$fgetc(1<<31))<11)のように書くこともできない。そのため少し長くなるが$fscanfを使ったほうが短くなった

Vim

Blue Team (@wass88, 43 bytes)

:v/\v(\d).*\1/s/^/S
:%s/\v(\D)(.\1){4}/F
ZZ
  1. 同じ数字が複数回現れない行の先頭にSを追加
  2. 同じ英字が4回現れるところをFに置換

Red Team (@siotouto, 44 bytes)

:%s/\v^(.)(.\1)*.$/&F
:v/\v(\d).*\1/s/./S
ZZ

Green Team (@kotatsugame, 45 bytes)

}ASQ%s/\v(.)(.\1){4}/F&
%s/\v(\d).*\1.*S
x

wake

Green Team (@bx4ay, 66 bytes)

_:$<
(.*?)\n(.*):S$1 $1"\n"$2
(.).(\1.)*:"F"
.*(\d).*\1.*:
.*:"$&"

@dnek さんが書いたものを@kotatsugame さんが縮めたものを@bx4ay が少し縮めたもの。アルファベットがすべて同じであればFを出力し、同じ数字が2つあればSを出力しない。

Red Team (@kurgm, 68 bytes)

a:$<
(.+?)\n(.*):$1F S$1"\n"$2
(\D)(.\1|.F)+|.*(\d).*\3.*|(.).*:"$+"

処理系の実装 (waker.rb) を読んでて $+ で最後のマッチした括弧の部分を取り出せるのおもしれー使えそうと思って使ったんですが、使わない方法で越されてしまいました… by @kurgm

Blue Team (@saito-ta, 本質@shinh, 64 bytes)

a:$<
(.)((.\1){4}.*):"F"$2
\S*(\d)\S*\1\S*.(.*):"\n"$2
.+:"S"1$&

shinh さんが提出した 65 bytes のコードは、 2 行目が

(.)(.(\1.)*\n.*):"F"$2

だった。これを saito-ta が深く考えずに縮めてしまったもの。結果論ですが、上の書き方でも、同じ数字が 5 つ来ても大丈夫っぽいです。全部同じカードが来たときには 2 行目が 2 回マッチして FF が出力されるが、問題ないので。

WebAssembly Text Format

Red Team (@nu4nu, 928 bytes)

コードを見る
(module(memory(import "env" "memory")1)(func(export "main")(param i32)(result i32)(loop
local.get 0
i32.const 11
i32.div_u
i32.const 3
i32.mul
i32.const 655430
local.get 0
i32.load8_u
local.get 0
i32.load8_u offset=2
i32.ne
i32.add
local.get 0
i32.load8_u
local.get 0
i32.load8_u offset=4
i32.ne
i32.add
local.get 0
i32.load8_u
local.get 0
i32.load8_u offset=6
i32.ne
i32.add
local.get 0
i32.load8_u
local.get 0
i32.load8_u offset=8
i32.ne
i32.add
i32.const 1
local.get 0
i32.load8_u offset=1
i32.const 2
i32.add
i32.mul
local.get 0
i32.load8_u offset=3
i32.const 2
i32.add
i32.mul
local.get 0
i32.load8_u offset=5
i32.const 2
i32.add
i32.mul
local.get 0
i32.load8_u offset=7
i32.const 2
i32.add
i32.mul
local.get 0
i32.load8_u offset=9
i32.const 2
i32.add
i32.mul
i32.const 27
i32.add
i32.const 256
i32.rem_u
i32.const 256
i32.mul
i32.add
i32.store
local.get 0
i32.const 11
i32.add
local.tee 0
i32.load
br_if 0)i32.const 1116))

終盤まで誰もやる気配がなかったので第7回write-upを見ながら適当に書いたもの。第7回時点との大きな違いは、wat2wasmのバージョンが変わったのか1行目のダブルクオートで囲まれたトークンのところにスペースが必要になったこと。それがわかるまでに時間を溶かし、短縮に至らなかった。

427Bに短縮したもの。

コードを見る
(module(memory(import "env" "memory")1)(func(export "main")(param i32 i32)(result i32)local.get 0(loop
local.get 0
i32.const 0
local.set 1(loop
i32.const 1
local.get 0
i32.load
i32.shl
local.get 1
i32.or
local.set 1
local.get 0
i32.const 1
i32.add
local.tee 0
i32.load
i32.const 80
i32.and
br_if 0)local.get 1
i32.const 1
i32.sub
local.get 1
i32.and
i32.const 23928902
i32.xor
i32.store offset=1
local.get 0
i32.load
br_if 0)))

ループで改行文字か終端(0が読める)に至るまでビットマスクを更新し、XORで4バイトまとめて作って書き込む方針。ここで、最初改行文字だけを引っ掛けてループを抜けるようにしていたところ、入力終端の改行文字が消えていてループを抜けられなくなり範囲外アクセスを起こしてハマっていた。この言語処理系に限らないが、入力終端の改行が消える可能性を疑うというのは次回以降の教訓となる。

他に変えたところとしては、無駄な文字を出してもOKなので出力を入力と同じアドレスに上書きするようにしてアドレス計算をなくしたことが挙げられる。返り値になるアドレスは最初にスタックに積んでおけばよい。

文言

Blue Team (@n4o847, 103 bytes)

施require('fs').readFileSync(s=0).map(a=>s=a-10?s|1<<a-49:console.log("S"[s&31^31],(s>>=16)&s-1||"F"))

Node.js のコードに を前置するだけで通る。赤チームは Node.js で勝っていて、文言を取れる状態だったにも関わらず、最後まで気づかれなかった。

Whitespace

Red Team (@angel-p57, 163 bytes)


  	

 	 
  					 
   					  

 	

 	

 	

 	

 	
   	        
	 			
  	
     	 	 
	
  
 
	

   
   
 
 	
	 			 
 	  

	

  

 	 
	    
	
 	 
 	  		
	 			    
	
	

  • S 文字は数字コード d に対して (Σd^2+124)%256 で、F 文字はスート文字コード b(最初),s(その他、改行もスートと見做す) に対して (Σ s^2%b^2)-30 で生成します。
  • Whitespace(のバックエンドであるHaskell)の仕様か、大きすぎる数値は putc に使えないので、S の方は mod 256 が必要です。
    ※ mod 128 でも良ければ 1B縮む??
    • (nu4nu追記)ここでmodを取る必要があるのは、Haskellの文字出力がUnicodeに対応しているために13139=U+3353=㍓が出てしまうためです。なおmod 128では\nが出るケースがあります。Fのほうは、Fを出すときは70、それ以外のときは70以上の数で最大で18000ぐらいの文字コードを出力しようとしますが、出力がUTF-8でジャッジ時に無視される文字しか出ないのでmodを取っていません。
  • F の方は当初一致・不一致を条件分岐してカウントしていましたが、rotary-o さんによる mod の和というアイデアで縮みました。
  • コード生成は自作インタプリタ/アセンブラ whitespace.rb を使い、以下の独自コードを $ ruby whitespace.rb -rRS 元コード.wss により .ws に変換して行っています。
生成元コード

mark MAIN                      # 0000: nss-t-n
 # 先頭のスート取得、EOFなら異常終了
 call GET                      # 0005: nst-s-n

 # 総和の初期値2個分 ( F用→S用 )  push -30 # 0010: ss-ttttts-n  push 124 # 0019: ss-stttttss-n  # 数字・スートの組の解析×5回(最後は改行をスートと見做す)  call SUB # 0030: nst--n  call SUB # 0034: nst--n  call SUB # 0038: nst--n  call SUB # 0042: nst--n  call SUB # 0046: nst--n

 # S文字出力 ( (124+Σc^2)%256 )  push 256 # 0050: ss-stssssssss-n  mod # 0063: tstt  putc # 0067: tnss  # F文字出力 ( 最後の改行の不一致分 +10*10 でFなら丁度 70 )  putc # 0071: tnss  # 改行出力  push 10 # 0075: ss-ststs-n  putc # 0083: tnss  # ループ先頭へ戻る  jump MAIN # 0087: nsn-t-n

# 一文字取得 ( 平方してスタックに積んでおく ) mark GET # 0092: nss-s-n  push 0 # 0097: ss-s-n  dup # 0101: sns  getc # 0104: tnts  retr # 0108: ttt  dup # 0111: sns  mul # 0114: tssn  ret # 0118: ntn

# 数字・スートの組の解析1組分 mark SUB # 0121: nss--n  # S判定用数字の処理、コードの平方をそのまま加算  call GET # 0125: nst-s-n  add # 0130: tsss  # スタックを入れ替えてF判定用の処理  swap # 0134: snt  call GET # 0137: nst-s-n  # 最初のスート文字(平方)でのmodを総和に足すことにより、不一致時のみ増加  copy +3 # 0142: sts-stt-n  mod # 0149: tstt  add # 0153: tsss  # スタックを入れ替え直して戻る  swap # 0157: snt  ret # 0160: ntn

後日更新版 (@angel-p57, 154 bytes)

nsstnnstsnsstttttsnsssnnstnnstnnstnnstnnstnsssttttstssntstttnsstnsssssnttttnssnsntnnsssnsssnsnstntstttsnstssnntnnssnnstsntssssntnstsnstssttntstttssssntntn

※書き辛いので、空白・タブ・改行を s,t,n に置き換えてます

  • 更新点1: S文字生成に mod 256 でなくても良いことに気付き、探したところ mod 244 なら F や改行への誤爆が無いうえ、総和の初期値を 0 にできて数字の桁数分 8B 短縮になりました。

  • 更新点2: 改行文字を push 10 で作るよりも、直前に getc した結果を retr で取得した方が 1B 短縮になりました。

  • 生成元コード
    
    mark MAIN                      # 0000: nss-t-n
     # 先頭のスート取得、EOFなら異常終了
     call GET                      # 0005: nst-s-n
    

     # 総和の初期値2個分 ( F用→S用 )  push -30 # 0010: ss-ttttts-n  push 0 # 0019: ss-s-n  # 数字・スートの組の解析×5回(最後は改行をスートと見做す)  call SUB # 0023: nst--n  call SUB # 0027: nst--n  call SUB # 0031: nst--n  call SUB # 0035: nst--n  call SUB # 0039: nst--n

     # S文字出力 ( (Σc^2)%244 )  push 256 # 0043: ss-stssssssss-n  mod # 0055: tstt  putc # 0059: tnss  # F文字出力 ( 最後の改行の不一致分 +10*10 でFなら丁度 70 )  putc # 0063: tnss  # 改行出力 ( 直前のgetc分を取ってきた方が10をpushするより1B短い )  push 0 # 0067: ss-ststs-n  retr # 0071: ss-ststs-n  putc # 0074: tnss  # ループ先頭へ戻る  jump MAIN # 0078: nsn-t-n

    # 一文字取得 ( 平方してスタックに積んでおく ) mark GET # 0083: nss-s-n  push 0 # 0088: ss-s-n  dup # 0092: sns  getc # 0095: tnts  retr # 0099: ttt  dup # 0102: sns  mul # 0105: tssn  ret # 0109: ntn

    # 数字・スートの組の解析1組分 mark SUB # 0112: nss--n  # S判定用数字の処理、コードの平方をそのまま加算  call GET # 0116: nst-s-n  add # 0121: tsss  # スタックを入れ替えてF判定用の処理  swap # 0125: snt  call GET # 0128: nst-s-n  # 最初のスート文字(平方)でのmodを総和に足すことにより、不一致時のみ増加  copy +3 # 0133: sts-stt-n  mod # 0140: tstt  add # 0144: tsss  # スタックを入れ替え直して戻る  swap # 0148: snt  ret # 0151: ntn

Width

Green Team (@kotatsugame, 54 bytes)

wcicwawAwacccAawAcwTFTAAcwaAAacwcATfcaFATAAOAfOwwccaww

Blue Team (@drafear, 62 bytes)

wcicwawAwacccAacwcAacFAFwcAFAFcwAGamGwwccFfcaTAFAAGAfGwwccawwf

:::spoiler

cmd 20 # incr counter
while
cmd 02 # input
cmd 212 # extend
cmd 0001 # sort

cmd 0201 # join

cmd 0 # dup
int 5
cmd 201 # [5]
int 5
cmd 021 # * 5
str F
cmd 2200 # replace

int 54321
cmd 11 # to str
str S
cmd 2200 # replace

cmd 22 # print
end

:::

XSLT

Red Team (@kurgm, 231 bytes)

<transform version="1" xmlns="http://www.w3.org/1999/XSL/Transform"><output method="text"/><template match="."><value-of select="replace(replace(replace(.,'(.)(.\1){4}.','F$0'),'.+','$0S'),'(\d).*\1.*','')"/></template></transform>

過去(第6,7回)のWriteupを参考にしたテンプレに、初期のsedの回答をそのまま移植してきました。 あと version を 1.0 から 1 に変えました

Blue Team (@drafear, 236 bytes)

<transform version="1" xmlns="http://www.w3.org/1999/XSL/Transform"><output method="text"/><template match="/"><value-of select="replace(replace(replace(.,'.+','S$0'),'(\D)(.\1){4}','F$0'),'S(.*(\d).*\2)','$1')"/></template></transform>

Zig

Red Team (@kurgm, 225 bytes)

pub fn main()!void{const s=@import("std");var b:[11]u8=.{};while(0<try s.os.read(0,&b)){s.sort.sort(u8,&b,{},s.sort.asc(u8));_=try s.os.write(1,&[3]u8{if(s.mem.eql(u8,b[1..6],"12345"))83 else 0,if(b[6]<b[10])0 else 70,10});}}

整形

pub fn main() !void {
  const s = @import("std");
  var b: [11]u8 = .{};
  while (0 < try s.os.read(0, &b)) {
    s.sort.sort(u8, &b, {}, s.sort.asc(u8));
    _ = try s.os.write(1, &[3]u8 {
      if (s.mem.eql(u8, b[1..6], "12345")) 83 else 0,
      if (b[6] < b[10]) 0 else 70,
      10
    });
  }
}

言語差し替えにより途中から盤面に出現した言語。誰も手を付けようとしていなかったっぽいのでやってみました。

オーバーフローとかに厳密みたいなので整数演算する路線は諦めました。文字列をソートして判定する方式です(改行含めてソートしているので 1-indexed っぽくなっている)。

Zig のバージョンがちょっと古くて (0.7.1)、昔のマニュアルを参照する必要がある。たとえば std.sort.sort は最新版には無くなっている? でも、昔のやつには std.os.read が載ってなかったりするので、やっぱり最新のマニュアルも合わせて見たほうがいい……

⚠️ **GitHub.com Fallback** ⚠️