GOTO M.

趣味のコーディングとか、勉強とか、読書とか

『第9回デスマコロシアム』答案

CodeIQ『第9回デスマコロシアム』(※)に参戦しました。
※以下を特徴とするコードゴルフ大会です。

  • ideoneで使用可能な言語なら何でもOK。
  • 有利な言語に偏りすぎないよう、参戦人数の多い言語にはハンディあり。
  • バイト数でなく、文字数で競う。
  • import文は文字数カウント対象外、ideoneの自動生成部分もカウント対象外。(本稿のコードも、自動生成部分は略記しています)

第9回出題テーマは、以下の文字マトリクスの標準出力への出力です。
(新年ということでテーマは「末広がり」、a~zが8回繰り返されつつ、8文字ごとに大文字になっています)

AbcdefghIjklmnopQrstuvwxYzabcdefGhijklmnOpqrstuvWxyzabcdEfghijklMnopqrstUvwxyzabCdefghijKlmnopqrStuvwxyzAbcdefghIjklmnopQrstuvwxYzabcdefGhijklmnOpqrstuvWxyzabcdEfghijklMnopqrstUvwxyzabCdefghijKlmnopqrStuvwxyz

今回もこれまで自分がデスマコロシアムでは使わなかった言語縛りで挑戦してみました。最終結果の最短とタイor短縮できているもののみ以下に挙げます。

brainf**k(114)

++++++++[>+++>+>+>+<[+++++++>]<++++<<<<-]>++>[<[>>+>+>>+<[<.>->-]>[<<<.>>+++++++>->]<<<<<<<+>-]<[>+>>->-<<<<-]>>-]
  • やはりちょっとだけ癖が有る言語なので、縮め方にもコツが要るのかなと思いました。自分は以下のような手順でした。
    1. 大枠のアルゴリズムを考え、ワーク領域の目星を付ける。
    2. 細かいアルゴリズムを考える。
    3. アルゴリズムに合わせ、ワーク領域の配置、準備部分のコードを書く。
  • 大枠のアルゴリズムを考え、ワーク領域の目星を付ける。
    • brainf**kには当然剰余演算子などは存在しないため、26ごとの循環、8ごとの循環を自前で作り込む必要が有ります。ということで、まずは以下の領域が必要かなと考えました。(……さらっと書いていますが、前回のAzicore様の解答を印刷して勉強しながら頑張りました)
W:26の待避用
Z:26回ループ用
S:8回ループ用
A:大文字のASCIIコード
a:小文字のASCIIコード
P:8文字ごとの大文字化判定用
Q:Pによるif-else制御用1
R:Pによるif-else制御用2
S[
  Z[
    Q+P[
         a.
         P-
       P>-]>
       [<
         A.
         P+++++++
       P>->]<<
    a+A+
    W+
    Z-
  ]
  W[Z+A-a-W-]
  S-
]
  • アルゴリズムに合わせ、ワーク領域の配置、準備部分のコードを書く。
    • Brainf**kはポインタの移動にも都度1バイト必要なので、近く参照する変数同士は近い領域に置いておくのが定石のようです。ということで、変数間の遷移を以下のようにざっくり整理して変数の配置を検討しました。

f:id:g_m_k:20150202234719p:plain
(できればこの辺も自動化できると格好良いのですが、いまは手動です)
結果 ++++++++[>+++>+>+>+<[+++++++>]<++++<<<<-]>++>>+>+<< 部分のコードになりました。(ゼロ値領域を利用した短縮テクもAzicoreさんの前回コードからパクっています……)

Whitespace(109)

  			 	    

  
 
  
    		 	 
	 		 
	  		   
	 		
	 	
   	     
	   
  	
   	     	
	   	
     	
	    
 
		
  • 分り易く可視化すると以下の通りです。(S:Space, T:Tab, N:Newline)
SS TTTSTSSSS N	#   PUSH -208
NSS  N		# null:
SNS		#   DUP
SNS		#   DUP
SS STTSTS N	#   PUSH 26	
TSTT		#   MOD
SNT		#   SWAP
SS TTSSS N	#   PUSH -8
TSTT		#   MOD
NTS T N		#   JZ T
SS STSSSSS N	#   PUSH 32
TSSS		#   ADD
NSS T N		# T:
SS STSSSSST N	#   PUSH 65
TSSS		#   ADD
TNSS		#   PUTCHAR
SS ST N TSSS	#   PUSH 1,ADD(カウンタ++)
SNS		#   DUP
NTT N		#   JN null

Java7/Java(53)(※これだけ、新挑戦言語ではない)

import static java.lang.System.out; #出題規則によりこの行は文字数のカウント外
for(int i=207;;)out.write(i<0?0:122-i%8/7*32-i--%26);
  • out.writeメソッドは、out.printf("%c",...)と書くよりも文字数を大きく節約できるのですが、バッファの内容を書き出すにはout.flush()を呼び出す必要があり、トータルでは効率が悪いです。(第8回デスマコロシアムでは奇跡的にflushが不要でした)
  • そこで、0(null)をエンドレスに送り続けて意図的にエラー終了させることで、バッファを無理矢理吐き出すとともに、for文も省略することとしました。

Icon(49)

every i:=-207to 0&writes(char(i%8/7*32+122+i%26))
  • 基本的な考え方はjavaと同じですが、こちらはループのインクリメントをevery文に任せているため、ループの1サイクル内で添え字をインクリメントする書き方が適しません。そこから素直に書くと「添え字が8の倍数の時に何かする(32を引くとか)」という処理が必要になるのですが、短く書くのはちょっと難しくなります。そこで、マイナススタートで添字を回すことで、上記を「添え字が7mod8の時に何かする」と書くことができ、難易度はグッと下がります。