Twenty Sixteen のブログタイトル部分に,ヘッダ画像とは別にワンポイント画像を入れる

Twenty Sixteen が 1.1 にバージョンアップされましたね.
バージョンアップしたということで, Andante con graziaAllegro con grazia に施していた修正をバージョンアップしたテーマに対応させてきました.
折角の機会なので,この2つのブログのタイトル部分にいるアリス,どうやって表示しているか紹介しておきます.

cropped-h_test-1.png
使用している画像(実際は背景を透明にしてあります)

ヘッダ画像には小さすぎる

Twenty Sixteen の推奨ヘッダ画像,サイズが 1200×280 ということで,流石に上のようなワンポイントの画像じゃ小さすぎます.
それに,ヘッダ画像に設定して,タイトルと記事との間に大きな隙間ができるのもあまり好ましくありません.
ということで,今回は,この画像を,タイトルを表示している div の背景とし,表示位置を右下,表示を一階だけの設定とすることで解決しました.
具体的には style.css の .site-header-main に以下を追加しています.

background:url(http://example.com/example.png) no-repeat right bottom;

その他の細かい変更点は後でまとめるので,ひとまず,なるほどと思っておいていただければ大丈夫です.

モバイルフレンドリーも忘れずに

さて,ここまでで,タイトル部分に画像が表示されるようになったかと思いますが,このままでは画面が狭い,スマートフォンのような端末から見た時にタイトルと画像が被ってしまいます.
なので,タイトルの欄をスマートフォンで見ても違和感のないくらいの高さに変更します.
具体的には, .site-header-main に

height: 300px;

を設定です. px 数は画像の大きさによって変えてください.

しかし,この設定にすると大きい画面の時でもタイトルが大きくなってしまって不格好です.
それを回避するために “@media screen and (min-width: 56.875em)” (大きいサイズの時のみ適用するオプションです)中の .site-header-main

height: auto;

を設定しましょう.
これで,画面サイズに合わせて画像の表示位置が変わり,タイトルを邪魔しなくなります.
もし,画面が大きい状態でも画像がタイトルを邪魔するようなら, auto を適切な値に書き換えて対応しましょう.

まとめ

追加するのは, style.css のひとつ目の .site-header-main 中に

background:url(http://example.com/example.png) no-repeat right bottom;
height: 300px;

二つ目(@media screen and (min-width: 56.875em) 内)の .site-header-main 中に

height: auto;

と,意外と簡単に出来てしまいました.
意外と需要があるかも.と思ったので解説してみました.
みなさんも,よろしければ試してみてくださいね.

WordPress の更新情報を外部から php + SQL で取得する

WordPress の更新をプラグインに頼らず取得

WordPress の更新情報 (What’s new) を,表示してくれるプラグインなどは存在するようですが,WordPress 外部から更新情報を持ってくる方法がいまいち見つからなかったので自分で作ってみました.
(現在, http://congrazia.net/ に WordPress の更新情報を表示しています)

php + SQL で更新情報を取得

使ったのは php で直接 MySQL の DataBase からデータを持ってくる方法.
DataBase の構造は公式のここに載ってます.
(私は見ずに作ってしまいました……)
このサイトの画像のなかでは “wp_” が table 名の接頭辞として設定されているようです.
(この接頭辞,末尾を “_” にしなかった場合,テーブル名と接頭辞の間に何も文字が入らないので,インストール時は末尾に “_” を入れるのが無難そうです.私は “presto” にしたものだから,テーブル名が “prestoposts” とか,ひどいもので “prestoterm_relationships” とかになっています)
以下では接頭辞は “prefix_” と表記することにします.
ここをインストール時に設定した接頭辞にすれば各自の WordPress 環境の DataBase と同じものになるはずです.

最新記事を得る

記事の情報が保存されているのは,table “prefix_posts” ですね.
気をつけるのは,記事の状態の情報( “post_status”)のカラムです.
公開されているとか,下書きであるとかですね.公開中の記事は “publish” になっているようです.
ここまで分かれば簡単ですね.SQL は
“SELECT * FROM %sposts WHERE post_status = ‘publish’ ORDER BY post_date DESC LIMIT %d”
%s は接頭辞 “prefix_” 等, %d は取得する件数です.

記事の情報を取り出す

欲しいのは題名 field “post_title”, 更新日時 field “post_date”, そして URL くらいでしょうか.
勿論その他の情報 “post_author”, “ID” なども取得可能です.
URL については,少し複雑なので,次の”パーマリンク取得”で説明します.

パーマリンク取得

先ほど取得した記事情報ですが,記事の URL は “guid” で得られる……のですが,これはデフォルト設定のパーマリンク,いわゆる Ugly 形式です.
このままリンクに使っても記事には飛べますが,SEO 的にはよろしくないので,しっかり,指定してあるパーマリンク方式でリンクしてあげたいですね.
ということで,まずはパーマリンクのストラクチャー(フォーマット)を取得します.
パーマリンクのストラクチャーは table “prefix_options” の field “option_name” が “permalink_structure” のレコードの “option_value” の値ですね.
あとは,この permalink_structure の %hoge% を色々 replace するだけです.
%postname% なら “posts” の field “post_name” の値ですね.
しかし,やはりここでも,よく使ううちの幾つかは少々複雑な取得方法になります.
特に %category%, %tag%, %author% でしょうか.
%category% と %tag% はほぼ同じですし, %author% も言うほど変わらない取得方法なので,ここでは一番良く使うであろう(そして私もよく使っている) %category% のみを解説します.
もし要望や時間があれば他の 2 つも作るかもしれませんが.

category_slug の取得

さて,カテゴリスラッグですが,記事の id から取得するに必要なテーブルが 3 つあります.
“prefix_term_relationships”, “prefix_term_taxonomy”, “prefix_terms”
ですね.
それぞれ
“記事 id と それに付いている category や tag”, “category や tag とその種類”, “category や tag とその slug”
を関連付けるのに必要です.それぞれのテーブルを r, t, s とした時のそれぞれの field の対応は
“r.object_id (= post_id), r.term_taxonomy_id”, “t.term_taxonomy_id (= r.term_taxonomy_id), t.taxonomy (= category, (or tag))”, “s.term_id (=r.(or t.)taxonomy_id, s.slug)”
と言った感じです.
これでカテゴリスラッグにたどり着けます.
SQL にすると
“SELECT s.slug FROM (%sterm_relationships r INNER JOIN %sterm_taxonomy t ON r.term_taxonomy_id = t.term_taxonomy_id AND r.object_id = %d AND t.taxonomy = ‘category’) INNER JOIN %sterms s ON t.term_taxonomy_id = s.term_id ORDER BY s.term_id ASC LIMIT 1”
(正しい整形がわからないのでワンライナー.汚くてすみません.)
これの %s 全てに $prefix を, %d に “ID”(post_id) を入れたものになりますね.
“ORDER BY s.term_id LIMIT 1” としているのは, WordPress では 1 つの記事に複数のカテゴリを指定でき,また,カテゴリスラッグが必要なときは一番若い id を持つカテゴリのものが使われるからです.

実際に php + SQL で更新情報を取得してみた(ソース)

関数を幾つか作りました.
class を使ったほうが綺麗かしらとも思ったのですが,一旦この形で.
print_new を prefix(データベース接頭辞), $directory(ワードプレスのトップページのあるディレクトリ)で指定すれば5件の更新情報を引っ張ってきて,適当な html で出力します.
(他の関数は private だと思ってもらって構いません)
あくまで参考程度にお願いします.
try, catch は全然書いてませんが,使うときはのせることを推奨いたします.

ちょっと面倒なのは,MySQL の DATETIME は strtotime で一度 int に変換しないと使えない点.
結構ハマった.

<?php
/*
$sql_link = mysql_connect("$dtbhost","$dtbuser","$dtbpass");
mysql_select_db("$dtbname", $sql_link);
でデータベースに接続しておく.
*/

function print_new($prefix, $directory)
{

    $post_query = sprintf("SELECT * FROM %sposts WHERE post_status = 'publish' ORDER BY post_date DESC LIMIT 5", $prefix);

    $posts = mysql_query("$post_query");
    if (!$posts) {
        die('クエリーが失敗しました。'.mysql_error());
    }
    echo '<table class="whatsnew">'."\n";
    while ($post = mysql_fetch_assoc($posts)){
        $category_slug = get_category_slug($post, $prefix);
        echo '<tr>'."\n";
        echo '<th>'.date("Y/m/d", strtotime($post['post_date'])).'</th>'."\n";
        echo '<td>';
        printf('<a href = "/%s%s">%s</a>', $directory, get_solved_permalink(get_permalink_structure($prefix), $post, $prefix), $post['post_title']);
        echo '</td>'."\n";
        echo '</tr>'."\n";
    }
    if(mysql_num_rows($posts)==0){
        echo '<tr><td>まだ記事がありません</td></tr>';
    }
    echo '</table>'."\n";

    printf('<div class="foot"><a href="/%s/">More</a></div>', $directory);
}


function get_category_slug($post, $prefix)
{
    $id = $post['ID'];
    $get_category_query = sprintf("SELECT s.slug FROM (%sterm_relationships r INNER JOIN %sterm_taxonomy t ON r.term_taxonomy_id = t.term_taxonomy_id AND r.object_id = %d AND t.taxonomy = 'category') INNER JOIN %sterms s ON t.term_taxonomy_id = s.term_id ORDER BY s.term_id ASC LIMIT 1", $prefix, $prefix, $id, $prefix);
    $category_slug = mysql_fetch_assoc(mysql_query($get_category_query))['slug'];
    return $category_slug;
}

function get_permalink_structure($prefix)
{
    $get_permalink_query = sprintf("SELECT option_value FROM %soptions WHERE option_name = 'permalink_structure'", $prefix);
    $permalink_structure = mysql_fetch_assoc(mysql_query($get_permalink_query))['option_value'];
    return $permalink_structure;
}

/*
function get_author_name($post, $prefix)
{
    
}

function get_tag_slug($post, $prefix)
{
    
}
*/

function get_solved_permalink($structure, $post, $prefix)
{
    $year = date("Y", strtotime($post['post_date']));
    $month = date("m", strtotime($post['post_date']));
    $day = date("d", strtotime($post['post_date']));
    $solved = str_replace('%year%', $year, $structure);
    $solved = str_replace('%monthnum%', $month, $solved);
    $solved = str_replace('%day%', $day, $solved);
    $solved = str_replace('%category%', get_category_slug($post, $prefix), $solved);
    $solved = str_replace('%postname%', $post['post_name'], $solved);
    $solved = str_replace('%post_id%', $post['ID'], $solved);
    /*
    $solved = str_replace('%author', get_author_name($post, $prefix), $solved);
    $solved = str_replace('%tag%', get_tag_slug($post, $prefix), $solved);
    */
    return $solved;
}
?>

%tag% と %author% の置き換えは未実装.
%tag% は %category% とほぼ同じなので,簡単に動きそう(同じ関数にしてオプションで変更で良さそう)
%author% は %category% よりは楽になるかと思います.
%author% 名を変えるプラグイン等を入れていたら面倒くさそうですが.

まとめ

・MySQL の接頭辞は末尾に “_” をつけるのが無難.
・実は category と tag は同じ terms 扱い.
・WordPress といえども,DataBase の情報を整形などなどしているだけ.DataBase の中身を理解すれば十分自分で SQL 文を発行して色々できる.
・自分で CMS を作っていた経験はすごく役に立ったので,みなさんも簡単な CMS を作ってみてはいかが?

Quine もどきで アスキーアート in 01/01/2016. Sincerely, AkinoHonami – 2016年年賀状

あけましておめでとうございます.
秋乃穂波です.(馴染みの無い方もいるかもしれませんが,ハンドルネーム,というやつですので,ページの間違いではありませんよ)

まずは,年賀状を受け取って(もしくは画像を見て)”なんのこっちゃい”と思った皆様.
申し訳ありません.それと,安心してください.ちゃんと解説します.

今年の年賀状,なんぞや

はい,まずは今年の年賀状.
こんな感じですね.
以下の解説はネタバレになりますので,コードだけ持って帰りたい人はコピーして一旦回れ右をどうぞ.

#ThisScriptIsWrittenInPython2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.
W=" ";U="_";D='"'*3;Q="'"*3;S="<";L="&";T=">";zc=r"""import<re,random,zlib,base64&f=['eNrtld0JgDAMhI
e                          5wb                          L/FgqC9pI   0ptGK                          D
w                          0I5                          UL5zG+BZ    WmT3e                          6
UxViMxZjLsEbO44DzRH6Sz     O8d     QsC4LtJ3eaykIOKq     5KBY0       QbQRk     CSYUT1IPlkqOu+pBE9hg0a
Thp8SXTz9BkIGT0JuiMmxM     ENE     c+HWw8EydcVlntGs     a/          Il5jz     wnz4mQ13yeicc+C1rZOQnq
28MQYmM+qLO82QbxiYzHjy     xCU     Z7z+jf7ENVaI+Qw=     =','eNr     dlkES     gDAIAx/Th+X/v3Cc8dCGQN
GBHuRYq1tLCIzxk8Ad/qM6     Bk4     wcIKBEwycYKCZodV     VzJCQao     aClDM     EpJ5hIQ0MA+lgMKSFQZAeh
iU69Zq9U1kYfHWZH37LYA3     4XW     YV5CuGfHfjmbsOpE     sPqW+Yj     c/qnr     HYS3wXtNEcyGfMPslysuF8
d+OFs+HnGEOr0bdC0x0TtS     jV6     DuhaPNRPpTG+PTRw     DCfKEas     6WUlh     JOPU/JybUrhks1wThROhmj
yA7cJfJlFHU8MKuNXcQF5D     VYr     ','eNrdlMsRwCAIR     IuxMPrv     IpOJ8     YMsioFD5IruUz6b0iFBd5h
TXhA/BNTyRAA1X4So540QF     P0R     g2YEgqnGIDrdKESj     HIco2pG     IrB6L     qJB4g4xFPBCpeCK4T77NnB
akSUw2Zpj1yigZ/LQFB5O9     JzP     YOcTAbg+vNoNPqPb     8pFJJ+I     K15SL     2WUO67fkaA8e/GMs7tMlIW
j+0sbcwEp4rvFtWhjLXoFx     EZg     bfpsF+iHvJDuOTJ5     4ZF7P2O     Cs=',     'eNpTVh4mIB4nICQJ10/QH
ArtiCfGDtyeQ6KxSsLNRRM     lJQ     AJ2IHmUJrYgc1Po3     aM2jFqB     8l2EC     6vSLCD7DKRSnYQCjNyw2rU
j                          lE7     Ru0YWXaQ3YajVplI     uR3DBAA     A7Lix                          k
w                          =='     ]&df=zlib.decomp     ress(ba     se64.                          b
6     4decode(f[0]))&fn=f[1:]+     f[0:1]&nc="#This     ScriptI     sWrit     tenInPython2.7.W     h
e     nYouExecute,PleaseDoInPy     thon2.7.NoteThat     ItDoesN     otWor     kInPython3.x."+'     W
=     "%s";U="%s";D=\'"\'*3;Q=     "\'"*3;S="%s";L=     "%s";T=     "%s";     zc=r'%(W,U,S,L,T     )
+     D+re.sub(r"f=\[.+?\]","f     =%s"%("".join(("     %s"%fn)     .spli     t())),"".join(zc     .
s     plit()))+';'+Q+"".join([     random.choice("a     bcdefgh     ijklm     nopqrstuvwxyz+-*     /
=     :;[](){}")<for<x<in<rang     e(3200)])&for<i<     in<rang     e(320     0):&>if<df[i]==U     :
&     >>nc=nc[:i]+W+nc[i:]&c="     "&for<i<in<range     (31):&>     c+=nc     [i*100:(i+1)*100     ]
+     "\n"&ec=Q+D+r';exec(("".     join(zc.split())     ).repla     ce(S,     W).replace(L,"\n     "
)     .replace(T,W*4))'&c=c[:-     len(ec)-1]+ec+"\     n#Pleas     eVisi     t'http://congraz     i
a     .net/presto/works/nengaj     o-2016.html'.Sin     cerely,     MyRea     lNameAkaAkinoHon     a
m     i"&print<c;'''ip};;i/ydp     ]vkzn/c:fmlx;jit     :m/dq-n     f=*=;     qhdk:={nmuq=={n(     x
}                          rul                          jon]g+z     o-r-x                          a
z                          ou;                          mje(-bm     qvznt                          =
k-;rpo+ny=z}xb)cf-:z-m'''""";exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))
#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'.Sincerely,MyRealNameAkaAkinoHonami

一応画像でも置いておきます.
2016年年賀状

さてさて,全くもって意味が分からないという方向けに説明を始めると,これは Python というプログラミング言語で書かれたプログラムです.
そして,このプログラムを実行すると……
と,これ以降は本当ネタバレ.
もう一度言いますが”ソース書くのがめんどうだから来た.実行してくるからコードよこせ”という方は,上のものをコピーしてください.

さて,話を元に戻しましょう.
これはプログラム……ということで,実行する訳です.
そこで,実行すると表示されるのが以下.

#ThisScriptIsWrittenInPython2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.
W=" ";U="_";D='"'*3;Q="'"*3;S="<";L="&";T=">";zc=r"""import<re,random,zlib,base64&f=['eNrdlkESgDAIAx
/Th+X/v3Cc8dCGQNGBHuRYq1tLCIzxk8Ad/qM6Bk4wcIKBEwycYKCZodVVzJCQaoaClDMEpJ5hIQ0MA+lgMKSFQZAehiU69Zq9U1
k    YfHWZH37LYA34X    WYV5CuGfHfjmbsOpEsPqW+Yjc/qnrHYS3wXtNEcyGfMPslysuF8d+OFs+HnGEOr0bdC0x0TtSjV6D
u    haPNRPpTG+PTRw    DCfKEas6WUlhJOPU/JybUrhks1wThROhmjyA7cJfJlFHU8MKuNXcQF5DVYr','eNrdlMsRwCAIRIu
x    MPrvIpOJ8YMsio    FD5IruUz6b0iFBd5hTXhA/BNTyRAA1X4So540QFP0Rg2YEgqnGIDrdKESjHIco2pGIrB6LqJB4g4x
F    PBCpeCK4T77NnB    akSUw2Zpj1yigZ/LQFB5O9JzPYOcTAbg+vNoNPqPb8pFJJ+IK15SL2WUO67fkaA8e/GMs7tMlIWj+
0    sbcwEp4rvFtWhj    LXoFxEZgbfpsF+iHvJDuOTJ54ZF7P2OCs=','eNpTVh4mIB4nICQJ10/QHArtiCfGDtyeQ6KxSsLN
R    RMlJQAJ2IHmUJr    Ygc1Po3aM2jFqB8l2EC6vSLCD7DKRSnYQCjNyw2rUjlE7Ru0YWXaQ3YajVplIuR3DBAAA7Lixkw==
'    ,'eNrtld0JgDAM    hIe5wbL/FgqC9pI0ptGKDw0I5UL5zG+BZWmT3e6UxViMxZjLsEbO44DzRH6SzO8dQsC4LtJ3eaykI
O    Kq5KBY0QbQRkCS    YUT1IPlkqOu+pBE9hg0aThp8SXTz9BkIGT0JuiMmxMENEc+HWw8EydcVlntGsa/Il5jzwnz4mQ13y
e    icc+C1rZOQnq28    MQYmM+qLO82QbxiYzHjyxCUZ7z+jf7ENVaI+Qw==']&df=zlib.decompress(base64.b64decod
e                      (f[0]))      &fn   =   f[1      :]+f[0:   1]&      nc="    #ThisScriptIs    W
r                      itten   InPy   t   h   o   n2.7   .When   Y   ouEx   ecu    te,PleaseDo    In
P    ython2.7.NoteT    hat   ItDoesNo     t     WorkInPy   tho     n3.x."+'   W=   "%s";U="%s"   ;D=
\    '"\'*3;Q="\'"*    3;   S="%s";L="    %    s";T="%s";   zc    =r'%(W,U,S   ,L   ,T)+D+re.   sub(
r    "f=\[.+?\]","f    =    %s"%("".joi   n   (("%s"%fn).    s   plit())),""    .    join(zc    .spl
i    t()))+';'+Q+""    .   join([random   .   choice("abcd   e   fghijklmnopq   rs   tuvwxyz   +-*/=
:    ;[](){}")<for<    x    <in<range(3   2   00)])&for<i    <   in<range(32    00)   :&>if   <df[i]
=    =U:&>>nc=nc[:i    ]+   W+nc[i:]&c    =   ""&for<i<in   <r   ange(31):&>   c+=n    c[i    *100:(
i    +1)*100]+"\n"&    ec=   Q+D+r';e     x     ec(("".j   oin     (zc.spli   t())).   rep   lace(S,
W    ).replace(L,"\    n").r   epla   c   e   (   T,W*   4))'&   c   =c[:   -len(ec)-   1   ]+ec+"\n
#    PleaseVisit'ht    tp://co      ngr   a   zia      .net/pr   est      o/works/neng     ajo-2016.
html'.Sincerely,MyRealNameAkaAkinoHonami"&p   rint<c;'''ip};;i   /ydp]vkzn/c:fmlx;jit:    m/dq-nf=*=
;qhdk:={nmuq=={n(x}ruljon]g+zo-r-xazou;mje(   -bmqvznt=k-;rpo+   ny=z}xb)cf-:z-m''';''   'vcot}j+*:]
-z+-qn)svfh;x-eml]v(m=y;c)r[{gjy(-[ipatv}m}   ;k(xqn}bhv)rwhyj   rtp:w[=p/:z{{=ktuj}i   t)ks}u]x[}s:
ztjgqcc;u/dyltj]+j:{gi[;hmviui(tj=-dh):h{l:   i}zzjy*c=c=qd[(h   xcpfyj:y-r/;z}xwtx=    ]p;yqyugo/mp
-b*s/nxn-tg:m{):kyratxzm:e/y]{]p*bgxocz[{ap   d}=jt)h]-v-)i:uj   g}[u;-pasnpfzxusb};   wac{eut{r;t*-
j/wmd:wjpgzvur)t=wacah;j][umot*qf;dzhuli//i   {zjp{;;qytkls-r(   ]:xgac/]ji}e+u)arw   wqnqhdb:yrcmlp
fso(=chkjha;g+=w=yerzmha-:m[jakgx}rwitwy[}[   zb-zt::qkmnnug]q   tc}]+*ihe-gjd*r]e    {+/)zdkrmdbetm
m[pe)l*-b)dme{gp(ifn[*'''""";exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))
#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'.Sincerely,MyRealNameAkaAkinoHonami

(ここからの画像は勘弁して下さい……)
……
なんとなく読めて来ましたかね.
これを実行すると表示されるのが

#ThisScriptIsWrittenInPython2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.
W=" ";U="_";D='"'*3;Q="'"*3;S="<";L="&";T=">";zc=r"""import<re,random,zlib,base64&f=['eNrdlMsRwCAIRI
u     xMPrvIpOJ8YMsioFD5IruU    z6b0iFBd5hTXhA/BNTyRAA1X4So540QFP0Rg2YEgqnGIDrdKESjHIco2pGIrB6LqJB4g
4      xFPBCpeCK4T77NnBakSUw    2Zpj1yigZ/LQFB5O9JzPYOcTAbg+vNoNPqPb8pFJJ+IK15SL2WUO67fkaA8e/GMs7tMl
I       Wj+0sbcwEp4rvFtWhjLX    oFxEZgbfpsF+iHvJDuOTJ54ZF7P2OCs=','eNpTVh4mIB4nICQJ10/QHArtiCfGDtyeQ
6        KxSsLNRRMlJQAJ2IHmU    JrYgc1Po3aM2jFqB8l2EC6vSLCD7DKRSnYQCjNyw2rUjlE7Ru0YWXaQ3YajVplIuR3DB
A         AA7Lixkw==','eNrtl    d0JgDAMhIe5wbL/FgqC9pI0ptGKDw0I5UL5zG+BZWmT3e6UxViMxZjLsEbO44DzRH6Sz
O          8dQsC4LtJ3eaykIOK    q5KBY0QbQRkCSYUT1IPlkqOu+pBE9hg0aThp8SXTz9BkIGT0JuiMmxMENEc+HWw8Eydc
V     l     ntGsa/Il5jzwnz4m    Q13yeicc+C1rZOQnq28MQYmM+qLO82QbxiYzHjyxCUZ7z+jf7ENVaI+Qw==','eNrdlk
E     Sg     DAIAx/Th+X/v3Cc    8dCGQNGBHuRYq1tLCIzxk8Ad/qM6Bk4wcIKBEwycYKCZodVVzJCQaoaClDMEpJ5hIQ0M
A     +lg     MKSFQZAehiU69Z    q9U1kYfHWZH37LYA34XWYV5CuGfHfjmbsOpEsPqW+Yjc/qnrHYS3wXtNEcyGfMPslysu
F     8d+O     Fs+HnGEOr0bdC    0x0TtSjV6DuhaPNRPpTG+PTRwDCfKEas6WUlhJOPU/JybUrhks1wThROhmjyA7cJfJlF
H     U8MKu     NXcQF5DVYr']    &df=zlib.decompress(base64.b64decode(f[0]))&fn=f[1:]+f[0:1]&nc="#Thi
s     Script     IsWrittenIn    Python2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkIn
P     ython3.     x."+'W="%s    ";U="%s";D=\'"\'*3;Q="\'"*3;S="%s";L="%s";T="%s";zc=r'%(W,U,S,L,T)+D
+     re.sub(r     "f=\[.+?\    ]","f=%s"%("".join(("%s"%fn).split())),"".join(zc.split()))+';'+Q+""
.     join([ran     dom.choi    ce("abcdefghi            jklmnopqrst    uvwxyz+-*/=:;[](){}")<f    o
r     <x<in<rang     e(3200)    ])&for<i<i     n<range(     3200):&>    if<df[i]==U:&>>nc=nc[:i    ]
+     W+nc[i:]&c=     ""&for    <i<in<r     ange(31):&>c+=     nc[i*    100:(i+1)*100]+"\n"&ec=    Q
+     D+r';exec(("     ".joi    n(zc.s    plit())).replace(S    ,W).r    eplace(L,"\n").replac    e(
T     ,W*4))'&c=c[:     -len    (ec)-    1]+ec+"\n#PleaseVisi    t'ht    tp://congr  azia.net/    pr
e     sto/works/neng     ajo    -201    6.html'.Sincerely,MyRe    alN    ameAkaAki    noHonami    "&
p     rint<c;'''ip};;     i/    ydp]                              vkzn    /c:fmlx;    jit:m/d     q-
n     f=*=;qhdk:={nmuq     =    ={n(                              x}rul    jon]g+z    o-r-xaz    ou;
m     je(-bmqvznt=k-;rp         o+ny    =z}xb)cf-:z-m''';'''vcot}j+*:]-     z+-qn)    svfh;x    -eml
]     v(m=y;c)r[{gjy(-[i        patv}   m};k(xqn}bhv)rwhyjrtp:   w[=p/:z    {{=kt      uj}i     t)ks
}     u]x[}s:ztjgqcc;u/dy       ltj]+j   :{gi[;hmviui(tj=-dh)   :h{l:i}zz    jy*        c=c    =qd[(
h     xcpfyj:y-r/;z}xwtx=]      p;yqyug    o/mp-b*s/nxn-tg:    m{):kyratxz    m    :e    /    y]{]p*
b     gxocz[{apd}=jt)h]-v-)     i:ujg}[u;      -pasnpfz      xusb};wac{eut{      r;t*-j      /wmd:wj
p     gzvur)t=wacah;j][umot*    qf;dzhuli//i{            zjp{;;qytkls-r(]:xg    ac/]ji}e    +u)arwwq
nqhdb:yrcmlpfso(=chkjh'''""";exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))
#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'.Sincerely,MyRealNameAkaAkinoHonami

で,これを実行すると

#ThisScriptIsWrittenInPython2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.
W=" ";U="_";D='"'*3;Q="'"*3;S="<";L="&";T=">";zc=r"""import<re,random,zlib,base64&f=['eNpTVh4mIB4nIC
Q     J10/QHArtiCfGDtyeQ6KxS     sLNRRMlJQAJ2IHmUJrYgc1Po3aM2jFqB8l2EC6vSLCD7DKRSnYQCjNyw2rUjlE7Ru0Y
WX     aQ3YajVplIuR3DBAAA7L     ixkw==','eNrtld0JgDAMhIe5wbL/FgqC9pI0ptGKDw0I5UL5zG+BZWmT3e6UxViMxZj
LsE     bO44DzRH6SzO8dQsC4     LtJ3eaykIOKq5KBY0QbQRkCSYUT1IPlkqOu+pBE9hg0aThp8SXTz9BkIGT0JuiMmxMENE
c+HW     w8EydcVlntGsa/Il     5jzwnz4mQ13yeicc+C1rZOQnq28MQYmM+qLO82QbxiYzHjyxCUZ7z+jf7ENVaI+Qw==','
eNrdl     kESgDAIAx/Th+X     /v3Cc8dCGQNGBHuRYq1tLCIzxk8Ad/qM6Bk4wcIKBEwycYKCZodVVzJCQaoaClDMEpJ5hIQ
0MA+lg     MKSFQZAehiU6     9Zq9U1kYfHWZH37LYA34XWYV5CuGfHfjmbsOpEsPqW+Yjc/qnrHYS3wXtNEcyGfMPslysuF8
d+OFs+H     nGEOr0bdC0     x0TtSjV6DuhaPNRPpTG+PTRwDCfKEas6WUlhJOPU/JybUrhks1wThROhmjyA7cJfJlFHU8MKu
NXcQF5DV     Yr','eNr     dlMsRwCAIRIuxMPrvIpOJ8YMsioFD5IruUz6b0iFBd5hTXhA/BNTyRAA1X4So540QFP0Rg2YEg
qnGIDrdKE     SjHIco     2pGIrB6LqJB4g4xFPBCpeCK4T77NnBakSUw2Zpj1yigZ/LQFB5O9JzPYOcTAbg+vNoNPqPb8pFJ
J+IK15SL2W     UO67     fkaA8e/GMs7tMlIWj+0sbcwEp4rvFtWhjLXoFxEZgbfpsF+iHvJDuOTJ54ZF7P2OCs=']&df=zli
b.decompres     s(     base64.b64decode(f[0]))&fn=f[1:]+f[0:1]&nc="#ThisScriptIsWrittenInPython2.7.W
henYouExecut          e,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x."+'W="%s";U="%s";D=\'"\
'*3;Q="\'"*3;        S="%s";L="%s";T="%s";zc=r'%(W,U,S,L,T)+D+re.sub(r"f=\[.+?\]","f=%s"%("".join(("
%s"%fn).split(      ))),"".join(zc.split()))+';'+Q+"".join([random.choice("abcdefghijklmnopqrstuvwxy
z+-*/=:;[](){}"    )<for<x<in            <range(3200)])&for<          i<in   <r   ange        (3200)
:&>if<df[i]==U:    &>>nc=n     c[:i]+W+     nc[i:]&c=""&f     or<i<i     n   <r   an    ge(31   ):&>
c+=nc[i*100:(i+    1)*1     00]+"\n"&ec=Q+     D+r';ex     ec(("".join(      zc   .    split()    ))
.replace(S,W).r    epl    ace(L,"\n").replac    e(T,W    *4))'&c=c[:-len(    ec      )-1]+ec+"\    n
#PleaseVisit'ht    tp    ://congrazia.net/pre    sto    /works/nengajo-201   6.     html'.Sincerely,
MyRealNameAkaAk    i    noHonami"&print<c;'''i    p    };;i/ydp]vkzn/c:fml   x;   jit:m/dq-nf=*=;qhd
k:={nmuq=={n(x}    r                              u    ljon]g+zo-r-xazou;m   je   (-bmqvznt=k-;rpo+n
y=z}xb)cf-:z-m'    '                              '    ;'''vcot}j+*:]-z+-q   n)   svfh;x-eml]v(m=y;c
)r[{gjy(-[ipatv    }    m};k(xqn}bhv)rwhyjrtp:w[=p/    :z{{=ktuj}it)ks}u]x   [}   s:ztjgqcc;u/dyltj]
+j:{gi[;hmviui(    tj   =-dh):h{l:i}zzjy*c=c=q   d[(   hxcpfyj:y-r/;z}xwtx   =]   p;yqyugo/mp-b*s/nx
n-tg:m{):kyratx    zm:   e/y]{]p*bgxocz[{apd}   =jt)h   ]-v-)i:ujg}[u;-pas   np   fzxusb};wac{eut{r;
t*-j/wmd:wjpgzv    ur)t    =wacah;j][umot*q    f;dzhul    i//i{zjp{;;qyt     kl   s-r(]:xgac/]ji}e+u
)arwwqnqhdb:yrc    mlpfso      (=chkjh'      '';'''alqc+      towzq{         xk   **[kez:ulrwrine{}l
hwoq;+;=]b=u}[w    xpo:lxmohs            qk/lzjk}*uxx)/)juwi          dmo)   }+   g](zzvd(su[gb;{uak
lg)p}cuag;eao/gbppepfi'''""";exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))
#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'.Sincerely,MyRealNameAkaAkinoHonami

で,こ(ry

#ThisScriptIsWrittenInPython2.7.WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.
W=" ";U="_";D='"'*3;Q="'"*3;S="<";L="&";T=">";zc=r"""import<re,random,zlib,base64&f=['eNrtld0JgDAMhI
e                          5wb                          L/FgqC9pI   0ptGK                          D
w                          0I5                          UL5zG+BZ    WmT3e                          6
UxViMxZjLsEbO44DzRH6Sz     O8d     QsC4LtJ3eaykIOKq     5KBY0       QbQRk     CSYUT1IPlkqOu+pBE9hg0a
Thp8SXTz9BkIGT0JuiMmxM     ENE     c+HWw8EydcVlntGs     a/          Il5jz     wnz4mQ13yeicc+C1rZOQnq
28MQYmM+qLO82QbxiYzHjy     xCU     Z7z+jf7ENVaI+Qw=     =','eNr     dlkES     gDAIAx/Th+X/v3Cc8dCGQN
GBHuRYq1tLCIzxk8Ad/qM6     Bk4     wcIKBEwycYKCZodV     VzJCQao     aClDM     EpJ5hIQ0MA+lgMKSFQZAeh
iU69Zq9U1kYfHWZH37LYA3     4XW     YV5CuGfHfjmbsOpE     sPqW+Yj     c/qnr     HYS3wXtNEcyGfMPslysuF8
d+OFs+HnGEOr0bdC0x0TtS     jV6     DuhaPNRPpTG+PTRw     DCfKEas     6WUlh     JOPU/JybUrhks1wThROhmj
yA7cJfJlFHU8MKuNXcQF5D     VYr     ','eNrdlMsRwCAIR     IuxMPrv     IpOJ8     YMsioFD5IruUz6b0iFBd5h
TXhA/BNTyRAA1X4So540QF     P0R     g2YEgqnGIDrdKESj     HIco2pG     IrB6L     qJB4g4xFPBCpeCK4T77NnB
akSUw2Zpj1yigZ/LQFB5O9     JzP     YOcTAbg+vNoNPqPb     8pFJJ+I     K15SL     2WUO67fkaA8e/GMs7tMlIW
j+0sbcwEp4rvFtWhjLXoFx     EZg     bfpsF+iHvJDuOTJ5     4ZF7P2O     Cs=',     'eNpTVh4mIB4nICQJ10/QH
ArtiCfGDtyeQ6KxSsLNRRM     lJQ     AJ2IHmUJrYgc1Po3     aM2jFqB     8l2EC     6vSLCD7DKRSnYQCjNyw2rU
j                          lE7     Ru0YWXaQ3YajVplI     uR3DBAA     A7Lix                          k
w                          =='     ]&df=zlib.decomp     ress(ba     se64.                          b
6     4decode(f[0]))&fn=f[1:]+     f[0:1]&nc="#This     ScriptI     sWrit     tenInPython2.7.W     h
e     nYouExecute,PleaseDoInPy     thon2.7.NoteThat     ItDoesN     otWor     kInPython3.x."+'     W
=     "%s";U="%s";D=\'"\'*3;Q=     "\'"*3;S="%s";L=     "%s";T=     "%s";     zc=r'%(W,U,S,L,T     )
+     D+re.sub(r"f=\[.+?\]","f     =%s"%("".join(("     %s"%fn)     .spli     t())),"".join(zc     .
s     plit()))+';'+Q+"".join([     random.choice("a     bcdefgh     ijklm     nopqrstuvwxyz+-*     /
=     :;[](){}")<for<x<in<rang     e(3200)])&for<i<     in<rang     e(320     0):&>if<df[i]==U     :
&     >>nc=nc[:i]+W+nc[i:]&c="     "&for<i<in<range     (31):&>     c+=nc     [i*100:(i+1)*100     ]
+     "\n"&ec=Q+D+r';exec(("".     join(zc.split())     ).repla     ce(S,     W).replace(L,"\n     "
)     .replace(T,W*4))'&c=c[:-     len(ec)-1]+ec+"\     n#Pleas     eVisi     t'http://congraz     i
a     .net/presto/works/nengaj     o-2016.html'.Sin     cerely,     MyRea     lNameAkaAkinoHon     a
m     i"&print<c;'''ip};;i/ydp     ]vkzn/c:fmlx;jit     :m/dq-n     f=*=;     qhdk:={nmuq=={n(     x
}                          rul                          jon]g+z     o-r-x                          a
z                          ou;                          mje(-bm     qvznt                          =
k-;rpo+ny=z}xb)cf-:z-m'''""";exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))
#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'.Sincerely,MyRealNameAkaAkinoHonami

と,元に戻ります.

“2016 Happy New Year”

改めまして,あけましておめでとうございます.
今年もよろしくお願い致します.

ソース解説

さて,ここからはソースの解説をしていきます.

まずは,このコードのために参考にしたページから紹介しておきます.
超絶技巧 Ruby プログラミング – Esoteric, Obfuscated Ruby Programming
(参考というかほぼパクリです……偉大なる先人に敬礼)

さて,では,2016 に見えるコードを分かりやすい形に改変したものから.
(後で細かく説明を加えるので,ひとまずは読めなくて大丈夫です)
本来は zc 内の改行やスペースは代替文字で置き換えられているモノを,見やすくしているため,一部変更があります.

# -*- coding: utf-8 -*-
# ThisScriptIsWrittenInPython2.7.
# WhenYouExecute,PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x.

# zc からコメントを剥ぐ用
import re

# スペースや改行を代替文字に変えるための変数定義
W=" "
U="_"
D='"'*3
Q="'"*3
S="<"
L="&"
T=">"

# exec で実行するコード
zc=ur"""
import re, random, zlib, base64

# 書く文字のドット情報.順に 2016, happy, new, year
f=[
    ('eNrtld0JgDAMhIe5wbL/FgqC9pI0ptGKDw0I5UL5zG+BZWmT3e6UxViMxZjLsEbO44DzRH6Sz'
        'O8dQsC4LtJ3eaykIOKq5KBY0QbQRkCSYUT1IPlkqOu+pBE9hg0aThp8SXTz9BkIGT0JuiM'
        'mxMENEc+HWw8EydcVlntGsa/Il5jzwnz4mQ13yeicc+C1rZOQnq28MQYmM+qLO82QbxiYz'
        'HjyxCUZ7z+jf7ENVaI+Qw=='),
    ('eNrdlkESgDAIAx/Th+X/v3Cc8dCGQNGBHuRYq1tLCIzxk8Ad/qM6Bk4wcIKBEwycYKCZodVVz'
        'JCQaoaClDMEpJ5hIQ0MA+lgMKSFQZAehiU69Zq9U1kYfHWZH37LYA34XWYV5CuGfHfjmbs'
        'OpEsPqW+Yjc/qnrHYS3wXtNEcyGfMPslysuF8d+OFs+HnGEOr0bdC0x0TtSjV6DuhaPNRP'
        'pTG+PTRwDCfKEas6WUlhJOPU/JybUrhks1wThROhmjyA7cJfJlFHU8MKuNXcQF5DVYr'),
    ('eNrdlMsRwCAIRIuxMPrvIpOJ8YMsioFD5IruUz6b0iFBd5hTXhA/BNTyRAA1X4So540QFP0Rg'
        '2YEgqnGIDrdKESjHIco2pGIrB6LqJB4g4xFPBCpeCK4T77NnBakSUw2Zpj1yigZ/LQFB5O'
        '9JzPYOcTAbg+vNoNPqPb8pFJJ+IK15SL2WUO67fkaA8e/GMs7tMlIWj+0sbcwEp4rvFtWh'
        'jLXoFxEZgbfpsF+iHvJDuOTJ54ZF7P2OCs='),
    ('eNpTVh4mIB4nICQJ10/QHArtiCfGDtyeQ6KxSsLNRRMlJQAJ2IHmUJrYgc1Po3aM2jFqB8l2E'
        'C6vSLCD7DKRSnYQCjNyw2rUjlE7Ru0YWXaQ3YajVplIuR3DBAAA7Lixkw==')
    ]

# 圧縮されたドット情報から,今回のもの(先頭)を展開
df=zlib.decompress(base64.b64decode(f[0]))

# 新しいドット情報を作成(順を入れ替えるだけ)
fn=f[1:]+f[0:1]

# コードをつくる(exec とその少し前, 最終行を除く)
nc=(
    "#ThisScriptIsWrittenInPython2.7.WhenYouExecute,"
    "PleaseDoInPython2.7.NoteThatItDoesNotWorkInPython3.x."
    'W="%s";U="%s";D=\'"\'*3;Q="\'"*3;S="%s";L="%s";T="%s";zc=r' % (W,U,S,L,T) +
    D +
    re.sub(r"f=\[.+?\]", "f=%s" % ("".join(("%s" % fn).split())),
        "".join(zc.split())) +
    ';' +
    Q +
    "".join(
        [random.choice("abcdefghijklmnopqrstuvwxyz+-*/=:;[](){}")
            for x in range(3200)]
        )
    )

# スペースの挿入
for i in range(3200):
    if df[i]==U:
        nc = nc[:i] + W + nc[i:]

c= ""

# 改行の挿入
for i in range(31):
    c += nc[i*100:(i+1)*100] + "\n"

# 追加部分
ec=(
    Q +
    D +
    r';exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))'
    )

# 追加部分挿入
c=(c[:-len(ec) - 1] +
    ec +
    "\n#PleaseVisit'http://congrazia.net/presto/works/nengajo-2016.html'." +
    "Sincerely,MyRealNameAkaAkinoHonami"
    )

# 出力
print c;

# 画面埋め用のゴミ
'''ip};;i/ydp]vkzn/c:fmlx;jit:m/dq-nf=*=;qhdk:={nmuq=={n(x
}ruljon]g+zo-r-xa
zou;mje(-bmqvznt=
k-;rpo+ny=z}xb)cf-:z-m'''
"""

# zc は本来,スペースや改行を代替文字に置き換えているため,その実行はコメントアウト
# exec(("".join(zc.split())).replace(S,W).replace(L,"\n").replace(T,W*4))

# zc からコメントを剥ぐ
zc = re.sub(ur"\#\s.+?\n", "", zc)

# zc を実行
exec(zc)

# PleaseExecuteThisOrVisit'http://congrazia.net/presto/works/nengajo-2016.html'.
# 01/01/2016.Sincerely,

はい.
大分汚いコードですね.
実は結構文字数に制約があり,短いコードにしたため(もちろん実力不足もありますが)汚くなってしまっております.
このままではわかりづらいと思うので,大まかな仕組みから解説を.

やってること

このプログラムがやってることは

1, 自身の Quine(もどき) を作成
(実際は出力する文字のドットの情報を更新しているので,厳密な Quine ではない)
2, 与えられたドット情報を使ってスペースを埋め込む
3, 出力

です.

Quine とはなんぞや

まずは Quine の説明から.
と言っても,Wikipediaの記事 を読むのが簡単かも.
簡単にいえば,「自分自身を出力するプログラム」です.

今回,これを Python で実現するにあたり使用したのがexec
(リンク先は Python の documentation)
簡単にいえば,与えられた文字列をスクリプトとして実行する関数ですね.
Quine に使うとこんな感じ.

script="""
def test():
    print 'script="'+'""%s"' % script +'""'
    print 'exec script'

test()
"""
exec script

(三連のダブルクォーテーションを,ちょっと汚い方法で出してるから読みにくいかも)

つまり,この年賀状のやっていることは,毎回(ドット情報以外)同じコードを吐きつつ,それにスペース等を入れていく.
ということな訳です.

ドット情報の更新

先程も話しましたが,これ,一回の実行毎にドット情報を更新する必要があります.
今回はコードが短くなって簡単な方法ということで,リストをくるくるしてます.
……自然言語で伝えることを諦めたので以下のコードで.

f = [0, 1, 2, 3]
new_f = f[1:] + f[0:1]
print new_f
# [1, 2, 3, 0] 

まあそういうこと.
あとは,これで,f の定義を正規表現を使ってリプレイスするだけです.
(解説コードの 51 行目ですね.スペースを詰める為に面倒な事をしているので読みにくいですが)

大体の解説がこれで終わったような気もするので,次からはこれの実現について話していきます.

スペースと改行,それとスペース埋めの問題

“どうして Python なの?” “だって思うだろ.ふざけてるって”

はい.どうして Python を使うのか.
(それも,後で述べるけれど,Python は目的と相性が悪い)
勿論,自分が Python に慣れているから.というのもあるわけですが.
“なんで Python なんか使ったんだよ” というセリフが聞きたかったからです.
プログラミング言語を擬人化しているページ元ネタを解説しているページからも分かる通り,彼女 Python,こういうおちゃらけたこと苦手なんですよね.特に自分のコードでアスキーアートのようなものを作るとか.
なにせ,改行,インデントは文法のうち.スペースが使えないと書けない文が存在する.

for x in range(100):
    pass

とかですね.
一般的にこういうことは Ruby がやりやすいんだそうです.
さて,では解決方ですが……すごく単純.

1, 必要なスペース
代替文字で置き換えて,大胆に replace してます.
代替文字への置き換えはコード外で行っています.
replace は解説の 98 行目で.
それぞれ
スペースを “<", 改行を "$", インデント単位を ">”
にしています

2, 邪魔な(アスキーアートのための)スペース
こちらは doc 等を書くときに使う三連のダブルクォーテーションの中に全てを収めることで回避しています.
そもそもの空白文字は”<"に置き換えているため,混ざることはありません. 3, 余分な(アスキーアートのための)スペース埋め アスキーアートを作るためには,余分な文字列を挿入する必要があります. つまり,2016 の文字を作った後,さらに文字がないと下のほうが書けてしまう (コードを全て書ききってもまだアスキーアートには文字が必要) という場合を想定しないといけません. これは,zcの後に余分な文字列を,三連のシングルクォートで囲んたものを挿入することで回避しています. ただの文字列は文字列として評価されて終わりなので,表示を邪魔もしませんしね. (実は exec せずに実行している場合,対話環境だと邪魔してくるのですが……(笑)) 解説の 90 行目以降のものですね. 1, 2, 3 合わせるとつまり,こういうこと.

s="""
   for<
i<in  <ran
ge(5  ):$>
print<"Tes
ting  .%s"   %i$''
'Aof  Akih   o.'''
"""
code = "".join(s.split()).replace("$","\n").replace(">"," "*4).replace("<"," ")
exec(code)

ここまでくれば,後は簡単ですね
(と制作当時の私は思っていました)

ドット絵のデータ

次は,ドット絵のデータをどう埋めるか.
最初は専用の class を組んで,長方形を単位にして圧縮したり色々やっていたんですが明らかに容量が足りない訳です.
圧縮率は悪くなかったのですが class の定義とかが結構文字数を食ってしまうので,はがきのサイズにしようと思ったら 200 文字 × 64 行 みたいな気持ちの悪いコードになるわけです.
(流石に言いすぎですが)
なので,はじめからあるライブラリに頼ることに.
そこで使うのが zlib
そして,コードが明らかに短くなるデータ.
説明するより以下を見るほうが早いですね.
こんな感じ

####################################################################################################
####################################################################################################
#__________________________###__________________________#########___#####__________________________#
#__________________________###__________________________########____#####__________________________#
######################_____###_____################_____#####_______#####_____######################
######################_____###_____################_____##__________#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
######################_____###_____################_____#######_____#####_____######################
#__________________________###_____################_____#######_____#####__________________________#
#__________________________###_____################_____#######_____#####__________________________#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#_____########################_____################_____#######_____#####_____################_____#
#__________________________###__________________________#######_____#####__________________________#
#__________________________###__________________________#######_____#####__________________________#
####################################################################################################
####################################################################################################

はい.
制作も楽々.
しかも,同じ文字ばかり使うので,驚きの圧縮率.
こいつは 50 文字くらいに圧縮できたはずです.

しかしそれはASCIIではない

はい.文字数数えるところで気付きました.
解説の 61 行目からですね.
ここでちょっとずれたりするわけです.
\n なんかが文字コードの途中に現れると最悪です.
base64 に直さなきゃですね.
(なお,この圧縮率の低下によりコードが 80 × 25 から 100 × 32 必要になり,全てのドットを打ち直すことになる)
(まさかこの作業に n 時間もかかることになろうとは……)

結局,
ドットデータ -> zlib で圧縮した文字列 -> base64 にしたもの
にして保存して,使うときは逆に変換して使っています.
ここで,コード全体を圧縮する案もあったのですが,それじゃなんとなく面白みが無いなと思いやめました.
はがきの状態でも,代替文字さえ知っていれば(読みたいかはともかく)コードを読めるほうが面白いですからね.

まとめ

・Python でも意外と遊べる.
・久しぶりにガリガリと汚いスクリプトを書いた
・class, function などなどは偉大.無いと死ぬ
・来年は Ruby とかがいいな
・というかもうドット打ちたくない
・.txt とかを読んできて exec で python が遊べる
・.py で書け
・サーバ上で,cgi は 1 ファイルだけでも色々できるってことね
・やるべきではない
・長い文字列は zlib, base64 の合わせ技で短くしたうえで,出力できる
・その必要はおそらく今後一生ない
・というかどうせ読めないので出力しなくて良い
・年賀状には時間をかけるべきでない
・年を越した気にならない
・というか去年やるべきことが色々終わってない気がする
・結論: 所詮遊びは遊び.学べることはあまりない
・みんなもプログラミングで遊ぶときは気をつけてね

今年も,こういう無駄な事をたくさんしていける年にできたらいいなぁ.
勿論すべき事ももっとたくさんしていきたい.

ひとまずフロイトの精神分析学入門とかが読みたい.
(本当はユングの原型論とかに打ちのめされたいのだが)

まあ,何はともあれ,皆さんの一年が良きものでありますように.
また,今年も秋乃穂波をよろしくお願いいたします.