MSP:Mine Sweeper Project

2005-05-28

test

2004-12-13

Ruby/Tk編(4) Tkとモデルの分離

タイマーを組んでみて気になった点:
 ラベルのテキストに状態を保持させていいものだろうか?

Tkはあくまでユーザインタフェースを構成するものであり、経過時間というような内部状態に相当するものは制御側(Ruby)が保持すべきものだろうと思うのである。
Tkはオブジェクトとしての記述が明確であり、Rubyから呼び出すとなおのこと実体が明確なので、ついつい内部状態を記述したくなる。RubyならTkLabelから派生させて適当なオブジェクトとすることも容易である。が、やはりTkと内部モデルはちゃんと分離すべきであろう。

次の組み込みは、いよいよ地雷の配置である。これはフィールド(10×10のボタン配列のことね)にランダムに割り当てることになるわけだが、ボタンが地雷を保持する、というのも不自然である。ちゃんとユーザのオペレーションとアプリケーションの役割を考えてみよう。
と、その前に用語定義:
フィールド:ゲームの領域全体
セル:ユーザの操作単位で地雷が設置される単位。ボタンに対応

ユーザのフィールドへの操作
・セルのオープン:地雷がないと判断しての操作。左クリックとする
・セルのマーク:地雷があると仮定して旗を立てる。右クリックまたはシフトクリックとする

セルをオープンした後の処理
・地雷がなければボタンを消し、隣接セルに存在する地雷数の合計を表示
・地雷があればゲームオーバー。全地雷の位置を表示

処理の内容をみると、「隣接セル」から情報をもらったり、「全地雷」の位置を表示したり、とセルの配置や地雷位置情報を処理する必要があることがわかる。特に「隣接セル」の定義は暗黙のうちに8近傍(上下左右斜め4)としているが、実はこの定義も抽象化できるはずである。セルの配置が格子状とは限らず、互い違いに並んでいたら隣接セルの数は6つになる。立体的な格子配置ならば26近傍である。(UIはどうなるかわからんが)
では隣接の情報はだれが管理するかというと、フィールドがセルの位置を管理するために実体として定義すべきではないかと思われる。フィールドが全部のセルの情報にアクセスできるのであれば、地雷に関する情報の経路も明確になる。

隣接セルの地雷数の合計を求める
・ユーザ→セルをオープン
・オープンされたセル→自分が地雷を保持しているかどうか確認
・地雷を保持していない:セル→フィールドに隣接セルの地雷数取得を指示
・フィールドはセルの配置情報から隣接セルのリスト作成
・フィールド→隣接セルの地雷有無を確認
・フィールド→セルに地雷数を通知

ちなみに地雷を当てた場合は、
・地雷を保持している:フィールドにゲームオーバー処理を通知
・フィールドは全セルにゲームオーバーを通知
・地雷を保持しているセルは地雷保持を表示

            
というわけで、オブジェクトの実体として、
・フィールド
・セル
の役割が明確になった。

2004-12-11

Ruby/Tk編(3) タイマーを仕込む

なんとなく見た目ができたところで機能の作りこみ、まずはタイマーである。
単純にボタンを押したら数字をカウントアップ、もう一度押したら停止、としてみる。

こ こでふと考える。周期的にイベントを発生するのは、制御側(Ruby)か、UI(Tk)側のどっちがいいのだろう。タイマー機能というものは見た目には 関係ないものだからUI側から分離したほうが自然なような気もするが、そうするとRuby側でThreadを使ってイベント生成、という面倒なことにな る。Windows版RubyはThreadで制約があるので安直にTkのTkAfter(またはTkTimer)を使うことにする。

タ イマーをUI・ツールキット側に含めたほうが簡単というは、Ruby+Tkという組み合わせに限ったことでない。ツールキットは外部からのイベント(主に ユーザー入力)を処理するインタフェースであり、イベント駆動型の構成となる。だからツールキットはループで処理されることになる。するとループの外には イベントが発生しない限りとばなくなるので、制御側にタイマーをおくためには、制御側とは独立にイベントを発生させる機構が必要になる。
というわけで、タイマーはツールキットに頼っとけば吉としておこう。

さて、実装である。
TkのタイマーといえばTkAfter(またはTkTimer)。メソッドはここを参照。色々あるが、startとstopでいいだろ。

# カウンター用のフレームを作成
TkFrame.new($puzzle_demo) {|lblframe|
borderwidth 2
relief 'sunken'
remain = TkLabel.new(lblframe) {
font $font
justify 'left'
text "labeltest"
}
remain.pack('side'=>'left','expand'=>'true' )

TkButton.new(lblframe) {
text '(^_^)'
command proc{
text '(*_*)'
$timecnt.start

}
}.pack('side'=>'left','expand'=>'true')

$counterlbl = TkLabel.new(lblframe) {
font $font
justify 'left'
text "0"
}
$counterlbl.pack('side'=>'left','expand'=>'true')

}.pack('side'=>'top', 'fill'=>'x', 'pady'=>'2m')

#タイマー生成
$timecnt = TkAfter.new( 1000, -1, proc {
$counterlbl.text = $counterlbl.text.to_i+1
} )

2004-12-05

Ruby/Tk編(2.2) 見た目がでけた


なんとか見た目がでけた。
上部の ラベル・ボタン・ラベル の3つの部品をそれなりに均等に置くのがよくわからんかったのだ。3つをフレームで囲って左詰めで配置し、expand属性を設定することでほぼ均等配置になることがわかった。

# カウンター・ボタン用のフレームを作成
TkFrame.new($puzzle_demo) {|lblframe|
borderwidth 2
relief 'sunken'
remain = TkLabel.new(lblframe) {
font $font
justify 'left'
text "labeltest"
}
remain.pack('side'=>'left','expand'=>'true' )

TkButton.new(lblframe) {
text '(^_^)'
command proc{ text '(*_*)' }
}.pack('side'=>'left','expand'=>'true')

counter = TkLabel.new(lblframe) {
font $font
justify 'left'
text "counter"
}
counter.pack('side'=>'left','expand'=>'true')

}.pack('side'=>'top', 'fill'=>'x', 'pady'=>'2m')

ボタンにはスマイルマークの絵を貼り付けたいところだが今のとこはキャラクタでごまかす。
#ところで、ソースのインデントってどうやって表示するのかな?

Ruby/Tk編(2.1) Widgeデモの改造・・・

(2)で書いたように、パズルの実体である puzzle.rb は、ほとんどボタンやラベルのレイアウトであった。で、レイアウトを変えれば、まずは見た目はマインスイーパーもどきになるだろうと思ったのだが、、、、 ジオメトリマネージャのpackの機能がよくわかっていないので、見当はずれのレイアウトになってしまう。永井さんのRuby256本を読み返さねば。

というわけでまだ(3)は遠い。

2004-12-03

Ruby/Tk編(2) Widgetデモを読んでみる

15パズルの実体は、C:\ruby\samples\tk\demos-jp\puzzle.rb
構成は意外とシンプル。

  • demo 用の toplevel widget を生成 $puzzle_demo = TkToplevel.new {・・・
  • label生成 msg = TkLabel.new($puzzle_demo) {・・・
  • frame 生成 TkFrame.new($puzzle_demo) {・・・
    # 内部に「閉じる」「コード参照」ボタン生成
  • #パズルピースの生成とレイアウト15個
  • #パズルピースをクリックしたときの処理 def puzzleSwitch(w, num)

本質的にはこれだけ。最後の処理以外は部品の生成と初期化だけであり、パズルのインタラクティブな処理に関する記述は最後のものだけである。これですら「クリックしたボタンとスペースが隣接にあれば場所を取り替える」というものでしかない。なかなか簡単なもんだ。
さて、これをちょっとずつマインスイーパーに変えていこう。

ちなみに、\は¥の半角のつもりだがバックスラッシュに見えるかも。ディレクトリについても自分の環境に読み替えてたもれ。

2004-12-01

第1ターゲット Ruby/Tk

ようやく本題である。
まずはRuby/Tkでのマインスイーパー作成が第1ターゲット。最近チョコチョコとRubyをかじっていて(というか、なめた程度だけど)なんかつくってみたいな、というただそれだけ。
幸い環境は全部フリーで手に入る。うちの環境はWindows。うまいぐあいに、Rubyのメーリングリストのruby-listでWindows用Ruby/Tkの情報があった。
参考情報は、 [ruby-list:40315] Ruby/Tk 講習会へのWindows での参加 
これは、永井秀利氏(Ruby/Tkのメンテナであり、Rubyを256倍使うための本 界道編 ようするにRuby/Tk編の著者)主催のRuby/Tk講習会参加にあたって、Windows環境整備の紹介記事。
・One Click Ruby Installer 182-14_rc9 のインストール
・ActiveTcl 8.4.7.0 か 8.5.0.0b2 のインストール
・One-Click Ruby Installer with ActiveTcl向け tcltklibバイナリの導入
についてまとめてあり、これでまず間違いなくWindowsでRuby/Tkが実行できる。もしかしたら環境変数の定義が必要かもしれないので、一応メモ。

環境変数の設定(XPの場合)
・スタート→コントロールパネルを開いてシステムを選択・起動
・「詳細設定」のタブを選択
・下の「環境変数」のボタンを押す
・ユーザーの環境変数のうち Path,TCL_LIBRARY,TK_LIBRARYを編集。なければ新規作成。
  Path C:\ruby\bin;C\Tcl\bin; を追加(Tclはいらないかも)
  TCL_LIBRARY C:\Tcl\lib\tcl8.4 バージョンはインストールしたものに合わせる
  TK_LIBRARY C:\Tk\lib\tk8.4 バージョンはインストールしたものに合わせる
  なお、パスは自分がインストールした場所に読み替える

これで、コマンドプロンプトから、
C:\ruby\samples\tk\demos-jp
で、
ruby widget
と入力で「Ruby/Tk:Widget デモンストレーション」が実行できる。ひとつの窓からさまざまなWidget機能のデモを呼び出せる。しかもそれぞれのソースもWidgetの機能で見ることができるようになっている。

これがその画面。このなかでマインスイーパーに応用できそうなデモがあった。15パズルである。

ボタンが並んでいるのはそのまま地雷フィールドに使えそうだし、文字ラベルもある。あとはタイマーを整備すればいいぐらいではないのかな。しばらくはこれでお勉強なのだ。

2004-11-30

タイトルを英語にする(IE対策)

FireFoxを使っていたので気がつかなかったのだが、このブログをIEで観ると真っ白けだったのである。ソースは読み込めているので何か設定がまずいのかな、と調べてみた。

ここに各種文字化け対策が出ていたので色々試してみたが駄目。で、ソースを見てて
タイトルとmetaヘッダの順序が
<head>
<title>MSP:マインスイーパープロジェクト</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
#<>は半角
となっているので、もしや、とタイトルを英語にしてみたらあっさりちゃんと表示された。
IEがおまぬなのか、テンプレートが悪いのか、素直に英語タイトルすればよかったのか、、、orz
なかなか本題が始まらない。