char*A="PEPH50" "MEPDRCRD)4Q)9" "CRBSBSBSBY" "FHBGF$>HBHEHB" "GFNGGBGFHBGGGB<" "+GFNGGBS2%BGGGB" "2;YF+AHBRCHEHB" "RBSBQDSBP0$DRC$" ":G/)$?FGD+:QCGMP" "!%EGGGEMEGZ4Z4/(" "Z4Z%=0JMEGGGBSUQ" "CGGGBST%(SBGGGBS" "THEH" "B48GG" "GHGZ" "GGG2=" "BGGGH" "GZGGG" "BGGG" "HGZHE" "H22B" "HEHH" "GZSB" "SHGZ+" "BQDQ" "I29G" "//0(" "Z&D0@" "MHMK" "GG&", *T="char*A=%c%s%c" ",*T=" "%c%s" "%c;#define/*QQ*/" "Q(X)" "char*" "M=#X,m[12345],c" ",Toho" "ku,Un" "iv;Xint/*Q*/spr" "intf(char*,const" "char*,...),i,j" ",k=1,putchar(int" "),f=34;Q(%s)"; #define/*QQ*/Q(X) char*M =#X,m[ 12345],c,Tohoku, Univ;X int/*Q*/sprintf (char* ,const char*,...),i ,j,k=1 ,putchar(int ),f=34 ;Q(int main(){i=j;typedef void**M1;sprintf (m,T,f ,A,f,f ,T,f,M);for(;(c=A[ i++]);)for(;c-->9+ 56;k++ %75?:~ putchar(10)){k=k+! putchar (i%2==1 ?(4|(c ==65|| k%75== 0))&f? f^=5,+ 34:+(f ^=m[j] ==34,m [j++]) :040); for(;m [j]==1 <<5;)j ++;}f=f ;struct ICPC{;} Asia(); signed ***kotatsugame(M1) ;f=2022/12/27-28+f ;short **ha15(M1);enum{ Aobayama_dropout };long mine691(M1); Tohoku=Univ; i=k;})
Aobayama_dropout_2022.c · GitHub
参考:一昨年、昨年の記事
はじめに
今年もアスキーアートQuineを作りました。これが最後の年です。
去年と同様メイキング記事を書きました。インターン先の勉強会で発表したときのスライドで大体は説明してあるので、それを補足するような形になっています。
1. AAを用意する
今年は「DROPOUT」を2行に書きました。特に参考にしたフォントはなく、手作業で角に丸みをつけたりしています。まず1文字の幅をマス、縦棒の太さをマスと決め、そこから「D」の文字を実際に作ってみて横棒の太さ3マス、文字の高さ10マスとしました。
そもそもマスの縦横比を2対1にしているので、見た目では同じ太さということになります。漢字なら縦棒を横棒より太くしたいところですが、アルファベットの場合、特にこういう半ば図案化したようなフォントにはあまり似合いませんでした。また高さも、これ以上高くすると間延びした印象を受けました。
実は「D」が完成すれば残りはほとんどそれのコピペで書けてしまいます。まず右半分を左に鏡映しにして「O」、その上半分を消して縦棒を伸ばし「U」。また、上下を少し縮めて縦棒を伸ばし「P」、その右下に足をつけて「R」ができます。最後の「T」は直線なので、棒の太ささえ決まっていれば迷うことがありません。
すべて完成してから、マス目に余裕があったので文字の間を1行・1列開けることにしました。余裕といえば、チーム紹介ドキュメントは60行まで使えるので「AOBAYAMA」という文字も書くことができますが、付け足した分を無意味なコードで埋めることになるだろうし、AAを作ってみたら思ったより見栄えが悪かったので、「DROPOUT」のみにしました。
2. AAを圧縮する
ランレングス圧縮して数列を文字列にするのは前2回と同様です。今年は空白が長く連続する箇所があってちょっと扱いづらかったため、最大25文字でいったん区切ることにしました。
最後に調整した結果圧縮した文字列の長さが243文字だとちょうど良かったので、そうなるよう適当な数を詰めています。
all=gets(nil).gsub(/\n/,'') a=[0] t=' #' all.each_char{|c| if t[a.size%2]==c a<<0<<0 if a[-1]==25 else a<<0 end a[-1]+=1 } while a.size+2<=243 i=rand(0..a.size) a[i,0]=[0,0] end a<<0 if a.size<243 STDERR.puts "WARNING" if a.min<0||a.max>25 STDERR.puts a.sum==all.size STDERR.puts a.map.with_index(1){|c,i|t[i%2]*c}.join==all Base=65 puts a.map{|c|c>0 ? (c+Base).chr : ([*33..Base]-"\"'*,-.".bytes).sample.chr}.join
3. 展開するコードを書く & 4. 不完全なQuineを作る
基本的には昨年と同じですが、マクロの引数内に文字列を置かないようにしました。
マクロの引数内にある文字列もAAに合わせて分割するのですが、文字列リテラルを並べる方法だと、そのために使った"
が変な位置に残ってしまい、出力時にさらに"
が追加されてQuineになりません。昨年はエスケープを使って改行を無視し、空白を含む一つの文字列とすることで対応したものの、出力フラグの管理がちょっと複雑でした。
ということで、昨年マクロの引数内にあったsprintf
のテンプレート文字列がマクロの外側に出ています。これでフラグ管理が非常に簡単になりました。文字の出力部を抜き出してみます。
putchar(i%2==1?(4|(c=='A'||k%75==0))&f?f^=5,'"':(f^=m[j]=='"',m[j++]):' ')
去年より明らかに短くなっています。f
の最下位bitで文字列リテラルの内か外か判定し、分割のため直前に"
を出力したかどうかをf
の下から3番目のbitに持っています。f
は別の用途にも使っていて初期値が34、つまり0b100010
なので、こういうことになっています。
また、昨年は諦めてしまった「警告が出ないアスキーアートQuineを書く」という目標を達成しました。これについてはほとんどスライドに書いてありますが、追加で1点。#include
を書かなかったのには、その行がコメントだらけになって見栄えが悪くなるほかにも理由がありました。
1行をそのために使ってしまうと当然全体的にコードが下にずれます。すると、putchar
という7文字のキーワードを置く位置が最下部にしかなくなってしまうのです。そこは昨年のようにチーム名等で埋めたかったので、嬉しくありませんでした。
5. 文字の位置を調整する
辛いだけで、根気よく揃えていくと何とかなりました。最下部をどのように埋めたかだけ説明したいと思います。
++;}f=f ;struct ICPC{;} Asia(); signed ***kotatsugame(M1) ;f=2022/12/27-28+f ;short **ha15(M1);enum{ Aobayama_dropout };long mine691(M1); Tohoku=Univ; i=k;})
下4行を取り出してきました。かなりいろいろ埋め込めたので自分としては非常に満足しています。ここで未使用の変数を宣言すると警告が出てしまうのですが、幸い関数とすることで回避できるようでした。
まず構造体の名前に「ICPC」、その関数「Asia」。除算や減算を日付だと思って「2022/12/27-28」、これは横浜大会の開催日です。また列挙型も警告が出ないようなので、enum
の定数として「Aobayama_dropout」を書きました。「Tohoku=Univ」については、上部をパディングする際に宣言しておいた二つの変数を並べています。
我ながら一番うまくいったなと思うのが、メンバーのハンドルネームと学年を埋め込んでいる部分です。ちゃんとポインタ*
で列を揃えることすらできました。実は上のほうでtypedef void**M1
としていたので警告は出ず、それぞれ適当な型の関数になります。その型もsigned***
、short**
、long
といろいろ取り揃えてみました。
完成!
enum
が太字になるようで少しずれたのが残念です。