JavaScript + Node.js で亀場を使う
このページは、JavaScript + Node.js を使って亀を操る方法をまとめた。 プログラムは十分に検証できていないので、まだ動作が怪しい部分があるかもしれないが、 非同期的な処理を色々と試すことができるので、物好きな方はお試しあれ。
1. Node.jsをインストールする
JavaScriptを実行する環境として、ここではNode.jsを用いる。 Node.jsは、JavaScriptの言語処理系(Googleが開発し、Chromeなどに内蔵されているV8)に 通信の機能等を加えたソフトウェアで、本来はウェブサーバー用に開発されたものである。 それを、ここでは、亀場を操るために利用させてもらうことにする。
まず、Node.jsをインストールしなければならない。 それぞれのパソコン環境でのインストール方法は〔node.js インストール ubuntu linux〕といった具合に検索すれば、すぐに見つかるはずだ。
例えば、MacPortsでフリーソフトウェアの管理をしているMacの場合は、 (すでにMacPortsはインストールされているとすると)、ターミナルで
sudo port install nodejs
を実行すればよい。
Linuxの場合は、yumやapt-getなどのツールを用いるのが一般的である。
2. turtle.jsをダウンロードし、プログラムを実行する
つぎに、亀場と通信するためのプログラムの入ったファイルturtle.jsをダウンロードし、 JavaScriptのプログラムと同じ場所に保存しておく。
そして、あなたのJavaScriptの始まりの部分に
var Turtle = require('./turtle.js') ;
を加える。こうすることで、ダウンロードしたturtle.jsで定義された機能が、あなたのプログラムでも利用できるようになる。
例として、お掃除ロボットのプログラム(cleaner.js)を以下に示す:
cleaner.js
var Turtle = require('./turtle.js') ; var ttl = new Turtle("localhost") ; var stop_going=false ; function scatter_coin(n) { var cnt ; if (n==1) { for (cnt=0; cnt<100; cnt++) ttl.coin() ; } } function keep_going() { if (!stop_going) ttl.fd(2) ; setTimeout(function(){ keep_going() ; },30) ; } ttl.found_coin = function(timecode) { ttl.pickcoin() ; } ; ttl.run_into_wall = ttl.run_into_turtle = function(timecode) { var cnt ; stop_going=true ; for (cnt=0; cnt<49; cnt++) ttl.lt(3) ; ttl.lt(3, function(){ stop_going=false; }) ; } ; ttl.bmode() ; ttl.nm("kame") ; ttl.q_nt(scatter_coin) ; keep_going() ;
上記のプログラムを cleaner.js というファイル名で保存したすると、このプログラムを実行するには、 亀場サーバーが立ち上がっている状態で、ターミナル上で
node cleaner.js
を実行する。実行を中断するには、ターミナルでCTRL-Cを押す。
上記の例では、亀場の状況を取得するためにコールバック関数を多用しているが、図形を描くだけであれば、 コールバック関数を使う必要は(ほとんど)無い。 そうした例として、ヒルベルト曲線を描くプログラム:hilbert-curve.js、 および、シェルピンスキーの三角形を描くプログラム:sierpinski-triangle.js、 を示しておく。
(以下、まだ書きかけ)
3. 亀を操るための機能(Node.js用)
右表の関数の引数のうち、「コールバック関数」には関数型のオブジェクトを渡す。
すると、処理が完了した時点で、その関数が呼び出される。
必要無い場合は、コールバック関数の箇所は省略可。
描画関数一覧
関数名 | 説明 | 使用例 |
---|---|---|
new Turtle(接続先) |
亀場サーバーに接続し、Turtleのオブジェクトを返す 「接続先」は相手のIPアドレスまたはドメイン名 自分のPCの亀場サーバーを使うときは接続先 として"localhost"を指定 |
t = new Turtle("localhost"); |
clr(コールバック関数) |
描画内容とゾンビ亀を消去する。 コールバック関数は、この処理が完了した際に呼び出される。 コールバック関数は省略しても構わない(以下、同様)。 |
t.clr(); |
rst(コールバック関数) |
亀を原点に移動し、方向を0度にセットする 亀の状態もリセットされる 対戦モード中は使えない |
t.rst(); |
home(コールバック関数) |
亀を原点(0,0)に移動し、方向を0度にセット 亀の状態(ペンの上下等)は変化なし 対戦モード中は使えない |
t.home(); |
fd(距離,コールバック関数) |
亀を指定の距離だけ前進させる 経路上に障害物(他の亀)が居るときは移動できない |
t.fd(100) ; |
bk(角度,コールバック関数) |
亀を指定の距離だけ後退させる 経路上に障害物(他の亀)が居るときは移動できない |
t.bk(s*10); |
jump(x座標,y座標,コールバック関数) |
指定の座標に亀をジャンプさせる 対戦モード中は使えない |
t.jump(x+10,y-10); |
rjump(x移動,y移動,コールバック関数) |
現在地から相対移動する 対戦モード中は使えない |
t.rjump(dx,dy); |
rt(角度,コールバック関数) |
その場で角度(°)だけ右(時計回り)回転 | t.rt(90); |
lt(角度,コールバック関数) |
その場で角度(°)だけ左(反時計回り)回転 | t.lt(rad*180/Math.PI); |
east(コールバック関数) north(コールバック関数) west(コールバック関数) south(コールバック関数)
|
右、上、左、下方向にそれぞれ頭の向きを変更 対戦モード中は無効 |
t.east(); |
lw(線の太さ,(コールバック関数)) |
描画する線の太さを指定 | t.lw(3.0); |
pd(コールバック関数) |
ペンを下ろす | t.pd(); |
pu(コールバック関数) |
ペンを上げる | t.pu(); |
say(文字列,コールバック関数) |
現在地に"文字列"をプリント 半角英数字のみ対応 |
t.say("I'm smart."); |
col(赤,緑,青,コールバック関数) |
描画する線の色を変更(初期値は赤) ペンダウン(pd)の前に指定する 光の三原色で指定。各値の最小は0、最大は1 例えば (1.0,1.0,0.0) は黄色 |
t.col(0.7,1.0,0.0); |
bgc(赤,緑,青,コールバック関数) |
「亀場」の背景色を設定(初期値は黒) | t.bgc(1.0,1.0,0.0); |
fill(コールバック関数) |
線分で囲まれた凸領域を塗りつぶす (ただし、頂点の数は6以下) |
t.fill(); |
brush(コールバック関数) |
毛筆のような線で描く | t.brush(); |
point(コールバック関数) |
現在地に点を打つ lw()で設定した「線の太さ」が点の大きさとなる |
t.point(); |
line(コールバック関数) |
塗りつぶしとブラシを解除し 線での描画を行う(初期設定) |
t.line(); |
card(種類,X座標,Y座標,コールバック関数) |
整数値で指定したトランプのカード を座標の位置に置く。カードはJokerも入れて53枚で 1から53の整数値で種類を指定 |
t.card(3,100,-20); |
clrcard(コールバック関数) |
カードを亀場から消去 | t.clrcard(); |
q_pos(コールバック関数) |
現在地の座標を問い合わせる コールバック関数の引数には座標(x,y)が渡される |
t.q_pos( function(x,y){console.log(x,y);} ); |
q_dir(コールバック関数) |
現在の方向角(度)を問い合わせる コールバック関数の引数には方向角が渡される |
t.q_dir( function(angle){console.log(angle);} ) ; |
亀ロボットの操作関係
関数名 | 機能 | 使用例 |
---|---|---|
bmode(コールバック関数) |
亀場を対戦モードに切り換える 背景が黒っぽいコルクボード状の柄に変わる 亀場の四方には壁ができ、外に出られなくなる 砲弾発射は-1点、相手に命中すると+10点、被弾は-30点 最初の持ち点は100。持ち点が0になると動けなくなる |
t.bmode(); |
gmode(コールバック関数) |
亀場を通常のタートルグラフィックス用に切り換える 背景が無地になる 亀は「外」にも移動できるようになり、 点数や名前の表示は消える |
t.gmode(); |
fire(コールバック関数) |
前方に「大砲」を発射する 一発発射ごとに持ち点が1ポイント減る ただし、他の亀に命中すると10ポイントをゲット 一方、被弾した亀は30ポイントを失う また、至近距離でドーナッツに命中すると10ポイント 大砲は1発ずつしか打てない(自分が発射した 砲弾は「亀場」に最大でも1つだけ)。 |
t.fire(); |
nm(文字列,コールバック関数) |
亀にニックネームをつける 文字列は "ダブルクォーテーションで囲む" |
t.nm("KAME"); |
tm(チーム番号,コールバック関数) |
亀のチーム番号(1〜4)を設定する |
t.tm(3); |
q_nt(コールバック関数) |
亀場で生存している亀と物体を合わせた数(自分も含める) ただし障害物は勘定に入らない。 コールバック関数の引数には亀の数が渡される |
t.q_nt( function(nt){console.log(nt);} ) ; |
q_radar(コールバック関数) |
自分に一番近い物体の方向角を問い合わせる 角度は自分の頭の方向を0とした相対値(度)で、 -180から+180の範囲 ただし、角度の計測結果には約±20度の誤差を含む コールバック関数の引数には、角度とその物体のチーム番号が渡される。 |
t.q_radar( function(angle,team){console.log(angle,team);} ) ; |
q_finder(コールバック関数) |
自分の正面の距離20(亀のサイズ)の範囲内に 他の亀や障害物、壁があるかどうか問い合わせる ただし砲弾には反応しない 0は障害物無し、1は亀、2はドーナッツ, 3は障害物、4は壁を検出 コールバック関数の引数には、上記の数値とその物体のチーム番号が渡される。 |
t.q_finder( function(type,team){console.log(type,team);} ) ; |
rf(長さ,コールバック関数) |
q_finderの探索範囲を変更する 初期値は20(亀のサイズ)になっている |
t.rf(100.0); |
bcas(文字列,コールバック関数) |
同じチームのメンバーにメッセージを送信 最大128文字。 チームメンバーにはGOT_MESSAGEイベントが発生。 |
t.bcas("COME ALL"); |
q_bcas(コールバック関数) |
チームメンバーからのメッセージを受信 コールバック関数には発信時刻(整数)と内容(文字列)が渡される |
t.q_bcas( function(time,msg){console.log(time,msg);} ) ; |
q_sonar(コールバック関数) |
自分の斜め左、正面、斜め右の距離20(亀のサイズ)の 範囲内に障害物があるかどうか問い合わせる ただし,障害物の種類は区別できず、砲弾には反応しない コールバック関数の引数には左, 前, 右 の状況が渡される。 trueが障害物有り、falseが無し |
t.q_sonar( function(left,front,right) {console.log(left,front,right);} ) ; |
q_time(コールバック関数) |
亀場が出来てからの経過時間を問い合わせる 自分の生存した時間、ではない点に注意 コールバック関数には、経過時間が渡される |
t.q_time( function(time){console.log(time);} ) ; |
q_score(コールバック関数) |
自分の持ち点を問い合わせる 最初に持ち点100から出発し、砲弾を発射する毎に-1点 ただし亀に命中すると+10点が加算される コールバック関数には、点数が渡される |
t.q_score( function(score){console.log(score);} ) ; |
robot(コールバック関数) |
亀場に「ロボット亀」を登場させる ロボットは大砲を発射してくるのでとても「危険」 |
t.robot(); |
donut(コールバック関数) |
亀場のランダムな位置に「ドーナッツ」をひとつ出す 一匹の亀が出せるドーナッツは最大10個 至近距離でドーナッツに砲弾が命中すると10ポイント |
t.donut(); |
coin(コールバック関数) |
亀場のランダムな位置に「ドーナッツ」を1枚出す コインは亀の移動の邪魔にならない |
t.coin(); |
borrowcoin(コールバック関数) |
亀場主からコインを10枚借りる 亀のはじめの所持金は0(コイン0枚) |
t.borrowcoin(); |
dropcoin(コールバック関数) |
現在位置にコイン(金貨)を1枚置く |
t.dropcoin(); |
pickcoin(コールバック関数) |
現在位置のコインを1枚拾う |
t.pickcoin(); |
q_coin(コールバック関数) |
現在位置の周りに何枚コインがあるか調べる コールバック関数にはコインの枚数が渡される |
t.q_coin( function(ncoin){console.log(ncoin);} ); |
q_mycoin(コールバック関数) |
手持ちのコインの枚数を調べる コールバック関数にはコインの枚数が渡される |
t.q_mycoin( function(ncoin){console.log(ncoin);} ); |
イベント処理
亀が被弾したり物体に衝突すると、決められた名前のコールバック関数が呼び出される。 こうしたイベントの検知を行うには、Turtleのオブジェクトに以下のようにして適宜コールバック関数を追加する。
t = new Turtle("localhost") ; t.run_into_turtle = function(time) { var cnt; for (cnt=0; cnt<20; cnt++) t.lt(2) ; } ;
イベント検知に用いることのできるコールバック関数は以下のとおり:
衝突イベントを用いる際は、以下の点に注意:
fd()メソッドやbk()メソッドで、前進(後退)させようとすると、亀場サーバーは
指定の距離だけ動いた際に、他の物体に衝突するかどうかを計算して判定する。
そして、もし移動の途中で物体に衝突するようであれば、亀を動かさない。
衝突イベントは、そのような場合に発生する。
単に、物体に接近しただけでは、イベントは発生しない。
コールバック関数名 | 機能 |
---|---|
hit_by_bullet(time) |
亀(自分)が被弾した際に呼び出されるメソッド 被弾した時刻(数値)が引数に渡される |
run_into_turtle(time) |
fdで前進しようとして、亀に衝突した際に呼び出されるメソッド 衝突した時刻(数値)が引数に渡される |
run_into_donut(time) |
fdで前進しようとして、ドーナッツに衝突した際に呼び出されるメソッド 衝突した時刻(数値)が引数に渡される |
run_into_stone(time) |
fdで前進しようとして、石に衝突した際に呼び出されるメソッド 衝突した時刻(数値)が引数に渡される |
run_into_wall(time) |
fdで前進しようとして、壁に衝突した際に呼び出されるメソッド 衝突した時刻(数値)が引数に渡される |
found_coin(time) |
自分の周囲にコインが見つかった際に呼び出されるメソッド 近くにコインが無い状態で、自分がコインを落とした際にも反応する 最初に発見した時刻(数値)が引数に渡される |
detected_by_finder(time) |
他の亀のFINDERに自分が検出された際に呼び出されるメソッド 見つかった時刻(数値)が引数に渡される |
detected_by_radar(time) |
他の亀のRADARに自分が検出された際に呼び出されるメソッド 見つかった時刻(数値)が引数に渡される |
got_message(time) |
同じチームメンバーからメッセージが送信された際に呼び出されるメソッド 発信された時刻(数値)が引数に渡される |
イベントのマスク
イベント処理中に他のイベントを禁止(マスク)したい場合は
disable_event(eventcode)
イベント処理を再開したい場合は
enable_event(eventcode)
を用いる。指定できるイベントコードは以下のとおり:
記号 値(16進) EVENT_HIT_BY_BULLET 00000001 被弾 EVENT_RUN_INTO_TURTLE 00000002 亀に衝突 EVENT_RUN_INTO_DONUT 00000004 ドーナッツに衝突 EVENT_RUN_INTO_STONE 00000008 石に衝突 EVENT_RUN_INTO_WALL 00000010 壁に衝突 EVENT_FOUND_COIN 00000020 コインをみつけた EVENT_DETECTED_BY_FINDER 00000040 FINDERで検知された EVENT_DETECTED_BY_RADAR 00000080 RADERで検知された EVENT_GOT_MESSAGE 00000100 チームメンバーからメッセージが発信された