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などのツールを用いるのが一般的である。

turtle.js

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  チームメンバーからメッセージが発信された