見出し画像

駅名だけの鉄道シミュレーター

日本には行ったことが無い場所がまだたくさんあるなぁ、と思うための鉄道シミュレーターです。
後にプログラムと実行方法を記載しています。

プログラム実行後にまず都道府県を選択します。

画像 007

選択した都道府県の路線を選択します。

画像 008

始発駅が表示されます。

画像 009

Enterキーを押すと進みます。
駅名だけですが運転席視点で表示されます。
Escキーを押すと次の駅で折り返します。
これがプログラムです。

// 駅名だけの鉄道シミュレーター

// プログラミング言語「ドリトル」

// V3.31以降のインストール版ドリトルで実行してください。

// 2020.06.23  蕪木 孝


// 「はい」に変更すると終点駅から始発駅へ直接進みます

環状線 = いいえ。


// -------------------- 設定値 --------------------

進む距離 = 0.1// km。前進ボタンで一度に進む距離。

一度の距離 = 110.94297// km。緯度経度の1度分の距離。
画角 = 110// 度。ウインドウの左端から右端までの視野。
最大表示距離 = 50// km。
駅一文字の幅 = 0.025// km。駅名一文字の幅。
最大文字サイズ = 240// これ以上大きくすると表示がおかしくなる。
最小文字サイズ = 1// どんなに遠い駅でもこのサイズで表示される。
曲る角度 = 22.5// 一回の前進で曲ることできる角度。
画面の幅 = 画面 ! 幅?。	// ウインドウの幅。
画面の高さ = 画面 ! 高さ?。	// ウインドウの高さ。


// -------------------- 配列 --------------------

駅一覧 = 配列 ! 作る。	// 選択した路線の全ての駅を保存するための配列


// -------------------- グローバル変数の初期値設定 --------------------

方角 = 0// 自分が向いている方角。東が0度、北が90度、西が180度、南が-90度。
次の駅の方角 = 0// 自分の位置を基準にした次の駅の方角
次の駅の番号 = 2// 始発から始まるので次の駅の番号は「2」
復路 = いいえ。	// 「いいえ」は終点駅方向、「はい」は始発駅方向に進む。
			// Escキーを押すと切り替わって次の駅で折り返す。


// -------------------- ブロックを作る (ブロックは機能のかたまり) --------------------

// 機能   : JSONデータから引数「key」で指定した値を読む 
// 引数   : data : JSONデータ
//          key  : 読む値を指定するキー
// 戻り値 : 読んだ値

値を読む = 「
	| data key ; p len n |
	p = data ! (key) 何文字目?。
	p = p + (key ! 長さ?) + 1。
	len = data ! 長さ?。
	n = 0// ダブルクオテーションの手前の文字までが値
	「ぜんぶ ! ((data ! (p + n) 1 部分) != dq) ((p + n) <= len) 本当」 ! の間 「
		n = n + 1。
	」 実行。

	data ! (p) (n) 部分。
」。


// 機能   : 路線名からその路線に含まれる駅を取得して駅一覧に追加する
// 引数   : 路線名
// 戻り値 : 無し

路線の駅 = 「
	| 路線名 ; w ret str 駅名 駅数 駅緯度 駅経度 a p |

	w = webapi ! 作る。
	w : url =  "http://express.heartrails.com/api/json"。
	w : method = "getStations"。
	w : line = 路線名。
	ret = w ! 読む。
	str = ""str = str + ret。

	駅名 = 値を読む ! (str) " name:" 実行。
	駅数 = 0。
	「駅名 != ""」 ! の間 「
		駅数 = 駅数 + 1。
		:駅一覧 ! (駅名) 書く。
		駅緯度 = 値を読む ! (str) " y:" 実行。
		:駅一覧 ! (駅緯度) 書く。
		駅経度 = 値を読む ! (str) " x:" 実行。
		:駅一覧 ! (駅経度) 書く。
		a = "駅ラベル" + (駅数) + " = ラベル ! 作る。"。    a ! 実行。
		p = str ! " y:" 何文字目?。
		p = p + 3。
		len = str ! 長さ?。
		str = str ! (p) (len - p + 1) 部分。
		「(str ! " name:" 何文字目?) != 0」 ! なら 「
			駅名 = 値を読む ! (str) " name:" 実行。
		」
		そうでなければ 「
			駅名 = ""。
		」 実行。
	」 実行。
」。


// 機能   : 画角内の駅を表示する
// 引数   : 無し
// 戻り値 : 無し

駅表示 = 「
	| ; 駅数 a 駅名 駅緯度 駅経度 y x d 駅方角 円周 円弧 s 角度 len x座標 y座標 幅 高さ str |

	駅数 = (:駅一覧 ! 要素数?) / 3。

	「
		| i |

		「:復路 == いいえ」 ! なら 「a = i」
		そうでなければ 「a = 駅数 - i + 1」 実行。

		駅名 = :駅一覧 ! ((a - 1) * 3 + 1) 読む。
		駅緯度 = :駅一覧 ! ((a - 1) * 3 + 2) 読む。
		駅経度 = :駅一覧 ! ((a - 1) * 3 + 3) 読む。

		// 現在地から駅までの距離を計算する
		y = 駅緯度 - :緯度。
		x = 駅経度 - :経度。
		d = sqrt((y ! 2 pow) + (x ! 2 pow)) * :一度の距離。	// km

		// 現在地から駅への方角を計算する
		駅方角 = x!(y) atan2。	// 東が0度、北が90度、西が180度、南が-90度

		// 駅名の文字サイズを計算する
		円周 = d * 2 * 3.14159// km
		円弧 = 円周 * :画角 / 360// km
		s = round(:画面の幅 * :駅一文字の幅 / 円弧)。
		「s > :最大文字サイズ」 ! なら 「
			s = :最大文字サイズ。
		」
		そうでなければ 「s < :最小文字サイズ」 なら 「
			s = :最小文字サイズ。
		」 実行。

		// 自分が向いている方角から駅への角度を計算する
		// 自分の真正面が0度。左方向がプラス、右方向がマイナスの角度
		角度 = 駅方角 - :方角。

		「角度 > 180」 ! なら 「角度 = 角度 - 360」
		そうでなければ 「角度 < (-180)」 なら 「角度 = 角度 + 360」 実行。

		// 駅が画角内でかつ最大表示距離以内にあるならば表示する
		「ぜんぶ ! (角度 >= (:画角 / 2 * (-1))) (角度 <= (:画角 / 2)) 
		(d <= 最大表示距離) 本当」 ! なら 「
			len = (駅名 ! 長さ?)。
			x座標 = round((角度 * (-1) * :画面の幅 / :画角) - (s * len / 2))。
			y座標 = s / 2。
			幅 = s * len。
			高さ = s。
			str = "駅ラベル" + a + " ! 現れる。"str ! 実行。
			str = "駅ラベル" + a + " ! " + 幅 + " " + 高さ + " 大きさ。"str ! 実行。
			str = "駅ラベル" + a + " ! " + s + " 文字サイズ。"str ! 実行。
			str = "駅ラベル" + a + " ! " + x座標 + " " + y座標 + " 位置。"str ! 実行。
			str = "駅ラベル" + a + " ! " + dq + 駅名 + dq + " 書く。"str ! 実行。
		」
		そうでなければ 「
			str = "駅ラベル" + a + " ! 消える。"str ! 実行。
		」 実行。

		// 次の駅に到着したので、その次の駅を設定する
		「ぜんぶ ! (a == 次の駅の番号) (d <= :進む距離) 本当」 ! なら 「
			「:復路 == いいえ」 ! なら 「
				「a < 駅数」 ! なら 「:次の駅の番号 = a + 1」
				そうでなければ 「:環状線 == はい」 なら 「:次の駅の番号 = 1」 実行。
			」
			そうでなければ 「
				「a > 1」 ! なら 「:次の駅の番号 = a - 1」
				そうでなければ 「:環状線 == はい」 なら 「:次の駅の番号 = 駅数」 実行。
			」 実行。
		」 実行。

		// 始発の駅では最初から次の駅に向いている
		「ぜんぶ ! (a == 2) (次の駅の番号 == 2) 本当」 ! なら 「
			:方角 = 駅方角。
		」 実行。

		// 始発駅以外の駅では次の駅の方角へ曲りながら向く
		「a == :次の駅の番号」 ! なら 「
			:次の駅の方角 = 駅方角。
		」 実行。

	」 ! (駅数) 繰り返す。
」。


// -------------------- メイン --------------------

x = 画面の幅 / 2 * (-1) + 20。
y = 画面の高さ / 2 - 20。
都道府県メニュー = 選択メニュー ! 作る    (白) 色    (x) (y) 位置。
都道府県ラベル = ラベル ! 作る    (x) (y) 位置    消える。
路線メニュー = 選択メニュー ! 作る    (白) 色    (x) (y - 35) 位置    消える。


// 都道府県を選択するためのメニュー

w = webapi ! 作る。
w : url =  "http://express.heartrails.com/api/json"。
w : method = "getPrefectures"。
ret = w ! 読む。
str = ""str = str + ret。
flg = はい。
「flg」 ! の間 「
	p1 = (str ! (dq) 何文字目?)。
	「p1 > 0」 ! なら 「
		str = str ! (p1 + 1) ((str ! 長さ?) - p1) 部分。
		p2 = (str ! (dq) 何文字目?)。
		都道府県名 = str ! 1 (p2 - 1) 部分。
		都道府県メニュー ! (都道府県名) 書く。
		str = str ! (p2 + 1) ((str ! 長さ?) - p2) 部分。
	」
	そうでなければ 「
		flg = いいえ。
	」 実行。
」 実行。

都道府県選択 = いいえ。	// 都道府県選択時に路線も選択済になってしまう
				// 状態へ対処するためのフラグ

都道府県メニュー : 動作 = 「
	| 都道府県名 |

	:都道府県選択 = はい。

	w = webapi ! 作る。
	w : url =  "http://express.heartrails.com/api/json"。
	w : method = "getLines"。
	w : prefecture = 都道府県名。
	ret = w ! 読む。
	str = ""str = str + ret。
	flg = はい。
	「flg」 ! の間 「
		p1 = (str ! (dq) 何文字目?)。
		「p1 > 0」 ! なら 「
			str = str ! (p1 + 1) ((str ! 長さ?) - p1) 部分。
			p2 = (str ! (dq) 何文字目?)。
			路線名 = str ! 1 (p2 - 1) 部分。
			路線メニュー ! (路線名) 書く。
			str = str ! (p2 + 1) ((str ! 長さ?) - p2) 部分。
		」
		そうでなければ 「
			flg = いいえ。
		」 実行。
	」 実行。
	都道府県メニュー ! 消える。
	都道府県ラベル ! (都道府県名) 書く    現れる。
	路線メニュー ! 現れる。
」。


// 路線を選択するためのメニュー

路線メニュー : 動作 = 「
	| 路線名 |

	「:都道府県選択 == いいえ」 ! なら 「
		都道府県ラベル ! 消える。
		路線メニュー ! 消える。

		路線の駅 ! (路線名) 実行。
	
		// スタート地点の緯度、経度を始発駅の緯度、経度に設定
		:緯度 = :駅一覧 ! 2 読む。
		:経度 = :駅一覧 ! 3 読む。
		駅表示 ! 実行。

		// 始発駅が見えるように少し後退する
		:緯度 = :緯度 - 1 / :一度の距離 * :進む距離 * sin (:方角)。
		:経度 = :経度 - 1 / :一度の距離 * :進む距離 * cos(:方角)。
		駅表示 ! 実行。
	」
	そうでなければ 「
		:都道府県選択  = いいえ。
	」 実行。
」。


// -------------------- キーボード操作の設定 --------------------

排他フラグ = 0// キー長押しで発生するプログラムダウンの対策用
			// (それでもたまにダウンするが)

前進ボタン = ボタン ! "" "ENTER" 作る    消える。
前進ボタン : 動作 = 
「
	「:排他フラグ == 0」 ! なら 「
		:排他フラグ = 1。

		「:方角 != :次の駅の方角」 ! なら 「

			角度 = :次の駅の方角 - :方角。
			「角度 > 180」 ! なら 「角度 = 角度 - 360」
			そうでなければ 「角度 < (-180)」 なら 「角度 = 角度 + 360」 実行。

			「abs(角度) <= 曲る角度」 ! なら 「
				:方角 = :次の駅の方角。
			」
			そうでなければ 「
				「角度 > 0」 ! なら 「:方角 = :方角 + 曲る角度」
				そうでなければ 「:方角 = :方角 - 曲る角度」 実行。

				「:方角 > 360」 ! なら 「:方角 = :方角 - 360」
				そうでなければ 「:方角 < 0」 なら 「:方角 = :方角 + 360」 実行。
			」 実行。
		」 実行。

		:緯度 = :緯度 + 1 / :一度の距離 * :進む距離 * sin (:方角)。
		:経度 = :経度 + 1 / :一度の距離 * :進む距離 * cos(:方角)。
		駅表示 ! 実行。

		:排他フラグ = 0。
	」 実行。
」。


進行方向切り換えボタン = ボタン ! "" "ESCAPE" 作る    消える。
進行方向切り換えボタン : 動作 = 
「
	「:排他フラグ == 0」 ! なら 「
		:排他フラグ = 1。

		「:復路 == いいえ」 ! なら 「:復路 = はい」
		そうでなければ 「:復路 = いいえ」 実行。

		:排他フラグ = 0。
	」 実行。
」。

実行方法

これは教育用プログラミング言語「ドリトル」で書いたプログラムです。
インストール版ドリトルを以下のサイトからダウンロードしてください。
https://dolittle.eplang.jp/download
パソコンのOSがWindowsなら「Windows用」をクリックするとダウンロードがはじまります。
ダウンロードしたら解凍してください。
解凍したら dolittle.bat をダブルクリックするとドリトルが起動します。
プログラムを貼り付けて「実行!」をクリックすると画面が表示されます。

環状線を選択する場合

環状線の場合はプログラムの先頭の「環状線 = いいえ。」を「環状線 = はい。」に書き換えてからプログラムを実行してください。

環状線 = はい。

終点駅の次に始発駅に進むようになります。
書き換えない場合は終点駅でEscキーを押して折り返すことになります。

駅の間隔が狭い路線を選択する場合

曲り切れないで駅に到着することができず、駅の回りを延々と回り続ける場合があります。(例えば札幌市電など)
その場合はプログラムの先頭の「進む距離 = 0.1。」を小さい値に書き換えてください。例えば「0.01」など。

進む距離 = 0.01// km。前進ボタンで一度に進む距離。

エラーになって実行できない場合

このプログラムはインターネット経由で駅の情報を取得するためにWeb APIというものを使っています。
エラーになって実行できない場合の調査については同じようにWeb APIを使っている次の記事の「緯度、経度、乗組員数が表示されない場合」を参照してください。

また、起動できたとしてもたまにプログラムが落ちることがあります。
キー押しっぱなしによる負荷が原因だと思います。
動作確認ではJR日豊本線を小倉から鹿児島中央までEnterキー押しっぱなしで行けたのでプログラムダウンはごくまれだと思います。


このプログラムは株式会社ハートレイルズが提供する次のWeb APIを使用しています。

都道府県、路線情報を取得するために使用したWeb API
http://express.heartrails.com/api.html

この記事が気に入ったらサポートをしてみませんか?