RPG的対戦のプロトコル

ポケモンでアリスとボブが対戦するとき、サーバなしで、できるだけチートができないようにすることを考える。

各ターンの行動前

各ターン開始時に、アリスは2048bitの乱数を生成する。その乱数を行動鍵Aとする。
行動鍵Aのハッシュ値(行動鍵ハッシュA)をボブに送る。
ボブも同様。

各ターンの行動決定

アリスが行動を決定する。行動には誰もが検証可能な一意な行動名Aがつく。"waza_hakaikousen"とか。
アリスは行動名Aと行動鍵AのHMAC(行動名HMAC_A)値をボブに送る。
ボブも同様

行動の確認

アリスは行動鍵Aをボブに送る。また、行動鍵Bをボブから受け取る。
行動鍵ハッシュBを用いて行動鍵Bが正しいことを確認する。

アリスは行動名Aをボブに送る。また、行動名Bをボブから受け取る。
行動名HMAC_Bと行動鍵Bを用いて行動名Bが正しいことを確認する。
ここで、互いに何の行動をしたか判明する。

技の乱数を決定

行動鍵Aと行動鍵Bのxorをとり、乱数シードを生成する。
ボブも同じ乱数シードを生成できる。
その乱数シードをアリスの技の乱数値として合意する。
また、乱数シードのハッシュ値をボブの技の乱数値として合意する。

ステータスや持ち物について

行動と同じような方法で1ターン目に交換する。

Windowsのデスクトップアプリ試作

Windowsのデスクトップアプリ作成の入門として、WPFマインスイーパを作った。
https://github.com/mob-a/Minebeginner

目的感

ネイティブなデスクトップアプリを作ってみる
題材はなんとなくゲームがいい
C#に触ってみる
Visual Studioに触ってみる
GitHubにアップロードしてみる

雑感

C#からxamlへの変数バインディングを使いたかったが断念した。ユーザの操作に応じて動的に画面を書き換えるのが難しい。結局手続き的に書き換えを行った
適当にvarで変数を指定してもだいたいいい感じ型を決めてくれて楽
Visual Studioの動作がすごい軽快
雑に全部小文字で書いても、いい感じで正しい変数名・メソッド名を指示してくれるのが快適
Emacsキーバインドが恋しい

備忘録2017

去年私が触れて楽しかったコンテンツを振り返る。

なろう小説

ずっとなろうを読んでいた。書籍版・コミカライズも大量に。
小説媒体はなろう発のものしか読んでない可能性がある。
今年読み始めた中で面白かったものをいくつか挙げる。

勇者召喚に巻き込まれたけど、異世界は平和でした

モーニングスター大賞の大賞受賞作。なろうのトップに常にバナーが出ている。
ひたすらに主人公がモテまくるハーレムラブコメ
基本的に全てのエピソードがヒロインの魅力を描写するために存在する(と感じる)。
舞台となる世界では神や魔族や人の有力者がほぼ全員美少女で、全員が主人公に好感を持つ。なので世界設定を描写するとそれが必然的にヒロインの描写になる。
ヒロインがかなり多いんだけど、その全員の魅力が妥協なく描かれる。あとがき専用ヒロイン(なにそれ)までかわいい。

齢5000年の草食ドラゴン、いわれなき邪竜認定〜やだこの生贄、人の話を聞いてくれない〜

2018年2月に角川スニーカー文庫で書籍化。ガンガンJOKERで2018年1月にコミカライズ開始。なんとコミカライズのほうが早い。
タイトル通りの勘違いコメディ。暴走する生贄少女とそれに振り回される邪竜様(他称)のやりとりがコントのようで楽しい。
あとポンコツ邪竜様とポンコツ聖女様がかわいい。

生き残り錬金術師は街で静かに暮らしたい

書籍版発売中・コミカライズも連載中。
https://comic-walker.com/contents/detail/KDCW_EB03200101010000_68/
薬品チートで薬屋兼喫茶店のようなものの経営、ゲーム的描写(アトリエ風)となろう的スローライフの王道っぽい。
その一方で舞台となる迷宮都市の悲壮感や迷宮攻略も描かれ、多面的に楽しめる。

はぐるまどらいぶ。

ヒーロー文庫から2018年2月に出版予定。
なろう×少年漫画×変身ヒーロー×童話って感じでロマンが詰まったよくわからない作品。
文体のアクが強くてやみつきになる。
さーしーえー(挿絵)も作者自ら描いていて雰囲気が良い。

漫画

読んだものを読書メーターで管理している。https://bookmeter.com/users/463730
面白かったものをいくつか。

三者三葉

おととしアニメ見て楽しかったので既刊を一気に読んだ。
仲が良いんだけど個々人は好き勝手に行動しているバランス感覚が好み。
奇人変人がdisりあう光景に心地よさを感じる。
原作は葉子様の主人公感が強かった。

ゆるキャン△

(5巻まだ読んでない)
冬の夜の描写が良い。
3巻で、リンちゃんがソロキャンをしつつ野クルメンバーと常に繋がっている描写があり、携帯通信端末の潜在能力に気付いた。気付くのがたぶん15年ぐらい遅い。
アニメも楽しみ。

ふつうの恋子ちゃん

表情が変わらないけど内面ではすっごいわたわたしてる、みたいなツンデレチョロインの脳内が読める。すごくいい。
男の子のほうもかわいい。

おはよう、いばら姫

最高にエモい漫画だった。
伏線がよく回収されて、6巻で過不足なく完結していた。

どうでもいいけど、最近ニコニコは漫画と大百科がメインコンテンツだと思うようになってきた。
コメント込みで楽しい http://seiga.nicovideo.jp/comic/29092

JRPG

「無名IPの萌豚向けオフラインJRPG」がやりたいという欲求が高まっていた。
クリアしたものが以下の通り(プレイ順)。

イメージエポック5、コンパイルハート2、ガスト1。偏っている。
アトリエは無名じゃないけど、まあ自分がやったことがないものということで。

ステラグロウが非常に楽しかった。ものすごく私向きだった。

STELLA GLOW - 3DS

STELLA GLOW - 3DS

キャラと演出と歌とBGMとシナリオが良い。
それビジュアルノベルでいいんじゃない?って感じではあるが、ゲーム部分とシナリオ部分をうまく融合させた演出ができていた。
例えば「敵のステータスが高くて明らかに勝てないピンチな状況で、魔女が新たな歌魔法を習得し、その歌魔法をコマンド入力して使用、逆転して勝利」みたいな感じでシナリオ上の展開がゲームシステムで表現される。
RPG(SRPG)ならではの体験であり、ビジュアルノベルでは表現が困難なものだと思う。

事実上の前作といわれるルミナスアーク1,2,3もやった。
さすがに今DSのゲームをやると音質が厳しい。ステラグロウのような演出の良さはあまりない。
とはいうものの、キャラは好みなので楽しめた。特に2の魔女勢が好き。結局のところ美少女がいればそれだけで満足です。


あとどうでもいいことだけど、魔女というか魔女帽大好きという性癖に目覚めた。
完全にルミナスアークのせい。

(自分用メモ)ベイズ統計モデリング入門中

説明変数x、目的変数yとして、3つのパラメータ(a,b,s)を持つ以下のようなモデルを作ってみる (簡単のため全て正規分布にしている)。

  y~Normal(ax+b, s)
  a(prior)~Normal(Hma, Hsa)
  b(prior)~Normal(Hmb, Hsb)
  s(prior)~Normal(Hms, Hss)

※Hma〜Hssはハイパーパラメータで、人手で恣意的に決めていい。

                  • -

以下、p(Y|M,S)を正規分布の確率密度の値とする。
  p(Y|M,S)=\frac{1}{\sqrt{2 \pi S^2}} \exp (-\frac{(Y-M)^2}{2S^2})
y~Normal(M,S)のモデルにおいて、y=Yとなる確率またはlikelihoodは、p(Y|M,S)である。
(確率の計算と尤度の計算に何の違いがあるのかわからん)。

                  • -

サンプルが3つ得られたとする。
(X1,Y1),(X2,Y2),(X3,Y3)

                  • -

パラメータが特定の値 (a,b,s)=(A,B,S) をとるとき、posteriorを求めたい。
ベイズ統計で一般的な以下の関係を使う。
  posterior ∝ prior * likelihood
priorとlikelihoodをそれぞれ求めれば、posterior(の相対比率)が求められる。

prior

モデルのところに記述した(a,b,s)の各パラメータのpriorを使う。
aのpriorであるNormal(Hma, Hsa)からa=Aとなる確率を求めると、p(A|Hma, Hsa)になる。
b,sでも同様にB,Sとなる確率を求めていき、それらの確率の積をとる。
  prior(A,B,S) = p(A|Hma, Hsa) * p(B|Hmb, Hsb) * p(S|Hms, Hss)

likelihood

(a,b,s)=(A,B,S)のとき、モデルは下記のものになる。
  y~Normal(Ax+B, S)

まず1つのサンプル(X1,Y1)について考える。
単純にモデルの変数を置き換えると以下のようになる。
  Y1~Normal(AX1+B, S)
すなわち「Y1は『平均がAX1+B, 標準偏差がSの正規分布』から生成された」ことを意味する。

この1サンプルでのlikelihoodは、
  p(Y1|AX1+B, S)
となる。X1,Y1,A,B,Sはすでに分かっている値なので、1つのスカラー値を導出可能である。

3つのサンプルでのlikelihoodは、それぞれのサンプルのlikelihooodの積をとる。
  likelihood(A,B,S,X,Y) = p(Y1|AX1+B,S)*p(Y2|AX2+B,S)*p(Y3|AX3+B,S)
ここで、サンプルごとに正規分布の平均が違うことに注意する。
すなわち、Y1,Y2,Y3はそれぞれ別の正規分布から生成されている。

                  • -

というわけでposteriorに戻ってくると、さっき求めたpriorとlikelihoodを使って以下のようになる。

  posterior ∝ prior * likelihood
  posterior ∝ {p(A|Hma, Hsa) * p(B|Hmb, Hsb) * p(S|Hms, Hss)} * {p(Y1|AX1+B, S) * p(Y2|AX2+B, S) * p(Y3|AX3+B, S)}

この式を見ると、サンプルの数が増えるごとにlikelihoodの影響力が強くなっていくことが分かる。

なお、posteriorの比例係数(ベイズの法則での分母)はモデルの形式とハイパーパラメータのみによって決まり、A,B,Sに依存しない値である。

  • MAP推定をする場合、posteriorの大小比較ができればいいだけなので、分母は気にしなくていい。
  • サンプリングをする場合、posteriorの比がとれればいいだけなので、分母は気にしなくていい。

一迅社のジャンル特化アンソロジー

一迅社のジャンル特化アンソロジーコミックが流行りに乗っかっていて面白かったのでタイトルを列挙してみる。
http://data.ichijinsha.co.jp/book/booksearch/booksearch_list.php?WRITER=%BD%BD%CA%B8%BB%FA%C0%C4&ISBN=&CATEGORY=&SALEY=&SALEM=&WORD=&TYPE=and&WI=1752


選定基準としては

  • 特定作品の二次創作でないもの
  • タイトルで内容が推測できるもの

とする。
また、シリーズのものは最初のみ載せる。
例えば女装少年アンソロジーコミックはシリーズで16冊出版されているが、最初の「女装少年アンソロジーコミック 白組」が出た2010年のみ記載。

                                                                            • -

2007年

2009年

  • 戦国主従異聞録

2010年

2011年

  • Girls Love
  • 拘束少女アンソロジーコミック

2012年

  • スポブラ少女アンソロジーコミック
  • ゾンビアンソロジーコミック

2013年

2014年

2015年

  • キマシ!

2016年

  • 異種恋愛物語集
  • オネエと私
  • 人外さんと恋する方法
  • 異世界転生して勇者になろう! アンソロジーコミック
  • 童貞を殺すアンソロジーコミック
  • 人外の嫁といちゃいちゃする アンソロジーコミック
  • 女の子の身体に入れ替わっちゃう アンソロジーコミック

2017年

  • 年下の男の子と恋するアンソロジー
  • ひょっとしてギャルは俺らに優しいのでは? アンソロジーコミック
  • 少年と私の事情アンソロジー
  • ボーイズ on ICE フィギュア男子アンソロジー
  • JCの魅力を楽しむ健全なアンソロジーコミック
  • 異世界に転生して愛されるアンソロジー
  • スキだらけJKといちゃエロできちゃう アンソロジーコミック
  • 年下の女子にマウントされる アンソロジーコミック

可読性を保ちつつデータをエスケープするおもちゃ

概要

CSVデータをsort,uniq,cut,awkだけで分析したいがそうはいかないことがある。
データ中にカンマが入っている場合、cutでは取得する列がずれる場合がある。さらに改行文字が入っている場合もうまくいかない。
その場合には、適切なCSVライブラリを使ってエスケープ・エスケープ解除をしなければならない。

データを全てbase64などでエンコードしてしまえば、改行文字やカンマなどの危険な文字は入ってこない。
ただし、その場合は人の可読性が全くなくなる。
可読性をそこそこに保ったまま危険な文字をエスケープする方法として、Quoted-printableを使うことが考えられる。
https://ja.wikipedia.org/wiki/Quoted-printable
これは、asciiの英数字と一部の記号はそのまま表示するが、ascii制御記号などの1バイトを、"=0A"などイコールの後に十六進数2文字で符号化する方式である。

ただし、日本語文を含むデータでQuoted-printableによるエスケープを行うと、日本語部分は符号化されて読めなくなる。
UTF8の漢字・ひらがな・カタカナはデータ中に含まれていてもsort,uniq,cut,awkするのにほとんど問題はない(※1,2)ため、できれば漢字・ひらがな・カタカナもそのまま残しておきたい。
符号化しない漢字の基準としては、とりあえずJIS第1第2水準漢字とする。
まとめると、以下のようなエスケープルールを作りたい。

  • 符号化しないバイト
    • アルファベット
    • 数字
    • 比較的安全なascii記号
    • JIS X 0208に含まれる文字に該当するバイト列 (UTF-8であることが前提)
  • 符号化するバイト
    • 「符号化しないバイト」以外全て
    • 例:ascii制御記号(タブ、CR、LFなど)、比較的危険なascii記号(カンマ、ダブルクオートなど) 、私が読めない文字(アラビア文字など)

試しにpythonで作った。パフォーマンスはかなり悪い。

(※1)UTF8では、asciiの1バイト文字では先頭ビットが0、漢字のようなマルチバイト文字では各バイトの先頭ビットが全て1になる。そのためマルチバイト文字中の1バイトをascii文字として解釈することはない。
https://ja.wikipedia.org/wiki/UTF-8

(※2)マルチバイト文字を含むテキストでsort,uniqするときには環境変数LC_ALL=Cを設定しておいたほうがいい.
https://linuxjm.osdn.jp/html/GNU_coreutils/man1/sort.1.html
google:sort uniq LC_ALL=C

実行サンプル

準備

まずはunicode.orgからJIS X 0208の文字一覧を取得してきて、扱いやすくするためJSONに変換する。

$ wget http://unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT
$ python create_jisx_json.py > jisx0208.json

サンプルデータとして、wikipediaのHyperText_Markup_Languageのページ( https://ja.wikipedia.org/wiki/HyperText_Markup_Language )からHTMLを用意。

<!DOCTYPE html>
<html lang="ja">
 <head>
  <meta charset="UTF-8">
  <link rel="author" href="mailto:mail@example.com">
  <title lang="en">HyperText Markup Language - Wikipedia</title>
 </head>
 <body>
  <article>
   <h1 lang="en">HyperText Markup Language</h1>
   <p>HTMLは、<a href="http://ja.wikipedia.org/wiki/SGML">SGML</a>
      アプリケーションの一つで、ハイパーテキストを利用してワールド
      ワイドウェブ上で情報を発信するために作られ、
      ワールドワイドウェブの<strong>基幹的役割</strong>をなしている。
      情報を発信するための文書構造を定義するために使われ、
      ある程度機械が理解可能な言語で、
      写真の埋め込みや、フォームの作成、
      ハイパーテキストによるHTML間の連携が可能である。</p>
  </article>
 </body>
</html>
エンコード

改行文字を含むascii制御文字は全てエスケープするため、確実に一行で表すことができる。

$ cat sample.html |python jisquopri_encode.py
<!DOCTYPE=20html>=0A<html=20lang=3D=22ja=22>=0A=20<head>=0A=20=20<meta=20charset=3D=22UTF-8=22>=0A=20=20<link=20rel=3D=22author=22=20href=3D=22mailto:mail@example.com=22>=0A=20=20<title=20lang=3D=22en=22>HyperText=20Markup=20Language=20-=20Wikipedia</title>=0A=20</head>=0A=20<body>=0A=20=20<article>=0A=20=20=20<h1=20lang=3D=22en=22>HyperText=20Markup=20Language</h1>=0A=20=20=20<p>HTMLは、<a=20href=3D=22http://ja.wikipedia.org/wiki/SGML=22>SGML</a>=0A=20=20=20=20=20=20アプリケーションの一つで、ハイパーテキストを利用してワールド=0A=20=20=20=20=20=20ワイドウェブ上で情報を発信するために作られ、=0A=20=20=20=20=20=20ワールドワイド ウェブの<strong>基幹的役割</strong>をなしている。=0A=20=20=20=20=20=20情報を発信するための文書構造を定義するために使われ、=0A=20=20=20=20=20=20ある程度機械が理解可能な言語で、=0A=20=20=20=20=20=20写真の埋め込み や、フォームの作成、=0A=20=20=20=20=20=20ハイパーテキストによるHTML間の連携が可能である。</p>=0A=20=20</article>=0A=20</body>=0A</html>=0A
エンコード(改行あり)

適当な位置で改行するオプションを使用(元データの改行位置とは関係ない)。
"DOCTYPE"や"アプリケーション"などの文字列はエスケープされないが、スペースやダブルクオートはエスケープされる。

$ cat sample.html |python jisquopri_encode.py --newline 40
<!DOCTYPE=20html>=0A<html=20lang=3D=22ja=22>=0A=20<head>
=0A=20=20<meta=20charset=3D=22UTF-8=22>=0A=20=20<link=20rel=3D=22a
uthor=22=20href=3D=22mailto:mail@example.com=22>=0A=20
=20<title=20lang=3D=22en=22>HyperText=20Markup=20Langu
age=20-=20Wikipedia</title>=0A=20</head>=0A=20<body>
=0A=20=20<article>=0A=20=20=20<h1=20lang=3D=22en=22>HyperText=20
Markup=20Language</h1>=0A=20=20=20<p>HTMLは、<a=20href
=3D=22http://ja.wikipedia.org/wiki/SGML=22>SGM
L</a>=0A=20=20=20=20=20=20アプリケーションの一つで、ハイパーテキストを利用してワー
ルド=0A=20=20=20=20=20=20ワイドウェブ上で情報を発信するために作られ、=0A=20=20=20=20=20=20ワー
ルドワイドウェブの<strong>基幹的役割</strong>をなしている。=0A=20
=20=20=20=20=20情報を発信するための文書構造を定義するために使われ、=0A=20=20=20=20=20=20ある
程度機械が理解可能な言語で、=0A=20=20=20=20=20=20写真の埋め込みや、フォームの作成、=0A=20
=20=20=20=20=20ハイパーテキストによるHTML間の連携が可能である。</p>=0A=20=20</
article>=0A=20</body>=0A</html>=0A
適当な圧縮データをエンコード

任意のバイト列をエンコードすることが可能。
ほとんどマルチバイト文字は現れないが、6行目にキリル文字のшが出現している。

$ cat create_jisx_json.py| gzip | python jisquopri_encode.py --newline 40
=1F=8B=08=00=22=C7=0DY=00=03=85R=B1n=C3=20=10=DD=F9=0AD=17=90=1C=E4z=A8=A2H^+=B5k3TJ=AC=88=1A=DC
=5CD=C0=82s=AB=AA=EA=BF=17l=27n=A6=DEb=B8w=EF=DD=F1=CEp=EE}@z=8A=DE=91=13D=0B=11iMw=0D=F9=04
<R=DF=1B=C7=D9=F3=D3KY=95k=B9}=DD=B2=82=B2=C0=04U=91v=1BBSt>P=0B=CEPp=B4=93=C1(=9Do=91
=8B=09=CE1=C2=F5=F8=91!b=80=9E=8B+=08=DD=08=EC=CA=86=D65ewl=E1=E5h=BDCp=83=B9&=B5=C2=8BV=EC-
=20g{d=8B=DC=E0=A0=F5=DA=1C@=A7*=F3=A1=2CO=84]=D5=FC=A9h=8F*$p=A9=94=E8=0Fo_=98=86=AE=0A=96$
=D1=1A&=A46=19=E6l=C0nu=FF=B0=CA=B9E=03=BB=F5D=C9f=8D=A7ш=E9=94=9C=18=9BH=E3=16=895=13=CD-=DF=1A
=F7=9E<N=8FI&/=8A7=D6=CC=C3&c=F6=FB=FF=9C=99=D7=27U=9F=B6=A6=F97=CB=5C=B6=994=D2=DEr=8B=B1C=CE
]=DB=CD=C04=CC=05=99n?=82=10=D2=07p=C8=F3=FF!=F5p=EE#=9F=DB=14=D4=B88=04sP=B1=05=A8=1F=95=8D=A6H
O=D7=C6a]=09A~=01*}=BF=A3Z=02=00=00
デコード

エンコードしたデータを元のバイト列に戻すことも可能

$ cat sample.html |python jisquopri_encode.py | python jisquopri_decode.py
<!DOCTYPE html>
<html lang="ja">
 <head>
  <meta charset="UTF-8">
  <link rel="author" href="mailto:mail@example.com">
  <title lang="en">HyperText Markup Language - Wikipedia</title>
 </head>
 <body>
  <article>
   <h1 lang="en">HyperText Markup Language</h1>
   <p>HTMLは、<a href="http://ja.wikipedia.org/wiki/SGML">SGML</a>
      アプリケーションの一つで、ハイパーテキストを利用してワールド
      ワイドウェブ上で情報を発信するために作られ、
      ワールドワイドウェブの<strong>基幹的役割</strong>をなしている。
      情報を発信するための文書構造を定義するために使われ、
      ある程度機械が理解可能な言語で、
      写真の埋め込みや、フォームの作成、
      ハイパーテキストによるHTML間の連携が可能である。</p>
  </article>
 </body>
</html>

コード(NYSLライセンス)

JISQuopri.py

import json

class JISQuopriEncode(object):
    CONTROLS = list(range(0x00, 0x20)) + [0x7f]
    MSBS = list(range(0x80, 0x100))
    ASCII_NOT_PRINTABLE = CONTROLS + MSBS

    BYTE_TO_BYTES = [i.to_bytes(1, "little") for i in range(256)]

    UTF8_3BYTE_MAX = 239
    UTF8_3BYTE_MIN = 224

    UTF8_2BYTE_MAX = 223
    UTF8_2BYTE_MIN = 192

    TO_HEXBYTES = [
        b"0",b"1",b"2",b"3",b"4",b"5",b"6",b"7",b"8",b"9",b"A",b"B",b"C",b"D",b"E",b"F"
    ]
    JISX_JSON_PATH = "jisx0208.json"

    def __init__(self, escape_char=b'=', escape_ascii_chars=[], escape_multibyte_chars=[], newline=None, ascii_only=False):
        self.escape_char = escape_char
        self.escape_byte = escape_char[0]
        self.ascii_only = ascii_only

        if isinstance(newline, int) and newline > 0:
            self.newline = newline
        else:
            self.newline = None

        self.create_table(escape_ascii_chars)
        if not ascii_only:
            self.create_multibyte_table(escape_multibyte_chars)


    def create_table(self, escape_target_src):
        escape_target = []
        for t in escape_target_src:
            if isinstance(t, str):
                escape_target.append(t.encode("ascii")[0])
            elif isinstance(t, bytes):
                escape_target.append(t[0])
            else:
                escape_target.append(t)
        escape_target.append(self.escape_byte)

        byte_to_encoded = [b""]*256
        for i in range(256):
            if i in escape_target:
                first = int(i / 16)
                second = i % 16
                val = self.escape_char + self.TO_HEXBYTES[first] + self.TO_HEXBYTES[second]
            else:
                val = self.BYTE_TO_BYTES[i]
            byte_to_encoded[i] = val

        self._tbl = byte_to_encoded

    def create_multibyte_table(self, escape_target_src):
        with open(self.JISX_JSON_PATH, "r") as f:
            jisx_list = json.load(f)

        self._mtbl = {}
        for char in jisx_list:
            if char["char"] in escape_target_src:
                continue

            if char["utf8length"] == 3:
                idx = char["utf8bytes"][0] * 65536 + char["utf8bytes"][1] * 256 + char["utf8bytes"][2]
            elif char["utf8length"] == 2:
                idx = char["utf8bytes"][0] * 65536 + char["utf8bytes"][1] * 256
            else:
                raise Exception

            self._mtbl[idx] = b"".join([self.BYTE_TO_BYTES[b] for b in char["utf8bytes"]])


    def encode(self, bytes_data):
        if self.ascii_only:
            return self._encode_onebyte(bytes_data)
        else:
            return self._encode_multibyte(bytes_data)

    def _encode_onebyte(self, bytes_data):
        result = []
        line_length = 0
        for b in bytes_data:
            result.append(self._tbl[b])
            if self.newline:
                line_length += 1
                if line_length >= self.newline:
                    result.append(b"\n")
                    line_length = 0

        if len(result) > 0 and result[-1] == b"\n":
            result = result[:-1]

        return b"".join(result)

    def _encode_multibyte(self, bytes_data):
        result = []
        line_length = 0

        len_bytes = len(bytes_data)
        i = 0
        while(i < len_bytes):
            b = bytes_data[i]
            success = False
            if (b >= self.UTF8_3BYTE_MIN) and (b <= self.UTF8_3BYTE_MAX) and (i + 2 < len_bytes):
                idx = b * 65536 + bytes_data[i+1] * 256 + bytes_data[i+2]
                val = self._mtbl.get(idx)
                if val is not None:
                    success = True
                    result.append(val)
                    i += 3
            if (not success) and (b >= self.UTF8_2BYTE_MIN) and (b <= self.UTF8_2BYTE_MAX) and (i + 1 < len_bytes):
                idx = b * 65536 + bytes_data[i+1] * 256
                val = self._mtbl.get(idx)
                if val is not None:
                    success = True
                    result.append(val)
                    i += 2
            if (not success):
                result.append(self._tbl[b])
                i += 1

            if self.newline:
                line_length += 1
                if line_length >= self.newline:
                    result.append(b"\n")
                    line_length = 0

        if len(result) > 0 and result[-1] == b"\n":
            result = result[:-1]

        return b"".join(result)

class JISQuopriDecode(object):
    CONTROLS = list(range(0x00, 0x20)) + [0x7f]
    BYTE_TO_BYTES = [i.to_bytes(1, "little") for i in range(256)]
    FROM_HEXBYTES = [None]*48 + [0,1,2,3,4,5,6,7,8,9] + [None]*7 + [10,11,12,13,14,15]

    def __init__(self, escape_char=b'='):
        self.escape_char = escape_char
        self.escape_byte = escape_char[0]

        self.is_control_tbl = [(i in self.CONTROLS) for i in range(256)]
        self.decode_state = 0

    def decode(self, bytes_data):
        result = []
        for b in bytes_data:
            if self.is_control_tbl[b]:
                continue
            elif self.decode_state == 1:
                self.decode_first_4bit = self.FROM_HEXBYTES[b] * 16
                self.decode_state = 2
            elif self.decode_state == 2:
                result.append(
                    self.BYTE_TO_BYTES[self.decode_first_4bit + self.FROM_HEXBYTES[b]]
                )
                self.decode_state = 0
            elif b == self.escape_byte:
                self.decode_state = 1
            else:
                result.append(self.BYTE_TO_BYTES[b])
        return b"".join(result)

jisquopri_encode.py

import sys
import argparse
from JISQuopri import JISQuopriEncode

if __name__ == "__main__":
    BUFSIZE = 1024

    parser = argparse.ArgumentParser()
    parser.add_argument('--ascii_only', '-a', dest='ascii_only', action='store_true')
    parser.add_argument('--newline', '-n', type=int, default=None, dest='newline')
    parser.set_defaults(ascii_only=False)

    args = parser.parse_args()

    if args.ascii_only:
        output_encoding = "ascii"
        escape_ascii_chars = JISQuopriEncode.ASCII_NOT_PRINTABLE + ['\\', '"', "'", ",", " "] # 一部のascii記号を追加でエスケープする
        escape_multibyte_chars = []
    else:
        output_encoding = "utf-8"
        escape_ascii_chars = JISQuopriEncode.ASCII_NOT_PRINTABLE + ['\\', '"', "'", ",", " "]
        escape_multibyte_chars = [" "] # 全角スペースはエスケープする
    jqe = JISQuopriEncode(escape_ascii_chars=escape_ascii_chars,
                          escape_multibyte_chars=escape_multibyte_chars,
                          newline=args.newline,
                          ascii_only=args.ascii_only)

    while True:
        dat = sys.stdin.buffer.read(BUFSIZE)
        if args.newline:
            sys.stdout.write(jqe.encode(dat).decode(output_encoding) + "\n")
        else:
            sys.stdout.write(jqe.encode(dat).decode(output_encoding))
        if len(dat) < BUFSIZE:
            break

    if not args.newline:
        print("")

jisquopri_decode.py

import sys
from JISQuopri import JISQuopriDecode

if __name__ == "__main__":
    BUFSIZE = 1024

    jqd = JISQuopriDecode()
    while True:
        dat = sys.stdin.buffer.read(BUFSIZE)
        sys.stdout.buffer.write(jqd.decode(dat))
        if len(dat) < BUFSIZE:
            break

create_jisx_json.py

import json
jislist = []
with open("JIS0208.TXT", "r") as f:
    for line in f.readlines():
        line = line.rstrip()
        if line[0] == "#":
            continue
        dat = line.split("\t")
        unicode_id = eval(dat[2])
        uchar = unicode_id.to_bytes(2,"little").decode("utf-16-le")
        utf8_bytes = [_byte for _byte in uchar.encode("utf-8")]
        utf8_length = len(utf8_bytes)
        if uchar == "\\":
            continue
        jislist.append({"char": uchar, "utf8bytes": utf8_bytes, "utf8length": utf8_length})


print(json.dumps(jislist, ensure_ascii=False, indent=2))

最近なろうで読んで面白かったもの

私、能力は平均値でって言ったよね!

http://ncode.syosetu.com/n6475db/
ハンター(冒険者のようなもの)の少女4人によるコメディ。全体的にノリがコントっぽい。
メイン4人は客観的には不幸な境遇なのだが、その不幸に押しつぶされることなく力業でねじ伏せていくし、冒険の中で問題が起きても楽しいイベントに無理矢理変換して解決していく。メンタルの強さが感じられる。


その無限の先へ

http://ncode.syosetu.com/n6811ck/
冒険者達が終わりの見えないダンジョン「無限回廊」に挑むコメディ。
出落ちレベルで個性の強いキャラが大量に登場する。特に変態とパンダが多い。その個性に沿った行動をさせるだけでネタが尽きない。
変態が変態のまま熱血展開をやることもあり、笑っていいのか燃えていいのかわからなくなる。
舞台が中世風、現代日本風、ダンジョン、SF、実質なんでもありな世界観でごった煮感が強烈。


転生少女の履歴書

http://ncode.syosetu.com/n4269cp/
魔法使いに非魔法使いが依存しきっている、というシンプルな世界観が軸となっていて、その世界観に対するキャラの向き合い方がそのままキャラ立ちになっている。
非魔法使いを家畜扱いする魔法使い、ブラック労働に従事する現場の魔法使い、魔法使いとの力の差に絶望する騎士、革命家などなど。
そこに非魔法使いでありながら現代日本の知識を持っている主人公が放り込まれる。「履歴書」のタイトルの通り、主人公がコロコロ立場を変えながらその場その場で人々と交流して人々の意識に影響を与えていく。


勇者召喚に巻き込まれたけど、異世界は平和でした

http://ncode.syosetu.com/n2273dh/
ひたすらに主人公がモテるインフレの激しいハーレム。世界トップクラスの力をもつ魔族に初対面で挨拶しただけで求婚されるレベル。
タイトル通り世界が平和で優しく、癒される。
作者によって先の展開を全てネタバレされるので注意(そこがいいのだが)。


令嬢はまったりをご所望。

http://ncode.syosetu.com/n1159db/
獣人や精霊や魔導師に囲まれる逆ハーレム。主人公も獣人達もかわいい。獣人をもふもふするシーンがなんかえろい。


プリンセス・アライヴ―異世界のお姫様が彼の料理で魔力を補充する―

http://ncode.syosetu.com/n9395dm/
銀髪で残念美少女な姫様と金髪ツインテの女子中学生声優に絶食系男子が迫られる(語弊あり)。
姫様が幸せそうにご飯を食べるのがとてもよい。


成人すると塩になる世界で生き残る話 (R18. 厳密にはなろうではない)

http://novel18.syosetu.com/n4991de/
バイオレンスでサバイバルでジュブナイルでエロ。ヒロインが盲目で黒髪ロングの美少女。
エンタメとして全く隙がない。