メイン

Java アーカイブ

2008年04月13日

JDBC+servlet連携appletを作ってみた

せっかくこの長老マシンに苦労してMySQLとTomcatをインストールしてJDBCとservletが動くようになったのに、これまで簡単なテストプログラムしか動かしておらず、有効活用できていなかったし、あまり勉強にもなっていなかったので、Javaの復習とSwingの勉強を兼ねて、JDBC servletと連携するJavaアプレットを作ってみた。
3択首都当てクイズのページへ
アプリケーションのネタが思いつかなかったので、とりあえず3択クイズにした。

Servletとappletの役割分担は、MVCモデルに従って、クイズの作成、正誤判定、終了判断といったモデルは全てservlet側で行い、applet側は表示だけを行うようにした。すなわち、Mをサーバー側、VとCをクライアント側に分けた。そのため、appletは1問ずつ問題をservletから取得し、解答者の選択をservletに送っている。クイズの終了判定もservletが行うため、servletから問題でなく成績表が送られてくると終了という設計にした。

セッション管理にはjavax.servlet.HttpSessionを使っており、複数のクライアントが同時にクイズを始めても、servletで全てのクライアントの途中経過が管理される。Cookieが無効にされているとサーバー側からセッションIDが送られ、クライアント側でURLのquery部にセッションIDを付けるようにしているので、CookieがOFFでも動くはず。

クイズの問題はMySQLのデータベース上にあり、servletは1問ずつJDBC経由で問題を取得する。1つのセッションの中で問題が重複しないように、出題する問題の番号だけはセッションの新規作成時に全て決めておくようにした。

クライアント側の描画にはSwingを使用した。CardLayoutでペインを切り替えるようにしている。クイズ画面の動く背景は、SwingタイマーとAWTを使って描画している。最初はこの背景描画が非常に重く、試行錯誤して改良している内に、事前に画像ファイルを用意してタイル貼りする方が負荷が軽いことが判明したが、画像ファイルを作成するのが面倒なので、そこまではしなかった。

続きを読む "JDBC+servlet連携appletを作ってみた" »

2008年04月17日

Javaと日本語文字

Javaアプレットとサーブレットを作っていて、日本語文字、エンコーディング関連でつまずいたが何とか解決したことを記録する。
ここに書くものは、1つの成功例であって、最善の方法であるとは限らない。

●Servletからappletへの日本語文字列の受け渡し
Applet-servlet間の通信は通常HTTPで行うことになるので、appletからはjava.net.URLConnectionを使うとする。
Servlet側では、javax.servlet.HttpServletResponse#setContentType()でエンコーディングを指定する。
例:

public void doGet(HttpServletRequest req, HttpServletResponse res){
res.setContentType("text/html; charset=utf-8");

Applet側では、InputStreamReaderを使って、送られてきた文字列を読み出す。InputStreamReaderの作成時にエンコーディングを指定する。
例:
URL url = new URL("http://...");
URLConnection uc = url.openConnection();
InputStreamReader isr = new InputStreamReader(uc.getInputStream(), "utf-8");
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();

●ソースコードへの日本語文字の記述
Swingのラベルや標準出力への出力文字列など、プログラムが表示する日本語文字をソースコードに直接埋め込む場合は、javacコマンドでコンパイルする時に、ソースコードで使用しているコーディングの名前を-encodingオプションで指定する。
例:

javac -encoding EUC-JP eucfile.java

コーディング名は、日本語だと"EUC-JP","ISO-2022-JP","UTF-8","Shift_JIS"などがある。
(参考:encoding.doc

●APIからの日本語文字の取得
JDBC等のAPIがJava内部で使用するものでないコーディングで文字コードを返す場合は、文字コードの変換が必要になる。Stringクラスのコンストラクタを使うとJavaのコーディングに変換できる。
例:

ResultSet rs;
String jstr = new String(rs.getBytes(1), "euc-jp");

●LinuxでJavaアプレットを表示すると日本語フォントが出ない(□になる)場合の対処
Mozilla上のJavaアプレットの表示がそうなるのも、appletviewerでそうなるのも共通の原因で、JREに適切にフォントパスを設定すると解決すると思われる。
JREのインストールディレクトリのlib/fonts/の下にfallbackというディレクトリを作成し、日本語を含むフォントファイルへのシンボリックリンクを置く。
例:

cd /usr/java/jdk1.5.0_07/jre/lib/fonts
mkdir fallback
ln -s /usr/share/fonts/ja/TrueType/kochi-gothic.ttf fallback/
ln -s /usr/share/fonts/ja/TrueType/kochi-mincho.ttf fallback/

(参考:JavaSE 5.0のリリースノート (日本語)(English)

2008年04月27日

MySQL Connector-Jでの再接続

JDBCのDriverManager#getConnectionによるデータベースへの接続は結構時間がかかるので、短時間に何度もDBにアクセスする場合は、Connectionオブジェクトを保持したい。
しかし、MySQLのデフォルト設定の動作では、DBへの接続は8時間使われないと切られてしまう。常駐型のservletなどの連続運転のアプリでは、途中でこのコネクションが切れることがあり得る。この時間を変更することもできるらしいが、いつか切れるので、それで解決にはならない。従って、切れれば再接続する必要がある。

JDBCアプリ側で再接続する場合、DBへの接続が有効か無効かを知る術が問題になる。接続が切れた後のクエリ送信時の例外はSocketクラスのIOExceptionであり、ドライバ(MySQL Connector-J)にてcatchされるので、JDBCアプリではcatchできない。JDKのAPI仕様書を見るとConnection#isClosedというメソッドがあるが、これはJDBCアプリからcloseしたかどうかを返すものであり、MySQL側で接続を切られてもclosedにはならない。

そもそもservletでは、こういう時の定石はconnection poolingらしい。ServletエンジンでDBへの接続を保持して、servletからの要求に応じて接続を貸し出す仕組みで、暇な時に再接続もしてくれる、J2EEに標準装備されている一般的なものである。勿論Tomcatでも使える。
しかし、Tomcatのマニュアルの"JNDI Datasource HOW-TO"のページを見ると、設定方法がかなり複雑である。もっと簡単な解決方法は無いかと思ってConnector-Jのマニュアルを眺めていると、autoReconnectという設定項目が見つかった。DBとの接続が切れていればドライバが自動的に再接続するという設定だ。Obsoleteなproperty(将来削除される設定項目)であり、SQLExceptionを処理できない場合以外は非推奨と書かれているが、今回はとりあえず目的が達成できれば良かったので、

conn = DriverManager.getConnection("jdbc:mysql:/
/localhost/xxxx?user=xxxx&password=xxxx&autoReconnect=true");

という風にautoReconnectの設定を追加した。

実にシンプルかつスマートに解決できたと思っていたが、Tomcatのログを見て、時々エラーが発生していることに気付いた。調べてみると、どうやらautoReconnectの設定をしていても、DBとの接続が切れると、1回はクエリ送信が例外(SQLException)で終わり、それによって再接続され、その次のクエリから接続が有効になるようだ。
そこで、1回はSQLExceptionに対して再試行する、ということも考えたが、そもそもSQLExceptionをcatchして再接続するのならautoReconnectの機能を使う意味が無い、connection poolingの方がスマートだろう、と考えてやめた。

という訳で、connection poolingを試すべく、TomcatのHowToの通りに設定を書き、テストプログラムをコンパイルすると、javax.sql.*が無いというエラーになった。一瞬で確信した通り、プールされたconnectionを取り出すためのDataSourceクラスが1.3のJVMには存在しないのだった(javax.sqlは1.4以降の仕様らしい)。

途方に暮れてさらにConnector-Jのマニュアルを読み進めると、"Common Problems and Solutions"の所に答えが書いてあった。Connection poolingするか、autoReconnectにした上でSQLExceptionをcatchし、MySQLのエラーコードを調べて、回数の上限を決めて再施行するか、のどちらかをする必要があるそうだ。SQLException#getSQLStateでMySQLのエラーコードがわかり、MySQLのinfoによると接続系のエラーが起こるとSQLStateが"08S01"になるらしいので、SQLStateが"08S01"の場合に最大2回クエリを再施行することで解決した。

int retryCount = 3;
boolean transactionCompleted = false;
do{
try{
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("xxxx");
transactionCompleted = true;
}
catch(SQLException e){
String sqlState = e.getSQLState();
if (sqlState.equals("08S01")){
retryCount--;
}else{
retryCount = 0;
}
}
finally{
...
}
} while(!transactionCompleted && retryCount > 0);

続きを読む "MySQL Connector-Jでの再接続" »

2008年05月18日

弾性衝突円盤アプレット

何かJavaのGraphics2Dクラスを使ったプログラミングの練習をしようと思って、昔作ったことがある思い出のアプリをJavaで作ってみた。
弾性衝突円盤アプレットのページへ

各円盤は面積に比例した質量を持ち、壁や他の円盤に完全弾性衝突する。
後は説明不要だと思う。
Pentium3だと動作がちょっと重いかも知れない。

大昔、X68000のHuman68k(つまりウィンドウシステムでない)上で動くものを作った時は、アセンブラで書いてスプライトを使って衝突計算アルゴリズムもかなり練って速度を出したのだが、今回、Javaのコードをほとんど工夫せずに書き、描画処理は単純に消して描いての繰り返しで、しかもJavaアプレットとしてブラウザ上で動かすと、遜色ない速度が出たので、驚いた。68000MPUとCeleron 3.2GHzの性能はかくも違うのか。PCの性能の進歩はなんと速いことか。

割とシンプルなコードになったので、ソースコード、説明を添えて公開するつもりだったが、時間が無かったので、とりあえずアプレットだけ先に公開することにした。

円盤の描画は、先にBufferedImageの画像を用意しておいて、背景を塗りつぶしてGraphics#drawImageでペタペタ貼っているだけである。当方の環境ではちらつきが目立たないので、ダブルバッファリングは省略した。
円盤同士の衝突の計算は、円盤の速度の衝突点方向の成分を求めて、高校の物理で習う完全弾性衝突の式を当てはめて、その方向の速度の変化を求めている。

結局Graphics2Dの機能は使わなかった。非矩形の画像の重ね合わせに必要だと思ったのだが、java.awt.imageのBufferedImageクラスのαプレーンを使えば難なくできた。
別の新たなネタを探さねばならない。

続きを読む "弾性衝突円盤アプレット" »

2008年06月14日

弾性衝突円盤アプレットの設計メモ

先日公開した弾性衝突円盤アプレットはまた改造して別バージョンを作ろうと思ってるので、自分自身がそのソースコードを読めなくならないよう、設計を書き残す。

●Swing関係
・JAppletを継承するクラスは、盤面クラスのインスタンスを作ってタイマー処理するだけにしている。
 表示画面が複数あっても、盤面のインスタンスを増やすだけで済む。

・タイマーはSwing timerを使っている。汎用のjava.util.Timerを使うのに比べて、タイマーイベントがSwingのイベント発行スレッドにて他のイベントと一緒にシリアライズされるので、タイマーイベントによって処理が割り込まれるということがなく、排他制御がシンプルになる。
 というようなことが、JavaSEのAPI仕様書のjavax.swing.Timerの項に書いてある。
 なお、所々でSwingUtilities.invokeLaterを使ってるのは、同様の効果を期待してのことだが、よく考えると、Swing timerを使ってるので意味が無かった。

・JComponentの盤面の大きさは、JComponent#getPreferredSize()が返すようにしている。レイアウトマネージャはFlowLayoutを使っており、FlowLayoutは余計な調節をしないので、これだけで済むはず。

●描画関係
・円盤の画像は、盤面インスタンスの生成時に全パターン用意するようにしている。(50-58行目)

・盤面の描画は、スプライト的なものを使わず、毎回矩形で塗り潰して全描画、としてるので、せっかくなので、描画の度に色相を少しずつ変えながら塗り潰すことにした。(72-73行目)
 bg_countはそのためだけの変数。
 74行目の青色設定は、状態表示にしか効いていない。

・円盤の座標は中心の座標を保持しているため、描画開始位置は半径を引いた座標になる。

●衝突計算関係
・円盤の移動後に衝突判定と衝突後の速度の計算をしている(127-182行目)が、壁との衝突と他の円盤との衝突が同時に起こった場合、円盤同士の衝突を先に計算すると、壁との衝突の影響が考慮されないので、壁がクッションのような扱いになってしまう。かと言って、壁と他の円盤との衝突を同時に計算しようとすると、複雑な計算になる。
 そもそも、円盤と壁が円盤を挟むように同時に衝突する場合、真ん中の円盤は壁の役割を兼ねるので、円盤同士の衝突の計算は、壁に向かう円盤同士の衝突ではなく、壁に向かう円盤と壁から跳ね返ってきた円盤との衝突とするべきである。
 そのため、壁との衝突を先に処理するようにしている。

・壁との衝突判定は、円盤が壁と深く重なってる場合は衝突状態が続くので、速度ベクトルが壁の方を向いているかどうかを考慮するようにした。(131,138行目)
 円盤同士が深く重なってる場合も衝突状態が続くので、前回の中心間の距離を保存して、距離が縮まっているかどうかを衝突判定の材料にしている。(hit_depth[][]が前回の距離の2乗)

・複数の円盤の衝突が同時に発生した場合の計算は非常に複雑になる。
 1つの円盤が他の2つの円盤に同時に衝突するのはまだいいが、さらに衝突相手がまた別の円盤と同時に衝突する場合は、衝突の影響が全ての繋がる円盤に及ぶので、計算が大変である。
 そのため、1つの円盤は同時に他の1つの円盤としか衝突せず、さらに他の円盤との衝突はその瞬間は無視し、めり込むことを許す、とした。(isHit[]が衝突フラグ)

・衝突による速度変化を一旦別の配列に入れているのは、1つの円盤が同時に複数の円盤と衝突する計算をする場合のことを考えてのものであり、今回の方式では意味が無い。

●円盤の増減関係
・動的に円盤の数を減らせるよう、規定の数の円盤以外は、画面外に出るまで衝突判定の対象外とした。(185-193行目)

・動的に円盤の数を増やす時、既にある円盤と同じ場所から、速度ベクトルを30°ずらしてスタートさせるようにしている。それにより、円盤が分裂する感じになる。
 一気に2つ以上の円盤が増える場合は、分裂する円盤をそれぞれ違う円盤にしている。0番の円盤から分裂させると、いつも同じ円盤から分裂する感じになるので、最後尾の円盤から分裂させるようにしている。(201-207行目)

2008年09月06日

Java3Dのアプレットを公開する方法

Java3Dがインストールされていない環境でもJavaアプレットでJava3Dを動かす方法があることを知ったので、試しに簡単なアプリを公開する。

Java3Dアプレットのページへのリンク
ソースコード

コードは、Java3Dのデモパッケージに入っているHelloUniverse.javaに少し味をつけただけのものである。この3次元世界にあり得ない動きなので、ちょっと気持ち悪いかもしれない。

Canvas3dのインスタンスを貼り付けたAppletのクラスを、普通にHTMLの<applet>タグで公開すると、Java3Dのランタイムがインストールされていない環境では実行できない。しかし、org.jdesktop.applet.util.JNLPAppletLauncherを使って起動するように公開すると、(メジャーな環境では)Java3Dのランタイムがインストールされていなくても実行可能になる。

より具体的な方法は、Java3Dがインストールされた環境で動くJavaアプレット(Canvas3dのインスタンスを貼り付けたAppletのクラス)をJAR形式で用意し、AppletLauncherのページにある通りに、下記のようなHTMLで起動を指示する。少々長いが、最低限の設定なら、触る所は
・width/heightの部分
・アプレットの指定部分(下記で言うと、2ヶ所あるHello4dUniverseの部分)
・subapplet.displaynameの部分
くらいで良いと思う。

<applet code="org.jdesktop.applet.util.JNLPAppletLauncher2"
  width=400 height=400
  archive="Hello4dUniverse.jar,
               http://download.java.net/media/applet-launcher/applet-launcher.jar,
               http://download.java.net/media/java3d/webstart/release/j3d/latest/j3dcore.jar,
               http://download.java.net/media/java3d/webstart/release/j3d/latest/j3dutils.jar,
               http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jar,
               http://download.java.net/media/gluegen/webstart/gluegen-rt.jar,
               http://download.java.net/media/java3d/webstart/release/vecmath/latest/vecmath.jar">
  <param name="codebase_lookup" value="false">
  <param name="subapplet.classname" value="Hello4dUniverse">
  <param name="subapplet.displayname" value="My Java 3D Applet">
  <param name="jnlpNumExtensions" value="1">
  <param name="jnlpExtension1" value="http://download.java.net/media/java3d/webstart/release/java3d-latest.jnlp">
  <param name="progressbar" value="true">
  <param name="noddraw.check" value="true">
</applet>

続きを読む "Java3Dのアプレットを公開する方法" »

2008年09月14日

Java3Dアプレット試作

Java3Dを触り始めたので、何か作ってみようと思って作り始めたのだが、想定したものが意外に難しいことがわかってきて、挫折気味である。しかし、その途中段階でできたテストアプリが、自己満足に浸れるくらいには興味深く動いたので、一旦区切りをつけて、公開することにする。

何らかのアプレットのページへのリンク
・ソースコード
 J3DTest2.java
 TimerBehavior.java
 TimerBehaviorProcess.java

Java3D(OpenGL)のイベントのスケジューラーはAWTやSwingのそれとは別であり、例えばAWTやSwingのパーツに対してrepaint()してもCanvas3Dは再描画されない。Java3Dのイベント処理モデルに合わせるには、Behavior(javax.media.j3d.Behavior)というクラスを継承するクラスでイベントを処理する必要がある。
Java3Dでアニメーションするには、その理由により、java.util.Timerやjavax.swing.Timerを使ってAWTやSwingで処理するだけではうまくいかず、Canvas3D、Bounds(BoundingBox,BoundingSphere etc.)に関連付けたBehaviorに処理を渡さないといけない。Java3Dのアニメーションをさせるだけなら、java.util.Timerやjavax.swing.Timerを使うよりBehaviorを使う方が賢明だと思われる。
とりあえず、javax.media.j3d.WakeupOnElapsedTimeのを使って周期的にBehaviorを動かせるようなので、今回のアプリでは、それを使ったTimerBehaviorというのを定義している。
一定の速度の移動や一定周期の回転や一定の拡大率の拡大など、変化量が一定なアニメーションなら、なんとかInterpolatorというBehaviorを使えばシンプルである。今回できたアプレットもInterpolatorを使うと美しく書けるのかも知れないが、拡張性を求めて敬遠した。

続きを読む "Java3Dアプレット試作" »

2008年09月25日

3Dカッチンアプレット

作ろうとしていたものが何となくそれらしく動き出したので、一旦ここに貼っておく。
3Dカッチンアプレットのページへのリンク
ソースコード

一応、衝突したら弾き合うのだが、わかりづらい。どうしたらわかり易くなるのだろう。
衝突したら赤く光るチェックボックスを付けてみたが、やはりわかりづらい。
3Dだと仕方ないのだろうか。

続きを読む "3Dカッチンアプレット" »

2008年11月02日

MacでJava3Dを使う時の注意

自作のJava3DアプレットがMacで動かないことに気付いて調べた結果、現時点でMac OS X向けに公式にリリースされているJava3Dのバージョンが1.4.2と古いことが原因だと判明した。

APIリファレンスにあるから使っていた、javax.vecmath.Vector3dとかのgetX(),getY(),getZ()がバージョン1.5以降にしかないのだ。
こういうメソッドは最初に無いものをなぜ後から追加するのだろう?最初から無いならずっと無いままでいいと思うのだが。互換性を考慮するのが面倒で仕方ない。そんなにメンバアクセスはメソッド呼び出しでできないと気が済まない人がいるのだろうか。

ともかく、仕方ないのでgetX()とかを使わないように書き直した。
3Dカッチンアプレット
 ソースコード
J3DTest2アプレット
 ソースコード

2009年01月04日

数独解きアプレット

一時期、数独にはまったことがあり、腕には覚えがある身であり、一般紙に載る程度の問題は朝飯前であるはずだったのだが、年末年始に帰省して暇を持て余して、ある新聞の正月版に載っていた、懸賞問題の数独を暇潰しに解こうとしたら、解けなかった。
数字を埋め始めた直後から決して順調では無かったのだが、5分くらいかかって1/3程度埋めた後、10分以上1つも埋められなかった。問題が難しいというよりは、解無しな感触だった。まさかそれなりに発行部数が多い新聞で不完全な問題は出るまい、と思って粘っていたのだが、諦めた。

しかし、その問題のことが気になって気になって仕方が無かった。
数独ソルバーアプレットのページへのリンク
ソースコード

続きを読む "数独解きアプレット" »

2009年01月18日

FMS(JAVA PRESS Vol.11より)

JAVA Press VOL.11のp.92から始まる「Java2で作るネットワークプログラム」という記事のコピーが手元にあった。いつかJavaの勉強のために読もうと思っていた、約9年前に発行された雑誌のコピーである。UDPを使って相手を探し、TCPを使ってメッセージやファイルをやり取りする、簡単なチャットアプリのTCP/UDP通信の部分のソースコードが掲載され、解説されている。画面イメージも載っている。"File&Message Send"の頭文字を取って"FMS"という名前のアプリだそうだ。当時はGUIの部分と合わせてフルセットコードがJAVA PRESSのWebサイトにあったらしいが、今は入手できないと思う。

掲載されているのは8つのクラスの内5つのクラスのソースコードで、合わせて300行程度である。画面イメージから読み取れるGUIはシンプルなものである。似たようなGUIさえ作れば動くんだろう、と思って作ってみたら、4倍近いコードに膨れ上がってしまった。記事に載っていたコードも、1台のマシンでもテストできるよう、動的にポートを切り替えるように改造したりしたので、結局大半のコードに手が入ってしまった。
折角なので、ここで公開することにする。

FMSアプリ(改)のソースコードとクラスファイル一式(JARファイル)

・画面イメージ

●起動方法
まず、FMSアプリを動かすそれぞれのマシンにfms.jarをダウンロードして、以下のコマンドで展開してください。(JREはそれぞれのマシンにインストール済だとします。)

jar xvf fms.jar

次に、接続先として検索するIPアドレスのリスト(seekips.txt)とユーザ名のリスト(uesrs.txt)を編集してください。ブロードキャストアドレスも指定可能です。ユーザ名は、名前とキーワード(2つ合わせて1つのユーザ名となる)をコロンで区切ってください。
・seekips.txtの例
192.168.0.255
172.1.1.1

・users.txtの例
John:America
Yamada:Hanako
Penguin:Antarctica
Whitebear:Arctica

次に、コマンドラインから下記のjavaコマンドで起動してください。
java fms.Run (自分の名前) (自分のキーワード)

1つのマシンで複数起動してもOKです。

●使用方法
アプリを起動すると、seekips.txtに書かれたIPアドレスとローカルマシンの別ポートに対してUDPのパケットが送信され、相手から返信があると、ウィンドウの左上の領域に相手が表示されます。但し、相手の名前が自分のusers.txtに無い場合は、表示されません(自分の名前が相手のusers.txtにある場合は、相手のウィンドウには自分の名前が表示されます)。
相手から検索があるとユーザ名の後ろが[COMING]となり、相手から検索の応答があると[ONLINE]となります。

[COMING]または[ONLINE]の相手が見つかったら、リスト上の接続相手を選択して、メニューバーの"Users"→"Connect"で接続します。接続に成功すると、相手の名前の後ろが[CONNECTED]になります。

接続に成功したら、メッセージまたはファイルを送る相手を選択し、ウィンドウ下段の入力フォームにメッセージまたはローカルマシン上のファイル名を入力して、"Msg Send"ボタンまたは"File Send"ボタンを押してください。送信に成功すると、相手側のウィンドウにメッセージが表示され、ファイルの場合は相手側のローカルマシンに同じファイル名で保存されます。

●ソースコードの説明
Seeker.java
相手を探したり自分の状態を伝えたりするためのUDPパケットを発信するクラス。
オリジナルのコードに動的ポート対応を加えている。
Watcher.java
UDPパケットを受信するクラス。
オリジナルのコードではユーザ情報の管理を別クラス(Users)にしていたらしいが、想像がつかなかったので、ここで管理するようにした。
SendPack.java
TCPで送信する1回分の情報を持つクラス。
オリジナルのコードから変更無し。
Connector.java
1つのTCP接続のソケットと入出力ストリームを保持するクラス。
オリジナルのコードに動的ポート対応を加えた。
Accepter.java
サーバーとしてTCPの接続要求を待つ(acceptする)クラス。接続要求が来る度にConnectorのインスタンスを作成する。
オリジナルのコードから変更無し。
Run.java
FMSアプリを起動するクラス。Seeker, Watcher, Accepter,MainPanelのインスタンスを作る。
GUIとしては、SwingのJFrameのインスタンスを用意して、メニューバー付きのウィンドウを作る。
全て自作。
MainPanel.java
ウィンドウ内の各部品を作り、それぞれに対する操作や要求を処理するクラス。実質のメイン処理。
全て自作。

より詳しく書くとJAVA PRESSの記事と被るので、説明はここまでとする。
なお、Run.JavaとMainPanel.JavaはEclipse+VisualEditor(GUIエディタ)で作ったので、他の環境のEclipse+VEでもGUIが読み込めるはず。

続きを読む "FMS(JAVA PRESS Vol.11より)" »

2009年02月14日

EclipseにTomcatプラグイン導入

仕事で科学技術に恩返しできないならインターネットで、と思ってサンプルプログラムをここに書いてきたが、1年前に公開した「3択首都当てクイズ」のソースコードは掲載しなかった。なぜかというと、大体動くようになったが最後、そのコードを触るのが嫌になったからである。なぜ嫌になったかというと、Javaのサーブレットの開発が大変面倒だったからだ。

この24時間運転のwebサーバーは玄箱HGという小型の機械で動いている。いわゆるパソコンではなく、消費電力は低いが計算能力も低い。サーブレットのプログラムを書き換えると、それをこの機械で動かすためにTomcatを再起動するのに2分以上かかる。加えて、思い通りに動かないと、解析するにはTomcatのログを見るしか無い。ちょっといじって動かなくなると、調べるのも嫌になるのである。

そんな苦い思い出を、パソコンにインストールしたEclipseのTomcatブラグインが吹き飛ばした。どうせServlet APIの補完が効くのがいいくらいでしょ、と期待せずに一応インストールしてみたのだが、とんでもなく便利である。何が便利かというと、servletをデバッグモードで動かせることと、servletのソースコードを書き換えると勝手にコンパイルされてTomcatに組み込まれて動作開始することである。

Javaのサーブレットのソースコード上でブレークポイントを設定してTomcatを起動しておくと、クライアントがEclipseのデバッグモードで起動したJavaアプレットであろうが普通のwebブラウザであろうが、クライアントからの要求が来ると、サーブレットの動作がブレークポイントで一時停止する。デバッグモードのTomcatはバックグラウンドで動くので、Tomcatを起動した後も何の支障もなくEclipse上で他の作業ができる。クライアントのJavaアプレットにもブレークポイントを設定しておくと、サーブレットとアプレットとを交互に一時停止させることもできる。両方の動作を時間順に追跡できるので、大変わかりやすい。

さらに、一時停止したサーブレットのコードを、その場で書き換えて、そのメソッドの先頭からやり直させることができる。既に到着したクライアントの要求はそのままに、書き忘れた処理を追加して続行させたり、試しに処理をコメントアウトして続行させたりできるのである。ある1つの要求に対して、処理を書き換えながら色々な処理を試せるのが便利だ。
サーブレットのコードを変えたつもりなのに動作が思い通りにならない時、きちんとコンパイルし直したか、正しくTomcatに組み込んで再起動したかをいちいち気にする必要も無くなった。ブレークしてその場でコードを追加してそこを通る様子を見ればいいのである。


という訳で、EclipseのTomcatプラグインを使って見るとボロボロだった3択首都当てクイズのコードを、デバッグして整形したので、その準備過程の一部を書き残す。

●Tomcatプラグインのインストール
インストール方法は色々な所に書かれているので、省略する。注意点として、一次配布元のSysdeoのサイトは長期間アクセス不能になっているので、Eclipse Wikiのベージからダウンロードするのが良い。
今回はV3.2.1をEclipse V3.4にインストールした。

●プラグインの設定方法
EclipseのPreferencesの画面で、Tomcatの設定を埋める。
といっても、今回明示的に設定したのは、以下の項目だけ。
・Tomcatバージョン
・Tomcatホーム
・JVM設定のJREのバージョン
・Tomcatアプリケーションマネージャーのユーザー名とパスワード

●Tomcatプロジェクトの作成
取っ掛かりの部分はeclipse@Wkiのページに書いてある。
JavaのソースはWEB-INF/srcに、web.xmlはWEB-INFに、JSPのソースはプロジェクトの直下に配置する。

●web.xmlの作成
Tomcatのサンプルをコピーしてテキストエディターで書き換えても良いが、今回は、Eclipseで.xmlを開くと自動的に起動する、Eclipse組み込みのXMLエディターを試してみた。

1. EclipseでXMLファイルを新規作成(wizardはXML/XMLを使用)
2. 最初から入ってる1行("?=? xml")を消して空にする
 (これが残っているとサーブレットが起動できなかった)
3. 右クリックでAdd child→New elementを選択して"web-app"エレメント追加
4. 右クリックでEdit namespacesを選択
 Edit Schema Informationの画面でAdd→Specify New Namesp...を選択
 Prefix: (空)
 Namespace Name: http://java.sun.com/xml/ns/j2ee
 Location Hint: http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
 を指定して追加
 これで定型部分が埋まってweb.xml用の補完が効くようになる
5. web-appを右クリック→Add attribute→versionで"version"属性追加
6. web-appを右クリック→Add Child→(略)→servletで"servlet"エレメント追加
 "servlet"エレメント以下に追加された項目を埋める
7. web-appを右クリック→Add Child→(略)→servlet-mappingで"servlet-mapping"エレメント追加
 "servlet-mapping"エレメント以下に追加された項目を埋める

●WARファイルのエクスポート
1. 作成したTomcatプロジェクトのフォルダアイコンを右クリック→Preferences選択
 →"Tomcat"の「WARエクスポート設定」タブにて.warのファイル名を設定
2. Tomcatプロジェクトのフォルダアイコンを右クリック→「Tomcatプロジェクト」→
 「プロジェクト設定に従いWARファイルを作成」を選択
3. できた.warをサーバーのTomcatのwebapps/の直下にコピー

●結果
・サーバー(Tomcat)側コード
TrichoiceServlet.java --- サーブレット本体
web.xml
QuizReader.java --- JSP用
listquiz.jsp
※ソースコード中のURL等は当方の開発用PCでのものです。

・クライアント側コード
Start.java --- メイン
MainPanel.java --- 3択画面
ServletAgent.java --- HTTP通信部分

・このサイトの起動ページ
3択アプレット
listquiz.jsp

・過去の関連エントリー
[1] [2] [3] [4]

続きを読む "EclipseにTomcatプラグイン導入" »

2009年03月29日

簡易MMLプレーヤー

javax.sound.midiを使うと割と簡単に音階を鳴らせることがわかったので、簡単なMML(Music Macro Language)プレーヤーを作ってみた。BASICのPLAY文でお馴染みの、ドレミファソラシドがCDEFGABCで表されるあれだ。

Simple MML Playerの起動ページへ
ソースコード

とりあえず、CDEFGとでも入れてPlayを押すと、何か鳴ると思う。

以下の文字をサポートしている。(大文字と小文字の区別は無し、[]は省略可、xは数字を表す)
・C,D,E,F,G,A,B: ドレミファソラシド
 形式:(C,D,E,F,G,A,Bのどれか)[+/-][x][.][&]
 +/-は#/♭、xは長さ(全音符の何分の1か)、"."は符点(長さ1.5倍指定)、&はタイ/スラー
 例)C4 --- 四分音符のド、D+2. --- 符点二分音符の#レ
  B2&B8 --- 二分音符+八分音符の長さのシ
・R: 休符
 形式:R[x][.]
 xは長さ(全休符の何分の1か)、"."は符点
・O[x]: オクターブ(デフォルトは4)
・L[x]: 長さ省略時の音符/休符の長さ(1-64、デフォルトは4)
・V[x]: 音の大きさ(0-100、デフォルトはチャンネル0が100、チャンネル1が75)
・T[x]: テンポ、1分当たりの4分音符の数(デフォルトは120)
・@[x]: 音色(0-127、デフォルトは0(グランドピアノ))(参考資料
・<,>: オクターブを1つ下がる/上がる
・(): 和音
 形式:([C/D/E/F/G/A/B/+/-/>/<]の羅列)[x][.][&]
 例)(CEG)4 --- 四分音符の長さでドミソを同時に鳴らす
  (A>C+E) --- ラ、1つ上の#ド、ミを同時に鳴らす
・","(カンマ): チャンネル区切り(チャンネルは2つのみ)
 例)C8D8E8D8C4,E4F4G4 --- チャンネル0でドレミレド、チャンネル1でミファソ

もちろん独自仕様である。いわゆる作者 is Godである。

1行の中に","で区切って2つのMMLを書くと、それぞれが別のチャンネルで同時に鳴る。
同じ行の2つのMMLは必ず同時に始まる。一方が短い場合、長い方が終わるまで次の行が演奏されない。

エラー処理は極めて曖昧である。というか、ほとんど何もしていない。不正な文字列を入力や30和音などの無茶な指定をした場合にきちんと鳴るかどうかは無保証である。

ブラウザ上でなく、ローカルのファイルシステムから実行すると、演奏する前にMIDIデータ(SMF形式)がsimple_mml.midというファイルに保存される。

スライドバーの音量設定は、次の演奏から有効になる。


以下は、javax.sound.midiに関する覚え書きである。
・概略
SequenceにTrackを作って、Track#add()で時系列のMidiEventを登録して、Sequencerに渡してSequencer#start()を呼ぶとMIDIシーケンスが演奏される。
MidiEventは、ShortMessageのNOTE_ONがある音階の鳴り始め、NOTE_OFFが鳴り終わりである。ピアノの1つの鍵を押すのと離すのに相当する。音階は0〜127の整数で、60が基準のド、61がド#、62がレ…である。

・テンポについて
JDK 1.6のドキュメント(API spec, jdk-6-docs/technotes/guides/sound/)にもMIDI 1.0の仕様書(MIDI 1.0 Spec, SMF spec)にも見つけられなかったので正しいやり方かどうかわからないが、筆者のSun JRE1.6の環境では、javax.sound.midiでもmeta eventのFF 51で四分音符の長さを設定できるようである(デファクトスタンダードの類?)。MetaMessageで登録する場合は、type=0x51とし、 dataはbig endianの3バイトで、四分音符の長さをμ秒で指定する。
このメタイベントを使う場合は、Sequenceを作る時のdivisionTypeをPPQとする。
このメタイベントを使う以外の方法としては、divisionTypeをSMPTE_30、resolutionを100とかにしてtickの長さを絶対時間にして(この引数だと1 tickが1/3000秒になる)テンポに応じて音の長さをtickの整数倍で指定していく方法しか思いつかなかった。この方法だと誤差が大きいし、tickの値がすぐ大きくなるのでMIDIシーケンスのデータが長くなってしまうので、好ましくない。

・演奏停止について
Sequencer#stop()を呼んでも、既に鳴り始めた音は鳴り止まないことがある。確実に音を止めるために、Sequencer.close()を呼ぶようにした。
また、シーケンスが終了しても最後の音が鳴り続けることがあるので、シーケンスの最後にメタイベントのFF 2F(トラック終端)を付けて、それをMetaEventListenerで受けて、音を止めるようにした。

続きを読む "簡易MMLプレーヤー" »

2009年04月14日

JScrollPaneを使ったミニゲーム

どうしてもJScrollPaneを使ってスクロールする何かを作ってみたくなった。

Javaアプレットの起動ページへ
ソースコード

2009年04月20日

ポリゴンできれいな球を作るのは難しい

ポリゴンで球を作ろうとすると、真っ先に考えるのは、地球儀の緯度・経度のように2つの角度で球面上の点を表す極座標モデルであろう。しかし、それだと、赤道の辺りのメッシュが荒く、極点の辺りが細かくなり、球面が均一にならない。何とか、なるべく均等な面積のポリゴン片で球を作れないものか…と考えてみた所、次のような方法を思いついた。

球面上の3点からなる三角形の各辺の中点を取り、それらの間を結んで、三角形を4等分する。そのまま、前述の中点を球面に張り付かせるように動かす。

上から、元の三角形、4等分して球面に張り付かせた4つの三角形、そのポリゴンに色をつけたものである。合同な三角形から成る正八面体から始めて、これを繰り返していけば、均一な球面ができるのではないか?と期待して、やってみた。

テストアプリ(Java3D)の起動ページへ
ソースコード
画面サンプル

右から反時計回りに、8, 32, 128, 512, 2048, 8192個の三角形ポリゴンからなる球であり、手前が線で表示したもの、奥が面で表示したものである。

うまくできているようにも見えるが、細かくしても正八面体の面影が残っている。よく見ると、元々頂点だった所が密で、面の中心だった所が疎であり、くっきりと境目がある。これは、上記の方法で三角形を4等分して各頂点を球面に張り付ける時に、真ん中の三角形の面積が他の3つより大きくなるのが原因である。このことは、三角形の辺の中点が球面から遠い、赤道に近い位置の大きな三角形で考えてみるとわかりやすい。
正八面体に代えて正四面体で始めてみると、その影響が顕著になる。

テストアプリ(Java3D)の起動ページへ
ソースコード
画面サンプル

こうなると、却って緯線・経線の四角形ポリゴンで作る球の方が自然に見えてしまう。

テストアプリ(Java3D)の起動ページへ
ソースコード
画面サンプル

何かいい方法がありそうな気がするのだが…

続きを読む "ポリゴンできれいな球を作るのは難しい" »

2009年04月26日

続・ポリゴンできれいな球を作る

先週は面積が均一なポリゴンで球を作れなかったので、Java3DのプリミティプであるSphereクラスの球を参考にすることにした。

テストアプリ(Java3D)の起動ページへ
(今回のはカーソルキーの左右とPageUp/PageDownキーで回転します)
ソースコード
画面サンプル

Sphereクラスのコンストラクタのdivisionsの値を変えることにより、ポリゴンの密度を変えることができる。右端から反時計回りに、divisionsの値が4,8,12,16,24,32の球である。
それぞれの三角形の面積が大体等しい、きれいな球面である。どういう仕組みなのだろうか。

まず、試しにdivisionsの値を変えながら重ねて表示してみると、5,6,7,8は同じ球、9,10,11,12も同じ球であることがわかった。どうやらdivisionsの値は4の倍数に切り上げられて使われるらしい。
また、divisions=4だと正8面体で、divisionsがいくらでも、その正8面体の辺を球面に投影した(球と同じ半径の)3つの円周で球面が8分割されていることがわかる。さらに、その3つの円周の1つと平行な円周で球面が輪切りにされていることがわかる。

より細かい分割方法の特徴を明確にするために、divisions=28の球面でさらに調べてみる。(ここまでで既に出てきた3や8の倍数は避け、divisions=20は正20面体と関係するかも知れないので避ける)

赤い線で囲まれた部分が、球面の1/8である。赤い線は内接正8面体の頂点を結ぶ円周の弦、水色の線は下の方の赤い線と接する円周(以下、赤道)と平行な円周(以下、緯線)の弦である。
赤道と残り2つの経線上に、それぞれ赤い線分が7つある。おそらく、divisionsの値は赤道や残り2つの経線を何等分するかという値であろう。この1/8球面において、赤道は7分割、その上の緯線は6分割、その上は5分割、以下同様に分割されている。1/8球面を三角形に見立てると、三角形を各辺に平行な線で等分割する要領である。

という訳で、その三角形を等分割する点(線形補間)を球面に投影した点でポリゴンを作ってみる。
テストアプリの起動ページへ
ソースコード
画面サンプル

大きな三角形の頂点の辺りは密で、辺の辺りは疎になった。先週のエントリーで書いたのと同じ問題である。やはり線形補間だとうまくいかない。

そこで、Sphereクラスに習って緯線に沿った円周補間に変える。
…と思ったが、クォータニオンを使った球面補間なら上のソースコードのVector3fをQuat4fに変えるだけだし、球面補間の方が1/8球面を均等に分けられるはずなので、球面補間にする。(Sphereクラスも球面補間か?)
テストアプリの起動ページへ
ソースコード
画面サンプル

うむ、丸い。

続きを読む "続・ポリゴンできれいな球を作る" »

2009年05月06日

Java3Dで正多面体を作ってみた

Java3Dのシェーディングがよくわからないので、勉強しようとして、そのための実験材料として、ちょっと多面体でも作ってみようと思った。正多面体というのはn=4,6,8,12,20の正n面体しか無いが、正8面体以下は点光源や平行光源の光が当たる面の数が少ないので、正12面体と正20面体にすることにした。

…ら、えらく手こずって、GWの後半をこれだけで潰してしまった。光源とか反射率とか色々実験する計画だったのだが、時間切れ及び肩凝り等のため、一区切りする。

Javaアプレットの起動ページへ
・ソースコード
LightTest1.java
MyHedron.java
MyHedron12.java
MyHedron20.java

・サンプル画像
LightTest11.png

LightTest12.png

ついでに、各面の中心を凹ませたり凸ませたりしてみた。

多面体の作成は、正12面体と正20面体の各面の法線を求めた上で、各面の中心と頂点を結んで三角形ポリゴンを作る方法で行っている。
正12面体の1つの面をXY平面と平行にすると、その面の法線は(0,0,1)であり、その面と隣の面とがなす角度をθとすると、tanθ=2である(説明は省く。調べると所々に出てくる63.4349...というのがこのθである)ので、(0,0,1)を360÷5=72°回転させると別の1つの法線が求まり、それをZ軸周りに72°ずつ回転させていくと、正12面体の上半分の法線が全て求まる。下半分は、それを逆さにしてZ軸周りに36°回転させて上半分とくっつけると求まる。
正12面体の天面の頂点の1つをX軸上に取ると、そのX座標は、sqrt((3/4) G/sqrt(5)) となる(Gは黄金比=(sqrt(5)+1)/2)(いい説明が思いつかないので説明は省く)。正12面体の各面は五角形なので、これを正12面体の法線で72°ずつ回転させていくと、全ての頂点が求まる。
正12面体の面と頂点を入れ替えると正20面体になるので、正20面体の頂点は正12面体の法線ベクトルのX,Y,Z成分をX,Y,Z座標にすることで得られ、正20面体の法線は正12面体の頂点の座標をベクトルの成分とすることで得られる。

光源は、環境光(赤)と平行光(青)を設定し、なんとなく点光源(黄)を回転させてみた。

続きを読む "Java3Dで正多面体を作ってみた" »

2009年07月05日

Java3DのInterpolatorを使ってみる

アニメーションの制御をしてくれるInterpolatorは、デフォルトの設定で使うなら簡単で便利この上ないのだが、ちょっと思い通りに動作を変えようとすると、途端に調べる事と考える事が増える。お仕着せのシンプルなI/Fは融通が利かないというのは世の常である。簡単に使えるようになっているものでも、結局どういう時にどれくらい便利なのか、不都合な副作用が無いのかどうかを綿密に調べる羽目になることはよくある。

過去に何度か、簡単なアニメーションのためにInterpolatorを使おうとしたが、結局はやりたいことをやる方法がわからなくて、諦めてWakeupOnElapsedTimeを使って時間毎の処理を自前で書くことにしてきた。

しかし、賢い人達が練り上げた、OpenGLより上位のJava3DのI/Fが、不便であるはずが無い。必ず理由があってこのようなI/Fになっているはずである。私はすっかりSUNの信者である。使いこなせば便利で安全で堅牢で可般で最適で信頼性が高いに違いないことは、宇宙の真理として決まっているのである。
それにしてもInterpolatorを使ったアプレットはよくクラッシュする。
という訳で、一度きちんとInterpolatorを使う練習をしてみることにした。

テストアプリの起動ページへ
ソースコード

今回は、
・PositionInterpolator
・RotationInterpolator
・ScaleInterpolator
・ColorInterpolator
・TransparencyInterpolator
を使ってみた。

Interpolatorを使う上で最も多く問題になるのは、おそらく、アニメーションの周期や時間毎の動き方を決めるAlphaが作れるかどうかであろう。一定の速度で一方向に動かすだけなら簡単だが、加速・減速させたり、端まで行ったら折り返したり、端まで行ったら直角に進路変更させたりしようとすると、AlphaのI/Fをよく理解しないといけなくなる。

Alphaのコンストラクタには、一番多いもので10個の引数がある。

Alpha(int loopCount, int mode, long triggerTime, long phaseDelayDuration, long increasingAlphaDuration, long increasingAlphaRampDuration, long alphaAtOneDuration, long decreasingAlphaDuration, long decreasingAlphaRampDuration, long alphaAtZeroDuration)

これの5つ目以降が、移動、加速、減速に関するものである。
Alphaというのは基本的には最小値(default=0.0)から最大値(default=1.0)までの間を動く値である。Alphaの動きは、次のようになる。

時刻速度時刻(パラメータ表記)
(1)最小値0(1)
加速(+)
(2)max(+)(1) + increasingAlphaRampDuration / 2
等速(+)
(3)max(+)(4) - increasingAlphaRampDuration / 2
減速(+)
(4)最大値0(1) + increasingAlphaDuration
停止
(5)最大値0(4) + alphaAtOneDuration
加速(-)
(6)max(-)(5) + decreasingAlphaRampDuration / 2
等速(-)
(7)max(-)(8) - decreasingAlphaRampDuration / 2
減速(-)
(8)最小値0(5) + decreasingAlphaDuration
停止
(9)最小値0(8) + alphaAtZeroDuration

AlphaのmodeがINCREASING_ENABLEの時(default)は(1)-(5)が繰り返され、(INCREASING_ENABLE | DECREASING_ENABLE)の時は(1)-(9)が繰り返され、DECREASING_ENABLEの時は(5)-(9)が繰り返される。
従って、端まで行ったら折り返す、というのは、Alphaのmodeを(INCREASING_ENABLE | DECREASING_ENABLE)にすればできる。

残りの引数については、
loopCount: 繰り返しの回数(-1だと無限)
triggerTime: Alphaが内部で動き始める時刻
phaseDelayDuration: Alphaが内部で動き始めてから値が動き始めるまでの時間
のようである。

続きを読む "Java3DのInterpolatorを使ってみる" »

2009年08月08日

8方向に掘り進めて迷路を作ってみる

昔、MSX-FANという雑誌に投稿されていたBASICのプログラムで、壁を8方向に掘り進めて迷路を作り、その一部分が画面に表示された状態でその中をさまようというゲームがあった。

ランダムな迷路を作る方法としては、格子状の壁を4方向に掘り進める方法はすぐに思いつく。既に掘り進めた通路のいずれかの壁をランダムに選び、その壁を取って通路が1マスだけ伸びるならその壁を取り払う(壁の向こうに既に通路があるとループができてしまうため)というのを繰り返せば良いのである。選んだ壁の向こうが未踏の地ならその壁を外す、というのを繰り返すとも言える。
他に、通路が4方向に進む迷路を作る方法としては、「棒倒し法」なる奇妙な方法があるようだが、いたずらに複雑な上にいびつな迷路になるので、取るに足りない。

しかし、雑誌に載っていたプログラムは、迷路を8方向に掘り進めた。そういうことができること自体が私にとっては不思議だった上、そのプログラムは今考えても戦慄が走る「1画面プログラム」すなわち40文字x23行に収まる激短プログラムだった。

プログラムのコードは見えていたのである。大概のプログラムは、手で打ち込んでいる内にどういう仕組みかは理解できた。理解できないのはマシン語が混ざっているものくらいだった。ALL BASICで手で打ち込んで実際に動かしてみてアルゴリズムが理解できなかったプログラムは、そのプログラムくらいしか記憶に無い。
加えて、その作者は当時の私と同じ奈良県民らしかったため、忘れられなかった。

それから約20年、そのプログラムのことを時々思い出しては、頭の中で考えて、その迷路を作る仕組みを不思議に思っていた。

昨日、またふとそのプログラムのことを思い出して、図を描いて考えてみたら、3分でわかった。

テストプログラム(Javaアプレット)の起動ページへ
ソースコード

ついでに自動的に解かせてみた。

要するに、隣り合う斜めの通路が接しないような幅で通路を掘れば良いのである。他に気を付けるのは、斜めのマスに通路を伸ばす時は直交する通路が無いことを確認することくらいで、基本的には4方向に掘り進める場合と同じように方眼のマス目を繋げていけば実現できる。

続きを読む "8方向に掘り進めて迷路を作ってみる" »

2009年12月31日

ペンローズ・タイル

ペンローズのタイルというものがある。鋭角72°、鈍角108°の同じ大きさの菱形を

このように分けてできる2種類のタイルで、同じパターンを繰り返すことなく、平面を無限に隙間なく敷き詰められるというものである。繰り返し無しというのがポイントで、例えば2種類のタイルを上の図のように合わせると菱形であるから、その菱形を繰り返し並べると無限に敷き詰められるのは当たり前である。そうではなくて、どの一部分を取っても、全体がそれを繰り返し並べたものではないように、平面を敷き詰めることが可能なのである。

10年以上前に、それを何かで読んで、ボール紙をはさみで切って試したことがある。いつかプログラムを作って自動的に埋めさせてみたいと思って、ついこの間までそのボール紙を取ってあったが、実現しないまま、もういいかと思って捨ててしまった。そしたら、今月、某TV番組でペンローズ・タイルが紹介されたのを見て、過去の思い出が蘇り、日々そのことが気になるようになってしまった。
頂点に並ぶタイルは最大5枚だから、単純に探索する順をランダムにして5手先まで縦型探索するのを繰り返せば良いのではないか…と思って、意図的かつ不可避に1人で過ごすことを選択したクリスマスイブの前日にその選択が正解だったことにするためにトライしたら、うまくいかなかった。やっぱり何十手も先まで読まないとNGとわからない置き方があるのと、それ以前に重なり判定が難しすぎるのである。(実際にイメージバッファに描きながら次に描く所が未使用かどうかを調べる方法でも難しい)

年を越す前になんとか一悶着、いや一段落を…と思ってインターネットを読み漁ったら、プログラむのにうってつけの簡単な方法が見つかった。
Conway's concept of "inflation" and "deflation"

"inflation"は少し面倒なので、"deflation"と切り抜きで繰り返せるようにしてみた。
"deflation"によるペンローズ・タイリングのJavaアプレットの起動ページ
ソースコード
一応、"deflate"と"zoom"を繰り返すと、無限にパターンが作れるはずである。

続きを読む "ペンローズ・タイル" »

2010年01月09日

Java3Dの光源と物体の表面属性を操作してみる

Java3Dで物体に色を付けるには、光源と物体の表面属性を設定する必要がある訳だが、光源色と表面属性の色成分の関係がよくわからない。光源色を変えても色が変わらなかったり、物体表面の反射成分を変えても色が変わらなかったりする。いろんなパラメーターをいじって試しても、物体が真っ白のままで何が悪いのかさっぱり見当がつかないと、全く理解が進まない。筆者はもう1年以上わけがわからないままである。

そこで、光源色と物体の表面属性をスライドバーで操作できるアプレットを作ってみた。

光源/表面属性のテストアプレット
ソースコード

点光源が上の方を旋回しており、平行光源は左手前にあって右奥に向けている。2つの小さな円錐は、光源のおおよその位置を表している。

操作できるパラメーターを説明する。

●光源について
・環境光源(Ambient light)
 空間全体に万遍なく存在する光。影を作らない。
・平行光源(Directional light)
 ある一定の方向から全空間を等しく照射する光。
・点光源(Point light)
 ある一点から放射状に放たれる光。光源からの距離に応じて減衰する。

●物体の表面属性(材質、Material)について
・拡散反射成分(Diffuse color)
 入射角に関係なく全方向に反射させる色成分。
・鏡面反射成分(Specular color)
 入射角と反射角の大きさが等しい反射をさせる色成分。
・環境光反射成分(Ambient color)
 環境光を反射させる色成分。
・発光色(Emissive color)
 光源に関係なく、自ら発する光の色成分。
・つや(Shininess)
 鏡面反射の鋭さ。

大体、自然界と同じように、光源色の一部の成分が反射光となる感じである。白色光に対しては、赤色光のみを反射する物体は赤くなるし、緑色光のみを反射する物体は緑色になるが、青色光に対しては、赤色光や緑色光のみを反射する物体は光を反射せず黒くなる。

バーをスライドさせても物体が真っ白のまま変化しなくても、根気よくいろんなバーをスライドさせていれば、どれかを触った時にきっと色に変化が起こる。
どうやら有効なパラメーターはTarget colorによって変わるようであるが、まだ関係がよくわからない。そのうち、ちゃんと理屈を知ってからいじって理解しようと思う。今回はこれを作っただけで満足する。


今回のアプレットでは、Java3Dのシーングラフ(オブジェクトの木構造)を途中で変更している("Change shape"ボタン押下時)。それに関して調べたことを記録する。
・J3Dスレッドが使用中のシーングラフから削除できるノードはBranchGroupノードのみである。従って、後で切り離したいノードは切り離し用のBranchGroupノードの下に配置する必要がある。
・シーングラフを操作するためには、一度Universeから切り離す必要がある。そのため、シーングラフにはALLOW_DETACHを設定しておく。
・SimpleUniverseの場合、シーングラフのdetachは

SimpleUniverse#getLocale()#removeBranchGraph()

で行う。その後のattachは通常のSimpleUniverse#addBranchGraph()で良い。
・BranchGroupノードに繋がる子ノードを追加したり削除したりするためには、そのBranchGroupノードにALLOW_CHILDREN_WRITEを設定する必要がある。
・AppearanceやMaterialのインスタンスは、シーングラフをdetachせずに変更してもJ3Dの描画に反映されるようである。

続きを読む "Java3Dの光源と物体の表面属性を操作してみる" »

2011年05月29日

JavaアプレットでSVGを再生する

ベクターグラフィクスのフォーマットに、SVGというのがある。GIMPやDiaなど、画像をベクターグラフィクス形式で保存することをサポートしている場合、そのフォーマットはSVGであることが多い。SVGがオープンな規格であることがその理由であることは想像に難くない。特に理由が無ければSVG形式を採用することになるのであろう。
SVGは、フォーマットがXML形式であり、それでいて、Adobe Flashと同様、アニメーションや、マウス操作等の入力に対する動作を記述することもできる。何て理想的でパーフェクトなのであろうか。
アニメーションするベクターグラフィックだとFlash形式が使われることの方が圧倒的に多いが、特に静止画像だとSVGはWikipediaでもよく使われており、マルチページで文書用途のPDFやPostScriptを除くと、既にデファクト・スタンダードになっていると思う。

筆者としては、SVGはテキストエディターで編集できるというのが特にポイントが高い。EmacsやPerlやPythonで扱い易いかどうかというのは根本的な問題であり、それを満たすものがあるならそれを満たさないものは無いのである。よって、筆者としてはFlashは無いのである。

しかしながら、SVGは環境によっては正しく再生されないことがしばしばある。筆者の自宅にあるWindows XPでは.svgファイルをダブルクリックしても開かないし、Internet Explorerでは、表示されてもアニメーションは動かない。Mac OS X 10.5のSafariではアニメーションも大体動くようだが、実に惜しいことに、α付きのフィルターが働かないようである。そんなのは些細なことだという話もあるが、たまたまそれを試したくてSafariでプレビューしながらSVGをEmacsで書いて思い通りに表示されず、何が間違ってるのだろうと悩んだ果てにSafariがそれに対応していないと知った時の心の傷は、深かったのである。

そんな訳でSVGの別の再生方法を探していると、Batik SVG ToolkitというJavaのパッケージを見つけた。まあまあ再生の成功率が高いようである。このサイトにあるMoving GearsというSVGのアニメーションが正しく表示されるのは、筆者が試したフリーのプレーヤーの中では、このBatikに含まれるSquiggleというプレーヤーだけであった(同サイトで指定されているOpera 9やAdobe SVG Viewer(サポート終了)は試していない)。
しかし、わざわざSVGを再生するのにJavaを起動することは無かろう、とパスしようとしたが、JSVGCanvasのデモのページを見て、気が変わった。何と、SVGファイルが、HTMLのフォームへの入力に従って書き換えられながら再生されているのである。

Webブラウザで表示されている画像ファイルそのものがユーザーの入力に従って変化するというのは、意外と難しい。HTMLのsubmitボタンやJavascriptによりユーザーの入力がサーバーに送信され、サーバー側プログラムが動的に画像ファイルを生成すれば、似たようなことができるだろうが、クライアントサイドで画像ファイルを書き換えながら再生できるのは、プラグインのプレーヤーならである。また、再生時の画像ファイルの加工方法として、例えばFlash VideoのメタデータをActionScript(Flash Videoファイル内の)やJavascriptで書き換えながら再生するというような、付属的なメタデータを動的に変更することはよくなされているようだが、上の例では、SVGファイルの相当コアな部分にあるベクトル図形が、Javaのコードで、しかも単なるXML APIで書き換えられているのである。

これは、筆者のようなJavaをかじった者に、触ってみろと言っているに違いない。

SVG書き換え再生テストの起動ページ
ソースコード
SVGファイル

Restartボタンを押すと、SVGファイルのid="TheString"の要素のテキストが書き換えられる。


それだけのことであり、SVGファイルを読み込んで再生する部分はJSVGCanvasのデモのページAppletDemo.javaから丸ごと拝借したのだが、実際に作ってみると、色々あった。

まず、アニメーションの再生終了後にSVGファイルをリロードせずにもう一度最初から再生する方法がわからなかった。
それくらいのI/FはJSVGCanvasクラスにあっても良さそうに思ったのだが、それらしいのは見当たらず、実際にいろんなメソッドを呼んでみたが、再開はしなかった。
結局、batik-users MLのアーカイブで見つけた、SVGAnimationEngine#setCurrentTime()を使う方法でできたが、そのメールでもこれが本当に正しいかどうかわからないと書いてあるし、Batikのjavadocを読んでも妥当性がさっぱりわからない。


次に、Safariでは発生しなかったが、IEでは、ネットワーク越しのSVGファイルのロード開始後になかなかアニメーションの再生が始まらないという問題が起こった。
当初はHTTP上のXML parserが何らかの理由で完了しないからかと思ったが、ファイル全体をStringとして保存してからparseするようにしても、さらにSVGファイルをJARに含めてそれを読み出すようにしても、直らなかった。
SVGファイルが参照するファイルの読み込みに時間がかかるのかと思って、svg11.dtd等の参照ファイルを全てローカルに置いても変化が無かった。
たまたま、JavaコンソールでGCさせるとアニメーションが開始することに気付いたので、色々なイベントリスナーでSystem.gc()を呼ぶようにしてみたが、効果が無かった。しかし、苦し紛れに、スレッドを作ってSystem.gc()を繰り返してみたら、すぐに再生が始まるようになった。
従って、やはり何らかのタイミングでSystem.gc()すれば良いのだと思うのだが、改めてSVGLoadのlisterやSVGLoadEventDispatcherListenerなど色々なイベントリスナーで、invokeLaterやsleepもしながらSystem.gc()してみたが、適当な場所は見つからず、結局別スレッドでGCする以外では解決しなかった。

後に、batik-users MLのアーカイブに詳しい原因が書いてあるのを見つけたが、Batikを改造しなくて済む回避策は書かれていなかった。Subversionには何らかの修正が入れられたようだが、それはBatik 1.7のリリース後のことのようである。


さらに、アニメーションが始まるまで要素を表示しない方法ではまった。
今回のSVGのテキストの下の角丸矩形は、テキストによって長さを変える必要があるので、その長さをECMAScriptで設定するようにした。そのため、それが呼ばれるまでは矩形の長さが不正であり、アニメーション開始前から表示するとちらつきが発生してしまう。
当初は、SVGの各要素をvisibility="hidden"にしておいて、onload時に矩形の長さを設定し、アニメーションのonbegin時にscriptでsetAttribute("visibility", "visible");するようにしていたのだが、Safari 5.0では<animation>タグのonbeginが効かないことが判明した。MacではSVGファイルをダブルクリックするだけでSafariが開いてアニメーションがほぼ完璧に再生できるので、SafariでレビューしながらEmacsでSVGを編集していた。

今回、その便利さを諦める気になれなかったので、Safariに合わせて、onbeginを使わないことにした。
それではと、onload時にvisibleにすると、それが矩形のサイズ変更後であっても、ちらつきが発生してしまったり、アニメーションの開始まで間があると画面が固まって見えてしまったりした。
アニメーションの開始直前にvisibleにすることができないのであれば、アニメーションの途中でvisibleにするしか無い。<animation>タグでvisibleとかinvisibleとかの定性値を変化させることもできるようだが、今回はopacityを0から1に変化させることで徐々に見えるようにした。

その前に、scaleが1だと小さく表示されるような座標で作って、アニメーション中にscaleを増やすことにより、アニメーションが始まるまでは不正な表示になっても目立たないようにしていたのだが、アニメーションの最後にopacityを変化させてフェードアウトするようにすると、Batikでもう一度最初から再生するとopacityが1に戻らず、opacityを変化させるまで表示されないということが起こってしまった。
SVGファイルの作り方の定跡は調べていないが、小さく表示してごまかすのは災いの元だと教えられた。


さらに、Batikで単にSVGAnimationEngine#setCurrentTime()でリピートするとonload時のスクリプトが実行されない問題があった。
SVGファイルのECMAScriptの関数を明示的に呼び出す方法がわからなかったので、ScriptingEnvironment#dispatchSVGLoadEvent()でSVGLoadイベントを発生させてonloadのスクリプトが実行されるようにして解決した。
SVGファイルの作り方の常道は知らないが、アニメーションの時刻を戻す可能性があるなら、時刻変更の度にonloadの処理を実行しなくて済むよう、初期化処理はonloadでせず全てonbeginでやる方がいいのかも知れない。

続きを読む "JavaアプレットでSVGを再生する" »

2011年07月18日

JavaアプレットでSVGを再生する(2)

以前にBatikを使ってJavaでSVGを描画したり書き換えたりしてみたが、特にWebブラウザ上で動かすJavaアプレットを作成する目的ではあまり満足のいく結果が得られなかったので、もう1つ気になっていた、軽量が売りのSVG Salamanderも使ってみることにした。

SVG書き換え再生テストの起動ページ

ソースコード
svgSalamanderTest1.java アプレット本体
SVGIconOnJPanel.java アイコン描画用にダブルバッファリングを有効にしたJPanel
BouncingIcon.java
SVGファイル
txtanitest1s.svg 文字列書き換えテスト用SVG
p_icon.svg アイコンのSVG

前回と同様に、SVGの文字列を動的に書き換えて再生する。このSVGの表示には、JPanelを継承していてJAppletに組み込みやすいSVGDisplayPanelクラスを使っている。
それとは別に、SVGIconクラスはjava.awt.Graphicsに描画できるので、Swingのコンポーネント上も含めて、アイコンを動かしながら表示してみた。
ちなみに、Popボタンを短時間に連打するとアイコンが初期サイズまで戻らなくなるのは、仕様である。


以下、今回何か作ってみてわかった、SVG Salamanderを使う上での注意事項をまとめる。

まず、再生可能なSVGはかなり限られると思っておいた方が良い。SafariやBatikのSquiggleでは思い通りに再生できるのにSVG Salamanderでは思い通りに再生されないということは頻繁に起こった。それがSVG的に正しくない(SafariやBatikは気を利かせて補間している)からなのかSVG Salamanderの対応が不十分なのかはあまり追究していないが、何せ、SVG Salamanderを使うなら、svgSalamander.jarを実行すると起動するSVGプレーヤーで再生確認しながら、SVGファイルを編集することになる。

SVG Salamanderの描画がSafariやBatikと違ってしまう要因の1つは、ECMAScriptが使えないことである。<script>タグが含まれるSVGファイルはSVG Salamanderでは正しく再生されないのである。動的なSVGコンテンツにはよくECMAScriptが使われるので、どこかから拾ったSVGはこれが原因で再生されないことが少なくない。

他に、SVG SalamanderのSVGの扱いに関して筆者が気づいたこととして、パスデータ(<path>タグのd属性などで与えられるもの)に、"C"/"c"(curveto)や"Q"/"q"(quadratic Bézier curveto)が先行せずに"S"/"s"(smooth curveto)や"T"/"t"(smooth quadratic Bézier curveto)があると、まず期待した動作にならない。SafariやBatikでは1つ目のcontrol pointを始点(またはその閉パスの最後のcontrol point?)とすることで突拍子も無い表示になることを避けているようだが、SVG Salamanderは2つ目のcontrol point或いは終点の、始点からの反対方向の鏡像点を1つ目のcontrol pointとしている節がある。


次に、Javaライブラリに関して気付いたことをまとめる。

SVGのテキスト要素の文字列の先頭や終端に余分な空白があると、描画時とText#getBoundingBox()実行時とで空白の扱いが異なってしまうようで、内部の計算位置と表示位置にずれが発生してしまう。これはSVGファイルの<text>〜</text>で括られたテキストの先頭や末尾の改行や空白についても同じことが起こるので、Javaコード内だけでなくSVGファイル内でも空白文字を消しておく必要がある。

SVGにフォントサイズのアニメーションがあると、SVGUniverse#updateTime()では表示に反映されないようだ。内部的に値は更新されるようで、Text#rebuild()すれば文字サイズに反映されるが、透明度のアニメーションと組み合わせると、ちらつく上、Text#rebuild()時に変化中の透明度が無視されて不自然な表示になった。

SVGElementクラスにhasAttribute(), setAttribute()があるのにgetAttribute()が無いのが謎である。現在の値からいくらか変化させるということができないことを意味する。
また、現実的な問題として、printfデバッグのために、その時点での属性の値を取得したいということはよくあったが、その手段が無かったのが結構辛かった。

それから、色々なクラスのset系のメソッドの引数にある、attribTypeの意味がわからない。JavadocのAPI referenceには与え得る値も何も書かれていない。今回は"Using SVG Salamander"のページのサンプルコードに書かれているAnimationElement.AT_XMLを一律に指定しているが、これで正しいのかどうかはわからない。

BatikのJSVGCanvasはSVGの表示位置が自動的に中央寄せになるが、SVG SalamanderのSVGDisplayPanelでは左寄せになってしまう。しかも、中央寄せにするメソッドが無い。SVGファイルの<svg>タグのviewBox属性を書き換えれば位置調整ができるが、メモリにロードしたviewBox属性を更新する手段がない。SVGDiagram#getRoot()で取れるSVGRootのprivate属性にはあることがわかったが、getAttribute()メソッドが無いので、値を取得することすらできない。
今回は、SVGDiagramのgetViewRect()とgetDeviceViewport()で得られた座標を元にSVGのviewBox属性を直接書き換えるという強引な方法で、何とか中央寄せができた。(svgSalamanderTest1.javaのcenterViewBox()参照)


最後に、Webサーバーに配置する時の注意点として、svgSalamander.jarはSVGプレーヤーが内蔵されていたり署名されていたりするので、そのSVGプレーヤーも署名する必要も無ければ、ライブラリとしての実行用にはsvgSalamander-tiny.jarを使うと良い。

続きを読む "JavaアプレットでSVGを再生する(2)" »

2012年02月19日

Swingでオセロを作成中

そういえば、これまでの人生において、思考ルーチンというやつを完成させたことがないことに気付いたので、一応、簡単に履修しておくことにした。

…ら、GUIを作るのだけで結構手間取ってしまった。マウス入力とアニメーションと思考ルーチンを並列動作させようとしたのが、思ったより大変だった。

肝心の思考ルーチンはほとんど空だが、とりあえず最後までゲームができるようになったので、一旦ここに置く。

Swingオセロの起動ページ

ソースコード
OthelloApplet.java
・残りは作成中

続きを読む "Swingでオセロを作成中" »

2012年03月21日

αβ探索だけのオセロの思考ルーチン作成

前回作りかけだったオセロの思考ルーチンの核として、とりあえず、最も基本的なゲーム木の探索アルゴリズムであるαβ探索を実装してみた。
正確に書くと、前回もαβ探索をしていたつもりだったのだが、バグが取れず、うまく動かなかった。

今日、このページのコードのような形に書き直したら、一発できちんと動いた。
すると、それだけで、特にオセロが得意でない筆者は全くコンピューターに勝てなくなってしまった。

Swingオセロの起動ページ

ソースコード
OthelloApplet.java
State.java
GameTreeNode.java
Evaluator.java


単純なゲーム木の探索ではまともな結果は得られないと思っていたので、局面の良し悪しを決める評価関数をうまく作る取り組みが次に必要だと覚悟していたのだが、今回の目的は強いプログラムを作ることではなくきちんと動く思考ルーチンを作ることであり、筆者にはこれ以上コンピューターの手の良し悪しが分からないので、これで良しとする。

一応、この後評価関数の改良として取り組む予定だった内容をメモしておく。今は隅が取れてたら点数が高いだけの評価関数である。


  • 辺の評価
    "□□●●●●□□"や"□●●●●●●□"は良くて"□●●●●●□□"や"□□□○□□●□"は良くない(最後のは黒の左側に白を置かれると必ず隅を取られる)とかいうやつである。
    辺の攻防で優勢になることを狙う思考ルーチンにしたかったので、辺の状態について細かく評価値を設定したかったが、計算式で書くと結構複雑なので、今回は見送った。
    辺の状態は3^8=6561種類しかないので、事前にテーブル化しておくことが可能であり、計算するのに時間がかからないのである。
  • 序盤と終盤とで思考ルーチンを分ける
    終盤は勝ち負けを読み切ればいいだけなので、細かい評価は要らないのである。
  • 着手可能な選択肢が少なければ評価を下げる
    強い人は相手にパスさせようとすると聞いたような気がする。
  • 「偶数理論」
    よく調べてないが、最後の石を打てると有利という発想によるもので、自分の手番で残りマス数が偶数だったら評価を下げるとか、連続する空きスペースが偶数だったら評価を下げるとかいうことではないかと推測している。
  • 「開放度理論」
    周囲に空きスペースが多い石は、ひっくり返される確率が高いので、評価を下げるという考え方だったと思う。
  • 乱数を加える
    どうせそんなに強くならないだろうから、たまたまいい手を打つ確率が0でないように、おまじないを掛けようと思った。
  • コンピューター同士の対戦
    評価関数の良し悪しを決める方法は、2つの評価関数を対戦させることしか思い付かない。
    乱数の要素も加えて、いくつかの変化可能なパラメーターを用意して、何通りかのパラメーターのセットを用意して、総当たりで何回か(先後入れ替え含め)対戦させて、弱かったセットを捨てて、強かったセットに乱数を少し加えたものを新たな候補として用意し、また総当たりさせる、というのを繰り返していいパラメーターを探す、GA的な方法を考えた。
  • 展開しながらの探索中に全てのノードを残さない
    あまり探索木を広げるとメモリ不足で落ちるので、今はノード数がある程度に達したら計算を打ち切っているが、全体的に評価の悪い部分木は取り払うとか、途中のノードは評価値だけ残して盤面は消すとかして、メモリを節約する方が良い。
  • 未評価の盤面の扱い
    今は評価値の初期値を0にしてるので、評価した局面の評価値が全て負の値だと、反復深化で読み直す時に未評価の手を優先してしまう、怪しい動作になっている。

続きを読む "αβ探索だけのオセロの思考ルーチン作成" »

2012年09月01日

将棋の棋譜再生iアプリを作ってみた

昨年はまあまあ満足できた将棋大会に、今年も参加したら、全く思い通りに指せなかった。
将棋では、相手に押さえ込まれて何もできなくなるというのはよくあることだが、そういう感じではなく、筆者の指し手に相当な自由度、選択肢があったのに、さっぱり頭が回らなかったのである。
何も思い付かないまま、何もできないまま、持ち時間が無くなって、2敗して予選落ちしてしまった。大会規程により、今年はA級で参加せざるを得なかったのだが、あの調子だとB級でも散々だっただろう。
そんなことあり、もうちょっと将棋を勉強しようという気になった。

図書館から定跡本を借りて時々読んでいると、ちょっと気が向いた時に見れないことが多いことに気付いた。筆者は割と外出先にでも本を持ち歩く習慣があるのだが、確実な待ち時間が予想できないと持ち歩かないようであるのと、本は携帯電話と違って片手では開けないことが要因としてあるようである。
それでなくても、携帯電話でアプリを動かしていると、将棋盤を再現できるアプリがあればいいのにな、と思うのは自然なことであろう。

筆者が持っている携帯電話は、昨年の将棋大会の日に買った、ドコモのP-01Cである。iモードアプリしか動かない「ガラパゴス携帯」である。スマホは保有していない。
これで動く棋譜再生アプリが見つかれば良かったのだが、毎月費用が105円とか315円とかかかるものを除くと、適当なアプリが見つからなかった。1つ、棋譜管理に特化した、丁度いいアプリがあった(今はもう見つからない)のだが、大抵のiモード機種に対応してるのに、なぜかP-01Cは対応しておらず、ダウンロードもできなかった。

それに対して、スマホ用の棋譜管理ができる将棋アプリはごろごろ転がっているようである。この機会にスマホを購入しようかと思ったが、それだけの為にスマホを買うのはどうかと思った。

棋譜再生アプリを自分で作ろうか、と、ちょっと思い始めた。しかし、筆者はiアプリの作り方をほとんど覚えていなかった。というより、5年ほど前に簡単なものを1回か2回作って実機で動かすのを試しただけなので、覚えたことがある程でもない。もしやるとすれば、アプリの一挙手一投足に至るまで、DoJaのAPI仕様書を検索しまくることになる。

今や世界シェアの50%以上がスマホというこのご時世に、今更DoJaアプリの開発に挑戦すべきだろうか。今から覚えるならAndroidかiPhoneのアプリの作り方であろう。この機会にスマホを1台買おう、とも思ったのだが、スマホを買うと、上述の通り、自分で棋譜再生アプリを作る必要性が無くなってしまう。

それに、スマホを買うと、きっともうP-01Cを使わなくなってしまうだろう。Javaに思い入れがある者としては、このまま自作のJavaアプリを1つも作らずにP-01Cを使い終わることは、ケーキを半分残して腐らせてしまうくらいに勿体ないことなのである。そうなれば、将来必ず後悔するであろう。その前に、このままでは必ず、廃棄する直前に、供養の為にもiアプリを1つは作って動かさなければならないという強迫観念に襲われるに違いない。もはやあと1回iアプリを作ることは筆者の宿命なのである。

・スクリーンショット
screen shot
※この画面はiDKDoJaのエミュレーターで作成したもので、実機の画面とは異なる。
 実際には、盤面の駒も含めて、文字は機種毎のシステムフォントが使用される。
i-mode用ダウンロードページへのリンク
・ボタン操作
 1-9: 1手進める
 *: 1手戻す
 #: 次の分岐点まで進める、または前の分岐点まで戻す
 0: 初期状態に戻す
・ソースコード(iDKDoJaプロジェクトのsrc/に配置)
 iKifuPlayer.java
 Board.java
 KifTree.java
・作成者
 maruinen(筆者のネット将棋でのハンドル)
・棋譜データの例(iDKDoJaプロジェクトのres/に配置)
 Kifs.txt
 空行は1つの棋譜の終わりとして扱われる。ファイル名は固定。
・棋譜データの入れ替え方法
 iDKDoJaと上のソースコードを使って、アプリ自体を作り直すしか無い。

続きを読む "将棋の棋譜再生iアプリを作ってみた" »

2013年08月09日

iモード棋譜再生アプリを更新し続ける

明後日は、一昨年から参加している将棋大会である。
準備不足だった昨年は散々な内容だったので、それ以来、ちょっと本気で将棋に取り組もうかと思い、筆者が所有するドコモのガラケーで棋譜を持ち歩けるようにiモードアプリを作ったりしたものである。全ては明後日のためであった。
…と言いながら、結局、この1年間も、筆者の脳には十分に準備ができなかったので、やっぱり来年に向けて準備することにする。

自分がどうすれば強くなるかはわかっている。まずは、自分の戦法を決めて、相手がどう指してきても対応できるように定跡を覚えることだ。集中して先を漏れなく読むことも苦手なので、ある程度時間の長い将棋を指して読む訓練をすることも必要だが、序盤がまともに指せず、すぐに崩れると、同じくらいの棋力の相手とじっくり中盤を指す機会も得られない。
しかし、筆者は幼少の頃から、勝つことよりも奇抜な手を考えて楽しむことを目的に将棋を指してきたので、勝つ為に勉強をする習慣が無いのである。定跡本を読んでいても、すぐに飽きて、持ち時間を極端に短くできるYahoo!将棋での早指し対局に走ってしまう。
そもそも、筆者は昔から、覚えるための勉強が苦手なのである。おそらく、記憶力が悪く、覚えようとしても覚えられないから、覚える作業が嫌いになったのだろう。

そういうこともあって、1年前に自分の携帯電話用の棋譜再生アプリを作ったのである。覚えるべき定跡や棋譜を集める作業を怠っていたので、予定通りの準備はできていないが、暇な時にいつでも携帯電話で定跡の盤面やプロの棋譜を見ることは、思ったよりも学習効果を実感できることだった。同じ定跡局面を3ヶ月くらい繰り返し見ていても、最初は気付かなかったその手の意味に気付くなど、時々新たな発見があるものである。

今やすっかり、このDoJaアプリが動くことのみによって、ガラケーP-01Cが手放せなくなった。このアプリが動く限り、スマホに買い換える気がしない。無い物は作れば良いのである。DoJa万歳。
従って、この自作のiモード対応棋譜再生アプリをメンテナンスし続けることにした。

■変更内容

  • 異なる手順からの同一局面の選択肢を結合できるようにした。
    ▲2六歩△3四歩▲7六歩 で始まる棋譜の次の手が、▲7六歩△3四歩▲2六歩と辿ると選択できない問題への対策。
    携帯電話でiアプリ起動時に同一局面を検索すると時間がかかり過ぎるので、棋譜中にラベルを付けて同一局面として扱うことを指定できるようにした。
    結合先の局面に至る手の後ろに「#label 番号」を付け、同一局面に至る手の後ろに「#merge 番号」を付けると、双方の次の手がどちらからでも選べるようになる。
    例:
    ▲2六歩
    △3四歩
    ▲7六歩 #label 1
    △4四歩

    ▲7六歩
    △3四歩
    ▲2六歩 #merge 1
    △8四歩

    こうすると、どちらから辿っても同じことになり、4手目の選択肢として△4四歩と△8四歩の両方が表示される。
  • 次の手が9種類以上ある場合に、親ノードを分ける(表示されている以外の選択肢を選ぶためには一手戻して同じ表記の別の選択肢を選ぶ必要がある)のでなく、"9"を押すと次のページに移動して9番目以降の選択肢が表示されるようにした。
  • 同じマスに移動できる駒が複数ある場合の移動駒特定の不具合修正
    • 成れる駒が1つしかないのに「成」で特定できなかったのを修正
    • 「左上」とかで左側に上がらない選択肢があると特定できなかったのを修正
  • "*"や"#"で複数の選択肢があるノードに戻った時に前回の選択を表示するよう変更

■スクリーンショット(エミュレーターの画面)
screen shot
i-mode用ダウンロードページへのリンク
■ソースコード
 iKifuPlayer.java
 Board.java
 KifTree.java
■棋譜データの例
 Kifs.txt

続きを読む "iモード棋譜再生アプリを更新し続ける" »

2014年12月07日

10パズルを解くiアプリ作成

10パズルとは、与えられた4つの数字に任意の四則演算子及び括弧を加えて10にする、割と有名な遊びである。電車の切符を買うと大抵4桁の通し番号が書いてあるので、高校生の時に友達に教えてもらって以来、切符を買う度によくやっていて、40代の今でも、たまに切符を買うとついやってしまう。

最近、2日連続で、解けそうなのに解けない番号に当たった。6287と5773であった。
気になって気になって、家に帰ってからWebで検索しても答えは見つからず、後日プログラムを作ってやっと発見できた。

今後は電車で長時間もやっとした気分を引きずらなくて済むよう、いつも持ち歩いているDoCoMoのfeature phoneに、プログラムを仕込んでおくことにした。

・10パズルソルバーのダウンロードURL
 http://ynomura.dip.jp/java/TenPuzzle/Download.html

・サンプル画像

・使い方
数字ボタンで4桁の数字を入力し、「答えを探す」ボタンを押します。

・補足
このゲームは4桁の数字の余白に+−×÷()のいずれかを挿入するだけのものである、という筆者のポリシーにより、数字の並び替えには対応していません。
先頭に「−」を置くことを許すかどうかと、2つの数字を繋げて2桁の数字とするのを許すかどうかは、議論があり(先頭の「−」は四則演算でないことと、4つの数字の演算にこだわるかどうか)、許す方がマイナーな気がしましたので、それぞれをOFFにするチェックボタンを用意しています。

続きを読む "10パズルを解くiアプリ作成" »

2015年08月30日

10キーで棋譜入力するiモードアプリ

筆者は未だにdocomoのガラケーを使っている。会社でも家でもネットに繋がったPCが手元にあるし、電車通勤ではないし、遠出も頻繁にはしないので、スマホを使う時間が無いのである。外出先では携帯電話は電話とメールとカメラさえ使えれば良く、音楽はiPodで聞くので、日常生活でスマホの必要性を感じる瞬間が全く無い。今だと携帯電話のランニングコストが年間2万円以下で済んでいるので、端末費合わせて2年で20万円かかるようなスマホを購入する気にはならない。

というより、今使ってるP-01Cを大変気に入っているのである。シンプルなデザインで軽くて薄くて自作アプリが動く。完璧である。

筆者はこのiモード携帯に自作の棋譜再生アプリをインストールして活用しており、筆者の1日数分間の将棋ライフに欠かせないものとなっている。

日々、数字ボタンと*#だけで将棋盤を操作していて、ある日、ふと、「7六歩」といった符号を761とかで入力できたら、意外と高速に棋譜入力できて、出先で棋譜を記録できて便利だったりしないか、と思った。筆者は時々、反省のために対局後に盤面を撮影するのだが、記憶力が無く、家に帰って写真を見ると既にそこまでの手順が再現できないことがよくあり、できるなら棋譜を記録したいのである。

携帯電話のアプリでよくある、カーソルキーで駒を選択して動かすUIは、ボタンを押す回数が尋常ではなく、あまりにも手間で時間がかかる。
パソコンやスマホであれば、棋譜入力はマウス操作やタッチパネル操作で行うのが通常で、これは相当に楽だし高速であるが、1手当たり3キーで入力できれば、それよりも速いのではなかろうか。
棋譜の符号に慣れている人でないと使入力しにくいだろうが(チェス初級者の筆者はコマンドライン版gnuchessのNf3とかBxf6といった符号入力に最後まで慣れなかった)、記録した棋譜は符号で書かれているのが一番読みやすいと思うので、そういう棋譜を求めるなら、記録される通りに符号で入力できるのが最善だと思う。


棋譜の符号の数字部分は2桁であり、数字ボタン2発で打てる。数字は1-9なので、0ボタンを「同」にすれば、到達地点の部分は全て2ストローク以内で入力できる。
その次の駒の種類(以下、3キー目と記す。実際には「同」なら2キー目である。)は、成駒を含めると14種類あるので、数字ボタン1回で入力可能とは限らないが、9種類以上の駒が1つのマス目に行ける(または打てる)ことは稀であるので、大抵数字ボタン1回で入力できる。
それ以上に入力が必要な場合というのは、「成」と「不成」がある場合と、同じ種類のそこに行ける(または打てる)駒が2つ以上あり、「打」「左」「引」とかで特定しないといけない場合である。飛車は2枚しかないので、例えば左右で特定するとして、最大でも「左成」「左不成」「右成」「右不成」の4通りである。角も2枚しか無いので同様、桂も最大2ヶ所からしか来ないので同様に最大4通りである。金の動きをする駒は成れないので、最大で「と」の場合に6つの駒を特定する為の6通りである。銀は最大4枚で全て盤上にあってそれぞれ「成」「不成」があれば8通りである。香歩は特定が必要になることがないので、「成」「不成」の2通りである。
従って、駒の種類より後の部分は最大8通りであり、8つのボタンに動的に割り当てれば、数字ボタン1回で入力できる。(以下、4キー目と記す。)

さらに、3キー目と4キー目の組み合わせが10種類を超えることは稀だから、それらをまとめて、例えば「玉」と「金左」と「金右」の選択肢が一緒に出るようにする方がボタンを押す回数は減るだろうが、5八金右は「5」「八」「金」「右」と4キーで入力する方が直感的だし、3キーで確定する場合の方が多いので、「金右」の時だけ異なるボタンになるのでなく、「金」のボタンが固定される方が、慣れによる入力速度UPが期待できるので、3キー目と4キー目は分ける方が良いだろう。

同じ理由で、3キー目は、数字ボタンに固定的に
1: 歩 2: 香 3: 桂 4: 銀 5: 金 6: 角 7: 飛 8: 玉
と割り当て、成駒はその時空いている番号とする方が良かろう。それも、空いてればなるべく成る前の駒の番号(馬なら角の6)、空いてなければなるべく成る前の駒の次になる(角、馬の順になる)ように、次に空いている番号にすると良いだろう。


という訳で、夏休みにそういうiアプリを作成した。

・スクリーンショット
1キー目待ち状態の画面

3キー目待ち状態の画面

4キー目待ち状態の画面

サブメニュー画面

i-mode用ダウンロードページへのリンク

・ソースコード
 iKifuRecorder.java
 Board.java
 KifTree.java
 CGI.java

・作成者
 maruinen(筆者のネット将棋でのハンドル)

3キー目は、選択し得る駒が10種類以下なら0-9ボタンで指定、11種類以上なら0をページ切替ボタンとした。
ソフトキーで棋譜入力モード/再生モードを切り替えたり、サブメニュー画面を開いたり、アプリを終了したりできる。
棋譜の途中から入力を開始すると、元の棋譜が消えるのでなく、棋譜の分岐ができる。分岐を消すには、消したい手の先頭まで再生して、サブメニューの「ここまでの棋譜の最終手以降を消去」のボタンを押す。

入力した棋譜は、ソフトキーで終了すると、スクラッチパッドに保存される。終話キーで終了すると、保存されない。スクラッチパッドに保存された棋譜は、次回の起動時に自動的に読み込まれる。
また、入力した棋譜は、このサーバーのWebページにアップロードすることもできる。アップロードされた棋譜は、
http://ynomura.dip.jp/tmp/uploaded_kifus.html
で見ることができる。このページから棋譜をコピー&ペーストして、柿木将棋やMacの桜花で読み込めることを確認済である。
なお、棋譜をアップロードするには、このアプリの「ソフト設定」の通信設定を「する」にしてから起動する必要がある。また、このページにアップロードされた棋譜は誰でも閲覧可能だし、古いものは勝手に消えていく。


参考リンク
棋譜の表記方法:日本将棋連盟

続きを読む "10キーで棋譜入力するiモードアプリ" »

About Java

ブログ「Weblog on mebius.tokaichiba.jp」のカテゴリ「Java」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

次のカテゴリはPC一般です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35