sugi memo
2004-04-09
◆ zsh 補完関数の書き方
<URL:http://www.ayu.ics.keio.ac.jp/~mukai/translate/write_zsh_functions.html>
zsh の独自補完の書き方は、日本語の情報がほとんどないのが現状なのでうれしい。
2004-04-15
◆ プロのコード、アマのコード
<URL:http://arton.no-ip.info/diary/20040415.html#p03> で見かけたので、 Test::Unit の練習も兼ねて Ruby で書いてみました。
以下、過程を全部書いてるので長いです。
テストファーストということで、まずはテストから書く。
# jankentest.rb
require 'test/unit'
require 'janken'
class TC_Janken < Test::Unit::TestCase
def test_win
p = Player.new
p.win
assert_equal("プレイヤーの勝敗数:1勝0敗0引き分け", p.results)
end
end
結果。
% ruby jankentest.rb
Loaded suite jankentest
Started
E
Finished in 0.001 seconds.
1) Error:
test_win(TC_Janken):
NameError: uninitialized constant TC_Janken::Player
jankentest.rb:6:in `test_win'
1 tests, 0 assertions, 0 failures, 1 errors
Player が全く実装されてないんだから当たり前。
# janken.rb
class Player
def win
end
def results
"プレイヤーの勝敗数:1勝0敗0引き分け"
end
end
結果をいきなり埋め込んでるけどとりあえずこれで通る。
class Player
def initialize
@n_win = 0
end
def win
@n_win += 1
end
def results
"プレイヤーの勝敗数:#{@n_win}勝0敗0引き分け"
end
end
ちゃんとカウントするようにする。
# TC_Janken
def test_lose
p = Player.new
p.lose
assert_equal("プレイヤーの勝敗数:0勝1敗0引き分け", p.results)
end
負けも同様のテスト。
class Player
def initialize
@n_win = 0
@n_lose = 0
end
def win
@n_win += 1
end
def lose
@n_lose += 1
end
def results
"プレイヤーの勝敗数:#{@n_win}勝#{@n_lose}敗0引き分け"
end
end
同様に実装。
def test_draw
p = Player.new
p.draw
assert_equal("プレイヤーの勝敗数:0勝0敗1引き分け", p.results)
end
引き分けのテスト。(実装略)
ここで、次に Player#hand で手を出させたいところだが、 このテストってどう書けばいいのだろうか……
よく分からないので、とりあえず
def test_hand p = Player.new p.hand end
とだけ書いて、実装されていることのテストだけしておく。
def hand puts '手を選んでください(0:グー、1:チョキ、2:パー)' s = gets s.to_i # todo: エラー処理 end
実装はこう。
次に試合の部分。
def test_game
p0 = Player.new
p1 = Player.new
p0.hand = 0
p1.hand = 0
g = Game.new
g.play(p0, p1)
assert_equal("プレイヤーの勝敗数:0勝0敗1引き分け", p0.results)
assert_equal("プレイヤーの勝敗数:0勝0敗1引き分け", p1.results)
end
どの手を出すかを分かっていないとテストが書けないので、 Player#hand= も作る。
# class Player
def hand
if @hand
h = @hand
@hand = nil
return h
end
puts '手を選んでください(0:グー、1:チョキ、2:パー)'
s = gets
s.to_i
# todo: エラー処理
end
def hand=(h)
@hand = h
end
Game の実装。
class Game def play(*players) end end
試合判定の部分がまだ。
class Game
JudgeTable = [
[0, 1, -1],
[-1, 0, 1],
[1, -1, 0],
]
def play(*players)
# assert players.size == 2
p0 = players[0]
p1 = players[1]
r = JudgeTable[p0.hand][p1.hand]
case r
when 0
p0.draw; p1.draw
when 1
p0.win; p1.lose
when -1
p0.lose; p1.win
end
end
end
YAGNI 的に(?)、2人ゲームの場合のみを実装する。
これで、一応テストは全部通ったが、引き分けの場合しかテストがまだないので 勝ちと負けのテストも書く。
def test_game_win
p0 = Player.new
p1 = Player.new
p0.hand = 0
p1.hand = 1
g = Game.new
g.play(p0, p1)
assert_equal("プレイヤーの勝敗数:1勝0敗0引き分け", p0.results)
assert_equal("プレイヤーの勝敗数:0勝1敗0引き分け", p1.results)
end
これも OK。
さて、じゃんけんのたびに「プレイヤーの勝ちです」などのように結果を表示しないといけない。
def test_game_draw
p0 = Player.new('プレイヤー0')
p1 = Player.new('プレイヤー1')
p0.hand = 0
p1.hand = 0
g = Game.new
result = g.play(p0, p1)
assert_equal(' プレイヤー0:グー プレイヤー1:グー …… あいこです', result)
assert_equal("プレイヤー0の勝敗数:0勝0敗1引き分け", p0.results)
assert_equal("プレイヤー1の勝敗数:0勝0敗1引き分け", p1.results)
end
def test_game_win
p0 = Player.new('プレイヤー0')
p1 = Player.new('プレイヤー1')
p0.hand = 0
p1.hand = 1
g = Game.new
result = g.play(p0, p1)
assert_equal(' プレイヤー0:グー プレイヤー1:チョキ …… プレイヤー0の勝ちです', result)
assert_equal("プレイヤー0の勝敗数:1勝0敗0引き分け", p0.results)
assert_equal("プレイヤー1の勝敗数:0勝1敗0引き分け", p1.results)
end
Player のコンストラクタの引数の数が合わない。
# class Player def initialize(name = 'プレイヤー') @name = name @n_win = 0 @n_lose = 0 @n_draw = 0 @hand = nil end
result の中にも名前を入れる。
# class Player
def results
"#{@name}の勝敗数:#{@n_win}勝#{@n_lose}敗#{@n_draw}引き分け"
end
この段階で、Game#play の戻り値がおかしいことがテストで分かるので修正。
def play(*players)
# assert players.size == 2
p0 = players[0]
p1 = players[1]
p0_hand = p0.hand
p1_hand = p1.hand
r = JudgeTable[p0_hand][p1_hand]
case r
when 0
p0.draw; p1.draw
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… あいこです"
when 1
p0.win; p1.lose
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… #{p0.name}の勝ちです"
when -1
p0.lose; p1.win
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… #{p1.name}の勝ちです"
end
end
まだ通らない。p0_hand はただの数値なので、グーとかいう文字にならない。 ここは、グー・チョキ・パーをクラスにしてしまうのがいいだろう。
テストをこう書き換えて
def test_game_draw
p0 = Player.new('プレイヤー0')
p1 = Player.new('プレイヤー1')
p0.hand = Hand::Guu
p1.hand = Hand::Guu
g = Game.new
result = g.play(p0, p1)
assert_equal(' プレイヤー0:グー プレイヤー1:グー …… あいこです', result)
assert_equal("プレイヤー0の勝敗数:0勝0敗1引き分け", p0.results)
assert_equal("プレイヤー1の勝敗数:0勝0敗1引き分け", p1.results)
end
手のクラスを作って
module Hand
class TGuu
def to_i; 0 end
def to_s; 'グー' end
end
class TChoki
def to_i; 1 end
def to_s; 'チョキ' end
end
class TPaa
def to_i; 2 end
def to_s; 'パー' end
end
Guu = TGuu.new
Choki = TChoki.new
Paa = TPaa.new
end
Game#play を書き換える
r = JudgeTable[p0_hand.to_i][p1_hand.to_i]
これで、テストが全部通る。
ここで、JudgeTable は Hand モジュールの中に移した方がいいように見えるので移動。 テストが通ることを確認。
次は、コンピュータプレイヤーを作る。
これまでの Player を GenericPlayer に名前を変え、 Player は GenericPlayer から継承させる。 hand と hand= の実装を GenericPlayer から Player に移動させ、 テストが通ることを確認。
と、ここで ComPlayer#hand を実装するわけだが、 その前に Player#hand が手のオブジェクトではなく数値を返していたので修正する。
Hand::create(n) を作り、0、1、2 に応じたオブジェクトを返す。
# module Hand
def create(n)
case n
when 0
Guu
when 1
Choki
when 2
Paa
end
end
module_function :create
ComPlayer の実装。
class ComPlayer < GenericPlayer
def initialize(name = 'コンピュータ')
super
end
def hand
Hand::create(rand(3))
end
end
最後に、5回プレイする部分を記述。
if $0 == __FILE__
p = Player.new
com = ComPlayer.new
g = Game.new
5.times do |i|
puts g.play(p, com)
end
puts p.results
end
これで終了。
◆ 最終結果
class GenericPlayer
attr_reader :name
def initialize(name = 'プレイヤー')
@name = name
@n_win = 0
@n_lose = 0
@n_draw = 0
end
def win
@n_win += 1
end
def lose
@n_lose += 1
end
def draw
@n_draw += 1
end
def results
"#{@name}の勝敗数:#{@n_win}勝#{@n_lose}敗#{@n_draw}引き分け"
end
end
class Player < GenericPlayer
def initialize(name = 'プレイヤー')
super
@hand = nil
end
def hand
if @hand
h = @hand
@hand = nil
return h
end
puts '手を選んでください(0:グー、1:チョキ、2:パー)'
s = gets
Hand::create(s.to_i)
# todo: エラー処理
end
def hand=(h)
@hand = h
end
end
class ComPlayer < GenericPlayer
def initialize(name = 'コンピュータ')
super
end
def hand
Hand::create(rand(3))
end
end
module Hand
JudgeTable = [
[0, 1, -1],
[-1, 0, 1],
[1, -1, 0],
]
class TGuu
def to_i; 0 end
def to_s; 'グー' end
end
class TChoki
def to_i; 1 end
def to_s; 'チョキ' end
end
class TPaa
def to_i; 2 end
def to_s; 'パー' end
end
Guu = TGuu.new
Choki = TChoki.new
Paa = TPaa.new
def create(n)
case n
when 0
Guu
when 1
Choki
when 2
Paa
end
end
module_function :create
end
class Game
def play(*players)
# assert players.size == 2
p0 = players[0]
p1 = players[1]
p0_hand = p0.hand
p1_hand = p1.hand
r = Hand::JudgeTable[p0_hand.to_i][p1_hand.to_i]
case r
when 0
p0.draw; p1.draw
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… あいこです"
when 1
p0.win; p1.lose
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… #{p0.name}の勝ちです"
when -1
p0.lose; p1.win
" #{p0.name}:#{p0_hand} #{p1.name}:#{p1_hand} …… #{p1.name}の勝ちです"
end
end
end
if $0 == __FILE__
p = Player.new
com = ComPlayer.new
g = Game.new
5.times do |i|
puts g.play(p, com)
end
puts p.results
end
うーん、何かまだまだ粗が目立ちますね……
◆ 感想
一番気になるのは、0、1、2 とグー・チョキ・パーの対応が、 JudgeTable、(TGuu|TChoki|TPaa)#to_i、Hand::create() の 3箇所にちらばっているところ。
じゃんけんは手が3種類だからこれでもまだいいけど、 もっと複雑なルールのゲームだとこれではまずいだろう。
何かいい方法あるかな……
_ ma383zda [c269t mp3 player tips2008 - http://Mp3Player2008.zoomshare..]
_ inkjet printer tips2008 [c1t internet web hosting tips2008 - http://InternetWebHost..]
_ goodyear tire tips2008 [c498t franklin mint tips2008 - http://FranklinMint2008.zoo..]
_ foreclosure property tips2008 [c42t refinancing loan tips2008 - http://RefinancingLoan200..]
_ ma934zda [c862t colorado refinance tips2008 - http://ColoradoRefinan..]
2004-04-16
_ Clayton-M-Cox [Some education links. Please delete this message if it was..]
_ Nicholas Conceicao [Your are Great. And so is your site! Awesome content. Good..]
_ Eva Sutherland [I am so thankful for finding your website! http://chat.tra..]
_ Christopher Siniscalo [Great work! http://qblog.askmrq.com/?u=nbuycarisoprodol7 <..]
_ Kai Boot [Very good site! Thanks! :-) http://zcheapphentermine8.onli..]
2004-04-22
◆ 今日のオプション
% du -sh ~/Mail
du の -h は --human-readable。
<URL:http://i.loveruby.net/d/20040421.html#p03>より。
ちなみに8.9Mでした。
一番古いのが1999年9月。個人的なメールが500通ほど、事務的なメールが200通ほど。
メーリングリストのメールは、以前は全部手元に残していたんですが、 今は2週間ほどで自動的に捨てています。
◆ [NovelEngine] ノベルゲームのスクリプティングについて
RandomNote に「わからない」 と書いたところ、 <URL:http://www.denpa.org/~go/denpa/200404/from21.html#21_2> にて詳しく説明をいただきました。ありがとうございます。
しばらく考えて、ようやく分かった気がします。
次のスクリプト
鈴菜:詩子も来るでしょ? [環境 待ち 1000][がっかり]と言うか来い。付き合え
は、
------>時刻 t 声: う_た_こ_も_く_る_で_しょ________|と_い_う_か_こ_い___つ_き_あ_え 文: 詩子も来るでしょ?___(1sec)______|と言うか来い。付き合え ________________________________待つ↑
のように実行される。(等幅フォントのつもりで)
これが、例えばメッセージ表示速度を遅くすると
------>時刻 t 声: う_た_こ_も_く_る_で_しょ________|と_い_う_か_こ_い___つ_き_あ_え 文: 詩__子__も__来__る__で__し__ょ__?______(1sec)___|と__言__う__か__来__い__。__付__き__合__え
となってしまいずれるから
鈴菜:詩子も来るでしょ? [声待ち 3000][がっかり]と言うか来い。付き合え
として
------>時刻 t ------------------- t=3↓ 声: う_た_こ_も_く_る_で_しょ________|と_い_う_か_こ_い___つ_き_あ_え 文: 詩子も来るでしょ?__(t=3まで)____|と言うか来い。付き合え ________________________________待つ↑
これでとりあえず解決した。しかし……という問題提起でいいのでしょうか?
構造化データについて
実際に書かれるスクリプト形式がどうなるのかが示されていないので何とも言えませんが、 テキストを演出記述などから分離するのは、補助ツール等をうまく作らないと かえって分かりづらくなるおそれもあると思います。
例えば上の例では、
鈴菜:詩子も来るでしょ? [環境 待ち 1000][がっかり]と言うか来い。付き合え
という記述では、これを見るだけで途中で「がっかり」に変わることが分かりますが、 テキストを分離してしまうと、テキストを見るだけではそれが分からなくなってしまいます。
ただこれについては、
- このことはそもそも問題なのか?
- うまくスクリプト文法を作れば解決できるのではないか?
- うまく補助ツールを作れば解決できるのではないか?
などの点に関してまだあまり考えていないため、判断は保留ということにしておきます。
_ Viagra [Your site is awesome <a href="http://students.concord.edu/..]
_ Viagra [You have a great site <a href="http://ibscore.dbs.umt.edu/..]
_ Viagra [You have a top notch site <a href="http://faculty.plattsbu..]
_ buy viagra online [ <a href="http://www.life2themax.net/links/jump.php?id=256..]
_ i was happy [I agree with RSA. This could work, but a simple bulletted ..]
2004-04-28
◆ [NovelEngine] ノベルゲームのスクリプティングについて(2)
ちょっと思いだしたのは Ruby の文法の話で、 Ruby は
if foobar func end
みたいな end 文法の言語なのですが、これを採用することになった理由の一つに end 文法を採用しても Emacs の ruby-mode を作れることが判明したからってのがあるそうです。
- 「ruby-modeの方が先にできた」(Matzにっき)
リンク先には書いてないのですが、もし ruby-mode が作れなかったら C 言語みたいに {} を使う 文法にしていたかもしれないというのも、確か Linux Magazine のまつもと氏の連載に書いてありました。
結局のところ全ては成果物を作りやすくするためにあるのですから、 やはり、スクリプタが演出を指定するために使うツールの側から考えた方がいいのかもしれません。
お返事
<URL:http://www.denpa.org/~go/denpa/200404/from21.html#27_1>
スクリプト文法的な問題もありません。 適切な(=論理的に目的とするデータ構造を再現する)文法はいくらでも提供できるでしょう。 ただ、その文法の熟知をスクリプタに求めるのは酷です。
この部分を読んでちょっとひっかかったのですが、これは要するに 「文法を熟知していればスクリプトの直書きも可能ではあるが、 文法を熟知していなくても演出指定ができるようにするべきである」 という理解でいいのでしょうか?
まず大雑把なテキスト入力をテキストエディタで行ない、 実際の細かい演出はオーサリングツール上で行なうという感じでしょうか。
ごうさんのお話しで、「結局、スクリプタが組む文法としてはどういうものを考えているんだろう」というのが 分からずにもやもやとしていたのですが、オーサリングツールを使ってやるということになるということで 何となくもやもやが晴れました。
文法はどうだとかいうことしか気にしていない時点でテキストエディタ打ち込みを暗黙のうちに仮定してしまっていたわけで、 それはまずかったなと思います。
Fate とか、スクリプトすげーことになってるわけですが、死にながら作業してたんだろうか。あと Qualtett も。
Fate は、typemoon の清兵衛氏がフロー管理用のフローチャートツールを公開しているようですが、 演出部のスクリプティングがどうなっているかは知らないです。 (というか Fate まだやってなかったり。夏まではできそうにないなぁ……)
Qualtett は体験版しかやってないのですが、自動生成っぽい感じを受けなかったんですよね。 あれを手で入力していくのはさすがにつらすぎるんじゃないかという気もするんですが……
_ Ikjmgd [<a href="http://codtramadoluk.squarespace.com/">tramadol o..]
_ Lmncxwe [<a href="http://shrinkurl.us/rxxanaxgener">xanax generic</..]
_ zvljxv [http://fm7.biz/tfb http://fm7.biz/tfc http://fm7.biz/tfd h..]
_ txymzw [http://fm7.biz/tfl http://fm7.biz/tfm http://fm7.biz/tfn h..]
_ Kouyusi [<a href="http://tomas.quickfreehost.com/buygenericviagra.h..]
Before...
_ jdlhed [[url=http://blog.360.yahoo.com/blog-12SVlrc5dKodOFwAkH67Ru..]
_ ccrbrnfkcbcqno [[url=http://blog.360.yahoo.com/blog-W8EBmvM5bqQN8_5ZvGa3c3..]
_ pjnsoycoywou [[url=http://blog.360.yahoo.com/blog-hDqen3M6erLjJgBOLGJWJi..]
_ drswkutw [[url=http://blog.360.yahoo.com/blog-10OWMx40d68qCcudLbVwJR..]
_ jboyfwkmogslqf [[url=http://blog.360.yahoo.com/blog-ZlHtA9Eyc6O3rsMW_MQ4wu..]