Code JP 2015 出し物班やりました(出題編) #codejp
前記事に引き続き、今度は(出題編)です。
これを利用して他の人にオリジナルのQRコード問題を出題しよう。 (きっと嫌がられます)
準備編
Pythonの qrcode
というライブラリを作って作成します。
画像作成のために、 pillow
も入れます。
$ mkdir -p /tmp/work/qrcode2 $ cd /tmp/work/qrcode2 $ virtualenv -p /usr/bin/python2.7 venv Running virtualenv with interpreter /usr/bin/python2.7 New python executable in venv/bin/python Installing setuptools, pip...done. $ . venv/bin/activate (venv)$ pip install -U pip (venv)$ pip install qrcode (venv)$ pip install pillow
q1の作成
リスト内容記法使うと大体3行で出来ます。 確認用にcryptを出力して確認してます。
#!/usr/bin/env python # coding: utf-8 import qrcode text = '[CodeJP2015]' def main(): crypt = ' '.join([str(ord(v)) for v in text]) qrcode.make(crypt).save('./01-01.png') if __name__ == '__main__': main()
q2の作成
上級へのベースとなった問題です。
文字数が均一になるように気をつけたり、あとあと zipfile
が使いやすいようにストリーム使ったりしました。
#!/usr/bin/env python # coding: utf-8 import qrcode import base64 import io text = 'Mt. Moiwa Ropeway,Sapporo Art Park,[Jozankei Hot Springs],Hoheikyo Dam,Marukoma Hot Spring Hotel' def main(): qr_count = 16 crypt = text.encode('utf-8') for i in range(4): crypt = base64.b64encode(crypt) crypt_len = len(crypt) parts_lens = [crypt_len / qr_count for i in range(qr_count)] surplus = crypt_len % qr_count if surplus != 0: for i in range(surplus): parts_lens[i] += 1 n = 0 for i, m in enumerate(parts_lens): part_text = crypt[n:n+m] qr = qrcode.QRCode() qr.add_data(part_text) qr.make(fit=True) img = qr.make_image() output_bytes = io.BytesIO() img.save(output_bytes, 'PNG') output_bytes.seek(0) with io.open('imgs/02-{0:0>2}.png'.format(i), 'wb') as f: f.write(output_bytes.read()) n = n + m if __name__ == '__main__': main()
q3の作成
「Drewのいつもの3語使った答えにしよう」と決まったのが開催1週間ぐらい前。
「よし、つくるぞ!」ってなったのが、イベント前夜。
悪い意味で「よく、間に合ったもんだ」と思いました。 ただ、やりたいことは決まってて、
でした。更に手作業を減らすために極力プログラム側で実装する!のを目標に作成しました。
まぁ、寝不足で書いていたコードだったので、バグが多く難産でした。
本当は1024枚程度にしようと思ってたのに、QRコードの使用上1枚1万強の文字のみなので、泣く泣く4倍の枚数に。
(因みに当日慌ててたのは、ここの実装作業で、PCが落ちてたりしました)
また、dummyの方も解いて欲しかったので、なるだけ正答と比較して同じ量のbase64の長さにするのを心掛けました。
\某煽り用URLは深夜テンションで思いつきました/
#!/usr/bin/env python # coding: utf-8 import qrcode import base64 import os import io import zipfile text = '[Eat.Drink.Sleep.]' dummy = '[https://goo.gl/xGFW4m]' def get_count_base64_length(text, min_length): b64 = base64.b64encode(text) count = 1 while len(b64) < min_length: b64 = base64.b64encode(b64) count = count + 1 return (count, len(b64)) def fizzbuzz_cond(max_n): loop_fizzbuzz = [False, False, True, False, True, True, False, False, True, True, False, True, False, False, True] ret_fizzbuzz = [] n = max_n while n >= 0: ret_fizzbuzz.extend(loop_fizzbuzz[:]) n = n - len(loop_fizzbuzz) else: ret_fizzbuzz = ret_fizzbuzz[:n] return ret_fizzbuzz def qr_encode(text, b64count, qr_count): png_images = [] crypt = text for i in range(b64count): crypt = base64.b64encode(crypt) crypt_len = len(crypt) parts_lens = [crypt_len / qr_count for i in range(qr_count)] surplus = crypt_len % qr_count if surplus != 0: for i in range(surplus): parts_lens[i] += 1 n = 0 for i, m in enumerate(parts_lens): part_text = crypt[n:n+m] qr = qrcode.QRCode() qr.add_data(part_text) qr.make(fit=True) img = qr.make_image() output_bytes = io.BytesIO() img.save(output_bytes, 'PNG') output_bytes.seek(0) png_images.append(output_bytes.read()) n = n + m if (i + 1) % 100 == 0: print i + 1 return png_images def shuffle_binimg(imgbin_list1, imgbin_list2, cond_list): if len(cond_list) < len(imgbin_list1) + len(imgbin_list2): return None ret_list = [] n, m = 0, 0 for c in cond_list: v = None if c: v = imgbin_list1[n] n = n + 1 else: v = imgbin_list2[m] m = m + 1 ret_list.append(v) return ret_list def main(): zio = io.BytesIO() if not os.path.exists('./q3_demo.zip'): qr_count = 128 cond = fizzbuzz_cond(qr_count * 2) crypt_count, crypt_len = get_count_base64_length(text, qr_count * 4) images = qr_encode(text, crypt_count, cond.count(False)) dummy_count, dummy_len = get_count_base64_length(dummy, crypt_len) dummy_images = qr_encode(dummy, dummy_count, cond.count(True)) mix_png_images = shuffle_binimg(dummy_images, images, cond) zio.seek(0) with zipfile.ZipFile(zio, 'w') as _zipfile: io_text = io.StringIO() io_text.write('! fizzbuzz'.decode('utf-8')) _zipfile.writestr('hint.txt', io_text.getvalue().encode('utf-8')) for i, image in enumerate(mix_png_images): n = i + 1 _zipfile.writestr('qr/{0:0>4}.png'.format(n), image) zio.seek(0) with io.open('q3_demo.zip', 'wb') as f: f.write(zio.read()) zio.seek(0) else: with io.open('q3_demo.zip', 'rb') as f: zio.write(f.read()) zio.seek(0) zip_b64 = base64.b64encode(zio.read()) url_scheme_str = 'application/zip;base64,' + zip_b64 zip_images = qr_encode(url_scheme_str, 3, 4096) for i, image in enumerate(zip_images): with io.open('qr/{0:0>4}.png'.format(i + 1), 'wb') as f: f.write(image) if __name__ == '__main__': main()
まとめ
反省点としては、「QRコードのreaderライブラリの存在有無調べなかった」点が大きいです。
あのあと自分で他言語で読み込めるか試した所、
Rubyで3個中3個中級のQRコードがところどころ読めないのが出てきました。
C++は準備に手間がかかって断念…。
結果的に言語を限定しちゃったのかなぁと思います。
ライブラリ探しが明暗を分ける問題になったのが如何ともし難い。
もうちょいコードのみに集中出来るものを(次回があるなら)作りたいかなぁ。
(でも、別言語で解けた人がいるならブログ書いて共有して欲しいな!上級解いた人は特にちらっちらっ)
出し物も「夜の宿題」形式ではなく、「きんぎょばち別トラック」形式でもう少し時間をとれたほうが嬉しいのだろうか?
(その場合、今回の上級は作成間に合わなかったけど…)
言語自由でコードに集中できるお題を探しましょう。適度に。
と、ネガティブ方面ばっかだとあれなので良かった点。
出題全般でLevelは結構出し物班で吟味しました。
上級はほぼほぼうちの自由にやりましたが、初級、中級は話し合いで結構詰めたので良かったかな。
今回の事でかなり問題に関係ない所の知識がついた気がします。
某人が「勉強会駆動開発」とかいってましたが、うちは「お題駆動開発」じゃないかなぁと。
投げ銭もそうですし、今回のお題もそうですしなんやかんやで自分の届きそうで届かない部分を頑張るのが好きみたいです。
あとは、byte streamの汎用性の高さに気づけたのが良かった。
今後役に立ちそう。
そんなこんなの「出し物班」の出題編でした。