「われ敗れたり」を読んだ
故・米長永世棋聖の「われ敗れたり」を本屋で立ち読みしてて首が痛くなってきたので衝動買いしました. 電王戦でボンクラーズに敗れた話がまとめられています. 将棋の棚にあったけど情報系の読み物としても十分おもしろいなぁと思いました(確かに将棋のルール知らないと一部読めない所もあるけど).
あの対局の中で,策のポイントみたいなものが3つ紹介されていました. それについては本のネタバレなので明かせないですが, 計算機知能の本質的な弱点なのかなと思い,深いと思いました.
あとは企画の話とか勝負師的な面とかメディアについてとか. ここまでで文庫本4冊分ぐらいの価値があると思います. それに棋士と開発者のインタビューが載ってる.
技術書ばっかりで脳が凝り固まってしまった気分だったので買って良かった.
MIDIファイルを生成する
Python3は文字列とかバイト列とかの扱いがすっきりした感じがするので練習がてらStandardMIDIFileを出力させてみた.
:::python
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import copy
import heapq
class SMF(object):
'''Standard MIDI File
'''
def __init__(self, fmt=1, division=480):
self.division = division
self.format = fmt
self.tracks = []
def measuretick(self, division):
return divmod(abs_time, division * 4)
def abs_time(self, measure, tick):
return measure * 4 + tick
def __bytes__(self):
# header signature
result = bytearray(b"MThd")
# header size
result += (6).to_bytes(4, byteorder='big')
# format
result += self.format.to_bytes(2, byteorder='big')
# number of tracks
if self.format == 0:
result += (1).to_bytes(2, byteorder='big')
elif self.format == 1:
result += len(self.tracks).to_bytes(2, byteorder='big')
# time division
result += self.division.to_bytes(2, byteorder='big')
if self.format == 0:
sumtrack = Track()
for t in self.tracks:
sumtrack.events += t.events
result += bytes(sumtrack)
elif self.format == 1:
for t in self.tracks:
result += bytes(t)
else:
raise ValueError("format is invalid: %d".format(self.format))
return bytes(result)
class Track(object):
'''Track data
'''
def __init__(self):
self.events = []
def to_vbytes(self, dt):
'''convert to the variable length format.
'''
result = [dt % 0x80]
dt //= 0x80
while dt > 0:
result += [dt % 0x80 | 0x80]
dt //= 0x80
return bytes(result[::-1])
def _block_bytes(self):
result = bytearray()
eventheap = copy.deepcopy(self.events)
heapq.heapify(eventheap)
t = 0
runninng = None
while len(eventheap) > 0:
e = heapq.heappop(eventheap)
delta = e.abs_time - t
t = e.abs_time
result += self.to_vbytes(delta)
eventbytes = bytes(e)
if runninng == eventbytes[0] and runninng != b"\xF0" and runninng != b"\xFF":
result += eventbytes[1:]
else:
result += eventbytes
runninng = eventbytes[0]
if e.has_after():
heapq.heappush(eventheap, e.after())
# track end
result += b"\x00\xFF\x2F\x00"
return result
def __bytes__(self):
# track signature
result = bytearray(b"MTrk")
blocks = self._block_bytes()
# block size
result += len(blocks).to_bytes(4, byteorder="big")
result += blocks
return bytes(result)
class Event(object):
def __init__(self, t=0):
self.abs_time = t
def __lt__(self, o):
return self.abs_time < o.abs_time
def __le__(self, o):
return self.abs_time <= o.abs_time
def __gt__(self, o):
return self.abs_time > o.abs_time
def __ge__(self, o):
return self.abs_time >= o.abs_time
def has_after(self):
return hasattr(self, "after")
# SysEx Events
class GMSystemOn(Event):
def __bytes__(self):
return b"\xF0\x05\x7E\x7F\x09\x01\xF7"
# Meta Events
class Tempo(Event):
def __init__(self, tempo=120, t=0):
Event.__init__(self, t)
if tempo <= 0:
tempo = 120
self.microsec = 60 * 1000000 // tempo
def __bytes__(self):
return b"\xFF\x51\x03" + self.microsec.to_bytes(3, "big")
# MIDI Events
class ControllChange(Event):
def __init__(self, t, ch, d):
Event.__init__(self, t)
self.ch = ch
self.d = d
def __bytes__(self):
return bytes([0xB0 | self.ch, self.ccnum, self.d])
class BankSelectMSB(ControllChange):
ccnum = 0x00
class Modulation(ControllChange):
ccnum = 0x01
class BreathController(ControllChange):
ccnum = 0x02
class FootController(ControllChange):
ccnum = 0x04
class Portamento(ControllChange):
ccnum = 0x05
class DataEntryMSB(ControllChange):
ccnum = 0x06
class Volume(ControllChange):
ccnum = 0x07
class Balance(ControllChange):
ccnum = 0x08
class Pan(ControllChange):
ccnum = 0x0a
class Expression(ControllChange):
ccnum = 0x0b
class Hold(ControllChange):
ccnum = 0x40
class Portamento(ControllChange):
ccnum = 0x41
class Sostenuto(ControllChange):
ccnum = 0x42
class Soft(ControllChange):
ccnum = 0x43
class Resonance(ControllChange):
ccnum = 0x47
class Release(ControllChange):
ccnum = 0x48
class Atack(ControllChange):
ccnum = 0x49
class CutOff(ControllChange):
ccnum = 0x4a
class NoteOff(Event):
def __init__(self, t, ch, n):
Event.__init__(self, t)
self.ch = ch
self.n = n
def __bytes__(self):
# use 0x90 for runninng status
return bytes([0x90 | self.ch, self.n, 0x00])
class NoteOn(Event):
def __init__(self, t, ch, n, vel, gate):
Event.__init__(self, t)
self.ch = ch
self.n = n
self.vel = vel
self.gate = gate
def __bytes__(self):
return bytes([0x90 | self.ch, self.n, self.vel])
def after(self):
return NoteOff(self.abs_time + self.gate, self.ch, self.n)
class ProgramChange(Event):
def __init__(self, t, ch, p):
Event.__init__(self, t)
self.ch = ch
self.p = p
def __bytes__(self):
return bytes([0xC0 | self.ch, self.p])
import unittest
class SMFTest(unittest.TestCase):
def test_smf_bytes(self):
f = SMF()
result = bytes(f)
print(result)
self.assertIsInstance(result, bytes)
self.assertEqual(result[:4], b"MThd")
print(int.from_bytes(result[4:8], "big"))
class TrackTest(unittest.TestCase):
def test_to_vbytes(self):
t = Track()
self.assertEqual(t.to_vbytes(0x00000000), b"\x00")
self.assertEqual(t.to_vbytes(0x00000040), b"\x40")
self.assertEqual(t.to_vbytes(0x0000007F), b"\x7F")
self.assertEqual(t.to_vbytes(0x00000080), b"\x81\x00")
self.assertEqual(t.to_vbytes(0x00002000), b"\xC0\x00")
self.assertEqual(t.to_vbytes(0x00003FFF), b"\xFF\x7F")
self.assertEqual(t.to_vbytes(0x00004000), b"\x81\x80\x00")
self.assertEqual(t.to_vbytes(0x00100000), b"\xC0\x80\x00")
self.assertEqual(t.to_vbytes(0x001FFFFF), b"\xFF\xFF\x7F")
self.assertEqual(t.to_vbytes(0x00200000), b"\x81\x80\x80\x00")
self.assertEqual(t.to_vbytes(0x08000000), b"\xC0\x80\x80\x00")
self.assertEqual(t.to_vbytes(0x0FFFFFFF), b"\xFF\xFF\xFF\x7F")
if __name__ == '__main__':
# unittest.main()
smf = SMF(fmt=1)
smf.tracks.append(Track())
smf.tracks[0].events.append(GMSystemOn(0))
smf.tracks[0].events.append(Tempo(tempo=120, t=40))
smf.tracks.append(Track())
ch = 1
smf.tracks[1].events.append(Volume(40, ch, 96))
smf.tracks[1].events.append(ProgramChange(60, ch, 0x39))
smf.tracks[1].events.append(NoteOn(0 + 480 * 4, ch, 0x3C, 127, 480))
smf.tracks[1].events.append(NoteOn(0 + 480 * 4, ch, 0x44-12, 127, 480))
smf.tracks[1].events.append(NoteOn(480 + 480 * 4, ch, 0x3E, 127, 480))
smf.tracks[1].events.append(NoteOn(480 + 480 * 4, ch, 0x45-12, 127, 480))
print("write to test.mid...")
with open("test.mid", "wb") as f:
f.write(bytes(smf))
print("OK!")
参考
Tkinterでウィンドウの最大化
Windowsでは
:::python
from Tkinter import *
root = Tk()
root.state('zoomed')
LinuxとかMacだと
:::python
from Tkinter import *
root = Tk()
root.attributes("-zoomed", "1")
middlewareで簡単アクセス解析
Djangoのmiddlewareって意外と単純なんですね.
process_reques から request データベースに放り込むようにすると簡単なアクセス解析アプリが作れる. 見る方は templates/admin/[アプリ名]/change_list.html あたりに.
今まで ssh で入ってログ眺めたりスクリプト走らせてたりしてたけど, アプリと連携できるようになるので便利.
python-whois と組み合わせたら良いかも.
django-skelを使うようにした
リファクタリングになったし表示速度にも効いてる気がする.
Djangoが分かってきたけど綺麗に作るには……?という人には非常に良いと思う.
settings/prod.py あたりをちょっと変えたら VPS な人も大丈夫です. 潔癖な方は reqs/ あたりも.
特に apps ディレクトリのアイディア見た時はなるほどと思った.
あと, settings.py に DEBUG = socket.gethostname() == '開発環境のホスト名' みたいなことしてたけど, DJANGO_SETTINGS_MODULE を使うのが正しかったのか.
あと, CACHE とか compress とか. こんなに手軽に高速化できるとは.
Djangoで更新する度に日付が変わるバグに遭遇した
DjangoでDateTimeFieldを使っていたら, なぜか管理画面で日付が更新ボタンを押す度に変わる. それも±9:00で.
modelのメソッドでバグ出しちゃったかなーと思ったけどそうでもなさそう.
結局settings.pyで TIME_ZONE = 'Asia/Tokyo' としてたせいでした. 'Japan'に変えたら治った.
なんで再現性のないバグになっちゃうんだろう…
腐ったmakeを支える技術
まさか自分と同い年のMakefileと戦うことになるとは。
— Tsushimaさん (@utisam) 12月 26, 2012
あんまり身に付けたくない力だけど付いてしまったものは仕方がない. これ以上C/C++のビルドごときで時間を取られたくないので文章化したい(パズルみたいでちょっと楽しいけど).
なぜビルドが通らないのか
- 依存するライブラリが無い
- 古い文法や関数が使われている
- コマンドのバージョンが変わっている
- 腐ってやがる
1. 依存するライブラリが無い
configureでエラー
ラッキーです. ディストリのリポジトリ,もしくは本家からダウンロードしましょう.
リンカエラー
関数名で調べてみましょう. 何か見つかるかもしれません.
ネット上に無い
あきらめてそいつをbuildしない方法を考えましょう.
2. 古い文法や関数が使われている
gcc3/4の違いでいろいろあるっぽい.
プロトタイプ宣言が関数内部に書かれている
3では通りますが4だと構造体と勘違いした感じのエラーを吐きます. グローバル領域に動かしましょう. staticをつけると安全です.
古いスタイルの宣言
VC++だとエラーになるらしいです. また,gcc-3.xは引数リストが一致しなくてもプロトタイプ宣言として有効らしいので, 4.xでコンパイルすると名前被りでエラーになります. 昔適当に書いていた野郎を恨みましょう.
varargs.h
冒頭のツイートの時に最も困ったのがこれ. 現在は stdarg.h があって varargs.h をincludeするとご丁寧にエラーにしてくれる.
解決策としては
- 昔のコンパイラをインストール
- varargs.h を自作する(探したらあるかも)
- ソースを修正する
3. コマンドのバージョンが変わっている
makeやスクリプトの内部で起動するコマンドのパスやバージョンが変わって,うまく動かない場合があります. gccのアーキテクチャの指定なんかは gcc -march=native で良いと思うので, エラーを吐いてるなら置き換えましょう.
bison とか flex っぽい拡張子でエラー吐くとかなり苦しい.
4. 腐ってやがる
f**k!!
エラーメッセージを注意深く読むしかありません. 何か分かるかも.
実行時エラー
- デバッガを使う
- strcpy -> strncpy など
- const をつける
- 最適化オプションを外す
逃げる
あなたの目的はビルドを通すことではなく問題を解決することのはずです. 他の手段を探した方が効率が良いかもしれません.
将来迷惑をかけないために
- configure をちゃんと書く
- READMEを作る
- 作った年ぐらいは書いておく
- 標準的な書き方に従う
- const をつける
- 保守する
「昔の環境で使っている人ガー……」ってのはあまり良い言い訳ではないと思う. 趣味とかで作ったものを保守するのってめんどくさいけどね.