Skip to content

Commit

Permalink
Merge pull request #3 from magicant/reverse_generation
Browse files Browse the repository at this point in the history
逆方向に路線を生成する
  • Loading branch information
magicant committed Feb 15, 2020
2 parents e1add79 + d38c0d3 commit 4db9b49
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 94 deletions.
25 changes: 15 additions & 10 deletions Scenarios/magicant/randommap/DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ map_misc フォルダーにあるファイルは上記以外のマップファ

停車場リストファイルは `stations_<時刻>h_<ID>.csv` 形式のファイル名をしており、例えば `stations_9h_0.csv` のようになる。

`<時刻>` は最初の駅を出発する時刻が何時台かを示しており、例えば `stations_9h_0.csv` は最初の駅を午前 9 時台に出発するダイヤである
`<時刻>` は最後の駅に到着する時刻が何時台かを示している。例えば `stations_9h_0.csv` は最後の駅に午前 9 時台に到着するダイヤである

`<ID>` は停車場リストのバリエーションの番号である。ID ごとに停車場リストの中身 (特に駅名) が異なっている。

マップファイルからランダムに停車場リストファイルを選んで読み込むことで、駅名とダイヤが毎回変化するようにしてある。停車場リストファイルの内容を動的に生成することはできないので、駅名やダイヤはファイルごとに一定である。また、マップファイルからは停車場リストに書かれた内容を読み取ることができないので、最初の駅の出発時刻は時からおおよその分と秒が機械的に決まるようにしてある (こうしないと、自車のダイヤと先行列車のダイヤを合わせることができない)。具体的にいうと、最初の駅の出発時刻は必ず以下のどれか (もしくは高々その数分前) となる
マップファイルからランダムに停車場リストファイルを選んで読み込むことで、駅名とダイヤが毎回変化するようにしてある。停車場リストファイルの内容を動的に生成することはできないので、駅名やダイヤはファイルごとに一定である。また、マップファイルからは停車場リストに書かれた内容を読み取ることができないので、最後の駅の到着時刻は時からおおよその分と秒が機械的に決まるようにしてある (こうしないと、自車のダイヤと先行列車のダイヤを合わせることができない)。具体的にいうと、最後の駅の到着時刻は必ず以下の時刻よりも後になる

- 5:31:00
- 6:51:35
Expand All @@ -54,8 +54,6 @@ map_misc フォルダーにあるファイルは上記以外のマップファ
- 22:20:55
- 23:41:30

また、最初の駅のパートファイルにおいて、先行列車は初期の `$pretrain_time` から 90 秒以内に最初の駅の出発信号より先に進んでいなければならない。(そうしないと先行列車が自車より後ろの位置に来る恐れがある)

## マップファイルの生成

map フォルダーにある build.sh というシェルスクリプトファイルを実行することでマップファイルが大量に生成される。また、シナリオファイルに記載すべき `Route = ...` の値が標準出力に出力される。
Expand All @@ -64,13 +62,20 @@ map フォルダーにある build.sh というシェルスクリプトファイ

なお、生成されるマップファイルが読み込むパートファイルの種類や順番はスクリプトを実行するたびに毎回ランダムに変化する。

各パートファイルは路線を**逆方向**に生成してゆくことに注意すること。例えば一つ目のパートファイルが距離程 12300~12500 メートルの区間を生成したら、次のパートファイルは例えば 12000~12300 メートル区間を生成する (12500~12700 メートル区間とかではない)。念のため用語の意味を明示しておく:

- 手前 (preceding) = 距離程の数字がより小さい
- 後 (following) = 距離程の数字がより大きい
- 次 (next) のパートファイル = より後に `include` されるパートファイル = より手前の区間を生成するパートファイル
- 前 (previous) のパートファイル = より先に `include` されるパートファイル = より後の区間を生成するパートファイル

### パートファイルの選択

各パートファイルには、`//NEXT: <ファイル名>` という形式のコメントが一つ以上書かれている。これにより、そのパートファイルが `include` された後にどのパートファイルが `include` されるかが決まる。コメントが複数ある場合はそれらの中からランダムに次のパートファイルが選択される。同じファイル名を指すコメントを何度も書くことでそのファイルが選ばれる確率が上がる。
各パートファイルには、`//FOLLOWS: <ファイル名>` という形式のコメントが一つ以上書かれている。これにより、そのパートファイルが `include` された後にどのパートファイルが `include` されるか (つまりそのパートファイルが生成する区間の手前にどのような区間が生成されるか) が決まる。コメントが複数ある場合はそれらの中からランダムに次のパートファイルが選択される。同じファイル名を指すコメントを何度も書くことでそのファイルが選ばれる確率が上がる。

パートファイルの中に `//DUMMY` というコメントがある場合、そのパートファイルはダミーとみなされる。ダミーのパートファイルは `include` されず、そこに書かれている `//NEXT:` のファイル名に従ってただ次のパートファイルが選択される
パートファイルの中に `//DUMMY` というコメントがある場合、そのパートファイルはダミーとみなされる。ダミーのパートファイルは `include` されず、そこに書かれている `//FOLLOWS:` のファイル名に従って再度パートファイルが選択される

ダミーファイルは、主に複数のパートファイルからランダムに選ぶのをやりやすくするために使用する。例えば grassland_s_any.txt というダミーファイルには名前が grassland_s で始まる種々のパートファイル (どれも草原を走る単線のパート) が次の候補として書かれている。これらのパートファイルをいちいち `//NEXT:` に列挙する代わりにダミーファイルを `//NEXT:` に指定することで、コメントの記述量も減るし、また新たに草原を走る単線のパートファイルが増えたときにはダミーファイルを書き換えるだけで他のパートファイルの続きとして候補に加えることができる
ダミーファイルは、主に複数のパートファイルからランダムに選ぶのをやりやすくするために使用する。例えば grassland_s_any.txt というダミーファイルには名前が grassland_s で始まる種々のパートファイル (どれも草原を走る単線のパート) `//FOLLOWS:` 対象として書かれている。他のパートファイルに `//FOLLOWS:` を記載する際、たくさんのパートファイルをいちいち `//FOLLOWS:` に列挙する代わりにダミーファイルを `//FOLLOWS:` に指定することで、 `//FOLLOWS:` の記述量も減るし、また新たに草原を走る単線のパートファイルが増えたときにはダミーファイルを書き換えるだけで他のパートファイルの手前として候補に加えることができる

なお、各マップファイルで最初に読み込まれるパートファイルは必ず any.txt で、これは全てのパートファイルからランダムに選択するためのダミーのファイルである。

Expand All @@ -89,9 +94,9 @@ map フォルダーにある build.sh というシェルスクリプトファイ
`$` の直後にアルファベットが来る変数はパートファイル間で様々なデータを引き継ぐために使用する。以下はそのような変数の一覧である。一つのパートファイルの処理が終わり次のパートファイルの処理に移るとき、変数の値は以下に説明する通りになっている必要がある。

- `$distance` = パートファイルが切り替わる地点の位置。
- `$next_station_number` = 次に駅のパートファイルが `include` された時に生成される駅の番号。駅の番号は初めから順に 0, 1, 2... と数える
- `$previous_station_location` = 一つ前の駅の停止位置
- `$previous_section_location` = 一つ前の閉塞の開始位置
- `$preceding_station_number` = 次に駅のパートファイルが `include` された時に生成される駅の番号。駅の番号は始発駅が 0 で、終着駅に向かって 1, 2, 3, ... と上がってゆく
- `$following_station_location` = 一つ後の駅の停止位置
- `$following_section_location` = 一つ後の閉塞の開始位置
- `$pretrain_time` = `$distance` の位置を先行列車が通過する時刻 (0 時からの秒数)。

以下は全てのパートファイルで使われる変数ではあるが、最初のパートファイルが `include` される前に初期化され、各パートファイルの中では変更されない。
Expand Down
6 changes: 3 additions & 3 deletions Scenarios/magicant/randommap/map/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set -eu
cd -P -- "$(dirname -- "$0")"
IFS=$' \t\n\r'

# TODO Terminal stations should be chosen as the last station only.
# TODO The last non-terminal station should be followed by some sections.

# $candidate_part_file = name of the candidate part file
candidate_is_dummy() {
Expand All @@ -24,11 +24,11 @@ candidate_is_station() {
# $candidate_part_file [in-out] = name of the candidate part file
next_candidate_part() {
candidates=($(
sed -n 's|^//NEXT:[[:space:]]*||p' "$candidate_part_file"
sed -n 's|^//FOLLOWS:[[:space:]]*||p' "$candidate_part_file"
))
if [ "${#candidates[@]}" -le 0 ]
then
printf 'Error: Part file "%s" has no next part\n' "$candidate_part_file" >&2
printf 'Error: Part file "%s" has no preceding part\n' "$candidate_part_file" >&2
return 1
fi
candidate_part_file=${candidates[$((RANDOM % ${#candidates[@]}))]}
Expand Down
8 changes: 4 additions & 4 deletions Scenarios/magicant/randommap/map_misc/init.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ BveTs Map 2.02
// このファイルは、他の init_*.txt ファイルから一度だけ include される。
// 全てのマップに共通の初期化を行う。

$distance = 0;
$next_station_number = 0;
$previous_station_location = 0;
$previous_section_location = 0;
$distance = 65536;
$preceding_station_number = 5;
$following_station_location = $distance;
$following_section_location = $distance;

Structure.Load('../structures/structures_' + floor(rand()) + '.csv');
$ballast_5m_count = 2;
Expand Down
49 changes: 28 additions & 21 deletions Scenarios/magicant/randommap/map_misc/section.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
BveTs Map 2.02

// 新しい閉塞に対する ATS 地上子を設置する。
// 一つ後の閉塞に対する ATS 地上子を設置する。
// このファイルは Section.Begin 文で新しい閉塞を区切った直後に include すること。
//
// このファイルで設置する地上子は現地点 ($distance) にある閉塞に対する地上子ではなく
// 一つ後の閉塞 ($following_section_location) に対する地上子である。
// これらの地上子を二つ以上手前の閉塞に設置すると地上子が情報を送信する閉塞の
// インデックスがずれるので、一つ手前の閉塞の位置 ($distance) が確定してから
// その位置に応じて地上子を設置する。
//
// このファイルを include する前に以下の変数を設定しておく必要がある。
// $distance = 現在位置 (Section.Begin 文のある位置)
// $ats_stop_beacon_type = ATS-P 即時停止地上子の種別番号
Expand All @@ -13,68 +20,68 @@ BveTs Map 2.02
// $ats_update_m1_beacon_type = ATS-P パターン更新地上子の種別番号
// $ats_update_m1_beacon_value = ATS-P パターン更新地上子に送る値
// $ats_update_transponder = ATS-P パターン更新地上子ストラクチャーキー
// $previous_section_location = 直前の閉塞の開始位置
// $following_section_location = 一つ後の閉塞の開始位置
// $_indexes = 新しい閉塞の信号インデックス設定 (信号現示受信地上子 (1012 番) に送る値)
// このファイルの中で以下の変数が変更される。
// $previous_section_location = この閉塞の開始位置 (= $distance)
// $following_section_location = この閉塞の開始位置 (= $distance)

$previous_section_location;
$distance + 0.01;
Beacon.Put(1012, 1, $_indexes);

// 即時停止地上子
$distance - 25;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 25;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_stop_beacon_type, $ats_stop_beacon_section, 0);

// 現示更新地上子
$distance - 25;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 25;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 50;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 50;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 85;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 85;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 130;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 130;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 180;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 180;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 280;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 280;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);
$distance - 600;
(distance + $previous_section_location + abs(distance - $previous_section_location)) / 2; // max(distance, $previous_section_location)
$following_section_location - 600;
(distance + $distance + abs(distance - $distance)) / 2; // max(distance, $distance)
Beacon.Put($ats_update_1_beacon_type, 1, $ats_update_1_beacon_value);
Beacon.Put($ats_update_2_beacon_type, 2, $ats_update_2_beacon_value);
Beacon.Put($ats_update_m1_beacon_type, -1, $ats_update_m1_beacon_value);
distance - 3;
Structure[$ats_update_transponder].Put0(0, 3, 0);

$previous_section_location = $distance;
$following_section_location = $distance;
Loading

0 comments on commit 4db9b49

Please sign in to comment.