yamicha.com's Blog - Presented by yamicha.com
Blog yamicha.com's Blog - 2018/01 の記事
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31]

yamicha.com's Blog
 諸事情により、現在更新休止中。ご了承ください。もし今後ブログを再開することがあるとすれば、その際にはこのブログスクリプトではなく、新しく開発したものによるかもしれません。
 当ブログ管理者についてはこちらをご参照。
開発魔法(737)
社会問題(733)
お知らせ(11)
質問・バトン回答(15)
ゲスト出演(8)
経済・知的財産(150)
ゲーム開発(182)
[Ada] 勝手に補足
- Note
- 金配りの次の一手


- Endless Ultimate Diary
- 銃世界

漢字バトン
- うるる雑記帳
- 漢字接力棒

ツキアイゲノムバトン
- ブログ@うにうに画像倉庫
- あぶ内閣

縺イ縺セ縺、縺カ縺励ヰ繝医Φ
- 月夜のボヤキ
- 騎士サーラバトン
パスワードを使う
名無し (2012/02/27)


開発者解放裁判
yamicha.com (2010/03/14)
Winnyに関しては、私も「純白」とまでは考えておりませんし、使用し..

開発者解放裁判
通りすがり (2010/03/08)
winnyに関しては「ダウンロードソフト板」なんてところを拠点に開発..

新型インフルエンザの恐怖
いげ太 (2009/11/03)
> C#などの「int Some(int , int)」は、F#では「(int * int) ->..

時効に関する思考
yamicha.com (2009/08/31)
>いげ太さんコメントありがとうございます。手元にドキュメントが少..
Homepage
Blog Top
Normal View
List View
Search
Calendar
Comment List
Trackback List
Blog RSS
Send Trackback
Phone Mode
Administrator
yamicha.com
Blog
るううるる。
Source
法令データ提供システム
FindLaw
Development
Java2 Platform SE 6
Java EE 6 API
MySQL Developer Zone
PHP Reference
MSDN Library
Ada Reference Manual
Objective Caml
Python Documentation
Erlang
Prolog Documents
正義なき新聞と新聞なき正義なら
2006/03/17(Fri)20:33:18
 Javaアプレットといえば、おそらく半数はゲームに使われているでしょうが(無論それなりに実用的なものも作れますが。私もチャットを作りましたし。しかし、ファイル保存ができないなど致命的な弱点が多々あるため、実用性はかなり落ちます)、それに関して。
 Episode.1の時はJavaをほとんど習得しておらず、C/C++から入った私はオブジェクト指向に関しても良く理解していませんでしたので、ただひたすら動くように作りました。当初は何もかもをAppletの継承クラスで実装していたのですが、あまりに肥大化しすぎたためステータスやエフェクト用にクラスを作ったという経緯があります。
 このようにクラスを分離させてはみたものの、相変わらず基底クラスのバカでかさといったらすさまじいのですが、バラしたクラスを使ってみるとこれが結構便利なのです。ですから最後の方の実装は「オブジェクト指向モドキ」になっていますが、大半をオブジェクト指向など無視してコーディングしたため、アプレットの継承クラスにあらゆる機能が集中しています。
 それを踏まえてTypingWindはオブジェクト指向でコーディングしましたが、例えばフィールドと戦闘画面は基底クラスが同じになっています。で、どのように使っているかといいますと、基底クラス型のstatic変数に対して描画やリスナ処理を行うようにしておき、これに戦闘やフィールドのインスタンスを代入することで画面を切り替えているのです、これが。
 少々荒っぽい手段ですが、動きます。しかも実装はかなりお手軽です。というのも、この方法で実装する場合、いかなる場合でもアプレットの継承クラスからの要求はスタティック変数にそのまま丸投げすれば良いわけですから。弱点としては、例えば戦闘に入るとフィールドクラスのデータは全部なくなるため、必要なデータはすべてstaticで管理する必要がありました。
 Tactical Revolutionでも似たような仕組みを使っていますが、staticメソッドではなくクラスを1つかませ、フィールドや戦闘画面のクラスへはそこからアクセスするようにしています。この中間のクラスが画面を媒介するようになっており、例えばステージを1つ選択すると、ステージの番号が中間のクラスに渡され、このクラスが新しいインスタンスを作って実行している、というわけです。
 こうして完全オブジェクト指向として作ったTactical Revolutionではありましたが、フィールドなどのクラスからさらにクラスを呼ぶことはできないという致命的弱点があります。また、例によってデータはしっかり保管しておかなければ消えてしまいます。途中までしか遊べない「New Game」モードの場合、データはstaticで管理しています。
 そこで考えた(というより本日突然ひらめいた)のが、「こうしたクラスから別のクラスを呼べるようにならないか」、さらには「メニューウィンドウもこれで実装してしまえないか」といったことです。実はWindowsもこれと似たような仕組みで動いているわけですが、それなら実装できないわけがありません
 まず前提条件ですが、

1.MouseListener、MouseMotionListener、KeyListener、描画メソッドのすべてがコールバックとして利用できなければならない
2.Threadを継承しなければならない
3.基本処理を書いた1つの抽象基底クラスを継承し、様々な処理が実装できなければならない。また、派生先では目的外のコーディングが最小限で済むようにしなければならない
4.メニューやダイアログとして利用する場合を考え、クラスからクラスを呼び出した場合は、呼び出し元の描画もなされるように、及び呼び出し元にもキーやマウスの動作を渡せるように実装することができなければならない(無論、これがなされない実装もできなければならない)
5.描画及び操作にsynchronizedロックがかけてあり、その上にスレッドのrun()もしくはそこから呼び出されるメソッドにsynchronizedロックがかけてある場合で、そのロック内からクラスが呼び出された上、呼び出し側は呼び出されたクラスのスレッドが終わるまで待機し、さらに呼び出されたクラスの側から呼び出し元のsynchronizedメソッドが呼び出されるような場合でも、デッドロックが起こらないようにしなければならない
6.ここで作るのはいわば「ワークフレーム」であるが、定数以外のstatic変数を極力使用してはならない

 結構難しいです。実は5番が問題で、Tactical Revolutionでもかなり苦労した部分です。例えばTactical Revolutionの場合、
// 実際にはこのような実装及び変数名ではありませんが
// 分かりやすさのために相当簡略化しています

synchronized public void run(){
	message.set("会話メッセージ");
	message.waiting();	// メッセージが閉じられるまで待機
	// ...
}
synchronized public void draw(Graphics g){
	message.draw(g);
	// ...
}
synchronized public void mousePressed(MouseEvent e){
	message.mousePressed();	// メッセージを閉じる
	// ...
}
 さて問題、これを実行するとどうなるでしょうか。まず、メッセージが閉じられるまで待機している時点でロックが取得されていますから、当然drawメソッドは実行されませんから、描画が行われない状態になります。また、mousePressedメソッドも実行されないため、永久にメッセージが閉じることはありません。見事なデッドロックの完成です。
 仕方がないのでTactical Revolutionでは「1マイクロ秒waitさせてはメッセージウィンドウが消されていないかチェック」という実装で(wait中はロックが解除される)監視を行っています。しかし、今回ははなっから「ウェイト状態の解除はinterrupt()」を前提として作成しました。この場合、待機スレッドとは別にinterrupt()を呼び出すスレッドが必要ですから、Tactical Revolutionの方式の方がリソース消費は少なそうですが。
 ちなみにTactical Revolutionの場合、メッセージなどのウィンドウはあくまでクラスのオブジェクトですが、今回の実装では選択ダイアログも1本のスレッドを持ちます。構造としては、Threadを継承して処理のベースクラスを作り、さらにそれを継承してダイアログ用にデッドロックフリーの待機メソッド(このクラスのスレッドが終了するまで呼び出し元のスレッドを待機)などが定義されたクラスを作っています。
 待機メソッドが使いたければダイアログ用クラスを継承すれば良いのですが、そうでないクラスでもデッドロックフリーの待機を実現するため、間にもう1本スレッドをかませて待機を行うためのクラスも実装しました。1つ余分にスレッドは立ってしまいますが、これで(ベースクラスさえ継承していれば)あらゆるクラスに対して待機が実現できます。
// 待機専用クラスの仕組み
public class 待機クラス extends Thread{
	public void run(){
		呼び出し先.join();
		呼び出し元.interrupt();
	}
	public void 待機メソッド(){
		start();
		呼び出し元.wait();
	}
}
 他に1番の条件に関しては、イベントリスナを登録したり描画先のクラスを指定するメソッドを書いたインタフェースを作り、それをアプレットを継承したクラスにインプリメントした上で、コンストラクタでそのインタフェース型を受け渡すようにしました。3番の条件はオブジェクト指向で作っている限り自動的に満たされます。
 後は4番ですが、同じくコンストラクタで呼び出し元のクラスオブジェクトを呼び出し先に渡すようにしました。その上で、描画やイベントメソッド内で呼び出し元のオブジェクトの同じメソッドを呼び出せば、呼び出し元の描画やイベント処理を行うことができます。さらに、この構造は階層構造のようなものですが、重要な機能を含んでいます。
 例えば適当な画面Xから選択ウィンドウAを呼び出したとします。選択ウィンドウAの描画処理では、Xの描画メソッドをコールしています。さて、ここでさらに選択ウィンドウAから選択ウィンドウBを作りました。そして選択ウィンドウBも呼び出し側の描画メソッドをコールするようにしました。すると、BからAの描画メソッドがコールされ、さらにAからXの描画メソッドがコールされるため、Xの画面の上にAのウィンドウ、その上にBのウィンドウが描かれるのです。
 これでフレームワークは完成しました。後はこれを使うアプレットを考えるだけですが、これが決まらないから困っているのではありませんか。

 仏で大変な法律が作られそうになっています。若年者雇用促進とかで、26歳までの社員は雇用後2年までは無条件で解雇できるそうです。つまり、雇用されても2年以内なら「もうあなたは明日から来なくて良い」と言われれば、仮に自分に非がなかろうが、会社都合だろうが何だろうが、理由を聞くことも許されずに解雇されるしかないわけです。
 これはすさまじい失業率を改善するための措置だそうですが、どのような条件を付けたとしてもポストの数はたいして変わらないわけですから、結局雇用が流動化するだけのような気がしますが。早い話が「フリーター社員」のようなものです。このような手段にまですがるとは、日本の状況など仏から見ればうらやましいようで。

 ここで名言を1つ。「インターネットなき新聞と新聞なきインターネットなら、私は迷わず、いや喜んで後者を選ぶ」。というのも、「情報源秘匿問題の判決に対して全マスコミが怪しいまでに声をそろえて抗議」と「新聞と教科書の特殊指定見直し」の2つの問題が重なっており、いつもよりさらにボロが出ているのです。
 まず後者ですが、新聞業界に続いて教科書業界も反対の嵐です。新聞はまだ社会的に反対する理由があるとしても、教科書にはないはずですが。つまり、それが新聞であれ教科書であれ、こうした規制緩和に業界は必ず大反対を行うということであり、裏返せば新聞社も「特殊指定見直しが社会のためにならない」などとは本気では考えておらず、ただ単に自分の利益のためだけに反対していることが分かります。公益のためなどと言い訳せずに、「我々の利益が損なわれるのが嫌だから反対します」と素直に言えば、まだ同情もできるというのに。
 当然、新聞はすぐにでも特殊指定を見直すべきでしょう。新聞などもう長くないメディアですし、その上に「特殊指定があるから山間部にでも配達できる」といった言い訳までしているようですが、特殊指定があってもなくても山間部の配達が不採算なのは変わらないでしょう。さすが新聞屋さん、詭弁が上手でいらっしゃる。
 さらに判決に関してですが、当然これは不当判決でしょう。新聞が情報源を秘匿しようがしまいがどうでも構いませんし、実際のところ「情報提供者の権利を守るために秘匿しよう」のような見上げたことはこれっぽっちも考えていないのでしょうが、こうした判決で善良なる情報提供者の利益が害されることだけは許せません
 この判決は読売新聞の記者の問題ですが、こういう時だけ全マスコミが不気味に歩調をあわせるというのも恐ろしいです。今回の決定は確かに不当判決ですが、これが仮にマスコミに不利な判決ではあってもまっとうな判決だとしたらどうでしょう。ペンは剣より強しとはこういうことを言うのでしょう。無理が通れば道理引っ込むとも。
 実際、こういう傾向は非常に恐ろしいものがあります。読売新聞は大量破壊兵器の存在をねつ造して謝罪もせず、政府にくみして理不尽な論調で民主党批判などを繰り返していましたが、そういう「政府にゴマすり新聞社」ですら、せっかく大好きな政府に有利な判決が出たのに、自己利益が絡んでくるとこれです。野球再編問題の時は「超高額所得者のスト」などとインチキ論理で選手らを批判しましたが、マスコミ自体がこういう体質ですから。
 日本の全国紙で最もジャーナリズムに近い新聞社である朝日新聞などは、そのおかげで一部から理不尽な批判にさらされることもたびたびあります。これが政府を敵に回すリスクです。つまり、報道機関が政府権力を敵に回すというのは、それだけの覚悟が必要なのです。そういう代償を払った新聞社でもなければ、「いいとこ取り」は許されません。
 また、例えば毎日新聞は(完全とはいえないまでも)マスコミ批判が入った遺族のコメント全文なども比較的しっかり記載していてその点は好感が持てるのですが、言うまでもなくこれは自らの評価を落としかねないリスクを引き受けた結果です。そのリスクも取らないような新聞社には、遺族に取材を行ったり、コメントを記載したりする権利はありません。
 そういえば大半の新聞社の皆さん、規制緩和の流れを大絶賛していらっしゃいましたね。その規制緩和の次なる対象として新聞も含まれた。それだけのことではありませんか。他だけ規制緩和させておいて自分の業界だけさせない、というのは許されません。これに反対して良いのは、規制緩和を批判していたマスコミのみです。
 とまあ、こうして見てみるだけでもマスコミが信用ならないと言われるのは当然の結果というものでして。とにかく、今回ばかりはマスコミの自己都合的「ペンは剣より強し」「無理が通れば」論理に負けてはいけません。言い出した以上、特殊指定見直しは必ず行われるべきです。皇室のようなバカげた結末はもう不必要です。

 以前にMySQLのストレージエンジンを扱った際、FEDERATEDテーブルを使うことができませんでしたが、検索してみたところ「FEDERATEDテーブルで自分のデーモンを参照した場合にはテーブルが作成できない」という同様のエラーに悩まされている人も存在する様子。
 これはまさか、自分のデーモンを参照しては使えないのでしょうか。ということは、1つのPCにポートの違う2つのデーモンを用意するか、2つのPCで使うしかなさそうです。前者の方法はPCのスペックからして少々無理がありますので、後者の方法を用いることにしました。接続先は「sub.localyamicha.com」(192.168.0.2)です。
 FEDERATEDテーブルを使用する場合、参照先のSQLにも全く同じテーブル構造のテーブルが存在しなければなりません。つまり、2つのPCで作業するなり、プロンプトを2つ開いてそのうちの1つで相手側のSQLに接続するなりしなければなりません。これを踏まえてFEDERATEDテーブルを作成してみるとしましょう。
 まずは5.0のマニュアルのサンプル通りにMyISAMで。PRIMARYとINDEXの2つを用いたのは、以前に「INDEXが指定されていない」エラーに見舞われたため、エラー回避を考えて念のために入れてみたのですが、結局は不必要でした。
// sub.localyamicha.com(サブPC)
USE test;

CREATE TABLE fed(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number) , INDEX(number)) ENGINE=MyISAM;

// www.localyamicha.com(メイン・サーバーPC)
CREATE TABLE fed(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number) , INDEX(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fed';

INSERT INTO fed VALUES(0 , 'Sara') , (1 , 'Harden');

// sub.localyamicha.com
SELECT * FROM fed;

// Result
number	name
0	Sara
1	harden
 では、次はInnoDBを使ってみます。
// sub.localyamicha.com
CREATE TABLE fe2(number INT , name VARCHAR(32) , PRIMARY KEY(number));

// www.localyamicha.com
CREATE TABLE fe2(number INT , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fe2';

ERROR 1121 (42000): Column 'number' is used with UNIQUE or INDEX 
but is not defined as NOT NULL
 どうしたものでしょう。変なエラーが出てしまいました。これ、実は「参照先のカラムはNOT NULLなのに、作成されたテーブルのカラムはNOT NULLになっていない」ということらしいです。確かに参照先のテーブルはPRIMARY KEYの暗黙的NOT NULLを利用しましたが、PRIMARY KEYといったら指定しなくてもNOT NULLと考えるのが普通でしょうよ。
// 上のコードより続く
CREATE TABLE fe2(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/fe2';

INSERT INTO fe2 VALUES(0 , 'Ilias');

// sub.localyamicha.com
SELECT * FROM fe2;

// Result
number	name
0	Ilias
 ストレージエンジンなどには関係なく動くようです。ということは、まさかビューも動いてしまう、ということはさすがになさそうですが、ダメもとで
// sub.localyamicha.com
CREATE VIEW feview AS SELECT * FROM fe2;

// www.localyamicha.com
CREATE TABLE feview(number INT NOT NULL , name VARCHAR(32) , 
PRIMARY KEY(number)) ENGINE=FEDERATED 
CONNECTION='mysql://root:password@sub.localyamicha.com/test/feview';

INSERT INTO feview VALUES(1 , 'Shelley');

// sub.localyamicha.com
SELECT * FROM fe2;

// Result
number	name
0	Ilias
1	Shelley
 動くのですね、これが。どうやらテーブルとして参照さえできれば何にでも使えるようです。そういえば、ドッキリいたずらとして「デスクトップのスクリーンキャプチャを壁紙に設定し、アイコンは非表示にしてしまう」というものがありましたが、FEDERATEDテーブルに関しては参照先のテーブルをリネームして保管しておき、代わりに同じ構造でBLACKHOLEテーブルを作っておくと、かなりいじわる度の高いいたずらができるのでは。コネクトできないのならともかく、クエリは成功したのになぜかデータが記録されない、という。

 ところで、紙に書かれたリストを見てふと考えたのですが、SQLの集計はどこまでのことができるのでしょうか。例えば「騎士と魔道士の2つのカテゴリが存在する場合で、それぞれの人数を返す」といったクエリは即座に打てますが、それなら「飲み会で騎士からは50ティル、魔道士からは40ティル徴収することにした。ここに参加者リストがあるとして、全部で何ティルの予算が得られるか」といった問題を1つだけのクエリで簡潔に実行するにはどうすれば良いでしょう。これは色々と応用が利きます。
 そういえば、以前にORDER BYに条件式を書いたことがありました。条件式を複数記述し、全部にDESCキーワードを指定しておけば、先頭から順に条件に当てはまるものをソートすることができるのです。これを集計でも応用できないものでしょうか。まず基本形より。
// サーラの練習試合結果テーブル(対戦相手 , 勝敗)
CREATE TABLE combat(enemy VARCHAR(32) , result VARCHAR(32));

INSERT INTO combat VALUES('イリアス' , '敗北') , ('ハードゥン' , '勝利') , 
('シェイン' , '勝利') , ('シェイン' , '勝利') , ('ブライトネス' , '勝利') , 
('サラサーラ' , '引き分け') , ('シェイン' , '勝利') , 
('ハードゥン' , '引き分け') , ('シンシア' , '勝利') , 
('ディヴァイン' , '勝利') , ('リンダ' , '勝利') , ('イリアス' , '勝利');
 ここで問題です。騎士サーラは一体何勝したのでしょうか。この程度なら簡単なものではありますが。
// 勝利数を得る(ちなみに結果は「9」)
SELECT COUNT(*) FROM combat WHERE result = '勝利';

// 相手ごとに勝利数を得る
SELECT enemy , COUNT(*) FROM combat WHERE result = '勝利' 
GROUP BY enemy WITH ROLLUP;

// Result
enemy	COUNT(*)
イリアス	1
シェイン	3
シンシア	1
ディヴァイン	1
ハードゥン	1
ブライトネス	1
リンダ	1
NULL	9
 確かに得られましたが、それなら「総戦闘数がこれだけで、そのうち勝利がこの程度」といったものを簡単に出す方法はないでしょうか。また、上の飲み会の例と同じようなものですが、サッカーのように勝利3点、引き分け1点、敗北0点のような計算をサブクエリレスで、しかも1つのSQLでごく簡単に行うことはできないのでしょうか。
 そんなこんなで、今回適当に考え付いてしまった方法を。
// 勝利数を得るクエリ
SELECT SUM(result = '勝利') FROM combat;

// 戦闘数と諸結果を得るクエリ
SELECT COUNT(*) AS '戦闘' , SUM(result = '勝利') AS '白星' , 
SUM(result = '敗北') AS '黒星' , SUM(result = '引き分け') AS '引分' 
FROM combat;

// Result
戦闘	白星	黒星	引分
12	9	1	2

// それをキャラ別に見るクエリ
SELECT enemy , COUNT(*) AS '戦闘' , SUM(result = '勝利') AS '白星' , 
SUM(result = '敗北') AS '黒星' , SUM(result = '引き分け') AS '引分' 
FROM combat GROUP BY enemy WITH ROLLUP;

// Result
enemy	戦闘	白星	黒星	引分
イリアス	2	1	1	0
サラサーラ	1	0	0	1
シェイン	3	3	0	0
シンシア	1	1	0	0
ディヴァイン	1	1	0	0
ハードゥン	2	1	0	1
ブライトネス	1	1	0	0
リンダ	1	1	0	0
NULL	12	9	1	2

// 勝ち点を出す
SELECT SUM(result = '勝利') * 3 + 
SUM(result = '引き分け') AS '勝ち点' FROM combat;

// Result
勝ち点
29
 この通り、きれいなものです。ふむ、サーラさんは12戦9勝2引き分け、勝ち点29ですか。ごく簡単にこうした結果が得られるのです。サブクエリなど使わせません。頭がこんがらがるSQLですが、このように意外と単純にいくことも多いのです。
// 付録 - 勝率の求め方(サーラさんの勝率は0.75)
SELECT SUM(result = '勝利') / COUNT(*) AS '勝率' FROM combat;

// キャラ別
SELECT enemy , SUM(result = '勝利') / COUNT(*) AS '勝率' 
FROM combat GROUP BY enemy;
 SQLの演算の性質を上手く利用すれば、かなり色々なことができます。これを知っておくのと知らないのでは全く違うばかりか、単純なSQLで実装できるところをわざわざ(4.1以降でしか使えない)サブクエリを使うことにもなりかねません。しかも、サブクエリを使っても実装が著しく長かったり難しかったりするものも。
 やはり「SQLとREGEXは使いよう」なのです。「使っても何もできない」人から「使えばなんでもできる」人まで。多少は何でもできるようになりたいものです。
カテゴリ [開発魔法][社会問題][ゲーム開発] [トラックバック 0][コメント 0]
<- 前の記事を参照 次の記事を参照 ->

- Blog by yamicha.com -