さすがにいろんな人から怒られることが多くなってきたのでいいかげん comjong.comの更新をしようかと。
前に書いたソースを見たら、手牌の先読みルーチンを書いてて、あまりの動作の遅さに投げ出したところで止まってました。なので、高速化するべくキャッシュ化をしてみたのでその覚え書き。断片コードだけなんだけど、雰囲気ね。
手牌を解析するときに、
def combination( t )
max = {}; max.default = 0
s = separate(t)
return max if s == []
if s.size > 1
a = []
s.each{|i| a << combination(i)}
return merge_combination(a)
else
b = s.first
end
b.shift while b.first == 0
b.pop while b.last == 0
return @@cache[b].dup if @@cache[b] != nil
c = {}; c.default = 0
for i in 0...b.size
# sequence
if b[i+2] != nil and b[i] >= 1 and b[i+1] >= 1 and b[i+2] >= 1
t = b.dup; t[i] -= 1; t[i+1] -= 1; t[i+2] -= 1
c = combination(t)
c["sets"] += 1;
c["headed_sets"] = if c["headed_pairs"] == 0 then 0 else c["headed_sets"] + 1 end
max = max_combination( c, max )
end
# triplet
if b[i] >= 3
t = b.dup; t[i] -= 3
c = combination(t)
c["sets"] += 1;
c["headed_sets"] = if c["headed_pairs"] == 0 then 0 else c["headed_sets"] + 1 end
max = max_combination( c, max )
end
....
_ な感じにして、今までの結果を最大限に生かしつつ、そこからの差分だけ読むようにしてやればいい感じかも。ちなみに [3, 1, 1, 1, 0, 1] を読ませたときのキャッシュの中身はこんな感じになります (Hash の default は 0 ね) 。
{[3, 1, 1]=>{"headed_pairs"=>1, "sets"=>1, "pairs"=>1, "headed_sets"=>1},
[2, 1]=>{"headed_pairs"=>1, "pairs"=>1},
[1, 1, 1, 0, 1]=>{"headed_pairs"=>0, "sets"=>1, "pairs"=>0},
[1]=>{},
[1, 1, 1, 1, 0, 1]=>{"headed_pairs"=>0, "sets"=>1, "pairs"=>1},
[2, 1, 0, 1, 0, 1]=>{"headed_pairs"=>2, "pairs"=>2},
[2, 0, 1]=>{"headed_pairs"=>1, "pairs"=>1},
[1, 1]=>{"headed_pairs"=>0, "pairs"=>1},
[3, 1]=>{"headed_pairs"=>2, "sets"=>1, "pairs"=>0, "headed_sets"=>0},
[3, 0, 1]=>{"headed_pairs"=>2, "sets"=>1, "pairs"=>0, "headed_sets"=>0},
[3]=>{"headed_pairs"=>1, "sets"=>1, "pairs"=>0},
[3, 1, 1, 1, 0, 1]=>{"headed_pairs"=>2, "sets"=>2, "pairs"=>0, "headed_sets"=>1},
[1, 0, 1, 0, 1]=>{"headed_pairs"=>0, "pairs"=>1},
[1, 1, 1]=>{"headed_pairs"=>0, "sets"=>1, "pairs"=>0},
[2, 0, 1, 1, 0, 1]=>{"headed_pairs"=>2, "pairs"=>2},
[2]=>{"headed_pairs"=>1, "pairs"=>1},
[1, 1, 0, 1]=>{"headed_pairs"=>0, "pairs"=>1},
[1, 0, 1]=>{"headed_pairs"=>0, "pairs"=>1}}


仕事が煮詰まってくると、急に掃除がしたくなってきますよね。よね? そう、期末テスト前になると急に部屋の掃除を始めてしまうアレです。というわけで、なぜか急にキーボードの汚れが気になってきたので、お掃除です。よく考えたらこのキーボード (fukumotoは Happy Hacking Keyboard Liteを愛用) 、買ってから数年間掃除してません。
キーボードのお手入れは簡単にするのならば、洗剤を少々含ませた布を堅く絞って拭くだけで大丈夫です。ですが、今回は気分がのっているので本格的にやってみましょう。キートップを外して丸洗いです・・・というところで、キートップリムーバがないのに気がつきました。たしかもらい物があったと思ったんだけどなぁ・・・。
まあ、キートップリムーバというものはそんなに複雑なものではないので、ちょっと手作りしてみました。写真左のゼムクリップをラジオペンチで右のように曲げると、なんと、どこにでもあるゼムクリップがキートップリムーバに!! 使い方は写真のような感じで、外したいキーを挟み込むようにして引っこ抜きます。



外したキートップは洗濯ネットに入れて洗います。洗面器に薄い洗濯液を張って数分間もみ洗いしてからすすいで、タオルでていねいに水分をふき取ります。
土台の方はさすがに水洗いできるものではないので、綿棒を駆使してていねいに汚れを落としていきます。
さ、これでキーボードも新品同様の姿を取り戻しました。ここまでの所要時間は1時間半。そろそろいい時間です。お仕事は明日ということでそろそろ寝ますか (だから仕事が遅いのー) 。
純粋に読み物としてもおもしろいですが、ソーシャルハッキング本として読んでもおもしろい……かも? ところで、岩波現代文庫って、文庫のくせになんでこんなに高いんでしょう? (この本は定価1,000円) 。
長期投資でもリスクは減らない
という部分は一読の価値有り。それ以外は、類書でよく言われていることが多いけど、よくまとまっているので手元に置きたい本です。

ついに禁断の世界へ足を踏み入れてしまいました。古書です。
一度ハマると抜け出せなくなるおそれがあるのであえて避けていたのですが、うっかり購入してしまいました。このまま浅見先生の様に突っ走ってしまったらどうしよう……。
買ってしまったものは仕方がないのでレビューです。今回購入したのは松雲堂出版の麻雀競技解説という本。著者は東明天 。この本は昭和5年9月5日発行の、いわゆる戦前本です。奥付をみるとまだ著者検印があったり、旧仮名遣いだったりと、時代を感じさせてくれます。内容はルールと、基本的な定石の解説です。
ルール部分はいわゆる清麻雀ベースのものです。ただし、立直、三色同順といった現在の主力ともいえる手役はまだ存在しません (ダブル立直はありますが) 。一気通貫、断幺ですら "現今使用して居るところは殆どありません" といった時代です。
定石部分をみると、この時代に既に現在の基本的な理論が紹介されていることに驚かされます。いわゆるスジ理論、生牌の絞り、ソバ聴理論、裏スジ理論、理牌の注意、槓の注意 (槓ドラなどないにもかかわらず、です) など、現在でも変わらぬ基礎理論はこの時代に既に完成されていたのです。逆に言うと、この時代から現在まで戦術に大きな進歩がないという事実が見えてきて、少し悲しさを覚えます。
しかし、この本を読んでると、戦中・戦後を通じての変遷が知りたくなってきます。集めるか……?
今さらなんですが、最近 eXtreme Programing (XP) をお勉強してます。そんなわけで、テストファーストプログラミングを導入してみました。
fukumotoは ruby 使いの端くれなので、もちろん ruby でテストファーストです。ruby の世界では、RubyUnitあたりが有名どころっぽいので、インストールして数週間使ってみました。
結論から言うと、非常に良いです。他への波及を気にせずにがしがし変更できるのは精神衛生上非常によろしい。それに、自分で書いてからちょっと間を置いたライブラリ (もちろんドキュメントなし) を使わねばならないとき、テストをみると実使用例が書いてあるというのは思いの外便利です。もう、これなしでは、大きめの部品づくりはできないかも。
使用方法なんですが、RubyUnitのページをみると一見面倒そうに見えますが、実際には雛形をコピーしてきて、assert_equal() と assert_exception() だけ知ってればとりあえず使えます。fukumotoもこの 2 つくらいしか使ってません。
ちなみに、comjong.com用の手牌ライブラリのテストはこんな感じ。実際には、もう少したくさんのテストパターンがあるし、例外のテストもあるけど省略。
#!/usr/local/bin/ruby
require 'runit/testcase'
require 'runit/cui/testrunner'
require 'Hand'
class TestHand < RUNIT::TestCase
def initialize(*args)
super
end
def setup
end
def test_hand
h = Hand.new "b2 b2 b2 b4 b4 b5 b5 b5 b6 b6 b6 b8 ww"
assert_equal(3, h.combination["sets"])
assert_equal(1, h.combination["pairs"])
assert_equal(3, h.combination["headed_sets"])
assert_equal(1, h.combination["headed_pairs"])
assert_equal(1, h.to_ready)
h = Hand.new "b2 b4 b5 b5 b6 b8 c4 c5 c6 d4 d5 d6 ww"
assert_equal(3, h.combination["sets"])
assert_equal(0, h.combination["pairs"])
assert_equal(2, h.combination["headed_sets"])
assert_equal(3, h.combination["headed_pairs"])
assert_equal(1, h.to_ready)
h = Hand.new "c1 c1 c1 c2 c3 c4 c5 c6 c7 c8 c9 c9 c9"
assert_equal(4, h.combination["sets"])
assert_equal(0, h.combination["pairs"])
assert_equal(3, h.combination["headed_sets"])
assert_equal(2, h.combination["headed_pairs"])
assert_equal(0, h.to_ready)
end
def teardown
end
end
RUNIT::CUI::TestRunner.run(TestHand.suite)
![]()
職場で健康診断を受けました。
その際に "生活習慣問診票" というのを書かされたのですが、これがわかりにくい。ダメなアンケートによくある、あいまいな質問が多い。たとえばこれ。
下の質問ってどういう意味なんでしょう? 最初の "1日" というのは "ほぼ毎日" と解釈するべき? "早足で休まず" というのは "通勤で歩くのは含まない" の意味? 歩き以外のスポーツは含まない?もしかして "歩きますか" は "歩けますか" の typo? うーん、そもそも何が聞きたいんだろう? 謎だ。
でも、自分も文章を書くときにうっかりこういう謎なものを書いたりしてるのかも。文章に落とす時に自分では自明だと思っているけど他人にはそうではないことをはぶいてしまったり。気をつけなきゃ。
ToDo/ToBuy