summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--2013-battlecity/.gitignore4
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge.sln20
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/AiAgent.cs196
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/App.config21
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/BoardCell.cs16
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Entelect.BattleCity.Challenge.csproj109
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/GameInProgress.cs157
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Move.cs24
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Program.cs27
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Properties/AssemblyInfo.cs36
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/ChallengeService.wsdl294
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.delta.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.game.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.loginResponse.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionResponse.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.state.datasource10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.cs1104
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.svcmap31
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration.svcinfo10
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration91.svcinfo201
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/compile.bat1
-rw-r--r--2013-battlecity/Entelect.BattleCity.Challenge/start.bat1
-rw-r--r--2013-battlecity/README.md12
-rw-r--r--2015-spacebot/.gitignore12
-rw-r--r--2015-spacebot/CMakeLists.txt17
-rw-r--r--2015-spacebot/Spacebot.sln20
-rw-r--r--2015-spacebot/Spacebot.vcxproj113
-rw-r--r--2015-spacebot/Spacebot.vcxproj.filters123
-rw-r--r--2015-spacebot/bot.json5
-rw-r--r--2015-spacebot/brain.nn14
-rw-r--r--2015-spacebot/compile.bat1
-rwxr-xr-x2015-spacebot/compile.sh3
-rw-r--r--2015-spacebot/include/alien.h11
-rw-r--r--2015-spacebot/include/brain/bias_node.h12
-rw-r--r--2015-spacebot/include/brain/neural_link.h17
-rw-r--r--2015-spacebot/include/brain/neural_network.h41
-rw-r--r--2015-spacebot/include/brain/neural_node.h18
-rw-r--r--2015-spacebot/include/brain/neuron.h22
-rw-r--r--2015-spacebot/include/brain/sensor.h10
-rw-r--r--2015-spacebot/include/building.h12
-rw-r--r--2015-spacebot/include/enemy_bullet.h12
-rw-r--r--2015-spacebot/include/game_entity.h17
-rw-r--r--2015-spacebot/include/game_state.h44
-rw-r--r--2015-spacebot/include/move.h12
-rw-r--r--2015-spacebot/include/move_string_mapper.h14
-rw-r--r--2015-spacebot/include/player_missile.h11
-rw-r--r--2015-spacebot/include/shield.h12
-rw-r--r--2015-spacebot/include/spacebot.h18
-rw-r--r--2015-spacebot/include/spaceship.h12
-rw-r--r--2015-spacebot/main/main.cpp25
-rw-r--r--2015-spacebot/run.bat1
-rwxr-xr-x2015-spacebot/run.sh3
-rw-r--r--2015-spacebot/src/alien.cpp7
-rw-r--r--2015-spacebot/src/brain/neural_link.cpp11
-rw-r--r--2015-spacebot/src/brain/neural_network.cpp163
-rw-r--r--2015-spacebot/src/brain/neuron.cpp40
-rw-r--r--2015-spacebot/src/building.cpp6
-rw-r--r--2015-spacebot/src/enemy_bullet.cpp6
-rw-r--r--2015-spacebot/src/game_entity.cpp13
-rw-r--r--2015-spacebot/src/game_state.cpp234
-rw-r--r--2015-spacebot/src/move_string_mapper.cpp21
-rw-r--r--2015-spacebot/src/player_missile.cpp6
-rw-r--r--2015-spacebot/src/shield.cpp6
-rw-r--r--2015-spacebot/src/spacebot.cpp48
-rw-r--r--2015-spacebot/src/spaceship.cpp6
-rwxr-xr-x2015-spacebot/test.sh3
-rw-r--r--2015-spacebot/test/catch.hpp9460
-rw-r--r--2015-spacebot/test/game_state.cpp90
-rw-r--r--2015-spacebot/test/move_string_mapper.cpp38
-rw-r--r--2015-spacebot/test/neural_network.cpp122
-rw-r--r--2015-spacebot/test/test_main.cpp2
-rwxr-xr-x2015-spacebot/watch.sh4
-rw-r--r--2017-battleships/.gitignore3
-rw-r--r--2017-battleships/Cargo.lock122
-rw-r--r--2017-battleships/Cargo.toml11
-rw-r--r--2017-battleships/bot.json8
-rw-r--r--2017-battleships/notes.org1447
-rw-r--r--2017-battleships/readme.txt20
-rw-r--r--2017-battleships/src/actions.rs90
-rw-r--r--2017-battleships/src/files.rs57
-rw-r--r--2017-battleships/src/knowledge.rs504
-rw-r--r--2017-battleships/src/lib.rs67
-rw-r--r--2017-battleships/src/main.rs19
-rw-r--r--2017-battleships/src/math.rs338
-rw-r--r--2017-battleships/src/placement.rs23
-rw-r--r--2017-battleships/src/ships.rs216
-rw-r--r--2017-battleships/src/shooting.rs47
-rw-r--r--2017-battleships/src/state.rs146
-rw-r--r--2018-tower-defence/.gitignore13
-rw-r--r--2018-tower-defence/Cargo.toml38
-rw-r--r--2018-tower-defence/Makefile24
-rw-r--r--2018-tower-defence/bot.json8
-rwxr-xr-x2018-tower-defence/import-replay.sh21
-rw-r--r--2018-tower-defence/license.org22
-rw-r--r--2018-tower-defence/readme.org52
-rw-r--r--2018-tower-defence/src/bin/perf-test.rs26
-rw-r--r--2018-tower-defence/src/engine/bitwise_engine.rs483
-rw-r--r--2018-tower-defence/src/engine/command.rs66
-rw-r--r--2018-tower-defence/src/engine/constants.rs52
-rw-r--r--2018-tower-defence/src/engine/geometry.rs71
-rw-r--r--2018-tower-defence/src/engine/mod.rs5
-rw-r--r--2018-tower-defence/src/engine/status.rs8
-rw-r--r--2018-tower-defence/src/input/json.rs191
-rw-r--r--2018-tower-defence/src/input/mod.rs1
-rw-r--r--2018-tower-defence/src/lib.rs20
-rw-r--r--2018-tower-defence/src/main.rs55
-rw-r--r--2018-tower-defence/src/strategy/mod.rs3
-rw-r--r--2018-tower-defence/src/strategy/monte_carlo.rs505
-rw-r--r--2018-tower-defence/src/strategy/monte_carlo_tree.rs243
-rw-r--r--2018-tower-defence/src/strategy/static_opening.rs21
-rw-r--r--2018-tower-defence/tests/live_comparison.rs68
-rw-r--r--2018-tower-defence/tests/monte_carlo_test.rs34
-rw-r--r--2018-tower-defence/tests/state0.json1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 000/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 000/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 001/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 001/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 002/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 002/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 003/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 003/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 004/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 004/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 005/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 005/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 006/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 006/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 007/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 007/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 008/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 008/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 009/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 009/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 010/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 010/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 011/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 011/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 012/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 012/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 013/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 013/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 014/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 014/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 015/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 015/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 016/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 016/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 017/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 017/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 018/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 018/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 019/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 019/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 020/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 020/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 021/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 021/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 022/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 022/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 023/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 023/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 024/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 024/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 025/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 025/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 026/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 026/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 027/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 027/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 028/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 028/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 029/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 029/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 030/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 030/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 031/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 031/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 032/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 032/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 033/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 033/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 034/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 034/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 035/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 035/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 036/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 036/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 037/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 037/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 038/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 038/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 039/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 039/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 040/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 040/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 041/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 041/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 042/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 042/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 043/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 043/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 044/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 044/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 045/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 045/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 046/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 046/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 047/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 047/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 048/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 048/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 049/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 049/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 050/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 050/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 051/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 051/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 052/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 052/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 053/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 053/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 054/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 054/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 055/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 055/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 056/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 056/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 057/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 057/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 058/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 058/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 059/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 059/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 060/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 060/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 061/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 061/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 062/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 062/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 063/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 063/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 064/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 064/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 065/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 065/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 066/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 066/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 067/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 067/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 068/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 068/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 069/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 069/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 070/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 070/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 071/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 071/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 072/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 072/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 073/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 073/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 074/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 074/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 075/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 075/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 076/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 076/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 077/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 077/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 078/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 078/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 079/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 079/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 080/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 080/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 081/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 081/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 082/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 082/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 083/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 083/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 084/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 084/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 085/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 085/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 086/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 086/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 087/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 087/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 088/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 088/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 089/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 089/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 090/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 090/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 091/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 091/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 092/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 092/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 093/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 093/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 094/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 094/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 095/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 095/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 096/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 096/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 097/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 097/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 098/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 098/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 099/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 099/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 100/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 100/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 101/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 101/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 102/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 102/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 103/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 103/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 104/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 104/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 105/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 105/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 106/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 106/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 107/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 107/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 108/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 108/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 109/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 109/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 110/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 110/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 111/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 111/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 112/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 112/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 113/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 113/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 114/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 114/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 115/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 115/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 116/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 116/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 117/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 117/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 118/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain/Round 118/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 000/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 000/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 001/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 001/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 002/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 002/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 003/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 003/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 004/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 004/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 005/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 005/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 006/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 006/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 007/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 007/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 008/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 008/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 009/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 009/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 010/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 010/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 011/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 011/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 012/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 012/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 013/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 013/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 014/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 014/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 015/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 015/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 016/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 016/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 017/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 017/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 018/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 018/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 019/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 019/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 020/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 020/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 021/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 021/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 022/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 022/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 023/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 023/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 024/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 024/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 025/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 025/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 026/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 026/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 027/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 027/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 028/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 028/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 029/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 029/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 030/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 030/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 031/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 031/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 032/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 032/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 033/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 033/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 034/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 034/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 035/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 035/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 036/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 036/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 037/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 037/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 038/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 038/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 039/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 039/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 040/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 040/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 041/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 041/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 042/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 042/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 043/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 043/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 044/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 044/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 045/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 045/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 046/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 046/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 047/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 047/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 048/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 048/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 049/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 049/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 050/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 050/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 051/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 051/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 052/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 052/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 053/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 053/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 054/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 054/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 055/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 055/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 056/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 056/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 057/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 057/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 058/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 058/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 059/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 059/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 060/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 060/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 061/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 061/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 062/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 062/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 063/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 063/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 064/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 064/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 065/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 065/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 066/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 066/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 067/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 067/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 068/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 068/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 069/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 069/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 070/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 070/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 071/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 071/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 072/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 072/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 073/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 073/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 074/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 074/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 075/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 075/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 076/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 076/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 077/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 077/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 078/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 078/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 079/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 079/PlayerCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 080/OpponentCommand.txt1
-rw-r--r--2018-tower-defence/tests/v300_normal_towers/Round 080/PlayerCommand.txt1
-rw-r--r--2019-worms/.gitignore3
-rw-r--r--2019-worms/Cargo.lock299
-rw-r--r--2019-worms/Cargo.toml23
-rw-r--r--2019-worms/Makefile16
-rw-r--r--2019-worms/README.md81
-rw-r--r--2019-worms/bot.json8
-rw-r--r--2019-worms/src/bin/benchmark.rs22
-rw-r--r--2019-worms/src/bin/explore-config.rs116
-rw-r--r--2019-worms/src/command.rs73
-rw-r--r--2019-worms/src/constants.rs218
-rw-r--r--2019-worms/src/constants/lava.rs6225
-rw-r--r--2019-worms/src/game.rs779
-rw-r--r--2019-worms/src/game/map.rs49
-rw-r--r--2019-worms/src/game/player.rs259
-rw-r--r--2019-worms/src/game/powerup.rs6
-rw-r--r--2019-worms/src/geometry.rs6
-rw-r--r--2019-worms/src/geometry/direction.rs67
-rw-r--r--2019-worms/src/geometry/point.rs37
-rw-r--r--2019-worms/src/geometry/vec.rs62
-rw-r--r--2019-worms/src/json.rs554
-rw-r--r--2019-worms/src/lib.rs8
-rw-r--r--2019-worms/src/main.rs46
-rw-r--r--2019-worms/src/strategy.rs5
-rw-r--r--2019-worms/src/strategy/minimax.rs330
-rw-r--r--2019-worms/tests/example-state.json1
-rwxr-xr-x2019-worms/tests/import-replay.sh12
-rw-r--r--2019-worms/tests/official-runner-matching.rs238
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.15.02/A-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.15.02/A-log.csv298
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.15.02/B-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.15.02/B-log.csv298
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.31.16/A-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.31.16/A-log.csv274
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.31.16/B-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.31.16/B-log.csv274
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.57.04/A-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.57.04/A-log.csv214
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.57.04/B-init.json1
-rw-r--r--2019-worms/tests/replays/2019.08.19.21.57.04/B-log.csv214
-rw-r--r--2019-worms/tests/strategy.rs27
-rw-r--r--readme.org4
708 files changed, 30105 insertions, 0 deletions
diff --git a/2013-battlecity/.gitignore b/2013-battlecity/.gitignore
new file mode 100644
index 0000000..ef293be
--- /dev/null
+++ b/2013-battlecity/.gitignore
@@ -0,0 +1,4 @@
+Entelect.BattleCity.Challenge/obj
+Entelect.BattleCity.Challenge/bin
+*.suo
+*.csproj.user
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge.sln b/2013-battlecity/Entelect.BattleCity.Challenge.sln
new file mode 100644
index 0000000..2b5f5ea
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entelect.BattleCity.Challenge", "Entelect.BattleCity.Challenge\Entelect.BattleCity.Challenge.csproj", "{B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/AiAgent.cs b/2013-battlecity/Entelect.BattleCity.Challenge/AiAgent.cs
new file mode 100644
index 0000000..a1ca0e5
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/AiAgent.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+
+namespace Entelect.BattleCity.Challenge
+{
+ class AiAgent
+ {
+ private int? _lastX;
+ private int? _lastY;
+ private ChallengeService.action _lastAction;
+ private bool _hasShotFromLastPosition;
+
+ private int _tankId;
+
+ private int? _targetX;
+
+ private bool _checkForOpenPathToMiddle;
+ private bool _headingToMiddle;
+
+ public AiAgent(int tankId, bool checkForOpenPathToMiddle)
+ {
+ _tankId = tankId;
+ _checkForOpenPathToMiddle = checkForOpenPathToMiddle;
+ _lastAction = ChallengeService.action.NONE;
+ _hasShotFromLastPosition = false;
+ _headingToMiddle = false;
+ }
+
+ public Move GetBestMove(ChallengeService.game game, BoardCell[][] board, ChallengeService.player me, ChallengeService.player enemy)
+ {
+ Move move = null;
+ ChallengeService.unit tank = findTankInPlayer(_tankId, me);
+
+ if (tank == null)
+ {
+ return null;
+ }
+
+ if (tank.x != _lastX || tank.y != _lastY)
+ {
+ _hasShotFromLastPosition = false;
+ }
+
+ var bulletInAir = checkIsBulletInAir(board, me, tank);
+ var stuckLastTurn = checkStuckLastTurn(tank);
+
+ var enemyBase = enemy.@base;
+
+ var pastMidpoint = (Math.Abs(enemyBase.y-tank.y) < (board[0].Length / 2));
+
+ if (stuckLastTurn && (tank.direction == ChallengeService.direction.UP || tank.direction == ChallengeService.direction.DOWN) && enemyBase.x != tank.x)
+ {
+ _targetX = tank.x + (pastMidpoint!=(tank.x > enemyBase.x) ? +1 : -1);
+ }
+
+ if (_checkForOpenPathToMiddle && !_headingToMiddle && tank.x != enemyBase.x)
+ {
+ _headingToMiddle = testPathToMiddleIsOpen(board, tank, enemyBase);
+ }
+ else if (_checkForOpenPathToMiddle && _headingToMiddle && tank.x == enemyBase.x)
+ {
+ _headingToMiddle = false;
+ }
+
+
+ ChallengeService.direction chosenDirection =
+ _headingToMiddle ?
+ (
+ tank.x > enemyBase.x ?
+ ChallengeService.direction.LEFT :
+ ChallengeService.direction.RIGHT
+ ) :
+ (
+ tank.y != enemyBase.y ?
+ (
+ _targetX.HasValue && _targetX != tank.x ?
+ (
+ tank.x > _targetX ?
+ ChallengeService.direction.LEFT :
+ ChallengeService.direction.RIGHT
+ ) :
+ (
+ tank.y > enemyBase.y ?
+ ChallengeService.direction.UP :
+ ChallengeService.direction.DOWN
+ )
+ ) :
+ (
+ tank.x > enemyBase.x ?
+ ChallengeService.direction.LEFT :
+ ChallengeService.direction.RIGHT
+ )
+ );
+
+ if (chosenDirection != tank.direction || bulletInAir || _headingToMiddle)
+ {
+ move = MoveInDirection(tank.id, chosenDirection);
+ }
+ else
+ {
+ move = new Move(tank.id, ChallengeService.action.FIRE);
+ _hasShotFromLastPosition = true;
+ }
+
+ _lastX = tank.x;
+ _lastY = tank.y;
+ _lastAction = move.Action;
+
+ return move;
+ }
+
+ private bool testPathToMiddleIsOpen(BoardCell[][] board, ChallengeService.unit tank, ChallengeService.@base enemyBase)
+ {
+ var minY = tank.y - 2;
+ var maxY = tank.y + 2;
+ var minX = Math.Min(tank.x, enemyBase.x)-2;
+ var maxX = Math.Max(tank.x, enemyBase.x)+2;
+
+ bool insideRange = board.Length > maxX && board[maxX].Length > maxY && 0 <= minX && 0 <= minY;
+ if (!insideRange)
+ {
+ return false;
+ }
+
+ for (int x = minX; x <= maxX; ++x)
+ {
+ for (int y = minY; y <= maxY; ++y)
+ {
+ if (board[x][y] != BoardCell.EMPTY)
+ {
+ return false;
+ }
+
+ }
+ }
+
+ return true;
+ }
+
+ private ChallengeService.unit findTankInPlayer(int tankId, ChallengeService.player me)
+ {
+ if (me != null && me.units != null)
+ {
+ foreach (var unit in me.units)
+ {
+ if (unit.id == _tankId)
+ {
+ return unit;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Move MoveInDirection(int tankId, ChallengeService.direction direction)
+ {
+ switch (direction)
+ {
+ case ChallengeService.direction.UP:
+ return new Move(tankId, ChallengeService.action.UP);
+ case ChallengeService.direction.DOWN:
+ return new Move(tankId, ChallengeService.action.DOWN);
+ case ChallengeService.direction.LEFT:
+ return new Move(tankId, ChallengeService.action.LEFT);
+ case ChallengeService.direction.RIGHT:
+ return new Move(tankId, ChallengeService.action.RIGHT);
+ default:
+ return new Move(tankId, ChallengeService.action.NONE);
+ }
+ }
+
+ private bool checkIsBulletInAir(BoardCell[][] board, ChallengeService.player me, ChallengeService.unit tank)
+ {
+ var bulletInAir = false;
+ if (me.bullets != null)
+ {
+ foreach (var bullet in me.bullets)
+ {
+ if (Math.Abs(bullet.x - tank.x) < board.Length / 6)
+ {
+ bulletInAir = true;
+ }
+ }
+ }
+
+ return bulletInAir;
+ }
+
+ private bool checkStuckLastTurn(ChallengeService.unit tank)
+ {
+ return !(_lastAction == ChallengeService.action.FIRE || _lastAction == ChallengeService.action.NONE)
+ && tank.x == _lastX && tank.y == _lastY
+ && _hasShotFromLastPosition;
+ }
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/App.config b/2013-battlecity/Entelect.BattleCity.Challenge/App.config
new file mode 100644
index 0000000..ef162fd
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/App.config
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+ <system.serviceModel>
+ <bindings>
+ <basicHttpBinding>
+ <binding name="ChallengeServiceSoapBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
+ <readerQuotas maxDepth="32" maxStringContentLength="2147483647"
+ maxArrayLength="16348" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
+ </binding>
+ </basicHttpBinding>
+ </bindings>
+ <client>
+ <endpoint address="http://localhost:9090/ChallengePort" binding="basicHttpBinding"
+ bindingConfiguration="ChallengeServiceSoapBinding" contract="ChallengeService.Challenge"
+ name="ChallengePort" />
+ </client>
+ </system.serviceModel>
+</configuration> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/BoardCell.cs b/2013-battlecity/Entelect.BattleCity.Challenge/BoardCell.cs
new file mode 100644
index 0000000..bd7063e
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/BoardCell.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entelect.BattleCity.Challenge
+{
+ public enum BoardCell
+ {
+ EMPTY,
+ WALL,
+ BASE,
+ OUT_OF_BOUNDS
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Entelect.BattleCity.Challenge.csproj b/2013-battlecity/Entelect.BattleCity.Challenge/Entelect.BattleCity.Challenge.csproj
new file mode 100644
index 0000000..0a8cb97
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Entelect.BattleCity.Challenge.csproj
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{B9BDD8BB-6AB8-47A9-810C-F689C5CE3259}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Entelect.BattleCity.Challenge</RootNamespace>
+ <AssemblyName>Entelect.BattleCity.Challenge</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.ServiceModel" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AiAgent.cs" />
+ <Compile Include="BoardCell.cs" />
+ <Compile Include="GameInProgress.cs" />
+ <Compile Include="Move.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Service References\ChallengeService\Reference.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <None Include="Service References\ChallengeService\ChallengeService.wsdl" />
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.delta.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.game.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.loginResponse.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.setActionResponse.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ <None Include="Service References\ChallengeService\Entelect.BattleCity.Challenge.ChallengeService.state.datasource">
+ <DependentUpon>Reference.svcmap</DependentUpon>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <WCFMetadata Include="Service References\" />
+ </ItemGroup>
+ <ItemGroup>
+ <WCFMetadataStorage Include="Service References\ChallengeService\" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Service References\ChallengeService\configuration91.svcinfo" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Service References\ChallengeService\configuration.svcinfo" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Service References\ChallengeService\Reference.svcmap">
+ <Generator>WCF Proxy Generator</Generator>
+ <LastGenOutput>Reference.cs</LastGenOutput>
+ </None>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/GameInProgress.cs b/2013-battlecity/Entelect.BattleCity.Challenge/GameInProgress.cs
new file mode 100644
index 0000000..23a6550
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/GameInProgress.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Windows;
+using System.Collections.Generic;
+using System.Threading;
+using System.Diagnostics;
+using System.Text;
+
+namespace Entelect.BattleCity.Challenge
+{
+ public class GameInProgress
+ {
+ private static ChallengeService.action _nullAction = ChallengeService.action.NONE;
+
+ private ChallengeService.ChallengeClient _service;
+ private ChallengeService.game _currentState;
+ private ChallengeService.player _me;
+ private ChallengeService.player _enemy;
+
+ private BoardCell[][] _board;
+
+ private AiAgent _tank1Ai;
+ private AiAgent _tank2Ai;
+
+ private Stopwatch _stepTimer;
+
+ public GameInProgress(ChallengeService.ChallengeClient service, ChallengeService.state?[][] board)
+ {
+ _service = service;
+ _board = getBoardCellArrayFromServiceStates(board);
+
+ updateGameStatus(true);
+ _board[_me.@base.x][_me.@base.y] = BoardCell.BASE;
+ _board[_enemy.@base.x][_enemy.@base.y] = BoardCell.BASE;
+
+ _tank1Ai = new AiAgent(_me.units[0].id, false);
+ _tank2Ai = new AiAgent(_me.units[1].id, true);
+ }
+
+
+
+ public void run()
+ {
+ while (true)
+ {
+ if (_currentState.millisecondsToNextTick-_stepTimer.ElapsedMilliseconds < 0)
+ {
+ updateGameStatus();
+ continue;
+ }
+ makeNextMove();
+ waitForNextTick();
+
+ updateGameStatus();
+ }
+ }
+
+ private void makeNextMove()
+ {
+ Move tank1Move = _tank1Ai.GetBestMove(_currentState, _board, _me, _enemy);
+ Move tank2Move = _tank2Ai.GetBestMove(_currentState, _board, _me, _enemy);
+
+ sendMovesToService(tank1Move, tank2Move);
+ }
+
+ private void sendMovesToService(Move tank1Move, Move tank2Move)
+ {
+ if (tank1Move != null && tank2Move != null)
+ {
+ _service.setActions(tank1Move.Action, tank2Move.Action);
+ }
+ else if (tank1Move != null)
+ {
+ _service.setAction(tank1Move.Tank, tank1Move.Action);
+ }
+ else if (tank2Move != null)
+ {
+ _service.setAction(tank2Move.Tank, tank2Move.Action);
+ }
+ }
+
+ private void waitForNextTick()
+ {
+ var sleepTime = TimeSpan.FromMilliseconds(_currentState.millisecondsToNextTick-_stepTimer.ElapsedMilliseconds+500);
+ if (sleepTime.Ticks > 0L)
+ {
+ try
+ {
+ Thread.Sleep(sleepTime);
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+
+ private void updateGameStatus(bool firstTime = false)
+ {
+ if (firstTime)
+ {
+ _currentState = _service.getStatus();
+ }
+ else
+ {
+ var previousTick = _currentState.currentTick;
+ while (previousTick == _currentState.currentTick)
+ {
+ _currentState = _service.getStatus();
+ }
+ }
+ _stepTimer = Stopwatch.StartNew();
+
+ foreach (ChallengeService.player player in _currentState.players)
+ {
+ if (player.name.Equals(_currentState.playerName))
+ {
+ _me = player;
+ }
+ else
+ {
+ _enemy = player;
+ }
+ }
+ }
+
+ private BoardCell[][] getBoardCellArrayFromServiceStates(ChallengeService.state?[][] stateBoard)
+ {
+ BoardCell[][] newBoard = new BoardCell[stateBoard.Length][];
+ for (int x = 0; x < stateBoard.Length; ++x)
+ {
+ newBoard[x] = new BoardCell[stateBoard[x].Length];
+ for (int y = 0; y < stateBoard[x].Length; ++y)
+ {
+ switch (stateBoard[x][y])
+ {
+ case null:
+ case ChallengeService.state.NONE:
+ case ChallengeService.state.EMPTY:
+ newBoard[x][y] = BoardCell.EMPTY;
+ break;
+ case ChallengeService.state.FULL:
+ newBoard[x][y] = BoardCell.WALL;
+ break;
+ case ChallengeService.state.OUT_OF_BOUNDS:
+ newBoard[x][y] = BoardCell.OUT_OF_BOUNDS;
+ break;
+ default:
+ newBoard[x][y] = BoardCell.OUT_OF_BOUNDS;
+ break;
+ }
+
+ }
+ }
+
+ return newBoard;
+ }
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Move.cs b/2013-battlecity/Entelect.BattleCity.Challenge/Move.cs
new file mode 100644
index 0000000..7fba664
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Move.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Entelect.BattleCity.Challenge
+{
+ class Move
+ {
+ public int Tank { get; private set; }
+ public ChallengeService.action Action { get; private set; }
+
+ public Move(int tank, ChallengeService.action action)
+ {
+ Tank = tank;
+ Action = action;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("Tank {0}: {1}", Tank, Action.ToString());
+ }
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Program.cs b/2013-battlecity/Entelect.BattleCity.Challenge/Program.cs
new file mode 100644
index 0000000..376807a
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Program.cs
@@ -0,0 +1,27 @@
+using System;
+using System.ServiceModel;
+
+namespace Entelect.BattleCity.Challenge
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ try
+ {
+ var endpointConfigurationName = "ChallengePort";
+ var address = new EndpointAddress(args[0]);
+ var service = new ChallengeService.ChallengeClient(endpointConfigurationName, address);
+ var board = service.login();
+
+ var game = new GameInProgress(service, board);
+ game.run();
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("Uncaught exception thrown. Exiting.");
+ Console.Error.WriteLine(ex.StackTrace.ToString());
+ }
+ }
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Properties/AssemblyInfo.cs b/2013-battlecity/Entelect.BattleCity.Challenge/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..97397b4
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Entelect.BattleCity.Challenge")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Entelect.BattleCity.Challenge")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8e9e513d-3232-4b92-83dd-13ec54131857")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/ChallengeService.wsdl b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/ChallengeService.wsdl
new file mode 100644
index 0000000..0d7d4a9
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/ChallengeService.wsdl
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://challenge.entelect.co.za/" name="ChallengeService" targetNamespace="http://challenge.entelect.co.za/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+ <wsdl:types>
+ <xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://challenge.entelect.co.za/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="getStatus" type="tns:getStatus" />
+ <xs:element name="getStatusResponse" type="tns:getStatusResponse" />
+ <xs:element name="login" type="tns:login" />
+ <xs:element name="loginResponse" type="tns:loginResponse" />
+ <xs:element name="setAction" type="tns:setAction" />
+ <xs:element name="setActionResponse" type="tns:setActionResponse" />
+ <xs:element name="setActions" type="tns:setActions" />
+ <xs:element name="setActionsResponse" type="tns:setActionsResponse" />
+ <xs:complexType name="getStatus">
+ <xs:sequence />
+ </xs:complexType>
+ <xs:complexType name="getStatusResponse">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="return" type="tns:game" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="game">
+ <xs:sequence>
+ <xs:element name="currentTick" type="xs:int" />
+ <xs:element minOccurs="0" name="events" type="tns:events" />
+ <xs:element name="millisecondsToNextTick" type="xs:long" />
+ <xs:element minOccurs="0" name="nextTickTime" type="xs:dateTime" />
+ <xs:element minOccurs="0" name="playerName" type="xs:string" />
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="players" nillable="true" type="tns:player" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="events">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="blockEvents" nillable="true" type="tns:blockEvent" />
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="unitEvents" nillable="true" type="tns:unitEvent" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="blockEvent">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="newState" type="tns:state" />
+ <xs:element minOccurs="0" name="point" type="tns:point" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="point">
+ <xs:sequence>
+ <xs:element name="x" type="xs:int" />
+ <xs:element name="y" type="xs:int" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="unitEvent">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="bullet" type="tns:bullet" />
+ <xs:element name="tickTime" type="xs:int" />
+ <xs:element minOccurs="0" name="unit" type="tns:unit" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="bullet">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="direction" type="tns:direction" />
+ <xs:element name="id" type="xs:int" />
+ <xs:element name="x" type="xs:int" />
+ <xs:element name="y" type="xs:int" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="unit">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="action" type="tns:action" />
+ <xs:element minOccurs="0" name="direction" type="tns:direction" />
+ <xs:element name="id" type="xs:int" />
+ <xs:element name="x" type="xs:int" />
+ <xs:element name="y" type="xs:int" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="player">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="base" type="tns:base" />
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="bullets" nillable="true" type="tns:bullet" />
+ <xs:element minOccurs="0" name="name" type="xs:string" />
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="units" nillable="true" type="tns:unit" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="base">
+ <xs:sequence>
+ <xs:element name="x" type="xs:int" />
+ <xs:element name="y" type="xs:int" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="abstractCollection" abstract="true">
+ <xs:sequence />
+ </xs:complexType>
+ <xs:complexType name="abstractList" abstract="true">
+ <xs:complexContent mixed="false">
+ <xs:extension base="tns:abstractCollection">
+ <xs:sequence />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="arrayList">
+ <xs:complexContent mixed="false">
+ <xs:extension base="tns:abstractList">
+ <xs:sequence />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="setAction">
+ <xs:sequence>
+ <xs:element name="arg0" type="xs:int" />
+ <xs:element minOccurs="0" name="arg1" type="tns:action" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="setActionResponse">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="return" type="tns:delta" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="delta">
+ <xs:sequence>
+ <xs:element name="millisecondsToNextTick" type="xs:long" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="login">
+ <xs:sequence />
+ </xs:complexType>
+ <xs:complexType name="loginResponse">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="return" type="tns:board" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="board">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="states" nillable="true" type="tns:stateArray" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="setActions">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="arg0" type="tns:action" />
+ <xs:element minOccurs="0" name="arg1" type="tns:action" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="setActionsResponse">
+ <xs:sequence>
+ <xs:element minOccurs="0" name="return" type="tns:delta" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="state">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="FULL" />
+ <xs:enumeration value="EMPTY" />
+ <xs:enumeration value="OUT_OF_BOUNDS" />
+ <xs:enumeration value="NONE" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="direction">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE" />
+ <xs:enumeration value="UP" />
+ <xs:enumeration value="DOWN" />
+ <xs:enumeration value="LEFT" />
+ <xs:enumeration value="RIGHT" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="action">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE" />
+ <xs:enumeration value="UP" />
+ <xs:enumeration value="DOWN" />
+ <xs:enumeration value="LEFT" />
+ <xs:enumeration value="RIGHT" />
+ <xs:enumeration value="FIRE" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="stateArray" final="#all">
+ <xs:sequence>
+ <xs:element minOccurs="0" maxOccurs="unbounded" name="item" nillable="true" type="tns:state" />
+ </xs:sequence>
+ </xs:complexType>
+ <xs:element name="EndOfGameException" type="tns:EndOfGameException" />
+ <xs:complexType name="EndOfGameException">
+ <xs:sequence />
+ </xs:complexType>
+ <xs:element name="NoBlameException" type="tns:NoBlameException" />
+ <xs:complexType name="NoBlameException">
+ <xs:sequence />
+ </xs:complexType>
+ </xs:schema>
+ </wsdl:types>
+ <wsdl:message name="setAction">
+ <wsdl:part name="parameters" element="tns:setAction" />
+ </wsdl:message>
+ <wsdl:message name="EndOfGameException">
+ <wsdl:part name="EndOfGameException" element="tns:EndOfGameException" />
+ </wsdl:message>
+ <wsdl:message name="setActions">
+ <wsdl:part name="parameters" element="tns:setActions" />
+ </wsdl:message>
+ <wsdl:message name="login">
+ <wsdl:part name="parameters" element="tns:login" />
+ </wsdl:message>
+ <wsdl:message name="setActionsResponse">
+ <wsdl:part name="parameters" element="tns:setActionsResponse" />
+ </wsdl:message>
+ <wsdl:message name="setActionResponse">
+ <wsdl:part name="parameters" element="tns:setActionResponse" />
+ </wsdl:message>
+ <wsdl:message name="loginResponse">
+ <wsdl:part name="parameters" element="tns:loginResponse" />
+ </wsdl:message>
+ <wsdl:message name="NoBlameException">
+ <wsdl:part name="NoBlameException" element="tns:NoBlameException" />
+ </wsdl:message>
+ <wsdl:message name="getStatus">
+ <wsdl:part name="parameters" element="tns:getStatus" />
+ </wsdl:message>
+ <wsdl:message name="getStatusResponse">
+ <wsdl:part name="parameters" element="tns:getStatusResponse" />
+ </wsdl:message>
+ <wsdl:portType name="Challenge">
+ <wsdl:operation name="getStatus">
+ <wsdl:input name="getStatus" message="tns:getStatus" />
+ <wsdl:output name="getStatusResponse" message="tns:getStatusResponse" />
+ </wsdl:operation>
+ <wsdl:operation name="setAction">
+ <wsdl:input name="setAction" message="tns:setAction" />
+ <wsdl:output name="setActionResponse" message="tns:setActionResponse" />
+ <wsdl:fault name="EndOfGameException" message="tns:EndOfGameException" />
+ </wsdl:operation>
+ <wsdl:operation name="login">
+ <wsdl:input name="login" message="tns:login" />
+ <wsdl:output name="loginResponse" message="tns:loginResponse" />
+ <wsdl:fault name="NoBlameException" message="tns:NoBlameException" />
+ <wsdl:fault name="EndOfGameException" message="tns:EndOfGameException" />
+ </wsdl:operation>
+ <wsdl:operation name="setActions">
+ <wsdl:input name="setActions" message="tns:setActions" />
+ <wsdl:output name="setActionsResponse" message="tns:setActionsResponse" />
+ <wsdl:fault name="EndOfGameException" message="tns:EndOfGameException" />
+ </wsdl:operation>
+ </wsdl:portType>
+ <wsdl:binding name="ChallengeServiceSoapBinding" type="tns:Challenge">
+ <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
+ <wsdl:operation name="getStatus">
+ <soap:operation soapAction="" style="document" />
+ <wsdl:input name="getStatus">
+ <soap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="getStatusResponse">
+ <soap:body use="literal" />
+ </wsdl:output>
+ </wsdl:operation>
+ <wsdl:operation name="setAction">
+ <soap:operation soapAction="" style="document" />
+ <wsdl:input name="setAction">
+ <soap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="setActionResponse">
+ <soap:body use="literal" />
+ </wsdl:output>
+ <wsdl:fault name="EndOfGameException">
+ <soap:fault use="literal" name="EndOfGameException" namespace="" />
+ </wsdl:fault>
+ </wsdl:operation>
+ <wsdl:operation name="login">
+ <soap:operation soapAction="" style="document" />
+ <wsdl:input name="login">
+ <soap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="loginResponse">
+ <soap:body use="literal" />
+ </wsdl:output>
+ <wsdl:fault name="NoBlameException">
+ <soap:fault use="literal" name="NoBlameException" namespace="" />
+ </wsdl:fault>
+ <wsdl:fault name="EndOfGameException">
+ <soap:fault use="literal" name="EndOfGameException" namespace="" />
+ </wsdl:fault>
+ </wsdl:operation>
+ <wsdl:operation name="setActions">
+ <soap:operation soapAction="" style="document" />
+ <wsdl:input name="setActions">
+ <soap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="setActionsResponse">
+ <soap:body use="literal" />
+ </wsdl:output>
+ <wsdl:fault name="EndOfGameException">
+ <soap:fault use="literal" name="EndOfGameException" namespace="" />
+ </wsdl:fault>
+ </wsdl:operation>
+ </wsdl:binding>
+ <wsdl:service name="ChallengeService">
+ <wsdl:port name="ChallengePort" binding="tns:ChallengeServiceSoapBinding">
+ <soap:address location="http://localhost:9090/ChallengePort" />
+ </wsdl:port>
+ </wsdl:service>
+</wsdl:definitions> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.delta.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.delta.datasource
new file mode 100644
index 0000000..ba30593
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.delta.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="delta" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.delta, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.game.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.game.datasource
new file mode 100644
index 0000000..295ea08
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.game.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="game" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.game, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse.datasource
new file mode 100644
index 0000000..6701b3e
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="getStatusResponse" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.loginResponse.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.loginResponse.datasource
new file mode 100644
index 0000000..edbecd4
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.loginResponse.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="loginResponse" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.loginResponse, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionResponse.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionResponse.datasource
new file mode 100644
index 0000000..4992e1b
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionResponse.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="setActionResponse" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.setActionResponse, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse.datasource
new file mode 100644
index 0000000..4ea2b14
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="setActionsResponse" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.state.datasource b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.state.datasource
new file mode 100644
index 0000000..caa94f3
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Entelect.BattleCity.Challenge.ChallengeService.state.datasource
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is automatically generated by Visual Studio .Net. It is
+ used to store generic object data source configuration information.
+ Renaming the file extension or editing the content of this file may
+ cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="state" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+ <TypeInfo>Entelect.BattleCity.Challenge.ChallengeService.state, Service References.ChallengeService.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.cs b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.cs
new file mode 100644
index 0000000..f7fda50
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.cs
@@ -0,0 +1,1104 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18051
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Entelect.BattleCity.Challenge.ChallengeService {
+
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class EndOfGameException : object, System.ComponentModel.INotifyPropertyChanged {
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class delta : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private long millisecondsToNextTickField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public long millisecondsToNextTick {
+ get {
+ return this.millisecondsToNextTickField;
+ }
+ set {
+ this.millisecondsToNextTickField = value;
+ this.RaisePropertyChanged("millisecondsToNextTick");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class @base : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private int xField;
+
+ private int yField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public int x {
+ get {
+ return this.xField;
+ }
+ set {
+ this.xField = value;
+ this.RaisePropertyChanged("x");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public int y {
+ get {
+ return this.yField;
+ }
+ set {
+ this.yField = value;
+ this.RaisePropertyChanged("y");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class player : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private @base baseField;
+
+ private bullet[] bulletsField;
+
+ private string nameField;
+
+ private unit[] unitsField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public @base @base {
+ get {
+ return this.baseField;
+ }
+ set {
+ this.baseField = value;
+ this.RaisePropertyChanged("base");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute("bullets", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=1)]
+ public bullet[] bullets {
+ get {
+ return this.bulletsField;
+ }
+ set {
+ this.bulletsField = value;
+ this.RaisePropertyChanged("bullets");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
+ public string name {
+ get {
+ return this.nameField;
+ }
+ set {
+ this.nameField = value;
+ this.RaisePropertyChanged("name");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute("units", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=3)]
+ public unit[] units {
+ get {
+ return this.unitsField;
+ }
+ set {
+ this.unitsField = value;
+ this.RaisePropertyChanged("units");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class bullet : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private direction directionField;
+
+ private bool directionFieldSpecified;
+
+ private int idField;
+
+ private int xField;
+
+ private int yField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public direction direction {
+ get {
+ return this.directionField;
+ }
+ set {
+ this.directionField = value;
+ this.RaisePropertyChanged("direction");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ public bool directionSpecified {
+ get {
+ return this.directionFieldSpecified;
+ }
+ set {
+ this.directionFieldSpecified = value;
+ this.RaisePropertyChanged("directionSpecified");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public int id {
+ get {
+ return this.idField;
+ }
+ set {
+ this.idField = value;
+ this.RaisePropertyChanged("id");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
+ public int x {
+ get {
+ return this.xField;
+ }
+ set {
+ this.xField = value;
+ this.RaisePropertyChanged("x");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=3)]
+ public int y {
+ get {
+ return this.yField;
+ }
+ set {
+ this.yField = value;
+ this.RaisePropertyChanged("y");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public enum direction {
+
+ /// <remarks/>
+ NONE,
+
+ /// <remarks/>
+ UP,
+
+ /// <remarks/>
+ DOWN,
+
+ /// <remarks/>
+ LEFT,
+
+ /// <remarks/>
+ RIGHT,
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class unit : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private action actionField;
+
+ private bool actionFieldSpecified;
+
+ private direction directionField;
+
+ private bool directionFieldSpecified;
+
+ private int idField;
+
+ private int xField;
+
+ private int yField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public action action {
+ get {
+ return this.actionField;
+ }
+ set {
+ this.actionField = value;
+ this.RaisePropertyChanged("action");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ public bool actionSpecified {
+ get {
+ return this.actionFieldSpecified;
+ }
+ set {
+ this.actionFieldSpecified = value;
+ this.RaisePropertyChanged("actionSpecified");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public direction direction {
+ get {
+ return this.directionField;
+ }
+ set {
+ this.directionField = value;
+ this.RaisePropertyChanged("direction");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ public bool directionSpecified {
+ get {
+ return this.directionFieldSpecified;
+ }
+ set {
+ this.directionFieldSpecified = value;
+ this.RaisePropertyChanged("directionSpecified");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
+ public int id {
+ get {
+ return this.idField;
+ }
+ set {
+ this.idField = value;
+ this.RaisePropertyChanged("id");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=3)]
+ public int x {
+ get {
+ return this.xField;
+ }
+ set {
+ this.xField = value;
+ this.RaisePropertyChanged("x");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=4)]
+ public int y {
+ get {
+ return this.yField;
+ }
+ set {
+ this.yField = value;
+ this.RaisePropertyChanged("y");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public enum action {
+
+ /// <remarks/>
+ NONE,
+
+ /// <remarks/>
+ UP,
+
+ /// <remarks/>
+ DOWN,
+
+ /// <remarks/>
+ LEFT,
+
+ /// <remarks/>
+ RIGHT,
+
+ /// <remarks/>
+ FIRE,
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class unitEvent : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private bullet bulletField;
+
+ private int tickTimeField;
+
+ private unit unitField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public bullet bullet {
+ get {
+ return this.bulletField;
+ }
+ set {
+ this.bulletField = value;
+ this.RaisePropertyChanged("bullet");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public int tickTime {
+ get {
+ return this.tickTimeField;
+ }
+ set {
+ this.tickTimeField = value;
+ this.RaisePropertyChanged("tickTime");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
+ public unit unit {
+ get {
+ return this.unitField;
+ }
+ set {
+ this.unitField = value;
+ this.RaisePropertyChanged("unit");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class point : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private int xField;
+
+ private int yField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public int x {
+ get {
+ return this.xField;
+ }
+ set {
+ this.xField = value;
+ this.RaisePropertyChanged("x");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public int y {
+ get {
+ return this.yField;
+ }
+ set {
+ this.yField = value;
+ this.RaisePropertyChanged("y");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class blockEvent : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private state newStateField;
+
+ private bool newStateFieldSpecified;
+
+ private point pointField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public state newState {
+ get {
+ return this.newStateField;
+ }
+ set {
+ this.newStateField = value;
+ this.RaisePropertyChanged("newState");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ public bool newStateSpecified {
+ get {
+ return this.newStateFieldSpecified;
+ }
+ set {
+ this.newStateFieldSpecified = value;
+ this.RaisePropertyChanged("newStateSpecified");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public point point {
+ get {
+ return this.pointField;
+ }
+ set {
+ this.pointField = value;
+ this.RaisePropertyChanged("point");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public enum state {
+
+ /// <remarks/>
+ FULL,
+
+ /// <remarks/>
+ EMPTY,
+
+ /// <remarks/>
+ OUT_OF_BOUNDS,
+
+ /// <remarks/>
+ NONE,
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class events : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private blockEvent[] blockEventsField;
+
+ private unitEvent[] unitEventsField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute("blockEvents", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=0)]
+ public blockEvent[] blockEvents {
+ get {
+ return this.blockEventsField;
+ }
+ set {
+ this.blockEventsField = value;
+ this.RaisePropertyChanged("blockEvents");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute("unitEvents", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=1)]
+ public unitEvent[] unitEvents {
+ get {
+ return this.unitEventsField;
+ }
+ set {
+ this.unitEventsField = value;
+ this.RaisePropertyChanged("unitEvents");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class game : object, System.ComponentModel.INotifyPropertyChanged {
+
+ private int currentTickField;
+
+ private events eventsField;
+
+ private long millisecondsToNextTickField;
+
+ private System.DateTime nextTickTimeField;
+
+ private bool nextTickTimeFieldSpecified;
+
+ private string playerNameField;
+
+ private player[] playersField;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
+ public int currentTick {
+ get {
+ return this.currentTickField;
+ }
+ set {
+ this.currentTickField = value;
+ this.RaisePropertyChanged("currentTick");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
+ public events events {
+ get {
+ return this.eventsField;
+ }
+ set {
+ this.eventsField = value;
+ this.RaisePropertyChanged("events");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
+ public long millisecondsToNextTick {
+ get {
+ return this.millisecondsToNextTickField;
+ }
+ set {
+ this.millisecondsToNextTickField = value;
+ this.RaisePropertyChanged("millisecondsToNextTick");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=3)]
+ public System.DateTime nextTickTime {
+ get {
+ return this.nextTickTimeField;
+ }
+ set {
+ this.nextTickTimeField = value;
+ this.RaisePropertyChanged("nextTickTime");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlIgnoreAttribute()]
+ public bool nextTickTimeSpecified {
+ get {
+ return this.nextTickTimeFieldSpecified;
+ }
+ set {
+ this.nextTickTimeFieldSpecified = value;
+ this.RaisePropertyChanged("nextTickTimeSpecified");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=4)]
+ public string playerName {
+ get {
+ return this.playerNameField;
+ }
+ set {
+ this.playerNameField = value;
+ this.RaisePropertyChanged("playerName");
+ }
+ }
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlElementAttribute("players", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=5)]
+ public player[] players {
+ get {
+ return this.playersField;
+ }
+ set {
+ this.playersField = value;
+ this.RaisePropertyChanged("players");
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ /// <remarks/>
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18053")]
+ [System.SerializableAttribute()]
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.ComponentModel.DesignerCategoryAttribute("code")]
+ [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://challenge.entelect.co.za/")]
+ public partial class NoBlameException : object, System.ComponentModel.INotifyPropertyChanged {
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged(string propertyName) {
+ System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if ((propertyChanged != null)) {
+ propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ServiceModel.ServiceContractAttribute(Namespace="http://challenge.entelect.co.za/", ConfigurationName="ChallengeService.Challenge")]
+ public interface Challenge {
+
+ // CODEGEN: Parameter 'return' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
+ [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
+ Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse getStatus(Entelect.BattleCity.Challenge.ChallengeService.getStatus request);
+
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse> getStatusAsync(Entelect.BattleCity.Challenge.ChallengeService.getStatus request);
+
+ // CODEGEN: Parameter 'return' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ [System.ServiceModel.FaultContractAttribute(typeof(Entelect.BattleCity.Challenge.ChallengeService.EndOfGameException), Action="", Name="EndOfGameException")]
+ [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
+ [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
+ Entelect.BattleCity.Challenge.ChallengeService.setActionResponse setAction(Entelect.BattleCity.Challenge.ChallengeService.setAction request);
+
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionResponse> setActionAsync(Entelect.BattleCity.Challenge.ChallengeService.setAction request);
+
+ // CODEGEN: Parameter 'return' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlArrayAttribute'.
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ [System.ServiceModel.FaultContractAttribute(typeof(Entelect.BattleCity.Challenge.ChallengeService.NoBlameException), Action="", Name="NoBlameException")]
+ [System.ServiceModel.FaultContractAttribute(typeof(Entelect.BattleCity.Challenge.ChallengeService.EndOfGameException), Action="", Name="EndOfGameException")]
+ [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
+ [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
+ Entelect.BattleCity.Challenge.ChallengeService.loginResponse login(Entelect.BattleCity.Challenge.ChallengeService.login request);
+
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.loginResponse> loginAsync(Entelect.BattleCity.Challenge.ChallengeService.login request);
+
+ // CODEGEN: Parameter 'return' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ [System.ServiceModel.FaultContractAttribute(typeof(Entelect.BattleCity.Challenge.ChallengeService.EndOfGameException), Action="", Name="EndOfGameException")]
+ [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
+ [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
+ Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse setActions(Entelect.BattleCity.Challenge.ChallengeService.setActions request);
+
+ [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse> setActionsAsync(Entelect.BattleCity.Challenge.ChallengeService.setActions request);
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="getStatus", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class getStatus {
+
+ public getStatus() {
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="getStatusResponse", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class getStatusResponse {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.game @return;
+
+ public getStatusResponse() {
+ }
+
+ public getStatusResponse(Entelect.BattleCity.Challenge.ChallengeService.game @return) {
+ this.@return = @return;
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="setAction", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class setAction {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public int arg0;
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=1)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.action arg1;
+
+ public setAction() {
+ }
+
+ public setAction(int arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ this.arg0 = arg0;
+ this.arg1 = arg1;
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="setActionResponse", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class setActionResponse {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.delta @return;
+
+ public setActionResponse() {
+ }
+
+ public setActionResponse(Entelect.BattleCity.Challenge.ChallengeService.delta @return) {
+ this.@return = @return;
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="login", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class login {
+
+ public login() {
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="loginResponse", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class loginResponse {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ [System.Xml.Serialization.XmlArrayItemAttribute("states", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ [System.Xml.Serialization.XmlArrayItemAttribute("item", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, NestingLevel=1)]
+ public System.Nullable<state>[][] @return;
+
+ public loginResponse() {
+ }
+
+ public loginResponse(System.Nullable<state>[][] @return) {
+ this.@return = @return;
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="setActions", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class setActions {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.action arg0;
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=1)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.action arg1;
+
+ public setActions() {
+ }
+
+ public setActions(Entelect.BattleCity.Challenge.ChallengeService.action arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ this.arg0 = arg0;
+ this.arg1 = arg1;
+ }
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ [System.ServiceModel.MessageContractAttribute(WrapperName="setActionsResponse", WrapperNamespace="http://challenge.entelect.co.za/", IsWrapped=true)]
+ public partial class setActionsResponse {
+
+ [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://challenge.entelect.co.za/", Order=0)]
+ [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
+ public Entelect.BattleCity.Challenge.ChallengeService.delta @return;
+
+ public setActionsResponse() {
+ }
+
+ public setActionsResponse(Entelect.BattleCity.Challenge.ChallengeService.delta @return) {
+ this.@return = @return;
+ }
+ }
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ public interface ChallengeChannel : Entelect.BattleCity.Challenge.ChallengeService.Challenge, System.ServiceModel.IClientChannel {
+ }
+
+ [System.Diagnostics.DebuggerStepThroughAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
+ public partial class ChallengeClient : System.ServiceModel.ClientBase<Entelect.BattleCity.Challenge.ChallengeService.Challenge>, Entelect.BattleCity.Challenge.ChallengeService.Challenge {
+
+ public ChallengeClient() {
+ }
+
+ public ChallengeClient(string endpointConfigurationName) :
+ base(endpointConfigurationName) {
+ }
+
+ public ChallengeClient(string endpointConfigurationName, string remoteAddress) :
+ base(endpointConfigurationName, remoteAddress) {
+ }
+
+ public ChallengeClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
+ base(endpointConfigurationName, remoteAddress) {
+ }
+
+ public ChallengeClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
+ base(binding, remoteAddress) {
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse Entelect.BattleCity.Challenge.ChallengeService.Challenge.getStatus(Entelect.BattleCity.Challenge.ChallengeService.getStatus request) {
+ return base.Channel.getStatus(request);
+ }
+
+ public Entelect.BattleCity.Challenge.ChallengeService.game getStatus() {
+ Entelect.BattleCity.Challenge.ChallengeService.getStatus inValue = new Entelect.BattleCity.Challenge.ChallengeService.getStatus();
+ Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse retVal = ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).getStatus(inValue);
+ return retVal.@return;
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse> Entelect.BattleCity.Challenge.ChallengeService.Challenge.getStatusAsync(Entelect.BattleCity.Challenge.ChallengeService.getStatus request) {
+ return base.Channel.getStatusAsync(request);
+ }
+
+ public System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.getStatusResponse> getStatusAsync() {
+ Entelect.BattleCity.Challenge.ChallengeService.getStatus inValue = new Entelect.BattleCity.Challenge.ChallengeService.getStatus();
+ return ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).getStatusAsync(inValue);
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ Entelect.BattleCity.Challenge.ChallengeService.setActionResponse Entelect.BattleCity.Challenge.ChallengeService.Challenge.setAction(Entelect.BattleCity.Challenge.ChallengeService.setAction request) {
+ return base.Channel.setAction(request);
+ }
+
+ public Entelect.BattleCity.Challenge.ChallengeService.delta setAction(int arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ Entelect.BattleCity.Challenge.ChallengeService.setAction inValue = new Entelect.BattleCity.Challenge.ChallengeService.setAction();
+ inValue.arg0 = arg0;
+ inValue.arg1 = arg1;
+ Entelect.BattleCity.Challenge.ChallengeService.setActionResponse retVal = ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).setAction(inValue);
+ return retVal.@return;
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionResponse> Entelect.BattleCity.Challenge.ChallengeService.Challenge.setActionAsync(Entelect.BattleCity.Challenge.ChallengeService.setAction request) {
+ return base.Channel.setActionAsync(request);
+ }
+
+ public System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionResponse> setActionAsync(int arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ Entelect.BattleCity.Challenge.ChallengeService.setAction inValue = new Entelect.BattleCity.Challenge.ChallengeService.setAction();
+ inValue.arg0 = arg0;
+ inValue.arg1 = arg1;
+ return ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).setActionAsync(inValue);
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ Entelect.BattleCity.Challenge.ChallengeService.loginResponse Entelect.BattleCity.Challenge.ChallengeService.Challenge.login(Entelect.BattleCity.Challenge.ChallengeService.login request) {
+ return base.Channel.login(request);
+ }
+
+ public System.Nullable<state>[][] login() {
+ Entelect.BattleCity.Challenge.ChallengeService.login inValue = new Entelect.BattleCity.Challenge.ChallengeService.login();
+ Entelect.BattleCity.Challenge.ChallengeService.loginResponse retVal = ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).login(inValue);
+ return retVal.@return;
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.loginResponse> Entelect.BattleCity.Challenge.ChallengeService.Challenge.loginAsync(Entelect.BattleCity.Challenge.ChallengeService.login request) {
+ return base.Channel.loginAsync(request);
+ }
+
+ public System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.loginResponse> loginAsync() {
+ Entelect.BattleCity.Challenge.ChallengeService.login inValue = new Entelect.BattleCity.Challenge.ChallengeService.login();
+ return ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).loginAsync(inValue);
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse Entelect.BattleCity.Challenge.ChallengeService.Challenge.setActions(Entelect.BattleCity.Challenge.ChallengeService.setActions request) {
+ return base.Channel.setActions(request);
+ }
+
+ public Entelect.BattleCity.Challenge.ChallengeService.delta setActions(Entelect.BattleCity.Challenge.ChallengeService.action arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ Entelect.BattleCity.Challenge.ChallengeService.setActions inValue = new Entelect.BattleCity.Challenge.ChallengeService.setActions();
+ inValue.arg0 = arg0;
+ inValue.arg1 = arg1;
+ Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse retVal = ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).setActions(inValue);
+ return retVal.@return;
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse> Entelect.BattleCity.Challenge.ChallengeService.Challenge.setActionsAsync(Entelect.BattleCity.Challenge.ChallengeService.setActions request) {
+ return base.Channel.setActionsAsync(request);
+ }
+
+ public System.Threading.Tasks.Task<Entelect.BattleCity.Challenge.ChallengeService.setActionsResponse> setActionsAsync(Entelect.BattleCity.Challenge.ChallengeService.action arg0, Entelect.BattleCity.Challenge.ChallengeService.action arg1) {
+ Entelect.BattleCity.Challenge.ChallengeService.setActions inValue = new Entelect.BattleCity.Challenge.ChallengeService.setActions();
+ inValue.arg0 = arg0;
+ inValue.arg1 = arg1;
+ return ((Entelect.BattleCity.Challenge.ChallengeService.Challenge)(this)).setActionsAsync(inValue);
+ }
+ }
+}
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.svcmap b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.svcmap
new file mode 100644
index 0000000..8cd1468
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/Reference.svcmap
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ReferenceGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="e8b44f4a-1597-4192-bc17-217131128c7c" xmlns="urn:schemas-microsoft-com:xml-wcfservicemap">
+ <ClientOptions>
+ <GenerateAsynchronousMethods>false</GenerateAsynchronousMethods>
+ <GenerateTaskBasedAsynchronousMethod>true</GenerateTaskBasedAsynchronousMethod>
+ <EnableDataBinding>true</EnableDataBinding>
+ <ExcludedTypes />
+ <ImportXmlTypes>false</ImportXmlTypes>
+ <GenerateInternalTypes>false</GenerateInternalTypes>
+ <GenerateMessageContracts>false</GenerateMessageContracts>
+ <NamespaceMappings />
+ <CollectionMappings />
+ <GenerateSerializableTypes>true</GenerateSerializableTypes>
+ <Serializer>Auto</Serializer>
+ <UseSerializerForFaults>true</UseSerializerForFaults>
+ <ReferenceAllAssemblies>true</ReferenceAllAssemblies>
+ <ReferencedAssemblies />
+ <ReferencedDataContractTypes />
+ <ServiceContractMappings />
+ </ClientOptions>
+ <MetadataSources>
+ <MetadataSource Address="C:\Workspace\Development\Entelect\C#.bot - starter pack\Entelect.BattleCity.Challenge\ChallengeService.wsdl" Protocol="file" SourceId="1" />
+ </MetadataSources>
+ <Metadata>
+ <MetadataFile FileName="ChallengeService.wsdl" MetadataType="Wsdl" ID="8f3e4ac3-8695-416b-ac20-ca2048dd4ef5" SourceId="1" SourceUrl="file:///C:/Workspace/Development/Entelect/C%23.bot - starter pack/Entelect.BattleCity.Challenge/ChallengeService.wsdl" />
+ </Metadata>
+ <Extensions>
+ <ExtensionFile FileName="configuration91.svcinfo" Name="configuration91.svcinfo" />
+ <ExtensionFile FileName="configuration.svcinfo" Name="configuration.svcinfo" />
+ </Extensions>
+</ReferenceGroup> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration.svcinfo b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration.svcinfo
new file mode 100644
index 0000000..6bbf9a9
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration.svcinfo
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configurationSnapshot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:schemas-microsoft-com:xml-wcfconfigurationsnapshot">
+ <behaviors />
+ <bindings>
+ <binding digest="System.ServiceModel.Configuration.BasicHttpBindingElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089:&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;Data name=&quot;ChallengeServiceSoapBinding&quot; /&gt;" bindingType="basicHttpBinding" name="ChallengeServiceSoapBinding" />
+ </bindings>
+ <endpoints>
+ <endpoint normalizedDigest="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;Data address=&quot;http://localhost:9090/ChallengePort&quot; binding=&quot;basicHttpBinding&quot; bindingConfiguration=&quot;ChallengeServiceSoapBinding&quot; contract=&quot;ChallengeService.Challenge&quot; name=&quot;ChallengePort&quot; /&gt;" digest="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;Data address=&quot;http://localhost:9090/ChallengePort&quot; binding=&quot;basicHttpBinding&quot; bindingConfiguration=&quot;ChallengeServiceSoapBinding&quot; contract=&quot;ChallengeService.Challenge&quot; name=&quot;ChallengePort&quot; /&gt;" contractName="ChallengeService.Challenge" name="ChallengePort" />
+ </endpoints>
+</configurationSnapshot> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration91.svcinfo b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration91.svcinfo
new file mode 100644
index 0000000..fea37b0
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/Service References/ChallengeService/configuration91.svcinfo
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="utf-8"?>
+<SavedWcfConfigurationInformation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="9.1" CheckSum="ClmKNZHJo7S7Ca55qfTBnf2SHrs=">
+ <bindingConfigurations>
+ <bindingConfiguration bindingType="basicHttpBinding" name="ChallengeServiceSoapBinding">
+ <properties>
+ <property path="/name" isComplexType="false" isExplicitlyDefined="true" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>ChallengeServiceSoapBinding</serializedValue>
+ </property>
+ <property path="/closeTimeout" isComplexType="false" isExplicitlyDefined="true" clrType="System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/openTimeout" isComplexType="false" isExplicitlyDefined="true" clrType="System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/receiveTimeout" isComplexType="false" isExplicitlyDefined="true" clrType="System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/sendTimeout" isComplexType="false" isExplicitlyDefined="true" clrType="System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/allowCookies" isComplexType="false" isExplicitlyDefined="true" clrType="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/bypassProxyOnLocal" isComplexType="false" isExplicitlyDefined="true" clrType="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/hostNameComparisonMode" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.HostNameComparisonMode, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>StrongWildcard</serializedValue>
+ </property>
+ <property path="/maxBufferPoolSize" isComplexType="false" isExplicitlyDefined="true" clrType="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/maxBufferSize" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>65536</serializedValue>
+ </property>
+ <property path="/maxReceivedMessageSize" isComplexType="false" isExplicitlyDefined="true" clrType="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/proxyAddress" isComplexType="false" isExplicitlyDefined="false" clrType="System.Uri, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/readerQuotas" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.XmlDictionaryReaderQuotasElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.XmlDictionaryReaderQuotasElement</serializedValue>
+ </property>
+ <property path="/readerQuotas/maxDepth" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>0</serializedValue>
+ </property>
+ <property path="/readerQuotas/maxStringContentLength" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>0</serializedValue>
+ </property>
+ <property path="/readerQuotas/maxArrayLength" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>0</serializedValue>
+ </property>
+ <property path="/readerQuotas/maxBytesPerRead" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>0</serializedValue>
+ </property>
+ <property path="/readerQuotas/maxNameTableCharCount" isComplexType="false" isExplicitlyDefined="false" clrType="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>0</serializedValue>
+ </property>
+ <property path="/textEncoding" isComplexType="false" isExplicitlyDefined="false" clrType="System.Text.Encoding, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.Text.UTF8Encoding</serializedValue>
+ </property>
+ <property path="/transferMode" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.TransferMode, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>Buffered</serializedValue>
+ </property>
+ <property path="/useDefaultWebProxy" isComplexType="false" isExplicitlyDefined="true" clrType="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/messageEncoding" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.WSMessageEncoding, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>Text</serializedValue>
+ </property>
+ <property path="/security" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.BasicHttpSecurityElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.BasicHttpSecurityElement</serializedValue>
+ </property>
+ <property path="/security/mode" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.BasicHttpSecurityMode, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>None</serializedValue>
+ </property>
+ <property path="/security/transport" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.HttpTransportSecurityElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.HttpTransportSecurityElement</serializedValue>
+ </property>
+ <property path="/security/transport/clientCredentialType" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.HttpClientCredentialType, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>None</serializedValue>
+ </property>
+ <property path="/security/transport/proxyCredentialType" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.HttpProxyCredentialType, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>None</serializedValue>
+ </property>
+ <property path="/security/transport/extendedProtectionPolicy" isComplexType="true" isExplicitlyDefined="false" clrType="System.Security.Authentication.ExtendedProtection.Configuration.ExtendedProtectionPolicyElement, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.Security.Authentication.ExtendedProtection.Configuration.ExtendedProtectionPolicyElement</serializedValue>
+ </property>
+ <property path="/security/transport/extendedProtectionPolicy/policyEnforcement" isComplexType="false" isExplicitlyDefined="false" clrType="System.Security.Authentication.ExtendedProtection.PolicyEnforcement, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>Never</serializedValue>
+ </property>
+ <property path="/security/transport/extendedProtectionPolicy/protectionScenario" isComplexType="false" isExplicitlyDefined="false" clrType="System.Security.Authentication.ExtendedProtection.ProtectionScenario, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>TransportSelected</serializedValue>
+ </property>
+ <property path="/security/transport/extendedProtectionPolicy/customServiceNames" isComplexType="true" isExplicitlyDefined="false" clrType="System.Security.Authentication.ExtendedProtection.Configuration.ServiceNameElementCollection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>(Collection)</serializedValue>
+ </property>
+ <property path="/security/transport/realm" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/security/message" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.BasicHttpMessageSecurityElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.BasicHttpMessageSecurityElement</serializedValue>
+ </property>
+ <property path="/security/message/clientCredentialType" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.BasicHttpMessageCredentialType, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>UserName</serializedValue>
+ </property>
+ <property path="/security/message/algorithmSuite" isComplexType="false" isExplicitlyDefined="false" clrType="System.ServiceModel.Security.SecurityAlgorithmSuite, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>Default</serializedValue>
+ </property>
+ </properties>
+ </bindingConfiguration>
+ </bindingConfigurations>
+ <endpoints>
+ <endpoint name="ChallengePort" contract="ChallengeService.Challenge" bindingType="basicHttpBinding" address="http://localhost:9090/ChallengePort" bindingConfiguration="ChallengeServiceSoapBinding">
+ <properties>
+ <property path="/address" isComplexType="false" isExplicitlyDefined="true" clrType="System.Uri, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>http://localhost:9090/ChallengePort</serializedValue>
+ </property>
+ <property path="/behaviorConfiguration" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/binding" isComplexType="false" isExplicitlyDefined="true" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>basicHttpBinding</serializedValue>
+ </property>
+ <property path="/bindingConfiguration" isComplexType="false" isExplicitlyDefined="true" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>ChallengeServiceSoapBinding</serializedValue>
+ </property>
+ <property path="/contract" isComplexType="false" isExplicitlyDefined="true" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>ChallengeService.Challenge</serializedValue>
+ </property>
+ <property path="/headers" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.AddressHeaderCollectionElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.AddressHeaderCollectionElement</serializedValue>
+ </property>
+ <property path="/headers/headers" isComplexType="false" isExplicitlyDefined="true" clrType="System.ServiceModel.Channels.AddressHeaderCollection, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>&lt;Header /&gt;</serializedValue>
+ </property>
+ <property path="/identity" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.IdentityElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.IdentityElement</serializedValue>
+ </property>
+ <property path="/identity/userPrincipalName" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.UserPrincipalNameElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.UserPrincipalNameElement</serializedValue>
+ </property>
+ <property path="/identity/userPrincipalName/value" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/servicePrincipalName" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.ServicePrincipalNameElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.ServicePrincipalNameElement</serializedValue>
+ </property>
+ <property path="/identity/servicePrincipalName/value" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/dns" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.DnsElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.DnsElement</serializedValue>
+ </property>
+ <property path="/identity/dns/value" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/rsa" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.RsaElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.RsaElement</serializedValue>
+ </property>
+ <property path="/identity/rsa/value" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/certificate" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.CertificateElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.CertificateElement</serializedValue>
+ </property>
+ <property path="/identity/certificate/encodedValue" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/certificateReference" isComplexType="true" isExplicitlyDefined="false" clrType="System.ServiceModel.Configuration.CertificateReferenceElement, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>System.ServiceModel.Configuration.CertificateReferenceElement</serializedValue>
+ </property>
+ <property path="/identity/certificateReference/storeName" isComplexType="false" isExplicitlyDefined="false" clrType="System.Security.Cryptography.X509Certificates.StoreName, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>My</serializedValue>
+ </property>
+ <property path="/identity/certificateReference/storeLocation" isComplexType="false" isExplicitlyDefined="false" clrType="System.Security.Cryptography.X509Certificates.StoreLocation, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>LocalMachine</serializedValue>
+ </property>
+ <property path="/identity/certificateReference/x509FindType" isComplexType="false" isExplicitlyDefined="false" clrType="System.Security.Cryptography.X509Certificates.X509FindType, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>FindBySubjectDistinguishedName</serializedValue>
+ </property>
+ <property path="/identity/certificateReference/findValue" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/identity/certificateReference/isChainIncluded" isComplexType="false" isExplicitlyDefined="false" clrType="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>False</serializedValue>
+ </property>
+ <property path="/name" isComplexType="false" isExplicitlyDefined="true" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue>ChallengePort</serializedValue>
+ </property>
+ <property path="/kind" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ <property path="/endpointConfiguration" isComplexType="false" isExplicitlyDefined="false" clrType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <serializedValue />
+ </property>
+ </properties>
+ </endpoint>
+ </endpoints>
+</SavedWcfConfigurationInformation> \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/compile.bat b/2013-battlecity/Entelect.BattleCity.Challenge/compile.bat
new file mode 100644
index 0000000..4f3d152
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/compile.bat
@@ -0,0 +1 @@
+MSBuild.exe /property:Configuration=Release Entelect.BattleCity.Challenge.csproj \ No newline at end of file
diff --git a/2013-battlecity/Entelect.BattleCity.Challenge/start.bat b/2013-battlecity/Entelect.BattleCity.Challenge/start.bat
new file mode 100644
index 0000000..521c376
--- /dev/null
+++ b/2013-battlecity/Entelect.BattleCity.Challenge/start.bat
@@ -0,0 +1 @@
+"bin/Release/Entelect.BattleCity.Challenge.exe" %1 \ No newline at end of file
diff --git a/2013-battlecity/README.md b/2013-battlecity/README.md
new file mode 100644
index 0000000..e868ec0
--- /dev/null
+++ b/2013-battlecity/README.md
@@ -0,0 +1,12 @@
+Botinator
+=========
+
+My entry for the Entelect 100k Challenge 2013.
+
+The task for the 2013 100k challenge was to write a program which connects to a SOAP web service and controls the tanks in a game of Battle City. Each player has two tanks and a base on a two dimensional board. The player who manages to destroy their opponent's base first wins.
+
+I'm currently employed by Entelect, so I could not enter the 100k challenge, but we did have an internal version of the challenge. This bot came second in the internal challenge.
+
+This is an intentionally 'stupid' bot. It doesn't use any fancy AI algorithms, and doesn't really even look at what is happening. Despite being pretty stupid, it actually works fairly well.
+
+The core of the algorithm is that each tank compares its position to that of the enemy base. If it is in line with the base horizontally, it faces the base. If it is not in line with the base, it faces in the direction it would need to head to be in line with the base. If the tank can shoot, it shoots. If it can't shoot, it moves forward. I find it easiest to visualise this as an 'L' pattern.
diff --git a/2015-spacebot/.gitignore b/2015-spacebot/.gitignore
new file mode 100644
index 0000000..3454691
--- /dev/null
+++ b/2015-spacebot/.gitignore
@@ -0,0 +1,12 @@
+CMakeCache.txt
+CMakeFiles
+*.cmake
+install_manifest.txt
+output/
+bin/
+*.sdf
+*.suo
+Debug/
+Testing/
+Makefile
+*~
diff --git a/2015-spacebot/CMakeLists.txt b/2015-spacebot/CMakeLists.txt
new file mode 100644
index 0000000..fd6a32d
--- /dev/null
+++ b/2015-spacebot/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.2.2)
+project(Spacebot)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic -Wextra")
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+include_directories(${PROJECT_SOURCE_DIR}/include)
+
+file(GLOB_RECURSE SRC_FILES RELATIVE ${PROJECT_SOURCE_DIR} src/*.cpp)
+file(GLOB_RECURSE MAIN_FILES RELATIVE ${PROJECT_SOURCE_DIR} main/*.cpp)
+add_executable(spacebot ${SRC_FILES} ${MAIN_FILES})
+
+enable_testing()
+file(GLOB_RECURSE TEST_FILES RELATIVE ${PROJECT_SOURCE_DIR} test/*.cpp)
+add_executable(spacebot_test ${SRC_FILES} ${TEST_FILES})
+add_test(Tests bin/spacebot_test)
diff --git a/2015-spacebot/Spacebot.sln b/2015-spacebot/Spacebot.sln
new file mode 100644
index 0000000..3b73b24
--- /dev/null
+++ b/2015-spacebot/Spacebot.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Spacebot", "Spacebot.vcxproj", "{1EFC7222-2DD3-442D-B3B9-1E4017444DE8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1EFC7222-2DD3-442D-B3B9-1E4017444DE8}.Debug|Win32.ActiveCfg = Debug|Win32
+ {1EFC7222-2DD3-442D-B3B9-1E4017444DE8}.Debug|Win32.Build.0 = Debug|Win32
+ {1EFC7222-2DD3-442D-B3B9-1E4017444DE8}.Release|Win32.ActiveCfg = Release|Win32
+ {1EFC7222-2DD3-442D-B3B9-1E4017444DE8}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/2015-spacebot/Spacebot.vcxproj b/2015-spacebot/Spacebot.vcxproj
new file mode 100644
index 0000000..a0f98e3
--- /dev/null
+++ b/2015-spacebot/Spacebot.vcxproj
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{1EFC7222-2DD3-442D-B3B9-1E4017444DE8}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <TargetMachine>MachineX86</TargetMachine>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="main\main.cpp" />
+ <ClCompile Include="src\alien.cpp" />
+ <ClCompile Include="src\brain\neural_link.cpp" />
+ <ClCompile Include="src\brain\neural_network.cpp" />
+ <ClCompile Include="src\brain\neuron.cpp" />
+ <ClCompile Include="src\enemy_bullet.cpp" />
+ <ClCompile Include="src\game_entity.cpp" />
+ <ClCompile Include="src\game_state.cpp" />
+ <ClCompile Include="src\move_string_mapper.cpp" />
+ <ClCompile Include="src\player_missile.cpp" />
+ <ClCompile Include="src\shield.cpp" />
+ <ClCompile Include="src\spacebot.cpp" />
+ <ClCompile Include="src\spaceship.cpp" />
+ <ClCompile Include="src\building.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\alien.h" />
+ <ClInclude Include="include\brain\bias_node.h" />
+ <ClInclude Include="include\brain\neural_link.h" />
+ <ClInclude Include="include\brain\neural_network.h" />
+ <ClInclude Include="include\brain\neural_node.h" />
+ <ClInclude Include="include\brain\neuron.h" />
+ <ClInclude Include="include\brain\sensor.h" />
+ <ClInclude Include="include\enemy_bullet.h" />
+ <ClInclude Include="include\game_entity.h" />
+ <ClInclude Include="include\game_state.h" />
+ <ClInclude Include="include\move.h" />
+ <ClInclude Include="include\move_string_mapper.h" />
+ <ClInclude Include="include\player_missile.h" />
+ <ClInclude Include="include\shield.h" />
+ <ClInclude Include="include\spacebot.h" />
+ <ClInclude Include="include\spaceship.h" />
+ <ClInclude Include="include\building.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/2015-spacebot/Spacebot.vcxproj.filters b/2015-spacebot/Spacebot.vcxproj.filters
new file mode 100644
index 0000000..c03ec82
--- /dev/null
+++ b/2015-spacebot/Spacebot.vcxproj.filters
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
+ </Filter>
+ <Filter Include="Header Files\brain">
+ <UniqueIdentifier>{b20525b7-b3c2-4e24-847b-d9c6f5630885}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\brain">
+ <UniqueIdentifier>{f31b18e6-4609-4702-a6ed-9bf41faa1c84}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\alien.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\enemy_bullet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\player_missile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\shield.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\spacebot.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\spaceship.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\game_entity.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\game_state.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\move_string_mapper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main\main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\bias_node.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\neural_link.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\neural_network.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\neural_node.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\neuron.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ <ClCompile Include="src\brain\sensor.cpp">
+ <Filter>Source Files\brain</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\alien.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\enemy_bullet.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\move.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\player_missile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\shield.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spacebot.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\spaceship.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\game_entity.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\game_state.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\move_string_mapper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\bias_node.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\neural_link.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\neural_network.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\neural_node.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\neuron.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ <ClInclude Include="include\brain\sensor.h">
+ <Filter>Header Files\brain</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/2015-spacebot/bot.json b/2015-spacebot/bot.json
new file mode 100644
index 0000000..542bff2
--- /dev/null
+++ b/2015-spacebot/bot.json
@@ -0,0 +1,5 @@
+{
+ "nickName": "Brick",
+ "author": "Justin Worthe",
+ "email", "justin.worthe@gmail.com"
+}
diff --git a/2015-spacebot/brain.nn b/2015-spacebot/brain.nn
new file mode 100644
index 0000000..e45ef1b
--- /dev/null
+++ b/2015-spacebot/brain.nn
@@ -0,0 +1,14 @@
+b0 n0 20
+s55 n3 10
+b0 n5 -10
+s59 n5 -50
+s60 n5 20
+b0 n6 10
+s51 n6 -10
+s53 n6 -10
+n3 n0 -20
+n5 n0 -20
+n6 n0 -20
+n3 n5 -20
+n6 n3 -20
+n6 n5 -20
diff --git a/2015-spacebot/compile.bat b/2015-spacebot/compile.bat
new file mode 100644
index 0000000..233ca35
--- /dev/null
+++ b/2015-spacebot/compile.bat
@@ -0,0 +1 @@
+msbuild.exe
diff --git a/2015-spacebot/compile.sh b/2015-spacebot/compile.sh
new file mode 100755
index 0000000..a073c19
--- /dev/null
+++ b/2015-spacebot/compile.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+cmake . && make
diff --git a/2015-spacebot/include/alien.h b/2015-spacebot/include/alien.h
new file mode 100644
index 0000000..5dda726
--- /dev/null
+++ b/2015-spacebot/include/alien.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "game_entity.h"
+
+class Alien : public GameEntity
+{
+public:
+ Alien(int x, int y);
+ const static char MAP_CHAR = 'x';
+private:
+};
diff --git a/2015-spacebot/include/brain/bias_node.h b/2015-spacebot/include/brain/bias_node.h
new file mode 100644
index 0000000..77c5884
--- /dev/null
+++ b/2015-spacebot/include/brain/bias_node.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "brain/neural_node.h"
+
+class BiasNode: public NeuralNode
+{
+public:
+BiasNode() : NeuralNode("b0")
+ {
+ _activation = 1;
+ }
+};
diff --git a/2015-spacebot/include/brain/neural_link.h b/2015-spacebot/include/brain/neural_link.h
new file mode 100644
index 0000000..14f58b9
--- /dev/null
+++ b/2015-spacebot/include/brain/neural_link.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "neural_node.h"
+
+class NeuralLink
+{
+public:
+ NeuralLink(NeuralNode* input, double weight);
+ double weightedActivation() const;
+
+ double weight() const { return _weight; }
+ std::string inputIdentifier() const { return _input->identifier(); }
+
+private:
+ NeuralNode* _input;
+ double _weight;
+};
diff --git a/2015-spacebot/include/brain/neural_network.h b/2015-spacebot/include/brain/neural_network.h
new file mode 100644
index 0000000..7fcf5f4
--- /dev/null
+++ b/2015-spacebot/include/brain/neural_network.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <memory>
+#include <istream>
+#include <vector>
+#include <map>
+#include <string>
+
+#include "brain/neural_node.h"
+#include "brain/sensor.h"
+#include "brain/bias_node.h"
+#include "brain/neuron.h"
+
+class NeuralNetwork
+{
+public:
+ NeuralNetwork(std::istream &&networkConfigFile, unsigned int numberOfSensors, unsigned int numberOfOutputs);
+ NeuralNetwork(std::istream &&networkConfigFile, std::vector<bool> sensorInitialValues, unsigned int numberOfOutputs);
+
+ void setInput(unsigned int inputIndex, double activation);
+ unsigned int findMaxOutputIndex() const;
+
+ unsigned int numberOfSensors() const { return _sensors.size(); }
+ unsigned int numberOfOutputs() const { return _outputs.size(); }
+ unsigned int numberOfNeurons() const { return _neurons.size(); }
+
+ bool linkExists(std::string srcIdentifier, std::string destIdentifier, double weight) const;
+
+private:
+ std::vector<std::shared_ptr<Sensor>> _sensors;
+ std::shared_ptr<BiasNode> _biasNode;
+ std::vector<std::shared_ptr<Neuron>> _neurons;
+ std::vector<std::shared_ptr<Neuron>> _outputs;
+
+ void parseFile(std::istream &&file);
+
+ void addLink(std::shared_ptr<NeuralNode> source, std::shared_ptr<Neuron> destination, double weight);
+ std::shared_ptr<Sensor> findOrAddSensor(unsigned int id);
+ std::shared_ptr<Neuron> findOrAddNeuron(unsigned int id);
+};
+
diff --git a/2015-spacebot/include/brain/neural_node.h b/2015-spacebot/include/brain/neural_node.h
new file mode 100644
index 0000000..2684146
--- /dev/null
+++ b/2015-spacebot/include/brain/neural_node.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <string>
+
+class NeuralNode
+{
+public:
+ double activation() const { return _activation; }
+ std::string identifier() const { return _identifier; }
+
+NeuralNode(std::string identifier): _identifier(identifier) {}
+
+protected:
+ double _activation;
+
+private:
+ std::string _identifier;
+};
diff --git a/2015-spacebot/include/brain/neuron.h b/2015-spacebot/include/brain/neuron.h
new file mode 100644
index 0000000..ca26c73
--- /dev/null
+++ b/2015-spacebot/include/brain/neuron.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "neural_node.h"
+#include "neural_link.h"
+
+class Neuron : public NeuralNode
+{
+public:
+ void addInput(NeuralLink&& link);
+ bool calculateActivation();
+
+Neuron(int index) : NeuralNode('n'+std::to_string(index)){};
+
+ bool hasInputWithWeight(std::string srcIdentifier, double weight) const;
+
+private:
+ std::vector<NeuralLink> _inputLinks;
+ double sigmoid(double input) const;
+};
diff --git a/2015-spacebot/include/brain/sensor.h b/2015-spacebot/include/brain/sensor.h
new file mode 100644
index 0000000..10f62a7
--- /dev/null
+++ b/2015-spacebot/include/brain/sensor.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "brain/neural_node.h"
+
+class Sensor: public NeuralNode
+{
+public:
+Sensor(int index) :NeuralNode('s'+std::to_string(index)){}
+ void setActivation(double activation) { _activation = activation; }
+};
diff --git a/2015-spacebot/include/building.h b/2015-spacebot/include/building.h
new file mode 100644
index 0000000..76d78fd
--- /dev/null
+++ b/2015-spacebot/include/building.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "game_entity.h"
+
+class Building : public GameEntity
+{
+public:
+ Building(int x, int y);
+ const static char MISSILE_CONTROLLER_CHAR = 'M';
+ const static char ALIEN_FACTORY_CHAR = 'X';
+};
+
diff --git a/2015-spacebot/include/enemy_bullet.h b/2015-spacebot/include/enemy_bullet.h
new file mode 100644
index 0000000..ff3b001
--- /dev/null
+++ b/2015-spacebot/include/enemy_bullet.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "game_entity.h"
+
+class EnemyBullet: public GameEntity
+{
+public:
+ EnemyBullet(int x, int y);
+ const static char ALIEN_MAP_CHAR = '|';
+ const static char ENEMY_MISSILE_MAP_CHAR = 'i';
+private:
+};
diff --git a/2015-spacebot/include/game_entity.h b/2015-spacebot/include/game_entity.h
new file mode 100644
index 0000000..09e0bcc
--- /dev/null
+++ b/2015-spacebot/include/game_entity.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <string>
+
+class GameEntity
+{
+public:
+ GameEntity(int x, int y);
+ int x() const {return _x;}
+ int y() const {return _y;}
+
+ std::string coords() const;
+private:
+ int _x;
+ int _y;
+};
+
diff --git a/2015-spacebot/include/game_state.h b/2015-spacebot/include/game_state.h
new file mode 100644
index 0000000..c129d28
--- /dev/null
+++ b/2015-spacebot/include/game_state.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "alien.h"
+#include "enemy_bullet.h"
+#include "player_missile.h"
+#include "shield.h"
+#include "spaceship.h"
+#include "building.h"
+#include <vector>
+#include <string>
+#include <istream>
+#include <memory>
+
+class GameState
+{
+public:
+ GameState(std::istream &&mapFile);
+ void logState() const;
+
+ const std::vector<Alien>& aliens() const { return _aliens; }
+ const std::vector<EnemyBullet>& bullets() const { return _bullets; }
+ const std::vector<PlayerMissile>& missiles() const { return _missiles; }
+ const std::vector<Shield>& shields() const { return _shields; }
+ const std::vector<Building>& missileControllers() const { return _missileControllers; }
+ const std::vector<Building>& alienFactories() const { return _alienFactories; }
+
+ const std::unique_ptr<Spaceship>& playerSpaceship() const { return _playerSpaceship; }
+ const std::unique_ptr<Spaceship>& enemySpaceship() const { return _enemySpaceship; }
+
+ std::vector<bool> toBitArray() const;
+
+private:
+ std::vector<Alien> _aliens;
+ std::vector<EnemyBullet> _bullets;
+ std::vector<PlayerMissile> _missiles;
+ std::vector<Shield> _shields;
+ std::vector<Building> _missileControllers;
+ std::vector<Building> _alienFactories;
+
+ std::unique_ptr<Spaceship> _playerSpaceship;
+ std::unique_ptr<Spaceship> _enemySpaceship;
+
+ int addEntity(int x, int y, char type);
+};
diff --git a/2015-spacebot/include/move.h b/2015-spacebot/include/move.h
new file mode 100644
index 0000000..489aeb9
--- /dev/null
+++ b/2015-spacebot/include/move.h
@@ -0,0 +1,12 @@
+#pragma once
+
+enum class Move: int
+{
+ NOTHING = 0,
+ MOVE_LEFT = 1,
+ MOVE_RIGHT = 2,
+ SHOOT = 3,
+ BUILD_MISSILE_CONTROLLER = 4,
+ BUILD_ALIEN_FACTORY = 5,
+ BUILD_SHIELD = 6
+};
diff --git a/2015-spacebot/include/move_string_mapper.h b/2015-spacebot/include/move_string_mapper.h
new file mode 100644
index 0000000..46144c7
--- /dev/null
+++ b/2015-spacebot/include/move_string_mapper.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <string>
+#include <map>
+#include "move.h"
+
+class MoveStringMapper
+{
+public:
+ MoveStringMapper();
+ std::string toString(const Move &move);
+private:
+ std::map<Move, const char*> moveMap;
+};
diff --git a/2015-spacebot/include/player_missile.h b/2015-spacebot/include/player_missile.h
new file mode 100644
index 0000000..c504a79
--- /dev/null
+++ b/2015-spacebot/include/player_missile.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "game_entity.h"
+
+class PlayerMissile: public GameEntity
+{
+public:
+ PlayerMissile(int x, int y);
+ const static char MAP_CHAR = '!';
+private:
+};
diff --git a/2015-spacebot/include/shield.h b/2015-spacebot/include/shield.h
new file mode 100644
index 0000000..be65be3
--- /dev/null
+++ b/2015-spacebot/include/shield.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "game_entity.h"
+
+class Shield: public GameEntity
+{
+public:
+ Shield(int x, int y);
+ const static char MAP_CHAR = '-';
+private:
+};
+
diff --git a/2015-spacebot/include/spacebot.h b/2015-spacebot/include/spacebot.h
new file mode 100644
index 0000000..9f7f83e
--- /dev/null
+++ b/2015-spacebot/include/spacebot.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <string>
+#include "move.h"
+#include "game_state.h"
+
+class Spacebot {
+public:
+ Spacebot(std::string outputPath, std::string brainFilename);
+ void writeNextMove();
+private:
+ std::string _outputFilename;
+ std::string _brainFilename;
+ GameState _gameState;
+
+ void writeMove(const Move& move);
+ Move chooseMove();
+};
diff --git a/2015-spacebot/include/spaceship.h b/2015-spacebot/include/spaceship.h
new file mode 100644
index 0000000..bd26b64
--- /dev/null
+++ b/2015-spacebot/include/spaceship.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "game_entity.h"
+
+class Spaceship: public GameEntity
+{
+public:
+ Spaceship(int x, int y);
+ const static char ENEMY_MAP_CHAR = 'V';
+ const static char PLAYER_MAP_CHAR = 'A';
+private:
+};
diff --git a/2015-spacebot/main/main.cpp b/2015-spacebot/main/main.cpp
new file mode 100644
index 0000000..5c7f183
--- /dev/null
+++ b/2015-spacebot/main/main.cpp
@@ -0,0 +1,25 @@
+#include <iostream>
+#include <string>
+
+#include "spacebot.h"
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2)
+ {
+ std::cout << "usage: " << argv[0] << " <output folder>" << std::endl;
+ return 1;
+ }
+
+ std::string brainFilename = "brain.nn";
+
+ if (argc >= 3)
+ {
+ brainFilename = argv[2];
+ }
+
+ Spacebot bot(argv[1], std::move(brainFilename));
+ bot.writeNextMove();
+
+ return 0;
+}
diff --git a/2015-spacebot/run.bat b/2015-spacebot/run.bat
new file mode 100644
index 0000000..10baeff
--- /dev/null
+++ b/2015-spacebot/run.bat
@@ -0,0 +1 @@
+.\Debug\Spacebot.exe %1
diff --git a/2015-spacebot/run.sh b/2015-spacebot/run.sh
new file mode 100755
index 0000000..1d4a835
--- /dev/null
+++ b/2015-spacebot/run.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./bin/spacebot $1
diff --git a/2015-spacebot/src/alien.cpp b/2015-spacebot/src/alien.cpp
new file mode 100644
index 0000000..3fa9b28
--- /dev/null
+++ b/2015-spacebot/src/alien.cpp
@@ -0,0 +1,7 @@
+#include "alien.h"
+
+Alien::Alien(int x, int y)
+ :GameEntity(x, y)
+{
+}
+
diff --git a/2015-spacebot/src/brain/neural_link.cpp b/2015-spacebot/src/brain/neural_link.cpp
new file mode 100644
index 0000000..6997bd6
--- /dev/null
+++ b/2015-spacebot/src/brain/neural_link.cpp
@@ -0,0 +1,11 @@
+#include "brain/neural_link.h"
+
+NeuralLink::NeuralLink(NeuralNode* input, double weight)
+ :_input(input), _weight(weight)
+{
+}
+
+double NeuralLink::weightedActivation() const
+{
+ return _input->activation()*_weight;
+}
diff --git a/2015-spacebot/src/brain/neural_network.cpp b/2015-spacebot/src/brain/neural_network.cpp
new file mode 100644
index 0000000..c8177e8
--- /dev/null
+++ b/2015-spacebot/src/brain/neural_network.cpp
@@ -0,0 +1,163 @@
+#include "brain/neural_network.h"
+#include "brain/neuron.h"
+#include <limits>
+
+NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, unsigned int numberOfSensors, unsigned int numberOfOutputs)
+{
+ _neurons.reserve(400);
+ _sensors.reserve(numberOfSensors);
+ _outputs.reserve(numberOfOutputs);
+
+ _biasNode = std::make_shared<BiasNode>();
+
+ for (unsigned int i=0; i<numberOfSensors; ++i)
+ {
+ auto sensor = std::make_shared<Sensor>(i);
+ _sensors.push_back(sensor);
+ }
+ for (unsigned int i=0; i<numberOfOutputs; ++i)
+ {
+ auto output = findOrAddNeuron(i);
+ _outputs.push_back(output);
+ }
+
+ parseFile(std::move(networkConfigFile));
+}
+
+NeuralNetwork::NeuralNetwork(std::istream &&networkConfigFile, std::vector<bool> sensorInitialValues, unsigned int numberOfOutputs)
+{
+ _neurons.reserve(400);
+ _sensors.reserve(sensorInitialValues.size());
+ _outputs.reserve(numberOfOutputs);
+
+ _biasNode = std::make_shared<BiasNode>();
+
+ for (unsigned int i=0; i<sensorInitialValues.size(); ++i)
+ {
+ auto sensor = std::make_shared<Sensor>(i);
+ sensor->setActivation(sensorInitialValues[i] ? 1 : 0);
+ _sensors.push_back(sensor);
+ }
+ for (unsigned int i=0; i<numberOfOutputs; ++i)
+ {
+ auto output = findOrAddNeuron(i);
+ _outputs.push_back(output);
+ }
+
+ parseFile(std::move(networkConfigFile));
+}
+
+void NeuralNetwork::parseFile(std::istream &&file)
+{
+ double weight;
+ char srcType;
+ unsigned int srcId;
+ unsigned int destId;
+
+ while (file.get(srcType) &&
+ file >> srcId &&
+ file.ignore(std::numeric_limits<std::streamsize>::max(), 'n') &&
+ file >> destId &&
+ file >> weight &&
+ file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'))
+ {
+ std::shared_ptr<NeuralNode> source;
+ std::shared_ptr<Neuron> destination;
+ switch (srcType)
+ {
+ case 's':
+ source = findOrAddSensor(srcId);
+ break;
+ case 'b':
+ source = _biasNode;
+ break;
+ default:
+ source = findOrAddNeuron(srcId);
+ }
+ destination = findOrAddNeuron(destId);
+
+ addLink(source, destination, weight);
+ }
+
+}
+
+void NeuralNetwork::addLink(std::shared_ptr<NeuralNode> source, std::shared_ptr<Neuron> destination, double weight)
+{
+ NeuralLink link(source.get(), weight);
+ destination->addInput(std::move(link));
+}
+
+std::shared_ptr<Sensor> NeuralNetwork::findOrAddSensor(unsigned int id)
+{
+ while (_sensors.size() <= id)
+ {
+ auto sensor = std::make_shared<Sensor>(_sensors.size());
+ _sensors.push_back(sensor);
+ }
+
+ return _sensors.at(id);
+}
+
+std::shared_ptr<Neuron> NeuralNetwork::findOrAddNeuron(unsigned int id)
+{
+ while (_neurons.size() <= id)
+ {
+ auto neuron = std::make_shared<Neuron>(_neurons.size());
+ _neurons.push_back(neuron);
+ }
+
+ return _neurons.at(id);
+}
+
+void NeuralNetwork::setInput(unsigned int inputIndex, double activation)
+{
+ _sensors.at(inputIndex)->setActivation(activation);
+}
+
+unsigned int NeuralNetwork::findMaxOutputIndex() const
+{
+ bool anyNodeChanged = true;
+ auto maxIterations = _neurons.size()*10;
+ for (unsigned int iteration=0; anyNodeChanged && iteration<maxIterations; ++iteration)
+ {
+ anyNodeChanged = false;
+ for (auto const& neuron : _neurons)
+ {
+ bool activationChanged = neuron->calculateActivation();
+ anyNodeChanged = anyNodeChanged || activationChanged;
+ }
+ }
+
+ int currentMaxIndex = 0;
+ double currentMaxActivation = _outputs.at(0)->activation();
+
+ for (unsigned int i=1; i<_outputs.size(); ++i)
+ {
+ double activation = _outputs.at(i)->activation();
+ if (activation >= currentMaxActivation)
+ {
+ currentMaxActivation = activation;
+ currentMaxIndex = i;
+ }
+ }
+ return currentMaxIndex;
+}
+
+bool NeuralNetwork::linkExists(std::string srcIdentifier, std::string destIdentifier, double weight) const
+{
+ std::shared_ptr<Neuron> dest;
+
+ for (auto const& node : _neurons)
+ {
+ if (node->identifier() == destIdentifier)
+ {
+ dest = node;
+ }
+ }
+
+ if (!dest)
+ {
+ return false;
+ }
+ return dest->hasInputWithWeight(srcIdentifier, weight);
+}
diff --git a/2015-spacebot/src/brain/neuron.cpp b/2015-spacebot/src/brain/neuron.cpp
new file mode 100644
index 0000000..c7dba2c
--- /dev/null
+++ b/2015-spacebot/src/brain/neuron.cpp
@@ -0,0 +1,40 @@
+#include "brain/neuron.h"
+#include <cmath>
+
+double Neuron::sigmoid(double input) const
+{
+ const double slope = 4.924273;
+ return (1/(1+(std::exp(-(slope*input)))));
+}
+
+bool Neuron::calculateActivation()
+{
+ double newActivation = 0;
+ for (auto const& link : _inputLinks)
+ {
+ newActivation += link.weightedActivation();
+ }
+ newActivation = sigmoid(newActivation);
+
+ const double errorMargin = 0.000001;
+ bool activationChanged = std::abs(newActivation - _activation) > errorMargin;
+ _activation = newActivation;
+ return activationChanged;
+}
+
+void Neuron::addInput(NeuralLink&& link)
+{
+ _inputLinks.push_back(std::move(link));
+}
+
+bool Neuron::hasInputWithWeight(std::string srcIdentifier, double weight) const
+{
+ for (auto const& link : _inputLinks)
+ {
+ if (link.inputIdentifier() == srcIdentifier && link.weight() == weight)
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/2015-spacebot/src/building.cpp b/2015-spacebot/src/building.cpp
new file mode 100644
index 0000000..8da322d
--- /dev/null
+++ b/2015-spacebot/src/building.cpp
@@ -0,0 +1,6 @@
+#include "building.h"
+
+Building::Building(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/enemy_bullet.cpp b/2015-spacebot/src/enemy_bullet.cpp
new file mode 100644
index 0000000..6ae5066
--- /dev/null
+++ b/2015-spacebot/src/enemy_bullet.cpp
@@ -0,0 +1,6 @@
+#include "enemy_bullet.h"
+
+EnemyBullet::EnemyBullet(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/game_entity.cpp b/2015-spacebot/src/game_entity.cpp
new file mode 100644
index 0000000..8acf8cd
--- /dev/null
+++ b/2015-spacebot/src/game_entity.cpp
@@ -0,0 +1,13 @@
+#include "game_entity.h"
+#include <sstream>
+
+GameEntity::GameEntity(int x, int y)
+ :_x(x), _y(y)
+{}
+
+std::string GameEntity::coords() const
+{
+ std::stringstream ss;
+ ss << "(" << _x << ", " << _y << ")";
+ return ss.str();
+}
diff --git a/2015-spacebot/src/game_state.cpp b/2015-spacebot/src/game_state.cpp
new file mode 100644
index 0000000..952c2f3
--- /dev/null
+++ b/2015-spacebot/src/game_state.cpp
@@ -0,0 +1,234 @@
+#include "game_state.h"
+#include <iostream>
+#include <fstream>
+#include <limits>
+#include <bitset>
+#include <cmath>
+
+const int OPENING_LINES = 6;
+const int GAME_AREA_LINES = 25;
+const int GAME_WIDTH = 19;
+
+void moveToNextChar(int &x, int &y, int &width, char &nextChar, std::istream &mapFile)
+{
+ if (nextChar == '\n')
+ {
+ x = 0;
+ ++y;
+ }
+ else
+ {
+ x += width;
+ }
+ if (width > 1)
+ {
+ mapFile.ignore(width-1);
+ }
+ nextChar = mapFile.get();
+}
+
+GameState::GameState(std::istream &&mapFile)
+{
+ for (int i=0; i<OPENING_LINES; ++i)
+ {
+ mapFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+ }
+
+ char nextChar = mapFile.get();
+ for (int x=0, y=0, width=1;
+ y < GAME_AREA_LINES && nextChar != EOF;
+ moveToNextChar(x, y, width, nextChar, mapFile))
+ {
+ width = addEntity(x, y, nextChar);
+ }
+}
+
+int GameState::addEntity(int x, int y, char type)
+{
+ switch (type)
+ {
+ case Alien::MAP_CHAR:
+ _aliens.push_back(Alien(x,y));
+ return 1;
+ case EnemyBullet::ALIEN_MAP_CHAR:
+ case EnemyBullet::ENEMY_MISSILE_MAP_CHAR:
+ _bullets.push_back(EnemyBullet(x,y));
+ return 1;
+ case PlayerMissile::MAP_CHAR:
+ _missiles.push_back(PlayerMissile(x,y));
+ return 1;
+ case Shield::MAP_CHAR:
+ _shields.push_back(Shield(x,y));
+ return 1;
+ case Spaceship::ENEMY_MAP_CHAR:
+ _enemySpaceship = std::unique_ptr<Spaceship>(new Spaceship(x+1, y));
+ return 3;
+ case Spaceship::PLAYER_MAP_CHAR:
+ _playerSpaceship = std::unique_ptr<Spaceship>(new Spaceship(x+1, y));
+ return 3;
+ case Building::MISSILE_CONTROLLER_CHAR:
+ _missileControllers.push_back(Building(x+1, y));
+ return 3;
+ case Building::ALIEN_FACTORY_CHAR:
+ _alienFactories.push_back(Building(x+1, y));
+ return 3;
+ }
+ return 1;
+}
+
+void GameState::logState() const
+{
+ for (auto const& alien : _aliens)
+ {
+ std::cout << "Alien " << alien.coords() << std::endl;
+ }
+ for (auto const& bullet : _bullets)
+ {
+ std::cout << "Enemy Bullet" << bullet.coords() << std::endl;
+ }
+ for (auto const& missile : _missiles)
+ {
+ std::cout << "Player Missile" << missile.coords() << std::endl;
+ }
+ for (auto const& shield : _shields)
+ {
+ std::cout << "Shield" << shield.coords() << std::endl;
+ }
+ if (_playerSpaceship)
+ {
+ std::cout << "Player Spaceship" << _playerSpaceship->coords() << std::endl;
+ }
+ if (_enemySpaceship)
+ {
+ std::cout << "Enemy Spaceship" << _enemySpaceship->coords() << std::endl;
+ }
+}
+
+int getPyramidOffset(int bottomWidth, int maxWidth, int x, int y, int resultIfLeft, int resultIfRight)
+{
+ int currentRowWidth = bottomWidth;
+ int currentRowOffset = 0;
+ for (int i=0; i<y; ++i)
+ {
+ currentRowOffset += currentRowWidth;
+ currentRowWidth += 2;
+ if (currentRowWidth > maxWidth)
+ {
+ currentRowWidth = maxWidth;
+ }
+ }
+
+ if (x > currentRowWidth/2)
+ {
+ return resultIfRight;
+ }
+ else if (x < -currentRowWidth/2)
+ {
+ return resultIfLeft;
+ }
+ else
+ {
+ return currentRowOffset + currentRowWidth/2 + x;
+ }
+}
+
+int getRectangularOffset(int width, int x, int y)
+{
+ int currentRowOffset = width*y;
+ return currentRowOffset + width/2 + x;
+}
+
+std::vector<bool> GameState::toBitArray() const
+{
+ std::vector<bool> result(61);
+
+ int alienOffset = 0; //Aliens are 0 to 35
+ int bulletOffset = 36; //Bullets are 36 to 50
+ int shieldOffset = 51; //Shields are 51 to 53
+ int missileOffset = 54; //Missile info is 54 to 55
+ int positionOffset = 56; //Position is 56 to 58
+ int existingBuildingsOffset = 59; //Existing buildings is 59
+ int canBuildOffset = 60; //Can build here is 60
+
+ int playerX = GAME_WIDTH/2;
+ int playerY = GAME_AREA_LINES-3;
+ if (_playerSpaceship)
+ {
+ playerX = _playerSpaceship->x();
+ }
+
+ for (auto const& alien : _aliens)
+ {
+ if (alien.y() > playerY-4 || alien.y() < playerY-7 || alien.x() > playerX+4 || alien.x() < playerX-4)
+ {
+ continue;
+ }
+
+ result.at(alienOffset + getRectangularOffset(9, alien.x()-playerX, playerY-4-alien.y())) = true;
+ }
+
+ for (auto const& bullet : _bullets)
+ {
+ if (bullet.y() >= playerY || bullet.y() < playerY-3 || bullet.x() > playerX+2 || bullet.x() < playerX-2)
+ {
+ continue;
+ }
+
+ result.at(bulletOffset + getRectangularOffset(5, bullet.x()-playerX, playerY-1-bullet.y())) = true;
+ }
+
+ for (auto const& shield : _shields)
+ {
+ if (shield.y() < playerY-3 || shield.x() < playerX-1 || shield.x() > playerX+1)
+ {
+ continue;
+ }
+
+ result.at(shieldOffset + shield.x()-playerX+1) = true;
+ }
+
+ if (_missiles.size() > 0)
+ {
+ result.at(missileOffset) = true;
+ }
+ if (_missiles.size() < 1)
+ {
+ result.at(missileOffset + 1) = true;
+ }
+
+ if (_playerSpaceship)
+ {
+ if (playerX <= GAME_WIDTH/3)
+ {
+ result.at(positionOffset) = true;
+ }
+ else if (playerX >= GAME_WIDTH*2/3)
+ {
+ result.at(positionOffset+2) = true;
+ }
+ else
+ {
+ result.at(positionOffset+1) = true;
+ }
+ }
+
+ result.at(canBuildOffset) = true;
+ for (auto const& missileController : _missileControllers)
+ {
+ if (missileController.y() < playerY)
+ {
+ continue;
+ }
+ result.at(existingBuildingsOffset + 0) = true;
+ if (_missiles.size() < 2)
+ {
+ result.at(missileOffset+1) = true;
+ }
+ if (abs(missileController.x() - playerX) < 3)
+ {
+ result.at(canBuildOffset) = false;
+ }
+ }
+
+ return result;
+}
diff --git a/2015-spacebot/src/move_string_mapper.cpp b/2015-spacebot/src/move_string_mapper.cpp
new file mode 100644
index 0000000..ed510c1
--- /dev/null
+++ b/2015-spacebot/src/move_string_mapper.cpp
@@ -0,0 +1,21 @@
+#include "move_string_mapper.h"
+
+#include "move.h"
+#include <map>
+#include <string>
+
+MoveStringMapper::MoveStringMapper()
+{
+ moveMap[Move::NOTHING] = "Nothing";
+ moveMap[Move::MOVE_LEFT] = "MoveLeft";
+ moveMap[Move::MOVE_RIGHT] = "MoveRight";
+ moveMap[Move::SHOOT] = "Shoot";
+ moveMap[Move::BUILD_MISSILE_CONTROLLER] = "BuildMissileController";
+ moveMap[Move::BUILD_ALIEN_FACTORY] = "BuildAlienFactory";
+ moveMap[Move::BUILD_SHIELD] = "BuildShield";
+}
+
+std::string MoveStringMapper::toString(const Move &move)
+{
+ return moveMap[move];
+}
diff --git a/2015-spacebot/src/player_missile.cpp b/2015-spacebot/src/player_missile.cpp
new file mode 100644
index 0000000..a95e928
--- /dev/null
+++ b/2015-spacebot/src/player_missile.cpp
@@ -0,0 +1,6 @@
+#include "player_missile.h"
+
+PlayerMissile::PlayerMissile(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/shield.cpp b/2015-spacebot/src/shield.cpp
new file mode 100644
index 0000000..7457554
--- /dev/null
+++ b/2015-spacebot/src/shield.cpp
@@ -0,0 +1,6 @@
+#include "shield.h"
+
+Shield::Shield(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/src/spacebot.cpp b/2015-spacebot/src/spacebot.cpp
new file mode 100644
index 0000000..17d20b5
--- /dev/null
+++ b/2015-spacebot/src/spacebot.cpp
@@ -0,0 +1,48 @@
+#include "spacebot.h"
+#include "move_string_mapper.h"
+#include "brain/neural_network.h"
+#include <fstream>
+#include <iostream>
+
+Spacebot::Spacebot(std::string outputPath, std::string brainFilename)
+ : _outputFilename(outputPath+"/move.txt"),
+ _brainFilename(brainFilename),
+ _gameState(std::ifstream(outputPath+"/map.txt"))
+{
+}
+
+void Spacebot::writeNextMove()
+{
+ Move move = chooseMove();
+ writeMove(move);
+}
+
+Move Spacebot::chooseMove()
+{
+ auto sensorInputs = _gameState.toBitArray();
+
+ if (!sensorInputs.at(51) || !sensorInputs.at(53))
+ {
+ return Move::BUILD_SHIELD;
+ }
+ else if (sensorInputs.at(55))
+ {
+ return Move::SHOOT;
+ }
+ else if (sensorInputs.at(60) && !sensorInputs.at(59))
+ {
+ return Move::BUILD_MISSILE_CONTROLLER;
+ }
+ else
+ {
+ return Move::NOTHING;
+ }
+}
+
+void Spacebot::writeMove(const Move& move)
+{
+ std::ofstream resultStream(_outputFilename);
+ resultStream << MoveStringMapper().toString(move) << std::endl;
+ return;
+}
+
diff --git a/2015-spacebot/src/spaceship.cpp b/2015-spacebot/src/spaceship.cpp
new file mode 100644
index 0000000..b1cee9a
--- /dev/null
+++ b/2015-spacebot/src/spaceship.cpp
@@ -0,0 +1,6 @@
+#include "spaceship.h"
+
+Spaceship::Spaceship(int x, int y)
+ :GameEntity(x, y)
+{
+}
diff --git a/2015-spacebot/test.sh b/2015-spacebot/test.sh
new file mode 100755
index 0000000..d491ba6
--- /dev/null
+++ b/2015-spacebot/test.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+make && valgrind ./bin/spacebot_test
diff --git a/2015-spacebot/test/catch.hpp b/2015-spacebot/test/catch.hpp
new file mode 100644
index 0000000..391c7ab
--- /dev/null
+++ b/2015-spacebot/test/catch.hpp
@@ -0,0 +1,9460 @@
+/*
+ * CATCH v1.1 build 3 (master branch)
+ * Generated: 2015-05-21 06:16:00.388118
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic ignored "-Wglobal-constructors"
+# pragma clang diagnostic ignored "-Wvariadic-macros"
+# pragma clang diagnostic ignored "-Wc99-extensions"
+# pragma clang diagnostic ignored "-Wunused-variable"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wc++98-compat"
+# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_SFINAE : is basic (C++03) SFINAE supported?
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// A lot of this code is based on Boost (1.53)
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#if (__BORLANDC__ > 0x582 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#if (__EDG_VERSION__ > 238 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#if (__DMC__ > 0x840 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+
+#if (__GNUC_MINOR__ >= 96 )
+//#define CATCH_CONFIG_SFINAE
+#endif
+
+#elif __GNUC__ >= 3
+
+// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
+
+#endif // __GNUC__ < 3
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#if (_MSC_VER >= 1600)
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_CONFIG_CPP11_NOEXCEPT
+#define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
+#define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if (__cplusplus >= 201103L)
+
+# define CATCH_CPP11_OR_GREATER
+
+# ifndef CATCH_CONFIG_CPP11_NULLPTR
+# define CATCH_CONFIG_CPP11_NULLPTR
+# endif
+
+# ifndef CATCH_CONFIG_CPP11_NOEXCEPT
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+# define CATCH_CONFIG_CPP11_GENERATED_METHODS
+# endif
+
+# ifndef CATCH_CONFIG_CPP11_IS_ENUM
+# define CATCH_CONFIG_CPP11_IS_ENUM
+# endif
+
+# ifndef CATCH_CONFIG_CPP11_TUPLE
+# define CATCH_CONFIG_CPP11_TUPLE
+# endif
+
+# ifndef CATCH_CONFIG_SFINAE
+//# define CATCH_CONFIG_SFINAE // Don't use, for now
+# endif
+
+# ifndef CATCH_CONFIG_VARIADIC_MACROS
+# define CATCH_CONFIG_VARIADIC_MACROS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+namespace Catch {
+
+ class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+#else
+ NonCopyable( NonCopyable const& info );
+ NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template<typename ContainerT>
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template<typename AssociativeContainerT>
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+ SourceLineInfo( SourceLineInfo const& other );
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+ bool operator < ( SourceLineInfo const& other ) const;
+
+ std::string file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include <ostream>
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template<typename T>
+ class Ptr {
+ public:
+ Ptr() : m_p( NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() { return m_p; }
+ const T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template<typename T = IShared>
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <stdlib.h>
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr<IConfig const> getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
+
+ };
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+struct AutoReg {
+
+ AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template<typename C>
+ AutoReg( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+ registerTestCase( new MethodTestCase<C>( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ void registerTestCase( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ std::string capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+ std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str("");
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition );
+
+ template<typename T>
+ ExpressionLhs<T const&> operator->* ( T const& operand );
+ ExpressionLhs<bool> operator->* ( bool value );
+
+ template<typename T>
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream.oss << value;
+ return *this;
+ }
+
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+ ResultBuilder& setLhs( std::string const& lhs );
+ ResultBuilder& setRhs( std::string const& rhs );
+ ResultBuilder& setOp( std::string const& op );
+
+ void endExpression();
+
+ std::string reconstructExpression() const;
+ AssertionResult build() const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+ struct ExprComponents {
+ ExprComponents() : testFalse( false ) {}
+ bool testFalse;
+ std::string lhs, rhs, op;
+ } m_exprComponents;
+ CopyableStream m_stream;
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+ template<typename T>
+ inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template<typename T1, typename T2, Operator Op>
+ class Evaluator{};
+
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return opCast( lhs ) == opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) != opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) < opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) > opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) >= opCast( rhs );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) <= opCast( rhs );
+ }
+ };
+
+ template<Operator Op, typename T1, typename T2>
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template<Operator Op, typename T1, typename T2>
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+
+ // unsigned X to long
+ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+
+ // int to unsigned X
+ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+// #included from: catch_sfinae.hpp
+#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
+
+// Try to detect if the current compiler supports SFINAE
+
+namespace Catch {
+
+ struct TrueType {
+ static const bool value = true;
+ typedef void Enable;
+ char sizer[1];
+ };
+ struct FalseType {
+ static const bool value = false;
+ typedef void Disable;
+ char sizer[2];
+ };
+
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<bool> struct NotABooleanExpression;
+
+ template<bool c> struct If : NotABooleanExpression<c> {};
+ template<> struct If<true> : TrueType {};
+ template<> struct If<false> : FalseType {};
+
+ template<int size> struct SizedIf;
+ template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
+ template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
+
+#endif // CATCH_CONFIG_SFINAE
+
+} // end namespace Catch
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+ extern std::string unprintableString;
+
+// SFINAE is currently disabled by default for all compilers.
+// If the non SFINAE version of IsStreamInsertable is ambiguous for you
+// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
+#ifdef CATCH_CONFIG_SFINAE
+
+ template<typename T>
+ class IsStreamInsertableHelper {
+ template<int N> struct TrueIfSizeable : TrueType {};
+
+ template<typename T2>
+ static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
+ static FalseType dummy(...);
+
+ public:
+ typedef SizedIf<sizeof(dummy((T*)0))> type;
+ };
+
+ template<typename T>
+ struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
+
+#else
+
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template<typename T>
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T,
+ bool IsEnum = std::is_enum<T>::value
+ >
+ struct EnumStringMaker
+ {
+ static std::string convert( T const& ) { return unprintableString; }
+ };
+
+ template<typename T>
+ struct EnumStringMaker<T,true>
+ {
+ static std::string convert( T const& v )
+ {
+ return ::Catch::toString(
+ static_cast<typename std::underlying_type<T>::type>(v)
+ );
+ }
+ };
+#endif
+ template<bool C>
+ struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T>
+ static std::string convert( T const& v )
+ {
+ return EnumStringMaker<T>::convert( v );
+ }
+#else
+ template<typename T>
+ static std::string convert( T const& ) { return unprintableString; }
+#endif
+ };
+
+ template<>
+ struct StringMakerBase<true> {
+ template<typename T>
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+ template<typename U>
+ static std::string convert( U* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+// static std::string convert( std::vector<T,Allocator> const& v ) {
+// return Detail::rangeToString( v.begin(), v.end() );
+// }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size<Tuple>::value)
+ >
+ struct ElementPrinter {
+ static void print( const Tuple& tuple, std::ostream& os )
+ {
+ os << ( N ? ", " : " " )
+ << Catch::toString(std::get<N>(tuple));
+ ElementPrinter<Tuple,N+1>::print(tuple,os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct ElementPrinter<Tuple,N,false> {
+ static void print( const Tuple&, std::ostream& ) {}
+ };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+ static std::string convert( const std::tuple<Types...>& tuple )
+ {
+ std::ostringstream os;
+ os << '{';
+ TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+ os << " }";
+ return os.str();
+ }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+ template<typename T>
+ std::string makeString( T const& value ) {
+ return StringMaker<T>::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+ return StringMaker<T>::convert( value );
+}
+
+ namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << Catch::toString( *first );
+ for( ++first ; first != last ; ++first )
+ oss << ", " << Catch::toString( *first );
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template<typename T>
+class ExpressionLhs {
+ ExpressionLhs& operator = ( ExpressionLhs const& );
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+# endif
+
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ ExpressionLhs( ExpressionLhs const& ) = default;
+ ExpressionLhs( ExpressionLhs && ) = default;
+# endif
+
+ template<typename RhsT>
+ ResultBuilder& operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ ResultBuilder& operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ ResultBuilder& operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ bool value = m_lhs ? true : false;
+ m_rb
+ .setLhs( Catch::toString( value ) )
+ .setResultType( value )
+ .endExpression();
+ }
+
+ // Only simple binary expressions are allowed on the LHS.
+ // If more complex compositions are required then place the sub expression in parentheses
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ ResultBuilder& captureExpression( RhsT const& rhs ) {
+ return m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
+ .setLhs( Catch::toString( m_lhs ) )
+ .setRhs( Catch::toString( rhs ) )
+ .setOp( Internal::OperatorTraits<Op>::getName() );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template<typename T>
+ inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
+ return ExpressionLhs<T const&>( *this, operand );
+ }
+
+ inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
+ return ExpressionLhs<bool>( *this, value );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+
+ virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_IPHONE
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include <string>
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #ifdef DEBUG
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_BREAK_INTO_DEBUGGER() \
+ if( Catch::isDebuggerActive() ) { \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" ); \
+ }
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+ #endif
+ #endif
+
+#elif defined(_MSC_VER)
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ ( __catchResult->*expr ).endExpression(); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+ try { \
+ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+ __catchResult \
+ .setLhs( Catch::toString( arg ) ) \
+ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
+ .setOp( "matches" ) \
+ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+ __catchResult.captureExpression(); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+ bool allOk() const {
+ return failed == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedMicroseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ uint64_t m_ticks;
+ };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+ class Section : NonCopyable {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast<int>( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast<std::size_t>( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator<T>* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator<T>* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector<const IGenerator<T>*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template<typename T>
+ CompositeGenerator<T> between( T from, T to ) {
+ CompositeGenerator<T> generators;
+ generators.add( new BetweenGenerator<T>( from, to ) );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3 ){
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ valuesGen->add( val4 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate() const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ virtual std::string translate() const {
+ try {
+ throw;
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+ static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+ }
+
+ friend bool operator == ( Approx const& lhs, double rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ friend bool operator != ( double lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ friend bool operator != ( Approx const& lhs, double rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+ };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+ return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ExpressionT>
+ struct Matcher : SharedImpl<IShared>
+ {
+ typedef ExpressionT ExpressionType;
+
+ virtual ~Matcher() {}
+ virtual Ptr<Matcher> clone() const = 0;
+ virtual bool match( ExpressionT const& expr ) const = 0;
+ virtual std::string toString() const = 0;
+ };
+
+ template<typename DerivedT, typename ExpressionT>
+ struct MatcherImpl : Matcher<ExpressionT> {
+
+ virtual Ptr<Matcher<ExpressionT> > clone() const {
+ return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
+ }
+ };
+
+ namespace Generic {
+
+ template<typename ExpressionT>
+ class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AllOf() {}
+ AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AllOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( !m_matchers[i]->match( expr ) )
+ return false;
+ return true;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " and ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ template<typename ExpressionT>
+ class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+ public:
+
+ AnyOf() {}
+ AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
+
+ AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+ m_matchers.push_back( matcher.clone() );
+ return *this;
+ }
+ virtual bool match( ExpressionT const& expr ) const
+ {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i )
+ if( m_matchers[i]->match( expr ) )
+ return true;
+ return false;
+ }
+ virtual std::string toString() const {
+ std::ostringstream oss;
+ oss << "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ oss << " or ";
+ oss << m_matchers[i]->toString();
+ }
+ oss << " )";
+ return oss.str();
+ }
+
+ private:
+ std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+ };
+
+ }
+
+ namespace StdString {
+
+ inline std::string makeString( std::string const& str ) { return str; }
+ inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+ struct Equals : MatcherImpl<Equals, std::string> {
+ Equals( std::string const& str ) : m_str( str ){}
+ Equals( Equals const& other ) : m_str( other.m_str ){}
+
+ virtual ~Equals();
+
+ virtual bool match( std::string const& expr ) const {
+ return m_str == expr;
+ }
+ virtual std::string toString() const {
+ return "equals: \"" + m_str + "\"";
+ }
+
+ std::string m_str;
+ };
+
+ struct Contains : MatcherImpl<Contains, std::string> {
+ Contains( std::string const& substr ) : m_substr( substr ){}
+ Contains( Contains const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~Contains();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) != std::string::npos;
+ }
+ virtual std::string toString() const {
+ return "contains: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct StartsWith : MatcherImpl<StartsWith, std::string> {
+ StartsWith( std::string const& substr ) : m_substr( substr ){}
+ StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~StartsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == 0;
+ }
+ virtual std::string toString() const {
+ return "starts with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+
+ struct EndsWith : MatcherImpl<EndsWith, std::string> {
+ EndsWith( std::string const& substr ) : m_substr( substr ){}
+ EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+
+ virtual ~EndsWith();
+
+ virtual bool match( std::string const& expr ) const {
+ return expr.find( m_substr ) == expr.size() - m_substr.size();
+ }
+ virtual std::string toString() const {
+ return "ends with: \"" + m_substr + "\"";
+ }
+
+ std::string m_substr;
+ };
+ } // namespace StdString
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+ }
+ template<typename ExpressionT>
+ inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+ Impl::Matcher<ExpressionT> const& m2,
+ Impl::Matcher<ExpressionT> const& m3 ) {
+ return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+ }
+
+ inline Impl::StdString::Equals Equals( std::string const& str ) {
+ return Impl::StdString::Equals( str );
+ }
+ inline Impl::StdString::Equals Equals( const char* str ) {
+ return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
+ }
+ inline Impl::StdString::Contains Contains( std::string const& substr ) {
+ return Impl::StdString::Contains( substr );
+ }
+ inline Impl::StdString::Contains Contains( const char* substr ) {
+ return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
+ return Impl::StdString::StartsWith( substr );
+ }
+ inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
+ return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+ }
+ inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
+ return Impl::StdString::EndsWith( substr );
+ }
+ inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
+ return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = NULL;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != NULL; }
+ bool none() const { return nullableValue == NULL; }
+
+ bool operator !() const { return nullableValue == NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T* nullableValue;
+ char storage[sizeof(T)];
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestCase;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ TestCaseInfo( TestCaseInfo const& other );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::set<std::string> tags;
+ std::set<std::string> lcaseTags;
+ std::string tagsAsString;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestCase* testCase, TestCaseInfo const& info );
+ TestCase( TestCase const& other );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ void swap( TestCase& other );
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+ TestCase& operator = ( TestCase const& other );
+
+ private:
+ Ptr<ITestCase> test;
+ };
+
+ TestCase makeTestCase( ITestCase* testCase,
+ std::string const& className,
+ std::string const& name,
+ std::string const& description,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+ class OcMethod : public SharedImpl<ITestCase> {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline size_t registerTestMethods() {
+ size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( NULL, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ template<typename MatcherT>
+ struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder<Equals> {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string toString() const {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder<Contains> {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string toString() const {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder<StartsWith> {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string toString() const {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder<EndsWith> {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( ExpressionType const& str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string toString() const {
+ return "ends with: " + Catch::toString( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_runner.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec {
+ struct Pattern : SharedImpl<> {
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ };
+ class NamePattern : public Pattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+ NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
+ if( startsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_name, "*" ) ) {
+ m_name = m_name.substr( 0, m_name.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_name == toLower( testCase.name );
+ case WildcardAtStart:
+ return endsWith( toLower( testCase.name ), m_name );
+ case WildcardAtEnd:
+ return startsWith( toLower( testCase.name ), m_name );
+ case WildcardAtBothEnds:
+ return contains( toLower( testCase.name ), m_name );
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+ throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ }
+ private:
+ std::string m_name;
+ WildcardPosition m_wildcard;
+ };
+ class TagPattern : public Pattern {
+ public:
+ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ virtual ~TagPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+ }
+ private:
+ std::string m_tag;
+ };
+ class ExcludedPattern : public Pattern {
+ public:
+ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+ virtual ~ExcludedPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ private:
+ Ptr<Pattern> m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<Ptr<Pattern> > m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const {
+ // All patterns in a filter must match for the filter to be a match
+ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
+ if( !(*it)->matches( testCase ) )
+ return false;
+ return true;
+ }
+ };
+
+ public:
+ bool hasFilters() const {
+ return !m_filters.empty();
+ }
+ bool matches( TestCaseInfo const& testCase ) const {
+ // A TestSpec matches if any filter matches
+ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+ if( it->matches( testCase ) )
+ return true;
+ return false;
+ }
+
+ private:
+ std::vector<Filter> m_filters;
+
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ visitChar( m_arg[m_pos] );
+ if( m_mode == Name )
+ addPattern<TestSpec::NamePattern>();
+ return *this;
+ }
+ TestSpec testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ private:
+ void visitChar( char c ) {
+ if( m_mode == None ) {
+ switch( c ) {
+ case ' ': return;
+ case '~': m_exclusion = true; return;
+ case '[': return startNewMode( Tag, ++m_pos );
+ case '"': return startNewMode( QuotedName, ++m_pos );
+ default: startNewMode( Name, m_pos ); break;
+ }
+ }
+ if( m_mode == Name ) {
+ if( c == ',' ) {
+ addPattern<TestSpec::NamePattern>();
+ addFilter();
+ }
+ else if( c == '[' ) {
+ if( subString() == "exclude:" )
+ m_exclusion = true;
+ else
+ addPattern<TestSpec::NamePattern>();
+ startNewMode( Tag, ++m_pos );
+ }
+ }
+ else if( m_mode == QuotedName && c == '"' )
+ addPattern<TestSpec::NamePattern>();
+ else if( m_mode == Tag && c == ']' )
+ addPattern<TestSpec::TagPattern>();
+ }
+ void startNewMode( Mode mode, std::size_t start ) {
+ m_mode = mode;
+ m_start = start;
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ if( startsWith( token, "exclude:" ) ) {
+ m_exclusion = true;
+ token = token.substr( 8 );
+ }
+ if( !token.empty() ) {
+ Ptr<TestSpec::Pattern> pattern = new T( token );
+ if( m_exclusion )
+ pattern = new TestSpec::ExcludedPattern( pattern );
+ m_currentFilter.m_patterns.push_back( pattern );
+ }
+ m_exclusion = false;
+ m_mode = None;
+ }
+ void addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+ };
+ inline TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct Verbosity { enum Level {
+ NoOutput = 0,
+ Quiet,
+ Normal
+ }; };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+ struct RunTests { enum InWhatOrder {
+ InDeclarationOrder,
+ InLexicographicalOrder,
+ InRandomOrder
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : IShared {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ virtual RunTests::InWhatOrder runOrder() const = 0;
+ virtual unsigned int rngSeed() const = 0;
+ virtual bool forceColour() const = 0;
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+#include <streambuf>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ class Stream {
+ public:
+ Stream();
+ Stream( std::streambuf* _streamBuf, bool _isOwned );
+ void release();
+
+ std::streambuf* streamBuf;
+
+ private:
+ bool isOwned;
+ };
+
+ std::ostream& cout();
+ std::ostream& cerr();
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <ctime>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct ConfigData {
+
+ ConfigData()
+ : listTests( false ),
+ listTags( false ),
+ listReporters( false ),
+ listTestNamesOnly( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ forceColour( false ),
+ abortAfter( -1 ),
+ rngSeed( 0 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter ),
+ runOrder( RunTests::InDeclarationOrder )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+ bool forceColour;
+
+ int abortAfter;
+ unsigned int rngSeed;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+ RunTests::InWhatOrder runOrder;
+
+ std::string reporterName;
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> testsOrTags;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ : m_os( Catch::cout().rdbuf() )
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_os( Catch::cout().rdbuf() )
+ {
+ if( !data.testsOrTags.empty() ) {
+ TestSpecParser parser( ITagAliasRegistry::get() );
+ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+ parser.parse( data.testsOrTags[i] );
+ m_testSpec = parser.testSpec();
+ }
+ }
+
+ virtual ~Config() {
+ m_os.rdbuf( Catch::cout().rdbuf() );
+ m_stream.release();
+ }
+
+ void setFilename( std::string const& filename ) {
+ m_data.outputFilename = filename;
+ }
+
+ std::string const& getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool listTests() const { return m_data.listTests; }
+ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool listTags() const { return m_data.listTags; }
+ bool listReporters() const { return m_data.listReporters; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+
+ void setStreamBuf( std::streambuf* buf ) {
+ m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
+ }
+
+ void useStream( std::string const& streamName ) {
+ Stream stream = createStream( streamName );
+ setStreamBuf( stream.streamBuf );
+ m_stream.release();
+ m_stream = stream;
+ }
+
+ std::string getReporterName() const { return m_data.reporterName; }
+
+ int abortAfter() const { return m_data.abortAfter; }
+
+ TestSpec const& testSpec() const { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+ bool showInvisibles() const { return m_data.showInvisibles; }
+
+ // IConfig interface
+ virtual bool allowThrows() const { return !m_data.noThrow; }
+ virtual std::ostream& stream() const { return m_os; }
+ virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
+ virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; }
+ virtual unsigned int rngSeed() const { return m_data.rngSeed; }
+ virtual bool forceColour() const { return m_data.forceColour; }
+
+ private:
+ ConfigData m_data;
+
+ Stream m_stream;
+ mutable std::ostream m_os;
+ TestSpec m_testSpec;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <memory>
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+ struct UnpositionalTag {};
+
+ extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+ UnpositionalTag _;
+#endif
+
+ namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ using namespace Tbc;
+
+ inline bool startsWith( std::string const& str, std::string const& prefix ) {
+ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+ }
+
+ template<typename T> struct RemoveConstRef{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+ template<typename T> struct IsBool { static const bool value = false; };
+ template<> struct IsBool<bool> { static const bool value = true; };
+
+ template<typename T>
+ void convertInto( std::string const& _source, T& _dest ) {
+ std::stringstream ss;
+ ss << _source;
+ ss >> _dest;
+ if( ss.fail() )
+ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+ }
+ inline void convertInto( std::string const& _source, std::string& _dest ) {
+ _dest = _source;
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+ _dest = true;
+ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+ _dest = false;
+ else
+ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
+ }
+ inline void convertInto( bool _source, bool& _dest ) {
+ _dest = _source;
+ }
+ template<typename T>
+ inline void convertInto( bool, T& ) {
+ throw std::runtime_error( "Invalid conversion" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+# endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual void setFlag( ConfigT& config ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ void setFlag( ConfigT& config ) const {
+ functionObj->setFlag( config );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) const {}
+ virtual void setFlag( C& ) const {}
+ virtual bool takesArg() const { return true; }
+ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+ };
+
+ template<typename C, typename M>
+ struct BoundDataMember : IArgFunction<C>{
+ BoundDataMember( M C::* _member ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ convertInto( stringValue, p.*member );
+ }
+ virtual void setFlag( C& p ) const {
+ convertInto( true, p.*member );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+ M C::* member;
+ };
+ template<typename C, typename M>
+ struct BoundUnaryMethod : IArgFunction<C>{
+ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( stringValue, value );
+ (p.*member)( value );
+ }
+ virtual void setFlag( C& p ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( true, value );
+ (p.*member)( value );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+ void (C::*member)( M );
+ };
+ template<typename C>
+ struct BoundNullaryMethod : IArgFunction<C>{
+ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ (p.*member)();
+ }
+ virtual void setFlag( C& p ) const {
+ (p.*member)();
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+ void (C::*member)();
+ };
+
+ template<typename C>
+ struct BoundUnaryFunction : IArgFunction<C>{
+ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ function( obj );
+ }
+ virtual void setFlag( C& p ) const {
+ function( p );
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+ void (*function)( C& );
+ };
+
+ template<typename C, typename T>
+ struct BoundBinaryFunction : IArgFunction<C>{
+ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( stringValue, value );
+ function( obj, value );
+ }
+ virtual void setFlag( C& obj ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( true, value );
+ function( obj, value );
+ }
+ virtual bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ struct Parser {
+ Parser() : separators( " \t=:" ) {}
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+ const std::string doubleDash = "--";
+ for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
+ parseIntoTokens( argv[i] , tokens);
+ }
+ void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
+ while( !arg.empty() ) {
+ Parser::Token token( Parser::Token::Positional, arg );
+ arg = "";
+ if( token.data[0] == '-' ) {
+ if( token.data.size() > 1 && token.data[1] == '-' ) {
+ token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
+ }
+ else {
+ token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
+ if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
+ arg = "-" + token.data.substr( 1 );
+ token.data = token.data.substr( 0, 1 );
+ }
+ }
+ }
+ if( token.type != Parser::Token::Positional ) {
+ std::size_t pos = token.data.find_first_of( separators );
+ if( pos != std::string::npos ) {
+ arg = token.data.substr( pos+1 );
+ token.data = token.data.substr( 0, pos );
+ }
+ }
+ tokens.push_back( token );
+ }
+ }
+ std::string separators;
+ };
+
+ template<typename ConfigT>
+ struct CommonArgProperties {
+ CommonArgProperties() {}
+ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+ Detail::BoundArgFunction<ConfigT> boundField;
+ std::string description;
+ std::string detail;
+ std::string placeholder; // Only value if boundField takes an arg
+
+ bool takesArg() const {
+ return !placeholder.empty();
+ }
+ void validate() const {
+ if( !boundField.isSet() )
+ throw std::logic_error( "option not bound" );
+ }
+ };
+ struct OptionArgProperties {
+ std::vector<std::string> shortNames;
+ std::string longName;
+
+ bool hasShortName( std::string const& shortName ) const {
+ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+ }
+ bool hasLongName( std::string const& _longName ) const {
+ return _longName == longName;
+ }
+ };
+ struct PositionalArgProperties {
+ PositionalArgProperties() : position( -1 ) {}
+ int position; // -1 means non-positional (floating)
+
+ bool isFixedPositional() const {
+ return position != -1;
+ }
+ };
+
+ template<typename ConfigT>
+ class CommandLine {
+
+ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+ Arg() {}
+ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+ using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+ std::string dbgName() const {
+ if( !longName.empty() )
+ return "--" + longName;
+ if( !shortNames.empty() )
+ return "-" + shortNames[0];
+ return "positional args";
+ }
+ std::string commands() const {
+ std::ostringstream oss;
+ bool first = true;
+ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+ for(; it != itEnd; ++it ) {
+ if( first )
+ first = false;
+ else
+ oss << ", ";
+ oss << "-" << *it;
+ }
+ if( !longName.empty() ) {
+ if( !first )
+ oss << ", ";
+ oss << "--" << longName;
+ }
+ if( !placeholder.empty() )
+ oss << " <" << placeholder << ">";
+ return oss.str();
+ }
+ };
+
+ // NOTE: std::auto_ptr is deprecated in c++11/c++0x
+#if defined(__cplusplus) && __cplusplus > 199711L
+ typedef std::unique_ptr<Arg> ArgAutoPtr;
+#else
+ typedef std::auto_ptr<Arg> ArgAutoPtr;
+#endif
+
+ friend void addOptName( Arg& arg, std::string const& optName )
+ {
+ if( optName.empty() )
+ return;
+ if( Detail::startsWith( optName, "--" ) ) {
+ if( !arg.longName.empty() )
+ throw std::logic_error( "Only one long opt may be specified. '"
+ + arg.longName
+ + "' already specified, now attempting to add '"
+ + optName + "'" );
+ arg.longName = optName.substr( 2 );
+ }
+ else if( Detail::startsWith( optName, "-" ) )
+ arg.shortNames.push_back( optName.substr( 1 ) );
+ else
+ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+ }
+ friend void setPositionalArg( Arg& arg, int position )
+ {
+ arg.position = position;
+ }
+
+ class ArgBuilder {
+ public:
+ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+ // Bind a non-boolean data member (requires placeholder string)
+ template<typename C, typename M>
+ void bind( M C::* field, std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+ m_arg->placeholder = placeholder;
+ }
+ // Bind a boolean data member (no placeholder required)
+ template<typename C>
+ void bind( bool C::* field ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+ }
+
+ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+ template<typename C, typename M>
+ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+ m_arg->placeholder = placeholder;
+ }
+
+ // Bind a method taking a single, boolean argument (no placeholder string required)
+ template<typename C>
+ void bind( void (C::* unaryMethod)( bool ) ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+ }
+
+ // Bind a method that takes no arguments (will be called if opt is present)
+ template<typename C>
+ void bind( void (C::* nullaryMethod)() ) {
+ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+ template<typename C>
+ void bind( void (* unaryFunction)( C& ) ) {
+ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+ template<typename C, typename T>
+ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+ m_arg->placeholder = placeholder;
+ }
+
+ ArgBuilder& describe( std::string const& description ) {
+ m_arg->description = description;
+ return *this;
+ }
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
+ return *this;
+ }
+
+ protected:
+ Arg* m_arg;
+ };
+
+ class OptBuilder : public ArgBuilder {
+ public:
+ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+ OptBuilder& operator[]( std::string const& optName ) {
+ addOptName( *ArgBuilder::m_arg, optName );
+ return *this;
+ }
+ };
+
+ public:
+
+ CommandLine()
+ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+ m_highestSpecifiedArgPosition( 0 ),
+ m_throwOnUnrecognisedTokens( false )
+ {}
+ CommandLine( CommandLine const& other )
+ : m_boundProcessName( other.m_boundProcessName ),
+ m_options ( other.m_options ),
+ m_positionalArgs( other.m_positionalArgs ),
+ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+ {
+ if( other.m_floatingArg.get() )
+ m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+ }
+
+ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+ m_throwOnUnrecognisedTokens = shouldThrow;
+ return *this;
+ }
+
+ OptBuilder operator[]( std::string const& optName ) {
+ m_options.push_back( Arg() );
+ addOptName( m_options.back(), optName );
+ OptBuilder builder( &m_options.back() );
+ return builder;
+ }
+
+ ArgBuilder operator[]( int position ) {
+ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+ if( position > m_highestSpecifiedArgPosition )
+ m_highestSpecifiedArgPosition = position;
+ setPositionalArg( m_positionalArgs[position], position );
+ ArgBuilder builder( &m_positionalArgs[position] );
+ return builder;
+ }
+
+ // Invoke this with the _ instance
+ ArgBuilder operator[]( UnpositionalTag ) {
+ if( m_floatingArg.get() )
+ throw std::logic_error( "Only one unpositional argument can be added" );
+ m_floatingArg.reset( new Arg() );
+ ArgBuilder builder( m_floatingArg.get() );
+ return builder;
+ }
+
+ template<typename C, typename M>
+ void bindProcessName( M C::* field ) {
+ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+ }
+ template<typename C, typename M>
+ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+ }
+
+ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+ std::size_t maxWidth = 0;
+ for( it = itBegin; it != itEnd; ++it )
+ maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+ for( it = itBegin; it != itEnd; ++it ) {
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
+ .setWidth( maxWidth+indent )
+ .setIndent( indent ) );
+ Detail::Text desc( it->description, Detail::TextAttributes()
+ .setWidth( width - maxWidth - 3 ) );
+
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
+ os << usageCol;
+
+ if( i < desc.size() && !desc[i].empty() )
+ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+ << desc[i];
+ os << "\n";
+ }
+ }
+ }
+ std::string optUsage() const {
+ std::ostringstream oss;
+ optUsage( oss );
+ return oss.str();
+ }
+
+ void argSynopsis( std::ostream& os ) const {
+ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+ if( i > 1 )
+ os << " ";
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+ if( it != m_positionalArgs.end() )
+ os << "<" << it->second.placeholder << ">";
+ else if( m_floatingArg.get() )
+ os << "<" << m_floatingArg->placeholder << ">";
+ else
+ throw std::logic_error( "non consecutive positional arguments with no floating args" );
+ }
+ // !TBD No indication of mandatory args
+ if( m_floatingArg.get() ) {
+ if( m_highestSpecifiedArgPosition > 1 )
+ os << " ";
+ os << "[<" << m_floatingArg->placeholder << "> ...]";
+ }
+ }
+ std::string argSynopsis() const {
+ std::ostringstream oss;
+ argSynopsis( oss );
+ return oss.str();
+ }
+
+ void usage( std::ostream& os, std::string const& procName ) const {
+ validate();
+ os << "usage:\n " << procName << " ";
+ argSynopsis( os );
+ if( !m_options.empty() ) {
+ os << " [options]\n\nwhere options are: \n";
+ optUsage( os, 2 );
+ }
+ os << "\n";
+ }
+ std::string usage( std::string const& procName ) const {
+ std::ostringstream oss;
+ usage( oss, procName );
+ return oss.str();
+ }
+
+ ConfigT parse( int argc, char const * const * argv ) const {
+ ConfigT config;
+ parseInto( argc, argv, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+ std::string processName = argv[0];
+ std::size_t lastSlash = processName.find_last_of( "/\\" );
+ if( lastSlash != std::string::npos )
+ processName = processName.substr( lastSlash+1 );
+ m_boundProcessName.set( config, processName );
+ std::vector<Parser::Token> tokens;
+ Parser parser;
+ parser.parseIntoTokens( argc, argv, tokens );
+ return populate( tokens, config );
+ }
+
+ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ validate();
+ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+ unusedTokens = populateFixedArgs( unusedTokens, config );
+ unusedTokens = populateFloatingArgs( unusedTokens, config );
+ return unusedTokens;
+ }
+
+ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ std::vector<std::string> errors;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+ for(; it != itEnd; ++it ) {
+ Arg const& arg = *it;
+
+ try {
+ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+ if( arg.takesArg() ) {
+ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+ errors.push_back( "Expected argument to option: " + token.data );
+ else
+ arg.boundField.set( config, tokens[++i].data );
+ }
+ else {
+ arg.boundField.setFlag( config );
+ }
+ break;
+ }
+ }
+ catch( std::exception& ex ) {
+ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+ }
+ }
+ if( it == itEnd ) {
+ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+ unusedTokens.push_back( token );
+ else if( errors.empty() && m_throwOnUnrecognisedTokens )
+ errors.push_back( "unrecognised option: " + token.data );
+ }
+ }
+ if( !errors.empty() ) {
+ std::ostringstream oss;
+ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+ it != itEnd;
+ ++it ) {
+ if( it != errors.begin() )
+ oss << "\n";
+ oss << *it;
+ }
+ throw std::runtime_error( oss.str() );
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ int position = 1;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+ if( it != m_positionalArgs.end() )
+ it->second.boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ if( token.type == Parser::Token::Positional )
+ position++;
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ if( !m_floatingArg.get() )
+ return tokens;
+ std::vector<Parser::Token> unusedTokens;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ if( token.type == Parser::Token::Positional )
+ m_floatingArg->boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ }
+ return unusedTokens;
+ }
+
+ void validate() const
+ {
+ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+ throw std::logic_error( "No options or arguments specified" );
+
+ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
+ itEnd = m_options.end();
+ it != itEnd; ++it )
+ it->validate();
+ }
+
+ private:
+ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+ std::vector<Arg> m_options;
+ std::map<int, Arg> m_positionalArgs;
+ ArgAutoPtr m_floatingArg;
+ int m_highestSpecifiedArgPosition;
+ bool m_throwOnUnrecognisedTokens;
+ };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+
+namespace Catch {
+
+ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+ inline void abortAfterX( ConfigData& config, int x ) {
+ if( x < 1 )
+ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+ config.abortAfter = x;
+ }
+ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+
+ inline void addWarning( ConfigData& config, std::string const& _warning ) {
+ if( _warning == "NoAssertions" )
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+ else
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
+ }
+ inline void setOrder( ConfigData& config, std::string const& order ) {
+ if( startsWith( "declared", order ) )
+ config.runOrder = RunTests::InDeclarationOrder;
+ else if( startsWith( "lexical", order ) )
+ config.runOrder = RunTests::InLexicographicalOrder;
+ else if( startsWith( "random", order ) )
+ config.runOrder = RunTests::InRandomOrder;
+ else
+ throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
+ }
+ inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+ if( seed == "time" ) {
+ config.rngSeed = static_cast<unsigned int>( std::time(0) );
+ }
+ else {
+ std::stringstream ss;
+ ss << seed;
+ ss >> config.rngSeed;
+ if( ss.fail() )
+ throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
+ }
+ }
+ inline void setVerbosity( ConfigData& config, int level ) {
+ // !TBD: accept strings?
+ config.verbosity = static_cast<Verbosity::Level>( level );
+ }
+ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+ config.showDurations = _showDurations
+ ? ShowDurations::Always
+ : ShowDurations::Never;
+ }
+ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+ std::ifstream f( _filename.c_str() );
+ if( !f.is_open() )
+ throw std::domain_error( "Unable to load input file: " + _filename );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, "#" ) )
+ addTestOrTags( config, "\"" + line + "\"," );
+ }
+ }
+
+ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+ using namespace Clara;
+ CommandLine<ConfigData> cli;
+
+ cli.bindProcessName( &ConfigData::processName );
+
+ cli["-?"]["-h"]["--help"]
+ .describe( "display usage information" )
+ .bind( &ConfigData::showHelp );
+
+ cli["-l"]["--list-tests"]
+ .describe( "list all/matching test cases" )
+ .bind( &ConfigData::listTests );
+
+ cli["-t"]["--list-tags"]
+ .describe( "list all/matching tags" )
+ .bind( &ConfigData::listTags );
+
+ cli["-s"]["--success"]
+ .describe( "include successful tests in output" )
+ .bind( &ConfigData::showSuccessfulTests );
+
+ cli["-b"]["--break"]
+ .describe( "break into debugger on failure" )
+ .bind( &ConfigData::shouldDebugBreak );
+
+ cli["-e"]["--nothrow"]
+ .describe( "skip exception tests" )
+ .bind( &ConfigData::noThrow );
+
+ cli["-i"]["--invisibles"]
+ .describe( "show invisibles (tabs, newlines)" )
+ .bind( &ConfigData::showInvisibles );
+
+ cli["-o"]["--out"]
+ .describe( "output filename" )
+ .bind( &ConfigData::outputFilename, "filename" );
+
+ cli["-r"]["--reporter"]
+// .placeholder( "name[:filename]" )
+ .describe( "reporter to use (defaults to console)" )
+ .bind( &ConfigData::reporterName, "name" );
+
+ cli["-n"]["--name"]
+ .describe( "suite name" )
+ .bind( &ConfigData::name, "name" );
+
+ cli["-a"]["--abort"]
+ .describe( "abort at first failure" )
+ .bind( &abortAfterFirst );
+
+ cli["-x"]["--abortx"]
+ .describe( "abort after x failures" )
+ .bind( &abortAfterX, "no. failures" );
+
+ cli["-w"]["--warn"]
+ .describe( "enable warnings" )
+ .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+// cli.into( &setVerbosity )
+// .describe( "level of verbosity (0=no output)" )
+// .shortOpt( "v")
+// .longOpt( "verbosity" )
+// .placeholder( "level" );
+
+ cli[_]
+ .describe( "which test or tests to use" )
+ .bind( &addTestOrTags, "test name, pattern or tags" );
+
+ cli["-d"]["--durations"]
+ .describe( "show test durations" )
+ .bind( &setShowDurations, "yes/no" );
+
+ cli["-f"]["--input-file"]
+ .describe( "load test names to run from a file" )
+ .bind( &loadTestNamesFromFile, "filename" );
+
+ // Less common commands which don't have a short form
+ cli["--list-test-names-only"]
+ .describe( "list all/matching test cases names only" )
+ .bind( &ConfigData::listTestNamesOnly );
+
+ cli["--list-reporters"]
+ .describe( "list all reporters" )
+ .bind( &ConfigData::listReporters );
+
+ cli["--order"]
+ .describe( "test case order (defaults to decl)" )
+ .bind( &setOrder, "decl|lex|rand" );
+
+ cli["--rng-seed"]
+ .describe( "set a specific seed for random numbers" )
+ .bind( &setRngSeed, "'time'|number" );
+
+ cli["--force-colour"]
+ .describe( "force colourised output" )
+ .bind( &ConfigData::forceColour );
+
+ return cli;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# endif
+# else
+# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+ using Tbc::Text;
+ using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = Yellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = Yellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour const& other );
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ bool m_moved;
+ };
+
+ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+#include <assert.h>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig> m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ ReporterPreferences()
+ : shouldRedirectStdOut( false )
+ {}
+
+ bool shouldRedirectStdOut;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat() : used( false ) {}
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name ) : name( _name ) {}
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+ virtual ~AssertionStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = default;
+ AssertionStats& operator = ( AssertionStats && ) = default;
+# endif
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+ virtual ~SectionStats();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+# endif
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+ virtual ~TestCaseStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+# endif
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+ virtual ~TestGroupStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+# endif
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ virtual ~TestRunStats();
+
+# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestRunStats( TestRunStats const& _other )
+ : runInfo( _other.runInfo ),
+ totals( _other.totals ),
+ aborting( _other.aborting )
+ {}
+# else
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+# endif
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct IStreamingReporter : IShared {
+ virtual ~IStreamingReporter();
+
+ // Implementing class must also provide the following static method:
+ // static std::string getDescription();
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+ };
+
+ struct IReporterFactory {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, IReporterFactory*> FactoryMap;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ };
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+ inline std::size_t listTests( Config const& config ) {
+
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Matching test cases:\n";
+ else {
+ Catch::cout() << "All available test cases:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::size_t matchedTests = 0;
+ TextAttributes nameAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+ if( !testCaseInfo.tags.empty() )
+ Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+ }
+
+ if( !config.testSpec().hasFilters() )
+ Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
+ else
+ Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
+ return matchedTests;
+ }
+
+ inline std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( !config.testSpec().hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Catch::cout() << testCaseInfo.name << std::endl;
+ }
+ return matchedTests;
+ }
+
+ struct TagInfo {
+ TagInfo() : count ( 0 ) {}
+ void add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+ std::string all() const {
+ std::string out;
+ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+ it != itEnd;
+ ++it )
+ out += "[" + *it + "]";
+ return out;
+ }
+ std::set<std::string> spellings;
+ std::size_t count;
+ };
+
+ inline std::size_t listTags( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+ Catch::cout() << "All available tags:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
+ tagItEnd = it->getTestCaseInfo().tags.end();
+ tagIt != tagItEnd;
+ ++tagIt ) {
+ std::string tagName = *tagIt;
+ std::string lcaseTagName = toLower( tagName );
+ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+ countItEnd = tagCounts.end();
+ countIt != countItEnd;
+ ++countIt ) {
+ std::ostringstream oss;
+ oss << " " << std::setw(2) << countIt->second.count << " ";
+ Text wrapper( countIt->second.all(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( oss.str().size() )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+ Catch::cout() << oss.str() << wrapper << "\n";
+ }
+ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
+ return tagCounts.size();
+ }
+
+ inline std::size_t listReporters( Config const& /*config*/ ) {
+ Catch::cout() << "Available reporters:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+ std::size_t maxNameLen = 0;
+ for(it = itBegin; it != itEnd; ++it )
+ maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+ for(it = itBegin; it != itEnd; ++it ) {
+ Text wrapper( it->second->getDescription(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( 7+maxNameLen )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+ Catch::cout() << " "
+ << it->first
+ << ":"
+ << std::string( maxNameLen - it->first.size() + 2, ' ' )
+ << wrapper << "\n";
+ }
+ Catch::cout() << std::endl;
+ return factories.size();
+ }
+
+ inline Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> listedCount;
+ if( config.listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( config );
+ if( config.listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+ if( config.listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( config );
+ if( config.listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters( config );
+ return listedCount;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_runner_impl.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+
+namespace Catch {
+namespace SectionTracking {
+
+ class TrackedSection {
+
+ typedef std::map<std::string, TrackedSection> TrackedSections;
+
+ public:
+ enum RunState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ Completed
+ };
+
+ TrackedSection( std::string const& name, TrackedSection* parent )
+ : m_name( name ), m_runState( NotStarted ), m_parent( parent )
+ {}
+
+ RunState runState() const { return m_runState; }
+
+ TrackedSection* findChild( std::string const& childName ) {
+ TrackedSections::iterator it = m_children.find( childName );
+ return it != m_children.end()
+ ? &it->second
+ : NULL;
+ }
+ TrackedSection* acquireChild( std::string const& childName ) {
+ if( TrackedSection* child = findChild( childName ) )
+ return child;
+ m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
+ return findChild( childName );
+ }
+ void enter() {
+ if( m_runState == NotStarted )
+ m_runState = Executing;
+ }
+ void leave() {
+ for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
+ it != itEnd;
+ ++it )
+ if( it->second.runState() != Completed ) {
+ m_runState = ExecutingChildren;
+ return;
+ }
+ m_runState = Completed;
+ }
+ TrackedSection* getParent() {
+ return m_parent;
+ }
+ bool hasChildren() const {
+ return !m_children.empty();
+ }
+
+ private:
+ std::string m_name;
+ RunState m_runState;
+ TrackedSections m_children;
+ TrackedSection* m_parent;
+
+ };
+
+ class TestCaseTracker {
+ public:
+ TestCaseTracker( std::string const& testCaseName )
+ : m_testCase( testCaseName, NULL ),
+ m_currentSection( &m_testCase ),
+ m_completedASectionThisRun( false )
+ {}
+
+ bool enterSection( std::string const& name ) {
+ TrackedSection* child = m_currentSection->acquireChild( name );
+ if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
+ return false;
+
+ m_currentSection = child;
+ m_currentSection->enter();
+ return true;
+ }
+ void leaveSection() {
+ m_currentSection->leave();
+ m_currentSection = m_currentSection->getParent();
+ assert( m_currentSection != NULL );
+ m_completedASectionThisRun = true;
+ }
+
+ bool currentSectionHasChildren() const {
+ return m_currentSection->hasChildren();
+ }
+ bool isCompleted() const {
+ return m_testCase.runState() == TrackedSection::Completed;
+ }
+
+ class Guard {
+ public:
+ Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
+ m_tracker.enterTestCase();
+ }
+ ~Guard() {
+ m_tracker.leaveTestCase();
+ }
+ private:
+ Guard( Guard const& );
+ void operator = ( Guard const& );
+ TestCaseTracker& m_tracker;
+ };
+
+ private:
+ void enterTestCase() {
+ m_currentSection = &m_testCase;
+ m_completedASectionThisRun = false;
+ m_testCase.enter();
+ }
+ void leaveTestCase() {
+ m_testCase.leave();
+ }
+
+ TrackedSection m_testCase;
+ TrackedSection* m_currentSection;
+ bool m_completedASectionThisRun;
+ };
+
+} // namespace SectionTracking
+
+using SectionTracking::TestCaseTracker;
+
+} // namespace Catch
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+ // Report the error condition then exit the process
+ inline void fatal( std::string const& message, int exitCode ) {
+ IContext& context = Catch::getCurrentContext();
+ IResultCapture* resultCapture = context.getResultCapture();
+ resultCapture->handleFatalErrorCondition( message );
+
+ if( Catch::alwaysTrue() ) // avoids "no return" warnings
+ exit( exitCode );
+ }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+
+} // namespace Catch
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#include <signal.h>
+
+namespace Catch {
+
+ struct SignalDefs { int id; const char* name; };
+ extern SignalDefs signalDefs[];
+ SignalDefs signalDefs[] = {
+ { SIGINT, "SIGINT - Terminal interrupt signal" },
+ { SIGILL, "SIGILL - Illegal instruction signal" },
+ { SIGFPE, "SIGFPE - Floating point error signal" },
+ { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+ { SIGTERM, "SIGTERM - Termination request signal" },
+ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+ };
+
+ struct FatalConditionHandler {
+
+ static void handleSignal( int sig ) {
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+ if( sig == signalDefs[i].id )
+ fatal( signalDefs[i].name, -sig );
+ fatal( "<unknown signal>", -sig );
+ }
+
+ FatalConditionHandler() : m_isSet( true ) {
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+ signal( signalDefs[i].id, handleSignal );
+ }
+ ~FatalConditionHandler() {
+ reset();
+ }
+ void reset() {
+ if( m_isSet ) {
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+ signal( signalDefs[i].id, SIG_DFL );
+ m_isSet = false;
+ }
+ }
+
+ bool m_isSet;
+ };
+
+} // namespace Catch
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+ class StreamRedirect {
+
+ public:
+ StreamRedirect( std::ostream& stream, std::string& targetString )
+ : m_stream( stream ),
+ m_prevBuf( stream.rdbuf() ),
+ m_targetString( targetString )
+ {
+ stream.rdbuf( m_oss.rdbuf() );
+ }
+
+ ~StreamRedirect() {
+ m_targetString += m_oss.str();
+ m_stream.rdbuf( m_prevBuf );
+ }
+
+ private:
+ std::ostream& m_stream;
+ std::streambuf* m_prevBuf;
+ std::ostringstream m_oss;
+ std::string& m_targetString;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ RunContext( RunContext const& );
+ void operator =( RunContext const& );
+
+ public:
+
+ explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
+ : m_runInfo( config->name() ),
+ m_context( getCurrentMutableContext() ),
+ m_activeTestCase( NULL ),
+ m_config( config ),
+ m_reporter( reporter ),
+ m_prevRunner( m_context.getRunner() ),
+ m_prevResultCapture( m_context.getResultCapture() ),
+ m_prevConfig( m_context.getConfig() )
+ {
+ m_context.setRunner( this );
+ m_context.setConfig( m_config );
+ m_context.setResultCapture( this );
+ m_reporter->testRunStarting( m_runInfo );
+ }
+
+ virtual ~RunContext() {
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+ m_context.setRunner( m_prevRunner );
+ m_context.setConfig( NULL );
+ m_context.setResultCapture( m_prevResultCapture );
+ m_context.setConfig( m_prevConfig );
+ }
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+ }
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+ }
+
+ Totals runTest( TestCase const& testCase ) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting( testInfo );
+
+ m_activeTestCase = &testCase;
+ m_testCaseTracker = TestCaseTracker( testInfo.name );
+
+ do {
+ do {
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isCompleted() && !aborting() );
+ }
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = NULL;
+ m_testCaseTracker.reset();
+
+ return deltaTotals;
+ }
+
+ Ptr<IConfig const> config() const {
+ return m_config;
+ }
+
+ private: // IResultCapture
+
+ virtual void assertionEnded( AssertionResult const& result ) {
+ if( result.getResultType() == ResultWas::Ok ) {
+ m_totals.assertions.passed++;
+ }
+ else if( !result.isOk() ) {
+ m_totals.assertions.failed++;
+ }
+
+ if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+ m_messages.clear();
+
+ // Reset working state
+ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastResult = result;
+ }
+
+ virtual bool sectionStarted (
+ SectionInfo const& sectionInfo,
+ Counts& assertions
+ )
+ {
+ std::ostringstream oss;
+ oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
+
+ if( !m_testCaseTracker->enterSection( oss.str() ) )
+ return false;
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 ||
+ !m_config->warnAboutMissingAssertions() ||
+ m_testCaseTracker->currentSectionHasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
+ if( std::uncaught_exception() ) {
+ m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
+ return;
+ }
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ m_testCaseTracker->leaveSection();
+
+ m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void pushScopedMessage( MessageInfo const& message ) {
+ m_messages.push_back( message );
+ }
+
+ virtual void popScopedMessage( MessageInfo const& message ) {
+ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+ }
+
+ virtual std::string getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : "";
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ virtual void handleFatalErrorCondition( std::string const& message ) {
+ ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
+ resultBuilder.setResultType( ResultWas::FatalErrorCondition );
+ resultBuilder << message;
+ resultBuilder.captureExpression();
+
+ handleUnfinishedSections();
+
+ // Recreate section for test case (as we will lose the one that was in scope)
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+ Counts assertions;
+ assertions.failed = 1;
+ SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+ m_reporter->sectionEnded( testCaseSectionStats );
+
+ TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+ Totals deltaTotals;
+ deltaTotals.testCases.failed = 1;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ "",
+ "",
+ false ) );
+ m_totals.testCases.failed++;
+ testGroupEnded( "", m_totals, 1, 1 );
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+ }
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const {
+ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+ }
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+ m_reporter->sectionStarting( testCaseSection );
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+ TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+ Timer timer;
+ timer.start();
+ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+ StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+ StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+ invokeActiveTestCase();
+ }
+ else {
+ invokeActiveTestCase();
+ }
+ duration = timer.getElapsedSeconds();
+ }
+ catch( TestFailureException& ) {
+ // This just means the test was aborted due to failure
+ }
+ catch(...) {
+ makeUnexpectedResultBuilder().useActiveException();
+ }
+ handleUnfinishedSections();
+ m_messages.clear();
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( testCaseInfo.okToFail() ) {
+ std::swap( assertions.failedButOk, assertions.failed );
+ m_totals.assertions.failed -= assertions.failedButOk;
+ m_totals.assertions.failedButOk += assertions.failedButOk;
+ }
+
+ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+ m_reporter->sectionEnded( testCaseSectionStats );
+ }
+
+ void invokeActiveTestCase() {
+ FatalConditionHandler fatalConditionHandler; // Handle signals
+ m_activeTestCase->invoke();
+ fatalConditionHandler.reset();
+ }
+
+ private:
+
+ ResultBuilder makeUnexpectedResultBuilder() const {
+ return ResultBuilder( m_lastAssertionInfo.macroName.c_str(),
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression.c_str(),
+ m_lastAssertionInfo.resultDisposition );
+ }
+
+ void handleUnfinishedSections() {
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+ m_unfinishedSections.clear();
+ }
+
+ struct UnfinishedSections {
+ UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
+ : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo info;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ Option<TestCaseTracker> m_testCaseTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ IRunner* m_prevRunner;
+ IResultCapture* m_prevResultCapture;
+ Ptr<IConfig const> m_prevConfig;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<UnfinishedSections> m_unfinishedSections;
+ };
+
+ IResultCapture& getResultCapture() {
+ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+ return *capture;
+ else
+ throw std::logic_error( "No result capture instance" );
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _buildNumber,
+ char const* const _branchName )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ buildNumber( _buildNumber ),
+ branchName( _branchName )
+ {}
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const buildNumber;
+ char const* const branchName;
+
+ private:
+ void operator=( Version const& );
+ };
+
+ extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ class Runner {
+
+ public:
+ Runner( Ptr<Config> const& config )
+ : m_config( config )
+ {
+ openStream();
+ makeReporter();
+ }
+
+ Totals runTests() {
+
+ RunContext context( m_config.get(), m_reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+
+ TestSpec testSpec = m_config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> testCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+
+ int testsRunForGroup = 0;
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it ) {
+ testsRunForGroup++;
+ if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+
+ if( context.aborting() )
+ break;
+
+ totals += context.runTest( *it );
+ m_testsAlreadyRun.insert( *it );
+ }
+ }
+ std::vector<TestCase> skippedTestCases;
+ getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+
+ for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
+ it != itEnd;
+ ++it )
+ m_reporter->skipTest( *it );
+
+ context.testGroupEnded( "all tests", totals, 1, 1 );
+ return totals;
+ }
+
+ private:
+ void openStream() {
+ // Open output file, if specified
+ if( !m_config->getFilename().empty() ) {
+ m_ofs.open( m_config->getFilename().c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << m_config->getFilename() << "'";
+ throw std::domain_error( oss.str() );
+ }
+ m_config->setStreamBuf( m_ofs.rdbuf() );
+ }
+ }
+ void makeReporter() {
+ std::string reporterName = m_config->getReporterName().empty()
+ ? "console"
+ : m_config->getReporterName();
+
+ m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
+ if( !m_reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ private:
+ Ptr<Config> m_config;
+ std::ofstream m_ofs;
+ Ptr<IStreamingReporter> m_reporter;
+ std::set<TestCase> m_testsAlreadyRun;
+ };
+
+ class Session : NonCopyable {
+ static bool alreadyInstantiated;
+
+ public:
+
+ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+ Session()
+ : m_cli( makeCommandLineParser() ) {
+ if( alreadyInstantiated ) {
+ std::string msg = "Only one instance of Catch::Session can ever be used";
+ Catch::cerr() << msg << std::endl;
+ throw std::logic_error( msg );
+ }
+ alreadyInstantiated = true;
+ }
+ ~Session() {
+ Catch::cleanUp();
+ }
+
+ void showHelp( std::string const& processName ) {
+ Catch::cout() << "\nCatch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " build "
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ Catch::cout() << " (" << libraryVersion.branchName << " branch)";
+ Catch::cout() << "\n";
+
+ m_cli.usage( Catch::cout(), processName );
+ Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+ }
+
+ int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+ if( m_configData.showHelp )
+ showHelp( m_configData.processName );
+ m_config.reset();
+ }
+ catch( std::exception& ex ) {
+ {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr() << "\nError(s) in input:\n"
+ << Text( ex.what(), TextAttributes().setIndent(2) )
+ << "\n\n";
+ }
+ m_cli.usage( Catch::cout(), m_configData.processName );
+ return (std::numeric_limits<int>::max)();
+ }
+ return 0;
+ }
+
+ void useConfigData( ConfigData const& _configData ) {
+ m_configData = _configData;
+ m_config.reset();
+ }
+
+ int run( int argc, char* const argv[] ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+
+ std::srand( m_configData.rngSeed );
+
+ Runner runner( m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runner.runTests().assertions.failed );
+ }
+ catch( std::exception& ex ) {
+ Catch::cerr() << ex.what() << std::endl;
+ return (std::numeric_limits<int>::max)();
+ }
+ }
+
+ Clara::CommandLine<ConfigData> const& cli() const {
+ return m_cli;
+ }
+ std::vector<Clara::Parser::Token> const& unusedTokens() const {
+ return m_unusedTokens;
+ }
+ ConfigData& configData() {
+ return m_configData;
+ }
+ Config& config() {
+ if( !m_config )
+ m_config = new Config( m_configData );
+ return *m_config;
+ }
+
+ private:
+ Clara::CommandLine<ConfigData> m_cli;
+ std::vector<Clara::Parser::Token> m_unusedTokens;
+ ConfigData m_configData;
+ Ptr<Config> m_config;
+ };
+
+ bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+
+namespace Catch {
+
+ class TestRegistry : public ITestCaseRegistry {
+ struct LexSort {
+ bool operator() (TestCase i,TestCase j) const { return (i<j);}
+ };
+ struct RandomNumberGenerator {
+ int operator()( int n ) const { return std::rand() % n; }
+ };
+
+ public:
+ TestRegistry() : m_unnamedCount( 0 ) {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name == "" ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+
+ if( m_functions.find( testCase ) == m_functions.end() ) {
+ m_functions.insert( testCase );
+ m_functionsInOrder.push_back( testCase );
+ if( !testCase.isHidden() )
+ m_nonHiddenFunctions.push_back( testCase );
+ }
+ else {
+ TestCase const& prev = *m_functions.find( testCase );
+ {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr() << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
+ << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functionsInOrder;
+ }
+
+ virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
+ return m_nonHiddenFunctions;
+ }
+
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
+
+ for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
+ itEnd = m_functionsInOrder.end();
+ it != itEnd;
+ ++it ) {
+ bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
+ if( includeTest != negated )
+ matchingTestCases.push_back( *it );
+ }
+ sortTests( config, matchingTestCases );
+ }
+
+ private:
+
+ static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
+
+ switch( config.runOrder() ) {
+ case RunTests::InLexicographicalOrder:
+ std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
+ break;
+ case RunTests::InRandomOrder:
+ {
+ RandomNumberGenerator rng;
+ std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
+ }
+ break;
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
+ }
+ }
+ std::set<TestCase> m_functions;
+ std::vector<TestCase> m_functionsInOrder;
+ std::vector<TestCase> m_nonHiddenFunctions;
+ size_t m_unnamedCount;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+ public:
+
+ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+ virtual void invoke() const {
+ m_fun();
+ }
+
+ private:
+ virtual ~FreeFunctionTestCase();
+
+ TestFunction m_fun;
+ };
+
+ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+ std::string className = classOrQualifiedMethodName;
+ if( startsWith( className, "&" ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ AutoReg::~AutoReg() {}
+
+ void AutoReg::registerTestCase( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ virtual ~ReporterRegistry() {
+ deleteAllValues( m_factories );
+ }
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+
+ FactoryMap const& getFactories() const {
+ return m_factories;
+ }
+
+ private:
+ FactoryMap m_factories;
+ };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry() {
+ deleteAll( m_translators );
+ }
+
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( translator );
+ }
+
+ virtual std::string translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ throw;
+ }
+ @catch (NSException *exception) {
+ return Catch::toString( [exception description] );
+ }
+#else
+ throw;
+#endif
+ }
+ catch( TestFailureException& ) {
+ throw;
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return tryTranslators( m_translators.begin() );
+ }
+ }
+
+ std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
+ if( it == m_translators.end() )
+ return "Unknown exception";
+
+ try {
+ return (*it)->translate();
+ }
+ catch(...) {
+ return tryTranslators( it+1 );
+ }
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+ return m_exceptionTranslatorRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = NULL;
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <ostream>
+
+namespace Catch {
+
+ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+ : m_lineInfo( lineInfo ) {
+ std::ostringstream oss;
+ oss << lineInfo << ": function ";
+ oss << "not implemented";
+ m_what = oss.str();
+ }
+
+ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+ return m_what.c_str();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+ class StreamBufBase : public std::streambuf {
+ public:
+ virtual ~StreamBufBase() CATCH_NOEXCEPT;
+ };
+}
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+ template<typename WriterF, size_t bufferSize=256>
+ class StreamBufImpl : public StreamBufBase {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() CATCH_NOEXCEPT {
+ sync();
+ }
+
+ private:
+ int overflow( int c ) {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ Stream::Stream()
+ : streamBuf( NULL ), isOwned( false )
+ {}
+
+ Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
+ : streamBuf( _streamBuf ), isOwned( _isOwned )
+ {}
+
+ void Stream::release() {
+ if( isOwned ) {
+ delete streamBuf;
+ streamBuf = NULL;
+ isOwned = false;
+ }
+ }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+ std::ostream& cout() {
+ return std::cout;
+ }
+ std::ostream& cerr() {
+ return std::cerr;
+ }
+#endif
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public: // IContext
+ virtual IResultCapture* getResultCapture() {
+ return m_resultCapture;
+ }
+ virtual IRunner* getRunner() {
+ return m_runner;
+ }
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+ return getGeneratorsForCurrentTest()
+ .getGeneratorInfo( fileInfo, totalSize )
+ .getCurrentIndex();
+ }
+ virtual bool advanceGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ return generators && generators->moveNext();
+ }
+
+ virtual Ptr<IConfig const> getConfig() const {
+ return m_config;
+ }
+
+ public: // IMutableContext
+ virtual void setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
+ }
+ virtual void setRunner( IRunner* runner ) {
+ m_runner = runner;
+ }
+ virtual void setConfig( Ptr<IConfig const> const& config ) {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IGeneratorsForTest* findGeneratorsForCurrentTest() {
+ std::string testName = getResultCapture()->getCurrentTestName();
+
+ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+ m_generatorsByTestName.find( testName );
+ return it != m_generatorsByTestName.end()
+ ? it->second
+ : NULL;
+ }
+
+ IGeneratorsForTest& getGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ if( !generators ) {
+ std::string testName = getResultCapture()->getCurrentTestName();
+ generators = createGeneratorsForTest();
+ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+ }
+ return *generators;
+ }
+
+ private:
+ Ptr<IConfig const> m_config;
+ IRunner* m_runner;
+ IResultCapture* m_resultCapture;
+ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+ };
+
+ namespace {
+ Context* currentContext = NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ Stream createStream( std::string const& streamName ) {
+ if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
+ if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
+ if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
+
+ throw std::domain_error( "Unknown stream: " + streamName );
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch {
+ namespace {
+
+ struct IColourImpl {
+ virtual ~IColourImpl() {}
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+
+ struct NoColourImpl : IColourImpl {
+ void use( Colour::Code ) {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+
+ } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+# ifdef CATCH_PLATFORM_WINDOWS
+# define CATCH_CONFIG_COLOUR_WINDOWS
+# else
+# define CATCH_CONFIG_COLOUR_ANSI
+# endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalAttributes = csbiInfo.wAttributes;
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+ }
+ HANDLE stdoutHandle;
+ WORD originalAttributes;
+ };
+
+ IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+ return &s_instance;
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public IColourImpl {
+ public:
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0:34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+ static IColourImpl* instance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+ private:
+ void setColour( const char* _escapeCode ) {
+ Catch::cout() << '\033' << _escapeCode;
+ }
+ };
+
+ IColourImpl* platformColourInstance() {
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+ ? PosixColourImpl::instance()
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+ void Colour::use( Code _colourCode ) {
+ static IColourImpl* impl = isDebuggerActive()
+ ? NoColourImpl::instance()
+ : platformColourInstance();
+ impl->use( _colourCode );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+ struct GeneratorInfo : IGeneratorInfo {
+
+ GeneratorInfo( std::size_t size )
+ : m_size( size ),
+ m_currentIndex( 0 )
+ {}
+
+ bool moveNext() {
+ if( ++m_currentIndex == m_size ) {
+ m_currentIndex = 0;
+ return false;
+ }
+ return true;
+ }
+
+ std::size_t getCurrentIndex() const {
+ return m_currentIndex;
+ }
+
+ std::size_t m_size;
+ std::size_t m_currentIndex;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class GeneratorsForTest : public IGeneratorsForTest {
+
+ public:
+ ~GeneratorsForTest() {
+ deleteAll( m_generatorsInOrder );
+ }
+
+ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+ if( it == m_generatorsByName.end() ) {
+ IGeneratorInfo* info = new GeneratorInfo( size );
+ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+ m_generatorsInOrder.push_back( info );
+ return *info;
+ }
+ return *it->second;
+ }
+
+ bool moveNext() {
+ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+ for(; it != itEnd; ++it ) {
+ if( (*it)->moveNext() )
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+ std::vector<IGeneratorInfo*> m_generatorsInOrder;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest()
+ {
+ return new GeneratorsForTest();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+ AssertionInfo::AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition )
+ {}
+
+ AssertionResult::AssertionResult() {}
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ AssertionResult::~AssertionResult() {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return "!" + m_info.capturedExpression;
+ else
+ return m_info.capturedExpression;
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName.empty() )
+ return m_info.capturedExpression;
+ else
+ return m_info.macroName + "( " + m_info.capturedExpression + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructedExpression;
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ std::string AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+namespace Catch {
+
+ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( startsWith( tag, "." ) ||
+ tag == "hide" ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr()
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n";
+ }
+ {
+ Colour colourGuard( Colour::FileName );
+ Catch::cerr() << _lineInfo << std::endl;
+ }
+ exit(1);
+ }
+ }
+
+ TestCase makeTestCase( ITestCase* _testCase,
+ std::string const& _className,
+ std::string const& _name,
+ std::string const& _descOrTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+ // Parse out tags
+ std::set<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+ char c = _descOrTags[i];
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+ if( prop == TestCaseInfo::IsHidden )
+ isHidden = true;
+ else if( prop == TestCaseInfo::None )
+ enforceNotReservedTag( tag, _lineInfo );
+
+ tags.insert( tag );
+ tag.clear();
+ inTag = false;
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ tags.insert( "hide" );
+ tags.insert( "." );
+ }
+
+ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, info );
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ tags( _tags ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ std::ostringstream oss;
+ for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
+ oss << "[" << *it << "]";
+ std::string lcaseTag = toLower( *it );
+ properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
+ lcaseTags.insert( lcaseTag );
+ }
+ tagsAsString = oss.str();
+ }
+
+ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+ : name( other.name ),
+ className( other.className ),
+ description( other.description ),
+ tags( other.tags ),
+ lcaseTags( other.lcaseTags ),
+ tagsAsString( other.tagsAsString ),
+ lineInfo( other.lineInfo ),
+ properties( other.properties )
+ {}
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+ TestCase::TestCase( TestCase const& other )
+ : TestCaseInfo( other ),
+ test( other.test )
+ {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::swap( TestCase& other ) {
+ test.swap( other.test );
+ name.swap( other.name );
+ className.swap( other.className );
+ description.swap( other.description );
+ tags.swap( other.tags );
+ lcaseTags.swap( other.lcaseTags );
+ tagsAsString.swap( other.tagsAsString );
+ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+ std::swap( lineInfo, other.lineInfo );
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+ TestCase& TestCase::operator = ( TestCase const& other ) {
+ TestCase temp( other );
+ swap( temp );
+ return *this;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+ // These numbers are maintained by a script
+ Version libraryVersion( 1, 1, 3, "master" );
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info )
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+ ScopedMessage::ScopedMessage( ScopedMessage const& other )
+ : m_info( other.m_info )
+ {}
+
+ ScopedMessage::~ScopedMessage() {
+ getResultCapture().popScopedMessage( m_info );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+ // Deprecated
+ struct IReporter : IShared {
+ virtual ~IReporter();
+
+ virtual bool shouldRedirectStdout() const = 0;
+
+ virtual void StartTesting() = 0;
+ virtual void EndTesting( Totals const& totals ) = 0;
+ virtual void StartGroup( std::string const& groupName ) = 0;
+ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+ virtual void Aborted() = 0;
+ virtual void Result( AssertionResult const& result ) = 0;
+ };
+
+ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+ {
+ public:
+ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+ virtual ~LegacyReporterAdapter();
+
+ virtual ReporterPreferences getPreferences() const;
+ virtual void noMatchingTestCases( std::string const& );
+ virtual void testRunStarting( TestRunInfo const& );
+ virtual void testGroupStarting( GroupInfo const& groupInfo );
+ virtual void testCaseStarting( TestCaseInfo const& testInfo );
+ virtual void sectionStarting( SectionInfo const& sectionInfo );
+ virtual void assertionStarting( AssertionInfo const& );
+ virtual bool assertionEnded( AssertionStats const& assertionStats );
+ virtual void sectionEnded( SectionStats const& sectionStats );
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+ virtual void testRunEnded( TestRunStats const& testRunStats );
+ virtual void skipTest( TestCaseInfo const& );
+
+ private:
+ Ptr<IReporter> m_legacyReporter;
+ };
+}
+
+namespace Catch
+{
+ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+ : m_legacyReporter( legacyReporter )
+ {}
+ LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+ return prefs;
+ }
+
+ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+ m_legacyReporter->StartTesting();
+ }
+ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+ m_legacyReporter->StartGroup( groupInfo.name );
+ }
+ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ m_legacyReporter->StartTestCase( testInfo );
+ }
+ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+ }
+ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+ // Not on legacy interface
+ }
+
+ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+ rb << it->message;
+ rb.setResultType( ResultWas::Info );
+ AssertionResult result = rb.build();
+ m_legacyReporter->Result( result );
+ }
+ }
+ }
+ m_legacyReporter->Result( assertionStats.assertionResult );
+ return true;
+ }
+ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+ if( sectionStats.missingAssertions )
+ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+ }
+ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ m_legacyReporter->EndTestCase
+ ( testCaseStats.testInfo,
+ testCaseStats.totals,
+ testCaseStats.stdOut,
+ testCaseStats.stdErr );
+ }
+ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ if( testGroupStats.aborting )
+ m_legacyReporter->Aborted();
+ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+ }
+ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+ m_legacyReporter->EndTesting( testRunStats.totals );
+ }
+ void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+ }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ uint64_t getCurrentTicks() {
+ static uint64_t hz=0, hzo=0;
+ if (!hz) {
+ QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+ }
+ uint64_t t;
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+ return ((t-hzo)*1000000)/hz;
+ }
+#else
+ uint64_t getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,NULL);
+ return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
+ }
+#endif
+ }
+
+ void Timer::start() {
+ m_ticks = getCurrentTicks();
+ }
+ unsigned int Timer::getElapsedMicroseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ unsigned int Timer::getElapsedMilliseconds() const {
+ return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+ }
+ double Timer::getElapsedSeconds() const {
+ return getElapsedMicroseconds()/1000000.0;
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), ::tolower );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
+ }
+
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+ while( i != std::string::npos ) {
+ replaced = true;
+ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+ if( i < str.size()-withThis.size() )
+ i = str.find( replaceThis, i+withThis.size() );
+ else
+ i = std::string::npos;
+ }
+ return replaced;
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << " " << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << "s";
+ return os;
+ }
+
+ SourceLineInfo::SourceLineInfo() : line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
+ : file( other.file ),
+ line( other.line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file.empty();
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && file == other.file;
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+ return line < other.line || ( line == other.line && file < other.file );
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << "(" << info.line << ")";
+#else
+ os << info.file << ":" << info.line;
+#endif
+ return os;
+ }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+ std::ostringstream oss;
+ oss << locationInfo << ": Internal Catch error: '" << message << "'";
+ if( alwaysTrue() )
+ throw std::logic_error( oss.str() );
+ }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description )
+ : name( _name ),
+ description( _description ),
+ lineInfo( _lineInfo )
+ {}
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+ Section::~Section() {
+ if( m_sectionIncluded )
+ getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ }
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#include <iostream>
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+
+ namespace Catch{
+
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ } // namespace Catch
+
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ inline bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+ extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+#else
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ Catch::cout() << text;
+ }
+ }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+ std::string unprintableString = "{?}";
+
+ namespace {
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ union _{
+ int asInt;
+ char asChar[sizeof (int)];
+ } u;
+
+ u.asInt = 1;
+ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size )
+ {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str();
+ }
+}
+
+std::string toString( std::string const& value ) {
+ std::string s = value;
+ if( getCurrentContext().getConfig()->showInvisibles() ) {
+ for(size_t i = 0; i < s.size(); ++i ) {
+ std::string subs;
+ switch( s[i] ) {
+ case '\n': subs = "\\n"; break;
+ case '\t': subs = "\\t"; break;
+ default: break;
+ }
+ if( !subs.empty() ) {
+ s = s.substr( 0, i ) + subs + s.substr( i+1 );
+ ++i;
+ }
+ }
+ }
+ return "\"" + s + "\"";
+}
+std::string toString( std::wstring const& value ) {
+
+ std::string s;
+ s.reserve( value.size() );
+ for(size_t i = 0; i < value.size(); ++i )
+ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+ return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+ return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value >= 255 )
+ oss << " (0x" << std::hex << value << ")";
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value >= 255 )
+ oss << " (0x" << std::hex << value << ")";
+ return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+ return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ std::ostringstream oss;
+ oss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = oss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+std::string toString( const double value ) {
+ return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+ return fpToString( value, 5 ) + "f";
+}
+
+std::string toString( bool value ) {
+ return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+ return value < ' '
+ ? toString( static_cast<unsigned int>( value ) )
+ : Detail::makeString( value );
+}
+
+std::string toString( signed char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+ return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSObject* const& nsObject ) {
+ return toString( [nsObject description] );
+ }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+ ResultBuilder::ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition )
+ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false )
+ {}
+
+ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+ m_data.resultType = result;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setResultType( bool result ) {
+ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
+ m_exprComponents.lhs = lhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
+ m_exprComponents.rhs = rhs;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
+ m_exprComponents.op = op;
+ return *this;
+ }
+
+ void ResultBuilder::endExpression() {
+ m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
+ captureExpression();
+ }
+
+ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+ m_assertionInfo.resultDisposition = resultDisposition;
+ m_stream.oss << Catch::translateActiveException();
+ captureResult( ResultWas::ThrewException );
+ }
+
+ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+ setResultType( resultType );
+ captureExpression();
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ getResultCapture().assertionEnded( result );
+
+ if( !result.isOk() ) {
+ if( getCurrentContext().getConfig()->shouldDebugBreak() )
+ m_shouldDebugBreak = true;
+ if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+ m_shouldThrow = true;
+ }
+ }
+ void ResultBuilder::react() {
+ if( m_shouldThrow )
+ throw Catch::TestFailureException();
+ }
+
+ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+ AssertionResult ResultBuilder::build() const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+
+ AssertionResultData data = m_data;
+
+ // Flip bool results if testFalse is set
+ if( m_exprComponents.testFalse ) {
+ if( data.resultType == ResultWas::Ok )
+ data.resultType = ResultWas::ExpressionFailed;
+ else if( data.resultType == ResultWas::ExpressionFailed )
+ data.resultType = ResultWas::Ok;
+ }
+
+ data.message = m_stream.oss.str();
+ data.reconstructedExpression = reconstructExpression();
+ if( m_exprComponents.testFalse ) {
+ if( m_exprComponents.op == "" )
+ data.reconstructedExpression = "!" + data.reconstructedExpression;
+ else
+ data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
+ }
+ return AssertionResult( m_assertionInfo, data );
+ }
+ std::string ResultBuilder::reconstructExpression() const {
+ if( m_exprComponents.op == "" )
+ return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+ else if( m_exprComponents.op == "matches" )
+ return m_exprComponents.lhs + " " + m_exprComponents.rhs;
+ else if( m_exprComponents.op != "!" ) {
+ if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
+ m_exprComponents.lhs.find("\n") == std::string::npos &&
+ m_exprComponents.rhs.find("\n") == std::string::npos )
+ return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
+ else
+ return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
+ }
+ else
+ return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ virtual ~TagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+ void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ static TagAliasRegistry& get();
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+#include <map>
+#include <iostream>
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return it->second;
+ else
+ return Option<TagAlias>();
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+ it != itEnd;
+ ++it ) {
+ std::size_t pos = expandedTestSpec.find( it->first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ it->second.tag +
+ expandedTestSpec.substr( pos + it->first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+ << "\tRedefined at " << lineInfo;
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ TagAliasRegistry& TagAliasRegistry::get() {
+ static TagAliasRegistry instance;
+ return instance;
+
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+ ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ try {
+ TagAliasRegistry::get().add( alias, tag, lineInfo );
+ }
+ catch( std::exception& ex ) {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr() << ex.what() << std::endl;
+ exit(1);
+ }
+ }
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+
+namespace Catch {
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+
+ virtual ~StreamingReporterBase();
+
+ virtual void noMatchingTestCases( std::string const& ) {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+ currentTestCaseInfo.reset();
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ virtual void skipTest( TestCaseInfo const& ) {
+ // Don't do anything with this by default.
+ // It can optionally be overridden in the derived class.
+ }
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ };
+
+ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+ template<typename T, typename ChildNodeT>
+ struct Node : SharedImpl<> {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode : SharedImpl<> {
+ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+ virtual ~SectionNode();
+
+ bool operator == ( SectionNode const& other ) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == ( Ptr<SectionNode> const& other ) const {
+ return operator==( *other );
+ }
+
+ SectionStats stats;
+ typedef std::vector<Ptr<SectionNode> > ChildSections;
+ typedef std::vector<AssertionStats> Assertions;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() ( Ptr<SectionNode> const& node ) const {
+ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+ }
+ private:
+ void operator=( BySectionInfo const& );
+ SectionInfo const& m_other;
+ };
+
+ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {}
+ ~CumulativeReporterBase();
+
+ virtual void testRunStarting( TestRunInfo const& ) {}
+ virtual void testGroupStarting( GroupInfo const& ) {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ Ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = new SectionNode( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ SectionNode::ChildSections::const_iterator it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = new SectionNode( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = node;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+ assert( m_sectionStack.size() == 0 );
+ node->children.push_back( m_rootSection );
+ m_testCases.push_back( node );
+ m_rootSection.reset();
+
+ assert( m_deepestSection );
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) {
+ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+ node->children.swap( m_testGroups );
+ m_testRuns.push_back( node );
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ virtual void skipTest( TestCaseInfo const& ) {}
+
+ Ptr<IConfig> m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+ std::vector<Ptr<TestCaseNode> > m_testCases;
+ std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+ std::vector<Ptr<TestRunNode> > m_testRuns;
+
+ Ptr<SectionNode> m_rootSection;
+ Ptr<SectionNode> m_deepestSection;
+ std::vector<Ptr<SectionNode> > m_sectionStack;
+
+ };
+
+ template<char C>
+ char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+ template<typename T>
+ class LegacyReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new LegacyReporterAdapter( new T( config ) );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ LegacyReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+
+ // *** Please Note ***:
+ // - If you end up here looking at a compiler error because it's trying to register
+ // your custom reporter class be aware that the native reporter interface has changed
+ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+ // However please consider updating to the new interface as the old one is now
+ // deprecated and will probably be removed quite soon!
+ // Please contact me via github if you have any questions at all about this.
+ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+ // no idea who is actually using custom reporters at all (possibly no-one!).
+ // The new interface is designed to minimise exposure to interface changes in the future.
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = NULL;
+ }
+
+ ~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ ScopedElement& writeText( std::string const& text, bool indent = true ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer;
+ };
+
+ XmlWriter()
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &Catch::cout() )
+ {}
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( &os )
+ {}
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ stream() << m_indent << "<" << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ ScopedElement scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ stream() << "/>\n";
+ m_tagIsOpen = false;
+ }
+ else {
+ stream() << m_indent << "</" << m_tags.back() << ">\n";
+ }
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() ) {
+ stream() << " " << name << "=\"";
+ writeEncodedText( attribute );
+ stream() << "\"";
+ }
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ if( !name.empty() )
+ stream() << " " << name << "=\"" << attribute << "\"";
+ return *this;
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ stream() << m_indent;
+ writeEncodedText( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ stream() << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ stream() << "\n";
+ return *this;
+ }
+
+ void setStream( std::ostream& os ) {
+ m_os = &os;
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ std::ostream& stream() {
+ return *m_os;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ stream() << ">\n";
+ m_tagIsOpen = false;
+ }
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ stream() << "\n";
+ m_needsNewline = false;
+ }
+ }
+
+ void writeEncodedText( std::string const& text ) {
+ static const char* charsToEncode = "<&\"";
+ std::string mtext = text;
+ std::string::size_type pos = mtext.find_first_of( charsToEncode );
+ while( pos != std::string::npos ) {
+ stream() << mtext.substr( 0, pos );
+
+ switch( mtext[pos] ) {
+ case '<':
+ stream() << "&lt;";
+ break;
+ case '&':
+ stream() << "&amp;";
+ break;
+ case '\"':
+ stream() << "&quot;";
+ break;
+ }
+ mtext = mtext.substr( pos+1 );
+ pos = mtext.find_first_of( charsToEncode );
+ }
+ stream() << mtext;
+ }
+
+ bool m_tagIsOpen;
+ bool m_needsNewline;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream* m_os;
+ };
+
+}
+namespace Catch {
+ class XmlReporter : public StreamingReporterBase {
+ public:
+ XmlReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_sectionDepth( 0 )
+ {}
+
+ virtual ~XmlReporter();
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+
+ public: // StreamingReporterBase
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = true;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& s ) {
+ StreamingReporterBase::noMatchingTestCases( s );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testInfo ) {
+ StreamingReporterBase::testRunStarting( testInfo );
+ m_xml.setStream( stream );
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+ StreamingReporterBase::testGroupStarting( groupInfo );
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupInfo.name );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+ StreamingReporterBase::testCaseStarting(testInfo);
+ m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ m_testCaseTimer.start();
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+ StreamingReporterBase::sectionStarting( sectionInfo );
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionInfo.name ) )
+ .writeAttribute( "description", sectionInfo.description );
+ }
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) { }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ const AssertionResult& assertionResult = assertionStats.assertionResult;
+
+ // Print any info messages in <Info> tags.
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ m_xml.scopedElement( "Info" )
+ .writeText( it->message );
+ } else if ( it->type == ResultWas::Warning ) {
+ m_xml.scopedElement( "Warning" )
+ .writeText( it->message );
+ }
+ }
+ }
+
+ // Drop out if result was successful but we're not printing them.
+ if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
+ return true;
+
+ // Print the expression if there is one.
+ if( assertionResult.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", assertionResult.succeeded() )
+ .writeAttribute( "type", assertionResult.getTestMacroName() )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( assertionResult.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( assertionResult.getExpandedExpression() );
+ }
+
+ // And... Print a result applicable to each result type.
+ switch( assertionResult.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.scopedElement( "Exception" )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::FatalErrorCondition:
+ m_xml.scopedElement( "Fatal Error Condition" )
+ .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+ .writeAttribute( "line", assertionResult.getSourceInfo().line )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ case ResultWas::Warning:
+ // Warning will already have been written
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.scopedElement( "Failure" )
+ .writeText( assertionResult.getMessage() );
+ break;
+ default:
+ break;
+ }
+
+ if( assertionResult.hasExpression() )
+ m_xml.endElement();
+
+ return true;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) {
+ StreamingReporterBase::sectionEnded( sectionStats );
+ if( --m_sectionDepth > 0 ) {
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+ e.writeAttribute( "successes", sectionStats.assertions.passed );
+ e.writeAttribute( "failures", sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+ m_xml.endElement();
+ }
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ StreamingReporterBase::testCaseEnded( testCaseStats );
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+ m_xml.endElement();
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ StreamingReporterBase::testGroupEnded( testGroupStats );
+ // TODO: Check testGroupStats.aborting and act accordingly.
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) {
+ StreamingReporterBase::testRunEnded( testRunStats );
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ private:
+ Timer m_testCaseTimer;
+ XmlWriter m_xml;
+ int m_sectionDepth;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() )
+ {}
+
+ ~JunitReporter();
+
+ static std::string getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = true;
+ return prefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() {
+ xml.endElement();
+ }
+
+ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "timestamp", "tbd" ); // !TBD
+
+ // Write test cases
+ for( TestGroupNode::ChildNodes::const_iterator
+ it = groupNode.children.begin(), itEnd = groupNode.children.end();
+ it != itEnd;
+ ++it )
+ writeTestCase( **it );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+ }
+
+ void writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ if( rootSection.childSections.empty() )
+ className = "global";
+ }
+ writeSection( className, "", rootSection );
+ }
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode ) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + "/" + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ }
+ for( SectionNode::ChildSections::const_iterator
+ it = sectionNode.childSections.begin(),
+ itEnd = sectionNode.childSections.end();
+ it != itEnd;
+ ++it )
+ if( className.empty() )
+ writeSection( name, "", **it );
+ else
+ writeSection( className, name, **it );
+ }
+
+ void writeAssertions( SectionNode const& sectionNode ) {
+ for( SectionNode::Assertions::const_iterator
+ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+ it != itEnd;
+ ++it )
+ writeAssertion( *it );
+ }
+ void writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ case ResultWas::FatalErrorCondition:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ elementName = "failure";
+ break;
+ case ResultWas::ExpressionFailed:
+ elementName = "failure";
+ break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ std::ostringstream oss;
+ if( !result.getMessage().empty() )
+ oss << result.getMessage() << "\n";
+ for( std::vector<MessageInfo>::const_iterator
+ it = stats.infoMessages.begin(),
+ itEnd = stats.infoMessages.end();
+ it != itEnd;
+ ++it )
+ if( it->type == ResultWas::Info )
+ oss << it->message << "\n";
+
+ oss << "at " << result.getSourceInfo();
+ xml.writeText( oss.str(), false );
+ }
+ }
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::ostringstream stdOutForSuite;
+ std::ostringstream stdErrForSuite;
+ unsigned int unexpectedExceptions;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter();
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) {
+ if( _sectionStats.missingAssertions ) {
+ lazyPrint();
+ Colour colour( Colour::ResultError );
+ if( m_sectionStack.size() > 1 )
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ if( m_headerPrinted ) {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ m_headerPrinted = false;
+ }
+ else {
+ if( m_config->showDurations() == ShowDurations::Always )
+ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+ if( currentGroupInfo.used ) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals( _testGroupStats.totals );
+ stream << "\n" << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded( _testGroupStats );
+ }
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotalsDivider( _testRunStats.totals );
+ printTotals( _testRunStats.totals );
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream ),
+ stats( _stats ),
+ result( _stats.assertionResult ),
+ colour( Colour::None ),
+ message( result.getMessage() ),
+ messages( _stats.infoMessages ),
+ printInfoMessages( _printInfoMessages )
+ {
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() ) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ }
+ else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with message";
+ break;
+ case ResultWas::FatalErrorCondition:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to a fatal error condition";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "explicitly with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if( stats.totals.assertions.total() > 0 ) {
+ if( result.isOk() )
+ stream << "\n";
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ }
+ else {
+ stream << "\n";
+ }
+ printMessage();
+ }
+
+ private:
+ void printResultType() const {
+ if( !passOrFail.empty() ) {
+ Colour colourGuard( colour );
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ Colour colourGuard( Colour::OriginalExpression );
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << "\n";
+ }
+ }
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ stream << "with expansion:\n";
+ Colour colourGuard( Colour::ReconstructedExpression );
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printMessage() const {
+ if( !messageLabel.empty() )
+ stream << messageLabel << ":" << "\n";
+ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+ it != itEnd;
+ ++it ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || it->type != ResultWas::Info )
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+ };
+
+ void lazyPrint() {
+
+ if( !currentTestRunInfo.used )
+ lazyPrintRunInfo();
+ if( !currentGroupInfo.used )
+ lazyPrintGroupInfo();
+
+ if( !m_headerPrinted ) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+ }
+ void lazyPrintRunInfo() {
+ stream << "\n" << getLineOfChars<'~'>() << "\n";
+ Colour colour( Colour::SecondaryText );
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion.majorVersion << "."
+ << libraryVersion.minorVersion << " b"
+ << libraryVersion.buildNumber;
+ if( libraryVersion.branchName != std::string( "master" ) )
+ stream << " (" << libraryVersion.branchName << ")";
+ stream << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ if( m_config->rngSeed() != 0 )
+ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+ currentTestRunInfo.used = true;
+ }
+ void lazyPrintGroupInfo() {
+ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+ printClosedHeader( "Group: " + currentGroupInfo->name );
+ currentGroupInfo.used = true;
+ }
+ }
+ void printTestCaseAndSectionHeader() {
+ assert( !m_sectionStack.empty() );
+ printOpenHeader( currentTestCaseInfo->name );
+
+ if( m_sectionStack.size() > 1 ) {
+ Colour colourGuard( Colour::Headers );
+
+ std::vector<SectionInfo>::const_iterator
+ it = m_sectionStack.begin()+1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for( ; it != itEnd; ++it )
+ printHeaderString( it->name, 2 );
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+ if( !lineInfo.empty() ){
+ stream << getLineOfChars<'-'>() << "\n";
+ Colour colourGuard( Colour::FileName );
+ stream << lineInfo << "\n";
+ }
+ stream << getLineOfChars<'.'>() << "\n" << std::endl;
+ }
+
+ void printClosedHeader( std::string const& _name ) {
+ printOpenHeader( _name );
+ stream << getLineOfChars<'.'>() << "\n";
+ }
+ void printOpenHeader( std::string const& _name ) {
+ stream << getLineOfChars<'-'>() << "\n";
+ {
+ Colour colourGuard( Colour::Headers );
+ printHeaderString( _name );
+ }
+ }
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+ std::size_t i = _string.find( ": " );
+ if( i != std::string::npos )
+ i+=2;
+ else
+ i = 0;
+ stream << Text( _string, TextAttributes()
+ .setIndent( indent+i)
+ .setInitialIndent( indent ) ) << "\n";
+ }
+
+ struct SummaryColumn {
+
+ SummaryColumn( std::string const& _label, Colour::Code _colour )
+ : label( _label ),
+ colour( _colour )
+ {}
+ SummaryColumn addRow( std::size_t count ) {
+ std::ostringstream oss;
+ oss << count;
+ std::string row = oss.str();
+ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+ while( it->size() < row.size() )
+ *it = " " + *it;
+ while( it->size() > row.size() )
+ row = " " + row;
+ }
+ rows.push_back( row );
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+ };
+
+ void printTotals( Totals const& totals ) {
+ if( totals.testCases.total() == 0 ) {
+ stream << Colour( Colour::Warning ) << "No tests ran\n";
+ }
+ else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+ stream << " ("
+ << pluralise( totals.assertions.passed, "assertion" ) << " in "
+ << pluralise( totals.testCases.passed, "test case" ) << ")"
+ << "\n";
+ }
+ else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back( SummaryColumn( "", Colour::None )
+ .addRow( totals.testCases.total() )
+ .addRow( totals.assertions.total() ) );
+ columns.push_back( SummaryColumn( "passed", Colour::Success )
+ .addRow( totals.testCases.passed )
+ .addRow( totals.assertions.passed ) );
+ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+ .addRow( totals.testCases.failed )
+ .addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+ .addRow( totals.testCases.failedButOk )
+ .addRow( totals.assertions.failedButOk ) );
+
+ printSummaryRow( "test cases", columns, 0 );
+ printSummaryRow( "assertions", columns, 1 );
+ }
+ }
+ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+ std::string value = it->rows[row];
+ if( it->label.empty() ) {
+ stream << label << ": ";
+ if( value != "0" )
+ stream << value;
+ else
+ stream << Colour( Colour::Warning ) << "- none -";
+ }
+ else if( value != "0" ) {
+ stream << Colour( Colour::LightGrey ) << " | ";
+ stream << Colour( it->colour )
+ << value << " " << it->label;
+ }
+ }
+ stream << "\n";
+ }
+
+ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+ }
+ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+ if( i > j && i > k )
+ return i;
+ else if( j > k )
+ return j;
+ else
+ return k;
+ }
+
+ void printTotalsDivider( Totals const& totals ) {
+ if( totals.testCases.total() > 0 ) {
+ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )++;
+ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+ if( totals.testCases.allPassed() )
+ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+ else
+ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+ }
+ else {
+ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+ }
+ stream << "\n";
+ }
+ void printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << "\n";
+ }
+
+ private:
+ bool m_headerPrinted;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase {
+
+ CompactReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual ~CompactReporter();
+
+ static std::string getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( _testRunStats.totals );
+ stream << "\n" << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream )
+ , stats( _stats )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( _printInfoMessages )
+ {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( Colour::ResultSuccess, passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() )
+ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+ else
+ printResultType( Colour::Error, failedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::FatalErrorCondition:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "fatal error condition with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( Colour::None, "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( Colour::None, "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "explicitly" );
+ printRemainingMessages( Colour::None );
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType( Colour::Error, "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ // Colour::LightGrey
+
+ static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+ static const char* failedString() { return "FAILED"; }
+ static const char* passedString() { return "PASSED"; }
+#else
+ static const char* failedString() { return "failed"; }
+ static const char* passedString() { return "passed"; }
+#endif
+
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ":";
+ }
+
+ void printResultType( Colour::Code colour, std::string passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << " " << passOrFail;
+ }
+ stream << ":";
+ }
+ }
+
+ void printIssue( std::string issue ) const {
+ stream << " " << issue;
+ }
+
+ void printExpressionWas() {
+ if( result.hasExpression() ) {
+ stream << ";";
+ {
+ Colour colour( dimColour() );
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ stream << " " << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ {
+ Colour colour( dimColour() );
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if ( itMessage != messages.end() ) {
+ stream << " '" << itMessage->message << "'";
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages( Colour::Code colour = dimColour() ) {
+ if ( itMessage == messages.end() )
+ return;
+
+ // using messages.end() directly yields compilation error:
+ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+ {
+ Colour colourGuard( colour );
+ stream << " with " << pluralise( N, "message" ) << ":";
+ }
+
+ for(; itMessage != itEnd; ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+ stream << " '" << itMessage->message << "'";
+ if ( ++itMessage != itEnd ) {
+ Colour colourGuard( dimColour() );
+ stream << " and";
+ }
+ }
+ }
+ }
+
+ private:
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+ };
+
+ // Colour, message variants:
+ // - white: No tests ran.
+ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+ // - white: Passed [both/all] N test cases (no assertions).
+ // - red: Failed N tests cases, failed M assertions.
+ // - green: Passed [both/all] N tests cases with M assertions.
+
+ std::string bothOrAll( std::size_t count ) const {
+ return count == 1 ? "" : count == 2 ? "both " : "all " ;
+ }
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "No tests ran.";
+ }
+ else if( totals.testCases.failed == totals.testCases.total() ) {
+ Colour colour( Colour::ResultError );
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll( totals.assertions.failed ) : "";
+ stream <<
+ "Failed " << bothOrAll( totals.testCases.failed )
+ << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else if( totals.assertions.total() == 0 ) {
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.total() )
+ << pluralise( totals.testCases.total(), "test case" )
+ << " (no assertions).";
+ }
+ else if( totals.assertions.failed ) {
+ Colour colour( Colour::ResultError );
+ stream <<
+ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
+ }
+ else {
+ Colour colour( Colour::ResultSuccess );
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.passed )
+ << pluralise( totals.testCases.passed, "test case" ) <<
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
+ }
+ }
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+ IContext::~IContext() {}
+ IResultCapture::~IResultCapture() {}
+ ITestCase::~ITestCase() {}
+ ITestCaseRegistry::~ITestCaseRegistry() {}
+ IRegistryHub::~IRegistryHub() {}
+ IMutableRegistryHub::~IMutableRegistryHub() {}
+ IExceptionTranslator::~IExceptionTranslator() {}
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+ IReporter::~IReporter() {}
+ IReporterFactory::~IReporterFactory() {}
+ IReporterRegistry::~IReporterRegistry() {}
+ IStreamingReporter::~IStreamingReporter() {}
+ AssertionStats::~AssertionStats() {}
+ SectionStats::~SectionStats() {}
+ TestCaseStats::~TestCaseStats() {}
+ TestGroupStats::~TestGroupStats() {}
+ TestRunStats::~TestRunStats() {}
+ CumulativeReporterBase::SectionNode::~SectionNode() {}
+ CumulativeReporterBase::~CumulativeReporterBase() {}
+
+ StreamingReporterBase::~StreamingReporterBase() {}
+ ConsoleReporter::~ConsoleReporter() {}
+ CompactReporter::~CompactReporter() {}
+ IRunner::~IRunner() {}
+ IMutableContext::~IMutableContext() {}
+ IConfig::~IConfig() {}
+ XmlReporter::~XmlReporter() {}
+ JunitReporter::~JunitReporter() {}
+ TestRegistry::~TestRegistry() {}
+ FreeFunctionTestCase::~FreeFunctionTestCase() {}
+ IGeneratorInfo::~IGeneratorInfo() {}
+ IGeneratorsForTest::~IGeneratorsForTest() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+ Matchers::Impl::StdString::Equals::~Equals() {}
+ Matchers::Impl::StdString::Contains::~Contains() {}
+ Matchers::Impl::StdString::StartsWith::~StartsWith() {}
+ Matchers::Impl::StdString::EndsWith::~EndsWith() {}
+
+ void Config::dummy() {}
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * const argv[]) {
+ return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return result;
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
+#else
+ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+ #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
+#else
+ #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc ) SECTION( " Given: " desc, "" )
+#define WHEN( desc ) SECTION( " When: " desc, "" )
+#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
+#define THEN( desc ) SECTION( " Then: " desc, "" )
+#define AND_THEN( desc ) SECTION( " And: " desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(pop)
+# else
+# pragma clang diagnostic pop
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/2015-spacebot/test/game_state.cpp b/2015-spacebot/test/game_state.cpp
new file mode 100644
index 0000000..a9975f2
--- /dev/null
+++ b/2015-spacebot/test/game_state.cpp
@@ -0,0 +1,90 @@
+#include "catch.hpp"
+#include <sstream>
+
+#include "game_state.h"
+
+SCENARIO("game state is read from istream")
+{
+ GIVEN("a valid map file")
+ {
+ std::stringstream file;
+ file << "###################" << std::endl;
+ file << "# Node Sample Bot #" << std::endl;
+ file << "# Round: 1 #" << std::endl;
+ file << "# Kills: 0 #" << std::endl;
+ file << "# Lives: 2 #" << std::endl;
+ file << "# Missiles: 0/1 #" << std::endl;
+ file << "###################" << std::endl;
+ file << "# #" << std::endl;
+ file << "# VVV #" << std::endl;
+ file << "# --- --- #" << std::endl;
+ file << "# --- --- #" << std::endl;
+ file << "# --- i --- #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# x x x #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# x x x #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# | #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# #" << std::endl;
+ file << "# ! #" << std::endl;
+ file << "# --- --- #" << std::endl;
+ file << "# --- --- #" << std::endl;
+ file << "# --- --- #" << std::endl;
+ file << "# AAA #" << std::endl;
+ file << "# #" << std::endl;
+ file << "###################" << std::endl;
+ file << "# Missiles: 0/1 #" << std::endl;
+ file << "# Lives: 2 #" << std::endl;
+ file << "# Kills: 0 #" << std::endl;
+ file << "# Round: 1 #" << std::endl;
+ file << "# Node Sample Bot #" << std::endl;
+ file << "###################" << std::endl;
+
+ WHEN ("the game state is initilized")
+ {
+ GameState state(std::move(file));
+
+ THEN("the aliens are read correctly")
+ {
+ auto aliens = state.aliens();
+ REQUIRE(aliens.size() == 6);
+ REQUIRE(aliens[0].x() == 10);
+ REQUIRE(aliens[0].y() == 11);
+ REQUIRE(aliens[5].x() == 16);
+ REQUIRE(aliens[5].y() == 13);
+ }
+
+ THEN("the shields are read correctly")
+ {
+ auto shields = state.shields();
+ REQUIRE(shields.size() == 36);
+ }
+
+ THEN("the enemy bullets are read correctly")
+ {
+ auto bullets = state.bullets();
+ REQUIRE(bullets.size() == 2);
+ }
+
+ THEN("the player missiles are read correctly")
+ {
+ auto missiles = state.missiles();
+ REQUIRE(missiles.size() == 1);
+ REQUIRE(missiles[0].x() == 11);
+ REQUIRE(missiles[0].y() == 18);
+ }
+
+ THEN("the spaceships are read correctly")
+ {
+ REQUIRE(state.playerSpaceship()->x() == 9);
+ REQUIRE(state.playerSpaceship()->y() == 22);
+ }
+ }
+ }
+}
diff --git a/2015-spacebot/test/move_string_mapper.cpp b/2015-spacebot/test/move_string_mapper.cpp
new file mode 100644
index 0000000..a7af36a
--- /dev/null
+++ b/2015-spacebot/test/move_string_mapper.cpp
@@ -0,0 +1,38 @@
+#include "catch.hpp"
+#include "move.h"
+#include <string>
+#include "move_string_mapper.h"
+
+SCENARIO("Writing a move")
+{
+ GIVEN("A Move")
+ {
+ Move move = Move::MOVE_LEFT;
+
+ WHEN("It is mapped to a string")
+ {
+ std::string moveString = MoveStringMapper().toString(move);
+
+ THEN("The string is correct")
+ {
+ REQUIRE(moveString == "MoveLeft");
+ }
+ }
+
+ }
+
+ GIVEN("Build missle controller move")
+ {
+ Move move = Move::BUILD_MISSILE_CONTROLLER;
+
+ WHEN("It is mapped to a string")
+ {
+ std::string moveString = MoveStringMapper().toString(move);
+
+ THEN("The string is correct")
+ {
+ REQUIRE(moveString == "BuildMissileController");
+ }
+ }
+ }
+}
diff --git a/2015-spacebot/test/neural_network.cpp b/2015-spacebot/test/neural_network.cpp
new file mode 100644
index 0000000..418f5c4
--- /dev/null
+++ b/2015-spacebot/test/neural_network.cpp
@@ -0,0 +1,122 @@
+#include "catch.hpp"
+#include <sstream>
+
+#include "brain/neural_network.h"
+
+SCENARIO("network is read from istream")
+{
+ GIVEN("an empty config file")
+ {
+ std::stringstream file;
+ file << "" << std::endl;
+
+ WHEN ("the network is initialized")
+ {
+ NeuralNetwork network(std::move(file), 1, 2);
+
+ THEN("the specified number of inputs and outputs are created")
+ {
+ REQUIRE(network.numberOfSensors() == 1);
+ REQUIRE(network.numberOfOutputs() == 2);
+ }
+ }
+ }
+
+ GIVEN("a valid config file")
+ {
+ std::stringstream file;
+ file << "s0 n3 0.5" << std::endl;
+ file << "n3 n1 1" << std::endl;
+ file << "b0 n0 0.4" << std::endl;
+ file << "b0 n3 0.5" << std::endl;
+
+ WHEN("the network is initialized")
+ {
+ NeuralNetwork network(std::move(file), 1, 3);
+
+ THEN("the network is constructed correctly")
+ {
+ REQUIRE(network.linkExists("s0", "n3", 0.5));
+ REQUIRE(network.linkExists("n3", "n1", 1));
+ REQUIRE(network.linkExists("b0", "n0", 0.4));
+ REQUIRE(network.linkExists("b0", "n3", 0.5));
+ }
+ THEN("The network evaluates correctly")
+ {
+ network.setInput(0, 1);
+ REQUIRE(network.findMaxOutputIndex() == 1);
+ }
+ }
+ }
+
+ GIVEN("a valid recurrant config file")
+ {
+ std::stringstream file;
+ file << "s0 n3 0.5" << std::endl;
+ file << "n3 n1 1" << std::endl;
+ file << "b0 n0 0.4" << std::endl;
+ file << "b0 n3 0.5" << std::endl;
+ file << "n1 n3 0.5" << std::endl;
+
+ WHEN("the network converges")
+ {
+ NeuralNetwork network(std::move(file), 1, 3);
+
+ THEN("the network is constructed correctly")
+ {
+ network.setInput(0, 1);
+ REQUIRE(network.findMaxOutputIndex() == 1);
+ }
+ }
+ }
+
+ GIVEN("my handcoded config file")
+ {
+ std::stringstream file;
+ file << "b0 n0 20" << std::endl;
+ file << "s55 n3 10" << std::endl;
+ file << "b0 n4 -10" << std::endl;
+ file << "s59 n4 -50" << std::endl;
+ file << "s60 n4 20" << std::endl;
+ file << "b0 n6 10" << std::endl;
+ file << "s51 n6 -10" << std::endl;
+ file << "s53 n6 -10" << std::endl;
+ file << "n3 n0 -20" << std::endl;
+ file << "n4 n0 -20" << std::endl;
+ file << "n6 n0 -20" << std::endl;
+ file << "n3 n4 -20" << std::endl;
+ file << "n6 n3 -20" << std::endl;
+ file << "n6 n4 -20" << std::endl;
+
+ WHEN("the netwok is constructed")
+ {
+ std::vector<bool> sensors(61);
+
+ NeuralNetwork network(std::move(file), sensors, 7);
+ THEN("it is constructred correctly")
+ {
+ REQUIRE(network.linkExists("b0", "n0", 20));
+ REQUIRE(network.linkExists("s55", "n3", 10));
+ REQUIRE(network.linkExists("b0", "n4", -10));
+ REQUIRE(network.linkExists("s59", "n4", -50));
+ REQUIRE(network.linkExists("s60", "n4", 20));
+ REQUIRE(network.linkExists("b0", "n6", 10));
+ REQUIRE(network.linkExists("s51", "n6", -10));
+ REQUIRE(network.linkExists("s53", "n6", -10));
+ REQUIRE(network.linkExists("n3", "n0", -20));
+ REQUIRE(network.linkExists("n4", "n0", -20));
+ REQUIRE(network.linkExists("n6", "n0", -20));
+ REQUIRE(network.linkExists("n3", "n4", -20));
+ REQUIRE(network.linkExists("n6", "n3", -20));
+ REQUIRE(network.linkExists("n6", "n4", -20));
+ }
+
+ THEN("it has the right number of nodes and sensors")
+ {
+ REQUIRE(network.numberOfSensors() == 61);
+ REQUIRE(network.numberOfOutputs() == 7);
+ REQUIRE(network.numberOfNeurons() == 7);
+ }
+ }
+ }
+}
diff --git a/2015-spacebot/test/test_main.cpp b/2015-spacebot/test/test_main.cpp
new file mode 100644
index 0000000..0c7c351
--- /dev/null
+++ b/2015-spacebot/test/test_main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
diff --git a/2015-spacebot/watch.sh b/2015-spacebot/watch.sh
new file mode 100755
index 0000000..101bfa2
--- /dev/null
+++ b/2015-spacebot/watch.sh
@@ -0,0 +1,4 @@
+while true; do
+ ./test.sh
+ inotifywait -r -e modify,move ./
+done
diff --git a/2017-battleships/.gitignore b/2017-battleships/.gitignore
new file mode 100644
index 0000000..a41bb2d
--- /dev/null
+++ b/2017-battleships/.gitignore
@@ -0,0 +1,3 @@
+/target
+
+/knowledge-state.json
diff --git a/2017-battleships/Cargo.lock b/2017-battleships/Cargo.lock
new file mode 100644
index 0000000..96ea518
--- /dev/null
+++ b/2017-battleships/Cargo.lock
@@ -0,0 +1,122 @@
+[root]
+name = "worthebot_battleships"
+version = "0.1.0"
+dependencies = [
+ "json 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dtoa"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itoa"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "json"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-traits"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "quote"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rand"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "synom"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
+"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
+"checksum json 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)" = "27600e8bb3b71bcc6213fb36b66b8dce60adc17a624257687ef5d1d4facafba7"
+"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502"
+"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
+"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
+"checksum serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "991ef6be409a3b7a46cb9ee701d86156ce851825c65dbee7f16dbd5c4e7e2d47"
+"checksum serde_derive 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd81eef9f0b4ec341b11095335b6a4b28ed85581b12dd27585dee1529df35e0"
+"checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1"
+"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b"
+"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
+"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
+"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
diff --git a/2017-battleships/Cargo.toml b/2017-battleships/Cargo.toml
new file mode 100644
index 0000000..cdebbe8
--- /dev/null
+++ b/2017-battleships/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "worthebot_battleships"
+version = "0.1.0"
+authors = ["Justin Worthe <justin.worthe@gmail.com>"]
+
+[dependencies]
+rand = "0.3"
+json = "0.11.6"
+serde = "1.0.4"
+serde_json = "1.0.2"
+serde_derive = "1.0.4" \ No newline at end of file
diff --git a/2017-battleships/bot.json b/2017-battleships/bot.json
new file mode 100644
index 0000000..6245b49
--- /dev/null
+++ b/2017-battleships/bot.json
@@ -0,0 +1,8 @@
+{
+ "Author":"Justin Worthe",
+ "Email":"justin.worthe@gmail.com",
+ "NickName" :"Admiral Worthebot",
+ "BotType": 8,
+ "ProjectLocation" : "",
+ "RunFile" : "target\\release\\worthebot_battleships.exe"
+}
diff --git a/2017-battleships/notes.org b/2017-battleships/notes.org
new file mode 100644
index 0000000..ff3e319
--- /dev/null
+++ b/2017-battleships/notes.org
@@ -0,0 +1,1447 @@
+* State.json
+
+#+BEGIN_EXAMPLE
+State = {PlayerMap, OpponentMap, GameVersion=String, GameLevel=u16, Round=u16, MapDimension=u16, Phase=u16, Player1Map=null, Player2Map=null}
+
+PlayerMap = {Cells=[PlayerCell], Owner, MapWidth=u16, MapHeight=u16}
+
+PlayerCell = {Occupied=bool, Hit=bool, X=u16, Y=u16}
+
+Owner = {FailedFirstPhaseCommands=u16, Name=String, Ships=[PlayerShip], Points=u16, Killed=bool, IsWinner=bool, ShotsFired=u16, ShotsHit=u16, ShipsRemaining=u16, Key=String}
+
+PlayerShip = {Cells=[PlayerCell], Destroyed=bool, Placed=bool, ShipType=String, Weapons=[PlayerWeapon], }
+
+PlayerWeapon = {WeaponType=String}
+
+OpponentMap = {Ships=[OpponentShip], Alive=bool, Points=u16, Name=String, Cells=[OpponentCell]}
+
+OpponentShip = {Destroyed=bool, ShipType=String}
+
+OpponentCell = {Damaged=bool, Missed=bool, X=u16, Y=u16}
+
+#+END_EXAMPLE
+
+* State.json example
+
+#+BEGIN_SRC json
+{
+ "PlayerMap": {
+ "Cells": [
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 0,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 0,
+ "Y": 1
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 2
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 3
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 0,
+ "Y": 5
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 0,
+ "Y": 6
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+
+ "X": 0,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 0,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 1,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 1,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 1,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 1,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 2,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 2,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 2,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 2,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 3,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 3,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 3,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 3,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 3,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 4,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 4,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 4,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 4,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 4,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 4,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 4,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 4,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 4,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 4,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 0
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 5,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 5,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 5,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 0
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 6,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 6,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 6,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 7
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 6,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 6,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 0
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 7,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 4
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 7,
+ "Y": 5
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 7,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 7
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 7,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 0
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 8,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 8,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 8,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 7
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 8,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 8,
+ "Y": 9
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 0
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 1
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 2
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 3
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 4
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 5
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 6
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 7
+ },
+ {
+ "Occupied": false,
+ "Hit": true,
+ "X": 9,
+ "Y": 8
+ },
+ {
+ "Occupied": false,
+ "Hit": false,
+ "X": 9,
+ "Y": 9
+ }
+ ],
+ "Owner": {
+ "FailedFirstPhaseCommands": 0,
+ "Name": "Admiral Worthebot",
+ "Ships": [
+ {
+ "Cells": [
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 2
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 3
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 4
+ }
+ ],
+ "Destroyed": false,
+ "Placed": true,
+ "ShipType": "Submarine",
+ "Weapons": [
+ {
+ "WeaponType": "SingleShot"
+ }
+ ]
+ },
+ {
+ "Cells": [
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 7,
+ "Y": 6
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 7,
+ "Y": 5
+ }
+ ],
+ "Destroyed": false,
+ "Placed": true,
+ "ShipType": "Destroyer",
+ "Weapons": [
+ {
+ "WeaponType": "SingleShot"
+ }
+ ]
+ },
+ {
+ "Cells": [
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 5,
+ "Y": 1
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 6,
+ "Y": 1
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 7,
+ "Y": 1
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 8,
+ "Y": 1
+ }
+ ],
+ "Destroyed": false,
+ "Placed": true,
+ "ShipType": "Battleship",
+ "Weapons": [
+ {
+ "WeaponType": "SingleShot"
+ }
+ ]
+ },
+ {
+ "Cells": [
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 5,
+ "Y": 8
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 4,
+ "Y": 8
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 3,
+ "Y": 8
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 2,
+ "Y": 8
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 1,
+ "Y": 8
+ }
+ ],
+ "Destroyed": false,
+ "Placed": true,
+ "ShipType": "Carrier",
+ "Weapons": [
+ {
+ "WeaponType": "SingleShot"
+ }
+ ]
+ },
+ {
+ "Cells": [
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 0,
+ "Y": 6
+ },
+ {
+ "Occupied": true,
+ "Hit": true,
+ "X": 0,
+ "Y": 7
+ },
+ {
+ "Occupied": true,
+ "Hit": false,
+ "X": 0,
+ "Y": 8
+ }
+ ],
+ "Destroyed": false,
+ "Placed": true,
+ "ShipType": "Cruiser",
+ "Weapons": [
+ {
+ "WeaponType": "SingleShot"
+ }
+ ]
+ }
+ ],
+ "Points": 280,
+ "Killed": false,
+ "IsWinner": false,
+ "ShotsFired": 86,
+ "ShotsHit": 16,
+ "ShipsRemaining": 5,
+ "Key": "B"
+ },
+ "MapWidth": 10,
+ "MapHeight": 10
+ },
+ "OpponentMap": {
+ "Ships": [
+ {
+ "Destroyed": false,
+ "ShipType": "Submarine"
+ },
+ {
+ "Destroyed": true,
+ "ShipType": "Destroyer"
+ },
+ {
+ "Destroyed": true,
+ "ShipType": "Battleship"
+ },
+ {
+ "Destroyed": true,
+ "ShipType": "Carrier"
+ },
+ {
+ "Destroyed": true,
+ "ShipType": "Cruiser"
+ }
+ ],
+ "Alive": true,
+ "Points": 50,
+ "Name": "John",
+ "Cells": [
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 0
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 0,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 0,
+ "Y": 9
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 1,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 1,
+ "Y": 1
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 1,
+ "Y": 2
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 1,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 1,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 1,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 1,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 1,
+ "Y": 7
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 1,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 1,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 0
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 2,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 2,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 2,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 3,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 3,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 3,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 3,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 3,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 3,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 3,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 3,
+ "Y": 7
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 3,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 3,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 4,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 4,
+ "Y": 1
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 4,
+ "Y": 2
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 4,
+ "Y": 3
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 4,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 4,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 4,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 4,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 4,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 4,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 5,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 5,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 5,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 6,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 6,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 6,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 7,
+ "Y": 0
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 7,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 2
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 7,
+ "Y": 3
+ },
+ {
+ "Damaged": true,
+ "Missed": false,
+ "X": 7,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 7,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 0
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 8,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": false,
+ "X": 8,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 8,
+ "Y": 9
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 0
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 1
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 2
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 3
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 4
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 5
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 6
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 7
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 8
+ },
+ {
+ "Damaged": false,
+ "Missed": true,
+ "X": 9,
+ "Y": 9
+ }
+ ]
+ },
+ "GameVersion": "1.0.0",
+ "GameLevel": 1,
+ "Round": 87,
+ "MapDimension": 10,
+ "Phase": 2,
+ "Player1Map": null,
+ "Player2Map": null
+}
+#+END_SRC
diff --git a/2017-battleships/readme.txt b/2017-battleships/readme.txt
new file mode 100644
index 0000000..ffffa2f
--- /dev/null
+++ b/2017-battleships/readme.txt
@@ -0,0 +1,20 @@
+* Admiral Worthebot
+
+** Compilation Instructions
+
+As per the Rust sample bot. Install the Rust build toolchain from https://www.rust-lang.org/en-US/install.html, then from the root directory of the project run
+
+cargo build --release
+
+** Project Structure
+
+Cargo.toml - Cargo project config, including project dependencies
+src/ - Soure code directory
+src/main.rs - Command line entrypoint (main function) and command line argument parsing
+src/lib.rs - Programs public interface (as used by main.rs and any integration tests)
+
+** Strategy
+
+- Track all possible ways that an opponent may have placed their ships
+- After every move, deduce which possibilities are now impossible
+- Shoot in an attempt to (possibly) eliminate as many possibilities as possible
diff --git a/2017-battleships/src/actions.rs b/2017-battleships/src/actions.rs
new file mode 100644
index 0000000..cf0059a
--- /dev/null
+++ b/2017-battleships/src/actions.rs
@@ -0,0 +1,90 @@
+use math::*;
+use ships::*;
+
+use std::fmt;
+
+use std::collections::HashSet;
+
+#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
+pub enum Action {
+ PlaceShips(Vec<ShipPlacement>),
+ Shoot(Weapon, Point)
+}
+
+impl fmt::Display for Action {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &Action::Shoot(w, p) => writeln!(f, "{},{},{}", w, p.x, p.y),
+ &Action::PlaceShips(ref ships) => ships.iter().map(|ref ship| {
+ writeln!(f, "{} {} {} {}", ship.ship_type, ship.point.x, ship.point.y, ship.direction)
+ }).fold(Ok(()), |acc, next| acc.and(next))
+ }
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
+pub struct ShipPlacement {
+ ship_type: Ship,
+ point: Point,
+ direction: Direction
+}
+
+impl ShipPlacement {
+ pub fn new(ship_type: Ship, point: Point, direction: Direction) -> ShipPlacement {
+ ShipPlacement {
+ ship_type: ship_type,
+ point: point,
+ direction: direction
+ }
+ }
+
+ pub fn valid(&self, map_size: u16) -> bool {
+ let start = self.point;
+ let end = start.move_point(self.direction, self.ship_type.length() as i32, map_size);
+ start.x < map_size && start.y < map_size && end.is_some()
+ }
+ pub fn valid_placements(placements: &Vec<ShipPlacement>, map_size: u16) -> bool {
+ let mut occupied = HashSet::new();
+
+ let individuals_valid = placements.iter().all(|p| p.valid(map_size));
+
+ let mut no_overlaps = true;
+ for placement in placements {
+ for i in 0..placement.ship_type.length() as i32 {
+ match placement.point.move_point(placement.direction, i, map_size) {
+ Some(block) => {
+ no_overlaps = no_overlaps && !occupied.contains(&block);
+ occupied.insert(block);
+ },
+ None => {
+ //invalid case here is handled above
+ }
+ }
+ }
+
+ //block out the area around the current ship to prevent adjacent ships
+ for i in 0..placement.ship_type.length() as i32 {
+ match placement.point.move_point(placement.direction, i, map_size) {
+ Some(current_block) => {
+ if let Some(p) = current_block.move_point(Direction::North, 1, map_size) {
+ occupied.insert(p);
+ }
+ if let Some(p) = current_block.move_point(Direction::South, 1, map_size) {
+ occupied.insert(p);
+ }
+ if let Some(p) = current_block.move_point(Direction::East, 1, map_size) {
+ occupied.insert(p);
+ }
+ if let Some(p) = current_block.move_point(Direction::West, 1, map_size) {
+ occupied.insert(p);
+ }
+ },
+ None => {
+ //invalid case here is handled above
+ }
+ }
+ }
+ }
+ individuals_valid && no_overlaps
+ }
+}
diff --git a/2017-battleships/src/files.rs b/2017-battleships/src/files.rs
new file mode 100644
index 0000000..0810a4e
--- /dev/null
+++ b/2017-battleships/src/files.rs
@@ -0,0 +1,57 @@
+use json;
+use serde_json;
+
+use std::io::prelude::*;
+use std::fs::File;
+use std::path::PathBuf;
+
+use actions::*;
+use knowledge::*;
+
+const STATE_FILE: &'static str = "state.json";
+
+const COMMAND_FILE: &'static str = "command.txt";
+const PLACE_FILE: &'static str = "place.txt";
+
+const KNOWLEDGE_FILE: &'static str = "knowledge-state.json";
+
+
+pub fn read_file(working_dir: &PathBuf) -> Result<json::JsonValue, String> {
+ let state_path = working_dir.join(STATE_FILE);
+ let mut file = File::open(state_path.as_path()).map_err(|e| e.to_string())?;
+ let mut content = String::new();
+ file.read_to_string(&mut content).map_err(|e| e.to_string())?;
+ json::parse(content.as_ref()).map_err(|e| e.to_string())
+}
+
+pub fn write_action(working_dir: &PathBuf, is_place_phase: bool, action: Action) -> Result<(), String> {
+ let filename = if is_place_phase {
+ PLACE_FILE
+ }
+ else {
+ COMMAND_FILE
+ };
+
+ let full_filename = working_dir.join(filename);
+ let mut file = File::create(full_filename.as_path()).map_err(|e| e.to_string())?;
+ write!(file, "{}", action).map_err(|e| e.to_string())?;
+
+ println!("Making move: {}", action);
+
+ Ok(())
+}
+
+pub fn read_knowledge() -> Result<Knowledge, String> {
+ let mut file = File::open(KNOWLEDGE_FILE).map_err(|e| e.to_string())?;
+ let mut content = String::new();
+ file.read_to_string(&mut content).map_err(|e| e.to_string())?;
+ serde_json::from_str(content.as_ref()).map_err(|e| e.to_string())
+}
+
+pub fn write_knowledge(knowledge: &Knowledge) -> Result<(), String> {
+ let json = serde_json::to_string(knowledge).map_err(|e| e.to_string())?;
+ let mut file = File::create(KNOWLEDGE_FILE).map_err(|e| e.to_string())?;
+ write!(file, "{}", json).map_err(|e| e.to_string())?;
+
+ Ok(())
+}
diff --git a/2017-battleships/src/knowledge.rs b/2017-battleships/src/knowledge.rs
new file mode 100644
index 0000000..4a66e8b
--- /dev/null
+++ b/2017-battleships/src/knowledge.rs
@@ -0,0 +1,504 @@
+use actions::*;
+use ships::*;
+use state::*;
+use math::*;
+
+use std::collections::HashMap;
+use std::cmp::Ordering;
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct Knowledge {
+ pub last_action: Action,
+ pub opponent_map: OpponentMapKnowledge,
+ pub map_size: u16,
+ pub available_weapons: Vec<Weapon>,
+ pub shootable_weapons: Vec<Weapon>,
+ pub charging_weapons: HashMap<Weapon, u16>
+}
+
+impl Knowledge {
+ pub fn new(map_size: u16, action: Action) -> Knowledge {
+ Knowledge {
+ last_action: action,
+ opponent_map: OpponentMapKnowledge::new(map_size),
+ map_size: map_size,
+ available_weapons: Vec::new(),
+ shootable_weapons: Vec::new(),
+ charging_weapons: HashMap::new()
+ }
+ }
+
+ pub fn with_action(&self, action: Action) -> Knowledge {
+ Knowledge {
+ last_action: action,
+ ..self.clone()
+ }
+ }
+
+ pub fn resolve_last_action(&self, state: &State) -> Knowledge {
+ let mut new_knowledge = self.clone();
+
+ let energy = state.player_map.energy;
+ let mut available_weapons: Vec<_> = state.player_map.ships.iter()
+ .filter(|&(_, ship_data)| !ship_data.destroyed)
+ .flat_map(|(ship, _)| ship.weapons())
+ .collect();
+
+ available_weapons.sort_by_key(|weapon| format!("{}",weapon));
+ available_weapons.dedup();
+ new_knowledge.available_weapons = available_weapons;
+
+ new_knowledge.shootable_weapons = new_knowledge.available_weapons.iter()
+ .filter(|weapon| weapon.energy_cost(state.map_size) <= energy)
+ .cloned()
+ .collect();
+
+ new_knowledge.charging_weapons = new_knowledge.available_weapons.iter()
+ .filter(|weapon| weapon.energy_cost(state.map_size) > energy)
+ .map(|weapon| (weapon.clone(), weapon.single_shot_rounds_to_ready(energy, state.map_size)))
+ .collect();
+
+ let (hits, misses, _) = match self.last_action {
+ Action::PlaceShips(_) => {
+ (vec!(), vec!(), vec!())
+ },
+ Action::Shoot(Weapon::SeekerMissle, p) => {
+ Knowledge::seeker_hits_and_misses(p, &state)
+ }
+
+ Action::Shoot(w, p) => {
+ Knowledge::to_hits_and_misses(w.affected_cells(p, state.map_size), &state)
+ }
+ };
+
+ let sunk_ships = new_knowledge.opponent_map.update_sunk_ships(&state);
+
+ new_knowledge.opponent_map.update_from_shot(hits, misses, sunk_ships);
+
+ new_knowledge
+ }
+
+ fn to_hits_and_misses(points: Vec<Point>, state: &State) -> (Vec<Point>, Vec<Point>, Vec<Point>) {
+ let hits = points.iter().filter(|p| state.opponent_map.cells[p.x as usize][p.y as usize].damaged).cloned().collect();
+ let misses = points.iter().filter(|p| state.opponent_map.cells[p.x as usize][p.y as usize].missed).cloned().collect();
+ let unknown = points.iter().filter(|p| !state.opponent_map.cells[p.x as usize][p.y as usize].missed && !state.opponent_map.cells[p.x as usize][p.y as usize].damaged).cloned().collect();
+
+ (hits, misses, unknown)
+ }
+
+ fn seeker_hits_and_misses(p: Point, state: &State) -> (Vec<Point>, Vec<Point>, Vec<Point>) {
+ let mut misses: Vec<Point> = Vec::new();
+ let mut hits: Vec<Point> = Vec::new();
+
+ let rings = vec!(
+ vec!(
+ //0
+ Some(p)
+ ),
+ vec!(
+ //1
+ p.move_point(Direction::North, 1, state.map_size),
+ p.move_point(Direction::East, 1, state.map_size),
+ p.move_point(Direction::South, 1, state.map_size),
+ p.move_point(Direction::West, 1, state.map_size),
+ ),
+ vec!(
+ //1.44
+ p.move_point(Direction::NorthEast, 1, state.map_size),
+ p.move_point(Direction::SouthEast, 1, state.map_size),
+ p.move_point(Direction::NorthWest, 1, state.map_size),
+ p.move_point(Direction::SouthWest, 1, state.map_size)
+ ),
+ vec!(
+ //2
+ p.move_point(Direction::North, 2, state.map_size),
+ p.move_point(Direction::East, 2, state.map_size),
+ p.move_point(Direction::South, 2, state.map_size),
+ p.move_point(Direction::West, 2, state.map_size),
+ )
+ );
+
+ //start in the center. Add rings, until I find a hit
+ //don't add more after a hit is found
+ for ring in rings {
+ if hits.is_empty() {
+ let (mut new_hits, mut new_misses, mut unknown) = Knowledge::to_hits_and_misses(ring.iter().filter_map(|&p| p).collect::<Vec<_>>(), &state);
+ misses.append(&mut new_misses);
+ if !new_hits.is_empty() {
+ hits.append(&mut new_hits);
+ } else {
+ misses.append(&mut unknown);
+ }
+ }
+ }
+
+ (hits, misses, vec!())
+ }
+
+ pub fn has_unknown_hits(&self) -> bool {
+ self.opponent_map.cells.iter().fold(false, |acc, x| {
+ x.iter().fold(acc, |acc, y| acc || y.unknown_hit())
+ })
+ }
+
+ pub fn get_best_adjacent_shots(&self) -> Vec<Point> {
+ let unknown_hits = self.opponent_map.cells_with_unknown_hits();
+ let adjacent_cells = self.opponent_map.adjacent_unshot_cells(&unknown_hits);
+
+ let possible_placements = self.opponent_map.possible_placements();
+
+ let mut max_score = 1;
+ let mut best_cells = Vec::new();
+
+ for placement in possible_placements {
+ for &cell in &adjacent_cells {
+ let score = placement.count_hit_cells(cell, &unknown_hits);
+ if score > max_score {
+ max_score = score;
+ best_cells = vec!(cell);
+ }
+ else if score == max_score {
+ best_cells.push(cell);
+ }
+ }
+ }
+
+ best_cells
+ }
+
+ pub fn get_best_seek_shots(&self) -> (Weapon, Vec<Point>) {
+ let possible_placements = self.opponent_map.possible_placements();
+ // let lattice = self.lattice_size(); //TODO use the lattice still?
+
+ let guaranteed_hits = self.get_points_that_touch_all_possibilities_on_unsunk_ship();
+ if !guaranteed_hits.is_empty() {
+ return (Weapon::SingleShot, guaranteed_hits);
+ }
+
+ let mut best_shots: HashMap<Weapon, (Vec<Point>, usize)> = HashMap::new();
+
+ for &weapon in self.available_weapons.iter() {
+ let mut current_best_score = 1;
+ let mut best_cells = Vec::new();
+
+ for target in self.opponent_map.flat_cell_position_list() {
+ let cells = if weapon == Weapon::SeekerMissle {
+ let full_range = weapon.affected_cells(target, self.map_size);
+ let has_hits = full_range.iter().any(|p| self.opponent_map.cells[p.x as usize][p.y as usize].hit);
+ if has_hits {
+ vec!()
+ }
+ else {
+ full_range
+ }
+ }
+ else {
+ weapon.affected_cells(target, self.map_size)
+ .iter()
+ .filter(|p| !self.opponent_map.cells[p.x as usize][p.y as usize].hit)
+ .cloned()
+ .collect()
+ };
+
+ let possibilities = possible_placements.iter()
+ .filter(|placement| placement.touches_any_point(&cells))
+ .count();
+
+ if possibilities > current_best_score {
+ current_best_score = possibilities;
+ best_cells = vec!(target);
+ }
+ else if possibilities == current_best_score {
+ best_cells.push(target);
+ }
+ }
+
+ best_shots.insert(weapon, (best_cells, current_best_score));
+ }
+
+ let best_single: Option<(Weapon, (Vec<Point>, usize))> =
+ best_shots.get(&Weapon::SingleShot).map(|x| (Weapon::SingleShot, x.clone()));
+
+ let best: (Weapon, (Vec<Point>, usize)) =
+ best_shots.iter()
+ .max_by(|&(weapon_a, &(_, score_a)), &(weapon_b, &(_, score_b))| {
+ let score = score_a.cmp(&score_b);
+ let cost = weapon_a.energy_cost(self.map_size).cmp(&weapon_b.energy_cost(self.map_size));
+ if score == Ordering::Equal { cost } else { score }
+ })
+ .and_then(|(&weapon, x)| {
+ if self.shootable_weapons.contains(&weapon) {
+ Some((weapon, x.clone()))
+ } else {
+ best_single
+ }
+ })
+ .unwrap_or((Weapon::SingleShot, (vec!(), 0)));
+
+
+ (best.0.clone(), (best.1).0)
+ }
+
+ fn get_points_that_touch_all_possibilities_on_unsunk_ship(&self) -> Vec<Point> {
+ self.opponent_map.flat_cell_position_list().iter().cloned().filter(|&point| {
+ self.opponent_map.ships.values()
+ .any(|ref ship| !ship.destroyed &&
+ ship.possible_placements.iter().all(|placement| {
+ placement.touches_point(point)
+ }))
+ }).collect()
+ }
+
+ fn lattice_size(&self) -> u16 {
+ let any_long_ships = self.opponent_map.ships.iter()
+ .any(|(ship, knowledge)| ship.length() >= 4 && !knowledge.destroyed);
+ if any_long_ships {
+ 4
+ }
+ else {
+ 2
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct OpponentMapKnowledge {
+ pub ships: HashMap<Ship, OpponentShipKnowledge>,
+ pub cells: Vec<Vec<KnowledgeCell>>
+}
+
+impl OpponentMapKnowledge {
+ fn new(map_size: u16) -> OpponentMapKnowledge {
+ let mut cells = Vec::with_capacity(map_size as usize);
+ for x in 0..map_size {
+ cells.push(Vec::with_capacity(map_size as usize));
+ for y in 0..map_size {
+ cells[x as usize].push(KnowledgeCell::new(x, y));
+ }
+ }
+
+ let ships = Ship::all_types().iter()
+ .map(|s| (s.clone(), OpponentShipKnowledge::new(s.clone(), map_size)))
+ .collect::<HashMap<_, _>>();
+
+ OpponentMapKnowledge {
+ ships: ships,
+ cells: cells
+ }
+ }
+
+ fn update_sunk_ships(&mut self, state: &State) -> Vec<Ship> {
+ let sunk_ships = self.ships.iter()
+ .filter(|&(_, x)| !x.destroyed)
+ .filter(|&(s, _)| state.opponent_map.ships.get(s).map(|x| x.destroyed) == Some(true))
+ .map(|(s, _)| s.clone())
+ .collect();
+
+ for &ship in &sunk_ships {
+ self.ships.get_mut(&ship).map(|ref mut ship_knowledge| ship_knowledge.destroyed = true);
+ }
+
+ sunk_ships
+ }
+
+ fn update_from_shot(&mut self, hit_cells: Vec<Point>, missed_cells: Vec<Point>, sunk_ships: Vec<Ship>) {
+ for &missed in &missed_cells {
+ self.cells[missed.x as usize][missed.y as usize].missed = true;
+ }
+ for &hit in &hit_cells {
+ self.cells[hit.x as usize][hit.y as usize].hit = true;
+ }
+
+ self.clear_sunk_ship_impossible_placements(&sunk_ships, &hit_cells);
+
+ let mut more_changes = true;
+ while more_changes {
+ more_changes = self.derive_ship_positions() || self.clear_impossible_placements();
+ }
+ }
+
+ fn derive_ship_positions(&mut self) -> bool {
+ let mut any_changes = false;
+ for knowledge in self.ships.values() {
+ if knowledge.possible_placements.len() == 1 {
+ let ref true_placement = knowledge.possible_placements[0];
+ for p in true_placement.points_on_ship() {
+ if self.cells[p.x as usize][p.y as usize].known_ship == None {
+ self.cells[p.x as usize][p.y as usize].known_ship = Some(true_placement.ship);
+ any_changes = true;
+ }
+ }
+ }
+ }
+ any_changes
+ }
+
+ fn clear_impossible_placements(&mut self) -> bool {
+ let mut any_changes = false;
+ let ref cells = self.cells;
+ for knowledge in self.ships.values_mut() {
+ let before = knowledge.possible_placements.len();
+ knowledge.possible_placements.retain(|x| x.all_could_be_hits(&cells));
+ let after = knowledge.possible_placements.len();
+ if before != after {
+ any_changes = true;
+ }
+ }
+ any_changes
+ }
+
+ fn clear_sunk_ship_impossible_placements(&mut self, sunk_ships: &Vec<Ship>, must_touch_any: &Vec<Point>) {
+ let cells_copy = self.cells.clone();
+
+ for knowledge in self.ships.values_mut() {
+ knowledge.possible_placements.retain(|x| !sunk_ships.contains(&x.ship) || (x.touches_any_point(&must_touch_any) && x.all_are_hits(&cells_copy)));
+ }
+ }
+
+ fn cells_with_unknown_hits(&self) -> Vec<Point> {
+ self.cells.iter().flat_map(|x| {
+ x.iter().filter(|y| y.unknown_hit()).map(|y| y.position)
+ }).collect()
+ }
+
+ fn adjacent_unshot_cells(&self, cells: &Vec<Point>) -> Vec<Point> {
+ self.cells.iter().flat_map(|x| {
+ x.iter()
+ .filter(|y| !y.shot_attempted())
+ .map(|y| y.position)
+ .filter(|&y| cells.iter().any(|z| z.is_adjacent(y)))
+ }).collect()
+ }
+
+ fn flat_cell_position_list(&self) -> Vec<Point> {
+ self.cells.iter().flat_map(|x| {
+ x.iter().map(|y| y.position)
+ }).collect()
+ }
+
+ fn cells_on_lattice(&self, lattice_size: u16) -> Vec<Point> {
+ self.cells.iter().flat_map(|x| {
+ x.iter()
+ .filter(|y| !y.shot_attempted() && y.position.is_on_lattice(lattice_size))
+ .map(|y| y.position)
+ }).collect()
+ }
+
+ fn possible_placements(&self) -> Vec<PossibleShipPlacement> {
+ self.ships
+ .values()
+ .flat_map(|knowledge| knowledge.possible_placements.clone())
+ .collect()
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct OpponentShipKnowledge {
+ pub ship: Ship,
+ pub destroyed: bool,
+ pub possible_placements: Vec<PossibleShipPlacement>
+}
+
+impl OpponentShipKnowledge {
+ fn new(ship: Ship, map_size: u16) -> OpponentShipKnowledge {
+ OpponentShipKnowledge {
+ ship: ship,
+ destroyed: false,
+ possible_placements: PossibleShipPlacement::enumerate(ship, map_size)
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct PossibleShipPlacement {
+ pub ship: Ship,
+ pub direction: Direction,
+ pub position: Point
+}
+
+impl PossibleShipPlacement {
+ fn enumerate(ship: Ship, map_size: u16) -> Vec<PossibleShipPlacement> {
+ (0..(map_size-ship.length()+1)).flat_map(move |par| {
+ (0..map_size).flat_map(move |per| {
+ vec!(
+ PossibleShipPlacement {
+ ship: ship,
+ direction: Direction::East,
+ position: Point::new(par, per)
+ },
+ PossibleShipPlacement {
+ ship: ship,
+ direction: Direction::North,
+ position: Point::new(per, par)
+ }
+ )
+ })
+ }).collect()
+ }
+
+ pub fn touches_point(&self, p: Point) -> bool {
+ p.check_for_ship_collision(self.position, self.direction, self.ship.length())
+ }
+ pub fn touches_any_point(&self, ps: &Vec<Point>) -> bool {
+ ps.iter().any(|&p| self.touches_point(p))
+ }
+
+ pub fn points_on_ship(&self) -> Vec<Point> {
+ (0..self.ship.length() as i32).map(|i| {
+ self.position.move_point_no_bounds_check(self.direction, i)
+ }).collect()
+ }
+
+ fn all_are_hits(&self, cells: &Vec<Vec<KnowledgeCell>>) -> bool {
+ self.points_on_ship()
+ .iter()
+ .fold(true, |acc, p| acc && cells[p.x as usize][p.y as usize].hit)
+ }
+
+ fn all_could_be_hits(&self, cells: &Vec<Vec<KnowledgeCell>>) -> bool {
+ self.points_on_ship()
+ .iter()
+ .all(|p| {
+ let ref cell = cells[p.x as usize][p.y as usize];
+ !cell.missed && cell.known_ship.map(|ship| ship == self.ship).unwrap_or(true)
+ })
+ }
+
+ fn count_hit_cells(&self, required: Point, wanted: &Vec<Point>) -> u16 {
+ if !self.touches_point(required) {
+ return 0;
+ }
+
+ wanted.iter().filter(|&&x| self.touches_point(x)).count() as u16
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct KnowledgeCell {
+ pub missed: bool,
+ pub hit: bool,
+ pub known_ship: Option<Ship>,
+ pub position: Point
+}
+
+impl KnowledgeCell {
+ fn new(x: u16, y: u16) -> KnowledgeCell {
+ KnowledgeCell {
+ missed: false,
+ hit: false,
+ position: Point::new(x, y),
+ known_ship: None
+ }
+ }
+
+ pub fn shot_attempted(&self) -> bool {
+ self.missed || self.hit
+ }
+
+ pub fn unknown_hit(&self) -> bool {
+ self.hit && self.known_ship.is_none()
+ }
+
+}
+
+
diff --git a/2017-battleships/src/lib.rs b/2017-battleships/src/lib.rs
new file mode 100644
index 0000000..00eaf02
--- /dev/null
+++ b/2017-battleships/src/lib.rs
@@ -0,0 +1,67 @@
+extern crate json;
+extern crate rand;
+extern crate serde;
+extern crate serde_json;
+#[macro_use]
+extern crate serde_derive;
+
+mod actions;
+mod math;
+mod files;
+mod ships;
+mod placement;
+mod shooting;
+mod state;
+mod knowledge;
+
+use actions::*;
+use math::*;
+use files::*;
+use ships::*;
+use placement::*;
+use shooting::*;
+use state::*;
+use knowledge::*;
+
+use std::path::PathBuf;
+
+pub fn write_move(working_dir: PathBuf) -> Result<(), String> {
+ let state_json = read_file(&working_dir)?;
+
+ let is_place_phase = state_json["Phase"] == 1;
+ let map_size = State::map_size_from_json(&state_json)?;
+
+ let (action, knowledge) = if is_place_phase {
+ placement(map_size)
+ }
+ else {
+ let state = State::new(&state_json)?;
+ shoot(&state)?
+ };
+
+ write_knowledge(&knowledge)
+ .map_err(|e| format!("Failed to write knowledge to file. Error: {}", e))?;
+
+ write_action(&working_dir, is_place_phase, action)
+ .map_err(|e| format!("Failed to write action to file. Error: {}", e))?;
+
+ println!("Knowledge:\n{}\n\n", serde_json::to_string(&knowledge).unwrap_or(String::from("")));
+
+ Ok(())
+}
+
+fn placement(map_size: u16) -> (Action, Knowledge) {
+ let action = place_ships_randomly(map_size);
+ let knowledge = Knowledge::new(map_size, action.clone());
+
+ (action, knowledge)
+}
+
+fn shoot(state: &State) -> Result<(Action, Knowledge), String> {
+ let old_knowledge = read_knowledge()?;
+ let knowledge = old_knowledge.resolve_last_action(&state);
+ let action = shoot_smartly(&knowledge);
+
+ Ok((action.clone(), knowledge.with_action(action)))
+}
+
diff --git a/2017-battleships/src/main.rs b/2017-battleships/src/main.rs
new file mode 100644
index 0000000..ee0ba59
--- /dev/null
+++ b/2017-battleships/src/main.rs
@@ -0,0 +1,19 @@
+extern crate worthebot_battleships;
+
+use worthebot_battleships as bot;
+use std::env;
+use std::path::PathBuf;
+
+fn main() {
+ let working_dir = env::args()
+ .nth(2)
+ .map(|x| PathBuf::from(x))
+ .ok_or(String::from("Requires game state folder to be passed as the second parameter"));
+
+ let result = working_dir.and_then(|working_dir| bot::write_move(working_dir));
+
+ match result {
+ Ok(()) => println!("Bot terminated successfully"),
+ Err(e) => println!("Error in bot execution: {}", e)
+ }
+}
diff --git a/2017-battleships/src/math.rs b/2017-battleships/src/math.rs
new file mode 100644
index 0000000..3187829
--- /dev/null
+++ b/2017-battleships/src/math.rs
@@ -0,0 +1,338 @@
+use std::fmt;
+use rand;
+use rand::distributions::{IndependentSample, Range};
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
+pub enum Direction {
+ North,
+ NorthEast,
+ East,
+ SouthEast,
+ South,
+ SouthWest,
+ West,
+ NorthWest
+}
+
+use Direction::*;
+
+impl fmt::Display for Direction {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(
+ match self {
+ &North => "North",
+ &East => "East",
+ &South => "South",
+ &West => "West",
+ &NorthEast => "NorthEast",
+ &SouthEast => "SouthEast",
+ &NorthWest => "NorthWest",
+ &SouthWest => "SouthWest"
+ }
+ )
+ }
+}
+
+impl Direction {
+ pub fn random() -> Direction {
+ let mut rng = rand::thread_rng();
+ let between = Range::new(0, 4);
+ let dir = between.ind_sample(&mut rng);
+ match dir {
+ 0 => North,
+ 1 => East,
+ 2 => South,
+ 3 => West,
+ _ => panic!("Invalid number generated by random number generator")
+ }
+ }
+}
+
+#[cfg(test)]
+mod direction_tests {
+ use super::*;
+
+ #[test]
+ fn random_direction_does_not_panic() {
+ for _ in 0..10000 {
+ Direction::random();
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
+pub struct Point {
+ pub x: u16,
+ pub y: u16
+}
+
+impl Point {
+ pub fn new(x: u16, y: u16) -> Point {
+ Point {
+ x: x,
+ y: y
+ }
+ }
+
+ pub fn random(map_size: u16) -> Point {
+ let mut rng = rand::thread_rng();
+ let between = Range::new(0, map_size);
+ let x = between.ind_sample(&mut rng);
+ let y = between.ind_sample(&mut rng);
+ Point::new(x, y)
+ }
+
+
+ pub fn move_point(&self, direction: Direction, distance: i32, map_size: u16) -> Option<Point> {
+ let x = self.x as i32 + match direction {
+ West|NorthWest|SouthWest => -distance,
+ East|NorthEast|SouthEast => distance,
+ _ => 0
+ };
+ let y = self.y as i32 + match direction {
+ South|SouthWest|SouthEast => -distance,
+ North|NorthWest|NorthEast => distance,
+ _ => 0
+ };
+ let max = map_size as i32;
+
+ if x >= max || y >= max || x < 0 || y < 0 {
+ None
+ }
+ else {
+ Some(Point::new(x as u16, y as u16))
+ }
+ }
+
+ pub fn move_point_no_bounds_check(&self, direction: Direction, distance: i32) -> Point {
+ let x = self.x as i32 + match direction {
+ West => -distance,
+ East => distance,
+ _ => 0
+ };
+ let y = self.y as i32 + match direction {
+ South => -distance,
+ North => distance,
+ _ => 0
+ };
+
+ Point::new(x as u16, y as u16)
+ }
+
+ pub fn check_for_ship_collision(&self, ship_start: Point, direction: Direction, length: u16) -> bool {
+ let reverse = match direction {
+ West | South => true,
+ East | North => false,
+ _ => false //ships cannot go diagonally
+ };
+
+ let same_cross = match direction {
+ East | West => self.y == ship_start.y,
+ North | South => self.x == ship_start.x,
+ _ => false //ships cannot go diagonally
+ };
+
+ let (parr_self, parr_ship) = match direction {
+ East | West => (self.x, ship_start.x),
+ North | South => (self.y, ship_start.y),
+ _ => (self.x, self.y) //ships cannot go diagonally
+ };
+
+ let corrected_parr_ship = match reverse {
+ true => 1 + parr_ship - length,
+ false => parr_ship
+ };
+
+ same_cross && parr_self >= corrected_parr_ship && parr_self < corrected_parr_ship + length
+ }
+
+ pub fn is_adjacent(&self, other: Point) -> bool {
+ let dx = if self.x > other.x {self.x - other.x} else {other.x - self.x};
+ let dy = if self.y > other.y {self.y - other.y} else {other.y - self.y};
+
+ (dx == 0 && dy == 1) ||
+ (dx == 1 && dy == 0)
+
+ }
+
+ pub fn is_on_lattice(&self, lattice_size: u16) -> bool {
+ (self.x + self.y) % lattice_size == 0
+ }
+}
+
+
+#[cfg(test)]
+mod point_tests {
+ use super::*;
+
+ #[test]
+ fn random_point_is_in_correct_range() {
+ for _ in 0..10000 {
+ let point = Point::random(15);
+ assert!(point.x < 15);
+ assert!(point.y < 15);
+ }
+ }
+
+ #[test]
+ fn move_point_works_north_west() {
+ assert_eq!(Some(Point::new(3,7)), Point::new(5,5).move_point(NorthWest, 2, 10));
+ assert_eq!(Some(Point::new(7,3)), Point::new(5,5).move_point(NorthWest, -2, 10));
+ assert_eq!(None, Point::new(5,5).move_point(NorthWest, 6, 10));
+ assert_eq!(None, Point::new(5,5).move_point(NorthWest, -5, 10));
+ }
+
+ #[test]
+ fn move_point_works_west() {
+ assert_eq!(Some(Point::new(3,5)), Point::new(5,5).move_point(West, 2, 10));
+ assert_eq!(Some(Point::new(7,5)), Point::new(5,5).move_point(West, -2, 10));
+ assert_eq!(None, Point::new(5,5).move_point(West, 6, 10));
+ assert_eq!(None, Point::new(5,5).move_point(West, -5, 10));
+ }
+
+ #[test]
+ fn move_point_works_east() {
+ assert_eq!(Some(Point::new(7,5)), Point::new(5,5).move_point(East, 2, 10));
+ assert_eq!(Some(Point::new(3,5)), Point::new(5,5).move_point(East, -2, 10));
+ assert_eq!(None, Point::new(5,5).move_point(East, 5, 10));
+ assert_eq!(None, Point::new(5,5).move_point(East, -6, 10));
+ }
+
+ #[test]
+ fn move_point_works_south() {
+ assert_eq!(Some(Point::new(5,3)), Point::new(5,5).move_point(South, 2, 10));
+ assert_eq!(Some(Point::new(5,7)), Point::new(5,5).move_point(South, -2, 10));
+ assert_eq!(None, Point::new(5,5).move_point(South, 6, 10));
+ assert_eq!(None, Point::new(5,5).move_point(South, -5, 10));
+ }
+
+ #[test]
+ fn move_point_works_north() {
+ assert_eq!(Some(Point::new(5,7)), Point::new(5,5).move_point(North, 2, 10));
+ assert_eq!(Some(Point::new(5,3)), Point::new(5,5).move_point(North, -2, 10));
+ assert_eq!(None, Point::new(5,5).move_point(North, 5, 10));
+ assert_eq!(None, Point::new(5,5).move_point(North, -6, 10));
+ }
+
+ #[test]
+ fn unrestricted_move_point_works_west() {
+ assert_eq!(Point::new(3,5), Point::new(5,5).move_point_no_bounds_check(West, 2));
+ assert_eq!(Point::new(7,5), Point::new(5,5).move_point_no_bounds_check(West, -2));
+ }
+
+ #[test]
+ fn unrestricted_move_point_works_east() {
+ assert_eq!(Point::new(7,5), Point::new(5,5).move_point_no_bounds_check(East, 2));
+ assert_eq!(Point::new(3,5), Point::new(5,5).move_point_no_bounds_check(East, -2));
+ }
+
+ #[test]
+ fn unrestricted_move_point_works_south() {
+ assert_eq!(Point::new(5,3), Point::new(5,5).move_point_no_bounds_check(South, 2));
+ assert_eq!(Point::new(5,7), Point::new(5,5).move_point_no_bounds_check(South, -2));
+ }
+
+ #[test]
+ fn unrestricted_move_point_works_north() {
+ assert_eq!(Point::new(5,7), Point::new(5,5).move_point_no_bounds_check(North, 2));
+ assert_eq!(Point::new(5,3), Point::new(5,5).move_point_no_bounds_check(North, -2));
+ }
+
+ #[test]
+ fn ship_collision_check_works_west() {
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), West, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(6,5), West, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(7,5), West, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(8,5), West, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(9,5), West, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(10,5), West, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,5), West, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,4), West, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,6), West, 5));
+ }
+
+ #[test]
+ fn ship_collision_check_works_east() {
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), East, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(4,5), East, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(3,5), East, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(2,5), East, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(1,5), East, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(0,5), East, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,5), East, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,4), East, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,6), East, 5));
+ }
+
+ #[test]
+ fn ship_collision_check_works_north() {
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), North, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,4), North, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,3), North, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,2), North, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,1), North, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,0), North, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,6), North, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,4), North, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,4), North, 5));
+ }
+
+ #[test]
+ fn ship_collision_check_works_south() {
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), South, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,6), South, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,7), South, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,8), South, 5));
+ assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,9), South, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,10), South, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,4), South, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,6), South, 5));
+ assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,6), South, 5));
+ }
+
+ #[test]
+ fn adjacency_check_works() {
+ assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(4,5)));
+ assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(6,5)));
+ assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(5,4)));
+ assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(5,6)));
+
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(4,4)));
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(6,6)));
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(6,4)));
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(4,6)));
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(5,5)));
+
+ assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(10,5)));
+
+ }
+
+ #[test]
+ fn point_on_4_lattice_works() {
+ assert_eq!(true, Point::new(0,0).is_on_lattice(4));
+ assert_eq!(true, Point::new(4,0).is_on_lattice(4));
+ assert_eq!(true, Point::new(0,4).is_on_lattice(4));
+ assert_eq!(true, Point::new(4,4).is_on_lattice(4));
+ assert_eq!(true, Point::new(1,3).is_on_lattice(4));
+ assert_eq!(true, Point::new(3,1).is_on_lattice(4));
+
+ assert_eq!(false, Point::new(0,1).is_on_lattice(4));
+ assert_eq!(false, Point::new(0,2).is_on_lattice(4));
+ assert_eq!(false, Point::new(0,3).is_on_lattice(4));
+ assert_eq!(false, Point::new(1,0).is_on_lattice(4));
+ assert_eq!(false, Point::new(2,0).is_on_lattice(4));
+ assert_eq!(false, Point::new(3,0).is_on_lattice(4));
+ }
+
+ #[test]
+ fn point_on_2_lattice_works() {
+ assert_eq!(true, Point::new(0,0).is_on_lattice(2));
+ assert_eq!(true, Point::new(2,0).is_on_lattice(2));
+ assert_eq!(true, Point::new(0,2).is_on_lattice(2));
+ assert_eq!(true, Point::new(2,2).is_on_lattice(2));
+ assert_eq!(true, Point::new(1,1).is_on_lattice(2));
+
+ assert_eq!(false, Point::new(0,1).is_on_lattice(2));
+ assert_eq!(false, Point::new(1,0).is_on_lattice(2));
+ }
+}
diff --git a/2017-battleships/src/placement.rs b/2017-battleships/src/placement.rs
new file mode 100644
index 0000000..4740d76
--- /dev/null
+++ b/2017-battleships/src/placement.rs
@@ -0,0 +1,23 @@
+use actions::*;
+use math::*;
+use ships::*;
+
+pub fn place_ships_randomly(map_size: u16) -> Action {
+ let mut current_placement: Vec<ShipPlacement>;
+
+ while {
+ current_placement = create_random_placement(map_size);
+ !ShipPlacement::valid_placements(&current_placement, map_size)
+ } {}
+ Action::PlaceShips(current_placement)
+}
+
+fn create_random_placement(map_size: u16) -> Vec<ShipPlacement> {
+ vec!(
+ ShipPlacement::new(Ship::Battleship, Point::random(map_size), Direction::random()),
+ ShipPlacement::new(Ship::Carrier, Point::random(map_size), Direction::random()),
+ ShipPlacement::new(Ship::Cruiser, Point::random(map_size), Direction::random()),
+ ShipPlacement::new(Ship::Destroyer, Point::random(map_size), Direction::random()),
+ ShipPlacement::new(Ship::Submarine, Point::random(map_size), Direction::random())
+ )
+}
diff --git a/2017-battleships/src/ships.rs b/2017-battleships/src/ships.rs
new file mode 100644
index 0000000..422f24e
--- /dev/null
+++ b/2017-battleships/src/ships.rs
@@ -0,0 +1,216 @@
+use std::fmt;
+use std::str;
+
+use math::*;
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
+pub enum Weapon {
+ SingleShot,
+ DoubleShotVertical,
+ DoubleShotHorizontal,
+ CornerShot,
+ CrossShotDiagonal,
+ CrossShotHorizontal,
+ SeekerMissle
+}
+
+impl fmt::Display for Weapon {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Weapon::*;
+
+ f.write_str(
+ match self {
+ &SingleShot => "1",
+ &DoubleShotVertical => "2",
+ &DoubleShotHorizontal => "3",
+ &CornerShot => "4",
+ &CrossShotDiagonal => "5",
+ &CrossShotHorizontal => "6",
+ &SeekerMissle => "7"
+ }
+ )
+ }
+}
+
+impl Weapon {
+ pub fn energy_per_round(map_size: u16) -> u16 {
+ if map_size < 10 {
+ 2
+ }
+ else if map_size < 14 {
+ 3
+ }
+ else {
+ 4
+ }
+ }
+ pub fn energy_cost(&self, map_size: u16) -> u16 {
+ use Weapon::*;
+ let epr = Weapon::energy_per_round(map_size);
+ match self {
+ &SingleShot => 1,
+ &DoubleShotVertical | &DoubleShotHorizontal => 8*epr,
+ &CornerShot => 10*epr,
+ &CrossShotDiagonal => 12*epr,
+ &CrossShotHorizontal => 14*epr,
+ &SeekerMissle => 10*epr
+ }
+ }
+ pub fn single_shot_rounds_to_ready(&self, current_energy: u16, map_size: u16) -> u16 {
+ let single_shot_cost = Weapon::SingleShot.energy_cost(map_size);
+ let energy_per_round = Weapon::energy_per_round(map_size) - single_shot_cost;
+ let required_energy = self.energy_cost(map_size) - current_energy;
+ //weird plus is to make the integer rounding up instead of down
+ (required_energy + energy_per_round - 1) / energy_per_round
+ }
+
+ pub fn affected_cells(&self, target: Point, map_size: u16) -> Vec<Point> {
+ use Weapon::*;
+
+ let p = target;
+ match self {
+ &SingleShot => {
+ vec!(Some(p))
+ },
+ &DoubleShotVertical => {
+ vec!(
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::South, 1, map_size)
+ )
+ },
+ &DoubleShotHorizontal => {
+ vec!(
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::West, 1, map_size)
+ )
+ },
+ &CornerShot => {
+ vec!(
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+ )
+ },
+ &CrossShotDiagonal => {
+ vec!(
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+ Some(p)
+ )
+ },
+ &CrossShotHorizontal => {
+ vec!(
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::South, 1, map_size),
+ p.move_point(Direction::West, 1, map_size),
+ Some(p)
+ )
+ },
+ &SeekerMissle => {
+ vec!(
+ Some(p),
+
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::South, 1, map_size),
+ p.move_point(Direction::West, 1, map_size),
+
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+
+ p.move_point(Direction::North, 2, map_size),
+ p.move_point(Direction::East, 2, map_size),
+ p.move_point(Direction::South, 2, map_size),
+ p.move_point(Direction::West, 2, map_size)
+ )
+ }
+ }.iter().filter_map(|&p| p).collect::<Vec<_>>()
+ }
+}
+
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
+pub enum Ship {
+ Battleship,
+ Carrier,
+ Cruiser,
+ Destroyer,
+ Submarine
+}
+
+impl fmt::Display for Ship {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Ship::*;
+
+ f.write_str(
+ match self {
+ &Battleship => "Battleship",
+ &Carrier => "Carrier",
+ &Cruiser => "Cruiser",
+ &Destroyer => "Destroyer",
+ &Submarine => "Submarine"
+ }
+ )
+ }
+}
+
+impl str::FromStr for Ship {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use Ship::*;
+
+ match s {
+ "Battleship" => Ok(Battleship),
+ "Carrier" => Ok(Carrier),
+ "Cruiser" => Ok(Cruiser),
+ "Destroyer" => Ok(Destroyer),
+ "Submarine" => Ok(Submarine),
+ _ => Err(String::from("ship type is not known"))
+ }
+ }
+}
+
+impl Ship {
+ pub fn length(&self) -> u16 {
+ use Ship::*;
+
+ match self {
+ &Battleship => 4,
+ &Carrier => 5,
+ &Cruiser => 3,
+ &Destroyer => 2,
+ &Submarine => 3
+ }
+ }
+
+ pub fn weapons(&self) -> Vec<Weapon> {
+ use Ship::*;
+ use Weapon::*;
+
+ match self {
+ &Battleship => vec!(SingleShot, CrossShotDiagonal),
+ &Carrier => vec!(SingleShot, CornerShot),
+ &Cruiser => vec!(SingleShot, CrossShotHorizontal),
+ &Destroyer => vec!(SingleShot, DoubleShotVertical, DoubleShotHorizontal),
+ &Submarine => vec!(SingleShot, SeekerMissle)
+ }
+ }
+
+ pub fn all_types() -> Vec<Ship> {
+ use Ship::*;
+
+ vec!(
+ Battleship,
+ Carrier,
+ Cruiser,
+ Destroyer,
+ Submarine
+ )
+ }
+}
diff --git a/2017-battleships/src/shooting.rs b/2017-battleships/src/shooting.rs
new file mode 100644
index 0000000..e0358ee
--- /dev/null
+++ b/2017-battleships/src/shooting.rs
@@ -0,0 +1,47 @@
+use rand;
+use rand::distributions::{IndependentSample, Range};
+
+use actions::*;
+use math::*;
+use knowledge::*;
+use ships::*;
+
+pub fn shoot_smartly(knowledge: &Knowledge) -> Action {
+ let (weapon, target) = if knowledge.has_unknown_hits() {
+ destroy_shoot(&knowledge)
+ }
+ else {
+ seek_shoot(&knowledge)
+ };
+
+ Action::Shoot(weapon, target)
+}
+
+fn seek_shoot(knowledge: &Knowledge) -> (Weapon, Point) {
+ let (weapon, possibilities) = knowledge.get_best_seek_shots();
+ if possibilities.is_empty() {
+ println!("All possible shots on the current lattice have been tried!");
+ (Weapon::SingleShot, Point::new(0,0))
+ }
+ else {
+ let mut rng = rand::thread_rng();
+ let between = Range::new(0, possibilities.len());
+ let i = between.ind_sample(&mut rng);
+
+ (weapon, possibilities[i as usize])
+ }
+}
+
+fn destroy_shoot(knowledge: &Knowledge) -> (Weapon, Point) {
+ let possibilities = knowledge.get_best_adjacent_shots();
+ if possibilities.is_empty() {
+ seek_shoot(&knowledge)
+ }
+ else {
+ let mut rng = rand::thread_rng();
+ let between = Range::new(0, possibilities.len());
+ let i = between.ind_sample(&mut rng);
+
+ (Weapon::SingleShot, possibilities[i as usize])
+ }
+}
diff --git a/2017-battleships/src/state.rs b/2017-battleships/src/state.rs
new file mode 100644
index 0000000..1756ad0
--- /dev/null
+++ b/2017-battleships/src/state.rs
@@ -0,0 +1,146 @@
+use json;
+use std::collections::HashMap;
+use ships::*;
+
+pub struct State {
+ pub map_size: u16,
+ pub player_map: PlayerMap,
+ pub opponent_map: OpponentMap
+}
+
+impl State {
+ pub fn new(json: &json::JsonValue) -> Result<State, String> {
+ let map_size = State::map_size_from_json(&json)?;
+
+ let ref player_map_json = json["PlayerMap"];
+ let player_map = PlayerMap::new(&player_map_json)?;
+
+ let ref opponent_map_json = json["OpponentMap"];
+ let opponent_map = OpponentMap::new(&opponent_map_json, map_size)?;
+
+ Ok(State {
+ map_size: map_size,
+ player_map: player_map,
+ opponent_map: opponent_map
+ })
+ }
+
+ pub fn map_size_from_json(json: &json::JsonValue) -> Result<u16, String> {
+ json["MapDimension"]
+ .as_u16()
+ .ok_or(String::from("Did not find the map dimension in the state json file"))
+ }
+}
+
+pub struct OpponentMap {
+ pub cells: Vec<Vec<Cell>>,
+ pub ships: HashMap<Ship, OpponentShip>,
+}
+
+impl OpponentMap {
+ fn new(json: &json::JsonValue, map_size: u16) -> Result<OpponentMap, String> {
+ let mut cells = Vec::with_capacity(map_size as usize);
+ for _ in 0..map_size {
+ let mut row = Vec::with_capacity(map_size as usize);
+ for _ in 0..map_size {
+ row.push(Cell::new());
+ }
+ cells.push(row);
+ }
+
+ for json_cell in json["Cells"].members() {
+ let x = json_cell["X"]
+ .as_u16()
+ .ok_or(String::from("Failed to read X value of opponent map cell in json file"))?;
+ let y = json_cell["Y"]
+ .as_u16()
+ .ok_or(String::from("Failed to read Y value of opponent map cell in json file"))?;
+ let damaged = json_cell["Damaged"]
+ .as_bool()
+ .ok_or(String::from("Failed to read Damaged value of opponent map cell in json file"))?;
+ let missed = json_cell["Missed"]
+ .as_bool()
+ .ok_or(String::from("Failed to read Missed value of opponent map cell in json file"))?;
+
+ cells[x as usize][y as usize].damaged = damaged;
+ cells[x as usize][y as usize].missed = missed;
+ }
+
+ let mut ships = HashMap::new();
+ for json_ship in json["Ships"].members() {
+ let ship_type_string = json_ship["ShipType"]
+ .as_str()
+ .ok_or(String::from("Failed to read ShipType value of opponent map ship in json file"))?;
+ let ship_type = ship_type_string.parse::<Ship>()?;
+
+ let destroyed = json_ship["Destroyed"]
+ .as_bool()
+ .ok_or(String::from("Failed to read Destroyed value of opponent map ship in json file"))?;
+ ships.insert(ship_type, OpponentShip {
+ destroyed: destroyed
+ });
+ }
+
+
+ Ok(OpponentMap {
+ cells: cells,
+ ships: ships
+ })
+ }
+}
+
+pub struct OpponentShip {
+ pub destroyed: bool
+}
+
+pub struct Cell {
+ pub damaged: bool,
+ pub missed: bool
+}
+
+impl Cell {
+ fn new() -> Cell {
+ Cell {
+ damaged: false,
+ missed: false
+ }
+ }
+}
+
+pub struct PlayerMap {
+ pub ships: HashMap<Ship, PlayerShip>,
+ pub energy: u16
+}
+
+impl PlayerMap {
+ fn new(json: &json::JsonValue) -> Result<PlayerMap, String> {
+ let mut ships = HashMap::new();
+ for json_ship in json["Owner"]["Ships"].members() {
+ let ship_type_string = json_ship["ShipType"]
+ .as_str()
+ .ok_or(String::from("Failed to read ShipType value of player map ship in json file"))?;
+ let ship_type = ship_type_string.parse::<Ship>()?;
+
+ let destroyed = json_ship["Destroyed"]
+ .as_bool()
+ .ok_or(String::from("Failed to read Destroyed value of player map ship in json file"))?;
+ ships.insert(ship_type, PlayerShip {
+ destroyed: destroyed
+ });
+ }
+
+ let energy = json["Owner"]["Energy"]
+ .as_u16()
+ .ok_or(String::from("Did not find the energy in the state json file"))?;
+
+ Ok(PlayerMap {
+ ships: ships,
+ energy: energy
+ })
+ }
+}
+
+
+pub struct PlayerShip {
+ pub destroyed: bool
+}
diff --git a/2018-tower-defence/.gitignore b/2018-tower-defence/.gitignore
new file mode 100644
index 0000000..54a07fd
--- /dev/null
+++ b/2018-tower-defence/.gitignore
@@ -0,0 +1,13 @@
+target
+command.txt
+state.json
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+/perf.data
+/perf.data.old
+/submission.zip
diff --git a/2018-tower-defence/Cargo.toml b/2018-tower-defence/Cargo.toml
new file mode 100644
index 0000000..120aa54
--- /dev/null
+++ b/2018-tower-defence/Cargo.toml
@@ -0,0 +1,38 @@
+[package]
+name = "zombot"
+version = "3.0.0"
+
+[dependencies]
+serde_derive = "1.0.71"
+serde = "1.0.71"
+serde_json = "1.0.26"
+
+rand = "0.5.5"
+time = "0.1.4"
+rayon = "1.0.2"
+
+arrayvec = "0.4.7"
+
+lazy_static = { version = "1.1.0", optional = true }
+
+[dev-dependencies]
+proptest = "0.8.4"
+
+[features]
+benchmarking = []
+single-threaded = []
+debug-decisions = []
+reduced-time = []
+extended-time = []
+
+energy-cutoff = []
+discard-poor-performers = []
+heuristic-random = ["lazy_static"]
+full-monte-carlo-tree = []
+static-opening = []
+weighted-win-ratio = []
+
+default = ["energy-cutoff", "discard-poor-performers", "static-opening", "weighted-win-ratio"]
+
+[profile.release]
+debug = true
diff --git a/2018-tower-defence/Makefile b/2018-tower-defence/Makefile
new file mode 100644
index 0000000..b5005da
--- /dev/null
+++ b/2018-tower-defence/Makefile
@@ -0,0 +1,24 @@
+default: build
+
+build:
+ cargo build --release
+
+test:
+ cargo test --release
+
+bench:
+ cargo run --release --features "benchmarking" --bin perf-test
+
+profile:
+ cargo build --release --features "benchmarking single-threaded extended-time"
+ mkdir -p target/profile
+ perf record -g target/release/perf-test
+ perf report
+
+clean:
+ cargo clean
+
+submission.zip: bot.json Cargo.lock Cargo.toml src
+ zip -r9 submission.zip bot.json Cargo.lock Cargo.toml src
+
+.PHONY: default build test bench profile clean
diff --git a/2018-tower-defence/bot.json b/2018-tower-defence/bot.json
new file mode 100644
index 0000000..14ed686
--- /dev/null
+++ b/2018-tower-defence/bot.json
@@ -0,0 +1,8 @@
+{
+ "author": "Justin Worthe",
+ "email": "justin@worthe-it.co.za",
+ "nickName": "Justin",
+ "botLocation": "/target/release/",
+ "botFileName": "zombot",
+ "botLanguage": "rust"
+}
diff --git a/2018-tower-defence/import-replay.sh b/2018-tower-defence/import-replay.sh
new file mode 100755
index 0000000..2a1b27e
--- /dev/null
+++ b/2018-tower-defence/import-replay.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+REPLAY_FOLDER=$1
+OUTPUT_FOLDER=$2
+
+mkdir -p $OUTPUT_FOLDER
+
+for round_folder in $REPLAY_FOLDER/*; do
+ round_name=`basename "$round_folder"`
+ mkdir -p "$OUTPUT_FOLDER/$round_name"
+
+ player_folders=( "$round_folder"/* )
+ player_folder=${player_folders[0]}
+ cp "$player_folder/JsonMap.json" "$OUTPUT_FOLDER/$round_name/state.json"
+ cp "$player_folder/PlayerCommand.txt" "$OUTPUT_FOLDER/$round_name/PlayerCommand.txt"
+
+ opponent_folder=${player_folders[1]}
+ cp "$opponent_folder/PlayerCommand.txt" "$OUTPUT_FOLDER/$round_name/OpponentCommand.txt"
+done
diff --git a/2018-tower-defence/license.org b/2018-tower-defence/license.org
new file mode 100644
index 0000000..d643604
--- /dev/null
+++ b/2018-tower-defence/license.org
@@ -0,0 +1,22 @@
+* The MIT License
+
+Copyright 2018 Justin Worthe
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/2018-tower-defence/readme.org b/2018-tower-defence/readme.org
new file mode 100644
index 0000000..e947202
--- /dev/null
+++ b/2018-tower-defence/readme.org
@@ -0,0 +1,52 @@
+* Entelect Challenge 2018 - Tower Defence - Rustbot
+
+This is the source code for my [[https://challenge.entelect.co.za/][Entelect Challenge]] 2018 bot. It did
+really well, coming in 3rd place in the finals.
+
+** How does it work?
+
+I've put together a blog post with the high level overview of how I
+got to this point and how it works [[https://www.offerzen.com/blog/coding-for-the-win-how-i-built-a-tower-defence-bot][here]]. I will be putting up more
+articles diving into the details shortly.
+
+The short explanation is that it's a Monte Carlo Tree Search. All
+possible moved I can make from the first state are generated. I then
+iterate through the list of possible moved and play random games that
+start with that move. The move that statistically wins the most random
+games is taken as the best move.
+
+** Environment Setup
+
+The Rust compiler tool-chain can be downloaded from the Rust project
+website.
+
+https://www.rust-lang.org/en-US/install.html
+
+** Compilation
+
+The bot is written in Rust, and compiled using Cargo. For the sake of
+running the bot in the tournament, you have to compile using the
+~--release~ flag (this is specified in [[./bot.json]]).
+
+#+BEGIN_SRC shell
+ cargo build --release
+#+END_SRC
+
+After compilation, there will be an executable in ~target/release/~.
+
+** Other useful commands
+
+You can find other interesting commands that I used in writing the bot
+in the [[./Makefile]]. Some notable ones are:
+
+- ~make bench~: compiles with the benchmarking feature turned on, and
+ runs my end to end benchmark.
+- ~make profile~: similar to the benchmark, but runs single threaded,
+ for a longer time, and uses ~perf~ to gather statistics on the run.
+- ~make submission.zip~: Creates the zip file to upload to the
+ Entelect Challenge servers.
+
+** License
+
+See [[./license.org]]
+
diff --git a/2018-tower-defence/src/bin/perf-test.rs b/2018-tower-defence/src/bin/perf-test.rs
new file mode 100644
index 0000000..ee0c2be
--- /dev/null
+++ b/2018-tower-defence/src/bin/perf-test.rs
@@ -0,0 +1,26 @@
+extern crate zombot;
+extern crate time;
+use time::{PreciseTime, Duration};
+
+use zombot::*;
+use zombot::engine::constants::*;
+
+const STATE_PATH: &str = "tests/state0.json";
+
+use std::process;
+
+fn main() {
+ println!("Running bitwise engine");
+ let start_time = PreciseTime::now();
+ let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+ Ok(ok) => ok,
+ Err(error) => {
+ println!("Error while parsing JSON file: {}", error);
+ process::exit(1);
+ }
+ };
+ let max_time = Duration::milliseconds(MAX_TIME_MILLIS);
+
+ #[cfg(feature = "full-monte-carlo-tree")] strategy::monte_carlo_tree::choose_move(&state, start_time, max_time);
+ #[cfg(not(feature = "full-monte-carlo-tree"))] strategy::monte_carlo::choose_move(&state, start_time, max_time);
+}
diff --git a/2018-tower-defence/src/engine/bitwise_engine.rs b/2018-tower-defence/src/engine/bitwise_engine.rs
new file mode 100644
index 0000000..694a309
--- /dev/null
+++ b/2018-tower-defence/src/engine/bitwise_engine.rs
@@ -0,0 +1,483 @@
+use engine::command::{Command, BuildingType};
+use engine::geometry::Point;
+use engine::constants::*;
+use engine::status::GameStatus;
+
+use arrayvec::ArrayVec;
+
+const LEFT_COL_MASK: u64 = 0x0101_0101_0101_0101;
+const RIGHT_COL_MASK: u64 = 0x8080_8080_8080_8080;
+
+const ROW_MASKS: [u64; MAP_HEIGHT as usize] = [
+ 0x0000_0000_0000_00ff,
+ 0x0000_0000_0000_ff00,
+ 0x0000_0000_00ff_0000,
+ 0x0000_0000_ff00_0000,
+ 0x0000_00ff_0000_0000,
+ 0x0000_ff00_0000_0000,
+ 0x00ff_0000_0000_0000,
+ 0xff00_0000_0000_0000,
+];
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct BitwiseGameState {
+ pub status: GameStatus,
+ pub player: Player,
+ pub opponent: Player,
+ pub round: u16
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Player {
+ pub energy: u16,
+ pub health: u8,
+ pub unconstructed: ArrayVec<[UnconstructedBuilding; MAX_CONCURRENT_CONSTRUCTION]>,
+ pub buildings: [u64; DEFENCE_HEALTH],
+ pub occupied: u64,
+
+ pub energy_towers: u64,
+
+ pub missile_towers: [u64; MISSILE_COOLDOWN_STATES],
+ pub firing_tower: usize,
+
+ pub missiles: [(u64, u64); MISSILE_MAX_SINGLE_CELL],
+ pub tesla_cooldowns: ArrayVec<[TeslaCooldown; TESLA_MAX]>,
+
+ pub iron_curtain_available: bool,
+ pub iron_curtain_remaining: u8,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UnconstructedBuilding {
+ pub pos: Point,
+ pub construction_time_left: u8,
+ pub building_type: BuildingType
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct TeslaCooldown {
+ pub pos: Point,
+ pub cooldown: u8,
+ pub age: u16
+}
+
+
+impl BitwiseGameState {
+ pub fn simulate(&mut self, player_command: Command, opponent_command: Command) -> GameStatus {
+ self.player.perform_command(player_command);
+ self.opponent.perform_command(opponent_command);
+
+ self.player.update_construction();
+ self.opponent.update_construction();
+
+ self.player.add_missiles();
+ self.opponent.add_missiles();
+
+ BitwiseGameState::fire_teslas(&mut self.player, &mut self.opponent);
+
+ BitwiseGameState::move_and_collide_missiles(&mut self.player, &mut self.opponent.missiles);
+ BitwiseGameState::move_and_collide_missiles(&mut self.opponent, &mut self.player.missiles);
+
+ BitwiseGameState::add_energy(&mut self.player);
+ BitwiseGameState::add_energy(&mut self.opponent);
+
+ BitwiseGameState::update_iron_curtain(&mut self.player, self.round);
+ BitwiseGameState::update_iron_curtain(&mut self.opponent, self.round);
+
+ self.round += 1;
+
+ self.update_status();
+ self.status
+ }
+}
+
+fn find_bit_index_from_rank(occupied: u64, i: u64) -> u8 {
+ // Adapted from https://graphics.stanford.edu/~seander/bithacks.html#SelectPosFromMSBRank
+ let v = !occupied;
+
+ let mut r = u64::from(v.count_ones()) - i;
+
+ let a: u64 = v - ((v >> 1) & (!0u64/3));
+ let b: u64 = (a & (!0u64/5)) + ((a >> 2) & (!0u64/5));
+ let c: u64 = (b + (b >> 4)) & (!0u64/0x11);
+ let d: u64 = (c + (c >> 8)) & (!0u64/0x101);
+ let mut t: u64 = (d >> 32) + (d >> 48);
+
+ let mut s: u64 = 64;
+ s -= (t.wrapping_sub(r) & 256) >> 3; r -= t & (t.wrapping_sub(r) >> 8);
+ t = (d >> (s - 16)) & 0xff;
+ s -= (t.wrapping_sub(r) & 256) >> 4; r -= t & (t.wrapping_sub(r) >> 8);
+ t = (c >> (s - 8)) & 0xf;
+ s -= (t.wrapping_sub(r) & 256) >> 5; r -= t & (t.wrapping_sub(r) >> 8);
+ t = (b >> (s - 4)) & 0x7;
+ s -= (t.wrapping_sub(r) & 256) >> 6; r -= t & (t.wrapping_sub(r) >> 8);
+ t = (a >> (s - 2)) & 0x3;
+ s -= (t.wrapping_sub(r) & 256) >> 7; r -= t & (t.wrapping_sub(r) >> 8);
+ t = (v >> (s - 1)) & 0x1;
+ s -= (t.wrapping_sub(r) & 256) >> 8;
+ s = 65 - s;
+
+ 64 - s as u8
+}
+
+impl BitwiseGameState {
+ pub fn new(
+ player: Player, opponent: Player,
+ round: u16
+ ) -> BitwiseGameState {
+ BitwiseGameState {
+ status: GameStatus::Continue,
+ player, opponent,
+ round
+ }
+ }
+
+ /**
+ * This is to make things more comparable when writing tests, not
+ * for actual use in the engine.
+ */
+ #[cfg(debug_assertions)]
+ pub fn sort(&mut self) {
+ for i in 0..MISSILE_MAX_SINGLE_CELL {
+ for j in i+1..MISSILE_MAX_SINGLE_CELL {
+ let move_down1 = !self.player.missiles[i].0 & self.player.missiles[j].0;
+ self.player.missiles[i].0 |= move_down1;
+ self.player.missiles[j].0 &= !move_down1;
+
+ let move_down2 = !self.player.missiles[i].1 & self.player.missiles[j].1;
+ self.player.missiles[i].1 |= move_down2;
+ self.player.missiles[j].1 &= !move_down2;
+
+ let move_down3 = !self.opponent.missiles[i].0 & self.opponent.missiles[j].0;
+ self.opponent.missiles[i].0 |= move_down3;
+ self.opponent.missiles[j].0 &= !move_down3;
+
+ let move_down4 = !self.opponent.missiles[i].1 & self.opponent.missiles[j].1;
+ self.opponent.missiles[i].1 |= move_down4;
+ self.opponent.missiles[j].1 &= !move_down4;
+ }
+ }
+
+ self.player.unconstructed.sort_by_key(|b| b.pos);
+ self.opponent.unconstructed.sort_by_key(|b| b.pos);
+
+ self.player.tesla_cooldowns.sort_by_key(|b| b.pos);
+ self.opponent.tesla_cooldowns.sort_by_key(|b| b.pos);
+
+
+ while self.player.firing_tower > 0 {
+ self.player.firing_tower -= 1;
+ let zero = self.player.missile_towers[0];
+ for i in 1..self.player.missile_towers.len() {
+ self.player.missile_towers[i-1] = self.player.missile_towers[i];
+ }
+ let end = self.player.missile_towers.len()-1;
+ self.player.missile_towers[end] = zero;
+ }
+ while self.opponent.firing_tower > 0 {
+ self.opponent.firing_tower -= 1;
+ let zero = self.opponent.missile_towers[0];
+ for i in 1..self.opponent.missile_towers.len() {
+ self.opponent.missile_towers[i-1] = self.opponent.missile_towers[i];
+ }
+ let end = self.opponent.missile_towers.len()-1;
+ self.opponent.missile_towers[end] = zero;
+ }
+ }
+
+ #[cfg(debug_assertions)]
+ pub fn sorted(&self) -> BitwiseGameState {
+ let mut res = self.clone();
+ res.sort();
+ res
+ }
+
+ fn update_iron_curtain(player: &mut Player, round: u16) {
+ if round != 0 && round % IRON_CURTAIN_UNLOCK_INTERVAL == 0 {
+ player.iron_curtain_available = true;
+ }
+ player.iron_curtain_remaining = player.iron_curtain_remaining.saturating_sub(1);
+ }
+
+ fn fire_teslas(player: &mut Player, opponent: &mut Player) {
+ BitwiseGameState::fire_single_players_teslas_without_cleanup(player, opponent);
+ BitwiseGameState::fire_single_players_teslas_without_cleanup(opponent, player);
+
+ BitwiseGameState::update_tesla_activity(player);
+ BitwiseGameState::update_tesla_activity(opponent);
+ }
+
+ fn fire_single_players_teslas_without_cleanup(player: &mut Player, opponent: &mut Player) {
+ // It's technically more accurate to have this in, but for
+ // most practical purposes it's a moot point and it's faster
+ // without it.
+ //
+ // player.tesla_cooldowns.sort_unstable_by(|a, b| b.age.cmp(&a.age));
+ for tesla in player.tesla_cooldowns.iter_mut() {
+ tesla.age += 1;
+ if tesla.cooldown > 0 {
+ tesla.cooldown -= 1;
+ } else if player.energy >= TESLA_FIRING_ENERGY && opponent.iron_curtain_remaining > 0 {
+ player.energy -= TESLA_FIRING_ENERGY;
+ tesla.cooldown = TESLA_COOLDOWN;
+ } else if player.energy >= TESLA_FIRING_ENERGY {
+ player.energy -= TESLA_FIRING_ENERGY;
+ tesla.cooldown = TESLA_COOLDOWN;
+
+ if tesla.pos.to_bitfield() & RIGHT_COL_MASK != 0 {
+ opponent.health = opponent.health.saturating_sub(TESLA_DAMAGE);
+ }
+
+ let x = tesla.pos.x();
+ let y = tesla.pos.y();
+ let missed_cells = (u32::from(SINGLE_MAP_WIDTH - x)).saturating_sub(2);
+
+ let top_row = y.saturating_sub(1);
+ let top_row_mask = ROW_MASKS[top_row as usize];
+ let mut destroy_mask = top_row_mask.wrapping_shl(missed_cells) & top_row_mask;
+
+ let mut hits = 0;
+ for _ in 0..(if y == 0 || y == MAP_HEIGHT-1 { 2 } else { 3 }) {
+ hits |= destroy_mask & opponent.buildings[0];
+ destroy_mask &= !hits;
+ destroy_mask <<= SINGLE_MAP_WIDTH;
+ }
+ BitwiseGameState::destroy_buildings(opponent, hits);
+ }
+ }
+ }
+
+ fn move_and_collide_missiles(opponent: &mut Player, player_missiles: &mut [(u64, u64); MISSILE_MAX_SINGLE_CELL]) {
+ let mut destroyed = 0;
+ let mut damaging = 0;
+ for _ in 0..MISSILE_SPEED {
+ for missile in player_missiles.iter_mut() {
+ let swapping_sides = if opponent.iron_curtain_remaining > 0 { 0 } else { missile.0 & RIGHT_COL_MASK };
+ let about_to_hit_opponent = missile.1 & LEFT_COL_MASK;
+
+ missile.0 = (missile.0 & !RIGHT_COL_MASK) << 1;
+ missile.1 = ((missile.1 & !LEFT_COL_MASK) >> 1) | swapping_sides;
+
+ damaging = (damaging << 1) | about_to_hit_opponent;
+
+ let mut hits = 0;
+ for health_tier in (0..DEFENCE_HEALTH).rev() {
+ hits = opponent.buildings[health_tier] & missile.1;
+ missile.1 &= !hits;
+ opponent.buildings[health_tier] &= !hits;
+ }
+ destroyed |= hits;
+ }
+ }
+ let damage = damaging.count_ones() as u8 * MISSILE_DAMAGE;
+ opponent.health = opponent.health.saturating_sub(damage);
+
+ BitwiseGameState::destroy_buildings(opponent, destroyed);
+ BitwiseGameState::update_tesla_activity(opponent);
+ }
+
+ fn destroy_buildings(buildings: &mut Player, hit_mask: u64) {
+ let deconstruct_mask = !hit_mask;
+
+ buildings.energy_towers &= deconstruct_mask;
+ for tier in &mut buildings.missile_towers {
+ *tier &= deconstruct_mask;
+ }
+ for tier in &mut buildings.buildings {
+ *tier &= deconstruct_mask;
+ }
+ buildings.occupied &= deconstruct_mask;
+ }
+
+ fn update_tesla_activity(buildings: &mut Player) {
+ let occupied = buildings.occupied;
+ buildings.tesla_cooldowns.retain(|t| (t.pos.to_bitfield() & occupied) != 0);
+ }
+
+
+ fn add_energy(player: &mut Player) {
+ player.energy += player.energy_generated();
+ }
+
+ fn update_status(&mut self) {
+ let player_dead = self.player.health == 0;
+ let opponent_dead = self.opponent.health == 0;
+ self.status = match (player_dead, opponent_dead) {
+ (true, true) => GameStatus::Draw,
+ (false, true) => GameStatus::PlayerWon,
+ (true, false) => GameStatus::OpponentWon,
+ (false, false) => GameStatus::Continue,
+ };
+ }
+
+}
+
+impl Player {
+ pub fn count_teslas(&self) -> usize {
+ self.tesla_cooldowns.len()
+ + self.unconstructed.iter().filter(|t| t.building_type == BuildingType::Tesla).count()
+ }
+
+ pub fn empty() -> Player {
+ Player {
+ health: 0,
+ energy: 0,
+ unconstructed: ArrayVec::new(),
+ buildings: [0; DEFENCE_HEALTH],
+ occupied: 0,
+ energy_towers: 0,
+ missile_towers: [0; MISSILE_COOLDOWN_STATES],
+ firing_tower: 0,
+ missiles: [(0,0); MISSILE_MAX_SINGLE_CELL],
+ tesla_cooldowns: ArrayVec::new(),
+ iron_curtain_available: false,
+ iron_curtain_remaining: 0,
+ }
+ }
+
+ pub fn energy_generated(&self) -> u16 {
+ ENERGY_GENERATED_BASE + self.energy_towers.count_ones() as u16 * ENERGY_GENERATED_TOWER
+ }
+
+ pub fn has_max_teslas(&self) -> bool {
+ self.count_teslas() >= TESLA_MAX
+ }
+
+ pub fn can_build_iron_curtain(&self) -> bool {
+ self.iron_curtain_available && self.iron_curtain_remaining == 0
+ }
+
+ pub fn can_build_iron_curtain_in(&self, round: u16, moves: u8) -> bool {
+ let unlocks = round % IRON_CURTAIN_UNLOCK_INTERVAL > round + u16::from(moves) % IRON_CURTAIN_UNLOCK_INTERVAL;
+ (self.iron_curtain_available || unlocks) && self.iron_curtain_remaining.saturating_sub(moves) == 0
+ }
+
+ pub fn unoccupied_cell_count(&self) -> usize { self.occupied.count_zeros() as usize }
+ pub fn location_of_unoccupied_cell(&self, i: usize) -> Point {
+ let bit = find_bit_index_from_rank(self.occupied, i as u64);
+ let point = Point { index: bit };
+ debug_assert!(point.to_bitfield() & self.occupied == 0);
+ point
+ }
+
+
+ fn perform_command(&mut self, command: Command) {
+ match command {
+ Command::Nothing => {},
+ Command::Build(p, b) => {
+ let bitfield = p.to_bitfield();
+
+ let price = match b {
+ BuildingType::Attack => MISSILE_PRICE,
+ BuildingType::Defence => DEFENCE_PRICE,
+ BuildingType::Energy => ENERGY_PRICE,
+ BuildingType::Tesla => TESLA_PRICE,
+ };
+ let construction_time = match b {
+ BuildingType::Attack => MISSILE_CONSTRUCTION_TIME,
+ BuildingType::Defence => DEFENCE_CONSTRUCTION_TIME,
+ BuildingType::Energy => ENERGY_CONSTRUCTION_TIME,
+ BuildingType::Tesla => TESLA_CONSTRUCTION_TIME,
+ };
+
+ // This is used internally. I should not be making
+ // invalid moves!
+ debug_assert!(self.buildings[0] & bitfield == 0);
+ debug_assert!(p.x() < FULL_MAP_WIDTH && p.y() < MAP_HEIGHT);
+ debug_assert!(self.energy >= price);
+ debug_assert!(b != BuildingType::Tesla ||
+ self.count_teslas() < TESLA_MAX);
+
+ self.energy -= price;
+ self.unconstructed.push(UnconstructedBuilding {
+ pos: p,
+ construction_time_left: construction_time,
+ building_type: b
+ });
+ self.occupied |= bitfield;
+ },
+ Command::IronCurtain => {
+ debug_assert!(self.iron_curtain_available);
+ debug_assert!(self.energy >= IRON_CURTAIN_PRICE);
+
+ self.energy -= IRON_CURTAIN_PRICE;
+ self.iron_curtain_available = false;
+ self.iron_curtain_remaining = IRON_CURTAIN_DURATION;
+ }
+ }
+ }
+
+ fn update_construction(&mut self) {
+ let mut buildings_len = self.unconstructed.len();
+ for i in (0..buildings_len).rev() {
+ if self.unconstructed[i].construction_time_left == 0 {
+ let building_type = self.unconstructed[i].building_type;
+ let health = if building_type == BuildingType::Defence { DEFENCE_HEALTH } else { 1 };
+
+ let pos = self.unconstructed[i].pos;
+ let bitfield = pos.to_bitfield();
+
+ for health_tier in 0..health {
+ self.buildings[health_tier] |= bitfield;
+ }
+ if building_type == BuildingType::Energy {
+ self.energy_towers |= bitfield;
+ }
+ if building_type == BuildingType::Attack {
+ self.missile_towers[self.firing_tower] |= bitfield;
+ }
+ if building_type == BuildingType::Tesla {
+ self.tesla_cooldowns.push(TeslaCooldown {
+ pos,
+ cooldown: 0,
+ age: 0
+ });
+ }
+
+ buildings_len -= 1;
+ self.unconstructed.swap(i, buildings_len);
+ } else {
+ self.unconstructed[i].construction_time_left -= 1
+ }
+ }
+ self.unconstructed.truncate(buildings_len);
+ }
+
+ fn add_missiles(&mut self) {
+ let mut missiles = self.missile_towers[self.firing_tower];
+ for mut tier in &mut self.missiles {
+ let setting = !tier.0 & missiles;
+ tier.0 |= setting;
+ missiles &= !setting;
+ }
+ self.firing_tower = (self.firing_tower + 1) % MISSILE_COOLDOWN_STATES;
+ }
+
+ fn any_missile_towers(&self) -> u64 {
+ self.missile_towers.iter().fold(0, |acc, next| acc | next)
+ }
+
+ pub fn count_attack_towers_in_row(&self, y: u8) -> u16 {
+ let mask = ROW_MASKS[y as usize];
+ (self.any_missile_towers() & mask).count_ones() as u16
+ }
+
+ pub fn count_energy_towers_in_row(&self, y: u8) -> u16 {
+ let mask = ROW_MASKS[y as usize];
+ (self.energy_towers & mask).count_ones() as u16
+ }
+
+ pub fn count_healthy_defence_in_row(&self, y: u8) -> u16 {
+ let mask = ROW_MASKS[y as usize];
+ (self.buildings[1] & mask).count_ones() as u16
+ }
+
+ pub fn count_towers_in_row(&self, y: u8) -> u16 {
+ let mask = ROW_MASKS[y as usize];
+ (self.occupied & mask).count_ones() as u16
+ }
+
+ pub fn count_towers(&self) -> u32 {
+ self.occupied.count_ones()
+ }
+}
diff --git a/2018-tower-defence/src/engine/command.rs b/2018-tower-defence/src/engine/command.rs
new file mode 100644
index 0000000..76cfaee
--- /dev/null
+++ b/2018-tower-defence/src/engine/command.rs
@@ -0,0 +1,66 @@
+use std::fmt;
+use super::constants::*;
+use super::geometry::Point;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Command {
+ Nothing,
+ Build(Point, BuildingType),
+ IronCurtain
+}
+
+impl fmt::Display for Command {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Command::Nothing => write!(f, ""),
+ Command::Build(p, b) => write!(f, "{},{},{}", p.x(), p.y(), b as u8),
+ Command::IronCurtain => write!(f, "0,0,5")
+ }
+ }
+}
+
+impl Command {
+ pub fn cant_build_yet(self, energy: u16) -> bool {
+ use self::Command::*;
+
+ match self {
+ Nothing => false,
+ Build(_, b) => b.cant_build_yet(energy),
+ IronCurtain => energy < IRON_CURTAIN_PRICE
+ }
+ }
+}
+
+
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum BuildingType {
+ Defence = 0,
+ Attack = 1,
+ Energy = 2,
+ Tesla = 4,
+}
+
+impl BuildingType {
+ pub fn all() -> [BuildingType; NUMBER_OF_BUILDING_TYPES] {
+ use self::BuildingType::*;
+ [Defence, Attack, Energy, Tesla]
+ }
+
+ pub fn from_u8(id: u8) -> Option<BuildingType> {
+ use std::mem;
+ if id <= 4 && id != 3 { Some(unsafe { mem::transmute(id) }) } else { None }
+ }
+
+ pub fn cant_build_yet(self, energy: u16) -> bool {
+ use self::BuildingType::*;
+
+ let required = match self {
+ Defence => DEFENCE_PRICE,
+ Attack => MISSILE_PRICE,
+ Energy => ENERGY_PRICE,
+ Tesla => TESLA_PRICE
+ };
+ energy < required
+ }
+}
diff --git a/2018-tower-defence/src/engine/constants.rs b/2018-tower-defence/src/engine/constants.rs
new file mode 100644
index 0000000..a66c9e1
--- /dev/null
+++ b/2018-tower-defence/src/engine/constants.rs
@@ -0,0 +1,52 @@
+pub const FULL_MAP_WIDTH: u8 = 16;
+pub const SINGLE_MAP_WIDTH: u8 = FULL_MAP_WIDTH/2;
+pub const MAP_HEIGHT: u8 = 8;
+
+pub const MAX_MOVES: u16 = 400;
+pub const INIT_SEED: [u8;16] = [0x7b, 0x6a, 0xe1, 0xf4, 0x41, 0x3c, 0xe9, 0x0f, 0x67, 0x81, 0x67, 0x99, 0x77, 0x0a, 0x6b, 0xda];
+
+pub const MISSILE_COOLDOWN: usize = 3;
+pub const MISSILE_COOLDOWN_STATES: usize = MISSILE_COOLDOWN+1;
+pub const MISSILE_SPEED: usize = 2;
+pub const MISSILE_MAX_SINGLE_CELL: usize = SINGLE_MAP_WIDTH as usize / MISSILE_SPEED;
+pub const MISSILE_DAMAGE: u8 = 5;
+pub const MISSILE_PRICE: u16 = 30;
+pub const MISSILE_CONSTRUCTION_TIME: u8 = 1;
+
+pub const DEFENCE_HEALTH: usize = 4; // '20' health is 4 hits
+pub const DEFENCE_PRICE: u16 = 30;
+pub const DEFENCE_CONSTRUCTION_TIME: u8 = 3;
+
+pub const TESLA_MAX: usize = 2;
+pub const TESLA_COOLDOWN: u8 = 10;
+pub const TESLA_FIRING_ENERGY: u16 = 100;
+pub const TESLA_DAMAGE: u8 = 20;
+pub const TESLA_PRICE: u16 = 100;
+pub const TESLA_CONSTRUCTION_TIME: u8 = 10;
+
+pub const ENERGY_GENERATED_BASE: u16 = 5;
+pub const ENERGY_GENERATED_TOWER: u16 = 3;
+pub const ENERGY_PRICE: u16 = 20;
+pub const ENERGY_CONSTRUCTION_TIME: u8 = 1;
+
+pub const IRON_CURTAIN_PRICE: u16 = 100;
+pub const IRON_CURTAIN_UNLOCK_INTERVAL: u16 = 30;
+pub const IRON_CURTAIN_DURATION: u8 = 6;
+
+pub const DECONSTRUCT_ENERGY: u16 = 5;
+
+pub const MAX_CONCURRENT_CONSTRUCTION: usize = 6; //2 teslas, and 3 of anything else, 1 extra because it's push here then update construction times
+
+
+pub const NUMBER_OF_BUILDING_TYPES: usize = 4;
+pub const NUMBER_OF_MAP_POSITIONS: usize = SINGLE_MAP_WIDTH as usize * MAP_HEIGHT as usize;
+pub const NUMBER_OF_POSSIBLE_MOVES: usize = NUMBER_OF_MAP_POSITIONS * NUMBER_OF_BUILDING_TYPES + 2;
+
+#[cfg(not(any(feature = "reduced-time", feature = "extended-time")))]
+pub const MAX_TIME_MILLIS: i64 = 1950;
+
+#[cfg(feature = "reduced-time")]
+pub const MAX_TIME_MILLIS: i64 = 950;
+
+#[cfg(feature = "extended-time")]
+pub const MAX_TIME_MILLIS: i64 = 19950;
diff --git a/2018-tower-defence/src/engine/geometry.rs b/2018-tower-defence/src/engine/geometry.rs
new file mode 100644
index 0000000..9cd1d90
--- /dev/null
+++ b/2018-tower-defence/src/engine/geometry.rs
@@ -0,0 +1,71 @@
+use engine::constants::*;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Point {
+ pub index: u8
+}
+
+impl Point {
+ pub fn new(x: u8, y: u8) -> Point {
+ let flipped_x = if x >= SINGLE_MAP_WIDTH {
+ FULL_MAP_WIDTH - x - 1
+ } else {
+ x
+ };
+ Point {
+ index: y * SINGLE_MAP_WIDTH + flipped_x
+ }
+ }
+
+ pub fn new_index(index: u8) -> Point {
+ Point {
+ index
+ }
+ }
+
+ pub fn new_double_bitfield(x: u8, y: u8, is_left_player: bool) -> (u64, u64) {
+ let bitfield = Point::new(x, y).to_bitfield();
+ if (x >= SINGLE_MAP_WIDTH) == is_left_player {
+ (0, bitfield)
+ } else {
+ (bitfield, 0)
+ }
+ }
+
+ pub fn x(self) -> u8 {
+ self.index % SINGLE_MAP_WIDTH
+ }
+
+ pub fn y(self) -> u8 {
+ self.index / SINGLE_MAP_WIDTH
+ }
+}
+
+impl Point {
+ /**
+ * # Bitfields
+ *
+ * 0,0 is the top left point.
+ * >> (towards 0) moves bits towards the player that owns that side
+ * << (towards max) moves bits towards the opponent
+ * This involves mirroring the x dimension for the opponent's side
+ */
+
+ pub fn to_bitfield(self) -> u64 {
+ 1u64 << self.index
+ }
+}
+
+use std::cmp::Ord;
+use std::cmp::Ordering;
+
+impl PartialOrd for Point {
+ fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl Ord for Point {
+ fn cmp(&self, other: &Point) -> Ordering {
+ self.index.cmp(&other.index)
+ }
+}
diff --git a/2018-tower-defence/src/engine/mod.rs b/2018-tower-defence/src/engine/mod.rs
new file mode 100644
index 0000000..f98ef6b
--- /dev/null
+++ b/2018-tower-defence/src/engine/mod.rs
@@ -0,0 +1,5 @@
+pub mod command;
+pub mod geometry;
+pub mod bitwise_engine;
+pub mod constants;
+pub mod status;
diff --git a/2018-tower-defence/src/engine/status.rs b/2018-tower-defence/src/engine/status.rs
new file mode 100644
index 0000000..d6ee4dd
--- /dev/null
+++ b/2018-tower-defence/src/engine/status.rs
@@ -0,0 +1,8 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GameStatus {
+ Continue,
+ PlayerWon,
+ OpponentWon,
+ Draw
+}
+
diff --git a/2018-tower-defence/src/input/json.rs b/2018-tower-defence/src/input/json.rs
new file mode 100644
index 0000000..a71d49e
--- /dev/null
+++ b/2018-tower-defence/src/input/json.rs
@@ -0,0 +1,191 @@
+use std::fs::File;
+use std::io::prelude::*;
+use serde_json;
+use std::error::Error;
+
+use engine;
+use engine::command;
+use engine::bitwise_engine;
+use engine::constants::*;
+
+pub fn read_bitwise_state_from_file(filename: &str) -> Result<bitwise_engine::BitwiseGameState, Box<Error>> {
+ let mut file = File::open(filename)?;
+ let mut content = String::new();
+ file.read_to_string(&mut content)?;
+ let state: State = serde_json::from_str(content.as_ref())?;
+
+ let engine_state = state.to_bitwise_engine();
+ Ok(engine_state)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct State {
+ game_details: GameDetails,
+ players: Vec<Player>,
+ game_map: Vec<Vec<GameCell>>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GameDetails {
+ round: u16
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Player {
+ player_type: char,
+ energy: u16,
+ health: u8,
+ iron_curtain_available: bool,
+ active_iron_curtain_lifetime: i16
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GameCell {
+ x: u8,
+ y: u8,
+ buildings: Vec<BuildingState>,
+ missiles: Vec<MissileState>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct BuildingState {
+ health: u8,
+ construction_time_left: i16,
+ weapon_cooldown_time_left: u8,
+ building_type: String,
+ x: u8,
+ y: u8,
+ player_type: char
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MissileState {
+ player_type: char
+}
+
+
+impl State {
+ fn to_bitwise_engine(&self) -> bitwise_engine::BitwiseGameState {
+ let mut player = bitwise_engine::Player::empty();
+ let mut opponent = bitwise_engine::Player::empty();
+
+ self.player().map_onto_engine(&mut player);
+ self.opponent().map_onto_engine(&mut opponent);
+
+ for row in &self.game_map {
+ for cell in row {
+ let point = engine::geometry::Point::new(cell.x, cell.y);
+ for building in &cell.buildings {
+ let building_type = building.convert_building_type();
+
+ let mut bitwise_buildings = if building.player_type == 'A' {
+ &mut player
+ } else {
+ &mut opponent
+ };
+ let bitfield = point.to_bitfield();
+
+ bitwise_buildings.occupied |= bitfield;
+ if building.construction_time_left >= 0 {
+ bitwise_buildings.unconstructed.push(building.to_bitwise_engine_unconstructed());
+ } else {
+ for health_tier in 0..DEFENCE_HEALTH {
+ if building.health > health_tier as u8 * MISSILE_DAMAGE {
+ bitwise_buildings.buildings[health_tier] |= bitfield;
+ }
+ }
+ if building_type == command::BuildingType::Energy {
+ bitwise_buildings.energy_towers |= bitfield;
+ }
+ else if building_type == command::BuildingType::Attack {
+ for cooldown_tier in 0..MISSILE_COOLDOWN + 1 {
+ if building.weapon_cooldown_time_left == cooldown_tier as u8 {
+ bitwise_buildings.missile_towers[cooldown_tier] |= bitfield;
+ }
+ }
+ }
+ else if building_type == command::BuildingType::Tesla {
+ bitwise_buildings.tesla_cooldowns.push(bitwise_engine::TeslaCooldown {
+ pos: point,
+ cooldown: building.weapon_cooldown_time_left,
+ age: building.construction_time_left.abs() as u16
+ });
+ }
+ }
+ }
+ for missile in &cell.missiles {
+ let (mut left, mut right) = engine::geometry::Point::new_double_bitfield(cell.x, cell.y, missile.player_type == 'A');
+ let mut bitwise_buildings = if missile.player_type == 'A' {
+ &mut player
+ } else {
+ &mut opponent
+ };
+
+ for mut tier in &mut bitwise_buildings.missiles {
+ let setting = (!tier.0 & left, !tier.1 & right);
+ tier.0 |= setting.0;
+ tier.1 |= setting.1;
+ left &= !setting.0;
+ right &= !setting.1;
+ }
+ }
+ }
+ }
+
+ bitwise_engine::BitwiseGameState::new(
+ player, opponent,
+ self.game_details.round
+ )
+ }
+
+ fn player(&self) -> &Player {
+ self.players.iter()
+ .find(|p| p.player_type == 'A')
+ .expect("Player character did not appear in state.json")
+ }
+
+ fn opponent(&self) -> &Player {
+ self.players.iter()
+ .find(|p| p.player_type == 'B')
+ .expect("Opponent character did not appear in state.json")
+ }
+}
+
+impl BuildingState {
+ fn to_bitwise_engine_unconstructed(&self) -> bitwise_engine::UnconstructedBuilding {
+ bitwise_engine::UnconstructedBuilding {
+ pos: engine::geometry::Point::new(self.x, self.y),
+ construction_time_left: self.construction_time_left as u8, // > 0 check already happened
+ building_type: self.convert_building_type()
+ }
+ }
+
+ fn convert_building_type(&self) -> command::BuildingType {
+ match self.building_type.as_ref() {
+ "ATTACK" => command::BuildingType::Attack,
+ "ENERGY" => command::BuildingType::Energy,
+ "TESLA" => command::BuildingType::Tesla,
+ _ => command::BuildingType::Defence,
+ }
+ }
+}
+
+
+impl Player {
+ fn map_onto_engine(&self, engine_player: &mut bitwise_engine::Player) {
+ engine_player.health = self.health;
+ engine_player.energy = self.energy;
+ engine_player.iron_curtain_available = self.iron_curtain_available;
+ engine_player.iron_curtain_remaining = if self.active_iron_curtain_lifetime < 0 {
+ 0
+ } else {
+ self.active_iron_curtain_lifetime as u8
+ };
+ }
+}
diff --git a/2018-tower-defence/src/input/mod.rs b/2018-tower-defence/src/input/mod.rs
new file mode 100644
index 0000000..22fdbb3
--- /dev/null
+++ b/2018-tower-defence/src/input/mod.rs
@@ -0,0 +1 @@
+pub mod json;
diff --git a/2018-tower-defence/src/lib.rs b/2018-tower-defence/src/lib.rs
new file mode 100644
index 0000000..6cd8730
--- /dev/null
+++ b/2018-tower-defence/src/lib.rs
@@ -0,0 +1,20 @@
+extern crate serde;
+extern crate serde_json;
+
+#[macro_use]
+extern crate serde_derive;
+
+extern crate rand;
+extern crate time;
+
+extern crate rayon;
+
+extern crate arrayvec;
+
+#[macro_use]
+#[cfg(feature = "heuristic-random")]
+extern crate lazy_static;
+
+pub mod input;
+pub mod engine;
+pub mod strategy;
diff --git a/2018-tower-defence/src/main.rs b/2018-tower-defence/src/main.rs
new file mode 100644
index 0000000..4fa0366
--- /dev/null
+++ b/2018-tower-defence/src/main.rs
@@ -0,0 +1,55 @@
+extern crate zombot;
+extern crate time;
+use time::{PreciseTime, Duration};
+
+use zombot::*;
+use zombot::engine::constants::*;
+use zombot::engine::command::Command;
+
+use std::error::Error;
+
+const STATE_PATH: &str = "state.json";
+
+const COMMAND_PATH: &str = "command.txt";
+
+use std::fs::File;
+use std::io::prelude::*;
+use std::process;
+
+fn write_command(filename: &str, command: Command) -> Result<(), Box<Error> > {
+ let mut file = File::create(filename)?;
+ write!(file, "{}", command)?;
+ Ok(())
+}
+
+fn main() {
+ let start_time = PreciseTime::now();
+ let max_time = Duration::milliseconds(MAX_TIME_MILLIS);
+
+ let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+ Ok(ok) => ok,
+ Err(error) => {
+ println!("Error while parsing JSON file: {}", error);
+ process::exit(1);
+ }
+ };
+
+ let command = if cfg!(feature = "static-opening") && state.round < strategy::static_opening::STATIC_OPENING_LENGTH {
+ strategy::static_opening::choose_move(&state)
+ } else if cfg!(feature = "full-monte-carlo-tree") {
+ strategy::monte_carlo_tree::choose_move(&state, start_time, max_time)
+ } else {
+ strategy::monte_carlo::choose_move(&state, start_time, max_time)
+ };
+
+ match write_command(COMMAND_PATH, command) {
+ Ok(()) => {}
+ Err(error) => {
+ println!("Error while writing command file: {}", error);
+ process::exit(1);
+ }
+ }
+
+ println!("Elapsed time: {}", start_time.to(PreciseTime::now()));
+}
+
diff --git a/2018-tower-defence/src/strategy/mod.rs b/2018-tower-defence/src/strategy/mod.rs
new file mode 100644
index 0000000..9ec41bb
--- /dev/null
+++ b/2018-tower-defence/src/strategy/mod.rs
@@ -0,0 +1,3 @@
+pub mod monte_carlo;
+pub mod monte_carlo_tree;
+pub mod static_opening;
diff --git a/2018-tower-defence/src/strategy/monte_carlo.rs b/2018-tower-defence/src/strategy/monte_carlo.rs
new file mode 100644
index 0000000..adbb911
--- /dev/null
+++ b/2018-tower-defence/src/strategy/monte_carlo.rs
@@ -0,0 +1,505 @@
+use engine::command::*;
+use engine::status::GameStatus;
+use engine::bitwise_engine::{Player, BitwiseGameState};
+use engine::constants::*;
+
+use std::fmt;
+
+use rand::{Rng, XorShiftRng, SeedableRng};
+
+use arrayvec::ArrayVec;
+
+use time::{Duration, PreciseTime};
+
+#[cfg(not(feature = "single-threaded"))]
+use rayon::prelude::*;
+
+#[cfg(feature = "energy-cutoff")] pub const ENERGY_PRODUCTION_CUTOFF: u16 = 50;
+#[cfg(feature = "energy-cutoff")] pub const ENERGY_STORAGE_CUTOFF: u16 = 120;
+
+pub fn choose_move(state: &BitwiseGameState, start_time: PreciseTime, max_time: Duration) -> Command {
+ let mut command_scores = CommandScore::init_command_scores(state);
+
+ let command = {
+ let best_command_score = simulate_options_to_timeout(&mut command_scores, state, start_time, max_time);
+ match best_command_score {
+ Some(best) if !best.starts_with_nothing => best.command,
+ _ => Command::Nothing
+ }
+ };
+
+ #[cfg(feature = "benchmarking")]
+ {
+ let total_iterations: u32 = command_scores.iter().map(|c| c.attempts).sum();
+ println!("Iterations: {}", total_iterations);
+ }
+ #[cfg(feature = "debug-decisions")]
+ {
+ debug_print_choices("ENERGY", &command_scores, |score| match score.command {
+ Command::Build(p, BuildingType::Energy) => Some((p, score.win_ratio())),
+ _ => None
+ });
+ debug_print_choices("ATTACK", &command_scores, |score| match score.command {
+ Command::Build(p, BuildingType::Attack) => Some((p, score.win_ratio())),
+ _ => None
+ });
+ debug_print_choices("DEFENCE", &command_scores, |score| match score.command {
+ Command::Build(p, BuildingType::Defence) => Some((p, score.win_ratio())),
+ _ => None
+ });
+ debug_print_choices("TESLA", &command_scores, |score| match score.command {
+ Command::Build(p, BuildingType::Tesla) => Some((p, score.win_ratio())),
+ _ => None
+ });
+
+ println!("NOTHING");
+ println!("{}", command_scores.iter().find(|c| c.command == Command::Nothing).map(|s| s.win_ratio()).unwrap_or(0));
+ println!();
+
+ println!("IRON CURTAIN");
+ println!("{}", command_scores.iter().find(|c| c.command == Command::IronCurtain).map(|s| s.win_ratio()).unwrap_or(0));
+ println!();
+ }
+
+ command
+}
+
+#[cfg(feature = "debug-decisions")]
+fn debug_print_choices<F: FnMut(&CommandScore) -> Option<(Point, i32)>>(label: &str, command_scores: &[CommandScore], extractor: F) {
+ println!("#+NAME: {}", label);
+ println!("#+PLOT: type:3d with:pm3d");
+ let relevant_moves: Vec<(Point, i32)> = command_scores.iter()
+ .filter_map(extractor)
+ .collect();
+ for y in 0..MAP_HEIGHT {
+ for x in 0..SINGLE_MAP_WIDTH {
+ let point = Point::new(x, y);
+ let score = relevant_moves.iter().find(|(p, _)| *p == point);
+ print!(" | {}", score.map(|(_,s)| s).cloned().unwrap_or(0));
+ }
+ println!(" |");
+ }
+ println!();
+}
+
+#[cfg(not(feature = "discard-poor-performers"))]
+fn simulate_options_to_timeout<'a>(command_scores: &'a mut Vec<CommandScore>, state: &BitwiseGameState, start_time: PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
+ loop {
+ simulate_all_options_once(command_scores, state);
+ if start_time.to(PreciseTime::now()) > max_time {
+ break;
+ }
+ }
+ command_scores.iter().max_by_key(|&c| c.win_ratio())
+}
+
+#[cfg(feature = "discard-poor-performers")]
+fn simulate_options_to_timeout<'a>(command_scores: &'a mut Vec<CommandScore>, state: &BitwiseGameState, start_time: PreciseTime, max_time: Duration) -> Option<&'a CommandScore> {
+ use std::cmp;
+ let min_options = cmp::min(command_scores.len(), 5);
+
+ let maxes = [max_time / 3, max_time * 2 / 3, max_time];
+ for (i, &max) in maxes.iter().enumerate() {
+ let new_length = cmp::max(min_options, command_scores.len() / (2usize.pow(i as u32)));
+ let active_scores = &mut command_scores[0..new_length];
+ loop {
+ simulate_all_options_once(active_scores, state);
+ if start_time.to(PreciseTime::now()) > max {
+ break;
+ }
+ }
+ active_scores.sort_unstable_by_key(|c| -c.win_ratio());
+ }
+ command_scores.first()
+}
+
+#[cfg(feature = "single-threaded")]
+fn simulate_all_options_once(command_scores: &mut[CommandScore], state: &BitwiseGameState) {
+ command_scores.iter_mut()
+ .for_each(|score| {
+ let mut rng = XorShiftRng::from_seed(score.next_seed);
+ simulate_to_endstate(score, state, &mut rng);
+ });
+}
+
+#[cfg(not(feature = "single-threaded"))]
+fn simulate_all_options_once(command_scores: &mut[CommandScore], state: &BitwiseGameState) {
+ command_scores.par_iter_mut()
+ .for_each(|score| {
+ let mut rng = XorShiftRng::from_seed(score.next_seed);
+ simulate_to_endstate(score, state, &mut rng);
+ });
+}
+
+fn simulate_to_endstate<R: Rng>(command_score: &mut CommandScore, state: &BitwiseGameState, rng: &mut R) {
+ let mut state_mut = state.clone();
+
+ let mut status = GameStatus::Continue; //state_mut.simulate(command_score.command, opponent_first);
+ let mut first_move_made = false;
+
+ for _ in 0..MAX_MOVES {
+ if status != GameStatus::Continue {
+ break;
+ }
+
+ let player_command = if first_move_made {
+ random_move(&state_mut.player, &state_mut.opponent, rng)
+ } else {
+ let do_nothing = command_score.command.cant_build_yet(state_mut.player.energy);
+ first_move_made = !do_nothing;
+ if do_nothing { Command::Nothing } else { command_score.command }
+ };
+ let opponent_command = random_move(&state_mut.opponent, &state_mut.player, rng);
+ status = state_mut.simulate(player_command, opponent_command);
+ }
+
+ let mut next_seed: [u8;16] = [0; 16];
+ rng.fill_bytes(&mut next_seed);
+ match status {
+ GameStatus::PlayerWon => command_score.add_victory(state_mut.player.count_towers() as i32 - state_mut.opponent.count_towers() as i32, next_seed),
+ GameStatus::OpponentWon => command_score.add_defeat(state_mut.opponent.count_towers() as i32 - state_mut.player.count_towers() as i32, next_seed),
+ GameStatus::Continue => command_score.add_stalemate(next_seed),
+ GameStatus::Draw => command_score.add_draw(next_seed)
+ }
+}
+
+#[cfg(feature = "heuristic-random")]
+pub fn random_move<R: Rng>(player: &Player, opponent: &Player, rng: &mut R) -> Command {
+ lazy_static! {
+ static ref MOVES: [Command; NUMBER_OF_POSSIBLE_MOVES] = {
+ let mut m = [Command::Nothing; NUMBER_OF_POSSIBLE_MOVES];
+ m[1] = Command::IronCurtain;
+ let mut i = 2;
+ for b in &[BuildingType::Energy, BuildingType::Defence, BuildingType::Attack, BuildingType::Tesla] {
+ for p in 0..NUMBER_OF_MAP_POSITIONS as u8 {
+ let point = Point::new_index(p);
+ m[i] = Command::Build(point, *b);
+ i += 1;
+ }
+ }
+ m
+ };
+ }
+
+ let mut cdf_other = [0; 2];
+ let mut cdf_energy = [0; NUMBER_OF_MAP_POSITIONS];
+ let mut cdf_defence = [0; NUMBER_OF_MAP_POSITIONS];
+ let mut cdf_attack = [0; NUMBER_OF_MAP_POSITIONS];
+ let mut cdf_tesla = [0; NUMBER_OF_MAP_POSITIONS];
+
+ let mut attack_metric_per_row = [0; MAP_HEIGHT as usize];
+ let mut defence_metric_per_row = [0; MAP_HEIGHT as usize];
+ for y in 0..MAP_HEIGHT {
+ let opponent_energy = opponent.count_energy_towers_in_row(y);
+ let opponent_attack = opponent.count_attack_towers_in_row(y);
+ let opponent_towers = opponent.count_towers_in_row(y);
+
+ let player_energy = player.count_energy_towers_in_row(y);
+ let player_attack = player.count_attack_towers_in_row(y);
+ let player_towers = player.count_towers_in_row(y);
+
+ defence_metric_per_row[y as usize] = if opponent_attack == 0 { 0 } else { opponent_attack + player_towers };
+ attack_metric_per_row[y as usize] = 8 + opponent_energy + opponent_towers + player_energy - player_attack;
+ }
+
+
+ let mut other_end: u16 = 0;
+ // Nothing
+ {
+ let weight = if player.can_build_iron_curtain() && player.energy < IRON_CURTAIN_PRICE {
+ 5
+ } else {
+ 0
+ };
+ other_end += weight;
+ cdf_other[0] = other_end;
+ }
+
+ // Iron Curtain
+ {
+ let weight = if player.can_build_iron_curtain() && player.energy >= IRON_CURTAIN_PRICE {
+ 50
+ } else {
+ 0
+ };
+ other_end += weight;
+ cdf_other[1] = other_end;
+ }
+
+ // Energy
+ let mut energy_end: u16 = other_end;
+ let needs_energy = player.energy_generated() <= ENERGY_PRODUCTION_CUTOFF ||
+ player.energy <= ENERGY_STORAGE_CUTOFF;
+ if needs_energy && player.energy >= ENERGY_PRICE {
+ for p in 0..NUMBER_OF_MAP_POSITIONS as u8 {
+ let point = Point::new_index(p);
+ let weight = if player.occupied & point.to_bitfield() != 0 {
+ 0
+ } else {
+ 2
+ };
+
+ energy_end += weight;
+ cdf_energy[p as usize] = energy_end;
+ }
+ }
+
+ // Defence
+ let mut defence_end: u16 = energy_end;
+ if player.energy >= DEFENCE_PRICE {
+ for p in 0..NUMBER_OF_MAP_POSITIONS as u8 {
+ let point = Point::new_index(p);
+ let y = usize::from(point.y());
+
+ let weight = if player.occupied & point.to_bitfield() != 0 || point.x() < 4 {
+ 0
+ } else {
+ defence_metric_per_row[y]
+ };
+
+ defence_end += weight;
+ cdf_defence[p as usize] = defence_end;
+ }
+ }
+
+ // Attack
+ let mut attack_end: u16 = defence_end;
+ if player.energy >= MISSILE_PRICE {
+ for p in 0..NUMBER_OF_MAP_POSITIONS as u8 {
+ let point = Point::new_index(p);
+ let weight = if player.occupied & point.to_bitfield() != 0 {
+ 0
+ } else {
+ let y = usize::from(point.y());
+ attack_metric_per_row[y]
+ };
+
+ attack_end += weight;
+ cdf_attack[p as usize] = attack_end;
+ }
+ }
+
+ // Tesla
+ let mut tesla_end: u16 = attack_end;
+ let cant_tesla = player.has_max_teslas() || player.energy < TESLA_PRICE;
+ if !cant_tesla {
+ for p in 0..NUMBER_OF_MAP_POSITIONS as u8 {
+ let point = Point::new_index(p);
+ let weight = if (player.occupied & point.to_bitfield() != 0) || point.y() < 7 {
+ 0
+ } else {
+ 10
+ };
+
+ tesla_end += weight;
+ cdf_tesla[p as usize] = tesla_end;
+ }
+ }
+
+ let cumulative_distribution = tesla_end;
+
+ if cumulative_distribution == 0 {
+ return Command::Nothing;
+ }
+
+ let choice = rng.gen_range(0, cumulative_distribution);
+
+ let index = match choice {
+ c if c < other_end => cdf_other.iter().position(|&c| c > choice).expect("Random number has exceeded cumulative distribution"),
+ c if c < energy_end => 2 + cdf_energy.iter().position(|&c| c > choice).expect("Random number has exceeded cumulative distribution"),
+ c if c < defence_end => 2 + NUMBER_OF_MAP_POSITIONS + cdf_defence.iter().position(|&c| c > choice).expect("Random number has exceeded cumulative distribution"),
+ c if c < attack_end => 2 + 2 * NUMBER_OF_MAP_POSITIONS + cdf_attack.iter().position(|&c| c > choice).expect("Random number has exceeded cumulative distribution"),
+ _ => 2 + 3 * NUMBER_OF_MAP_POSITIONS + cdf_tesla.iter().position(|&c| c > choice).expect("Random number has exceeded cumulative distribution"),
+ };
+
+ MOVES[index]
+}
+
+#[cfg(not(feature = "heuristic-random"))]
+pub fn random_move<R: Rng>(player: &Player, _opponent: &Player, rng: &mut R) -> Command {
+ let free_positions_count = player.unoccupied_cell_count();
+
+ let open_building_spot = free_positions_count > 0;
+
+ let all_buildings = sensible_buildings(player, open_building_spot);
+
+ let iron_curtain_count = if player.can_build_iron_curtain() && player.energy >= IRON_CURTAIN_PRICE { 1 } else { 0 };
+ let nothing_count = 1;
+
+ let building_choice_index = rng.gen_range(0, all_buildings.len() + nothing_count + iron_curtain_count);
+
+ if building_choice_index < all_buildings.len() {
+ let position_choice = rng.gen_range(0, free_positions_count);
+ Command::Build(
+ player.location_of_unoccupied_cell(position_choice),
+ all_buildings[building_choice_index]
+ )
+ }
+ else if building_choice_index == all_buildings.len() {
+ Command::Nothing
+ } else {
+ Command::IronCurtain
+ }
+}
+
+#[derive(Debug)]
+struct CommandScore {
+ command: Command,
+ starts_with_nothing: bool,
+ victory_score: i32,
+ victories: u32,
+ defeat_score: i32,
+ defeats: u32,
+ draws: u32,
+ stalemates: u32,
+ attempts: u32,
+ next_seed: [u8; 16]
+}
+
+impl CommandScore {
+ fn new(command: Command, starts_with_nothing: bool) -> CommandScore {
+ CommandScore {
+ command, starts_with_nothing,
+ victory_score: 0,
+ victories: 0,
+ defeat_score: 0,
+ defeats: 0,
+ draws: 0,
+ stalemates: 0,
+ attempts: 0,
+ next_seed: INIT_SEED
+ }
+ }
+
+ fn add_victory(&mut self, weight: i32, next_seed: [u8; 16]) {
+ use std::cmp;
+ self.victory_score += cmp::max(weight, 1);
+ self.victories += 1;
+ self.attempts += 1;
+ self.next_seed = next_seed;
+ }
+
+ fn add_defeat(&mut self, weight: i32, next_seed: [u8; 16]) {
+ use std::cmp;
+ self.defeat_score += cmp::max(weight, 1);
+ self.defeats += 1;
+ self.attempts += 1;
+ self.next_seed = next_seed;
+ }
+
+ fn add_draw(&mut self, next_seed: [u8; 16]) {
+ self.draws += 1;
+ self.attempts += 1;
+ self.next_seed = next_seed;
+ }
+
+ fn add_stalemate(&mut self, next_seed: [u8; 16]) {
+ self.stalemates += 1;
+ self.attempts += 1;
+ self.next_seed = next_seed;
+ }
+
+ #[cfg(feature = "weighted-win-ratio")]
+ fn win_ratio(&self) -> i32 {
+ (self.victory_score - self.defeat_score) * 10000 / (self.attempts as i32)
+ }
+
+ #[cfg(not(feature = "weighted-win-ratio"))]
+ fn win_ratio(&self) -> i32 {
+ (self.victories as i32 - self.defeats as i32) * 10000 / (self.attempts as i32)
+ }
+
+ fn init_command_scores(state: &BitwiseGameState) -> Vec<CommandScore> {
+ let unoccupied_cells_count = state.player.unoccupied_cell_count();
+ let unoccupied_cells = (0..unoccupied_cells_count)
+ .map(|i| state.player.location_of_unoccupied_cell(i));
+ let energy_generated = state.player.energy_generated();
+
+ let mut all_buildings: ArrayVec<[BuildingType; NUMBER_OF_BUILDING_TYPES]> = ArrayVec::new();
+ if DEFENCE_PRICE <= state.player.energy {
+ all_buildings.push(BuildingType::Defence);
+ }
+ if MISSILE_PRICE <= state.player.energy {
+ all_buildings.push(BuildingType::Attack);
+ }
+ if ENERGY_PRICE <= state.player.energy {
+ all_buildings.push(BuildingType::Energy);
+ }
+ if !state.player.has_max_teslas() && (TESLA_PRICE.saturating_sub(state.player.energy) / energy_generated < 4) {
+ all_buildings.push(BuildingType::Tesla);
+ }
+
+ let building_command_count = unoccupied_cells.len()*all_buildings.len();
+
+ let mut commands = Vec::with_capacity(building_command_count + 1);
+ let time_to_curtain_energy = (IRON_CURTAIN_PRICE.saturating_sub(state.player.energy) / energy_generated) as u8;
+
+ if time_to_curtain_energy < 4 && state.player.can_build_iron_curtain_in(state.round, time_to_curtain_energy) {
+ commands.push(CommandScore::new(Command::IronCurtain, state.player.energy < IRON_CURTAIN_PRICE));
+ }
+
+ for position in unoccupied_cells {
+ for &building in &all_buildings {
+ commands.push(CommandScore::new(Command::Build(position, building), building.cant_build_yet(state.player.energy)));
+ }
+ }
+
+ commands
+ }
+}
+
+impl fmt::Display for CommandScore {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{},{}", self.command, self.win_ratio())
+ }
+}
+
+#[cfg(all(not(feature = "heuristic-random"), not(feature = "energy-cutoff")))]
+fn sensible_buildings(player: &Player, open_building_spot: bool) -> ArrayVec<[BuildingType; NUMBER_OF_BUILDING_TYPES]> {
+ let mut result = ArrayVec::new();
+ if !open_building_spot {
+ return result;
+ }
+
+ if DEFENCE_PRICE <= player.energy {
+ result.push(BuildingType::Defence);
+ }
+ if MISSILE_PRICE <= player.energy {
+ result.push(BuildingType::Attack);
+ }
+ if ENERGY_PRICE <= player.energy {
+ result.push(BuildingType::Energy);
+ }
+ if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
+ result.push(BuildingType::Tesla);
+ }
+
+ result
+}
+
+#[cfg(all(not(feature = "heuristic-random"), feature = "energy-cutoff"))]
+fn sensible_buildings(player: &Player, open_building_spot: bool) -> ArrayVec<[BuildingType; NUMBER_OF_BUILDING_TYPES]> {
+ let mut result = ArrayVec::new();
+ if !open_building_spot {
+ return result;
+ }
+
+ let needs_energy = player.energy_generated() <= ENERGY_PRODUCTION_CUTOFF ||
+ player.energy <= ENERGY_STORAGE_CUTOFF;
+
+ if DEFENCE_PRICE <= player.energy {
+ result.push(BuildingType::Defence);
+ }
+ if MISSILE_PRICE <= player.energy {
+ result.push(BuildingType::Attack);
+ }
+ if ENERGY_PRICE <= player.energy && needs_energy {
+ result.push(BuildingType::Energy);
+ }
+ if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
+ result.push(BuildingType::Tesla);
+ }
+
+ result
+}
+
diff --git a/2018-tower-defence/src/strategy/monte_carlo_tree.rs b/2018-tower-defence/src/strategy/monte_carlo_tree.rs
new file mode 100644
index 0000000..24b2088
--- /dev/null
+++ b/2018-tower-defence/src/strategy/monte_carlo_tree.rs
@@ -0,0 +1,243 @@
+use engine::command::*;
+use engine::status::GameStatus;
+use engine::bitwise_engine::{Player, BitwiseGameState};
+use engine::constants::*;
+
+use rand::{Rng, XorShiftRng, SeedableRng};
+use time::{Duration, PreciseTime};
+
+use strategy::monte_carlo;
+
+use arrayvec::ArrayVec;
+
+#[derive(Debug)]
+struct NodeStats {
+ wins: f32,
+ losses: f32,
+ attempts: f32,
+ average: f32,
+ confidence: f32,
+ explored: Vec<(Command, NodeStats)>,
+ unexplored: Vec<Command>,
+}
+
+impl NodeStats {
+ fn create_node(player: &Player) -> NodeStats {
+ let unoccupied_cells_count = player.unoccupied_cell_count();
+ let unoccupied_cells = (0..unoccupied_cells_count)
+ .map(|i| player.location_of_unoccupied_cell(i));
+
+ let mut all_buildings: ArrayVec<[BuildingType; NUMBER_OF_BUILDING_TYPES]> = ArrayVec::new();
+ if DEFENCE_PRICE <= player.energy {
+ all_buildings.push(BuildingType::Defence);
+ }
+ if MISSILE_PRICE <= player.energy {
+ all_buildings.push(BuildingType::Attack);
+ }
+ if ENERGY_PRICE <= player.energy {
+ all_buildings.push(BuildingType::Energy);
+ }
+ if TESLA_PRICE <= player.energy && !player.has_max_teslas() {
+ all_buildings.push(BuildingType::Tesla);
+ }
+
+ let building_command_count = unoccupied_cells.len()*all_buildings.len();
+
+ let mut commands = Vec::with_capacity(building_command_count + 2);
+
+ commands.push(Command::Nothing);
+ if IRON_CURTAIN_PRICE <= player.energy && player.can_build_iron_curtain() {
+ commands.push(Command::IronCurtain);
+ }
+
+ for position in unoccupied_cells {
+ for &building in &all_buildings {
+ commands.push(Command::Build(position, building));
+ }
+ }
+
+ NodeStats {
+ wins: 0.,
+ losses: 0.,
+ attempts: 0.,
+ average: 0.,
+ confidence: 0.,
+ explored: Vec::with_capacity(commands.len()),
+ unexplored: commands
+ }
+ }
+
+ fn node_with_highest_ucb<'a>(&'a mut self) -> &'a mut (Command, NodeStats) {
+ debug_assert!(self.unexplored.is_empty());
+ debug_assert!(self.explored.len() > 0);
+ let sqrt_n = self.attempts.sqrt();
+
+ let mut max_position = 0;
+ let mut max_value = self.explored[0].1.ucb(sqrt_n);
+ for i in 1..self.explored.len() {
+ let value = self.explored[i].1.ucb(sqrt_n);
+ if value > max_value {
+ max_position = i;
+ max_value = value;
+ }
+ }
+ &mut self.explored[max_position]
+ }
+
+ fn ucb(&self, sqrt_n: f32) -> f32 {
+ self.average + sqrt_n * self.confidence
+ }
+
+ fn add_node<'a>(&'a mut self, player: &Player, command: Command) -> &'a mut (Command, NodeStats) {
+ let node = NodeStats::create_node(player);
+ self.explored.push((command, node));
+ self.unexplored.retain(|c| *c != command);
+ self.explored.last_mut().unwrap()
+ }
+
+ fn add_victory(&mut self) {
+ self.attempts += 1.;
+ self.wins += 1.;
+ self.update_confidence();
+ }
+ fn add_defeat(&mut self) {
+ self.attempts += 1.;
+ self.losses += 1.;
+ self.update_confidence();
+ }
+ fn add_draw(&mut self) {
+ self.attempts += 1.;
+ self.update_confidence();
+ }
+ fn update_confidence(&mut self) {
+ self.average = self.wins / self.attempts;
+ self.confidence = (2.0 / self.attempts).sqrt();
+ }
+
+ #[cfg(feature = "benchmarking")]
+ fn count_explored(&self) -> usize {
+ 1 + self.explored.iter().map(|(_, n)| n.count_explored()).sum::<usize>()
+ }
+}
+
+pub fn choose_move(state: &BitwiseGameState, start_time: PreciseTime, max_time: Duration) -> Command {
+ let mut rng = XorShiftRng::from_seed(INIT_SEED);
+
+ let mut root = NodeStats::create_node(&state.player);
+
+ while start_time.to(PreciseTime::now()) < max_time {
+ tree_search(&state, &mut root, &mut rng);
+ }
+
+ #[cfg(feature = "benchmarking")]
+ {
+ println!("Explored nodes: {}", root.count_explored());
+ }
+
+ let (command, _) = root.node_with_highest_ucb();
+ command.clone()
+}
+
+fn tree_search<R: Rng>(state: &BitwiseGameState, stats: &mut NodeStats, rng: &mut R) -> GameStatus {
+ // root is opponent move
+ // node being added is player move
+
+ if state.round >= MAX_MOVES {
+ return GameStatus::Draw
+ }
+
+ if stats.unexplored.is_empty() {
+ let result = {
+ let (next_command, next_tree) = stats.node_with_highest_ucb();
+ tree_search_opponent(state, next_tree, next_command.clone(), rng)
+ };
+ match result {
+ GameStatus::PlayerWon => {stats.add_defeat()},
+ GameStatus::OpponentWon => {stats.add_victory()},
+ _ => {stats.add_draw()}
+ };
+ result
+ } else {
+ let next_command = rng.choose(&stats.unexplored).expect("Partially explored had no options").clone();
+ let result = {
+ let (_, next_stats) = stats.add_node(&state.opponent, next_command);
+
+ let opponent_random = monte_carlo::random_move(&state.opponent, &state.player, rng);
+ let mut next_state = state.clone();
+ next_state.simulate(next_command, opponent_random);
+
+ let result = simulate_to_endstate(next_state, rng);
+ match result {
+ GameStatus::PlayerWon => {next_stats.add_victory()},
+ GameStatus::OpponentWon => {next_stats.add_defeat()},
+ _ => {next_stats.add_draw()}
+ };
+
+ result
+ };
+
+ match result {
+ GameStatus::PlayerWon => {stats.add_defeat()},
+ GameStatus::OpponentWon => {stats.add_victory()},
+ _ => {stats.add_draw()}
+ };
+ result
+ }
+}
+
+fn tree_search_opponent<R: Rng>(state: &BitwiseGameState, stats: &mut NodeStats, player_command: Command, rng: &mut R) -> GameStatus {
+ // root is player move
+ // node being added is opponent move
+
+ if stats.unexplored.is_empty() {
+ let result = {
+ let (next_command, next_tree) = stats.node_with_highest_ucb();
+ let mut next_state = state.clone();
+ next_state.simulate(player_command, next_command.clone());
+ tree_search(&next_state, next_tree, rng)
+ };
+ match result {
+ GameStatus::PlayerWon => {stats.add_victory()},
+ GameStatus::OpponentWon => {stats.add_defeat()},
+ _ => {stats.add_draw()}
+ };
+ result
+ } else {
+ let next_command = rng.choose(&stats.unexplored).expect("Partially explored had no options").clone();
+ let mut next_state = state.clone();
+ next_state.simulate(player_command, next_command);
+
+ let result = {
+ let (_, next_stats) = stats.add_node(&next_state.player, next_command);
+
+ let result = simulate_to_endstate(next_state, rng);
+ match result {
+ GameStatus::PlayerWon => {next_stats.add_defeat()},
+ GameStatus::OpponentWon => {next_stats.add_victory()},
+ _ => {next_stats.add_draw()}
+ };
+
+ result
+ };
+
+ match result {
+ GameStatus::PlayerWon => {stats.add_victory()},
+ GameStatus::OpponentWon => {stats.add_defeat()},
+ _ => {stats.add_draw()}
+ };
+ result
+ }
+}
+
+
+fn simulate_to_endstate<R: Rng>(mut state: BitwiseGameState, rng: &mut R) -> GameStatus {
+ let mut status = GameStatus::Continue;
+
+ while status == GameStatus::Continue && state.round < MAX_MOVES {
+ let player_command = monte_carlo::random_move(&state.player, &state.opponent, rng);
+ let opponent_command = monte_carlo::random_move(&state.opponent, &state.player, rng);
+ status = state.simulate(player_command, opponent_command);
+ }
+ status
+}
+
diff --git a/2018-tower-defence/src/strategy/static_opening.rs b/2018-tower-defence/src/strategy/static_opening.rs
new file mode 100644
index 0000000..f7e101c
--- /dev/null
+++ b/2018-tower-defence/src/strategy/static_opening.rs
@@ -0,0 +1,21 @@
+use engine::geometry::*;
+use engine::command::*;
+use engine::bitwise_engine::*;
+
+pub const STATIC_OPENING_LENGTH: u16 = 12;
+
+pub fn choose_move(state: &BitwiseGameState) -> Command {
+ match state.round {
+ 0 => Command::Build(Point::new(0,0), BuildingType::Energy),
+ 3 => Command::Build(Point::new(0,1), BuildingType::Energy),
+ 5 => Command::Build(Point::new(0,2), BuildingType::Energy),
+ 7 => Command::Build(Point::new(0,3), BuildingType::Energy),
+ 9 => Command::Build(Point::new(0,4), BuildingType::Energy),
+ 10 => Command::Build(Point::new(0,5), BuildingType::Energy),
+ 11 => Command::Build(Point::new(0,6), BuildingType::Energy),
+ 12 => Command::Build(Point::new(0,7), BuildingType::Energy),
+ 13 => Command::Build(Point::new(1,0), BuildingType::Energy),
+ 14 => Command::Build(Point::new(1,7), BuildingType::Energy),
+ _ => Command::Nothing
+ }
+}
diff --git a/2018-tower-defence/tests/live_comparison.rs b/2018-tower-defence/tests/live_comparison.rs
new file mode 100644
index 0000000..c85e3fe
--- /dev/null
+++ b/2018-tower-defence/tests/live_comparison.rs
@@ -0,0 +1,68 @@
+extern crate zombot;
+
+use zombot::input::json;
+use zombot::engine::command::{Command, BuildingType};
+use zombot::engine::geometry::Point;
+
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::Path;
+
+#[test]
+fn it_successfully_simulates_replay() {
+ test_from_replay(&Path::new("tests/v300_normal_towers"));
+}
+
+#[test]
+fn it_successfully_simulates_replay_with_iron_curtain() {
+ test_from_replay(&Path::new("tests/v300_iron_curtain"));
+}
+
+#[test]
+fn it_successfully_simulates_replay_with_iron_curtain_with_teslas() {
+ test_from_replay(&Path::new("tests/v300_iron_curtain_with_teslas"));
+}
+
+
+fn test_from_replay(replay_folder: &Path) {
+ let length = replay_folder.read_dir().unwrap().count()-1;
+
+ let mut state = json::read_bitwise_state_from_file(&format!("{}/Round 000/state.json", replay_folder.display())).unwrap();
+
+ for i in 0..length {
+ let player = read_player_command(&format!("{}/Round {:03}/PlayerCommand.txt", replay_folder.display(), i));
+ let opponent = read_opponent_command(&format!("{}/Round {:03}/OpponentCommand.txt", replay_folder.display(), i));
+ let mut expected_state = json::read_bitwise_state_from_file(&format!("{}/Round {:03}/state.json", replay_folder.display(), i+1)).unwrap();
+
+ state.simulate(player, opponent);
+ state.sort();
+ expected_state.sort();
+
+ println!("State {}: {:?}", i+1, state);
+ assert_eq!(state, expected_state, "\nFailed on state {}\n", i+1);
+ }
+}
+
+fn read_player_command(filename: &str) -> Command {
+ let mut file = File::open(filename).unwrap();
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+ if content.trim() == "No Command" {
+ Command::Nothing
+ }
+ else {
+ let mut components = content.split(',');
+ let point = Point::new(components.next().unwrap().trim().parse().unwrap(),
+ components.next().unwrap().trim().parse().unwrap());
+ let action_type = components.next().unwrap().trim().parse().unwrap();
+ if action_type == 5 {
+ Command::IronCurtain
+ } else {
+ Command::Build(point, BuildingType::from_u8(action_type).unwrap())
+ }
+ }
+}
+
+fn read_opponent_command(filename: &str) -> Command {
+ read_player_command(filename)
+}
diff --git a/2018-tower-defence/tests/monte_carlo_test.rs b/2018-tower-defence/tests/monte_carlo_test.rs
new file mode 100644
index 0000000..cec3256
--- /dev/null
+++ b/2018-tower-defence/tests/monte_carlo_test.rs
@@ -0,0 +1,34 @@
+extern crate zombot;
+extern crate time;
+use time::{PreciseTime, Duration};
+
+use zombot::*;
+
+const STATE_PATH: &str = "tests/state0.json";
+
+// there are assertions in the game engine, run when it's in debug mode
+#[test]
+fn it_does_a_normal_turn_successfully() {
+ let start_time = PreciseTime::now();
+ let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+ Ok(ok) => ok,
+ Err(error) => panic!("Error while parsing JSON file: {}", error)
+ };
+ let max_time = Duration::milliseconds(200);
+ strategy::monte_carlo::choose_move(&state, start_time, max_time);
+
+ assert!(start_time.to(PreciseTime::now()) < max_time + Duration::milliseconds(50))
+}
+
+#[test]
+fn it_does_a_normal_tree_serach_turn_successfully() {
+ let start_time = PreciseTime::now();
+ let state = match input::json::read_bitwise_state_from_file(STATE_PATH) {
+ Ok(ok) => ok,
+ Err(error) => panic!("Error while parsing JSON file: {}", error)
+ };
+ let max_time = Duration::milliseconds(200);
+ strategy::monte_carlo_tree::choose_move(&state, start_time, max_time);
+
+ assert!(start_time.to(PreciseTime::now()) < max_time + Duration::milliseconds(50))
+}
diff --git a/2018-tower-defence/tests/state0.json b/2018-tower-defence/tests/state0.json
new file mode 100644
index 0000000..572fcf9
--- /dev/null
+++ b/2018-tower-defence/tests/state0.json
@@ -0,0 +1 @@
+{"gameDetails":{"round":0,"maxRounds":400,"mapWidth":16,"mapHeight":8,"roundIncomeEnergy":5,"buildingPrices":{"TESLA":100,"DEFENSE":30,"ATTACK":30,"ENERGY":20},"buildingsStats":{"TESLA":{"health":5,"constructionTime":10,"price":100,"weaponDamage":20,"weaponSpeed":0,"weaponCooldownPeriod":10,"energyGeneratedPerTurn":0,"destroyMultiplier":10,"constructionScore":20},"DEFENSE":{"health":20,"constructionTime":3,"price":30,"weaponDamage":0,"weaponSpeed":0,"weaponCooldownPeriod":0,"energyGeneratedPerTurn":0,"destroyMultiplier":1,"constructionScore":10},"ATTACK":{"health":5,"constructionTime":1,"price":30,"weaponDamage":5,"weaponSpeed":2,"weaponCooldownPeriod":3,"energyGeneratedPerTurn":0,"destroyMultiplier":1,"constructionScore":4},"ENERGY":{"health":5,"constructionTime":1,"price":20,"weaponDamage":0,"weaponSpeed":0,"weaponCooldownPeriod":0,"energyGeneratedPerTurn":3,"destroyMultiplier":1,"constructionScore":3}},"ironCurtainStats":{"activeRounds":6,"resetPeriod":50,"price":150,"constructionScore":20}},"players":[{"playerType":"A","energy":20,"health":100,"hitsTaken":0,"score":0,"ironCurtainAvailable":false,"activeIronCurtainLifetime":0},{"playerType":"B","energy":20,"health":100,"hitsTaken":0,"score":0,"ironCurtainAvailable":false,"activeIronCurtainLifetime":0}],"gameMap":[[{"x":0,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":0,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":0,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":1,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":1,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":2,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":2,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":3,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":3,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":4,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":4,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":5,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":5,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":6,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":6,"buildings":[],"missiles":[],"cellOwner":"B"}],[{"x":0,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":1,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":2,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":3,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":4,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":5,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":6,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":7,"y":7,"buildings":[],"missiles":[],"cellOwner":"A"},{"x":8,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":9,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":10,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":11,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":12,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":13,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":14,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"},{"x":15,"y":7,"buildings":[],"missiles":[],"cellOwner":"B"}]],"teslaHitList":[],"ironcurtainHitList":[]} \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 000/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 000/OpponentCommand.txt
new file mode 100644
index 0000000..4a9590d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 000/OpponentCommand.txt
@@ -0,0 +1 @@
+0,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 000/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 000/PlayerCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 000/PlayerCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 001/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 001/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 001/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 001/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 001/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 001/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 002/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 002/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 002/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 002/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 002/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 002/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 003/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 003/OpponentCommand.txt
new file mode 100644
index 0000000..49dd99d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 003/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 003/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 003/PlayerCommand.txt
new file mode 100644
index 0000000..4a9590d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 003/PlayerCommand.txt
@@ -0,0 +1 @@
+0,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 004/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 004/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 004/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 004/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 004/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 004/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 005/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 005/OpponentCommand.txt
new file mode 100644
index 0000000..ca8db41
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 005/OpponentCommand.txt
@@ -0,0 +1 @@
+3,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 005/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 005/PlayerCommand.txt
new file mode 100644
index 0000000..94bee18
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 005/PlayerCommand.txt
@@ -0,0 +1 @@
+0,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 006/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 006/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 006/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 006/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 006/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 006/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 007/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 007/OpponentCommand.txt
new file mode 100644
index 0000000..6c57709
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 007/OpponentCommand.txt
@@ -0,0 +1 @@
+1,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 007/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 007/PlayerCommand.txt
new file mode 100644
index 0000000..4d83fd9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 007/PlayerCommand.txt
@@ -0,0 +1 @@
+3,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 008/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 008/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 008/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 008/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 008/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 008/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 009/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 009/OpponentCommand.txt
new file mode 100644
index 0000000..d9a0acb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 009/OpponentCommand.txt
@@ -0,0 +1 @@
+2,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 009/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 009/PlayerCommand.txt
new file mode 100644
index 0000000..433ff46
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 009/PlayerCommand.txt
@@ -0,0 +1 @@
+6,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 010/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 010/OpponentCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 010/OpponentCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 010/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 010/PlayerCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 010/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 011/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 011/OpponentCommand.txt
new file mode 100644
index 0000000..1260cea
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 011/OpponentCommand.txt
@@ -0,0 +1 @@
+5,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 011/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 011/PlayerCommand.txt
new file mode 100644
index 0000000..f3c8f77
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 011/PlayerCommand.txt
@@ -0,0 +1 @@
+2,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 012/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 012/OpponentCommand.txt
new file mode 100644
index 0000000..19fbb8f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 012/OpponentCommand.txt
@@ -0,0 +1 @@
+4,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 012/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 012/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 012/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 013/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 013/OpponentCommand.txt
new file mode 100644
index 0000000..ca8db41
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 013/OpponentCommand.txt
@@ -0,0 +1 @@
+3,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 013/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 013/PlayerCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 013/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 014/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 014/OpponentCommand.txt
new file mode 100644
index 0000000..f3c8f77
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 014/OpponentCommand.txt
@@ -0,0 +1 @@
+2,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 014/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 014/PlayerCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 014/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 015/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 015/OpponentCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 015/OpponentCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 015/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 015/PlayerCommand.txt
new file mode 100644
index 0000000..b0fd0dc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 015/PlayerCommand.txt
@@ -0,0 +1 @@
+0,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 016/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 016/OpponentCommand.txt
new file mode 100644
index 0000000..10532f2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 016/OpponentCommand.txt
@@ -0,0 +1 @@
+0,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 016/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 016/PlayerCommand.txt
new file mode 100644
index 0000000..1c0a0b0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 016/PlayerCommand.txt
@@ -0,0 +1 @@
+1,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 017/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 017/OpponentCommand.txt
new file mode 100644
index 0000000..0f83bc0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 017/OpponentCommand.txt
@@ -0,0 +1 @@
+0,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 017/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 017/PlayerCommand.txt
new file mode 100644
index 0000000..487bf6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 017/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 018/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 018/OpponentCommand.txt
new file mode 100644
index 0000000..433ff46
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 018/OpponentCommand.txt
@@ -0,0 +1 @@
+6,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 018/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 018/PlayerCommand.txt
new file mode 100644
index 0000000..75b785b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 018/PlayerCommand.txt
@@ -0,0 +1 @@
+4,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 019/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 019/OpponentCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 019/OpponentCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 019/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 019/PlayerCommand.txt
new file mode 100644
index 0000000..e09f712
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 019/PlayerCommand.txt
@@ -0,0 +1 @@
+5,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 020/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 020/OpponentCommand.txt
new file mode 100644
index 0000000..3ab3f32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 020/OpponentCommand.txt
@@ -0,0 +1 @@
+5,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 020/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 020/PlayerCommand.txt
new file mode 100644
index 0000000..a943cb9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 020/PlayerCommand.txt
@@ -0,0 +1 @@
+3,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 021/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 021/OpponentCommand.txt
new file mode 100644
index 0000000..48cfbfe
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 021/OpponentCommand.txt
@@ -0,0 +1 @@
+7,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 021/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 021/PlayerCommand.txt
new file mode 100644
index 0000000..433ff46
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 021/PlayerCommand.txt
@@ -0,0 +1 @@
+6,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 022/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 022/OpponentCommand.txt
new file mode 100644
index 0000000..9f12d31
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 022/OpponentCommand.txt
@@ -0,0 +1 @@
+6,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 022/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 022/PlayerCommand.txt
new file mode 100644
index 0000000..4371338
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 022/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 023/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 023/OpponentCommand.txt
new file mode 100644
index 0000000..487bf6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 023/OpponentCommand.txt
@@ -0,0 +1 @@
+7,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 023/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 023/PlayerCommand.txt
new file mode 100644
index 0000000..d9a0acb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 023/PlayerCommand.txt
@@ -0,0 +1 @@
+2,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 024/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 024/OpponentCommand.txt
new file mode 100644
index 0000000..1260cea
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 024/OpponentCommand.txt
@@ -0,0 +1 @@
+5,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 024/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 024/PlayerCommand.txt
new file mode 100644
index 0000000..c7d9109
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 024/PlayerCommand.txt
@@ -0,0 +1 @@
+5,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 025/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 025/OpponentCommand.txt
new file mode 100644
index 0000000..8ba7f16
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 025/OpponentCommand.txt
@@ -0,0 +1 @@
+1,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 025/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 025/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 025/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 026/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 026/OpponentCommand.txt
new file mode 100644
index 0000000..94bee18
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 026/OpponentCommand.txt
@@ -0,0 +1 @@
+0,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 026/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 026/PlayerCommand.txt
new file mode 100644
index 0000000..8a842f9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 026/PlayerCommand.txt
@@ -0,0 +1 @@
+4,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 027/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 027/OpponentCommand.txt
new file mode 100644
index 0000000..9b5a49a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 027/OpponentCommand.txt
@@ -0,0 +1 @@
+6,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 027/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 027/PlayerCommand.txt
new file mode 100644
index 0000000..c7d9109
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 027/PlayerCommand.txt
@@ -0,0 +1 @@
+5,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 028/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 028/OpponentCommand.txt
new file mode 100644
index 0000000..b2c26e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 028/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 028/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 028/PlayerCommand.txt
new file mode 100644
index 0000000..08ceedf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 028/PlayerCommand.txt
@@ -0,0 +1 @@
+4,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 029/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 029/OpponentCommand.txt
new file mode 100644
index 0000000..239b17a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 029/OpponentCommand.txt
@@ -0,0 +1 @@
+1,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 029/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 029/PlayerCommand.txt
new file mode 100644
index 0000000..9408cb0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 029/PlayerCommand.txt
@@ -0,0 +1 @@
+6,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 030/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 030/OpponentCommand.txt
new file mode 100644
index 0000000..75b785b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 030/OpponentCommand.txt
@@ -0,0 +1 @@
+4,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 030/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 030/PlayerCommand.txt
new file mode 100644
index 0000000..87d322f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 030/PlayerCommand.txt
@@ -0,0 +1 @@
+3,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 031/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 031/OpponentCommand.txt
new file mode 100644
index 0000000..d51905f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 031/OpponentCommand.txt
@@ -0,0 +1 @@
+7,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 031/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 031/PlayerCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 031/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 032/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 032/OpponentCommand.txt
new file mode 100644
index 0000000..9233a2a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 032/OpponentCommand.txt
@@ -0,0 +1 @@
+0,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 032/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 032/PlayerCommand.txt
new file mode 100644
index 0000000..85eacdb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 032/PlayerCommand.txt
@@ -0,0 +1 @@
+3,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 033/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 033/OpponentCommand.txt
new file mode 100644
index 0000000..c27eaf9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 033/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 033/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 033/PlayerCommand.txt
new file mode 100644
index 0000000..c37c6f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 033/PlayerCommand.txt
@@ -0,0 +1 @@
+1,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 034/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 034/OpponentCommand.txt
new file mode 100644
index 0000000..a81a341
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 034/OpponentCommand.txt
@@ -0,0 +1 @@
+7,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 034/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 034/PlayerCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 034/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 035/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 035/OpponentCommand.txt
new file mode 100644
index 0000000..b557a00
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 035/OpponentCommand.txt
@@ -0,0 +1 @@
+4,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 035/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 035/PlayerCommand.txt
new file mode 100644
index 0000000..8ac3a56
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 035/PlayerCommand.txt
@@ -0,0 +1 @@
+1,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 036/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 036/OpponentCommand.txt
new file mode 100644
index 0000000..b7adddf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 036/OpponentCommand.txt
@@ -0,0 +1 @@
+5,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 036/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 036/PlayerCommand.txt
new file mode 100644
index 0000000..9477e06
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 036/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 037/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 037/OpponentCommand.txt
new file mode 100644
index 0000000..8a6627b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 037/OpponentCommand.txt
@@ -0,0 +1 @@
+1,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 037/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 037/PlayerCommand.txt
new file mode 100644
index 0000000..87d322f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 037/PlayerCommand.txt
@@ -0,0 +1 @@
+3,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 038/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 038/OpponentCommand.txt
new file mode 100644
index 0000000..3fff544
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 038/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 038/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 038/PlayerCommand.txt
new file mode 100644
index 0000000..4119710
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 038/PlayerCommand.txt
@@ -0,0 +1 @@
+2,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 039/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 039/OpponentCommand.txt
new file mode 100644
index 0000000..61f66b5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 039/OpponentCommand.txt
@@ -0,0 +1 @@
+3,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 039/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 039/PlayerCommand.txt
new file mode 100644
index 0000000..41d5370
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 039/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 040/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 040/OpponentCommand.txt
new file mode 100644
index 0000000..b77a79c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 040/OpponentCommand.txt
@@ -0,0 +1 @@
+2,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 040/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 040/PlayerCommand.txt
new file mode 100644
index 0000000..c7d9109
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 040/PlayerCommand.txt
@@ -0,0 +1 @@
+5,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 041/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 041/OpponentCommand.txt
new file mode 100644
index 0000000..17d7db2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 041/OpponentCommand.txt
@@ -0,0 +1 @@
+5,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 041/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 041/PlayerCommand.txt
new file mode 100644
index 0000000..5c3de37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 041/PlayerCommand.txt
@@ -0,0 +1 @@
+6,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 042/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 042/OpponentCommand.txt
new file mode 100644
index 0000000..9f12d31
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 042/OpponentCommand.txt
@@ -0,0 +1 @@
+6,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 042/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 042/PlayerCommand.txt
new file mode 100644
index 0000000..ad5f821
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 042/PlayerCommand.txt
@@ -0,0 +1 @@
+7,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 043/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 043/OpponentCommand.txt
new file mode 100644
index 0000000..3362217
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 043/OpponentCommand.txt
@@ -0,0 +1 @@
+0,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 043/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 043/PlayerCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 043/PlayerCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 044/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 044/OpponentCommand.txt
new file mode 100644
index 0000000..487bf6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 044/OpponentCommand.txt
@@ -0,0 +1 @@
+7,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 044/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 044/PlayerCommand.txt
new file mode 100644
index 0000000..d05a714
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 044/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 045/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 045/OpponentCommand.txt
new file mode 100644
index 0000000..e09f712
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 045/OpponentCommand.txt
@@ -0,0 +1 @@
+5,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 045/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 045/PlayerCommand.txt
new file mode 100644
index 0000000..323dbb1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 045/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 046/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 046/OpponentCommand.txt
new file mode 100644
index 0000000..055ca5b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 046/OpponentCommand.txt
@@ -0,0 +1 @@
+0,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 046/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 046/PlayerCommand.txt
new file mode 100644
index 0000000..f3c8f77
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 046/PlayerCommand.txt
@@ -0,0 +1 @@
+2,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 047/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 047/OpponentCommand.txt
new file mode 100644
index 0000000..e874b1f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 047/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 047/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 047/PlayerCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 047/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 048/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 048/OpponentCommand.txt
new file mode 100644
index 0000000..75b785b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 048/OpponentCommand.txt
@@ -0,0 +1 @@
+4,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 048/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 048/PlayerCommand.txt
new file mode 100644
index 0000000..49c1201
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 048/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 049/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 049/OpponentCommand.txt
new file mode 100644
index 0000000..8e935c8
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 049/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 049/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 049/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 049/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 050/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 050/OpponentCommand.txt
new file mode 100644
index 0000000..94bee18
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 050/OpponentCommand.txt
@@ -0,0 +1 @@
+0,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 050/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 050/PlayerCommand.txt
new file mode 100644
index 0000000..7388cff
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 050/PlayerCommand.txt
@@ -0,0 +1 @@
+4,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 051/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 051/OpponentCommand.txt
new file mode 100644
index 0000000..d9e32bb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 051/OpponentCommand.txt
@@ -0,0 +1 @@
+2,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 051/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 051/PlayerCommand.txt
new file mode 100644
index 0000000..16ddcd7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 051/PlayerCommand.txt
@@ -0,0 +1 @@
+7,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 052/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 052/OpponentCommand.txt
new file mode 100644
index 0000000..226a1f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 052/OpponentCommand.txt
@@ -0,0 +1 @@
+2,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 052/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 052/PlayerCommand.txt
new file mode 100644
index 0000000..41d5370
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 052/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 053/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 053/OpponentCommand.txt
new file mode 100644
index 0000000..4a8cf07
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 053/OpponentCommand.txt
@@ -0,0 +1 @@
+4,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 053/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 053/PlayerCommand.txt
new file mode 100644
index 0000000..9b5a49a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 053/PlayerCommand.txt
@@ -0,0 +1 @@
+6,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 054/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 054/OpponentCommand.txt
new file mode 100644
index 0000000..1e7a5f9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 054/OpponentCommand.txt
@@ -0,0 +1 @@
+2,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 054/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 054/PlayerCommand.txt
new file mode 100644
index 0000000..f3c8f77
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 054/PlayerCommand.txt
@@ -0,0 +1 @@
+2,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 055/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 055/OpponentCommand.txt
new file mode 100644
index 0000000..6c57709
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 055/OpponentCommand.txt
@@ -0,0 +1 @@
+1,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 055/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 055/PlayerCommand.txt
new file mode 100644
index 0000000..0a612db
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 055/PlayerCommand.txt
@@ -0,0 +1 @@
+5,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 056/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 056/OpponentCommand.txt
new file mode 100644
index 0000000..b2c26e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 056/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 056/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 056/PlayerCommand.txt
new file mode 100644
index 0000000..a6f3f91
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 056/PlayerCommand.txt
@@ -0,0 +1 @@
+2,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 057/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 057/OpponentCommand.txt
new file mode 100644
index 0000000..429fd32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 057/OpponentCommand.txt
@@ -0,0 +1 @@
+5,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 057/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 057/PlayerCommand.txt
new file mode 100644
index 0000000..d9d71ea
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 057/PlayerCommand.txt
@@ -0,0 +1 @@
+4,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 058/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 058/OpponentCommand.txt
new file mode 100644
index 0000000..722ec58
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 058/OpponentCommand.txt
@@ -0,0 +1 @@
+4,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 058/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 058/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 058/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 059/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 059/OpponentCommand.txt
new file mode 100644
index 0000000..07b92b5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 059/OpponentCommand.txt
@@ -0,0 +1 @@
+3,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 059/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 059/PlayerCommand.txt
new file mode 100644
index 0000000..19fbb8f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 059/PlayerCommand.txt
@@ -0,0 +1 @@
+4,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 060/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 060/OpponentCommand.txt
new file mode 100644
index 0000000..8bb009c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 060/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 060/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 060/PlayerCommand.txt
new file mode 100644
index 0000000..117d6c2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 060/PlayerCommand.txt
@@ -0,0 +1 @@
+3,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 061/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 061/OpponentCommand.txt
new file mode 100644
index 0000000..323dbb1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 061/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 061/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 061/PlayerCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 061/PlayerCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 062/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 062/OpponentCommand.txt
new file mode 100644
index 0000000..08ecb10
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 062/OpponentCommand.txt
@@ -0,0 +1 @@
+3,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 062/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 062/PlayerCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 062/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 063/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 063/OpponentCommand.txt
new file mode 100644
index 0000000..816366d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 063/OpponentCommand.txt
@@ -0,0 +1 @@
+0,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 063/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 063/PlayerCommand.txt
new file mode 100644
index 0000000..9b9f49b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 063/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 064/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 064/OpponentCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 064/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 064/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 064/PlayerCommand.txt
new file mode 100644
index 0000000..8a6627b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 064/PlayerCommand.txt
@@ -0,0 +1 @@
+1,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 065/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 065/OpponentCommand.txt
new file mode 100644
index 0000000..ab857c9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 065/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 065/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 065/PlayerCommand.txt
new file mode 100644
index 0000000..26912c7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 065/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 066/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 066/OpponentCommand.txt
new file mode 100644
index 0000000..4f716a1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 066/OpponentCommand.txt
@@ -0,0 +1 @@
+2,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 066/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 066/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 066/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 067/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 067/OpponentCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 067/OpponentCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 067/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 067/PlayerCommand.txt
new file mode 100644
index 0000000..c27eaf9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 067/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 068/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 068/OpponentCommand.txt
new file mode 100644
index 0000000..d9e32bb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 068/OpponentCommand.txt
@@ -0,0 +1 @@
+2,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 068/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 068/PlayerCommand.txt
new file mode 100644
index 0000000..50688ac
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 068/PlayerCommand.txt
@@ -0,0 +1 @@
+6,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 069/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 069/OpponentCommand.txt
new file mode 100644
index 0000000..ebfc684
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 069/OpponentCommand.txt
@@ -0,0 +1 @@
+0,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 069/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 069/PlayerCommand.txt
new file mode 100644
index 0000000..41d5370
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 069/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 070/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 070/OpponentCommand.txt
new file mode 100644
index 0000000..c742585
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 070/OpponentCommand.txt
@@ -0,0 +1 @@
+5,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 070/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 070/PlayerCommand.txt
new file mode 100644
index 0000000..16ddcd7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 070/PlayerCommand.txt
@@ -0,0 +1 @@
+7,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 071/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 071/OpponentCommand.txt
new file mode 100644
index 0000000..4a8cf07
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 071/OpponentCommand.txt
@@ -0,0 +1 @@
+4,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 071/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 071/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 071/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 072/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 072/OpponentCommand.txt
new file mode 100644
index 0000000..1c0a0b0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 072/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 072/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 072/PlayerCommand.txt
new file mode 100644
index 0000000..323dbb1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 072/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 073/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 073/OpponentCommand.txt
new file mode 100644
index 0000000..a91c23f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 073/OpponentCommand.txt
@@ -0,0 +1 @@
+7,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 073/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 073/PlayerCommand.txt
new file mode 100644
index 0000000..95a4cf3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 073/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 074/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 074/OpponentCommand.txt
new file mode 100644
index 0000000..daa46d9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 074/OpponentCommand.txt
@@ -0,0 +1 @@
+0,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 074/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 074/PlayerCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 074/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 075/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 075/OpponentCommand.txt
new file mode 100644
index 0000000..743727a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 075/OpponentCommand.txt
@@ -0,0 +1 @@
+7,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 075/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 075/PlayerCommand.txt
new file mode 100644
index 0000000..910a1ab
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 075/PlayerCommand.txt
@@ -0,0 +1 @@
+1,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 076/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 076/OpponentCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 076/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 076/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 076/PlayerCommand.txt
new file mode 100644
index 0000000..4119710
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 076/PlayerCommand.txt
@@ -0,0 +1 @@
+2,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 077/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 077/OpponentCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 077/OpponentCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 077/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 077/PlayerCommand.txt
new file mode 100644
index 0000000..e09f712
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 077/PlayerCommand.txt
@@ -0,0 +1 @@
+5,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 078/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 078/OpponentCommand.txt
new file mode 100644
index 0000000..77bf522
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 078/OpponentCommand.txt
@@ -0,0 +1 @@
+3,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 078/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 078/PlayerCommand.txt
new file mode 100644
index 0000000..41d5370
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 078/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 079/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 079/OpponentCommand.txt
new file mode 100644
index 0000000..e02c049
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 079/OpponentCommand.txt
@@ -0,0 +1 @@
+3,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 079/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 079/PlayerCommand.txt
new file mode 100644
index 0000000..f8007d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 079/PlayerCommand.txt
@@ -0,0 +1 @@
+0,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 080/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 080/OpponentCommand.txt
new file mode 100644
index 0000000..9b5a49a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 080/OpponentCommand.txt
@@ -0,0 +1 @@
+6,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 080/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 080/PlayerCommand.txt
new file mode 100644
index 0000000..08ecb10
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 080/PlayerCommand.txt
@@ -0,0 +1 @@
+3,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 081/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 081/OpponentCommand.txt
new file mode 100644
index 0000000..4f8f464
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 081/OpponentCommand.txt
@@ -0,0 +1 @@
+5,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 081/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 081/PlayerCommand.txt
new file mode 100644
index 0000000..c49791c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 081/PlayerCommand.txt
@@ -0,0 +1 @@
+0,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 082/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 082/OpponentCommand.txt
new file mode 100644
index 0000000..ce49ef6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 082/OpponentCommand.txt
@@ -0,0 +1 @@
+4,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 082/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 082/PlayerCommand.txt
new file mode 100644
index 0000000..3ab3f32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 082/PlayerCommand.txt
@@ -0,0 +1 @@
+5,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 083/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 083/OpponentCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 083/OpponentCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 083/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 083/PlayerCommand.txt
new file mode 100644
index 0000000..b4e7071
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 083/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 084/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 084/OpponentCommand.txt
new file mode 100644
index 0000000..b2c26e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 084/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 084/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 084/PlayerCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 084/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 085/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 085/OpponentCommand.txt
new file mode 100644
index 0000000..d51905f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 085/OpponentCommand.txt
@@ -0,0 +1 @@
+7,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 085/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 085/PlayerCommand.txt
new file mode 100644
index 0000000..4d83fd9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 085/PlayerCommand.txt
@@ -0,0 +1 @@
+3,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 086/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 086/OpponentCommand.txt
new file mode 100644
index 0000000..dd03d6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 086/OpponentCommand.txt
@@ -0,0 +1 @@
+3,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 086/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 086/PlayerCommand.txt
new file mode 100644
index 0000000..6643b0d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 086/PlayerCommand.txt
@@ -0,0 +1 @@
+5,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 087/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 087/OpponentCommand.txt
new file mode 100644
index 0000000..b7adddf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 087/OpponentCommand.txt
@@ -0,0 +1 @@
+5,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 087/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 087/PlayerCommand.txt
new file mode 100644
index 0000000..c7d9109
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 087/PlayerCommand.txt
@@ -0,0 +1 @@
+5,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 088/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 088/OpponentCommand.txt
new file mode 100644
index 0000000..4b87d86
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 088/OpponentCommand.txt
@@ -0,0 +1 @@
+2,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 088/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 088/PlayerCommand.txt
new file mode 100644
index 0000000..f238916
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 088/PlayerCommand.txt
@@ -0,0 +1 @@
+2,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 089/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 089/OpponentCommand.txt
new file mode 100644
index 0000000..af58f31
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 089/OpponentCommand.txt
@@ -0,0 +1 @@
+2,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 089/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 089/PlayerCommand.txt
new file mode 100644
index 0000000..2a21cf5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 089/PlayerCommand.txt
@@ -0,0 +1 @@
+3,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 090/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 090/OpponentCommand.txt
new file mode 100644
index 0000000..94d7b0a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 090/OpponentCommand.txt
@@ -0,0 +1 @@
+6,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 090/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 090/PlayerCommand.txt
new file mode 100644
index 0000000..8bb009c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 090/PlayerCommand.txt
@@ -0,0 +1 @@
+6,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 091/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 091/OpponentCommand.txt
new file mode 100644
index 0000000..85eacdb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 091/OpponentCommand.txt
@@ -0,0 +1 @@
+3,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 091/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 091/PlayerCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 091/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 092/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 092/OpponentCommand.txt
new file mode 100644
index 0000000..7d08a5b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 092/OpponentCommand.txt
@@ -0,0 +1 @@
+3,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 092/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 092/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 092/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 093/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 093/OpponentCommand.txt
new file mode 100644
index 0000000..7ae2a9c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 093/OpponentCommand.txt
@@ -0,0 +1 @@
+6,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 093/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 093/PlayerCommand.txt
new file mode 100644
index 0000000..5ee21e6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 093/PlayerCommand.txt
@@ -0,0 +1 @@
+4,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 094/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 094/OpponentCommand.txt
new file mode 100644
index 0000000..8ac3a56
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 094/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 094/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 094/PlayerCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 094/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 095/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 095/OpponentCommand.txt
new file mode 100644
index 0000000..a6f3f91
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 095/OpponentCommand.txt
@@ -0,0 +1 @@
+2,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 095/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 095/PlayerCommand.txt
new file mode 100644
index 0000000..6643b0d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 095/PlayerCommand.txt
@@ -0,0 +1 @@
+5,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 096/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 096/OpponentCommand.txt
new file mode 100644
index 0000000..a81a341
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 096/OpponentCommand.txt
@@ -0,0 +1 @@
+7,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 096/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 096/PlayerCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 096/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 097/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 097/OpponentCommand.txt
new file mode 100644
index 0000000..ce49ef6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 097/OpponentCommand.txt
@@ -0,0 +1 @@
+4,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 097/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 097/PlayerCommand.txt
new file mode 100644
index 0000000..a943cb9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 097/PlayerCommand.txt
@@ -0,0 +1 @@
+3,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 098/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 098/OpponentCommand.txt
new file mode 100644
index 0000000..c4e7948
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 098/OpponentCommand.txt
@@ -0,0 +1 @@
+2,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 098/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 098/PlayerCommand.txt
new file mode 100644
index 0000000..429fd32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 098/PlayerCommand.txt
@@ -0,0 +1 @@
+5,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 099/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 099/OpponentCommand.txt
new file mode 100644
index 0000000..e2634f0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 099/OpponentCommand.txt
@@ -0,0 +1 @@
+5,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 099/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 099/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 099/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 100/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 100/OpponentCommand.txt
new file mode 100644
index 0000000..f82aafb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 100/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 100/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 100/PlayerCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 100/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 101/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 101/OpponentCommand.txt
new file mode 100644
index 0000000..d17d619
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 101/OpponentCommand.txt
@@ -0,0 +1 @@
+5,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 101/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 101/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 101/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 102/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 102/OpponentCommand.txt
new file mode 100644
index 0000000..910a1ab
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 102/OpponentCommand.txt
@@ -0,0 +1 @@
+1,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 102/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 102/PlayerCommand.txt
new file mode 100644
index 0000000..8c5ef78
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 102/PlayerCommand.txt
@@ -0,0 +1 @@
+4,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 103/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 103/OpponentCommand.txt
new file mode 100644
index 0000000..4a9590d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 103/OpponentCommand.txt
@@ -0,0 +1 @@
+0,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 103/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 103/PlayerCommand.txt
new file mode 100644
index 0000000..b548cc7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 103/PlayerCommand.txt
@@ -0,0 +1 @@
+0,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 104/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 104/OpponentCommand.txt
new file mode 100644
index 0000000..ac6c42a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 104/OpponentCommand.txt
@@ -0,0 +1 @@
+5,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 104/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 104/PlayerCommand.txt
new file mode 100644
index 0000000..e638283
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 104/PlayerCommand.txt
@@ -0,0 +1 @@
+3,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 105/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 105/OpponentCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 105/OpponentCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 105/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 105/PlayerCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 105/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 106/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 106/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 106/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 106/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 106/PlayerCommand.txt
new file mode 100644
index 0000000..f24e83b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 106/PlayerCommand.txt
@@ -0,0 +1 @@
+4,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 107/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 107/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 107/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 107/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 107/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 107/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 108/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 108/OpponentCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 108/OpponentCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 108/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 108/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 108/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 109/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 109/OpponentCommand.txt
new file mode 100644
index 0000000..4119710
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 109/OpponentCommand.txt
@@ -0,0 +1 @@
+2,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 109/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 109/PlayerCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 109/PlayerCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 110/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 110/OpponentCommand.txt
new file mode 100644
index 0000000..dd03d6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 110/OpponentCommand.txt
@@ -0,0 +1 @@
+3,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 110/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 110/PlayerCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 110/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 111/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 111/OpponentCommand.txt
new file mode 100644
index 0000000..f23ef17
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 111/OpponentCommand.txt
@@ -0,0 +1 @@
+4,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 111/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 111/PlayerCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 111/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 112/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 112/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 112/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 112/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 112/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 112/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 113/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 113/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 113/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 113/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 113/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 113/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 114/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 114/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 114/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 114/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 114/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 114/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 115/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 115/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 115/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 115/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 115/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 115/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 116/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 116/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 116/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 116/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 116/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 116/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 117/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 117/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 117/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 117/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 117/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 117/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 118/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 118/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 118/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain/Round 118/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain/Round 118/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain/Round 118/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/OpponentCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/OpponentCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/PlayerCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 000/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 001/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 002/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/OpponentCommand.txt
new file mode 100644
index 0000000..9233a2a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/OpponentCommand.txt
@@ -0,0 +1 @@
+0,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/PlayerCommand.txt
new file mode 100644
index 0000000..9233a2a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 003/PlayerCommand.txt
@@ -0,0 +1 @@
+0,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 004/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/OpponentCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/OpponentCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/PlayerCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 005/PlayerCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 006/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/OpponentCommand.txt
new file mode 100644
index 0000000..72ca43d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/OpponentCommand.txt
@@ -0,0 +1 @@
+0,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/PlayerCommand.txt
new file mode 100644
index 0000000..72ca43d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 007/PlayerCommand.txt
@@ -0,0 +1 @@
+0,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 008/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/OpponentCommand.txt
new file mode 100644
index 0000000..4a9590d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/OpponentCommand.txt
@@ -0,0 +1 @@
+0,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/PlayerCommand.txt
new file mode 100644
index 0000000..4a9590d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 009/PlayerCommand.txt
@@ -0,0 +1 @@
+0,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/OpponentCommand.txt
new file mode 100644
index 0000000..b0fd0dc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/OpponentCommand.txt
@@ -0,0 +1 @@
+0,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/PlayerCommand.txt
new file mode 100644
index 0000000..b0fd0dc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 010/PlayerCommand.txt
@@ -0,0 +1 @@
+0,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/OpponentCommand.txt
new file mode 100644
index 0000000..94bee18
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/OpponentCommand.txt
@@ -0,0 +1 @@
+0,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/PlayerCommand.txt
new file mode 100644
index 0000000..94bee18
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 011/PlayerCommand.txt
@@ -0,0 +1 @@
+0,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/OpponentCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/OpponentCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/PlayerCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 012/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/OpponentCommand.txt
new file mode 100644
index 0000000..3362217
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/OpponentCommand.txt
@@ -0,0 +1 @@
+0,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/PlayerCommand.txt
new file mode 100644
index 0000000..3362217
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 013/PlayerCommand.txt
@@ -0,0 +1 @@
+0,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/OpponentCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/OpponentCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 014/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/OpponentCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/OpponentCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/PlayerCommand.txt
new file mode 100644
index 0000000..239b17a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 015/PlayerCommand.txt
@@ -0,0 +1 @@
+1,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/OpponentCommand.txt
new file mode 100644
index 0000000..87d322f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/OpponentCommand.txt
@@ -0,0 +1 @@
+3,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/PlayerCommand.txt
new file mode 100644
index 0000000..a5bd5ef
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 016/PlayerCommand.txt
@@ -0,0 +1 @@
+1,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/OpponentCommand.txt
new file mode 100644
index 0000000..8ba7f16
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/OpponentCommand.txt
@@ -0,0 +1 @@
+1,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/PlayerCommand.txt
new file mode 100644
index 0000000..b77a79c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 017/PlayerCommand.txt
@@ -0,0 +1 @@
+2,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/OpponentCommand.txt
new file mode 100644
index 0000000..36e6f4c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/PlayerCommand.txt
new file mode 100644
index 0000000..36e6f4c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 018/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/OpponentCommand.txt
new file mode 100644
index 0000000..48cfbfe
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/OpponentCommand.txt
@@ -0,0 +1 @@
+7,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/PlayerCommand.txt
new file mode 100644
index 0000000..48cfbfe
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 019/PlayerCommand.txt
@@ -0,0 +1 @@
+7,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/OpponentCommand.txt
new file mode 100644
index 0000000..6c57709
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/OpponentCommand.txt
@@ -0,0 +1 @@
+1,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/PlayerCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 020/PlayerCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/OpponentCommand.txt
new file mode 100644
index 0000000..474bb6c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/OpponentCommand.txt
@@ -0,0 +1 @@
+7,2,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/PlayerCommand.txt
new file mode 100644
index 0000000..474bb6c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 021/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/OpponentCommand.txt
new file mode 100644
index 0000000..a5bd5ef
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/PlayerCommand.txt
new file mode 100644
index 0000000..533b1c8
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 022/PlayerCommand.txt
@@ -0,0 +1 @@
+2,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/OpponentCommand.txt
new file mode 100644
index 0000000..239b17a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/OpponentCommand.txt
@@ -0,0 +1 @@
+1,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/PlayerCommand.txt
new file mode 100644
index 0000000..6c57709
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 023/PlayerCommand.txt
@@ -0,0 +1 @@
+1,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/OpponentCommand.txt
new file mode 100644
index 0000000..0b12f52
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/OpponentCommand.txt
@@ -0,0 +1 @@
+2,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/PlayerCommand.txt
new file mode 100644
index 0000000..8ba7f16
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 024/PlayerCommand.txt
@@ -0,0 +1 @@
+1,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/OpponentCommand.txt
new file mode 100644
index 0000000..601aa29
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/OpponentCommand.txt
@@ -0,0 +1 @@
+2,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/PlayerCommand.txt
new file mode 100644
index 0000000..19fbb8f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 025/PlayerCommand.txt
@@ -0,0 +1 @@
+4,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/OpponentCommand.txt
new file mode 100644
index 0000000..8a6627b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/OpponentCommand.txt
@@ -0,0 +1 @@
+1,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/PlayerCommand.txt
new file mode 100644
index 0000000..8a6627b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 026/PlayerCommand.txt
@@ -0,0 +1 @@
+1,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/OpponentCommand.txt
new file mode 100644
index 0000000..1c0a0b0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/PlayerCommand.txt
new file mode 100644
index 0000000..d9a0acb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 027/PlayerCommand.txt
@@ -0,0 +1 @@
+2,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/OpponentCommand.txt
new file mode 100644
index 0000000..a6f3f91
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/OpponentCommand.txt
@@ -0,0 +1 @@
+2,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/PlayerCommand.txt
new file mode 100644
index 0000000..a01c7f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 028/PlayerCommand.txt
@@ -0,0 +1 @@
+7,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/OpponentCommand.txt
new file mode 100644
index 0000000..77bf522
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/OpponentCommand.txt
@@ -0,0 +1 @@
+3,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/PlayerCommand.txt
new file mode 100644
index 0000000..429fd32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 029/PlayerCommand.txt
@@ -0,0 +1 @@
+5,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/OpponentCommand.txt
new file mode 100644
index 0000000..1ba30d4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/OpponentCommand.txt
@@ -0,0 +1 @@
+7,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/PlayerCommand.txt
new file mode 100644
index 0000000..ddc7f56
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 030/PlayerCommand.txt
@@ -0,0 +1 @@
+7,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/OpponentCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/OpponentCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/PlayerCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 031/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/OpponentCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/PlayerCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 032/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/OpponentCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/OpponentCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/PlayerCommand.txt
new file mode 100644
index 0000000..bee7857
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 033/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/OpponentCommand.txt
new file mode 100644
index 0000000..94d7b0a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/OpponentCommand.txt
@@ -0,0 +1 @@
+6,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/PlayerCommand.txt
new file mode 100644
index 0000000..43be3f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 034/PlayerCommand.txt
@@ -0,0 +1 @@
+2,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/OpponentCommand.txt
new file mode 100644
index 0000000..bee7857
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/PlayerCommand.txt
new file mode 100644
index 0000000..49dd99d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 035/PlayerCommand.txt
@@ -0,0 +1 @@
+1,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/OpponentCommand.txt
new file mode 100644
index 0000000..5ff9de4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/OpponentCommand.txt
@@ -0,0 +1 @@
+3,5,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/PlayerCommand.txt
new file mode 100644
index 0000000..87d322f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 036/PlayerCommand.txt
@@ -0,0 +1 @@
+3,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/OpponentCommand.txt
new file mode 100644
index 0000000..af58f31
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/OpponentCommand.txt
@@ -0,0 +1 @@
+2,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/PlayerCommand.txt
new file mode 100644
index 0000000..743727a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 037/PlayerCommand.txt
@@ -0,0 +1 @@
+7,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/OpponentCommand.txt
new file mode 100644
index 0000000..7388cff
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/OpponentCommand.txt
@@ -0,0 +1 @@
+4,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/PlayerCommand.txt
new file mode 100644
index 0000000..a2a45e9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 038/PlayerCommand.txt
@@ -0,0 +1 @@
+4,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/OpponentCommand.txt
new file mode 100644
index 0000000..9f12d31
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/OpponentCommand.txt
@@ -0,0 +1 @@
+6,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/PlayerCommand.txt
new file mode 100644
index 0000000..d9d71ea
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 039/PlayerCommand.txt
@@ -0,0 +1 @@
+4,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/OpponentCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/PlayerCommand.txt
new file mode 100644
index 0000000..412a2df
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 040/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/OpponentCommand.txt
new file mode 100644
index 0000000..f238916
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/OpponentCommand.txt
@@ -0,0 +1 @@
+2,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/PlayerCommand.txt
new file mode 100644
index 0000000..a91c23f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 041/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/OpponentCommand.txt
new file mode 100644
index 0000000..474bb6c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/OpponentCommand.txt
@@ -0,0 +1 @@
+7,2,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/PlayerCommand.txt
new file mode 100644
index 0000000..ea179d3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 042/PlayerCommand.txt
@@ -0,0 +1 @@
+3,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/OpponentCommand.txt
new file mode 100644
index 0000000..dd03d6a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/OpponentCommand.txt
@@ -0,0 +1 @@
+3,4,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/PlayerCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 043/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/OpponentCommand.txt
new file mode 100644
index 0000000..08ecb10
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/OpponentCommand.txt
@@ -0,0 +1 @@
+3,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/PlayerCommand.txt
new file mode 100644
index 0000000..85eacdb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 044/PlayerCommand.txt
@@ -0,0 +1 @@
+3,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/OpponentCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/PlayerCommand.txt
new file mode 100644
index 0000000..f23ef17
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 045/PlayerCommand.txt
@@ -0,0 +1 @@
+4,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/OpponentCommand.txt
new file mode 100644
index 0000000..3de7cb6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/OpponentCommand.txt
@@ -0,0 +1 @@
+7,0,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/PlayerCommand.txt
new file mode 100644
index 0000000..49c1201
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 046/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/OpponentCommand.txt
new file mode 100644
index 0000000..816366d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/OpponentCommand.txt
@@ -0,0 +1 @@
+0,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/PlayerCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 047/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/OpponentCommand.txt
new file mode 100644
index 0000000..e638283
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/OpponentCommand.txt
@@ -0,0 +1 @@
+3,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/PlayerCommand.txt
new file mode 100644
index 0000000..7d93635
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 048/PlayerCommand.txt
@@ -0,0 +1 @@
+2,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/OpponentCommand.txt
new file mode 100644
index 0000000..07b92b5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/OpponentCommand.txt
@@ -0,0 +1 @@
+3,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 049/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/OpponentCommand.txt
new file mode 100644
index 0000000..f24e83b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/OpponentCommand.txt
@@ -0,0 +1 @@
+4,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/PlayerCommand.txt
new file mode 100644
index 0000000..7d08a5b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 050/PlayerCommand.txt
@@ -0,0 +1 @@
+3,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/PlayerCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 051/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/OpponentCommand.txt
new file mode 100644
index 0000000..a943cb9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/OpponentCommand.txt
@@ -0,0 +1 @@
+3,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/PlayerCommand.txt
new file mode 100644
index 0000000..3775784
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 052/PlayerCommand.txt
@@ -0,0 +1 @@
+1,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/OpponentCommand.txt
new file mode 100644
index 0000000..ab857c9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/PlayerCommand.txt
new file mode 100644
index 0000000..41d5370
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 053/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/OpponentCommand.txt
new file mode 100644
index 0000000..5c88dd1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/OpponentCommand.txt
@@ -0,0 +1 @@
+6,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/PlayerCommand.txt
new file mode 100644
index 0000000..3de7cb6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 054/PlayerCommand.txt
@@ -0,0 +1 @@
+7,0,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/PlayerCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 055/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/OpponentCommand.txt
new file mode 100644
index 0000000..bee7857
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/PlayerCommand.txt
new file mode 100644
index 0000000..c41707e
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 056/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/OpponentCommand.txt
new file mode 100644
index 0000000..b743516
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/OpponentCommand.txt
@@ -0,0 +1 @@
+4,7,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 057/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/OpponentCommand.txt
new file mode 100644
index 0000000..a5bd5ef
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/PlayerCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 058/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/OpponentCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/OpponentCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/PlayerCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 059/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/OpponentCommand.txt
new file mode 100644
index 0000000..61f66b5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/OpponentCommand.txt
@@ -0,0 +1 @@
+3,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/PlayerCommand.txt
new file mode 100644
index 0000000..474bb6c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 060/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,4 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/PlayerCommand.txt
new file mode 100644
index 0000000..674d299
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 061/PlayerCommand.txt
@@ -0,0 +1 @@
+6,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/OpponentCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/OpponentCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/PlayerCommand.txt
new file mode 100644
index 0000000..0b1714b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 062/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,5 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/OpponentCommand.txt
new file mode 100644
index 0000000..0c3ccbf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/OpponentCommand.txt
@@ -0,0 +1 @@
+4,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/PlayerCommand.txt
new file mode 100644
index 0000000..b0f2a85
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 063/PlayerCommand.txt
@@ -0,0 +1 @@
+6,4,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/OpponentCommand.txt
new file mode 100644
index 0000000..94d7b0a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/OpponentCommand.txt
@@ -0,0 +1 @@
+6,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/PlayerCommand.txt
new file mode 100644
index 0000000..ea179d3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 064/PlayerCommand.txt
@@ -0,0 +1 @@
+3,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/OpponentCommand.txt
new file mode 100644
index 0000000..b548cc7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/OpponentCommand.txt
@@ -0,0 +1 @@
+0,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/PlayerCommand.txt
new file mode 100644
index 0000000..b4e7071
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 065/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/PlayerCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 066/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/OpponentCommand.txt
new file mode 100644
index 0000000..22d278e
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/OpponentCommand.txt
@@ -0,0 +1 @@
+7,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/PlayerCommand.txt
new file mode 100644
index 0000000..ca8db41
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 067/PlayerCommand.txt
@@ -0,0 +1 @@
+3,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 068/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 069/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 070/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 071/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 072/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 073/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 074/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/OpponentCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/PlayerCommand.txt b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_iron_curtain_with_teslas/Round 075/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 000/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 000/OpponentCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 000/OpponentCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 000/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 000/PlayerCommand.txt
new file mode 100644
index 0000000..6c57709
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 000/PlayerCommand.txt
@@ -0,0 +1 @@
+1,7,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 001/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 001/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 001/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 001/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 001/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 001/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 002/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 002/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 002/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 002/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 002/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 002/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 003/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 003/OpponentCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 003/OpponentCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 003/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 003/PlayerCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 003/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 004/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 004/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 004/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 004/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 004/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 004/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 005/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 005/OpponentCommand.txt
new file mode 100644
index 0000000..7ca2987
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 005/OpponentCommand.txt
@@ -0,0 +1 @@
+1,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 005/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 005/PlayerCommand.txt
new file mode 100644
index 0000000..3362217
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 005/PlayerCommand.txt
@@ -0,0 +1 @@
+0,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 006/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 006/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 006/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 006/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 006/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 006/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 007/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 007/OpponentCommand.txt
new file mode 100644
index 0000000..a5bd5ef
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 007/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 007/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 007/PlayerCommand.txt
new file mode 100644
index 0000000..b0fd0dc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 007/PlayerCommand.txt
@@ -0,0 +1 @@
+0,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 008/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 008/OpponentCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 008/OpponentCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 008/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 008/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 008/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 009/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 009/OpponentCommand.txt
new file mode 100644
index 0000000..4a8cf07
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 009/OpponentCommand.txt
@@ -0,0 +1 @@
+4,0,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 009/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 009/PlayerCommand.txt
new file mode 100644
index 0000000..d5cd851
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 009/PlayerCommand.txt
@@ -0,0 +1 @@
+5,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 010/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 010/OpponentCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 010/OpponentCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 010/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 010/PlayerCommand.txt
new file mode 100644
index 0000000..ab857c9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 010/PlayerCommand.txt
@@ -0,0 +1 @@
+7,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 011/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 011/OpponentCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 011/OpponentCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 011/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 011/PlayerCommand.txt
new file mode 100644
index 0000000..4119710
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 011/PlayerCommand.txt
@@ -0,0 +1 @@
+2,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 012/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 012/OpponentCommand.txt
new file mode 100644
index 0000000..9b5a49a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 012/OpponentCommand.txt
@@ -0,0 +1 @@
+6,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 012/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 012/PlayerCommand.txt
new file mode 100644
index 0000000..ea9e316
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 012/PlayerCommand.txt
@@ -0,0 +1 @@
+6,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 013/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 013/OpponentCommand.txt
new file mode 100644
index 0000000..87d322f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 013/OpponentCommand.txt
@@ -0,0 +1 @@
+3,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 013/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 013/PlayerCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 013/PlayerCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 014/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 014/OpponentCommand.txt
new file mode 100644
index 0000000..d17d619
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 014/OpponentCommand.txt
@@ -0,0 +1 @@
+5,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 014/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 014/PlayerCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 014/PlayerCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 015/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 015/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 015/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 015/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 015/PlayerCommand.txt
new file mode 100644
index 0000000..e02c049
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 015/PlayerCommand.txt
@@ -0,0 +1 @@
+3,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 016/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 016/OpponentCommand.txt
new file mode 100644
index 0000000..a81a341
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 016/OpponentCommand.txt
@@ -0,0 +1 @@
+7,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 016/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 016/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 016/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 017/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 017/OpponentCommand.txt
new file mode 100644
index 0000000..ea179d3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 017/OpponentCommand.txt
@@ -0,0 +1 @@
+3,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 017/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 017/PlayerCommand.txt
new file mode 100644
index 0000000..f23ef17
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 017/PlayerCommand.txt
@@ -0,0 +1 @@
+4,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 018/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 018/OpponentCommand.txt
new file mode 100644
index 0000000..0a612db
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 018/OpponentCommand.txt
@@ -0,0 +1 @@
+5,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 018/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 018/PlayerCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 018/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 019/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 019/OpponentCommand.txt
new file mode 100644
index 0000000..b557a00
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 019/OpponentCommand.txt
@@ -0,0 +1 @@
+4,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 019/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 019/PlayerCommand.txt
new file mode 100644
index 0000000..26912c7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 019/PlayerCommand.txt
@@ -0,0 +1 @@
+4,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 020/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 020/OpponentCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 020/OpponentCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 020/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 020/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 020/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 021/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 021/OpponentCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 021/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 021/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 021/PlayerCommand.txt
new file mode 100644
index 0000000..67f6e86
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 021/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 022/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 022/OpponentCommand.txt
new file mode 100644
index 0000000..8e935c8
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 022/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 022/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 022/PlayerCommand.txt
new file mode 100644
index 0000000..8a6627b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 022/PlayerCommand.txt
@@ -0,0 +1 @@
+1,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 023/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 023/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 023/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 023/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 023/PlayerCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 023/PlayerCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 024/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 024/OpponentCommand.txt
new file mode 100644
index 0000000..d05a714
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 024/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 024/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 024/PlayerCommand.txt
new file mode 100644
index 0000000..49c1201
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 024/PlayerCommand.txt
@@ -0,0 +1 @@
+7,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 025/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 025/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 025/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 025/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 025/PlayerCommand.txt
new file mode 100644
index 0000000..7388cff
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 025/PlayerCommand.txt
@@ -0,0 +1 @@
+4,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 026/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 026/OpponentCommand.txt
new file mode 100644
index 0000000..9477e06
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 026/OpponentCommand.txt
@@ -0,0 +1 @@
+6,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 026/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 026/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 026/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 027/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 027/OpponentCommand.txt
new file mode 100644
index 0000000..f217f6d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 027/OpponentCommand.txt
@@ -0,0 +1 @@
+5,6,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 027/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 027/PlayerCommand.txt
new file mode 100644
index 0000000..08ecb10
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 027/PlayerCommand.txt
@@ -0,0 +1 @@
+3,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 028/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 028/OpponentCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 028/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 028/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 028/PlayerCommand.txt
new file mode 100644
index 0000000..4dd67d5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 028/PlayerCommand.txt
@@ -0,0 +1 @@
+1,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 029/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 029/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 029/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 029/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 029/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 029/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 030/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 030/OpponentCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 030/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 030/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 030/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 030/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 031/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 031/OpponentCommand.txt
new file mode 100644
index 0000000..67f6e86
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 031/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 031/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 031/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 031/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 032/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 032/OpponentCommand.txt
new file mode 100644
index 0000000..addc906
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 032/OpponentCommand.txt
@@ -0,0 +1 @@
+4,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 032/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 032/PlayerCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 032/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 033/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 033/OpponentCommand.txt
new file mode 100644
index 0000000..5e4b046
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 033/OpponentCommand.txt
@@ -0,0 +1 @@
+0,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 033/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 033/PlayerCommand.txt
new file mode 100644
index 0000000..7f7238b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 033/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 034/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 034/OpponentCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 034/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 034/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 034/PlayerCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 034/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 035/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 035/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 035/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 035/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 035/PlayerCommand.txt
new file mode 100644
index 0000000..153865b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 035/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 036/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 036/OpponentCommand.txt
new file mode 100644
index 0000000..3177984
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 036/OpponentCommand.txt
@@ -0,0 +1 @@
+2,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 036/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 036/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 036/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 037/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 037/OpponentCommand.txt
new file mode 100644
index 0000000..7ca2987
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 037/OpponentCommand.txt
@@ -0,0 +1 @@
+1,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 037/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 037/PlayerCommand.txt
new file mode 100644
index 0000000..1571d81
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 037/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 038/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 038/OpponentCommand.txt
new file mode 100644
index 0000000..9f89a93
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 038/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 038/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 038/PlayerCommand.txt
new file mode 100644
index 0000000..addc906
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 038/PlayerCommand.txt
@@ -0,0 +1 @@
+4,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 039/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 039/OpponentCommand.txt
new file mode 100644
index 0000000..a825030
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 039/OpponentCommand.txt
@@ -0,0 +1 @@
+1,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 039/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 039/PlayerCommand.txt
new file mode 100644
index 0000000..3362217
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 039/PlayerCommand.txt
@@ -0,0 +1 @@
+0,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 040/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 040/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 040/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 040/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 040/PlayerCommand.txt
new file mode 100644
index 0000000..3ca9676
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 040/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 041/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 041/OpponentCommand.txt
new file mode 100644
index 0000000..67f6e86
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 041/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 041/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 041/PlayerCommand.txt
new file mode 100644
index 0000000..7f7238b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 041/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 042/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 042/OpponentCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 042/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 042/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 042/PlayerCommand.txt
new file mode 100644
index 0000000..aa178b0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 042/PlayerCommand.txt
@@ -0,0 +1 @@
+3,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 043/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 043/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 043/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 043/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 043/PlayerCommand.txt
new file mode 100644
index 0000000..55526f5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 043/PlayerCommand.txt
@@ -0,0 +1 @@
+1,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 044/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 044/OpponentCommand.txt
new file mode 100644
index 0000000..b4e7071
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 044/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 044/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 044/PlayerCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 044/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 045/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 045/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 045/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 045/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 045/PlayerCommand.txt
new file mode 100644
index 0000000..3177984
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 045/PlayerCommand.txt
@@ -0,0 +1 @@
+2,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 046/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 046/OpponentCommand.txt
new file mode 100644
index 0000000..cb47d55
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 046/OpponentCommand.txt
@@ -0,0 +1 @@
+0,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 046/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 046/PlayerCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 046/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 047/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 047/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 047/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 047/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 047/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 047/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 048/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 048/OpponentCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 048/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 048/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 048/PlayerCommand.txt
new file mode 100644
index 0000000..f1d02f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 048/PlayerCommand.txt
@@ -0,0 +1 @@
+0,0,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 049/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 049/OpponentCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 049/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 049/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 049/PlayerCommand.txt
new file mode 100644
index 0000000..3ca9676
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 049/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 050/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 050/OpponentCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 050/OpponentCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 050/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 050/PlayerCommand.txt
new file mode 100644
index 0000000..816366d
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 050/PlayerCommand.txt
@@ -0,0 +1 @@
+0,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 051/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 051/OpponentCommand.txt
new file mode 100644
index 0000000..43be3f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 051/OpponentCommand.txt
@@ -0,0 +1 @@
+2,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 051/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 051/PlayerCommand.txt
new file mode 100644
index 0000000..7f7238b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 051/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 052/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 052/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 052/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 052/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 052/PlayerCommand.txt
new file mode 100644
index 0000000..17d7db2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 052/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 053/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 053/OpponentCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 053/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 053/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 053/PlayerCommand.txt
new file mode 100644
index 0000000..0c3ccbf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 053/PlayerCommand.txt
@@ -0,0 +1 @@
+4,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 054/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 054/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 054/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 054/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 054/PlayerCommand.txt
new file mode 100644
index 0000000..a81a341
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 054/PlayerCommand.txt
@@ -0,0 +1 @@
+7,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 055/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 055/OpponentCommand.txt
new file mode 100644
index 0000000..b548cc7
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 055/OpponentCommand.txt
@@ -0,0 +1 @@
+0,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 055/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 055/PlayerCommand.txt
new file mode 100644
index 0000000..1084f37
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 055/PlayerCommand.txt
@@ -0,0 +1 @@
+6,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 056/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 056/OpponentCommand.txt
new file mode 100644
index 0000000..ac6c42a
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 056/OpponentCommand.txt
@@ -0,0 +1 @@
+5,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 056/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 056/PlayerCommand.txt
new file mode 100644
index 0000000..61f66b5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 056/PlayerCommand.txt
@@ -0,0 +1 @@
+3,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 057/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 057/OpponentCommand.txt
new file mode 100644
index 0000000..4d83fd9
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 057/OpponentCommand.txt
@@ -0,0 +1 @@
+3,1,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 057/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 057/PlayerCommand.txt
new file mode 100644
index 0000000..3ca9676
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 057/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 058/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 058/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 058/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 058/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 058/PlayerCommand.txt
new file mode 100644
index 0000000..1571d81
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 058/PlayerCommand.txt
@@ -0,0 +1 @@
+5,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 059/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 059/OpponentCommand.txt
new file mode 100644
index 0000000..b4e7071
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 059/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 059/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 059/PlayerCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 059/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 060/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 060/OpponentCommand.txt
new file mode 100644
index 0000000..429fd32
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 060/OpponentCommand.txt
@@ -0,0 +1 @@
+5,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 060/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 060/PlayerCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 060/PlayerCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 061/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 061/OpponentCommand.txt
new file mode 100644
index 0000000..ccd082b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 061/OpponentCommand.txt
@@ -0,0 +1 @@
+6,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 061/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 061/PlayerCommand.txt
new file mode 100644
index 0000000..0c3ccbf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 061/PlayerCommand.txt
@@ -0,0 +1 @@
+4,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 062/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 062/OpponentCommand.txt
new file mode 100644
index 0000000..49c1201
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 062/OpponentCommand.txt
@@ -0,0 +1 @@
+7,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 062/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 062/PlayerCommand.txt
new file mode 100644
index 0000000..722ec58
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 062/PlayerCommand.txt
@@ -0,0 +1 @@
+4,2,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 063/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 063/OpponentCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 063/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 063/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 063/PlayerCommand.txt
new file mode 100644
index 0000000..c602c71
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 063/PlayerCommand.txt
@@ -0,0 +1 @@
+2,0,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 064/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 064/OpponentCommand.txt
new file mode 100644
index 0000000..3dee0c6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 064/OpponentCommand.txt
@@ -0,0 +1 @@
+6,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 064/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 064/PlayerCommand.txt
new file mode 100644
index 0000000..3ca9676
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 064/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 065/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 065/OpponentCommand.txt
new file mode 100644
index 0000000..a7503e5
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 065/OpponentCommand.txt
@@ -0,0 +1 @@
+7,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 065/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 065/PlayerCommand.txt
new file mode 100644
index 0000000..a81a341
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 065/PlayerCommand.txt
@@ -0,0 +1 @@
+7,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 066/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 066/OpponentCommand.txt
new file mode 100644
index 0000000..a6f3f91
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 066/OpponentCommand.txt
@@ -0,0 +1 @@
+2,6,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 066/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 066/PlayerCommand.txt
new file mode 100644
index 0000000..b77a79c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 066/PlayerCommand.txt
@@ -0,0 +1 @@
+2,3,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 067/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 067/OpponentCommand.txt
new file mode 100644
index 0000000..bb03eca
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 067/OpponentCommand.txt
@@ -0,0 +1 @@
+5,3,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 067/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 067/PlayerCommand.txt
new file mode 100644
index 0000000..7f7238b
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 067/PlayerCommand.txt
@@ -0,0 +1 @@
+6,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 068/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 068/OpponentCommand.txt
new file mode 100644
index 0000000..e874b1f
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 068/OpponentCommand.txt
@@ -0,0 +1 @@
+1,6,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 068/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 068/PlayerCommand.txt
new file mode 100644
index 0000000..b0fd0dc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 068/PlayerCommand.txt
@@ -0,0 +1 @@
+0,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 069/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 069/OpponentCommand.txt
new file mode 100644
index 0000000..46660d6
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 069/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 069/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 069/PlayerCommand.txt
new file mode 100644
index 0000000..ddc7f56
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 069/PlayerCommand.txt
@@ -0,0 +1 @@
+7,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 070/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 070/OpponentCommand.txt
new file mode 100644
index 0000000..dc922cc
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 070/OpponentCommand.txt
@@ -0,0 +1 @@
+1,5,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 070/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 070/PlayerCommand.txt
new file mode 100644
index 0000000..93ec9b2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 070/PlayerCommand.txt
@@ -0,0 +1 @@
+6,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 071/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 071/OpponentCommand.txt
new file mode 100644
index 0000000..ee791e3
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 071/OpponentCommand.txt
@@ -0,0 +1 @@
+4,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 071/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 071/PlayerCommand.txt
new file mode 100644
index 0000000..3ca9676
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 071/PlayerCommand.txt
@@ -0,0 +1 @@
+7,3,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 072/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 072/OpponentCommand.txt
new file mode 100644
index 0000000..b4e7071
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 072/OpponentCommand.txt
@@ -0,0 +1 @@
+5,1,0 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 072/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 072/PlayerCommand.txt
new file mode 100644
index 0000000..3d765f0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 072/PlayerCommand.txt
@@ -0,0 +1 @@
+5,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 073/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 073/OpponentCommand.txt
new file mode 100644
index 0000000..a01c7f4
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 073/OpponentCommand.txt
@@ -0,0 +1 @@
+7,4,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 073/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 073/PlayerCommand.txt
new file mode 100644
index 0000000..08ceedf
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 073/PlayerCommand.txt
@@ -0,0 +1 @@
+4,5,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 074/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 074/OpponentCommand.txt
new file mode 100644
index 0000000..10532f2
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 074/OpponentCommand.txt
@@ -0,0 +1 @@
+0,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 074/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 074/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 074/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 075/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 075/OpponentCommand.txt
new file mode 100644
index 0000000..08ecb10
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 075/OpponentCommand.txt
@@ -0,0 +1 @@
+3,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 075/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 075/PlayerCommand.txt
new file mode 100644
index 0000000..c41707e
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 075/PlayerCommand.txt
@@ -0,0 +1 @@
+7,7,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 076/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 076/OpponentCommand.txt
new file mode 100644
index 0000000..8bb009c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 076/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 076/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 076/PlayerCommand.txt
new file mode 100644
index 0000000..85eacdb
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 076/PlayerCommand.txt
@@ -0,0 +1 @@
+3,2,2 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 077/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 077/OpponentCommand.txt
new file mode 100644
index 0000000..323dbb1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 077/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 077/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 077/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 077/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 078/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 078/OpponentCommand.txt
new file mode 100644
index 0000000..1c0a0b0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 078/OpponentCommand.txt
@@ -0,0 +1 @@
+1,2,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 078/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 078/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 078/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 079/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 079/OpponentCommand.txt
new file mode 100644
index 0000000..8bb009c
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 079/OpponentCommand.txt
@@ -0,0 +1 @@
+6,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 079/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 079/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 079/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 080/OpponentCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 080/OpponentCommand.txt
new file mode 100644
index 0000000..323dbb1
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 080/OpponentCommand.txt
@@ -0,0 +1 @@
+7,1,1 \ No newline at end of file
diff --git a/2018-tower-defence/tests/v300_normal_towers/Round 080/PlayerCommand.txt b/2018-tower-defence/tests/v300_normal_towers/Round 080/PlayerCommand.txt
new file mode 100644
index 0000000..bdb74d0
--- /dev/null
+++ b/2018-tower-defence/tests/v300_normal_towers/Round 080/PlayerCommand.txt
@@ -0,0 +1 @@
+No Command \ No newline at end of file
diff --git a/2019-worms/.gitignore b/2019-worms/.gitignore
new file mode 100644
index 0000000..791452f
--- /dev/null
+++ b/2019-worms/.gitignore
@@ -0,0 +1,3 @@
+target
+**/*.rs.bk
+submission.zip
diff --git a/2019-worms/Cargo.lock b/2019-worms/Cargo.lock
new file mode 100644
index 0000000..f5c96f0
--- /dev/null
+++ b/2019-worms/Cargo.lock
@@ -0,0 +1,299 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "arrayvec"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "either"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "fnv"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itoa"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memoffset"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-traits"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ryu"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "scopeguard"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "steam-powered-wyrm"
+version = "1.0.0"
+dependencies = [
+ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.15.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
+"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
+"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13"
+"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
+"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
+"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
+"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
+"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
+"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
+"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
+"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
+"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
+"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
+"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
+"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
+"checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4"
+"checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2"
+"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
+"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
+"checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79"
+"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
+"checksum syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b4cfac95805274c6afdb12d8f770fa2d27c045953e7b630a81801953699a9a"
+"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/2019-worms/Cargo.toml b/2019-worms/Cargo.toml
new file mode 100644
index 0000000..e7e33fc
--- /dev/null
+++ b/2019-worms/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "steam-powered-wyrm"
+version = "1.0.0"
+edition = "2018"
+
+[dependencies]
+serde = { version = "1.0.90", features = ["derive"] }
+serde_json = "1.0.39"
+time = "0.1.42"
+num-traits = "0.2.6"
+arrayvec = "0.4.10"
+fnv = "1.0.6"
+rayon = "1.1.0"
+
+[profile.release]
+# debug = true
+# debug-assertions = true
+# overflow-checks = true
+
+[features]
+logging = []
+
+default = []
diff --git a/2019-worms/Makefile b/2019-worms/Makefile
new file mode 100644
index 0000000..3f3b532
--- /dev/null
+++ b/2019-worms/Makefile
@@ -0,0 +1,16 @@
+build:
+ cargo build --release
+
+test:
+ cargo test --release
+
+profile:
+ cargo flamegraph --bin benchmark
+
+clean:
+ cargo clean
+
+submission.zip:
+ zip --filesync -r9 submission.zip bot.json Cargo.lock Cargo.toml src
+
+.PHONY: build test profile clean submission.zip
diff --git a/2019-worms/README.md b/2019-worms/README.md
new file mode 100644
index 0000000..a0c3008
--- /dev/null
+++ b/2019-worms/README.md
@@ -0,0 +1,81 @@
+# Steam Powered Wyrm
+
+This is an entry to the 2019 Entelect Challenge.
+
+## Environment Setup
+
+The Rust compiler toolchain can be downloaded from the Rust project
+website.
+
+https://www.rust-lang.org/en-US/install.html
+
+The project requires these versions of the toolchain (or later).
+
+- cargo 1.34.0
+- rustc 1.34.0
+
+## Building
+
+Rust's official build tool is called Cargo. It will download
+dependencies and call the Rust compiler as required. Dependencies are
+configured in [Cargo.toml](./Cargo.toml).
+
+Cargo needs to be called from the root of the bot (the folder with the
+Cargo.toml file).
+
+```sh
+cargo build --release
+```
+
+## Running Tests
+
+Rust has support for unit testing built into the language. Any
+functions marked with the `#[test]` annotation are considered tests.
+
+Tests can be run using Cargo:
+
+```sh
+cargo test
+```
+
+More information on how to write tests is available in
+[The Rust Programming Language](https://doc.rust-lang.org/book/ch11-00-testing.html).
+
+## Exporting the compiled executable
+
+By default, Rust produces statically linked binaries, so you can just
+copy out the executable file from the target directory and put it
+wherever you want.
+
+The name of the binary will match the name of the binary crate in
+Cargo.toml.
+
+Note: This binary has been built for the platform that it was compiled
+on. In other words, if it was compiled on 64 bit Linux, you cannot
+copy the binary to a Windows machine and run it. You WILL be able to
+copy the binary between similar 64 bit Linux machines.
+
+The machine that the compiled binary is run on does not need to have
+the Rust toolchain installed.
+
+```sh
+cp ./target/release/<botFileName> <dest>
+```
+
+## Running
+
+The compiled binary can be executed directly.
+
+```sh
+./target/release/<botFileName>
+```
+
+For convenience in development, you can compile and run through Cargo.
+
+Note: This is not recommended for the tournament servers, since there
+is a small runtime cost in Cargo checking that the compiled binary is
+up to date before running it.
+
+```sh
+cargo run --release
+```
diff --git a/2019-worms/bot.json b/2019-worms/bot.json
new file mode 100644
index 0000000..5578730
--- /dev/null
+++ b/2019-worms/bot.json
@@ -0,0 +1,8 @@
+{
+ "author": "Justin Wernick",
+ "email": "justin@worthe-it.co.za",
+ "nickName": "Steam Powered Wyrm",
+ "botLocation": "/target/release/",
+ "botFileName": "steam-powered-wyrm",
+ "botLanguage": "rust"
+}
diff --git a/2019-worms/src/bin/benchmark.rs b/2019-worms/src/bin/benchmark.rs
new file mode 100644
index 0000000..84e869e
--- /dev/null
+++ b/2019-worms/src/bin/benchmark.rs
@@ -0,0 +1,22 @@
+use std::path::Path;
+
+use time::{Duration, PreciseTime};
+
+use steam_powered_wyrm::game;
+use steam_powered_wyrm::json;
+use steam_powered_wyrm::strategy::{choose_move, ScoreConfig};
+
+fn main() {
+ let max_time = Duration::milliseconds(19950);
+ let start_time = PreciseTime::now();
+
+ match json::read_state_from_json_file(&Path::new(&format!("./tests/example-state.json"))) {
+ Ok(json_state) => {
+ let new_board = game::GameBoard::new(json_state);
+ let _ = choose_move(&new_board, &ScoreConfig::default(), start_time, max_time);
+ }
+ Err(e) => {
+ eprintln!("WARN: State file could not be parsed: {}", e);
+ }
+ };
+}
diff --git a/2019-worms/src/bin/explore-config.rs b/2019-worms/src/bin/explore-config.rs
new file mode 100644
index 0000000..5fb599a
--- /dev/null
+++ b/2019-worms/src/bin/explore-config.rs
@@ -0,0 +1,116 @@
+use std::path::Path;
+
+use rayon::prelude::*;
+
+use steam_powered_wyrm::game;
+use steam_powered_wyrm::json;
+use steam_powered_wyrm::strategy::{choose_move_with_normalized_perf, ScoreConfig};
+
+fn main() {
+ let initial_state = game::GameBoard::new(
+ json::read_state_from_json_file(&Path::new(&format!("./tests/example-state.json")))
+ .unwrap(),
+ );
+ let depth = 1000;
+
+ let configs = ScoreConfigTrials {
+ max_health_weight: vec![50., 150.],
+ total_health_weight: vec![5., 20.],
+ points_weight: vec![1.],
+ victory_weight: vec![3000., 3500., 4000., 4500., 5000., 5500., 6000., 7000.],
+ snowball_weight: vec![10.],
+ bomb_weight: vec![0.],
+ explore_exploit_weight: vec![10., 500.],
+ }
+ .reify();
+
+ eprintln!("{} configs being tested", configs.len());
+
+ let mut victories = vec![0; configs.len()];
+
+ for i in 0..configs.len() {
+ eprintln!("Progress: {} of {}", i, configs.len());
+
+ let outcomes: Vec<(usize, game::SimulationOutcome)> = (i + 1..configs.len())
+ .collect::<Vec<usize>>()
+ .par_iter()
+ .map(|j| {
+ let mut state = initial_state.clone();
+
+ while state.outcome == game::SimulationOutcome::Continue {
+ let commands = [
+ choose_move_with_normalized_perf(&state, &configs[i], 0, depth),
+ choose_move_with_normalized_perf(&state, &configs[*j], 1, depth),
+ ];
+ state.simulate(commands);
+ }
+
+ (*j, state.outcome)
+ })
+ .collect();
+ for (j, outcome) in outcomes {
+ match outcome {
+ game::SimulationOutcome::PlayerWon(0) => victories[i] += 1,
+ game::SimulationOutcome::PlayerWon(1) => victories[j] += 1,
+ _ => {}
+ };
+ }
+ }
+
+ println!("victories, max_health_weight, total_health_weight, points_weight, victory_weight, snowball_weight, bomb_weight, explore_exploit_weight");
+
+ for (config, victories) in configs.into_iter().zip(victories.iter()) {
+ println!(
+ "{}, {}, {}, {}, {}, {}, {}, {}",
+ victories,
+ config.max_health_weight,
+ config.total_health_weight,
+ config.points_weight,
+ config.victory_weight,
+ config.snowball_weight,
+ config.bomb_weight,
+ config.explore_exploit_weight
+ );
+ }
+}
+
+pub struct ScoreConfigTrials {
+ pub max_health_weight: Vec<f32>,
+ pub total_health_weight: Vec<f32>,
+ pub points_weight: Vec<f32>,
+ pub victory_weight: Vec<f32>,
+ pub snowball_weight: Vec<f32>,
+ pub bomb_weight: Vec<f32>,
+ pub explore_exploit_weight: Vec<f32>,
+}
+
+impl ScoreConfigTrials {
+ fn reify(self) -> Vec<ScoreConfig> {
+ let mut result = Vec::new();
+ for max_health_weight in &self.max_health_weight {
+ for total_health_weight in &self.total_health_weight {
+ for points_weight in &self.points_weight {
+ for victory_weight in &self.victory_weight {
+ for snowball_weight in &self.snowball_weight {
+ for bomb_weight in &self.bomb_weight {
+ for explore_exploit_weight in &self.explore_exploit_weight {
+ result.push(ScoreConfig {
+ max_health_weight: *max_health_weight,
+ total_health_weight: *total_health_weight,
+ points_weight: *points_weight,
+ victory_weight: *victory_weight,
+ snowball_weight: *snowball_weight,
+ bomb_weight: *bomb_weight,
+ explore_exploit_weight: *explore_exploit_weight,
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result
+ }
+}
diff --git a/2019-worms/src/command.rs b/2019-worms/src/command.rs
new file mode 100644
index 0000000..c6d6695
--- /dev/null
+++ b/2019-worms/src/command.rs
@@ -0,0 +1,73 @@
+use crate::geometry::Direction;
+use crate::geometry::Point2d;
+use std::fmt;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct Command {
+ pub worm: Option<i32>,
+ pub action: Action,
+}
+
+impl fmt::Display for Command {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.worm {
+ Some(worm) => write!(f, "select {};{}", worm, self.action),
+ None => write!(f, "{}", self.action),
+ }
+ }
+}
+
+impl Command {
+ pub fn with_select(worm: i32, action: Action) -> Command {
+ Command {
+ worm: Some(worm),
+ action,
+ }
+ }
+
+ pub fn new(action: Action) -> Command {
+ Command { worm: None, action }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum Action {
+ Move(Point2d),
+ Dig(Point2d),
+ Shoot(Direction),
+ Bomb(Point2d),
+ Snowball(Point2d),
+ DoNothing,
+}
+
+impl fmt::Display for Action {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Action::*;
+ match self {
+ Move(p) => write!(f, "move {} {}", p.x, p.y),
+ Dig(p) => write!(f, "dig {} {}", p.x, p.y),
+ Shoot(dir) => write!(f, "shoot {}", dir),
+ Bomb(p) => write!(f, "banana {} {}", p.x, p.y),
+ Snowball(p) => write!(f, "snowball {} {}", p.x, p.y),
+ DoNothing => write!(f, "nothing"),
+ }
+ }
+}
+
+impl Action {
+ pub fn is_attack(&self) -> bool {
+ use Action::*;
+ match self {
+ Shoot(_) | Bomb(_) => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_snowball(&self) -> bool {
+ use Action::*;
+ match self {
+ Snowball(_) => true,
+ _ => false,
+ }
+ }
+}
diff --git a/2019-worms/src/constants.rs b/2019-worms/src/constants.rs
new file mode 100644
index 0000000..3f36db4
--- /dev/null
+++ b/2019-worms/src/constants.rs
@@ -0,0 +1,218 @@
+use crate::geometry::Vec2d;
+
+pub mod lava;
+pub use self::lava::*;
+
+pub const MAP_SIZE: usize = 33;
+pub const MAP_ROW_SIZE: [MapRow; MAP_SIZE] = [
+ MapRow {
+ start_bit: 0,
+ x_offset: 11,
+ },
+ MapRow {
+ start_bit: 11,
+ x_offset: 8,
+ },
+ MapRow {
+ start_bit: 28,
+ x_offset: 7,
+ },
+ MapRow {
+ start_bit: 47,
+ x_offset: 6,
+ },
+ MapRow {
+ start_bit: 68,
+ x_offset: 4,
+ },
+ MapRow {
+ start_bit: 93,
+ x_offset: 4,
+ },
+ MapRow {
+ start_bit: 118,
+ x_offset: 3,
+ },
+ MapRow {
+ start_bit: 145,
+ x_offset: 2,
+ },
+ MapRow {
+ start_bit: 174,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 205,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 236,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 267,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 300,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 333,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 366,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 399,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 432,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 465,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 498,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 531,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 564,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 597,
+ x_offset: 0,
+ },
+ MapRow {
+ start_bit: 630,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 661,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 692,
+ x_offset: 1,
+ },
+ MapRow {
+ start_bit: 723,
+ x_offset: 2,
+ },
+ MapRow {
+ start_bit: 752,
+ x_offset: 3,
+ },
+ MapRow {
+ start_bit: 779,
+ x_offset: 4,
+ },
+ MapRow {
+ start_bit: 804,
+ x_offset: 4,
+ },
+ MapRow {
+ start_bit: 829,
+ x_offset: 6,
+ },
+ MapRow {
+ start_bit: 850,
+ x_offset: 7,
+ },
+ MapRow {
+ start_bit: 869,
+ x_offset: 8,
+ },
+ MapRow {
+ start_bit: 886,
+ x_offset: 11,
+ },
+];
+pub const MAP_BITSIZE: usize = 897;
+pub const MAP_U64S: usize = 15;
+
+pub struct MapRow {
+ pub start_bit: usize,
+ pub x_offset: usize,
+}
+
+impl MapRow {
+ pub fn len(&self) -> usize {
+ MAP_SIZE - 2 * self.x_offset
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+}
+
+pub const HEALTH_PACK_VALUE: i32 = 10;
+
+pub const SHOOT_RANGE: i8 = 4;
+pub const SHOOT_RANGE_DIAGONAL: i8 = 3;
+pub const SHOOT_DAMAGE: i32 = 8;
+
+pub const BOMB_RANGE: i8 = 5;
+pub const BOMB_DAMAGED_SPACES: usize = 13;
+pub const BOMB_DAMAGE_RANGE: i8 = 2;
+pub const BOMB_DAMAGES: [(Vec2d, i32); BOMB_DAMAGED_SPACES] = [
+ (Vec2d::new(0, -2), 7),
+ (Vec2d::new(2, 0), 7),
+ (Vec2d::new(0, 2), 7),
+ (Vec2d::new(-2, 0), 7),
+ (Vec2d::new(1, -1), 11),
+ (Vec2d::new(1, 1), 11),
+ (Vec2d::new(-1, 1), 11),
+ (Vec2d::new(-1, -1), 11),
+ (Vec2d::new(0, -1), 13),
+ (Vec2d::new(1, 0), 13),
+ (Vec2d::new(0, 1), 13),
+ (Vec2d::new(-1, 0), 13),
+ (Vec2d::new(0, 0), 20),
+];
+
+pub const SNOWBALL_RANGE: i8 = 5;
+pub const SNOWBALL_FREEZES_SPACES: usize = 9;
+pub const SNOWBALL_FREEZE_RANGE: i8 = 1;
+pub const SNOWBALL_FREEZES: [Vec2d; SNOWBALL_FREEZES_SPACES] = [
+ Vec2d::new(-1, -1),
+ Vec2d::new(0, -1),
+ Vec2d::new(1, -1),
+ Vec2d::new(-1, 0),
+ Vec2d::new(0, 0),
+ Vec2d::new(1, 0),
+ Vec2d::new(-1, 1),
+ Vec2d::new(0, 1),
+ Vec2d::new(1, 1),
+];
+
+pub const MISSED_ATTACK_SCORE: i32 = 2;
+pub const ATTACK_SCORE_MULTIPLIER: i32 = 2;
+pub const KILL_SCORE: i32 = 40;
+pub const DIG_SCORE: i32 = 7;
+pub const MOVE_SCORE: i32 = 5;
+pub const INVALID_COMMAND_SCORE_PENALTY: i32 = 4;
+
+pub const STARTING_BOMBS: u8 = 3;
+pub const STARTING_SNOWBALLS: u8 = 3;
+
+pub const COLLISION_DAMAGE: i32 = 20;
+
+pub const LAVA_DAMAGE: i32 = 3;
+
+pub const LAVA_ROUND_START: usize = 100;
+pub const LAVA_ROUND_END: usize = 350;
+pub const MAX_ROUNDS: usize = 400;
+
+pub const FREEZE_DURATION: u8 = 5;
+pub const FREEZE_SCORE: i32 = 17;
diff --git a/2019-worms/src/constants/lava.rs b/2019-worms/src/constants/lava.rs
new file mode 100644
index 0000000..238d668
--- /dev/null
+++ b/2019-worms/src/constants/lava.rs
@@ -0,0 +1,6225 @@
+use crate::constants::*;
+use crate::game::map::Map;
+
+#[allow(clippy::all)]
+pub const LAVA_MAP: [Map; MAX_ROUNDS as usize + 1] = [
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE0003E003FDF,
+ 0x60000038000038,
+ 0xE00000030000,
+ 0x180000003800,
+ 0x180000000C00,
+ 0x600000003000,
+ 0x80000000C000,
+ 0x6000000020000,
+ 0x180000000C0000,
+ 0x60000000300000,
+ 0x38000000300000,
+ 0x18000000E0000,
+ 0x3800003800000C00,
+ 0xF7F800F8000E0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE0003E003FFF,
+ 0x60000038000038,
+ 0xE00000030000,
+ 0x180000003800,
+ 0x180000000C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x60000000300000,
+ 0x38000000300000,
+ 0x18000000E0000,
+ 0x3800003800000C00,
+ 0xFFF800F8000E0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE0003E003FFF,
+ 0x60000038000038,
+ 0xE00000030000,
+ 0x180000003800,
+ 0x180000000C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x60000000300000,
+ 0x38000000300000,
+ 0x18000000E0000,
+ 0x3800003800000C00,
+ 0xFFF800F8000E0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1E0003F007FFF,
+ 0x6000003800003C,
+ 0xF00000070000,
+ 0x180000003800,
+ 0x1C0000001C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x70000000700000,
+ 0x38000000300000,
+ 0x1C000001E0000,
+ 0x7800003800000C00,
+ 0xFFFC01F8000F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1E0003F007FFF,
+ 0x6000003800003C,
+ 0xF00000070000,
+ 0x180000003800,
+ 0x1C0000001C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x70000000700000,
+ 0x38000000300000,
+ 0x1C000001E0000,
+ 0x7800003800000C00,
+ 0xFFFC01F8000F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1E0003F007FFF,
+ 0x6000003800003C,
+ 0xF00000070000,
+ 0x180000003800,
+ 0x1C0000001C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x70000000700000,
+ 0x38000000300000,
+ 0x1C000001E0000,
+ 0x7800003800000C00,
+ 0xFFFC01F8000F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007F007FFF,
+ 0xE000003C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x1C0000001C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x70000000700000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007800000E00,
+ 0xFFFC01FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007F007FFF,
+ 0xF000007C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x1C0000001C00,
+ 0x600000003000,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x180000000C0000,
+ 0x70000000700000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007C00001E00,
+ 0xFFFC01FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007F80FFFF,
+ 0xF000007C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x3C0000001C00,
+ 0x600000003800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x380000000C0000,
+ 0x70000000780000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007C00001E00,
+ 0xFFFE03FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007F80FFFF,
+ 0xF000007C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x3C0000001C00,
+ 0x600000003800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x380000000C0000,
+ 0x70000000780000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007C00001E00,
+ 0xFFFE03FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007F80FFFF,
+ 0xF000007C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x3C0000001C00,
+ 0x600000003800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x380000000C0000,
+ 0x70000000780000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007C00001E00,
+ 0xFFFE03FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x1F0007FC1FFFF,
+ 0xF000007C00007C,
+ 0xF00000078000,
+ 0x1C0000007800,
+ 0x3C0000001C00,
+ 0x700000007800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x3C0000001C0000,
+ 0x70000000780000,
+ 0x3C000000700000,
+ 0x3C000001E0000,
+ 0x7C00007C00001E00,
+ 0xFFFF07FC001F0000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F0007FC1FFFF,
+ 0xF000007C00007E,
+ 0x1F00000078000,
+ 0x1C0000007C00,
+ 0x3C0000001C00,
+ 0x700000007800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x3C0000001C0000,
+ 0x70000000780000,
+ 0x7C000000700000,
+ 0x3C000001F0000,
+ 0xFC00007C00001E00,
+ 0xFFFF07FC001F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F800FFC1FFFF,
+ 0xF000007C00007E,
+ 0x1F00000078000,
+ 0x3C0000007C00,
+ 0x3C0000001E00,
+ 0x700000007800,
+ 0x180000000C000,
+ 0x6000000030000,
+ 0x3C0000001C0000,
+ 0xF0000000780000,
+ 0x7C000000780000,
+ 0x3C000001F0000,
+ 0xFC00007C00001E00,
+ 0xFFFF07FE003F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F800FFE3FFFF,
+ 0xF000007C00007E,
+ 0x1F00000078000,
+ 0x3C0000007C00,
+ 0x3C0000001E00,
+ 0xF00000007800,
+ 0x180000000E000,
+ 0xE000000030000,
+ 0x3C0000001E0000,
+ 0xF0000000780000,
+ 0x7C000000780000,
+ 0x3C000001F0000,
+ 0xFC00007C00001E00,
+ 0xFFFF8FFE003F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F800FFE3FFFF,
+ 0xF000007C00007E,
+ 0x1F00000078000,
+ 0x3C0000007C00,
+ 0x3C0000001E00,
+ 0xF00000007800,
+ 0x180000000E000,
+ 0xE000000030000,
+ 0x3C0000001E0000,
+ 0xF0000000780000,
+ 0x7C000000780000,
+ 0x3C000001F0000,
+ 0xFC00007C00001E00,
+ 0xFFFF8FFE003F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F800FFFFFFFF,
+ 0xF000007E0000FE,
+ 0x1F800000F8000,
+ 0x3C0000007C00,
+ 0x3C0000001E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF0000000780000,
+ 0x7C000000780000,
+ 0x3E000003F0000,
+ 0xFE0000FC00001E00,
+ 0xFFFFFFFE003F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3F800FFFFFFFF,
+ 0xF000007E0000FE,
+ 0x1F800000F8000,
+ 0x3C0000007C00,
+ 0x3C0000001E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF0000000780000,
+ 0x7C000000780000,
+ 0x3E000003F0000,
+ 0xFE0000FC00001E00,
+ 0xFFFFFFFE003F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3FC01FFFFFFFF,
+ 0x1F80000FE0000FE,
+ 0x1F800000FC000,
+ 0x3C0000007C00,
+ 0x3E0000003E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF8000000F80000,
+ 0x7C000000780000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF007F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x3FC01FFFFFFFF,
+ 0x1F80000FE0000FE,
+ 0x1F800000FC000,
+ 0x3C0000007C00,
+ 0x3E0000003E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF8000000F80000,
+ 0x7C000000780000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF007F8000,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FC01FFFFFFFF,
+ 0x1F80000FE0000FF,
+ 0x1F800000FC000,
+ 0x3E000000FC00,
+ 0x3E0000003E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF8000000F80000,
+ 0x7E000000F80000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF007FC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FC01FFFFFFFF,
+ 0x1F80000FE0000FF,
+ 0x1F800000FC000,
+ 0x3E000000FC00,
+ 0x3E0000003E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF8000000F80000,
+ 0x7E000000F80000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF007FC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FC01FFFFFFFF,
+ 0x1F80000FE0000FF,
+ 0x1F800000FC000,
+ 0x3E000000FC00,
+ 0x3E0000003E00,
+ 0xF00000007800,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x3C0000001E0000,
+ 0xF8000000F80000,
+ 0x7E000000F80000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF007FC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FE03FFFFFFFF,
+ 0x1F80000FE0000FF,
+ 0x1F800000FC000,
+ 0x3E000000FC00,
+ 0x7E0000003E00,
+ 0xF00000007C00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7C0000001E0000,
+ 0xF8000000FC0000,
+ 0x7E000000F80000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF80FFC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FE03FFFFFFFF,
+ 0x1F80000FE0000FF,
+ 0x1F800000FC000,
+ 0x3E000000FC00,
+ 0x7E0000003E00,
+ 0xF00000007C00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7C0000001E0000,
+ 0xF8000000FC0000,
+ 0x7E000000F80000,
+ 0x7E000003F0000,
+ 0xFE0000FE00003F00,
+ 0xFFFFFFFF80FFC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FE03FFFFFFFF,
+ 0x1F80000FF0001FF,
+ 0x3F800000FC000,
+ 0x3E000000FE00,
+ 0x7E0000003E00,
+ 0xF00000007C00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7C0000001E0000,
+ 0xF8000000FC0000,
+ 0xFE000000F80000,
+ 0x7E000003F8000,
+ 0xFF0001FE00003F00,
+ 0xFFFFFFFF80FFC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x7FE03FFFFFFFF,
+ 0x1F80000FF0001FF,
+ 0x3F800000FC000,
+ 0x3E000000FE00,
+ 0x7E0000003E00,
+ 0xF00000007C00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7C0000001E0000,
+ 0xF8000000FC0000,
+ 0xFE000000F80000,
+ 0x7E000003F8000,
+ 0xFF0001FE00003F00,
+ 0xFFFFFFFF80FFC001,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFF07FFFFFFFF,
+ 0x1F80000FF0001FF,
+ 0x3F800000FC000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0xF8000000FC00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7E0000003E0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0x7E000003F8000,
+ 0xFF0001FE00003F00,
+ 0xFFFFFFFFC1FFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFF07FFFFFFFF,
+ 0x1FC0001FF0001FF,
+ 0x3FC00001FC000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0xF8000000FC00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7E0000003E0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0x7F000007F8000,
+ 0xFF0001FF00007F00,
+ 0xFFFFFFFFC1FFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFF07FFFFFFFF,
+ 0x1FC0001FF0001FF,
+ 0x3FC00001FC000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0xF8000000FC00,
+ 0x3C0000001E000,
+ 0xF000000078000,
+ 0x7E0000003E0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0x7F000007F8000,
+ 0xFF0001FF00007F00,
+ 0xFFFFFFFFC1FFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFF8FFFFFFFFF,
+ 0x3FC0001FF0001FF,
+ 0x3FC00001FE000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0x1F8000000FC00,
+ 0x3C0000001F000,
+ 0x1F000000078000,
+ 0x7E0000003F0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0xFF000007F8000,
+ 0xFF0001FF00007F80,
+ 0xFFFFFFFFE3FFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFFDFFFFFFFFF,
+ 0x3FC0001FF0001FF,
+ 0x3FC00001FE000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0x1F8000000FC00,
+ 0x3E0000003F000,
+ 0x1F8000000F8000,
+ 0x7E0000003F0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0xFF000007F8000,
+ 0xFF0001FF00007F80,
+ 0xFFFFFFFFF7FFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0x800FFFFFFFFFFFFF,
+ 0x3FC0001FF0001FF,
+ 0x3FC00001FE000,
+ 0x7E000000FE00,
+ 0x7E0000003F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1F8000000FC0000,
+ 0xFE000000FC0000,
+ 0xFF000007F8000,
+ 0xFF0001FF00007F80,
+ 0xFFFFFFFFFFFFE003,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xC01FFFFFFFFFFFFF,
+ 0x3FC0001FF8003FF,
+ 0x3FC00001FE000,
+ 0x7F000001FE00,
+ 0x7F0000007F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1FC000001FC0000,
+ 0xFF000001FC0000,
+ 0xFF000007F8000,
+ 0xFF8003FF00007F80,
+ 0xFFFFFFFFFFFFF007,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xC01FFFFFFFFFFFFF,
+ 0x3FC0001FF8003FF,
+ 0x3FC00001FE000,
+ 0x7F000001FE00,
+ 0x7F0000007F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1FC000001FC0000,
+ 0xFF000001FC0000,
+ 0xFF000007F8000,
+ 0xFF8003FF00007F80,
+ 0xFFFFFFFFFFFFF007,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xC01FFFFFFFFFFFFF,
+ 0x3FC0001FF8003FF,
+ 0x3FC00001FE000,
+ 0x7F000001FE00,
+ 0x7F0000007F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1FC000001FC0000,
+ 0xFF000001FC0000,
+ 0xFF000007F8000,
+ 0xFF8003FF00007F80,
+ 0xFFFFFFFFFFFFF007,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xC01FFFFFFFFFFFFF,
+ 0x3FC0001FF8003FF,
+ 0x3FC00001FE000,
+ 0x7F000001FE00,
+ 0x7F0000007F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1FC000001FC0000,
+ 0xFF000001FC0000,
+ 0xFF000007F8000,
+ 0xFF8003FF00007F80,
+ 0xFFFFFFFFFFFFF007,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xC01FFFFFFFFFFFFF,
+ 0x3FC0001FF8003FF,
+ 0x3FC00001FE000,
+ 0x7F000001FE00,
+ 0x7F0000007F00,
+ 0x1F8000000FC00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0x7E0000003F0000,
+ 0x1FC000001FC0000,
+ 0xFF000001FC0000,
+ 0xFF000007F8000,
+ 0xFF8003FF00007F80,
+ 0xFFFFFFFFFFFFF007,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE03FFFFFFFFFFFFF,
+ 0x3FE0003FF8003FF,
+ 0x7FC00001FE000,
+ 0x7F000001FF00,
+ 0xFF0000007F00,
+ 0x1F8000000FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFE0000003F0000,
+ 0x1FC000001FE0000,
+ 0x1FF000001FC0000,
+ 0xFF000007FC000,
+ 0xFF8003FF8000FF80,
+ 0xFFFFFFFFFFFFF80F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE03FFFFFFFFFFFFF,
+ 0x3FE0003FF8003FF,
+ 0x7FC00001FE000,
+ 0x7F000001FF00,
+ 0xFF0000007F00,
+ 0x1F8000000FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFE0000003F0000,
+ 0x1FC000001FE0000,
+ 0x1FF000001FC0000,
+ 0xFF000007FC000,
+ 0xFF8003FF8000FF80,
+ 0xFFFFFFFFFFFFF80F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE03FFFFFFFFFFFFF,
+ 0x7FE0003FF8003FF,
+ 0x7FE00003FF000,
+ 0x7F000001FF00,
+ 0xFF0000007F00,
+ 0x1F8000000FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFE0000003F0000,
+ 0x1FC000001FE0000,
+ 0x1FF000001FC0000,
+ 0x1FF80000FFC000,
+ 0xFF8003FF8000FFC0,
+ 0xFFFFFFFFFFFFF80F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xE03FFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x1F8000000FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFE0000003F0000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFF80F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xF07FFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x1FC000001FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFF0000007F0000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFFC1F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xF07FFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x1FC000001FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFF0000007F0000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFFC1F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xF07FFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x1FC000001FE00,
+ 0x7E0000003F000,
+ 0x1F8000000FC000,
+ 0xFF0000007F0000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFFC1F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xF8FFFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x3FC000001FE00,
+ 0x7E0000003F800,
+ 0x3F8000000FC000,
+ 0xFF0000007F8000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFFE3F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xF8FFFFFFFFFFFFFF,
+ 0x7FE0003FFC007FF,
+ 0x7FE00003FF000,
+ 0xFF000001FF00,
+ 0xFF0000007F80,
+ 0x3FC000001FE00,
+ 0x7E0000003F800,
+ 0x3F8000000FC000,
+ 0xFF0000007F8000,
+ 0x3FC000001FE0000,
+ 0x1FF000001FE0000,
+ 0x1FF80000FFC000,
+ 0xFFC007FF8000FFC0,
+ 0xFFFFFFFFFFFFFE3F,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FF0007FFE00FFF,
+ 0x7FE00003FF000,
+ 0xFF800003FF00,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x1FF800003FE0000,
+ 0x1FF80000FFC000,
+ 0xFFE00FFFC001FFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FF0007FFE00FFF,
+ 0x7FE00003FF000,
+ 0xFF800003FF00,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x1FF800003FE0000,
+ 0x1FF80000FFC000,
+ 0xFFE00FFFC001FFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FF0007FFE00FFF,
+ 0x7FE00003FF000,
+ 0xFF800003FF00,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x1FF800003FE0000,
+ 0x1FF80000FFC000,
+ 0xFFE00FFFC001FFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF0007FFE00FFF,
+ 0xFFE00003FF800,
+ 0xFF800003FF80,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x3FF800003FE0000,
+ 0x3FF80000FFE000,
+ 0xFFE00FFFC001FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF0007FFE00FFF,
+ 0xFFE00003FF800,
+ 0xFF800003FF80,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x3FF800003FE0000,
+ 0x3FF80000FFE000,
+ 0xFFE00FFFC001FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF0007FFE00FFF,
+ 0xFFF00007FF800,
+ 0xFF800003FF80,
+ 0xFF800000FF80,
+ 0x3FC000001FE00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0xFF0000007F8000,
+ 0x3FE000003FE0000,
+ 0x3FF800003FE0000,
+ 0x3FFC0001FFE000,
+ 0xFFE00FFFC001FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF0007FFF01FFF,
+ 0xFFF00007FF800,
+ 0xFF800003FF80,
+ 0x1FF800000FF80,
+ 0x3FC000001FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF0000007F8000,
+ 0x3FE000003FF0000,
+ 0x3FF800003FE0000,
+ 0x3FFC0001FFE000,
+ 0xFFF01FFFC001FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF0007FFF01FFF,
+ 0xFFF00007FF800,
+ 0xFF800003FF80,
+ 0x1FF800000FF80,
+ 0x3FC000001FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF0000007F8000,
+ 0x3FE000003FF0000,
+ 0x3FF800003FE0000,
+ 0x3FFC0001FFE000,
+ 0xFFF01FFFC001FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF800FFFF01FFF,
+ 0xFFF00007FF800,
+ 0x1FF800003FF80,
+ 0x1FF800000FFC0,
+ 0x3FC000001FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF0000007F8000,
+ 0x7FE000003FF0000,
+ 0x3FF800003FF0000,
+ 0x3FFC0001FFE000,
+ 0xFFF01FFFE003FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF800FFFF01FFF,
+ 0xFFF00007FF800,
+ 0x1FF800003FF80,
+ 0x1FF800000FFC0,
+ 0x3FC000001FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF0000007F8000,
+ 0x7FE000003FF0000,
+ 0x3FF800003FF0000,
+ 0x3FFC0001FFE000,
+ 0xFFF01FFFE003FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF800FFFF83FFF,
+ 0xFFF00007FF800,
+ 0x1FF800003FF80,
+ 0x1FF800000FFC0,
+ 0x3FE000003FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF800000FF8000,
+ 0x7FE000003FF0000,
+ 0x3FF800003FF0000,
+ 0x3FFC0001FFE000,
+ 0xFFF83FFFE003FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF800FFFF83FFF,
+ 0xFFF00007FF800,
+ 0x1FF800003FF80,
+ 0x1FF800000FFC0,
+ 0x3FE000003FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF800000FF8000,
+ 0x7FE000003FF0000,
+ 0x3FF800003FF0000,
+ 0x3FFC0001FFE000,
+ 0xFFF83FFFE003FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFF800FFFF83FFF,
+ 0xFFF00007FF800,
+ 0x1FF800003FF80,
+ 0x1FF800000FFC0,
+ 0x3FE000003FF00,
+ 0xFF0000007F800,
+ 0x3FC000001FE000,
+ 0x1FF800000FF8000,
+ 0x7FE000003FF0000,
+ 0x3FF800003FF0000,
+ 0x3FFC0001FFE000,
+ 0xFFF83FFFE003FFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFF800FFFFC7FFF,
+ 0xFFF00007FFC00,
+ 0x1FFC00007FF80,
+ 0x1FF800000FFC0,
+ 0x7FE000003FF00,
+ 0xFF0000007FC00,
+ 0x7FC000001FE000,
+ 0x1FF800000FFC000,
+ 0x7FE000003FF0000,
+ 0x3FFC00007FF0000,
+ 0x7FFC0001FFE000,
+ 0xFFFC7FFFE003FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFF800FFFFC7FFF,
+ 0xFFF00007FFC00,
+ 0x1FFC00007FF80,
+ 0x1FF800000FFC0,
+ 0x7FE000003FF00,
+ 0xFF0000007FC00,
+ 0x7FC000001FE000,
+ 0x1FF800000FFC000,
+ 0x7FE000003FF0000,
+ 0x3FFC00007FF0000,
+ 0x7FFC0001FFE000,
+ 0xFFFC7FFFE003FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFEFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0xFF800000FFC00,
+ 0x7FE000003FE000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFEFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFFFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFFFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFFFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFFFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFFFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFFFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFFFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFFFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFC01FFFFFFFFF,
+ 0x1FFF8000FFFC00,
+ 0x1FFC00007FFC0,
+ 0x1FFC00001FFC0,
+ 0x7FE000003FF00,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x1FF800000FFC000,
+ 0x7FF000007FF0000,
+ 0x7FFC00007FF0000,
+ 0x7FFE0003FFF000,
+ 0xFFFFFFFFF007FFF0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE03FFFFFFFFF,
+ 0x1FFF8000FFFE00,
+ 0x3FFC00007FFC0,
+ 0x3FFC00001FFE0,
+ 0x7FE000003FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FF800000FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFC00007FF8000,
+ 0xFFFE0003FFF000,
+ 0xFFFFFFFFF80FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE03FFFFFFFFF,
+ 0x1FFF8000FFFE00,
+ 0x3FFC00007FFC0,
+ 0x3FFC00001FFE0,
+ 0x7FE000003FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FF800000FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFC00007FF8000,
+ 0xFFFE0003FFF000,
+ 0xFFFFFFFFF80FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE03FFFFFFFFF,
+ 0x1FFF8000FFFE00,
+ 0x3FFC00007FFC0,
+ 0x3FFC00001FFE0,
+ 0x7FE000003FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FF800000FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFC00007FF8000,
+ 0xFFFE0003FFF000,
+ 0xFFFFFFFFF80FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE03FFFFFFFFF,
+ 0x1FFF8000FFFE00,
+ 0x3FFC00007FFC0,
+ 0x3FFC00001FFE0,
+ 0x7FE000003FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FF800000FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFC00007FF8000,
+ 0xFFFE0003FFF000,
+ 0xFFFFFFFFF80FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFF07FFFFFFFFF,
+ 0x1FFFC001FFFE00,
+ 0x3FFE0000FFFC0,
+ 0x3FFC00001FFE0,
+ 0x7FF000007FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FFC00001FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFE0000FFF8000,
+ 0xFFFF0007FFF000,
+ 0xFFFFFFFFFC1FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFF07FFFFFFFFF,
+ 0x1FFFC001FFFE00,
+ 0x3FFE0000FFFC0,
+ 0x3FFC00001FFE0,
+ 0x7FF000007FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FFC00001FFC000,
+ 0xFFF000007FF8000,
+ 0x7FFE0000FFF8000,
+ 0xFFFF0007FFF000,
+ 0xFFFFFFFFFC1FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFF07FFFFFFFFF,
+ 0x3FFFC001FFFE00,
+ 0x3FFE0000FFFE0,
+ 0x3FFC00001FFE0,
+ 0x7FF000007FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FFC00001FFC000,
+ 0xFFF000007FF8000,
+ 0xFFFE0000FFF8000,
+ 0xFFFF0007FFF800,
+ 0xFFFFFFFFFC1FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFF07FFFFFFFFF,
+ 0x3FFFC001FFFE00,
+ 0x3FFE0000FFFE0,
+ 0x3FFC00001FFE0,
+ 0x7FF000007FF80,
+ 0x1FF800000FFC00,
+ 0x7FE000003FF000,
+ 0x3FFC00001FFC000,
+ 0xFFF000007FF8000,
+ 0xFFFE0000FFF8000,
+ 0xFFFF0007FFF800,
+ 0xFFFFFFFFFC1FFFF8,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF8FFFFFFFFFF,
+ 0x3FFFC001FFFF00,
+ 0x3FFE0000FFFE0,
+ 0x3FFE00003FFE0,
+ 0xFFF000007FF80,
+ 0x1FF800000FFE00,
+ 0xFFE000003FF000,
+ 0x3FFC00001FFE000,
+ 0xFFF80000FFF8000,
+ 0xFFFE0000FFF8000,
+ 0x1FFFF0007FFF800,
+ 0xFFFFFFFFFE3FFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF8FFFFFFFFFF,
+ 0x3FFFC001FFFF00,
+ 0x3FFE0000FFFE0,
+ 0x3FFE00003FFE0,
+ 0xFFF000007FF80,
+ 0x1FF800000FFE00,
+ 0xFFE000003FF000,
+ 0x3FFC00001FFE000,
+ 0xFFF80000FFF8000,
+ 0xFFFE0000FFF8000,
+ 0x1FFFF0007FFF800,
+ 0xFFFFFFFFFE3FFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFDFFFFFFFFFF,
+ 0x3FFFC001FFFF00,
+ 0x3FFE0000FFFE0,
+ 0x3FFE00003FFE0,
+ 0xFFF000007FF80,
+ 0x1FFC00001FFE00,
+ 0xFFF000007FF000,
+ 0x3FFC00001FFE000,
+ 0xFFF80000FFF8000,
+ 0xFFFE0000FFF8000,
+ 0x1FFFF0007FFF800,
+ 0xFFFFFFFFFF7FFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFFF,
+ 0x3FFFC001FFFF00,
+ 0x3FFE0000FFFE0,
+ 0x3FFE00003FFE0,
+ 0xFFF000007FF80,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x3FFC00001FFE000,
+ 0xFFF80000FFF8000,
+ 0xFFFE0000FFF8000,
+ 0x1FFFF0007FFF800,
+ 0xFFFFFFFFFFFFFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFFF,
+ 0x3FFFC001FFFF00,
+ 0x3FFE0000FFFE0,
+ 0x3FFE00003FFE0,
+ 0xFFF000007FF80,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x3FFC00001FFE000,
+ 0xFFF80000FFF8000,
+ 0xFFFE0000FFF8000,
+ 0x1FFFF0007FFF800,
+ 0xFFFFFFFFFFFFFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFFF,
+ 0x3FFFE003FFFF00,
+ 0x7FFE0000FFFE0,
+ 0x3FFE00003FFF0,
+ 0xFFF000007FF80,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x3FFC00001FFE000,
+ 0x1FFF80000FFF8000,
+ 0xFFFE0000FFFC000,
+ 0x1FFFF800FFFF800,
+ 0xFFFFFFFFFFFFFFFC,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE003FFFF80,
+ 0x7FFE0000FFFE0,
+ 0x7FFE00003FFF0,
+ 0xFFF000007FFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFC00001FFE000,
+ 0x1FFF80000FFFC000,
+ 0xFFFE0000FFFC000,
+ 0x3FFFF800FFFF800,
+ 0xFFFFFFFFFFFFFFFE,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFE003FFFF80,
+ 0x7FFE0000FFFE0,
+ 0x7FFE00003FFF0,
+ 0xFFF000007FFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFC00001FFE000,
+ 0x1FFF80000FFFC000,
+ 0xFFFE0000FFFC000,
+ 0x3FFFF800FFFF800,
+ 0xFFFFFFFFFFFFFFFE,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFF80,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF000007FFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFC00001FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x3FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFE,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFF80,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF000007FFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFC00001FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x3FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFE,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFF80,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF000007FFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFC00001FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x3FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFE,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFFC1,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF80000FFFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFE00003FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x7FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFFC1,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF80000FFFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFE00003FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x7FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFE003FFFFC1,
+ 0x7FFF0001FFFF0,
+ 0x7FFE00003FFF0,
+ 0xFFF80000FFFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFE00003FFE000,
+ 0x1FFF80000FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x7FFFF800FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF007FFFFC1,
+ 0x7FFF0001FFFF0,
+ 0x7FFF00007FFF0,
+ 0xFFF80000FFFC0,
+ 0x3FFC00001FFE00,
+ 0xFFF000007FF800,
+ 0x7FFE00003FFE000,
+ 0x1FFFC0001FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x7FFFFC01FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF007FFFFE3,
+ 0x7FFF0001FFFF0,
+ 0x7FFF00007FFF0,
+ 0x1FFF80000FFFC0,
+ 0x3FFC00001FFF00,
+ 0x1FFF000007FF800,
+ 0x7FFE00003FFF000,
+ 0x1FFFC0001FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x8FFFFFC01FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF007FFFFE3,
+ 0x7FFF0001FFFF0,
+ 0x7FFF00007FFF0,
+ 0x1FFF80000FFFC0,
+ 0x3FFC00001FFF00,
+ 0x1FFF000007FF800,
+ 0x7FFE00003FFF000,
+ 0x1FFFC0001FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0x8FFFFFC01FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFF007FFFFF7,
+ 0x7FFF0001FFFF0,
+ 0x7FFF00007FFF0,
+ 0x1FFF80000FFFC0,
+ 0x3FFE00003FFF00,
+ 0x1FFF80000FFF800,
+ 0x7FFE00003FFF000,
+ 0x1FFFC0001FFFC000,
+ 0x1FFFF0001FFFC000,
+ 0xDFFFFFC01FFFFC00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF007FFFFFF,
+ 0xFFFF0001FFFF8,
+ 0x7FFF00007FFF8,
+ 0x1FFF80000FFFC0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0x7FFE00003FFF000,
+ 0x3FFFC0001FFFC000,
+ 0x3FFFF0001FFFE000,
+ 0xFFFFFFC01FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF007FFFFFF,
+ 0xFFFF0001FFFF8,
+ 0x7FFF00007FFF8,
+ 0x1FFF80000FFFC0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0x7FFE00003FFF000,
+ 0x3FFFC0001FFFC000,
+ 0x3FFFF0001FFFE000,
+ 0xFFFFFFC01FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF007FFFFFF,
+ 0xFFFF8003FFFF8,
+ 0x7FFF00007FFF8,
+ 0x1FFF80000FFFC0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0x7FFE00003FFF000,
+ 0x3FFFC0001FFFC000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFC01FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF80FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFF80000FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFE00003FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFE03FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF80FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFF80000FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFE00003FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFE03FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF80FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFF80000FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFE00003FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFE03FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF80FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFF80000FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFE00003FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFE03FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFF80FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFF80000FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFE00003FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFE03FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFC1FFFFFFF,
+ 0xFFFF8003FFFF8,
+ 0xFFFF00007FFF8,
+ 0x1FFFC0001FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFF00007FFF000,
+ 0x3FFFC0001FFFE000,
+ 0x3FFFF8003FFFE000,
+ 0xFFFFFFF07FFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFC1FFFFFFF,
+ 0xFFFF8003FFFFC,
+ 0xFFFF8000FFFF8,
+ 0x1FFFC0001FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFF00007FFF000,
+ 0x3FFFE0003FFFE000,
+ 0x7FFFF8003FFFE000,
+ 0xFFFFFFF07FFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFC1FFFFFFF,
+ 0xFFFF8003FFFFC,
+ 0xFFFF8000FFFF8,
+ 0x1FFFC0001FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFF00007FFF000,
+ 0x3FFFE0003FFFE000,
+ 0x7FFFF8003FFFE000,
+ 0xFFFFFFF07FFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFC1FFFFFFF,
+ 0xFFFF8003FFFFC,
+ 0xFFFF8000FFFF8,
+ 0x1FFFC0001FFFE0,
+ 0x7FFE00003FFF00,
+ 0x1FFF80000FFFC00,
+ 0xFFFF00007FFF000,
+ 0x3FFFE0003FFFE000,
+ 0x7FFFF8003FFFE000,
+ 0xFFFFFFF07FFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFE3FFFFFFF,
+ 0x1FFFFC007FFFFC,
+ 0xFFFF8000FFFFC,
+ 0x3FFFC0001FFFE0,
+ 0x7FFE00003FFF80,
+ 0x3FFF80000FFFC00,
+ 0xFFFF00007FFF800,
+ 0x7FFFE0003FFFE000,
+ 0x7FFFFC007FFFF000,
+ 0xFFFFFFF8FFFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFE3FFFFFFF,
+ 0x1FFFFC007FFFFC,
+ 0xFFFF8000FFFFC,
+ 0x3FFFC0001FFFE0,
+ 0x7FFE00003FFF80,
+ 0x3FFF80000FFFC00,
+ 0xFFFF00007FFF800,
+ 0x7FFFE0003FFFE000,
+ 0x7FFFFC007FFFF000,
+ 0xFFFFFFF8FFFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFE3FFFFFFF,
+ 0x1FFFFC007FFFFC,
+ 0xFFFF8000FFFFC,
+ 0x3FFFC0001FFFE0,
+ 0x7FFE00003FFF80,
+ 0x3FFF80000FFFC00,
+ 0xFFFF00007FFF800,
+ 0x7FFFE0003FFFE000,
+ 0x7FFFFC007FFFF000,
+ 0xFFFFFFF8FFFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFF7FFFFFFF,
+ 0x1FFFFC007FFFFC,
+ 0xFFFF8000FFFFC,
+ 0x3FFFC0001FFFE0,
+ 0x7FFF00007FFF80,
+ 0x3FFFC0001FFFC00,
+ 0xFFFF00007FFF800,
+ 0x7FFFE0003FFFE000,
+ 0x7FFFFC007FFFF000,
+ 0xFFFFFFFDFFFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFC,
+ 0xFFFF8000FFFFC,
+ 0x3FFFC0001FFFE0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0xFFFF00007FFF800,
+ 0x7FFFE0003FFFE000,
+ 0x7FFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFE,
+ 0x1FFFF8000FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFE0003FFFF000,
+ 0xFFFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFE,
+ 0x1FFFF8000FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFE0003FFFF000,
+ 0xFFFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFE,
+ 0x1FFFF8000FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFE0003FFFF000,
+ 0xFFFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFE,
+ 0x1FFFF8000FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFE0003FFFF000,
+ 0xFFFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFC007FFFFE,
+ 0x1FFFF8000FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFE0003FFFF000,
+ 0xFFFFFC007FFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFFFFFFFFFFF,
+ 0x1FFFFE00FFFFFE,
+ 0x1FFFFC001FFFFC,
+ 0x3FFFC0001FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF00007FFF800,
+ 0x7FFFF0007FFFF000,
+ 0xFFFFFE00FFFFF000,
+ 0xFFFFFFFFFFFFFF80,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFF,
+ 0x1FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFC,
+ 0x3FFFE0003FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF8000FFFF800,
+ 0x7FFFF0007FFFF000,
+ 0xFFFFFE00FFFFF000,
+ 0xFFFFFFFFFFFFFFC1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x3FFFE0003FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF8000FFFF800,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFC1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x3FFFE0003FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF8000FFFF800,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFC1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x3FFFE0003FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF8000FFFF800,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFC1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x3FFFE0003FFFF0,
+ 0xFFFF00007FFF80,
+ 0x3FFFC0001FFFE00,
+ 0x1FFFF8000FFFF800,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFC1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x8FFFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x7FFFE0003FFFF0,
+ 0xFFFF00007FFFC0,
+ 0x7FFFC0001FFFE00,
+ 0x1FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFE3,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x8FFFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x7FFFE0003FFFF0,
+ 0xFFFF00007FFFC0,
+ 0x7FFFC0001FFFE00,
+ 0x1FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFE3,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x8FFFFFFFFFFFFFFF,
+ 0x3FFFFE00FFFFFF,
+ 0x1FFFFC001FFFFE,
+ 0x7FFFE0003FFFF0,
+ 0xFFFF00007FFFC0,
+ 0x7FFFC0001FFFE00,
+ 0x1FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF000,
+ 0xFFFFFE00FFFFF800,
+ 0xFFFFFFFFFFFFFFE3,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xDFFFFFFFFFFFFFFF,
+ 0x3FFFFF01FFFFFF,
+ 0x3FFFFC001FFFFE,
+ 0x7FFFE0003FFFF8,
+ 0xFFFF8000FFFFC0,
+ 0x7FFFE0003FFFE00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF800,
+ 0xFFFFFF01FFFFF800,
+ 0xFFFFFFFFFFFFFFF7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFF01FFFFFF,
+ 0x3FFFFC001FFFFE,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF800,
+ 0xFFFFFF01FFFFF800,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x3FFFFF01FFFFFF,
+ 0x3FFFFC001FFFFE,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF0007FFFF800,
+ 0xFFFFFF01FFFFF800,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF01FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF01FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF01FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF01FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF01FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF01FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF01FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFE0003FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFF8000FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF01FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF83FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFF0007FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFFC001FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF83FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF83FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFF0007FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFFC001FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF83FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF83FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFF0007FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFFC001FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF83FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF83FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFF0007FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFFC001FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF83FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFF83FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0x7FFFF0007FFFF8,
+ 0x1FFFF8000FFFFC0,
+ 0x7FFFE0003FFFF00,
+ 0x3FFFFC001FFFFC00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFF83FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFC7FFFFFF,
+ 0x3FFFFE003FFFFF,
+ 0xFFFFF0007FFFF8,
+ 0x1FFFF8000FFFFE0,
+ 0xFFFFE0003FFFF00,
+ 0x3FFFFC001FFFFE00,
+ 0xFFFFF800FFFFF800,
+ 0xFFFFFFC7FFFFFC01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFC7FFFFFF,
+ 0x7FFFFE003FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x1FFFF8000FFFFE0,
+ 0xFFFFE0003FFFF00,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFF800FFFFFC00,
+ 0xFFFFFFC7FFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFC7FFFFFF,
+ 0x7FFFFE003FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x1FFFF8000FFFFE0,
+ 0xFFFFE0003FFFF00,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFF800FFFFFC00,
+ 0xFFFFFFC7FFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFEFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x1FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF00,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFEFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x80FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF0007FFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFC001FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFE03,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF800FFFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFE003FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF800FFFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFE003FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF800FFFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFE003FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0x7FFFFF007FFFFF,
+ 0xFFFFF800FFFFFC,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0x7FFFFE003FFFFE00,
+ 0xFFFFFC01FFFFFC00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0xFFFFF800FFFFFE,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0xFFFFFE003FFFFE00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC1FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0xFFFFF800FFFFFE,
+ 0x3FFFFC001FFFFE0,
+ 0xFFFFF0007FFFF80,
+ 0xFFFFFE003FFFFE00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFF07,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xE3FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x3FFFFC001FFFFF0,
+ 0x1FFFFF0007FFFF80,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFF8F,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xE3FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x3FFFFC001FFFFF0,
+ 0x1FFFFF0007FFFF80,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFF8F,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xE3FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x3FFFFC001FFFFF0,
+ 0x1FFFFF0007FFFF80,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFF8F,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xF7FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x3FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFF80,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFFDF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xF7FFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x3FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFF80,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFFDF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFF80FFFFFF,
+ 0x1FFFFF800FFFFFE,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFE003FFFFF00,
+ 0xFFFFFE03FFFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFE,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFE,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFE,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFE00,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFC1FFFFFF,
+ 0x1FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF0,
+ 0x1FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF00,
+ 0xFFFFFF07FFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFE3FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF8,
+ 0x3FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFF8FFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFE3FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF8,
+ 0x3FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFF8FFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFE3FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF8,
+ 0x3FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFF8FFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFE3FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF8,
+ 0x3FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFF8FFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFE3FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFE003FFFFF8,
+ 0x3FFFFF800FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFF8FFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1FFFFFFF7FFFFFF,
+ 0x3FFFFFC01FFFFFF,
+ 0x7FFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFC0,
+ 0xFFFFFF007FFFFF80,
+ 0xFFFFFFDFFFFFFF01,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x83FFFFFFFFFFFFFF,
+ 0x3FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFF8,
+ 0x3FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFF80,
+ 0xFFFFFFFFFFFFFF83,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC7FFFFFFFFFFFFFF,
+ 0x7FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFFC,
+ 0x7FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFFC0,
+ 0xFFFFFFFFFFFFFFC7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC7FFFFFFFFFFFFFF,
+ 0x7FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFFC,
+ 0x7FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFFC0,
+ 0xFFFFFFFFFFFFFFC7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC7FFFFFFFFFFFFFF,
+ 0x7FFFFFE03FFFFFF,
+ 0xFFFFFF007FFFFFC,
+ 0x7FFFFFC01FFFFFE0,
+ 0xFFFFFF80FFFFFFC0,
+ 0xFFFFFFFFFFFFFFC7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC7FFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0xFFFFFF007FFFFFC,
+ 0x7FFFFFC01FFFFFE0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFC7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xC7FFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0xFFFFFF007FFFFFC,
+ 0x7FFFFFC01FFFFFE0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFC7,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xEFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0xFFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFE0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFEF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xEFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0xFFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFE0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFEF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x7FFFFFF07FFFFFF,
+ 0x1FFFFFF80FFFFFFC,
+ 0x7FFFFFE03FFFFFF0,
+ 0xFFFFFFC1FFFFFFC0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFF8FFFFFFF,
+ 0x1FFFFFF80FFFFFFE,
+ 0xFFFFFFE03FFFFFF0,
+ 0xFFFFFFE3FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+ Map {
+ cells: [
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFDFFFFFFF,
+ 0x1FFFFFFC1FFFFFFE,
+ 0xFFFFFFF07FFFFFF0,
+ 0xFFFFFFF7FFFFFFE0,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0x1,
+ ],
+ },
+];
diff --git a/2019-worms/src/game.rs b/2019-worms/src/game.rs
new file mode 100644
index 0000000..00289a0
--- /dev/null
+++ b/2019-worms/src/game.rs
@@ -0,0 +1,779 @@
+use crate::command::{Action, Command};
+use crate::constants::*;
+use crate::geometry::*;
+use crate::json;
+
+mod player;
+use player::*;
+
+mod powerup;
+use powerup::*;
+
+pub mod map;
+use map::*;
+
+use arrayvec::ArrayVec;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct GameBoard {
+ pub round: u16,
+ pub max_rounds: u16,
+ pub players: [Player; 2],
+ pub powerups: ArrayVec<[Powerup; 2]>,
+ pub map: Map,
+ pub occupied_cells: ArrayVec<[Point2d; 6]>,
+ pub outcome: SimulationOutcome,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum SimulationOutcome {
+ PlayerWon(usize),
+ Draw,
+ Continue,
+}
+
+impl GameBoard {
+ pub fn new(json: json::State) -> GameBoard {
+ let player = Player {
+ active_worm: json.active_worm_index().unwrap(),
+ moves_score: json.my_player.score - json.my_player.health_score(),
+ select_moves: json.my_player.remaining_worm_selections,
+ worms: json
+ .my_player
+ .worms
+ .iter()
+ .map(|w| Worm {
+ id: w.id,
+ health: w.health,
+ position: Point2d::new(w.position.x, w.position.y),
+ rounds_until_unfrozen: w.rounds_until_unfrozen,
+ bombs: w.banana_bombs.as_ref().map(|b| b.count).unwrap_or(0),
+ snowballs: w.snowballs.as_ref().map(|b| b.count).unwrap_or(0),
+ })
+ .collect(),
+ };
+
+ let opponent = Player {
+ active_worm: 0,
+ moves_score: json.opponents[0].score - json.opponents[0].health_score(),
+ select_moves: json.opponents[0].remaining_worm_selections,
+ worms: json
+ .opponents
+ .iter()
+ .flat_map(|o| &o.worms)
+ .map(|w| Worm {
+ id: w.id,
+ health: w.health,
+ position: Point2d::new(w.position.x, w.position.y),
+ rounds_until_unfrozen: w.rounds_until_unfrozen,
+ bombs: if w.profession == json::WormType::Agent {
+ STARTING_BOMBS
+ } else {
+ 0
+ },
+ snowballs: if w.profession == json::WormType::Technologist {
+ STARTING_SNOWBALLS
+ } else {
+ 0
+ },
+ })
+ .collect(),
+ };
+
+ let mut map = Map::default();
+ for cell in json.map.iter().flatten() {
+ if cell.cell_type == json::CellType::Dirt {
+ map.set(Point2d::new(cell.x, cell.y))
+ }
+ }
+
+ let players = [player, opponent];
+ let occupied_cells = players
+ .iter()
+ .flat_map(|p| p.worms.iter())
+ .map(|w| w.position)
+ .collect();
+
+ GameBoard {
+ round: json.current_round,
+ max_rounds: json.max_rounds,
+ players,
+ powerups: json
+ .map
+ .iter()
+ .flatten()
+ .filter_map(|c| {
+ c.powerup.as_ref().map(|_p| Powerup {
+ position: Point2d::new(c.x, c.y),
+ })
+ })
+ .collect(),
+ map,
+ occupied_cells,
+ outcome: SimulationOutcome::Continue,
+ }
+ }
+
+ pub fn update(&mut self, json: json::State) {
+ for w in &json.my_player.worms {
+ if let Some(worm) = self.players[0].find_worm_mut(w.id) {
+ worm.health = w.health;
+ worm.position = Point2d::new(w.position.x, w.position.y);
+ worm.bombs = w.banana_bombs.as_ref().map(|b| b.count).unwrap_or(0);
+ worm.snowballs = w.snowballs.as_ref().map(|b| b.count).unwrap_or(0);
+ }
+ }
+ for w in json.opponents.iter().flat_map(|o| &o.worms) {
+ if let Some(worm) = self.players[1].find_worm_mut(w.id) {
+ worm.health = w.health;
+ worm.position = Point2d::new(w.position.x, w.position.y);
+ }
+ }
+
+ if !self.players[1].active_worm_is_frozen() {
+ if json
+ .opponents
+ .iter()
+ .any(|o| o.previous_command.starts_with("banana"))
+ {
+ for worm in &mut self.players[1].worms {
+ worm.bombs = worm.bombs.saturating_sub(1);
+ }
+ }
+ if json
+ .opponents
+ .iter()
+ .any(|o| o.previous_command.starts_with("snowball"))
+ {
+ for worm in &mut self.players[1].worms {
+ worm.snowballs = worm.snowballs.saturating_sub(1);
+ }
+ }
+ }
+
+ self.players[0].moves_score = json.my_player.score - json.my_player.health_score();
+ self.players[1].moves_score = json.opponents[0].score - json.opponents[0].health_score();
+
+ self.players[0].select_moves = json.my_player.remaining_worm_selections;
+ self.players[1].select_moves = json.opponents[0].remaining_worm_selections;
+
+ self.powerups = json
+ .map
+ .iter()
+ .flatten()
+ .filter_map(|c| {
+ c.powerup.as_ref().map(|_p| Powerup {
+ position: Point2d::new(c.x, c.y),
+ })
+ })
+ .collect();
+
+ for cell in json.map.iter().flatten() {
+ let point = Point2d::new(cell.x, cell.y);
+
+ if cfg!(debug_assertions) {
+ // This checks if my lava map is the same as the game
+ // engine's lava map. It's off by one because the game
+ // engine will update its one at the beginning of
+ // processing the round.
+ let lava = LAVA_MAP[self.round as usize];
+
+ let lava_at = lava.at(point);
+ // NB: Map hasn't been updated yet, so it can be used to tell previous state.
+ match (&cell.cell_type, self.map.at(point)) {
+ (json::CellType::Air, Some(false)) => assert!(
+ lava_at == Some(false),
+ "Lava at {:?} expected Some(false), but found {:?}",
+ point,
+ lava_at
+ ),
+ (json::CellType::Air, _) => assert!(
+ lava_at.is_some(),
+ "Lava at {:?} expected Some(_), but found {:?}",
+ point,
+ lava_at
+ ),
+ (json::CellType::Lava, _) => assert!(
+ lava_at == Some(true),
+ "Lava at {:?} expected Some(true), but found {:?}",
+ point,
+ lava_at
+ ),
+ (json::CellType::DeepSpace, _) => assert!(
+ lava_at == None,
+ "Lava at {:?} expected None, but found {:?}",
+ point,
+ lava_at
+ ),
+ (json::CellType::Dirt, _) => assert!(
+ lava_at.is_some(),
+ "Lava at {:?} expected Some(_), but found {:?}",
+ point,
+ lava_at
+ ),
+ };
+ }
+
+ if cell.cell_type == json::CellType::Air {
+ self.map.clear(point)
+ }
+ }
+
+ self.clear_dead_worms();
+ self.players[0].active_worm = json.active_worm_index().unwrap_or(0);
+ self.players[1].active_worm = json.opponent_active_worm_index().unwrap_or(0);
+
+ self.round += 1;
+ debug_assert_eq!(json.current_round, self.round);
+ }
+
+ pub fn simulate(&mut self, moves: [Command; 2]) {
+ self.simulate_worms_on_lava();
+ self.simulate_tick_frozen_timers();
+
+ self.simulate_select(moves);
+
+ let actions = self.identify_actions(moves);
+
+ self.simulate_moves(actions);
+ self.simulate_digs(actions);
+ self.simulate_bombs(actions);
+ self.simulate_shoots(actions);
+ self.simulate_snowballs(actions);
+
+ self.clear_dead_worms();
+
+ for player in &mut self.players {
+ player.next_active_worm();
+ }
+
+ self.round += 1;
+
+ self.outcome = match (
+ self.players[0].worms.len(),
+ self.players[1].worms.len(),
+ self.round > self.max_rounds,
+ ) {
+ (0, 0, _) => SimulationOutcome::Draw,
+ (_, 0, _) => SimulationOutcome::PlayerWon(0),
+ (0, _, _) => SimulationOutcome::PlayerWon(1),
+ (_, _, true) => SimulationOutcome::Draw,
+ _ => SimulationOutcome::Continue,
+ };
+ }
+
+ fn simulate_worms_on_lava(&mut self) {
+ let lava_map = LAVA_MAP[self.round as usize];
+ self.players
+ .iter_mut()
+ .flat_map(|p| p.worms.iter_mut())
+ .filter(|w| lava_map.at(w.position) == Some(true))
+ .for_each(|ref mut w| w.health -= LAVA_DAMAGE);
+ }
+
+ fn simulate_tick_frozen_timers(&mut self) {
+ self.players
+ .iter_mut()
+ .flat_map(|p| p.worms.iter_mut())
+ .filter(|w| w.health > 0)
+ .for_each(|ref mut w| {
+ w.rounds_until_unfrozen = w.rounds_until_unfrozen.saturating_sub(1)
+ });
+ }
+
+ fn identify_actions(&self, moves: [Command; 2]) -> [Action; 2] {
+ let mut it = self.players.iter().zip(moves.iter()).map(|(p, m)| {
+ if p.active_worm_is_frozen() {
+ Action::DoNothing
+ } else {
+ m.action
+ }
+ });
+ [it.next().unwrap(), it.next().unwrap()]
+ }
+
+ fn simulate_select(&mut self, moves: [Command; 2]) {
+ moves
+ .iter()
+ .zip(self.players.iter_mut())
+ .filter(|(_m, player)| !player.active_worm_is_frozen())
+ .for_each(|(m, player)| {
+ if let Some(worm) = m.worm {
+ debug_assert!(
+ player.select_moves > 0,
+ "Could not make select move, out of select tokens"
+ );
+ player.select_moves = player.select_moves.saturating_sub(1);
+ player.active_worm = player.find_worm_position(worm).unwrap_or(0);
+ }
+ });
+ }
+
+ fn simulate_moves(&mut self, actions: [Action; 2]) {
+ match actions {
+ [Action::Move(p1), Action::Move(p2)] if p1.x == p2.x && p1.y == p2.y => {
+ let damage = COLLISION_DAMAGE;
+
+ debug_assert_eq!(
+ Some(false),
+ self.map.at(Point2d::new(p1.x, p1.y)),
+ "Movement target wasn't empty, ({}, {})",
+ p1.x,
+ p1.y
+ );
+ // Worms have a 50% chance of swapping places
+ // here. I'm treating that as an edge case that I
+ // don't need to handle for now.
+ for player in &mut self.players {
+ if let Some(worm) = player.active_worm_mut() {
+ worm.health -= damage;
+ }
+ // You might expect damage score too here, but nope
+ player.moves_score += MOVE_SCORE;
+ }
+ }
+ _ => {
+ for player_index in 0..actions.len() {
+ if let Action::Move(p) = actions[player_index] {
+ debug_assert_eq!(
+ Some(false),
+ self.map.at(p),
+ "Movement target wasn't empty, ({}, {})",
+ p.x,
+ p.y
+ );
+
+ self.players[player_index].moves_score += MOVE_SCORE;
+
+ if let Some(worm) = self.players[player_index].active_worm_mut() {
+ debug_assert!(
+ (worm.position.x - p.x).abs() <= 1
+ && (worm.position.y - p.y).abs() <= 1,
+ "Tried to move too far away, ({}, {})",
+ p.x,
+ p.y
+ );
+
+ worm.position = p;
+
+ self.powerups.retain(|power| {
+ if power.position == worm.position {
+ worm.health += HEALTH_PACK_VALUE;
+ false
+ } else {
+ true
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn simulate_digs(&mut self, actions: [Action; 2]) {
+ for player_index in 0..actions.len() {
+ if let Action::Dig(p) = actions[player_index] {
+ debug_assert!(
+ Some(true) == self.map.at(p)
+ || (player_index == 1 && actions[0] == Action::Dig(p)),
+ "Tried to dig through air, ({}, {})",
+ p.x,
+ p.y
+ );
+ debug_assert! {
+ (self.players[player_index].active_worm().unwrap().position.x - p.x).abs() <= 1 &&
+ (self.players[player_index].active_worm().unwrap().position.y - p.y).abs() <= 1,
+ "Tried to dig too far away, ({}, {})", p.x, p.y
+ };
+
+ self.players[player_index].moves_score += DIG_SCORE;
+
+ self.map.clear(p);
+ }
+ }
+ }
+
+ fn simulate_bombs(&mut self, actions: [Action; 2]) {
+ // NB: Damage radius has the cell distance rounded UP, throwing range has the cell distance rounded DOWN
+
+ let map_clone: Map = self.map;
+
+ for player_index in 0..actions.len() {
+ if let Action::Bomb(p) = actions[player_index] {
+ if self.map.at(p).is_some() {
+ if let Some(worm) = self.players[player_index].active_worm_mut() {
+ debug_assert!(worm.bombs > 0, "Worm is throwing a bomb it doesn't have");
+ debug_assert!((worm.position - p).magnitude_squared() < 6 * 6); // max range is 5, but it's 5 after rounding down
+
+ worm.bombs = worm.bombs.saturating_sub(1);
+
+ for &(damage_offset, weapon_damage) in BOMB_DAMAGES.iter() {
+ let target = p + damage_offset;
+
+ if map_clone.at(target) == Some(true) {
+ self.map.clear(target);
+ self.players[player_index].moves_score += DIG_SCORE;
+ }
+ self.powerups.retain(|powerup| powerup.position != target);
+
+ let target_own_worm: Option<&mut Worm> = self.players[player_index]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+
+ if let Some(target_worm) = target_own_worm {
+ target_worm.health -= weapon_damage;
+ self.players[player_index].moves_score -=
+ weapon_damage * ATTACK_SCORE_MULTIPLIER;
+ if target_worm.health <= 0 {
+ self.players[player_index].moves_score -= KILL_SCORE;
+ }
+ }
+
+ let target_opponent_worm: Option<&mut Worm> = self.players
+ [GameBoard::opponent(player_index)]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+ if let Some(target_worm) = target_opponent_worm {
+ target_worm.health -= weapon_damage;
+ self.players[player_index].moves_score +=
+ weapon_damage * ATTACK_SCORE_MULTIPLIER;
+ if target_worm.health <= 0 {
+ self.players[player_index].moves_score += KILL_SCORE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ pub fn simulate_snowballs(&mut self, actions: [Action; 2]) {
+ for player_index in 0..actions.len() {
+ if let Action::Snowball(p) = actions[player_index] {
+ if self.map.at(p).is_some() {
+ if let Some(worm) = self.players[player_index].active_worm_mut() {
+ debug_assert!(
+ worm.snowballs > 0,
+ "Worm is throwing a snowball it doesn't have"
+ );
+ debug_assert!((worm.position - p).magnitude_squared() < 6 * 6); // max range is 5, but it's 5 after rounding down
+
+ worm.snowballs = worm.snowballs.saturating_sub(1);
+
+ for &freeze_offset in SNOWBALL_FREEZES.iter() {
+ let target = p + freeze_offset;
+
+ let target_own_worm: Option<&mut Worm> = self.players[player_index]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+
+ if let Some(target_worm) = target_own_worm {
+ target_worm.rounds_until_unfrozen = FREEZE_DURATION;
+ self.players[player_index].moves_score -= FREEZE_SCORE;
+ }
+
+ let target_opponent_worm: Option<&mut Worm> = self.players
+ [GameBoard::opponent(player_index)]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+ if let Some(target_worm) = target_opponent_worm {
+ target_worm.rounds_until_unfrozen = FREEZE_DURATION;
+ self.players[player_index].moves_score += FREEZE_SCORE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn simulate_shoots(&mut self, actions: [Action; 2]) {
+ 'players_loop: for player_index in 0..actions.len() {
+ if let Action::Shoot(dir) = actions[player_index] {
+ if let Some(worm) = self.players[player_index].active_worm() {
+ let center = worm.position;
+ let diff = dir.as_vec();
+
+ let range = if dir.is_diagonal() {
+ SHOOT_RANGE_DIAGONAL
+ } else {
+ SHOOT_RANGE
+ };
+
+ for distance in 1..=range {
+ let target = center + diff * distance;
+ match self.map.at(target) {
+ Some(false) => {
+ let target_own_worm: Option<&mut Worm> = self.players[player_index]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+
+ if let Some(target_worm) = target_own_worm {
+ target_worm.health -= SHOOT_DAMAGE;
+ self.players[player_index].moves_score -=
+ SHOOT_DAMAGE * ATTACK_SCORE_MULTIPLIER;
+ if target_worm.health <= 0 {
+ self.players[player_index].moves_score -= KILL_SCORE;
+ }
+ continue 'players_loop;
+ }
+
+ let target_opponent_worm: Option<&mut Worm> = self.players
+ [GameBoard::opponent(player_index)]
+ .worms
+ .iter_mut()
+ .find(|w| w.position == target);
+
+ if let Some(target_worm) = target_opponent_worm {
+ target_worm.health -= SHOOT_DAMAGE;
+ self.players[player_index].moves_score +=
+ SHOOT_DAMAGE * ATTACK_SCORE_MULTIPLIER;
+ if target_worm.health <= 0 {
+ self.players[player_index].moves_score += KILL_SCORE;
+ }
+
+ continue 'players_loop;
+ }
+ }
+ _ => break,
+ }
+ }
+
+ // You get here if the shot missed. Hits are an early return.
+ self.players[player_index].moves_score += MISSED_ATTACK_SCORE;
+ }
+ }
+ }
+ }
+
+ fn clear_dead_worms(&mut self) {
+ for player in &mut self.players {
+ player.clear_dead_worms();
+ }
+
+ self.occupied_cells = self
+ .players
+ .iter()
+ .flat_map(|p| p.worms.iter())
+ .map(|w| w.position)
+ .collect();
+ }
+
+ pub fn opponent(player_index: usize) -> usize {
+ (player_index + 1) % 2
+ }
+
+ fn selects_iter(&self, player_index: usize) -> impl Iterator<Item = (Option<i32>, &Worm)> {
+ let no_select = self.players[player_index]
+ .active_worm()
+ .into_iter()
+ .map(|w| (None, w));
+
+ let has_select_moves = self.players[player_index].select_moves > 0;
+ let active_worm_index = self.players[player_index].active_worm;
+ let selects = self.players[player_index]
+ .worms
+ .iter()
+ .enumerate()
+ .filter(move |(p, _w)| has_select_moves && active_worm_index != *p)
+ .map(|(_p, w)| (Some(w.id), w));
+
+ no_select.chain(selects)
+ }
+
+ fn pruned_valid_move_commands(&self, player_index: usize) -> ArrayVec<[Command; 8]> {
+ self.players[player_index]
+ .active_worm()
+ .into_iter()
+ .flat_map(|worm| {
+ // TODO: If you aren't on lava, don't step onto the lava
+ Direction::ALL
+ .iter()
+ .map(Direction::as_vec)
+ .map(move |d| worm.position + d)
+ .filter(|p| !self.occupied_cells.contains(p))
+ .filter_map(|p| match self.map.at(p) {
+ Some(false) => Some(Action::Move(p)),
+ Some(true) => Some(Action::Dig(p)),
+ _ => None,
+ })
+ .map(Command::new)
+ })
+ .collect()
+ }
+
+ fn pruned_valid_bomb_commands(&self, player_index: usize) -> Vec<Command> {
+ self.selects_iter(player_index)
+ .filter(|(_, worm)| worm.bombs > 0)
+ .flat_map(|(select, worm)| {
+ let mut result = Vec::with_capacity((BOMB_RANGE * 2 + 1).pow(2) as usize - 12);
+ let own_worm_positions: ArrayVec<[Point2d; 3]> = self.players[player_index]
+ .worms
+ .iter()
+ .map(|w| w.position)
+ .collect();
+ let opponent_worm_positions: ArrayVec<[Point2d; 3]> = self.players
+ [GameBoard::opponent(player_index)]
+ .worms
+ .iter()
+ .map(|w| w.position)
+ .collect();
+
+ for y in worm.position.y - BOMB_RANGE..=worm.position.y + BOMB_RANGE {
+ for x in worm.position.x - BOMB_RANGE..=worm.position.x + BOMB_RANGE {
+ let target = Point2d::new(x, y);
+ if self.map.at(target).is_some()
+ && (worm.position - target).magnitude_squared()
+ < (BOMB_RANGE + 1).pow(2)
+ {
+ let own_affected_worms = own_worm_positions.iter().any(|p| {
+ (target - *p).magnitude_squared()
+ <= BOMB_DAMAGE_RANGE * BOMB_DAMAGE_RANGE
+ });
+ let opponent_affected_worms = opponent_worm_positions.iter().any(|p| {
+ (target - *p).magnitude_squared()
+ <= BOMB_DAMAGE_RANGE * BOMB_DAMAGE_RANGE
+ });
+
+ if !own_affected_worms && opponent_affected_worms {
+ result.push(Command {
+ worm: select,
+ action: Action::Bomb(target),
+ });
+ }
+ }
+ }
+ }
+
+ result
+ })
+ .collect()
+ }
+
+ fn pruned_valid_snowball_commands(&self, player_index: usize) -> Vec<Command> {
+ self.selects_iter(player_index)
+ .filter(|(_, worm)| worm.snowballs > 0)
+ .flat_map(|(select, worm)| {
+ let mut result = Vec::with_capacity((SNOWBALL_RANGE * 2 + 1).pow(2) as usize - 12);
+ let own_worm_positions: ArrayVec<[Point2d; 3]> = self.players[player_index]
+ .worms
+ .iter()
+ .map(|w| w.position)
+ .collect();
+ let opponent_worm_positions: ArrayVec<[Point2d; 3]> = self.players
+ [GameBoard::opponent(player_index)]
+ .worms
+ .iter()
+ .map(|w| w.position)
+ .collect();
+
+ for y in worm.position.y - SNOWBALL_RANGE..=worm.position.y + SNOWBALL_RANGE {
+ for x in worm.position.x - SNOWBALL_RANGE..=worm.position.x + SNOWBALL_RANGE {
+ let target = Point2d::new(x, y);
+ if self.map.at(target).is_some()
+ && (worm.position - target).magnitude_squared()
+ < (SNOWBALL_RANGE + 1).pow(2)
+ {
+ let own_affected_worms = own_worm_positions.iter().any(|p| {
+ (target - *p).magnitude_squared()
+ <= SNOWBALL_FREEZE_RANGE * SNOWBALL_FREEZE_RANGE
+ });
+ let opponent_affected_worms = opponent_worm_positions.iter().any(|p| {
+ (target - *p).magnitude_squared()
+ <= SNOWBALL_FREEZE_RANGE * SNOWBALL_FREEZE_RANGE
+ });
+
+ if !own_affected_worms && opponent_affected_worms {
+ result.push(Command {
+ worm: select,
+ action: Action::Snowball(target),
+ });
+ }
+ }
+ }
+ }
+
+ result
+ })
+ .collect()
+ }
+
+ fn pruned_valid_shoot_commands(&self, player_index: usize) -> Vec<Command> {
+ self.selects_iter(player_index)
+ .flat_map(|(select, worm)| {
+ self.players[GameBoard::opponent(player_index)]
+ .worms
+ .iter()
+ .filter_map(move |w| {
+ let diff = w.position - worm.position;
+ if diff.x == 0 && diff.y.abs() <= SHOOT_RANGE {
+ if diff.y > 0 {
+ Some((Direction::South, diff.y))
+ } else {
+ Some((Direction::North, -diff.y))
+ }
+ } else if diff.y == 0 && diff.x.abs() <= SHOOT_RANGE {
+ if diff.x > 0 {
+ Some((Direction::East, diff.x))
+ } else {
+ Some((Direction::West, -diff.x))
+ }
+ } else if diff.x.abs() == diff.y.abs()
+ && diff.x.abs() <= SHOOT_RANGE_DIAGONAL
+ {
+ match (diff.x > 0, diff.y > 0) {
+ (true, true) => Some((Direction::SouthEast, diff.x)),
+ (false, true) => Some((Direction::SouthWest, -diff.x)),
+ (true, false) => Some((Direction::NorthEast, diff.x)),
+ (false, false) => Some((Direction::NorthWest, -diff.x)),
+ }
+ } else {
+ None
+ }
+ })
+ .filter(move |(dir, range)| {
+ let diff = dir.as_vec();
+ // NB: This is up to range EXCLUSIVE, so if there's
+ // anything in the way, even another good shot, skip.
+ !(1..*range).any(|distance| {
+ self.map.at(worm.position + diff * distance) != Some(false)
+ && !self
+ .players
+ .iter()
+ .flat_map(|p| p.worms.iter())
+ .any(|w| w.position == worm.position + diff * distance)
+ })
+ })
+ .map(move |(dir, _range)| Command {
+ worm: select,
+ action: Action::Shoot(dir),
+ })
+ })
+ .collect()
+ }
+
+ pub fn pruned_valid_moves(&self, player_index: usize) -> Vec<Command> {
+ if self.players[player_index].active_worm_is_frozen_after_tick() {
+ vec![Command::new(Action::DoNothing)]
+ } else {
+ self.pruned_valid_shoot_commands(player_index)
+ .iter()
+ .chain(self.pruned_valid_move_commands(player_index).iter())
+ .chain(self.pruned_valid_bomb_commands(player_index).iter())
+ .chain(self.pruned_valid_snowball_commands(player_index).iter())
+ .chain([Command::new(Action::DoNothing)].iter())
+ .cloned()
+ .collect()
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {}
diff --git a/2019-worms/src/game/map.rs b/2019-worms/src/game/map.rs
new file mode 100644
index 0000000..84ec99a
--- /dev/null
+++ b/2019-worms/src/game/map.rs
@@ -0,0 +1,49 @@
+use crate::constants::*;
+use crate::geometry::*;
+
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Map {
+ pub cells: [u64; MAP_U64S],
+}
+
+impl Map {
+ fn internal_index(p: Point2d) -> Option<(usize, usize)> {
+ if p.y < 0 || p.y as usize >= MAP_SIZE {
+ None
+ } else {
+ let row_data = &MAP_ROW_SIZE[p.y as usize];
+ if p.x < row_data.x_offset as i8 || p.x as usize >= row_data.x_offset + row_data.len() {
+ None
+ } else {
+ let global_bit = row_data.start_bit + p.x as usize - row_data.x_offset;
+ let integer = global_bit / 64;
+ let bit = global_bit % 64;
+ Some((integer, bit))
+ }
+ }
+ }
+
+ pub fn at(&self, p: Point2d) -> Option<bool> {
+ Map::internal_index(p).map(|(integer, bit)| {
+ let mask = 1 << bit;
+ self.cells[integer] & mask != 0
+ })
+ }
+
+ pub fn set(&mut self, p: Point2d) {
+ if let Some((integer, bit)) = Map::internal_index(p) {
+ let mask = 1 << bit;
+ self.cells[integer] |= mask;
+ } else {
+ panic!("Tried to set an out of bounds bit, {:?}", p);
+ }
+ }
+ pub fn clear(&mut self, p: Point2d) {
+ if let Some((integer, bit)) = Map::internal_index(p) {
+ let mask = !(1 << bit);
+ self.cells[integer] &= mask;
+ } else {
+ panic!("Tried to set an out of bounds bit, {:?}", p);
+ }
+ }
+}
diff --git a/2019-worms/src/game/player.rs b/2019-worms/src/game/player.rs
new file mode 100644
index 0000000..0874c76
--- /dev/null
+++ b/2019-worms/src/game/player.rs
@@ -0,0 +1,259 @@
+use crate::geometry::*;
+use arrayvec::ArrayVec;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Player {
+ pub moves_score: i32,
+ pub active_worm: usize,
+ pub select_moves: u8,
+ pub worms: ArrayVec<[Worm; 3]>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Worm {
+ pub id: i32,
+ pub health: i32,
+ pub position: Point2d,
+ pub bombs: u8,
+ pub snowballs: u8,
+ pub rounds_until_unfrozen: u8,
+}
+
+impl Player {
+ pub fn find_worm_position(&self, id: i32) -> Option<usize> {
+ self.worms.iter().position(|w| w.id == id)
+ }
+
+ pub fn find_worm(&self, id: i32) -> Option<&Worm> {
+ self.worms.iter().find(|w| w.id == id)
+ }
+
+ pub fn find_worm_mut(&mut self, id: i32) -> Option<&mut Worm> {
+ self.worms.iter_mut().find(|w| w.id == id)
+ }
+
+ pub fn active_worm(&self) -> Option<&Worm> {
+ self.worms.get(self.active_worm)
+ }
+
+ pub fn active_worm_mut(&mut self) -> Option<&mut Worm> {
+ self.worms.get_mut(self.active_worm)
+ }
+
+ pub fn health(&self) -> i32 {
+ self.worms.iter().map(|w| w.health).sum()
+ }
+
+ pub fn max_worm_health(&self) -> i32 {
+ self.worms.iter().map(|w| w.health).max().unwrap_or(0)
+ }
+
+ pub fn clear_dead_worms(&mut self) {
+ for worm_index in (0..self.worms.len()).rev() {
+ if self.worms[worm_index].health <= 0 {
+ self.worms.remove(worm_index);
+ if self.active_worm >= worm_index {
+ self.active_worm = if self.active_worm > 0 {
+ self.active_worm - 1
+ } else if self.worms.len() > 0 {
+ self.worms.len() - 1
+ } else {
+ 0
+ };
+ }
+ }
+ }
+ }
+
+ pub fn next_active_worm(&mut self) {
+ self.active_worm = (self.active_worm + 1)
+ .checked_rem(self.worms.len())
+ .unwrap_or(0);
+ }
+
+ fn health_score(&self) -> i32 {
+ self.health() / 3
+ }
+
+ pub fn score(&self) -> i32 {
+ self.moves_score + self.health_score()
+ }
+
+ pub fn active_worm_is_frozen(&self) -> bool {
+ self.active_worm()
+ .map(|worm| worm.rounds_until_unfrozen > 0)
+ .unwrap_or(false)
+ }
+
+ pub fn active_worm_is_frozen_after_tick(&self) -> bool {
+ self.active_worm()
+ .map(|worm| worm.rounds_until_unfrozen > 1)
+ .unwrap_or(false)
+ }
+
+ pub fn bombs(&self) -> u8 {
+ self.worms.iter().map(|w| w.bombs).sum()
+ }
+
+ pub fn snowballs(&self) -> u8 {
+ self.worms.iter().map(|w| w.snowballs).sum()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn clear_dead_worms_after_active_worm() {
+ let mut worms = ArrayVec::new();
+ worms.push(Worm {
+ id: 1,
+ health: 50,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 2,
+ health: 10,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 3,
+ health: -2,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ let mut player = Player {
+ active_worm: 1,
+ moves_score: 0,
+ select_moves: 0,
+ worms,
+ };
+
+ player.clear_dead_worms();
+
+ assert_eq!(2, player.worms.len());
+ assert_eq!(1, player.active_worm);
+
+ assert_eq!(1, player.worms[0].id);
+ assert_eq!(2, player.worms[1].id);
+ }
+
+ #[test]
+ fn clear_dead_worms_before_active_worm() {
+ let mut worms = ArrayVec::new();
+ worms.push(Worm {
+ id: 1,
+ health: 0,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 2,
+ health: 10,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 3,
+ health: 2,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ let mut player = Player {
+ active_worm: 1,
+ moves_score: 0,
+ worms,
+ select_moves: 0,
+ };
+
+ player.clear_dead_worms();
+
+ assert_eq!(2, player.worms.len());
+ assert_eq!(0, player.active_worm);
+
+ assert_eq!(2, player.worms[0].id);
+ assert_eq!(3, player.worms[1].id);
+ }
+
+ #[test]
+ fn clear_dead_worms_before_active_worm_wrapping() {
+ let mut worms = ArrayVec::new();
+ worms.push(Worm {
+ id: 1,
+ health: 0,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 2,
+ health: 10,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ worms.push(Worm {
+ id: 3,
+ health: 2,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ let mut player = Player {
+ active_worm: 0,
+ moves_score: 0,
+ worms,
+ select_moves: 0,
+ };
+
+ player.clear_dead_worms();
+
+ assert_eq!(2, player.worms.len());
+ assert_eq!(1, player.active_worm);
+
+ assert_eq!(2, player.worms[0].id);
+ assert_eq!(3, player.worms[1].id);
+ }
+
+ #[test]
+ fn clear_last_dead_worm() {
+ let mut worms = ArrayVec::new();
+ worms.push(Worm {
+ id: 1,
+ health: -10,
+ position: Point2d::new(0, 0),
+ rounds_until_unfrozen: 0,
+ bombs: 0,
+ snowballs: 0,
+ });
+ let mut player = Player {
+ active_worm: 0,
+ moves_score: 0,
+ worms,
+ select_moves: 0,
+ };
+
+ player.clear_dead_worms();
+
+ assert_eq!(0, player.worms.len());
+ // active worm is undefined in this case, but clearing the worms must not panic.
+ }
+}
diff --git a/2019-worms/src/game/powerup.rs b/2019-worms/src/game/powerup.rs
new file mode 100644
index 0000000..47e73a1
--- /dev/null
+++ b/2019-worms/src/game/powerup.rs
@@ -0,0 +1,6 @@
+use crate::geometry::*;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Powerup {
+ pub position: Point2d,
+}
diff --git a/2019-worms/src/geometry.rs b/2019-worms/src/geometry.rs
new file mode 100644
index 0000000..1bcdace
--- /dev/null
+++ b/2019-worms/src/geometry.rs
@@ -0,0 +1,6 @@
+mod vec;
+pub use self::vec::*;
+mod point;
+pub use self::point::*;
+mod direction;
+pub use self::direction::*;
diff --git a/2019-worms/src/geometry/direction.rs b/2019-worms/src/geometry/direction.rs
new file mode 100644
index 0000000..e37f750
--- /dev/null
+++ b/2019-worms/src/geometry/direction.rs
@@ -0,0 +1,67 @@
+use crate::geometry::vec::Vec2d;
+use std::fmt;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Direction {
+ North,
+ NorthEast,
+ East,
+ SouthEast,
+ South,
+ SouthWest,
+ West,
+ NorthWest,
+}
+
+impl fmt::Display for Direction {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Direction::*;
+ let s = match self {
+ North => "N",
+ NorthEast => "NE",
+ East => "E",
+ SouthEast => "SE",
+ South => "S",
+ SouthWest => "SW",
+ West => "W",
+ NorthWest => "NW",
+ };
+ f.write_str(s)
+ }
+}
+
+impl Direction {
+ pub fn is_diagonal(&self) -> bool {
+ use Direction::*;
+
+ match self {
+ NorthEast | SouthEast | SouthWest | NorthWest => true,
+ _ => false,
+ }
+ }
+
+ pub fn as_vec(&self) -> Vec2d {
+ use Direction::*;
+ match self {
+ North => Vec2d::new(0, -1),
+ NorthEast => Vec2d::new(1, -1),
+ East => Vec2d::new(1, 0),
+ SouthEast => Vec2d::new(1, 1),
+ South => Vec2d::new(0, 1),
+ SouthWest => Vec2d::new(-1, 1),
+ West => Vec2d::new(-1, 0),
+ NorthWest => Vec2d::new(-1, -1),
+ }
+ }
+
+ pub const ALL: [Direction; 8] = [
+ Direction::North,
+ Direction::NorthEast,
+ Direction::East,
+ Direction::SouthEast,
+ Direction::South,
+ Direction::SouthWest,
+ Direction::West,
+ Direction::NorthWest,
+ ];
+}
diff --git a/2019-worms/src/geometry/point.rs b/2019-worms/src/geometry/point.rs
new file mode 100644
index 0000000..1ab9b36
--- /dev/null
+++ b/2019-worms/src/geometry/point.rs
@@ -0,0 +1,37 @@
+use crate::geometry::vec::*;
+
+use std::ops::*;
+
+#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
+pub struct Point2d {
+ pub x: i8,
+ pub y: i8,
+}
+
+impl Point2d {
+ pub fn new(x: i8, y: i8) -> Point2d {
+ Point2d { x, y }
+ }
+}
+
+impl Add<Vec2d> for Point2d {
+ type Output = Self;
+
+ fn add(self, other: Vec2d) -> Self {
+ Point2d {
+ x: self.x.saturating_add(other.x),
+ y: self.y.saturating_add(other.y),
+ }
+ }
+}
+
+impl Sub for Point2d {
+ type Output = Vec2d;
+
+ fn sub(self, other: Self) -> Vec2d {
+ Vec2d {
+ x: self.x.saturating_sub(other.x),
+ y: self.y.saturating_sub(other.y),
+ }
+ }
+}
diff --git a/2019-worms/src/geometry/vec.rs b/2019-worms/src/geometry/vec.rs
new file mode 100644
index 0000000..375a0f9
--- /dev/null
+++ b/2019-worms/src/geometry/vec.rs
@@ -0,0 +1,62 @@
+use std::ops::*;
+
+#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
+pub struct Vec2d {
+ pub x: i8,
+ pub y: i8,
+}
+
+impl Vec2d {
+ pub const fn new(x: i8, y: i8) -> Vec2d {
+ Vec2d { x, y }
+ }
+ pub fn magnitude_squared(&self) -> i8 {
+ self.x
+ .saturating_pow(2)
+ .saturating_add(self.y.saturating_pow(2))
+ }
+}
+
+impl Add for Vec2d {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ Vec2d {
+ x: self.x.saturating_add(other.x),
+ y: self.y.saturating_add(other.y),
+ }
+ }
+}
+
+impl Sub for Vec2d {
+ type Output = Self;
+
+ fn sub(self, other: Self) -> Self {
+ Vec2d {
+ x: self.x.saturating_sub(other.x),
+ y: self.y.saturating_sub(other.y),
+ }
+ }
+}
+
+impl Mul<i8> for Vec2d {
+ type Output = Self;
+
+ fn mul(self, other: i8) -> Self {
+ Vec2d {
+ x: self.x.saturating_mul(other),
+ y: self.y.saturating_mul(other),
+ }
+ }
+}
+
+impl Neg for Vec2d {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ Vec2d {
+ x: -self.x,
+ y: -self.y,
+ }
+ }
+}
diff --git a/2019-worms/src/json.rs b/2019-worms/src/json.rs
new file mode 100644
index 0000000..a83f102
--- /dev/null
+++ b/2019-worms/src/json.rs
@@ -0,0 +1,554 @@
+use std::error::Error;
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::Path;
+
+use serde::{Deserialize, Serialize};
+use serde_json;
+
+pub fn read_state_from_json_file(filename: &Path) -> Result<State, Box<dyn Error>> {
+ let mut file = File::open(filename)?;
+ let mut content = String::new();
+ file.read_to_string(&mut content)?;
+ let state: State = serde_json::from_str(content.as_ref())?;
+
+ Ok(state)
+}
+
+// TODO: Narrow numeric types
+// TODO: comment out stuff I don't want / need
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct State {
+ pub current_round: u16,
+ pub max_rounds: u16,
+ pub pushback_damage: i32,
+ pub lava_damage: i32,
+ pub map_size: u8,
+ pub current_worm_id: i32,
+ pub consecutive_do_nothing_count: u32,
+ pub my_player: Player,
+ pub opponents: Vec<Opponent>,
+ pub map: Vec<Vec<Cell>>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Player {
+ pub id: i32,
+ pub score: i32,
+ pub health: i32,
+ pub worms: Vec<PlayerWorm>,
+ pub remaining_worm_selections: u8,
+}
+
+impl Player {
+ pub fn health_score(&self) -> i32 {
+ self.health / 3
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct PlayerWorm {
+ pub id: i32,
+ pub health: i32,
+ pub position: Position,
+ pub digging_range: u32,
+ pub movement_range: u32,
+ pub rounds_until_unfrozen: u8,
+ pub weapon: Weapon,
+ pub banana_bombs: Option<Bomb>,
+ pub snowballs: Option<Snowball>,
+ pub profession: WormType,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Opponent {
+ pub id: i32,
+ pub score: i32,
+ pub current_worm_id: i32,
+ pub previous_command: String,
+ pub worms: Vec<OpponentWorm>,
+ pub remaining_worm_selections: u8,
+}
+
+impl Opponent {
+ pub fn health_score(&self) -> i32 {
+ self.worms.iter().map(|w| w.health).sum::<i32>() / 3
+ }
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct OpponentWorm {
+ pub id: i32,
+ pub health: i32,
+ pub position: Position,
+ pub digging_range: u32,
+ pub movement_range: u32,
+ pub rounds_until_unfrozen: u8,
+ pub profession: WormType,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "PascalCase")]
+pub enum WormType {
+ Commando,
+ Agent,
+ Technologist,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Cell {
+ pub x: i8,
+ pub y: i8,
+ #[serde(rename = "type")]
+ pub cell_type: CellType,
+ pub occupier: Option<CellWorm>,
+ pub powerup: Option<Powerup>,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum CellType {
+ Air,
+ Dirt,
+ Lava,
+ DeepSpace,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(untagged)]
+#[serde(rename_all = "camelCase")]
+pub enum CellWorm {
+ #[serde(rename_all = "camelCase")]
+ PlayerWorm {
+ id: i32,
+ player_id: i32,
+ health: i32,
+ position: Position,
+ digging_range: u32,
+ movement_range: u32,
+ weapon: Weapon,
+ },
+ #[serde(rename_all = "camelCase")]
+ OpponentWorm {
+ id: i32,
+ player_id: i32,
+ health: i32,
+ position: Position,
+ digging_range: u32,
+ movement_range: u32,
+ },
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Powerup {
+ #[serde(rename = "type")]
+ pub powerup_type: PowerupType,
+ pub value: i32,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum PowerupType {
+ HealthPack,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Position {
+ pub x: i8,
+ pub y: i8,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Weapon {
+ pub damage: i32,
+ pub range: u8,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Bomb {
+ pub damage: i32,
+ pub range: u8,
+ pub count: u8,
+ pub damage_radius: u8,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct Snowball {
+ pub freeze_duration: u8,
+ pub range: u8,
+ pub count: u8,
+ pub freeze_radius: u8,
+}
+
+impl State {
+ pub fn active_worm_index(&self) -> Option<usize> {
+ self.my_player
+ .worms
+ .iter()
+ .filter(|w| w.health > 0)
+ .position(|w| w.id == self.current_worm_id)
+ }
+
+ pub fn opponent_active_worm_index(&self) -> Option<usize> {
+ self.opponents[0]
+ .worms
+ .iter()
+ .filter(|w| w.health > 0)
+ .position(|w| w.id == self.opponents[0].current_worm_id)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn example_parses_correctly() {
+ let example = r#"
+{
+ "currentRound": 0,
+ "maxRounds": 200,
+ "pushbackDamage": 20,
+ "lavaDamage": 3,
+ "mapSize": 33,
+ "currentWormId": 1,
+ "consecutiveDoNothingCount": 0,
+ "myPlayer": {
+ "id": 1,
+ "score": 100,
+ "health": 300,
+ "currentWormId": 1,
+ "remainingWormSelections": 1,
+ "worms": [
+ {
+ "id": 1,
+ "health": 100,
+ "position": {
+ "x": 24,
+ "y": 29
+ },
+ "weapon": {
+ "damage": 1,
+ "range": 3
+ },
+ "bananaBombs": {
+ "damage": 20,
+ "range": 5,
+ "count": 3,
+ "damageRadius": 2
+ },
+ "diggingRange": 1,
+ "movementRange": 1,
+ "roundsUntilUnfrozen": 0,
+ "profession": "Agent"
+ },
+ {
+ "id": 2,
+ "health": 150,
+ "position": {
+ "x": 1,
+ "y": 16
+ },
+ "weapon": {
+ "damage": 1,
+ "range": 3
+ },
+ "diggingRange": 1,
+ "movementRange": 1,
+ "roundsUntilUnfrozen": 0,
+ "profession": "Commando"
+ },
+ {
+ "id": 3,
+ "health": 100,
+ "position": {
+ "x": 24,
+ "y": 4
+ },
+ "weapon": {
+ "damage": 8,
+ "range": 4
+ },
+ "snowballs": {
+ "freezeDuration": 5,
+ "range": 5,
+ "count": 3,
+ "freezeRadius": 1
+ },
+ "diggingRange": 1,
+ "movementRange": 1,
+ "roundsUntilUnfrozen": 3,
+ "profession": "Technologist"
+ }
+ ]
+ },
+ "opponents": [
+ {
+ "id": 2,
+ "score": 100,
+ "currentWormId": 3,
+ "remainingWormSelections": 2,
+ "previousCommand": "nothing",
+ "worms": [
+ {
+ "id": 1,
+ "health": 100,
+ "position": {
+ "x": 31,
+ "y": 16
+ },
+ "diggingRange": 1,
+ "movementRange": 1,
+ "roundsUntilUnfrozen": 0,
+ "profession": "Commando"
+ }
+ ]
+ }
+ ],
+ "map": [
+ [
+ {
+ "x": 0,
+ "y": 0,
+ "type": "DEEP_SPACE"
+ },
+ {
+ "x": 1,
+ "y": 0,
+ "type": "AIR"
+ },
+ {
+ "x": 2,
+ "y": 0,
+ "type": "DIRT"
+ }
+ ],
+ [
+ {
+ "x": 0,
+ "y": 1,
+ "type": "AIR",
+ "powerup": {
+ "type": "HEALTH_PACK",
+ "value": 5
+ }
+ },
+ {
+ "x": 1,
+ "y": 1,
+ "type": "AIR",
+ "occupier": {
+ "id": 1,
+ "playerId": 2,
+ "health": 100,
+ "position": {
+ "x": 1,
+ "y": 1
+ },
+ "diggingRange": 1,
+ "movementRange": 1
+ }
+ },
+ {
+ "x": 2,
+ "y": 1,
+ "type": "AIR",
+ "occupier": {
+ "id": 1,
+ "playerId": 1,
+ "health": 100,
+ "position": {
+ "x": 2,
+ "y": 1
+ },
+ "weapon": {
+ "damage": 1,
+ "range": 3
+ },
+ "diggingRange": 1,
+ "movementRange": 1
+ }
+ }
+ ]
+ ]
+}"#;
+
+ let expected = State {
+ current_round: 0,
+ max_rounds: 200,
+ pushback_damage: 20,
+ lava_damage: 3,
+ map_size: 33,
+ current_worm_id: 1,
+ consecutive_do_nothing_count: 0,
+ my_player: Player {
+ id: 1,
+ score: 100,
+ health: 300,
+ remaining_worm_selections: 1,
+ worms: vec![
+ PlayerWorm {
+ id: 1,
+ health: 100,
+ position: Position { x: 24, y: 29 },
+ weapon: Weapon {
+ damage: 1,
+ range: 3,
+ },
+ digging_range: 1,
+ movement_range: 1,
+ banana_bombs: Some(Bomb {
+ damage: 20,
+ range: 5,
+ count: 3,
+ damage_radius: 2,
+ }),
+ snowballs: None,
+ rounds_until_unfrozen: 0,
+ profession: WormType::Agent,
+ },
+ PlayerWorm {
+ id: 2,
+ health: 150,
+ position: Position { x: 1, y: 16 },
+ weapon: Weapon {
+ damage: 1,
+ range: 3,
+ },
+ digging_range: 1,
+ movement_range: 1,
+ banana_bombs: None,
+ snowballs: None,
+ rounds_until_unfrozen: 0,
+ profession: WormType::Commando,
+ },
+ PlayerWorm {
+ id: 3,
+ health: 100,
+ position: Position { x: 24, y: 4 },
+ weapon: Weapon {
+ damage: 8,
+ range: 4,
+ },
+ digging_range: 1,
+ movement_range: 1,
+ banana_bombs: None,
+ snowballs: Some(Snowball {
+ freeze_duration: 5,
+ range: 5,
+ count: 3,
+ freeze_radius: 1,
+ }),
+ rounds_until_unfrozen: 3,
+ profession: WormType::Technologist,
+ },
+ ],
+ },
+ opponents: vec![Opponent {
+ id: 2,
+ score: 100,
+ remaining_worm_selections: 2,
+ current_worm_id: 3,
+ previous_command: "nothing".into(),
+ worms: vec![OpponentWorm {
+ id: 1,
+ health: 100,
+ position: Position { x: 31, y: 16 },
+ digging_range: 1,
+ movement_range: 1,
+ rounds_until_unfrozen: 0,
+ profession: WormType::Commando,
+ }],
+ }],
+ map: vec![
+ vec![
+ Cell {
+ x: 0,
+ y: 0,
+ cell_type: CellType::DeepSpace,
+ occupier: None,
+ powerup: None,
+ },
+ Cell {
+ x: 1,
+ y: 0,
+ cell_type: CellType::Air,
+ occupier: None,
+ powerup: None,
+ },
+ Cell {
+ x: 2,
+ y: 0,
+ cell_type: CellType::Dirt,
+ occupier: None,
+ powerup: None,
+ },
+ ],
+ vec![
+ Cell {
+ x: 0,
+ y: 1,
+ cell_type: CellType::Air,
+ occupier: None,
+ powerup: Some(Powerup {
+ powerup_type: PowerupType::HealthPack,
+ value: 5,
+ }),
+ },
+ Cell {
+ x: 1,
+ y: 1,
+ cell_type: CellType::Air,
+ occupier: Some(CellWorm::OpponentWorm {
+ id: 1,
+ player_id: 2,
+ health: 100,
+ position: Position { x: 1, y: 1 },
+ digging_range: 1,
+ movement_range: 1,
+ }),
+ powerup: None,
+ },
+ Cell {
+ x: 2,
+ y: 1,
+ cell_type: CellType::Air,
+ occupier: Some(CellWorm::PlayerWorm {
+ id: 1,
+ player_id: 1,
+ health: 100,
+ position: Position { x: 2, y: 1 },
+ digging_range: 1,
+ movement_range: 1,
+ weapon: Weapon {
+ damage: 1,
+ range: 3,
+ },
+ }),
+ powerup: None,
+ },
+ ],
+ ],
+ };
+
+ let parsed: State = serde_json::from_str(example).unwrap();
+
+ assert_eq!(
+ parsed, expected,
+ "Parsed value did not match the expected value.\nParsed = {:#?}\nExpected = {:#?}",
+ parsed, expected
+ );
+ }
+}
diff --git a/2019-worms/src/lib.rs b/2019-worms/src/lib.rs
new file mode 100644
index 0000000..9922cce
--- /dev/null
+++ b/2019-worms/src/lib.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::all)]
+
+pub mod command;
+pub mod json;
+pub mod geometry;
+pub mod game;
+pub mod strategy;
+pub mod constants;
diff --git a/2019-worms/src/main.rs b/2019-worms/src/main.rs
new file mode 100644
index 0000000..4f98e75
--- /dev/null
+++ b/2019-worms/src/main.rs
@@ -0,0 +1,46 @@
+use std::io::prelude::*;
+use std::io::stdin;
+use std::path::Path;
+
+use time::{Duration, PreciseTime};
+
+use steam_powered_wyrm::command::{Action, Command};
+use steam_powered_wyrm::game;
+use steam_powered_wyrm::json;
+use steam_powered_wyrm::strategy::{choose_move, ScoreConfig};
+
+fn main() {
+ let max_time = Duration::milliseconds(900);
+ let config = ScoreConfig::default();
+
+ let mut game_board = None;
+ for line in stdin().lock().lines() {
+ let start_time = PreciseTime::now();
+
+ let round_number = line.expect("Failed to read line from stdin: {}");
+
+ let command = match json::read_state_from_json_file(&Path::new(&format!(
+ "./rounds/{}/state.json",
+ round_number
+ ))) {
+ Ok(json_state) => match &mut game_board {
+ None => {
+ let new_board = game::GameBoard::new(json_state);
+ let command = choose_move(&new_board, &config, start_time, max_time);
+ game_board = Some(new_board);
+ command
+ }
+ Some(game_board) => {
+ game_board.update(json_state);
+ choose_move(&game_board, &config, start_time, max_time)
+ }
+ },
+ Err(e) => {
+ #[cfg(feature = "logging")]
+ eprintln!("WARN: State file could not be parsed: {}", e);
+ Command::new(Action::DoNothing)
+ }
+ };
+ println!("C;{};{}", round_number, command);
+ }
+}
diff --git a/2019-worms/src/strategy.rs b/2019-worms/src/strategy.rs
new file mode 100644
index 0000000..fce842b
--- /dev/null
+++ b/2019-worms/src/strategy.rs
@@ -0,0 +1,5 @@
+//mod mcts;
+//pub use mcts::{choose_move, Node};
+
+mod minimax;
+pub use minimax::{choose_move, choose_move_with_normalized_perf, Node, ScoreConfig};
diff --git a/2019-worms/src/strategy/minimax.rs b/2019-worms/src/strategy/minimax.rs
new file mode 100644
index 0000000..656ee36
--- /dev/null
+++ b/2019-worms/src/strategy/minimax.rs
@@ -0,0 +1,330 @@
+use crate::command::{Action, Command};
+use crate::constants::*;
+use crate::game::{GameBoard, SimulationOutcome};
+
+use fnv::FnvHashMap;
+use std::cmp;
+use std::ops::*;
+use time::{Duration, PreciseTime};
+
+#[derive(Debug, Clone)]
+pub struct ScoreConfig {
+ pub max_health_weight: f32,
+ pub total_health_weight: f32,
+ pub points_weight: f32,
+ pub victory_weight: f32,
+ pub snowball_weight: f32,
+ pub bomb_weight: f32,
+ pub explore_exploit_weight: f32,
+}
+
+impl Default for ScoreConfig {
+ fn default() -> ScoreConfig {
+ ScoreConfig {
+ max_health_weight: 100.,
+ total_health_weight: 10.,
+ points_weight: 1.,
+ victory_weight: 4500.,
+ snowball_weight: 10.,
+ bomb_weight: 10.,
+ explore_exploit_weight: 100.,
+ }
+ }
+}
+
+pub fn choose_move(
+ state: &GameBoard,
+ config: &ScoreConfig,
+ start_time: PreciseTime,
+ max_time: Duration,
+) -> Command {
+ let mut root_node = Node {
+ score_sum: ScoreSum::new(),
+ player_score_sums: [FnvHashMap::default(), FnvHashMap::default()],
+ unexplored: move_combos(state),
+ children: FnvHashMap::default(),
+ };
+
+ #[cfg(feature = "logging")]
+ {
+ let mut max_expand_time = Duration::milliseconds(0);
+ while start_time.to(PreciseTime::now()) < max_time {
+ let expand_start_time = PreciseTime::now();
+ let _ = expand_tree(&mut root_node, state.clone(), config);
+ max_expand_time = max_expand_time.max(expand_start_time.to(PreciseTime::now()));
+ }
+ eprintln!(
+ "Max expand time: {:?} ns",
+ max_expand_time.num_nanoseconds()
+ );
+ }
+ #[cfg(not(feature = "logging"))]
+ {
+ while start_time.to(PreciseTime::now()) < max_time {
+ let _ = expand_tree(&mut root_node, state.clone(), config);
+ }
+ }
+
+ #[cfg(feature = "logging")]
+ {
+ eprintln!("Number of simulations: {}", root_node.score_sum.visit_count);
+ for (command, score_sum) in &root_node.player_score_sums[0] {
+ eprintln!(
+ "{} = {} ({} visits)",
+ command,
+ score_sum.avg().val,
+ score_sum.visit_count
+ );
+ }
+ }
+
+ best_player_move(&root_node, 0)
+}
+
+pub fn choose_move_with_normalized_perf(
+ state: &GameBoard,
+ config: &ScoreConfig,
+ player_index: usize,
+ iterations: usize,
+) -> Command {
+ let mut root_node = Node {
+ score_sum: ScoreSum::new(),
+ player_score_sums: [FnvHashMap::default(), FnvHashMap::default()],
+ unexplored: move_combos(state),
+ children: FnvHashMap::default(),
+ };
+
+ for _ in 0..iterations {
+ let _ = expand_tree(&mut root_node, state.clone(), config);
+ }
+
+ #[cfg(feature = "logging")]
+ {
+ eprintln!("Number of simulations: {}", root_node.score_sum.visit_count);
+ for (command, score_sum) in &root_node.player_score_sums[player_index] {
+ eprintln!(
+ "{} = {} ({} visits)",
+ command,
+ score_sum.avg().val,
+ score_sum.visit_count
+ );
+ }
+ }
+
+ best_player_move(&root_node, player_index)
+}
+
+pub struct Node {
+ score_sum: ScoreSum,
+ player_score_sums: [FnvHashMap<Command, ScoreSum>; 2],
+ unexplored: Vec<[Command; 2]>,
+ children: FnvHashMap<[Command; 2], Node>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+struct Score {
+ val: f32,
+}
+
+impl AddAssign for Score {
+ fn add_assign(&mut self, other: Self) {
+ self.val = self.val + other.val;
+ }
+}
+
+impl Div<u32> for Score {
+ type Output = Self;
+ fn div(self, other: u32) -> Self {
+ Score {
+ val: self.val / other as f32,
+ }
+ }
+}
+
+impl Mul<f32> for Score {
+ type Output = Self;
+ fn mul(self, other: f32) -> Self {
+ Score {
+ val: self.val * other,
+ }
+ }
+}
+
+impl cmp::Eq for Score {}
+impl cmp::Ord for Score {
+ fn cmp(&self, other: &Score) -> cmp::Ordering {
+ self.val
+ .partial_cmp(&other.val)
+ .unwrap_or(cmp::Ordering::Equal)
+ }
+}
+
+struct ScoreSum {
+ sum: Score,
+ visit_count: u32,
+}
+
+impl ScoreSum {
+ fn new() -> ScoreSum {
+ ScoreSum {
+ sum: Score { val: 0. },
+ visit_count: 0,
+ }
+ }
+ fn with_initial(score: Score) -> ScoreSum {
+ ScoreSum {
+ sum: score,
+ visit_count: 1,
+ }
+ }
+ fn avg(&self) -> Score {
+ self.sum / self.visit_count
+ }
+}
+
+impl AddAssign<Score> for ScoreSum {
+ fn add_assign(&mut self, other: Score) {
+ self.sum += other;
+ self.visit_count = self.visit_count.saturating_add(1);
+ }
+}
+
+fn expand_tree(node: &mut Node, mut state: GameBoard, config: &ScoreConfig) -> Score {
+ if state.outcome != SimulationOutcome::Continue {
+ score(&state, config)
+ } else if let Some(commands) = node.unexplored.pop() {
+ state.simulate(commands);
+ let score = score(&state, config);
+ let unexplored = if state.outcome == SimulationOutcome::Continue {
+ move_combos(&state)
+ } else {
+ Vec::new()
+ };
+
+ let new_node = Node {
+ score_sum: ScoreSum::with_initial(score),
+ player_score_sums: [FnvHashMap::default(), FnvHashMap::default()],
+ unexplored,
+ children: FnvHashMap::default(),
+ };
+ node.children.insert(commands, new_node);
+ update(node, commands, score);
+
+ score
+ } else {
+ let commands = choose_existing(node, config);
+ state.simulate(commands);
+ let score = expand_tree(
+ node.children
+ .get_mut(&commands)
+ .expect("The existing node hasn't been tried yet"),
+ state,
+ config,
+ );
+ update(node, commands, score);
+ score
+ }
+}
+
+fn move_combos(state: &GameBoard) -> Vec<[Command; 2]> {
+ let player_moves = state.pruned_valid_moves(0);
+ let opponent_moves = state.pruned_valid_moves(1);
+ debug_assert!(!player_moves.is_empty(), "No player moves");
+ debug_assert!(!opponent_moves.is_empty(), "No opponent moves");
+
+ let mut result = Vec::with_capacity(player_moves.len() * opponent_moves.len());
+ for p in &player_moves {
+ for o in &opponent_moves {
+ result.push([*p, *o]);
+ }
+ }
+
+ result
+}
+
+fn best_player_move(node: &Node, player_index: usize) -> Command {
+ let multiplier = if player_index == 0 { 1. } else { -1. };
+ node.player_score_sums[player_index]
+ .iter()
+ .max_by_key(|(_command, score_sum)| score_sum.avg() * multiplier)
+ .map(|(command, _score_sum)| *command)
+ .unwrap_or_else(|| Command::new(Action::DoNothing))
+}
+
+fn score(state: &GameBoard, config: &ScoreConfig) -> Score {
+ let max_health =
+ (state.players[0].max_worm_health() - state.players[1].max_worm_health()) as f32;
+ let total_health = (state.players[0].health() - state.players[1].health()) as f32;
+ let points = (state.players[0].score() - state.players[1].score()) as f32;
+ let victory = match state.outcome {
+ SimulationOutcome::PlayerWon(0) => 1.,
+ SimulationOutcome::PlayerWon(1) => -1.,
+ _ => 0.,
+ };
+
+ let time_to_end = MAX_ROUNDS as f32 - state.round as f32 + 1.;
+
+ let snowballs = state.players[0].snowballs() as f32 - state.players[1].snowballs() as f32;
+ let bombs = state.players[0].bombs() as f32 - state.players[1].bombs() as f32;
+
+ Score {
+ val: max_health * config.max_health_weight
+ + total_health * config.total_health_weight
+ + points * config.points_weight
+ + victory * config.victory_weight
+ + snowballs * config.snowball_weight / time_to_end
+ + bombs * config.bomb_weight / time_to_end,
+ }
+}
+
+fn choose_existing(node: &Node, config: &ScoreConfig) -> [Command; 2] {
+ [
+ choose_one_existing(node, 0, config),
+ choose_one_existing(node, 1, config),
+ ]
+}
+
+fn choose_one_existing(node: &Node, player_index: usize, config: &ScoreConfig) -> Command {
+ let ln_n = (node.score_sum.visit_count as f32).ln();
+ let multiplier = if player_index == 0 { 1. } else { -1. };
+ let mut command_confidences =
+ node.player_score_sums[player_index]
+ .iter()
+ .map(|(command, score_sum)| {
+ (
+ command,
+ (score_sum.avg() * multiplier).val
+ + config.explore_exploit_weight
+ * (ln_n / score_sum.visit_count as f32).sqrt(),
+ )
+ });
+
+ command_confidences
+ .next()
+ .map(|first| {
+ command_confidences
+ .fold(
+ first,
+ |(acc_command, acc_confidence), (next_command, next_confidence)| {
+ if acc_confidence > next_confidence {
+ (acc_command, acc_confidence)
+ } else {
+ (next_command, next_confidence)
+ }
+ },
+ )
+ .0
+ .clone()
+ })
+ .unwrap_or_else(|| Command::new(Action::DoNothing))
+}
+
+fn update(node: &mut Node, commands: [Command; 2], score: Score) {
+ *node.player_score_sums[0]
+ .entry(commands[0])
+ .or_insert_with(ScoreSum::new) += score;
+ *node.player_score_sums[1]
+ .entry(commands[1])
+ .or_insert_with(ScoreSum::new) += score;
+ node.score_sum += score;
+}
diff --git a/2019-worms/tests/example-state.json b/2019-worms/tests/example-state.json
new file mode 100644
index 0000000..d7a8fa3
--- /dev/null
+++ b/2019-worms/tests/example-state.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":1,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":2,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"AIR"},{"x":13,"y":0,"type":"AIR"},{"x":14,"y":0,"type":"AIR"},{"x":15,"y":0,"type":"DIRT"},{"x":16,"y":0,"type":"DIRT"},{"x":17,"y":0,"type":"DIRT"},{"x":18,"y":0,"type":"AIR"},{"x":19,"y":0,"type":"AIR"},{"x":20,"y":0,"type":"AIR"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"DIRT"},{"x":10,"y":1,"type":"DIRT"},{"x":11,"y":1,"type":"DIRT"},{"x":12,"y":1,"type":"AIR"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"DIRT"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"DIRT"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"DIRT"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"AIR"},{"x":21,"y":1,"type":"DIRT"},{"x":22,"y":1,"type":"DIRT"},{"x":23,"y":1,"type":"DIRT"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"AIR"},{"x":12,"y":2,"type":"AIR"},{"x":13,"y":2,"type":"AIR"},{"x":14,"y":2,"type":"AIR"},{"x":15,"y":2,"type":"DIRT"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"DIRT"},{"x":18,"y":2,"type":"AIR"},{"x":19,"y":2,"type":"AIR"},{"x":20,"y":2,"type":"AIR"},{"x":21,"y":2,"type":"AIR"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"AIR"},{"x":12,"y":3,"type":"AIR"},{"x":13,"y":3,"type":"AIR"},{"x":14,"y":3,"type":"AIR"},{"x":15,"y":3,"type":"DIRT"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"DIRT"},{"x":18,"y":3,"type":"AIR"},{"x":19,"y":3,"type":"AIR"},{"x":20,"y":3,"type":"AIR"},{"x":21,"y":3,"type":"AIR"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"AIR"},{"x":5,"y":4,"type":"AIR"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"AIR"},{"x":12,"y":4,"type":"AIR"},{"x":13,"y":4,"type":"DIRT"},{"x":14,"y":4,"type":"DIRT"},{"x":15,"y":4,"type":"DIRT"},{"x":16,"y":4,"type":"AIR"},{"x":17,"y":4,"type":"DIRT"},{"x":18,"y":4,"type":"DIRT"},{"x":19,"y":4,"type":"DIRT"},{"x":20,"y":4,"type":"AIR"},{"x":21,"y":4,"type":"AIR"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"AIR"},{"x":28,"y":4,"type":"AIR"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"AIR"},{"x":5,"y":5,"type":"AIR"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"AIR"},{"x":13,"y":5,"type":"AIR"},{"x":14,"y":5,"type":"AIR"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"AIR"},{"x":19,"y":5,"type":"AIR"},{"x":20,"y":5,"type":"AIR"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"AIR"},{"x":28,"y":5,"type":"AIR"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"AIR"},{"x":4,"y":6,"type":"AIR"},{"x":5,"y":6,"type":"AIR"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"DIRT"},{"x":12,"y":6,"type":"AIR"},{"x":13,"y":6,"type":"AIR"},{"x":14,"y":6,"type":"AIR"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"AIR"},{"x":19,"y":6,"type":"AIR"},{"x":20,"y":6,"type":"AIR"},{"x":21,"y":6,"type":"DIRT"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"AIR"},{"x":28,"y":6,"type":"AIR"},{"x":29,"y":6,"type":"AIR"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"AIR"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"AIR"},{"x":5,"y":7,"type":"AIR"},{"x":6,"y":7,"type":"AIR"},{"x":7,"y":7,"type":"AIR"},{"x":8,"y":7,"type":"AIR"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"AIR"},{"x":12,"y":7,"type":"DIRT"},{"x":13,"y":7,"type":"DIRT"},{"x":14,"y":7,"type":"AIR"},{"x":15,"y":7,"type":"AIR"},{"x":16,"y":7,"type":"DIRT"},{"x":17,"y":7,"type":"AIR"},{"x":18,"y":7,"type":"AIR"},{"x":19,"y":7,"type":"DIRT"},{"x":20,"y":7,"type":"DIRT"},{"x":21,"y":7,"type":"AIR"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"AIR"},{"x":25,"y":7,"type":"AIR"},{"x":26,"y":7,"type":"AIR"},{"x":27,"y":7,"type":"AIR"},{"x":28,"y":7,"type":"AIR"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"AIR"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"DIRT"},{"x":2,"y":8,"type":"DIRT"},{"x":3,"y":8,"type":"AIR"},{"x":4,"y":8,"type":"AIR"},{"x":5,"y":8,"type":"AIR"},{"x":6,"y":8,"type":"AIR"},{"x":7,"y":8,"type":"DIRT"},{"x":8,"y":8,"type":"DIRT"},{"x":9,"y":8,"type":"AIR"},{"x":10,"y":8,"type":"AIR"},{"x":11,"y":8,"type":"AIR"},{"x":12,"y":8,"type":"DIRT"},{"x":13,"y":8,"type":"DIRT"},{"x":14,"y":8,"type":"DIRT"},{"x":15,"y":8,"type":"AIR"},{"x":16,"y":8,"type":"DIRT"},{"x":17,"y":8,"type":"AIR"},{"x":18,"y":8,"type":"DIRT"},{"x":19,"y":8,"type":"DIRT"},{"x":20,"y":8,"type":"DIRT"},{"x":21,"y":8,"type":"AIR"},{"x":22,"y":8,"type":"AIR"},{"x":23,"y":8,"type":"AIR"},{"x":24,"y":8,"type":"DIRT"},{"x":25,"y":8,"type":"DIRT"},{"x":26,"y":8,"type":"AIR"},{"x":27,"y":8,"type":"AIR"},{"x":28,"y":8,"type":"AIR"},{"x":29,"y":8,"type":"AIR"},{"x":30,"y":8,"type":"DIRT"},{"x":31,"y":8,"type":"DIRT"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"DIRT"},{"x":2,"y":9,"type":"DIRT"},{"x":3,"y":9,"type":"AIR"},{"x":4,"y":9,"type":"AIR"},{"x":5,"y":9,"type":"AIR"},{"x":6,"y":9,"type":"DIRT"},{"x":7,"y":9,"type":"AIR"},{"x":8,"y":9,"type":"DIRT"},{"x":9,"y":9,"type":"DIRT"},{"x":10,"y":9,"type":"AIR"},{"x":11,"y":9,"type":"DIRT"},{"x":12,"y":9,"type":"DIRT"},{"x":13,"y":9,"type":"DIRT"},{"x":14,"y":9,"type":"DIRT"},{"x":15,"y":9,"type":"AIR"},{"x":16,"y":9,"type":"DIRT"},{"x":17,"y":9,"type":"AIR"},{"x":18,"y":9,"type":"DIRT"},{"x":19,"y":9,"type":"DIRT"},{"x":20,"y":9,"type":"DIRT"},{"x":21,"y":9,"type":"DIRT"},{"x":22,"y":9,"type":"AIR"},{"x":23,"y":9,"type":"DIRT"},{"x":24,"y":9,"type":"DIRT"},{"x":25,"y":9,"type":"AIR"},{"x":26,"y":9,"type":"DIRT"},{"x":27,"y":9,"type":"AIR"},{"x":28,"y":9,"type":"AIR"},{"x":29,"y":9,"type":"AIR"},{"x":30,"y":9,"type":"DIRT"},{"x":31,"y":9,"type":"DIRT"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"AIR"},{"x":2,"y":10,"type":"AIR"},{"x":3,"y":10,"type":"DIRT"},{"x":4,"y":10,"type":"DIRT"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"DIRT"},{"x":7,"y":10,"type":"AIR"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"DIRT"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"DIRT"},{"x":12,"y":10,"type":"DIRT"},{"x":13,"y":10,"type":"DIRT"},{"x":14,"y":10,"type":"DIRT"},{"x":15,"y":10,"type":"DIRT"},{"x":16,"y":10,"type":"DIRT"},{"x":17,"y":10,"type":"DIRT"},{"x":18,"y":10,"type":"DIRT"},{"x":19,"y":10,"type":"DIRT"},{"x":20,"y":10,"type":"DIRT"},{"x":21,"y":10,"type":"DIRT"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"DIRT"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"AIR"},{"x":26,"y":10,"type":"DIRT"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"DIRT"},{"x":29,"y":10,"type":"DIRT"},{"x":30,"y":10,"type":"AIR"},{"x":31,"y":10,"type":"AIR"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"AIR"},{"x":1,"y":11,"type":"AIR"},{"x":2,"y":11,"type":"DIRT"},{"x":3,"y":11,"type":"DIRT"},{"x":4,"y":11,"type":"DIRT"},{"x":5,"y":11,"type":"AIR"},{"x":6,"y":11,"type":"DIRT"},{"x":7,"y":11,"type":"AIR"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"AIR"},{"x":10,"y":11,"type":"DIRT"},{"x":11,"y":11,"type":"DIRT"},{"x":12,"y":11,"type":"DIRT"},{"x":13,"y":11,"type":"DIRT"},{"x":14,"y":11,"type":"AIR"},{"x":15,"y":11,"type":"AIR"},{"x":16,"y":11,"type":"AIR"},{"x":17,"y":11,"type":"AIR"},{"x":18,"y":11,"type":"AIR"},{"x":19,"y":11,"type":"DIRT"},{"x":20,"y":11,"type":"DIRT"},{"x":21,"y":11,"type":"DIRT"},{"x":22,"y":11,"type":"DIRT"},{"x":23,"y":11,"type":"AIR"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"AIR"},{"x":26,"y":11,"type":"DIRT"},{"x":27,"y":11,"type":"AIR"},{"x":28,"y":11,"type":"DIRT"},{"x":29,"y":11,"type":"DIRT"},{"x":30,"y":11,"type":"DIRT"},{"x":31,"y":11,"type":"AIR"},{"x":32,"y":11,"type":"AIR"}],[{"x":0,"y":12,"type":"AIR"},{"x":1,"y":12,"type":"AIR"},{"x":2,"y":12,"type":"DIRT"},{"x":3,"y":12,"type":"AIR"},{"x":4,"y":12,"type":"AIR"},{"x":5,"y":12,"type":"DIRT"},{"x":6,"y":12,"type":"DIRT"},{"x":7,"y":12,"type":"AIR"},{"x":8,"y":12,"type":"AIR"},{"x":9,"y":12,"type":"AIR"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"DIRT"},{"x":12,"y":12,"type":"DIRT"},{"x":13,"y":12,"type":"AIR"},{"x":14,"y":12,"type":"AIR"},{"x":15,"y":12,"type":"DIRT"},{"x":16,"y":12,"type":"DIRT"},{"x":17,"y":12,"type":"DIRT"},{"x":18,"y":12,"type":"AIR"},{"x":19,"y":12,"type":"AIR"},{"x":20,"y":12,"type":"DIRT"},{"x":21,"y":12,"type":"DIRT"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"AIR"},{"x":24,"y":12,"type":"AIR"},{"x":25,"y":12,"type":"AIR"},{"x":26,"y":12,"type":"DIRT"},{"x":27,"y":12,"type":"DIRT"},{"x":28,"y":12,"type":"AIR"},{"x":29,"y":12,"type":"AIR"},{"x":30,"y":12,"type":"DIRT"},{"x":31,"y":12,"type":"AIR"},{"x":32,"y":12,"type":"AIR"}],[{"x":0,"y":13,"type":"AIR"},{"x":1,"y":13,"type":"AIR"},{"x":2,"y":13,"type":"DIRT"},{"x":3,"y":13,"type":"DIRT"},{"x":4,"y":13,"type":"AIR"},{"x":5,"y":13,"type":"DIRT"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"AIR"},{"x":8,"y":13,"type":"DIRT"},{"x":9,"y":13,"type":"DIRT"},{"x":10,"y":13,"type":"DIRT"},{"x":11,"y":13,"type":"DIRT"},{"x":12,"y":13,"type":"AIR"},{"x":13,"y":13,"type":"AIR"},{"x":14,"y":13,"type":"DIRT"},{"x":15,"y":13,"type":"DIRT"},{"x":16,"y":13,"type":"DIRT"},{"x":17,"y":13,"type":"DIRT"},{"x":18,"y":13,"type":"DIRT"},{"x":19,"y":13,"type":"AIR"},{"x":20,"y":13,"type":"AIR"},{"x":21,"y":13,"type":"DIRT"},{"x":22,"y":13,"type":"DIRT"},{"x":23,"y":13,"type":"DIRT"},{"x":24,"y":13,"type":"DIRT"},{"x":25,"y":13,"type":"AIR"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"DIRT"},{"x":28,"y":13,"type":"AIR"},{"x":29,"y":13,"type":"DIRT"},{"x":30,"y":13,"type":"DIRT"},{"x":31,"y":13,"type":"AIR"},{"x":32,"y":13,"type":"AIR"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"AIR"},{"x":5,"y":14,"type":"AIR"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"AIR"},{"x":8,"y":14,"type":"AIR"},{"x":9,"y":14,"type":"AIR"},{"x":10,"y":14,"type":"AIR"},{"x":11,"y":14,"type":"DIRT"},{"x":12,"y":14,"type":"AIR"},{"x":13,"y":14,"type":"AIR"},{"x":14,"y":14,"type":"DIRT"},{"x":15,"y":14,"type":"DIRT"},{"x":16,"y":14,"type":"AIR"},{"x":17,"y":14,"type":"DIRT"},{"x":18,"y":14,"type":"DIRT"},{"x":19,"y":14,"type":"AIR"},{"x":20,"y":14,"type":"AIR"},{"x":21,"y":14,"type":"DIRT"},{"x":22,"y":14,"type":"AIR"},{"x":23,"y":14,"type":"AIR"},{"x":24,"y":14,"type":"AIR"},{"x":25,"y":14,"type":"AIR"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"AIR"},{"x":28,"y":14,"type":"AIR"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"AIR"},{"x":5,"y":15,"type":"DIRT"},{"x":6,"y":15,"type":"DIRT"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"AIR"},{"x":9,"y":15,"type":"AIR"},{"x":10,"y":15,"type":"AIR"},{"x":11,"y":15,"type":"AIR"},{"x":12,"y":15,"type":"AIR"},{"x":13,"y":15,"type":"AIR"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"AIR"},{"x":16,"y":15,"type":"AIR"},{"x":17,"y":15,"type":"AIR"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"AIR"},{"x":20,"y":15,"type":"AIR"},{"x":21,"y":15,"type":"AIR"},{"x":22,"y":15,"type":"AIR"},{"x":23,"y":15,"type":"AIR"},{"x":24,"y":15,"type":"AIR"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"DIRT"},{"x":27,"y":15,"type":"DIRT"},{"x":28,"y":15,"type":"AIR"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"AIR"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"DIRT"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"DIRT"},{"x":10,"y":16,"type":"AIR"},{"x":11,"y":16,"type":"AIR"},{"x":12,"y":16,"type":"AIR"},{"x":13,"y":16,"type":"AIR"},{"x":14,"y":16,"type":"AIR"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"AIR"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"AIR"},{"x":19,"y":16,"type":"AIR"},{"x":20,"y":16,"type":"AIR"},{"x":21,"y":16,"type":"AIR"},{"x":22,"y":16,"type":"AIR"},{"x":23,"y":16,"type":"DIRT"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"DIRT"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"AIR"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"AIR"},{"x":5,"y":17,"type":"AIR"},{"x":6,"y":17,"type":"AIR"},{"x":7,"y":17,"type":"AIR"},{"x":8,"y":17,"type":"DIRT"},{"x":9,"y":17,"type":"DIRT"},{"x":10,"y":17,"type":"DIRT"},{"x":11,"y":17,"type":"AIR"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"DIRT"},{"x":14,"y":17,"type":"DIRT"},{"x":15,"y":17,"type":"DIRT"},{"x":16,"y":17,"type":"AIR"},{"x":17,"y":17,"type":"DIRT"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"DIRT"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"AIR"},{"x":22,"y":17,"type":"DIRT"},{"x":23,"y":17,"type":"DIRT"},{"x":24,"y":17,"type":"DIRT"},{"x":25,"y":17,"type":"AIR"},{"x":26,"y":17,"type":"AIR"},{"x":27,"y":17,"type":"AIR"},{"x":28,"y":17,"type":"AIR"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"AIR"},{"x":5,"y":18,"type":"AIR"},{"x":6,"y":18,"type":"AIR"},{"x":7,"y":18,"type":"AIR"},{"x":8,"y":18,"type":"DIRT"},{"x":9,"y":18,"type":"DIRT"},{"x":10,"y":18,"type":"DIRT"},{"x":11,"y":18,"type":"AIR"},{"x":12,"y":18,"type":"AIR"},{"x":13,"y":18,"type":"AIR"},{"x":14,"y":18,"type":"DIRT"},{"x":15,"y":18,"type":"AIR"},{"x":16,"y":18,"type":"AIR"},{"x":17,"y":18,"type":"AIR"},{"x":18,"y":18,"type":"DIRT"},{"x":19,"y":18,"type":"AIR"},{"x":20,"y":18,"type":"AIR"},{"x":21,"y":18,"type":"AIR"},{"x":22,"y":18,"type":"DIRT"},{"x":23,"y":18,"type":"DIRT"},{"x":24,"y":18,"type":"DIRT"},{"x":25,"y":18,"type":"AIR"},{"x":26,"y":18,"type":"AIR"},{"x":27,"y":18,"type":"AIR"},{"x":28,"y":18,"type":"AIR"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"DIRT"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"AIR"},{"x":3,"y":19,"type":"AIR"},{"x":4,"y":19,"type":"AIR"},{"x":5,"y":19,"type":"AIR"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"AIR"},{"x":8,"y":19,"type":"AIR"},{"x":9,"y":19,"type":"DIRT"},{"x":10,"y":19,"type":"DIRT"},{"x":11,"y":19,"type":"DIRT"},{"x":12,"y":19,"type":"AIR"},{"x":13,"y":19,"type":"AIR"},{"x":14,"y":19,"type":"DIRT"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"DIRT"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"DIRT"},{"x":19,"y":19,"type":"AIR"},{"x":20,"y":19,"type":"AIR"},{"x":21,"y":19,"type":"DIRT"},{"x":22,"y":19,"type":"DIRT"},{"x":23,"y":19,"type":"DIRT"},{"x":24,"y":19,"type":"AIR"},{"x":25,"y":19,"type":"AIR"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"AIR"},{"x":28,"y":19,"type":"AIR"},{"x":29,"y":19,"type":"AIR"},{"x":30,"y":19,"type":"AIR"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"DIRT"}],[{"x":0,"y":20,"type":"DIRT"},{"x":1,"y":20,"type":"DIRT"},{"x":2,"y":20,"type":"DIRT"},{"x":3,"y":20,"type":"AIR"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"AIR"},{"x":6,"y":20,"type":"AIR"},{"x":7,"y":20,"type":"AIR"},{"x":8,"y":20,"type":"AIR"},{"x":9,"y":20,"type":"DIRT"},{"x":10,"y":20,"type":"DIRT"},{"x":11,"y":20,"type":"DIRT"},{"x":12,"y":20,"type":"AIR"},{"x":13,"y":20,"type":"DIRT"},{"x":14,"y":20,"type":"DIRT"},{"x":15,"y":20,"type":"DIRT"},{"x":16,"y":20,"type":"DIRT"},{"x":17,"y":20,"type":"DIRT"},{"x":18,"y":20,"type":"DIRT"},{"x":19,"y":20,"type":"DIRT"},{"x":20,"y":20,"type":"AIR"},{"x":21,"y":20,"type":"DIRT"},{"x":22,"y":20,"type":"DIRT"},{"x":23,"y":20,"type":"DIRT"},{"x":24,"y":20,"type":"AIR"},{"x":25,"y":20,"type":"AIR"},{"x":26,"y":20,"type":"AIR"},{"x":27,"y":20,"type":"AIR"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"AIR"},{"x":30,"y":20,"type":"DIRT"},{"x":31,"y":20,"type":"DIRT"},{"x":32,"y":20,"type":"DIRT"}],[{"x":0,"y":21,"type":"DIRT"},{"x":1,"y":21,"type":"DIRT"},{"x":2,"y":21,"type":"DIRT"},{"x":3,"y":21,"type":"AIR"},{"x":4,"y":21,"type":"AIR"},{"x":5,"y":21,"type":"DIRT"},{"x":6,"y":21,"type":"DIRT"},{"x":7,"y":21,"type":"DIRT"},{"x":8,"y":21,"type":"AIR"},{"x":9,"y":21,"type":"AIR"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"DIRT"},{"x":12,"y":21,"type":"DIRT"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"DIRT"},{"x":15,"y":21,"type":"DIRT"},{"x":16,"y":21,"type":"AIR"},{"x":17,"y":21,"type":"DIRT"},{"x":18,"y":21,"type":"DIRT"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"DIRT"},{"x":21,"y":21,"type":"DIRT"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"AIR"},{"x":24,"y":21,"type":"AIR"},{"x":25,"y":21,"type":"DIRT"},{"x":26,"y":21,"type":"DIRT"},{"x":27,"y":21,"type":"DIRT"},{"x":28,"y":21,"type":"AIR"},{"x":29,"y":21,"type":"AIR"},{"x":30,"y":21,"type":"DIRT"},{"x":31,"y":21,"type":"DIRT"},{"x":32,"y":21,"type":"DIRT"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"DIRT"},{"x":2,"y":22,"type":"DIRT"},{"x":3,"y":22,"type":"AIR"},{"x":4,"y":22,"type":"DIRT"},{"x":5,"y":22,"type":"AIR"},{"x":6,"y":22,"type":"DIRT"},{"x":7,"y":22,"type":"DIRT"},{"x":8,"y":22,"type":"AIR"},{"x":9,"y":22,"type":"AIR"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"DIRT"},{"x":13,"y":22,"type":"AIR"},{"x":14,"y":22,"type":"AIR"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"AIR"},{"x":19,"y":22,"type":"AIR"},{"x":20,"y":22,"type":"DIRT"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"AIR"},{"x":24,"y":22,"type":"AIR"},{"x":25,"y":22,"type":"DIRT"},{"x":26,"y":22,"type":"DIRT"},{"x":27,"y":22,"type":"AIR"},{"x":28,"y":22,"type":"DIRT"},{"x":29,"y":22,"type":"AIR"},{"x":30,"y":22,"type":"DIRT"},{"x":31,"y":22,"type":"DIRT"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"AIR"},{"x":2,"y":23,"type":"AIR"},{"x":3,"y":23,"type":"AIR"},{"x":4,"y":23,"type":"DIRT"},{"x":5,"y":23,"type":"AIR"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"DIRT"},{"x":8,"y":23,"type":"AIR"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"DIRT"},{"x":13,"y":23,"type":"AIR"},{"x":14,"y":23,"type":"AIR"},{"x":15,"y":23,"type":"AIR"},{"x":16,"y":23,"type":"AIR"},{"x":17,"y":23,"type":"AIR"},{"x":18,"y":23,"type":"AIR"},{"x":19,"y":23,"type":"AIR"},{"x":20,"y":23,"type":"DIRT"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"AIR"},{"x":25,"y":23,"type":"DIRT"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"AIR"},{"x":28,"y":23,"type":"DIRT"},{"x":29,"y":23,"type":"AIR"},{"x":30,"y":23,"type":"AIR"},{"x":31,"y":23,"type":"AIR"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"AIR"},{"x":2,"y":24,"type":"AIR"},{"x":3,"y":24,"type":"AIR"},{"x":4,"y":24,"type":"DIRT"},{"x":5,"y":24,"type":"AIR"},{"x":6,"y":24,"type":"AIR"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"AIR"},{"x":9,"y":24,"type":"DIRT"},{"x":10,"y":24,"type":"DIRT"},{"x":11,"y":24,"type":"AIR"},{"x":12,"y":24,"type":"DIRT"},{"x":13,"y":24,"type":"DIRT"},{"x":14,"y":24,"type":"DIRT"},{"x":15,"y":24,"type":"AIR"},{"x":16,"y":24,"type":"AIR"},{"x":17,"y":24,"type":"AIR"},{"x":18,"y":24,"type":"DIRT"},{"x":19,"y":24,"type":"DIRT"},{"x":20,"y":24,"type":"DIRT"},{"x":21,"y":24,"type":"AIR"},{"x":22,"y":24,"type":"DIRT"},{"x":23,"y":24,"type":"DIRT"},{"x":24,"y":24,"type":"AIR"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"AIR"},{"x":27,"y":24,"type":"AIR"},{"x":28,"y":24,"type":"DIRT"},{"x":29,"y":24,"type":"AIR"},{"x":30,"y":24,"type":"AIR"},{"x":31,"y":24,"type":"AIR"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"AIR"},{"x":3,"y":25,"type":"AIR"},{"x":4,"y":25,"type":"AIR"},{"x":5,"y":25,"type":"AIR"},{"x":6,"y":25,"type":"AIR"},{"x":7,"y":25,"type":"AIR"},{"x":8,"y":25,"type":"DIRT"},{"x":9,"y":25,"type":"DIRT"},{"x":10,"y":25,"type":"AIR"},{"x":11,"y":25,"type":"AIR"},{"x":12,"y":25,"type":"AIR"},{"x":13,"y":25,"type":"AIR"},{"x":14,"y":25,"type":"AIR"},{"x":15,"y":25,"type":"AIR"},{"x":16,"y":25,"type":"AIR"},{"x":17,"y":25,"type":"AIR"},{"x":18,"y":25,"type":"AIR"},{"x":19,"y":25,"type":"AIR"},{"x":20,"y":25,"type":"AIR"},{"x":21,"y":25,"type":"AIR"},{"x":22,"y":25,"type":"AIR"},{"x":23,"y":25,"type":"DIRT"},{"x":24,"y":25,"type":"DIRT"},{"x":25,"y":25,"type":"AIR"},{"x":26,"y":25,"type":"AIR"},{"x":27,"y":25,"type":"AIR"},{"x":28,"y":25,"type":"AIR"},{"x":29,"y":25,"type":"AIR"},{"x":30,"y":25,"type":"AIR"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"AIR"},{"x":5,"y":26,"type":"DIRT"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"DIRT"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"AIR"},{"x":14,"y":26,"type":"AIR"},{"x":15,"y":26,"type":"AIR"},{"x":16,"y":26,"type":"AIR"},{"x":17,"y":26,"type":"AIR"},{"x":18,"y":26,"type":"AIR"},{"x":19,"y":26,"type":"AIR"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"DIRT"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"DIRT"},{"x":28,"y":26,"type":"AIR"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"AIR"},{"x":5,"y":27,"type":"AIR"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"AIR"},{"x":14,"y":27,"type":"AIR"},{"x":15,"y":27,"type":"AIR"},{"x":16,"y":27,"type":"AIR"},{"x":17,"y":27,"type":"AIR"},{"x":18,"y":27,"type":"AIR"},{"x":19,"y":27,"type":"AIR"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"AIR"},{"x":28,"y":27,"type":"AIR"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"AIR"},{"x":5,"y":28,"type":"AIR"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"DIRT"},{"x":12,"y":28,"type":"DIRT"},{"x":13,"y":28,"type":"DIRT"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"AIR"},{"x":16,"y":28,"type":"AIR"},{"x":17,"y":28,"type":"AIR"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"DIRT"},{"x":20,"y":28,"type":"DIRT"},{"x":21,"y":28,"type":"DIRT"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"AIR"},{"x":28,"y":28,"type":"AIR"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"AIR"},{"x":12,"y":29,"type":"AIR"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"DIRT"},{"x":15,"y":29,"type":"AIR"},{"x":16,"y":29,"type":"AIR"},{"x":17,"y":29,"type":"AIR"},{"x":18,"y":29,"type":"DIRT"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"AIR"},{"x":21,"y":29,"type":"AIR"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"AIR"},{"x":12,"y":30,"type":"AIR"},{"x":13,"y":30,"type":"AIR"},{"x":14,"y":30,"type":"DIRT"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"DIRT"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"DIRT"},{"x":19,"y":30,"type":"AIR"},{"x":20,"y":30,"type":"AIR"},{"x":21,"y":30,"type":"AIR"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"AIR"},{"x":9,"y":31,"type":"AIR"},{"x":10,"y":31,"type":"AIR"},{"x":11,"y":31,"type":"DIRT"},{"x":12,"y":31,"type":"DIRT"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"AIR"},{"x":16,"y":31,"type":"DIRT"},{"x":17,"y":31,"type":"AIR"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"DIRT"},{"x":21,"y":31,"type":"DIRT"},{"x":22,"y":31,"type":"AIR"},{"x":23,"y":31,"type":"AIR"},{"x":24,"y":31,"type":"AIR"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"DIRT"},{"x":12,"y":32,"type":"DIRT"},{"x":13,"y":32,"type":"DIRT"},{"x":14,"y":32,"type":"AIR"},{"x":15,"y":32,"type":"AIR"},{"x":16,"y":32,"type":"AIR"},{"x":17,"y":32,"type":"AIR"},{"x":18,"y":32,"type":"AIR"},{"x":19,"y":32,"type":"DIRT"},{"x":20,"y":32,"type":"DIRT"},{"x":21,"y":32,"type":"DIRT"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/import-replay.sh b/2019-worms/tests/import-replay.sh
new file mode 100755
index 0000000..216c235
--- /dev/null
+++ b/2019-worms/tests/import-replay.sh
@@ -0,0 +1,12 @@
+#/bin/sh
+
+# $1: The match-log directory
+# Should be run from the tests directory.
+
+logname=$(basename $1)
+
+mkdir replays/$logname
+cp $1/A*.csv replays/$logname/A-log.csv
+cp $1/B*.csv replays/$logname/B-log.csv
+cp $1/Round\ 001/A*/JsonMap.json replays/$logname/A-init.json
+cp $1/Round\ 001/B*/JsonMap.json replays/$logname/B-init.json
diff --git a/2019-worms/tests/official-runner-matching.rs b/2019-worms/tests/official-runner-matching.rs
new file mode 100644
index 0000000..1b62088
--- /dev/null
+++ b/2019-worms/tests/official-runner-matching.rs
@@ -0,0 +1,238 @@
+use steam_powered_wyrm::command::{Action, Command};
+use steam_powered_wyrm::constants::*;
+use steam_powered_wyrm::game::*;
+use steam_powered_wyrm::geometry::*;
+use steam_powered_wyrm::json;
+
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::Path;
+
+#[test]
+fn simulates_the_same_match() {
+ let replays = Path::new("tests/replays/");
+ for replay in replays.read_dir().expect("read_dir failed") {
+ let replay = replay.expect("error on replay").path();
+
+ let mut game_board = GameBoard::new(
+ json::read_state_from_json_file(&replay.join(Path::new("A-init.json")))
+ .expect("Failed to read initial state"),
+ );
+ let player_csv = read_file_lines(&replay.join(Path::new("A-log.csv")), 1);
+ let opponent_csv = read_file_lines(&replay.join(Path::new("B-log.csv")), 1);
+
+ for round in 0..player_csv.len() {
+ println!("Testing round {}", round);
+
+ let player = split_csv(&player_csv[round]);
+ let opponent = split_csv(&opponent_csv[round]);
+
+ assert_eq!(
+ round + 1,
+ player[0]
+ .parse::<usize>()
+ .expect(&format!("Invalid player input on round {}", round))
+ );
+ assert_eq!(
+ round + 1,
+ opponent[0]
+ .parse::<usize>()
+ .expect(&format!("Invalid opponent input on round {}", round))
+ );
+
+ if round != 0 {
+ let player_move = read_move(
+ &player,
+ game_board.players[0].worms[game_board.players[0].active_worm].id,
+ );
+ let opponent_move = read_move(
+ &opponent,
+ game_board.players[1].worms[game_board.players[1].active_worm].id,
+ );
+ let _ = game_board.simulate([player_move, opponent_move]);
+ if player[1] == "invalid" {
+ game_board.players[0].moves_score -= INVALID_COMMAND_SCORE_PENALTY;
+ }
+ if opponent[1] == "invalid" {
+ game_board.players[1].moves_score -= INVALID_COMMAND_SCORE_PENALTY;
+ }
+ }
+
+ for player_index in 0..2 {
+ let csv_row = match player_index {
+ 0 => &player,
+ _ => &opponent,
+ };
+ assert_eq!(
+ csv_row[4].parse::<i32>().unwrap(),
+ game_board.players[player_index].score(),
+ "Score is incorrect for player {}, Row: {:?}",
+ player_index,
+ csv_row
+ );
+ for worm_index in 0..3 {
+ let worm_id = worm_index as i32 + 1;
+
+ match game_board.players[player_index].find_worm(worm_id) {
+ Some(worm) => {
+ assert_eq!(
+ csv_row[6 + worm_index * 3].parse::<i32>().unwrap(),
+ worm.health,
+ "Worm health is incorrect for worm {} on player {}, Row: {:?}",
+ worm_id,
+ player_index,
+ csv_row
+ );
+ assert_eq!(
+ csv_row[7 + worm_index * 3].parse::<i8>().unwrap(),
+ worm.position.x,
+ "Worm x is incorrect for worm {} on player {}, Row: {:?}",
+ worm_id,
+ player_index,
+ csv_row
+ );
+ assert_eq!(
+ csv_row[8 + worm_index * 3].parse::<i8>().unwrap(),
+ worm.position.y,
+ "Worm y is incorrect for worm {} on player {}, Row: {:?}",
+ worm_id,
+ player_index,
+ csv_row
+ );
+ }
+ None => {
+ // If the worms don't appear in my state, they should be dead
+ assert!(
+ csv_row[6 + worm_index * 3].parse::<i32>().unwrap() <= 0,
+ "Worm is not actually dead"
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+fn read_file_lines(path: &Path, skip: usize) -> Vec<String> {
+ let mut file = File::open(path).unwrap();
+ let mut contents = String::new();
+ file.read_to_string(&mut contents).unwrap();
+ contents
+ .split("\n")
+ .skip(skip)
+ .map(String::from)
+ .filter(|s| !s.is_empty())
+ .collect()
+}
+
+fn split_csv(input: &str) -> Vec<String> {
+ let mut result = Vec::new();
+ let mut next = Vec::new();
+ let mut quoted = false;
+
+ for c in input.chars() {
+ match c {
+ '"' => {
+ quoted = !quoted;
+ }
+ ',' if !quoted => {
+ result.push(next.iter().collect());
+ next = Vec::new();
+ }
+ c => {
+ next.push(c);
+ }
+ }
+ }
+ result.push(next.iter().collect());
+ result
+}
+
+fn read_move(csv_line: &[String], expected_worm_id: i32) -> Command {
+ let worm_id = csv_line[3].parse::<i32>().unwrap();
+ let select = if worm_id == expected_worm_id {
+ None
+ } else {
+ Some(worm_id)
+ };
+ match csv_line[1].as_ref() {
+ "move" => {
+ let (x, y) = read_xy_pair(&csv_line[2]);
+ Command {
+ worm: select,
+ action: Action::Move(Point2d::new(x, y)),
+ }
+ }
+ "dig" => {
+ let (x, y) = read_xy_pair(&csv_line[2]);
+ Command {
+ worm: select,
+ action: Action::Dig(Point2d::new(x, y)),
+ }
+ }
+ "banana" => {
+ let (x, y) = read_xy_pair(&csv_line[2]);
+ Command {
+ worm: select,
+ action: Action::Bomb(Point2d::new(x, y)),
+ }
+ }
+ "snowball" => {
+ let (x, y) = read_xy_pair(&csv_line[2]);
+ Command {
+ worm: select,
+ action: Action::Snowball(Point2d::new(x, y)),
+ }
+ }
+ "nothing" | "invalid" => Command {
+ worm: select,
+ action: Action::DoNothing,
+ },
+ "shoot" => {
+ use steam_powered_wyrm::geometry::Direction::*;
+
+ let dir = match csv_line[2].as_ref() {
+ "shoot N" => North,
+ "shoot NE" => NorthEast,
+ "shoot E" => East,
+ "shoot SE" => SouthEast,
+ "shoot S" => South,
+ "shoot SW" => SouthWest,
+ "shoot W" => West,
+ "shoot NW" => NorthWest,
+ _ => panic!("Unknown shoot direction: {}", csv_line[2]),
+ };
+ Command {
+ worm: select,
+ action: Action::Shoot(dir),
+ }
+ }
+ x => {
+ panic!("Unknown command {}", x);
+ }
+ }
+}
+
+fn read_xy_pair(input: &str) -> (i8, i8) {
+ let mut char_iter = input.chars();
+ let _ = char_iter
+ .by_ref()
+ .take_while(|c| *c != ' ')
+ .collect::<String>();
+ let x = char_iter
+ .by_ref()
+ .take_while(|c| *c != ' ')
+ .collect::<String>()
+ .trim()
+ .parse::<i8>()
+ .unwrap();
+ let y = char_iter
+ .by_ref()
+ .take_while(|c| *c != ' ')
+ .collect::<String>()
+ .trim()
+ .parse::<i8>()
+ .unwrap();
+ (x, y)
+}
diff --git a/2019-worms/tests/replays/2019.08.19.21.15.02/A-init.json b/2019-worms/tests/replays/2019.08.19.21.15.02/A-init.json
new file mode 100644
index 0000000..ea2e948
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.15.02/A-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":1,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":2,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"AIR"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"DIRT"},{"x":15,"y":0,"type":"DIRT"},{"x":16,"y":0,"type":"AIR"},{"x":17,"y":0,"type":"DIRT"},{"x":18,"y":0,"type":"DIRT"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"AIR"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"AIR"},{"x":10,"y":1,"type":"AIR"},{"x":11,"y":1,"type":"AIR"},{"x":12,"y":1,"type":"AIR"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"DIRT"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"AIR"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"DIRT"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"AIR"},{"x":21,"y":1,"type":"AIR"},{"x":22,"y":1,"type":"AIR"},{"x":23,"y":1,"type":"AIR"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"AIR"},{"x":14,"y":2,"type":"AIR"},{"x":15,"y":2,"type":"DIRT"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"DIRT"},{"x":18,"y":2,"type":"AIR"},{"x":19,"y":2,"type":"AIR"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"DIRT"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"AIR"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"AIR"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"DIRT"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"DIRT"},{"x":5,"y":4,"type":"DIRT"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"DIRT"},{"x":12,"y":4,"type":"DIRT"},{"x":13,"y":4,"type":"DIRT"},{"x":14,"y":4,"type":"DIRT"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"DIRT"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"DIRT"},{"x":19,"y":4,"type":"DIRT"},{"x":20,"y":4,"type":"DIRT"},{"x":21,"y":4,"type":"DIRT"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"DIRT"},{"x":28,"y":4,"type":"DIRT"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"AIR"},{"x":5,"y":5,"type":"AIR"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"DIRT"},{"x":13,"y":5,"type":"DIRT"},{"x":14,"y":5,"type":"DIRT"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"DIRT"},{"x":19,"y":5,"type":"DIRT"},{"x":20,"y":5,"type":"DIRT"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"AIR"},{"x":28,"y":5,"type":"AIR"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"DIRT"},{"x":4,"y":6,"type":"DIRT"},{"x":5,"y":6,"type":"AIR"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"DIRT"},{"x":12,"y":6,"type":"DIRT"},{"x":13,"y":6,"type":"DIRT"},{"x":14,"y":6,"type":"DIRT"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"DIRT"},{"x":19,"y":6,"type":"DIRT"},{"x":20,"y":6,"type":"DIRT"},{"x":21,"y":6,"type":"DIRT"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"AIR"},{"x":28,"y":6,"type":"DIRT"},{"x":29,"y":6,"type":"DIRT"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"AIR"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"DIRT"},{"x":5,"y":7,"type":"AIR"},{"x":6,"y":7,"type":"AIR"},{"x":7,"y":7,"type":"DIRT"},{"x":8,"y":7,"type":"DIRT"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"DIRT"},{"x":12,"y":7,"type":"DIRT"},{"x":13,"y":7,"type":"AIR"},{"x":14,"y":7,"type":"AIR"},{"x":15,"y":7,"type":"AIR"},{"x":16,"y":7,"type":"DIRT"},{"x":17,"y":7,"type":"AIR"},{"x":18,"y":7,"type":"AIR"},{"x":19,"y":7,"type":"AIR"},{"x":20,"y":7,"type":"DIRT"},{"x":21,"y":7,"type":"DIRT"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"DIRT"},{"x":25,"y":7,"type":"DIRT"},{"x":26,"y":7,"type":"AIR"},{"x":27,"y":7,"type":"AIR"},{"x":28,"y":7,"type":"DIRT"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"AIR"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"AIR"},{"x":2,"y":8,"type":"AIR"},{"x":3,"y":8,"type":"AIR"},{"x":4,"y":8,"type":"AIR"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"DIRT"},{"x":7,"y":8,"type":"DIRT"},{"x":8,"y":8,"type":"DIRT"},{"x":9,"y":8,"type":"DIRT"},{"x":10,"y":8,"type":"DIRT"},{"x":11,"y":8,"type":"DIRT"},{"x":12,"y":8,"type":"DIRT"},{"x":13,"y":8,"type":"AIR"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"DIRT"},{"x":16,"y":8,"type":"DIRT"},{"x":17,"y":8,"type":"DIRT"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"AIR"},{"x":20,"y":8,"type":"DIRT"},{"x":21,"y":8,"type":"DIRT"},{"x":22,"y":8,"type":"DIRT"},{"x":23,"y":8,"type":"DIRT"},{"x":24,"y":8,"type":"DIRT"},{"x":25,"y":8,"type":"DIRT"},{"x":26,"y":8,"type":"DIRT"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"AIR"},{"x":29,"y":8,"type":"AIR"},{"x":30,"y":8,"type":"AIR"},{"x":31,"y":8,"type":"AIR"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"AIR"},{"x":2,"y":9,"type":"AIR"},{"x":3,"y":9,"type":"AIR"},{"x":4,"y":9,"type":"DIRT"},{"x":5,"y":9,"type":"DIRT"},{"x":6,"y":9,"type":"DIRT"},{"x":7,"y":9,"type":"DIRT"},{"x":8,"y":9,"type":"DIRT"},{"x":9,"y":9,"type":"DIRT"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"AIR"},{"x":12,"y":9,"type":"DIRT"},{"x":13,"y":9,"type":"AIR"},{"x":14,"y":9,"type":"AIR"},{"x":15,"y":9,"type":"DIRT"},{"x":16,"y":9,"type":"DIRT"},{"x":17,"y":9,"type":"DIRT"},{"x":18,"y":9,"type":"AIR"},{"x":19,"y":9,"type":"AIR"},{"x":20,"y":9,"type":"DIRT"},{"x":21,"y":9,"type":"AIR"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"DIRT"},{"x":24,"y":9,"type":"DIRT"},{"x":25,"y":9,"type":"DIRT"},{"x":26,"y":9,"type":"DIRT"},{"x":27,"y":9,"type":"DIRT"},{"x":28,"y":9,"type":"DIRT"},{"x":29,"y":9,"type":"AIR"},{"x":30,"y":9,"type":"AIR"},{"x":31,"y":9,"type":"AIR"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"AIR"},{"x":2,"y":10,"type":"AIR"},{"x":3,"y":10,"type":"AIR"},{"x":4,"y":10,"type":"AIR"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"DIRT"},{"x":7,"y":10,"type":"DIRT"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"DIRT"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"AIR"},{"x":12,"y":10,"type":"DIRT"},{"x":13,"y":10,"type":"DIRT"},{"x":14,"y":10,"type":"DIRT"},{"x":15,"y":10,"type":"DIRT"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"DIRT"},{"x":18,"y":10,"type":"DIRT"},{"x":19,"y":10,"type":"DIRT"},{"x":20,"y":10,"type":"DIRT"},{"x":21,"y":10,"type":"AIR"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"DIRT"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"DIRT"},{"x":26,"y":10,"type":"DIRT"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"AIR"},{"x":29,"y":10,"type":"AIR"},{"x":30,"y":10,"type":"AIR"},{"x":31,"y":10,"type":"AIR"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"AIR"},{"x":3,"y":11,"type":"AIR"},{"x":4,"y":11,"type":"AIR"},{"x":5,"y":11,"type":"AIR"},{"x":6,"y":11,"type":"DIRT"},{"x":7,"y":11,"type":"DIRT"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"DIRT"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"DIRT"},{"x":13,"y":11,"type":"DIRT"},{"x":14,"y":11,"type":"DIRT"},{"x":15,"y":11,"type":"AIR"},{"x":16,"y":11,"type":"AIR"},{"x":17,"y":11,"type":"AIR"},{"x":18,"y":11,"type":"DIRT"},{"x":19,"y":11,"type":"DIRT"},{"x":20,"y":11,"type":"DIRT"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"DIRT"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"DIRT"},{"x":26,"y":11,"type":"DIRT"},{"x":27,"y":11,"type":"AIR"},{"x":28,"y":11,"type":"AIR"},{"x":29,"y":11,"type":"AIR"},{"x":30,"y":11,"type":"AIR"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"AIR"},{"x":1,"y":12,"type":"AIR"},{"x":2,"y":12,"type":"AIR"},{"x":3,"y":12,"type":"AIR"},{"x":4,"y":12,"type":"AIR"},{"x":5,"y":12,"type":"AIR"},{"x":6,"y":12,"type":"AIR"},{"x":7,"y":12,"type":"DIRT"},{"x":8,"y":12,"type":"DIRT"},{"x":9,"y":12,"type":"DIRT"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"AIR"},{"x":13,"y":12,"type":"DIRT"},{"x":14,"y":12,"type":"DIRT"},{"x":15,"y":12,"type":"AIR"},{"x":16,"y":12,"type":"AIR"},{"x":17,"y":12,"type":"AIR"},{"x":18,"y":12,"type":"DIRT"},{"x":19,"y":12,"type":"DIRT"},{"x":20,"y":12,"type":"AIR"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"DIRT"},{"x":24,"y":12,"type":"DIRT"},{"x":25,"y":12,"type":"DIRT"},{"x":26,"y":12,"type":"AIR"},{"x":27,"y":12,"type":"AIR"},{"x":28,"y":12,"type":"AIR"},{"x":29,"y":12,"type":"AIR"},{"x":30,"y":12,"type":"AIR"},{"x":31,"y":12,"type":"AIR"},{"x":32,"y":12,"type":"AIR"}],[{"x":0,"y":13,"type":"AIR"},{"x":1,"y":13,"type":"AIR"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"AIR"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"DIRT"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"DIRT"},{"x":8,"y":13,"type":"DIRT"},{"x":9,"y":13,"type":"DIRT"},{"x":10,"y":13,"type":"DIRT"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"DIRT"},{"x":13,"y":13,"type":"DIRT"},{"x":14,"y":13,"type":"DIRT"},{"x":15,"y":13,"type":"AIR"},{"x":16,"y":13,"type":"AIR"},{"x":17,"y":13,"type":"AIR"},{"x":18,"y":13,"type":"DIRT"},{"x":19,"y":13,"type":"DIRT"},{"x":20,"y":13,"type":"DIRT"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"DIRT"},{"x":23,"y":13,"type":"DIRT"},{"x":24,"y":13,"type":"DIRT"},{"x":25,"y":13,"type":"DIRT"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"DIRT"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"AIR"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"AIR"},{"x":32,"y":13,"type":"AIR"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"DIRT"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"DIRT"},{"x":8,"y":14,"type":"DIRT"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"AIR"},{"x":11,"y":14,"type":"AIR"},{"x":12,"y":14,"type":"AIR"},{"x":13,"y":14,"type":"DIRT"},{"x":14,"y":14,"type":"AIR"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"AIR"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"AIR"},{"x":19,"y":14,"type":"DIRT"},{"x":20,"y":14,"type":"AIR"},{"x":21,"y":14,"type":"AIR"},{"x":22,"y":14,"type":"AIR"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"DIRT"},{"x":25,"y":14,"type":"DIRT"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"DIRT"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"DIRT"},{"x":5,"y":15,"type":"DIRT"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"AIR"},{"x":9,"y":15,"type":"AIR"},{"x":10,"y":15,"type":"AIR"},{"x":11,"y":15,"type":"AIR"},{"x":12,"y":15,"type":"AIR"},{"x":13,"y":15,"type":"AIR"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"AIR"},{"x":16,"y":15,"type":"AIR"},{"x":17,"y":15,"type":"AIR"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"AIR"},{"x":20,"y":15,"type":"AIR"},{"x":21,"y":15,"type":"AIR"},{"x":22,"y":15,"type":"AIR"},{"x":23,"y":15,"type":"AIR"},{"x":24,"y":15,"type":"AIR"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"DIRT"},{"x":28,"y":15,"type":"DIRT"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"DIRT"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"AIR"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"AIR"},{"x":10,"y":16,"type":"DIRT"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"AIR"},{"x":14,"y":16,"type":"AIR"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"AIR"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"AIR"},{"x":19,"y":16,"type":"AIR"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"DIRT"},{"x":23,"y":16,"type":"AIR"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"AIR"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"DIRT"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"AIR"},{"x":7,"y":17,"type":"AIR"},{"x":8,"y":17,"type":"DIRT"},{"x":9,"y":17,"type":"DIRT"},{"x":10,"y":17,"type":"AIR"},{"x":11,"y":17,"type":"AIR"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"DIRT"},{"x":14,"y":17,"type":"AIR"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"DIRT"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"DIRT"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"AIR"},{"x":22,"y":17,"type":"AIR"},{"x":23,"y":17,"type":"DIRT"},{"x":24,"y":17,"type":"DIRT"},{"x":25,"y":17,"type":"AIR"},{"x":26,"y":17,"type":"AIR"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"AIR"},{"x":6,"y":18,"type":"AIR"},{"x":7,"y":18,"type":"DIRT"},{"x":8,"y":18,"type":"DIRT"},{"x":9,"y":18,"type":"DIRT"},{"x":10,"y":18,"type":"AIR"},{"x":11,"y":18,"type":"AIR"},{"x":12,"y":18,"type":"AIR"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"DIRT"},{"x":15,"y":18,"type":"AIR"},{"x":16,"y":18,"type":"DIRT"},{"x":17,"y":18,"type":"AIR"},{"x":18,"y":18,"type":"DIRT"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"AIR"},{"x":21,"y":18,"type":"AIR"},{"x":22,"y":18,"type":"AIR"},{"x":23,"y":18,"type":"DIRT"},{"x":24,"y":18,"type":"DIRT"},{"x":25,"y":18,"type":"DIRT"},{"x":26,"y":18,"type":"AIR"},{"x":27,"y":18,"type":"AIR"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"AIR"},{"x":3,"y":19,"type":"AIR"},{"x":4,"y":19,"type":"DIRT"},{"x":5,"y":19,"type":"DIRT"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"AIR"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"AIR"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"DIRT"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"AIR"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"DIRT"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"AIR"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"AIR"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"DIRT"},{"x":28,"y":19,"type":"DIRT"},{"x":29,"y":19,"type":"AIR"},{"x":30,"y":19,"type":"AIR"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"DIRT"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"AIR"},{"x":3,"y":20,"type":"AIR"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"DIRT"},{"x":6,"y":20,"type":"DIRT"},{"x":7,"y":20,"type":"AIR"},{"x":8,"y":20,"type":"AIR"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"DIRT"},{"x":11,"y":20,"type":"DIRT"},{"x":12,"y":20,"type":"DIRT"},{"x":13,"y":20,"type":"DIRT"},{"x":14,"y":20,"type":"DIRT"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"AIR"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"DIRT"},{"x":19,"y":20,"type":"DIRT"},{"x":20,"y":20,"type":"DIRT"},{"x":21,"y":20,"type":"DIRT"},{"x":22,"y":20,"type":"DIRT"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"AIR"},{"x":25,"y":20,"type":"AIR"},{"x":26,"y":20,"type":"DIRT"},{"x":27,"y":20,"type":"DIRT"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"AIR"},{"x":30,"y":20,"type":"AIR"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"DIRT"}],[{"x":0,"y":21,"type":"DIRT"},{"x":1,"y":21,"type":"AIR"},{"x":2,"y":21,"type":"AIR"},{"x":3,"y":21,"type":"AIR"},{"x":4,"y":21,"type":"AIR"},{"x":5,"y":21,"type":"DIRT"},{"x":6,"y":21,"type":"DIRT"},{"x":7,"y":21,"type":"DIRT"},{"x":8,"y":21,"type":"AIR"},{"x":9,"y":21,"type":"DIRT"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"DIRT"},{"x":12,"y":21,"type":"DIRT"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"AIR"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"DIRT"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"AIR"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"DIRT"},{"x":21,"y":21,"type":"DIRT"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"DIRT"},{"x":24,"y":21,"type":"AIR"},{"x":25,"y":21,"type":"DIRT"},{"x":26,"y":21,"type":"DIRT"},{"x":27,"y":21,"type":"DIRT"},{"x":28,"y":21,"type":"AIR"},{"x":29,"y":21,"type":"AIR"},{"x":30,"y":21,"type":"AIR"},{"x":31,"y":21,"type":"AIR"},{"x":32,"y":21,"type":"DIRT"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"AIR"},{"x":2,"y":22,"type":"AIR"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"DIRT"},{"x":5,"y":22,"type":"DIRT"},{"x":6,"y":22,"type":"DIRT"},{"x":7,"y":22,"type":"DIRT"},{"x":8,"y":22,"type":"DIRT"},{"x":9,"y":22,"type":"DIRT"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"DIRT"},{"x":13,"y":22,"type":"DIRT"},{"x":14,"y":22,"type":"AIR"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"AIR"},{"x":19,"y":22,"type":"DIRT"},{"x":20,"y":22,"type":"DIRT"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"DIRT"},{"x":24,"y":22,"type":"DIRT"},{"x":25,"y":22,"type":"DIRT"},{"x":26,"y":22,"type":"DIRT"},{"x":27,"y":22,"type":"DIRT"},{"x":28,"y":22,"type":"DIRT"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"AIR"},{"x":31,"y":22,"type":"AIR"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"AIR"},{"x":2,"y":23,"type":"DIRT"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"DIRT"},{"x":5,"y":23,"type":"AIR"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"DIRT"},{"x":8,"y":23,"type":"DIRT"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"DIRT"},{"x":13,"y":23,"type":"AIR"},{"x":14,"y":23,"type":"AIR"},{"x":15,"y":23,"type":"DIRT"},{"x":16,"y":23,"type":"DIRT"},{"x":17,"y":23,"type":"DIRT"},{"x":18,"y":23,"type":"AIR"},{"x":19,"y":23,"type":"AIR"},{"x":20,"y":23,"type":"DIRT"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"DIRT"},{"x":25,"y":23,"type":"DIRT"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"AIR"},{"x":28,"y":23,"type":"DIRT"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"DIRT"},{"x":31,"y":23,"type":"AIR"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"DIRT"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"DIRT"},{"x":5,"y":24,"type":"AIR"},{"x":6,"y":24,"type":"AIR"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"DIRT"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"DIRT"},{"x":11,"y":24,"type":"DIRT"},{"x":12,"y":24,"type":"DIRT"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"DIRT"},{"x":15,"y":24,"type":"DIRT"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"DIRT"},{"x":18,"y":24,"type":"DIRT"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"DIRT"},{"x":21,"y":24,"type":"DIRT"},{"x":22,"y":24,"type":"DIRT"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"DIRT"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"AIR"},{"x":27,"y":24,"type":"AIR"},{"x":28,"y":24,"type":"DIRT"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"DIRT"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"DIRT"},{"x":3,"y":25,"type":"DIRT"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"DIRT"},{"x":6,"y":25,"type":"DIRT"},{"x":7,"y":25,"type":"DIRT"},{"x":8,"y":25,"type":"DIRT"},{"x":9,"y":25,"type":"DIRT"},{"x":10,"y":25,"type":"DIRT"},{"x":11,"y":25,"type":"DIRT"},{"x":12,"y":25,"type":"DIRT"},{"x":13,"y":25,"type":"DIRT"},{"x":14,"y":25,"type":"DIRT"},{"x":15,"y":25,"type":"DIRT"},{"x":16,"y":25,"type":"AIR"},{"x":17,"y":25,"type":"DIRT"},{"x":18,"y":25,"type":"DIRT"},{"x":19,"y":25,"type":"DIRT"},{"x":20,"y":25,"type":"DIRT"},{"x":21,"y":25,"type":"DIRT"},{"x":22,"y":25,"type":"DIRT"},{"x":23,"y":25,"type":"DIRT"},{"x":24,"y":25,"type":"DIRT"},{"x":25,"y":25,"type":"DIRT"},{"x":26,"y":25,"type":"DIRT"},{"x":27,"y":25,"type":"DIRT"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"DIRT"},{"x":30,"y":25,"type":"DIRT"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"DIRT"},{"x":5,"y":26,"type":"DIRT"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"DIRT"},{"x":14,"y":26,"type":"AIR"},{"x":15,"y":26,"type":"AIR"},{"x":16,"y":26,"type":"AIR"},{"x":17,"y":26,"type":"AIR"},{"x":18,"y":26,"type":"AIR"},{"x":19,"y":26,"type":"DIRT"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"DIRT"},{"x":28,"y":26,"type":"DIRT"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"DIRT"},{"x":5,"y":27,"type":"DIRT"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"DIRT"},{"x":14,"y":27,"type":"AIR"},{"x":15,"y":27,"type":"AIR"},{"x":16,"y":27,"type":"DIRT"},{"x":17,"y":27,"type":"AIR"},{"x":18,"y":27,"type":"AIR"},{"x":19,"y":27,"type":"DIRT"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"DIRT"},{"x":28,"y":27,"type":"DIRT"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"DIRT"},{"x":5,"y":28,"type":"DIRT"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"DIRT"},{"x":12,"y":28,"type":"DIRT"},{"x":13,"y":28,"type":"DIRT"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"DIRT"},{"x":16,"y":28,"type":"DIRT"},{"x":17,"y":28,"type":"DIRT"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"DIRT"},{"x":20,"y":28,"type":"DIRT"},{"x":21,"y":28,"type":"DIRT"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"DIRT"},{"x":28,"y":28,"type":"DIRT"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"AIR"},{"x":12,"y":29,"type":"DIRT"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"AIR"},{"x":15,"y":29,"type":"DIRT"},{"x":16,"y":29,"type":"AIR"},{"x":17,"y":29,"type":"DIRT"},{"x":18,"y":29,"type":"AIR"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"DIRT"},{"x":21,"y":29,"type":"AIR"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"AIR"},{"x":12,"y":30,"type":"DIRT"},{"x":13,"y":30,"type":"AIR"},{"x":14,"y":30,"type":"AIR"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"DIRT"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"AIR"},{"x":19,"y":30,"type":"AIR"},{"x":20,"y":30,"type":"DIRT"},{"x":21,"y":30,"type":"AIR"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"AIR"},{"x":9,"y":31,"type":"DIRT"},{"x":10,"y":31,"type":"AIR"},{"x":11,"y":31,"type":"AIR"},{"x":12,"y":31,"type":"AIR"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"DIRT"},{"x":16,"y":31,"type":"DIRT"},{"x":17,"y":31,"type":"DIRT"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"AIR"},{"x":21,"y":31,"type":"AIR"},{"x":22,"y":31,"type":"AIR"},{"x":23,"y":31,"type":"DIRT"},{"x":24,"y":31,"type":"AIR"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"AIR"},{"x":12,"y":32,"type":"AIR"},{"x":13,"y":32,"type":"AIR"},{"x":14,"y":32,"type":"AIR"},{"x":15,"y":32,"type":"DIRT"},{"x":16,"y":32,"type":"AIR"},{"x":17,"y":32,"type":"DIRT"},{"x":18,"y":32,"type":"AIR"},{"x":19,"y":32,"type":"AIR"},{"x":20,"y":32,"type":"AIR"},{"x":21,"y":32,"type":"AIR"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.15.02/A-log.csv b/2019-worms/tests/replays/2019.08.19.21.15.02/A-log.csv
new file mode 100644
index 0000000..373de29
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.15.02/A-log.csv
@@ -0,0 +1,298 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,24,4,100,24,28,100,1,16
+2,move,"move 23 3",1,121,350,150,23,3,100,24,28,100,1,16
+3,move,"move 23 28",2,126,350,150,23,3,100,23,28,100,1,16
+4,move,"move 2 16",3,131,350,150,23,3,100,23,28,100,2,16
+5,dig,"dig 22 3",1,138,350,150,23,3,100,23,28,100,2,16
+6,dig,"dig 22 28",2,145,350,150,23,3,100,23,28,100,2,16
+7,dig,"dig 3 16",3,152,350,150,23,3,100,23,28,100,2,16
+8,move,"move 22 3",1,157,350,150,22,3,100,23,28,100,2,16
+9,move,"move 22 28",2,162,350,150,22,3,100,22,28,100,2,16
+10,move,"move 2 15",3,167,350,150,22,3,100,22,28,100,2,15
+11,move,"move 23 3",1,172,350,150,23,3,100,22,28,100,2,15
+12,move,"move 23 28",2,177,350,150,23,3,100,23,28,100,2,15
+13,move,"move 2 16",3,182,350,150,23,3,100,23,28,100,2,16
+14,move,"move 22 3",1,187,350,150,22,3,100,23,28,100,2,16
+15,move,"move 22 28",2,192,350,150,22,3,100,22,28,100,2,16
+16,move,"move 2 15",3,197,350,150,22,3,100,22,28,100,2,15
+17,move,"move 23 3",1,202,350,150,23,3,100,22,28,100,2,15
+18,move,"move 23 28",2,207,350,150,23,3,100,23,28,100,2,15
+19,move,"move 2 16",3,212,350,150,23,3,100,23,28,100,2,16
+20,move,"move 22 3",1,217,350,150,22,3,100,23,28,100,2,16
+21,move,"move 22 28",2,222,350,150,22,3,100,22,28,100,2,16
+22,move,"move 2 15",3,227,350,150,22,3,100,22,28,100,2,15
+23,move,"move 23 3",1,232,350,150,23,3,100,22,28,100,2,15
+24,move,"move 23 28",2,237,350,150,23,3,100,23,28,100,2,15
+25,move,"move 2 16",3,242,350,150,23,3,100,23,28,100,2,16
+26,move,"move 22 3",1,247,350,150,22,3,100,23,28,100,2,16
+27,move,"move 22 28",2,252,350,150,22,3,100,22,28,100,2,16
+28,move,"move 2 15",3,257,350,150,22,3,100,22,28,100,2,15
+29,move,"move 23 3",1,262,350,150,23,3,100,22,28,100,2,15
+30,move,"move 23 28",2,267,350,150,23,3,100,23,28,100,2,15
+31,move,"move 2 16",3,272,350,150,23,3,100,23,28,100,2,16
+32,move,"move 22 3",1,277,350,150,22,3,100,23,28,100,2,16
+33,move,"move 22 28",2,282,350,150,22,3,100,22,28,100,2,16
+34,move,"move 2 15",3,287,350,150,22,3,100,22,28,100,2,15
+35,move,"move 23 3",1,292,350,150,23,3,100,22,28,100,2,15
+36,move,"move 23 28",2,297,350,150,23,3,100,23,28,100,2,15
+37,move,"move 2 16",3,302,350,150,23,3,100,23,28,100,2,16
+38,move,"move 24 3",1,307,350,150,24,3,100,23,28,100,2,16
+39,move,"move 22 28",2,312,350,150,24,3,100,22,28,100,2,16
+40,move,"move 2 15",3,317,350,150,24,3,100,22,28,100,2,15
+41,move,"move 24 4",1,322,350,150,24,4,100,22,28,100,2,15
+42,dig,"dig 21 27",2,329,350,150,24,4,100,22,28,100,2,15
+43,move,"move 1 16",3,334,350,150,24,4,100,22,28,100,1,16
+44,move,"move 25 3",1,339,350,150,25,3,100,22,28,100,1,16
+45,move,"move 23 28",2,344,350,150,25,3,100,23,28,100,1,16
+46,move,"move 2 16",3,349,350,150,25,3,100,23,28,100,2,16
+47,dig,"dig 25 2",1,356,350,150,25,3,100,23,28,100,2,16
+48,move,"move 22 28",2,361,350,150,25,3,100,22,28,100,2,16
+49,move,"move 2 15",3,366,350,150,25,3,100,22,28,100,2,15
+50,move,"move 24 3",1,371,350,150,24,3,100,22,28,100,2,15
+51,move,"move 23 28",2,376,350,150,24,3,100,23,28,100,2,15
+52,dig,"dig 1 14",3,383,350,150,24,3,100,23,28,100,2,15
+53,dig,"dig 24 2",1,390,350,150,24,3,100,23,28,100,2,15
+54,move,"move 24 27",2,395,350,150,24,3,100,24,27,100,2,15
+55,move,"move 2 16",3,400,350,150,24,3,100,24,27,100,2,16
+56,move,"move 23 3",1,405,350,150,23,3,100,24,27,100,2,16
+57,move,"move 23 28",2,410,350,150,23,3,100,23,28,100,2,16
+58,move,"move 2 15",3,415,350,150,23,3,100,23,28,100,2,15
+59,move,"move 24 2",1,420,350,150,24,2,100,23,28,100,2,15
+60,move,"move 22 28",2,425,350,150,24,2,100,22,28,100,2,15
+61,move,"move 2 16",3,430,350,150,24,2,100,22,28,100,2,16
+62,move,"move 23 3",1,435,350,150,23,3,100,22,28,100,2,16
+63,move,"move 23 28",2,440,350,150,23,3,100,23,28,100,2,16
+64,move,"move 2 15",3,445,350,150,23,3,100,23,28,100,2,15
+65,move,"move 24 2",1,450,350,150,24,2,100,23,28,100,2,15
+66,move,"move 22 28",2,455,350,150,24,2,100,22,28,100,2,15
+67,move,"move 2 16",3,460,350,150,24,2,100,22,28,100,2,16
+68,move,"move 23 3",1,465,350,150,23,3,100,22,28,100,2,16
+69,move,"move 23 28",2,470,350,150,23,3,100,23,28,100,2,16
+70,move,"move 2 15",3,475,350,150,23,3,100,23,28,100,2,15
+71,move,"move 24 2",1,480,350,150,24,2,100,23,28,100,2,15
+72,move,"move 22 28",2,485,350,150,24,2,100,22,28,100,2,15
+73,move,"move 2 16",3,490,350,150,24,2,100,22,28,100,2,16
+74,move,"move 23 3",1,495,350,150,23,3,100,22,28,100,2,16
+75,move,"move 23 28",2,500,350,150,23,3,100,23,28,100,2,16
+76,dig,"dig 3 17",3,507,350,150,23,3,100,23,28,100,2,16
+77,move,"move 24 2",1,512,350,150,24,2,100,23,28,100,2,16
+78,move,"move 22 28",2,517,350,150,24,2,100,22,28,100,2,16
+79,move,"move 2 15",3,522,350,150,24,2,100,22,28,100,2,15
+80,move,"move 25 3",1,527,350,150,25,3,100,22,28,100,2,15
+81,dig,"dig 22 27",2,534,350,150,25,3,100,22,28,100,2,15
+82,dig,"dig 2 14",3,541,350,150,25,3,100,22,28,100,2,15
+83,move,"move 25 2",1,546,350,150,25,2,100,22,28,100,2,15
+84,move,"move 23 28",2,551,350,150,25,2,100,23,28,100,2,15
+85,dig,"dig 3 14",3,558,350,150,25,2,100,23,28,100,2,15
+86,move,"move 25 3",1,563,350,150,25,3,100,23,28,100,2,15
+87,move,"move 24 28",2,568,350,150,25,3,100,24,28,100,2,15
+88,nothing,"nothing "Player chose to do nothing"",3,568,350,150,25,3,100,24,28,100,2,15
+89,move,"move 25 2",1,573,350,150,25,2,100,24,28,100,2,15
+90,move,"move 23 28",2,578,350,150,25,2,100,23,28,100,2,15
+91,move,"move 1 16",3,583,350,150,25,2,100,23,28,100,1,16
+92,move,"move 24 3",1,588,350,150,24,3,100,23,28,100,1,16
+93,move,"move 23 29",2,587,330,130,24,3,100,23,29,100,1,16
+94,move,"move 2 16",3,592,330,130,24,3,100,23,29,100,2,16
+95,move,"move 23 4",1,597,330,130,23,4,100,23,29,100,2,16
+96,move,"move 22 28",2,595,310,110,23,4,100,22,28,100,2,16
+97,move,"move 1 16",3,600,310,110,23,4,100,22,28,100,1,16
+98,move,"move 22 3",1,600,310,110,23,4,100,22,28,100,1,16
+99,shoot,"shoot S",1,593,290,90,23,4,100,22,28,100,1,16
+100,shoot,"shoot S",1,591,282,82,23,4,100,22,28,100,1,16
+101,shoot,"shoot S",1,588,274,74,23,4,100,22,28,100,1,16
+102,shoot,"shoot S",1,601,266,66,23,4,100,22,28,100,1,16
+103,shoot,"shoot S",1,615,258,58,23,4,100,22,28,100,1,16
+104,dig,"dig 21 28",2,619,250,50,23,4,100,22,28,100,1,16
+105,nothing,"nothing "Player chose to do nothing"",3,619,250,50,23,4,100,22,28,100,1,16
+106,move,"move 24 3",1,624,250,50,24,3,100,22,28,100,1,16
+107,nothing,"nothing "Player chose to do nothing"",2,624,250,50,24,3,100,22,28,100,1,16
+108,move,"move 2 17",3,629,250,50,24,3,100,22,28,100,2,17
+109,move,"move 25 4",1,629,250,50,24,3,100,22,28,100,2,17
+110,move,"move 23 28",2,634,250,50,24,3,100,23,28,100,2,17
+111,move,"move 1 17",3,636,242,42,24,3,100,23,28,100,1,17
+112,move,"move 25 4",1,641,242,42,25,4,100,23,28,100,1,17
+113,move,"move 23 27",2,646,242,42,25,4,100,23,27,100,1,17
+114,move,"move 2 16",3,649,234,34,25,4,100,23,27,100,2,16
+115,shoot,"shoot W",1,649,234,34,25,4,100,23,27,100,2,16
+116,move,"move 22 28",2,651,226,26,25,4,100,22,28,100,2,16
+117,move,"move 3 16",3,652,215,15,25,4,100,22,28,100,3,16
+118,shoot,"shoot W",1,667,212,12,25,4,100,22,28,100,3,16
+119,move,"move 23 27",2,669,201,1,25,4,100,23,27,100,3,16
+120,move,"move 2 17",3,673,200,-10,25,4,100,23,27,100,2,17
+121,move,"move 24 27",2,678,200,-10,25,4,100,24,27,100,2,17
+122,move,"move 2 16",3,683,200,-10,25,4,100,24,27,100,2,16
+123,dig,"dig 25 26",2,690,200,-10,25,4,100,24,27,100,2,16
+124,move,"move 3 17",3,695,200,-10,25,4,100,24,27,100,3,17
+125,move,"move 23 28",2,700,200,-10,25,4,100,23,28,100,3,17
+126,move,"move 2 17",3,705,200,-10,25,4,100,23,28,100,2,17
+127,move,"move 22 28",2,710,200,-10,25,4,100,22,28,100,2,17
+128,dig,"dig 3 18",3,717,200,-10,25,4,100,22,28,100,2,17
+129,move,"move 23 28",2,722,200,-10,25,4,100,23,28,100,2,17
+130,move,"move 3 18",3,727,200,-10,25,4,100,23,28,100,3,18
+131,move,"move 22 28",2,732,200,-10,25,4,100,22,28,100,3,18
+132,move,"move 3 19",3,737,200,-10,25,4,100,22,28,100,3,19
+133,move,"move 22 27",2,742,200,-10,25,4,100,22,27,100,3,19
+134,move,"move 4 20",3,747,200,-10,25,4,100,22,27,100,4,20
+135,move,"move 21 26",2,752,200,-10,25,4,100,21,26,100,4,20
+136,move,"move 4 21",3,757,200,-10,25,4,100,21,26,100,4,21
+137,dig,"dig 20 25",2,764,200,-10,25,4,100,21,26,100,4,21
+138,nothing,"nothing "Player chose to do nothing"",3,764,200,-10,25,4,100,21,26,100,4,21
+139,move,"move 22 27",2,769,200,-10,25,4,100,22,27,100,4,21
+140,dig,"dig 5 20",3,776,200,-10,25,4,100,22,27,100,4,21
+141,move,"move 23 27",2,781,200,-10,25,4,100,23,27,100,4,21
+142,move,"move 4 20",3,786,200,-10,25,4,100,23,27,100,4,20
+143,nothing,"nothing "Player chose to do nothing"",2,786,200,-10,25,4,100,23,27,100,4,20
+144,move,"move 5 20",3,791,200,-10,25,4,100,23,27,100,5,20
+145,dig,"dig 23 26",2,798,200,-10,25,4,100,23,27,100,5,20
+146,dig,"dig 6 21",3,805,200,-10,25,4,100,23,27,100,5,20
+147,move,"move 23 26",2,810,200,-10,25,4,100,23,26,100,5,20
+148,dig,"dig 6 20",3,817,200,-10,25,4,100,23,26,100,5,20
+149,dig,"dig 22 25",2,824,200,-10,25,4,100,23,26,100,5,20
+150,dig,"dig 5 21",3,831,200,-10,25,4,100,23,26,100,5,20
+151,dig,"dig 24 25",2,838,200,-10,25,4,100,23,26,100,5,20
+152,move,"move 5 21",3,843,200,-10,25,4,100,23,26,100,5,21
+153,nothing,"nothing "Player chose to do nothing"",2,843,200,-10,25,4,100,23,26,100,5,21
+154,move,"move 5 20",3,848,200,-10,25,4,100,23,26,100,5,20
+155,dig,"dig 24 26",2,855,200,-10,25,4,100,23,26,100,5,20
+156,nothing,"nothing "Player chose to do nothing"",3,855,200,-10,25,4,100,23,26,100,5,20
+157,dig,"dig 22 26",2,862,200,-10,25,4,100,23,26,100,5,20
+158,move,"move 6 19",3,867,200,-10,25,4,100,23,26,100,6,19
+159,move,"move 24 25",2,872,200,-10,25,4,100,24,25,100,6,19
+160,move,"move 5 18",3,877,200,-10,25,4,100,24,25,100,5,18
+161,move,"move 23 24",2,882,200,-10,25,4,100,23,24,100,5,18
+162,dig,"dig 4 19",3,889,200,-10,25,4,100,23,24,100,5,18
+163,dig,"dig 22 24",2,896,200,-10,25,4,100,23,24,100,5,18
+164,dig,"dig 5 19",3,903,200,-10,25,4,100,23,24,100,5,18
+165,banana,"banana 23 19",2,952,200,-10,25,4,100,23,24,100,5,18
+166,move,"move 6 19",3,957,200,-10,25,4,100,23,24,100,6,19
+167,banana,"banana 23 19",2,997,200,-10,25,4,100,23,24,100,6,19
+168,move,"move 7 19",3,1002,200,-10,25,4,100,23,24,100,7,19
+169,banana,"banana 23 19",2,1056,200,-10,25,4,100,23,24,100,7,19
+170,dig,"dig 7 18",3,1063,200,-10,25,4,100,23,24,100,7,19
+171,dig,"dig 23 23",2,1070,200,-10,25,4,100,23,24,100,7,19
+172,move,"move 7 18",3,1075,200,-10,25,4,100,23,24,100,7,18
+173,move,"move 22 24",2,1080,200,-10,25,4,100,22,24,100,7,18
+174,move,"move 7 17",3,1085,200,-10,25,4,100,22,24,100,7,17
+175,dig,"dig 21 25",2,1092,200,-10,25,4,100,22,24,100,7,17
+176,dig,"dig 8 18",3,1099,200,-10,25,4,100,22,24,100,7,17
+177,dig,"dig 21 23",2,1106,200,-10,25,4,100,22,24,100,7,17
+178,dig,"dig 8 17",3,1113,200,-10,25,4,100,22,24,100,7,17
+179,move,"move 21 25",2,1118,200,-10,25,4,100,21,25,100,7,17
+180,nothing,"nothing "Player chose to do nothing"",3,1118,200,-10,25,4,100,21,25,100,7,17
+181,move,"move 20 25",2,1123,200,-10,25,4,100,20,25,100,7,17
+182,nothing,"nothing "Player chose to do nothing"",3,1123,200,-10,25,4,100,20,25,100,7,17
+183,move,"move 19 24",2,1128,200,-10,25,4,100,19,24,100,7,17
+184,move,"move 8 18",3,1133,200,-10,25,4,100,19,24,100,8,18
+185,move,"move 20 25",2,1138,200,-10,25,4,100,20,25,100,8,18
+186,move,"move 8 17",3,1143,200,-10,25,4,100,20,25,100,8,17
+187,move,"move 19 24",2,1148,200,-10,25,4,100,19,24,100,8,17
+188,dig,"dig 9 18",3,1155,200,-10,25,4,100,19,24,100,8,17
+189,shoot,"shoot NE",2,1171,200,-10,25,4,100,19,24,100,8,17
+190,move,"move 8 18",3,1174,192,-10,25,4,92,19,24,100,8,18
+191,shoot,"shoot NE",2,1190,192,-10,25,4,92,19,24,100,8,18
+192,move,"move 9 18",3,1195,192,-10,25,4,92,19,24,100,9,18
+193,shoot,"shoot NE",2,1208,184,-10,25,4,84,19,24,100,9,18
+194,move,"move 9 19",3,1213,184,-10,25,4,84,19,24,100,9,19
+195,move,"move 18 23",2,1218,184,-10,25,4,84,18,23,100,9,19
+196,move,"move 10 19",3,1223,184,-10,25,4,84,18,23,100,10,19
+197,shoot,"shoot NE",2,1236,176,-10,25,4,76,18,23,100,10,19
+198,move,"move 9 20",3,1239,168,-10,25,4,68,18,23,100,9,20
+199,shoot,"shoot E",2,1255,168,-10,25,4,68,18,23,100,9,20
+200,dig,"dig 10 20",3,1259,160,-10,25,4,60,18,23,100,9,20
+201,move,"move 17 22",2,1264,160,-10,25,4,60,17,22,100,9,20
+202,move,"move 10 20",3,1269,160,-10,25,4,60,17,22,100,10,20
+203,shoot,"shoot E",2,1285,160,-10,25,4,60,17,22,100,10,20
+204,move,"move 9 20",3,1290,160,-10,25,4,60,17,22,100,9,20
+205,move,"move 16 22",2,1295,160,-10,25,4,60,16,22,100,9,20
+206,dig,"dig 9 21",3,1299,152,-10,25,4,52,16,22,100,9,20
+207,shoot,"shoot E",2,1315,152,-10,25,4,52,16,22,100,9,20
+208,move,"move 10 19",3,1320,152,-10,25,4,52,16,22,100,10,19
+209,shoot,"shoot E",2,1334,144,-10,25,4,44,16,22,100,10,19
+210,move,"move 11 19",3,1339,144,-10,25,4,44,16,22,100,11,19
+211,shoot,"shoot E",2,1355,144,-10,25,4,44,16,22,100,11,19
+212,move,"move 11 18",3,1360,144,-10,25,4,44,16,22,100,11,18
+213,shoot,"shoot NE",2,1373,136,-10,25,4,36,16,22,100,11,18
+214,snowball,"snowball 16 20",3,1390,136,-10,25,4,36,16,22,100,11,18
+215,shoot,"shoot E",2,1406,136,-10,25,4,36,16,22,100,11,18
+216,dig,"dig 12 17",3,1410,128,-10,25,4,28,16,22,100,11,18
+217,shoot,"shoot E",2,1426,128,-10,25,4,28,16,22,100,11,18
+218,snowball,"snowball 16 20",3,1443,128,-10,25,4,28,16,22,100,11,18
+219,shoot,"shoot E",2,1457,120,-10,25,4,20,16,22,100,11,18
+220,snowball,"snowball 16 20",3,1474,120,-10,25,4,20,16,22,100,11,18
+221,move,"move 15 21",2,1479,120,-10,25,4,20,15,21,100,11,18
+222,invalid,"invalid",3,1475,120,-10,25,4,20,15,21,100,11,18
+223,shoot,"shoot E",2,1491,120,-10,25,4,20,15,21,100,11,18
+224,move,"move 12 17",3,1496,120,-10,25,4,20,15,21,100,12,17
+225,shoot,"shoot E",2,1512,120,-10,25,4,20,15,21,100,12,17
+226,dig,"dig 13 18",3,1519,120,-10,25,4,20,15,21,100,12,17
+227,shoot,"shoot E",2,1535,120,-10,25,4,20,15,21,100,12,17
+228,move,"move 13 18",3,1537,112,-10,25,4,12,15,21,100,13,18
+229,shoot,"shoot E",2,1553,112,-10,25,4,12,15,21,100,13,18
+230,shoot,"shoot SE",3,1569,112,-10,25,4,12,15,21,100,13,18
+231,shoot,"shoot E",2,1582,104,-10,25,4,4,15,21,100,13,18
+232,shoot,"shoot SE",3,1598,104,-10,25,4,4,15,21,100,13,18
+233,shoot,"shoot E",2,1614,104,-10,25,4,4,15,21,100,13,18
+234,move,"move 14 17",3,1618,100,-10,25,4,-4,15,21,100,14,17
+235,invalid,"invalid",3,1614,100,-10,25,4,-4,15,21,100,14,17
+236,move,"move 15 18",3,1619,100,-10,25,4,-4,15,21,100,15,18
+237,move,"move 15 19",3,1624,100,-10,25,4,-4,15,21,100,15,19
+238,shoot,"shoot SE",3,1640,100,-10,25,4,-4,15,21,100,15,19
+239,shoot,"shoot S",3,1653,92,-10,25,4,-4,15,21,92,15,19
+240,shoot,"shoot SE",3,1667,84,-10,25,4,-4,15,21,84,15,19
+241,shoot,"shoot SE",3,1683,84,-10,25,4,-4,15,21,84,15,19
+242,move,"move 16 19",3,1688,84,-10,25,4,-4,15,21,84,16,19
+243,move,"move 17 20",3,1693,84,-10,25,4,-4,15,21,84,17,20
+244,shoot,"shoot S",3,1709,84,-10,25,4,-4,15,21,84,17,20
+245,shoot,"shoot W",3,1722,76,-10,25,4,-4,15,21,76,17,20
+246,shoot,"shoot S",3,1738,76,-10,25,4,-4,15,21,76,17,20
+247,move,"move 18 19",3,1743,76,-10,25,4,-4,15,21,76,18,19
+248,move,"move 17 19",3,1748,76,-10,25,4,-4,15,21,76,17,19
+249,shoot,"shoot SW",3,1764,76,-10,25,4,-4,15,21,76,17,19
+250,move,"move 18 19",3,1769,76,-10,25,4,-4,15,21,76,18,19
+251,move,"move 17 19",3,1774,76,-10,25,4,-4,15,21,76,17,19
+252,shoot,"shoot SE",3,1790,76,-10,25,4,-4,15,21,76,17,19
+253,move,"move 17 18",3,1795,76,-10,25,4,-4,15,21,76,17,18
+254,shoot,"shoot SW",3,1851,76,-10,25,4,-4,15,21,76,17,18
+255,shoot,"shoot S",3,1867,76,-10,25,4,-4,15,21,76,17,18
+256,move,"move 18 17",3,1872,76,-10,25,4,-4,15,21,76,18,17
+257,move,"move 18 16",3,1877,76,-10,25,4,-4,15,21,76,18,16
+258,move,"move 18 17",3,1882,76,-10,25,4,-4,15,21,76,18,17
+259,shoot,"shoot S",3,1938,76,-10,25,4,-4,15,21,76,18,17
+260,move,"move 17 18",3,1943,76,-10,25,4,-4,15,21,76,17,18
+261,move,"move 18 18",3,1948,76,-10,25,4,-4,15,21,76,18,18
+262,move,"move 17 18",3,1953,76,-10,25,4,-4,15,21,76,17,18
+263,move,"move 18 18",3,1958,76,-10,25,4,-4,15,21,76,18,18
+264,move,"move 17 18",3,1963,76,-10,25,4,-4,15,21,76,17,18
+265,move,"move 18 18",3,1968,76,-10,25,4,-4,15,21,76,18,18
+266,move,"move 17 19",3,1973,76,-10,25,4,-4,15,21,76,17,19
+267,move,"move 17 20",3,1978,76,-10,25,4,-4,15,21,76,17,20
+268,move,"move 17 19",3,1983,76,-10,25,4,-4,15,21,76,17,19
+269,move,"move 17 20",3,1988,76,-10,25,4,-4,15,21,76,17,20
+270,move,"move 17 19",3,1993,76,-10,25,4,-4,15,21,76,17,19
+271,move,"move 17 20",3,1998,76,-10,25,4,-4,15,21,76,17,20
+272,move,"move 17 19",3,2003,76,-10,25,4,-4,15,21,76,17,19
+273,move,"move 17 20",3,2008,76,-10,25,4,-4,15,21,76,17,20
+274,move,"move 17 19",3,2013,76,-10,25,4,-4,15,21,76,17,19
+275,move,"move 18 20",3,2018,76,-10,25,4,-4,15,21,76,18,20
+276,move,"move 17 20",3,2023,76,-10,25,4,-4,15,21,76,17,20
+277,move,"move 17 19",3,2028,76,-10,25,4,-4,15,21,76,17,19
+278,move,"move 17 20",3,2033,76,-10,25,4,-4,15,21,76,17,20
+279,move,"move 18 20",3,2038,76,-10,25,4,-4,15,21,76,18,20
+280,move,"move 17 20",3,2043,76,-10,25,4,-4,15,21,76,17,20
+281,move,"move 17 19",3,2048,76,-10,25,4,-4,15,21,76,17,19
+282,move,"move 17 20",3,2053,76,-10,25,4,-4,15,21,76,17,20
+283,move,"move 17 19",3,2058,76,-10,25,4,-4,15,21,76,17,19
+284,move,"move 17 20",3,2063,76,-10,25,4,-4,15,21,76,17,20
+285,move,"move 17 19",3,2068,76,-10,25,4,-4,15,21,76,17,19
+286,move,"move 18 20",3,2073,76,-10,25,4,-4,15,21,76,18,20
+287,move,"move 17 19",3,2077,73,-10,25,4,-4,15,21,73,17,19
+288,move,"move 18 20",3,2082,73,-10,25,4,-4,15,21,73,18,20
+289,move,"move 17 19",3,2086,70,-10,25,4,-4,15,21,70,17,19
+290,move,"move 18 20",3,2091,70,-10,25,4,-4,15,21,70,18,20
+291,move,"move 17 19",3,2095,67,-10,25,4,-4,15,21,67,17,19
+292,move,"move 18 20",3,2100,67,-10,25,4,-4,15,21,67,18,20
+293,move,"move 17 19",3,2104,64,-10,25,4,-4,15,21,64,17,19
+294,move,"move 18 20",3,2109,64,-10,25,4,-4,15,21,64,18,20
+295,move,"move 17 20",3,2113,61,-10,25,4,-4,15,21,61,17,20
+296,move,"move 18 20",3,2117,58,-10,25,4,-4,15,21,58,18,20
+297,move,"move 17 20",3,2121,55,-10,25,4,-4,15,21,55,17,20
diff --git a/2019-worms/tests/replays/2019.08.19.21.15.02/B-init.json b/2019-worms/tests/replays/2019.08.19.21.15.02/B-init.json
new file mode 100644
index 0000000..8889378
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.15.02/B-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":2,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":1,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"AIR"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"DIRT"},{"x":15,"y":0,"type":"DIRT"},{"x":16,"y":0,"type":"AIR"},{"x":17,"y":0,"type":"DIRT"},{"x":18,"y":0,"type":"DIRT"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"AIR"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"AIR"},{"x":10,"y":1,"type":"AIR"},{"x":11,"y":1,"type":"AIR"},{"x":12,"y":1,"type":"AIR"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"DIRT"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"AIR"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"DIRT"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"AIR"},{"x":21,"y":1,"type":"AIR"},{"x":22,"y":1,"type":"AIR"},{"x":23,"y":1,"type":"AIR"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"AIR"},{"x":14,"y":2,"type":"AIR"},{"x":15,"y":2,"type":"DIRT"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"DIRT"},{"x":18,"y":2,"type":"AIR"},{"x":19,"y":2,"type":"AIR"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"DIRT"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"AIR"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"AIR"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"DIRT"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"DIRT"},{"x":5,"y":4,"type":"DIRT"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"DIRT"},{"x":12,"y":4,"type":"DIRT"},{"x":13,"y":4,"type":"DIRT"},{"x":14,"y":4,"type":"DIRT"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"DIRT"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"DIRT"},{"x":19,"y":4,"type":"DIRT"},{"x":20,"y":4,"type":"DIRT"},{"x":21,"y":4,"type":"DIRT"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"DIRT"},{"x":28,"y":4,"type":"DIRT"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"AIR"},{"x":5,"y":5,"type":"AIR"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"DIRT"},{"x":13,"y":5,"type":"DIRT"},{"x":14,"y":5,"type":"DIRT"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"DIRT"},{"x":19,"y":5,"type":"DIRT"},{"x":20,"y":5,"type":"DIRT"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"AIR"},{"x":28,"y":5,"type":"AIR"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"DIRT"},{"x":4,"y":6,"type":"DIRT"},{"x":5,"y":6,"type":"AIR"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"DIRT"},{"x":12,"y":6,"type":"DIRT"},{"x":13,"y":6,"type":"DIRT"},{"x":14,"y":6,"type":"DIRT"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"DIRT"},{"x":19,"y":6,"type":"DIRT"},{"x":20,"y":6,"type":"DIRT"},{"x":21,"y":6,"type":"DIRT"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"AIR"},{"x":28,"y":6,"type":"DIRT"},{"x":29,"y":6,"type":"DIRT"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"AIR"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"DIRT"},{"x":5,"y":7,"type":"AIR"},{"x":6,"y":7,"type":"AIR"},{"x":7,"y":7,"type":"DIRT"},{"x":8,"y":7,"type":"DIRT"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"DIRT"},{"x":12,"y":7,"type":"DIRT"},{"x":13,"y":7,"type":"AIR"},{"x":14,"y":7,"type":"AIR"},{"x":15,"y":7,"type":"AIR"},{"x":16,"y":7,"type":"DIRT"},{"x":17,"y":7,"type":"AIR"},{"x":18,"y":7,"type":"AIR"},{"x":19,"y":7,"type":"AIR"},{"x":20,"y":7,"type":"DIRT"},{"x":21,"y":7,"type":"DIRT"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"DIRT"},{"x":25,"y":7,"type":"DIRT"},{"x":26,"y":7,"type":"AIR"},{"x":27,"y":7,"type":"AIR"},{"x":28,"y":7,"type":"DIRT"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"AIR"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"AIR"},{"x":2,"y":8,"type":"AIR"},{"x":3,"y":8,"type":"AIR"},{"x":4,"y":8,"type":"AIR"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"DIRT"},{"x":7,"y":8,"type":"DIRT"},{"x":8,"y":8,"type":"DIRT"},{"x":9,"y":8,"type":"DIRT"},{"x":10,"y":8,"type":"DIRT"},{"x":11,"y":8,"type":"DIRT"},{"x":12,"y":8,"type":"DIRT"},{"x":13,"y":8,"type":"AIR"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"DIRT"},{"x":16,"y":8,"type":"DIRT"},{"x":17,"y":8,"type":"DIRT"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"AIR"},{"x":20,"y":8,"type":"DIRT"},{"x":21,"y":8,"type":"DIRT"},{"x":22,"y":8,"type":"DIRT"},{"x":23,"y":8,"type":"DIRT"},{"x":24,"y":8,"type":"DIRT"},{"x":25,"y":8,"type":"DIRT"},{"x":26,"y":8,"type":"DIRT"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"AIR"},{"x":29,"y":8,"type":"AIR"},{"x":30,"y":8,"type":"AIR"},{"x":31,"y":8,"type":"AIR"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"AIR"},{"x":2,"y":9,"type":"AIR"},{"x":3,"y":9,"type":"AIR"},{"x":4,"y":9,"type":"DIRT"},{"x":5,"y":9,"type":"DIRT"},{"x":6,"y":9,"type":"DIRT"},{"x":7,"y":9,"type":"DIRT"},{"x":8,"y":9,"type":"DIRT"},{"x":9,"y":9,"type":"DIRT"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"AIR"},{"x":12,"y":9,"type":"DIRT"},{"x":13,"y":9,"type":"AIR"},{"x":14,"y":9,"type":"AIR"},{"x":15,"y":9,"type":"DIRT"},{"x":16,"y":9,"type":"DIRT"},{"x":17,"y":9,"type":"DIRT"},{"x":18,"y":9,"type":"AIR"},{"x":19,"y":9,"type":"AIR"},{"x":20,"y":9,"type":"DIRT"},{"x":21,"y":9,"type":"AIR"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"DIRT"},{"x":24,"y":9,"type":"DIRT"},{"x":25,"y":9,"type":"DIRT"},{"x":26,"y":9,"type":"DIRT"},{"x":27,"y":9,"type":"DIRT"},{"x":28,"y":9,"type":"DIRT"},{"x":29,"y":9,"type":"AIR"},{"x":30,"y":9,"type":"AIR"},{"x":31,"y":9,"type":"AIR"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"AIR"},{"x":2,"y":10,"type":"AIR"},{"x":3,"y":10,"type":"AIR"},{"x":4,"y":10,"type":"AIR"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"DIRT"},{"x":7,"y":10,"type":"DIRT"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"DIRT"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"AIR"},{"x":12,"y":10,"type":"DIRT"},{"x":13,"y":10,"type":"DIRT"},{"x":14,"y":10,"type":"DIRT"},{"x":15,"y":10,"type":"DIRT"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"DIRT"},{"x":18,"y":10,"type":"DIRT"},{"x":19,"y":10,"type":"DIRT"},{"x":20,"y":10,"type":"DIRT"},{"x":21,"y":10,"type":"AIR"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"DIRT"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"DIRT"},{"x":26,"y":10,"type":"DIRT"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"AIR"},{"x":29,"y":10,"type":"AIR"},{"x":30,"y":10,"type":"AIR"},{"x":31,"y":10,"type":"AIR"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"AIR"},{"x":3,"y":11,"type":"AIR"},{"x":4,"y":11,"type":"AIR"},{"x":5,"y":11,"type":"AIR"},{"x":6,"y":11,"type":"DIRT"},{"x":7,"y":11,"type":"DIRT"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"DIRT"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"DIRT"},{"x":13,"y":11,"type":"DIRT"},{"x":14,"y":11,"type":"DIRT"},{"x":15,"y":11,"type":"AIR"},{"x":16,"y":11,"type":"AIR"},{"x":17,"y":11,"type":"AIR"},{"x":18,"y":11,"type":"DIRT"},{"x":19,"y":11,"type":"DIRT"},{"x":20,"y":11,"type":"DIRT"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"DIRT"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"DIRT"},{"x":26,"y":11,"type":"DIRT"},{"x":27,"y":11,"type":"AIR"},{"x":28,"y":11,"type":"AIR"},{"x":29,"y":11,"type":"AIR"},{"x":30,"y":11,"type":"AIR"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"AIR"},{"x":1,"y":12,"type":"AIR"},{"x":2,"y":12,"type":"AIR"},{"x":3,"y":12,"type":"AIR"},{"x":4,"y":12,"type":"AIR"},{"x":5,"y":12,"type":"AIR"},{"x":6,"y":12,"type":"AIR"},{"x":7,"y":12,"type":"DIRT"},{"x":8,"y":12,"type":"DIRT"},{"x":9,"y":12,"type":"DIRT"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"AIR"},{"x":13,"y":12,"type":"DIRT"},{"x":14,"y":12,"type":"DIRT"},{"x":15,"y":12,"type":"AIR"},{"x":16,"y":12,"type":"AIR"},{"x":17,"y":12,"type":"AIR"},{"x":18,"y":12,"type":"DIRT"},{"x":19,"y":12,"type":"DIRT"},{"x":20,"y":12,"type":"AIR"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"DIRT"},{"x":24,"y":12,"type":"DIRT"},{"x":25,"y":12,"type":"DIRT"},{"x":26,"y":12,"type":"AIR"},{"x":27,"y":12,"type":"AIR"},{"x":28,"y":12,"type":"AIR"},{"x":29,"y":12,"type":"AIR"},{"x":30,"y":12,"type":"AIR"},{"x":31,"y":12,"type":"AIR"},{"x":32,"y":12,"type":"AIR"}],[{"x":0,"y":13,"type":"AIR"},{"x":1,"y":13,"type":"AIR"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"AIR"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"DIRT"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"DIRT"},{"x":8,"y":13,"type":"DIRT"},{"x":9,"y":13,"type":"DIRT"},{"x":10,"y":13,"type":"DIRT"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"DIRT"},{"x":13,"y":13,"type":"DIRT"},{"x":14,"y":13,"type":"DIRT"},{"x":15,"y":13,"type":"AIR"},{"x":16,"y":13,"type":"AIR"},{"x":17,"y":13,"type":"AIR"},{"x":18,"y":13,"type":"DIRT"},{"x":19,"y":13,"type":"DIRT"},{"x":20,"y":13,"type":"DIRT"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"DIRT"},{"x":23,"y":13,"type":"DIRT"},{"x":24,"y":13,"type":"DIRT"},{"x":25,"y":13,"type":"DIRT"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"DIRT"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"AIR"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"AIR"},{"x":32,"y":13,"type":"AIR"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"DIRT"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"DIRT"},{"x":8,"y":14,"type":"DIRT"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"AIR"},{"x":11,"y":14,"type":"AIR"},{"x":12,"y":14,"type":"AIR"},{"x":13,"y":14,"type":"DIRT"},{"x":14,"y":14,"type":"AIR"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"AIR"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"AIR"},{"x":19,"y":14,"type":"DIRT"},{"x":20,"y":14,"type":"AIR"},{"x":21,"y":14,"type":"AIR"},{"x":22,"y":14,"type":"AIR"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"DIRT"},{"x":25,"y":14,"type":"DIRT"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"DIRT"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"DIRT"},{"x":5,"y":15,"type":"DIRT"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"AIR"},{"x":9,"y":15,"type":"AIR"},{"x":10,"y":15,"type":"AIR"},{"x":11,"y":15,"type":"AIR"},{"x":12,"y":15,"type":"AIR"},{"x":13,"y":15,"type":"AIR"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"AIR"},{"x":16,"y":15,"type":"AIR"},{"x":17,"y":15,"type":"AIR"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"AIR"},{"x":20,"y":15,"type":"AIR"},{"x":21,"y":15,"type":"AIR"},{"x":22,"y":15,"type":"AIR"},{"x":23,"y":15,"type":"AIR"},{"x":24,"y":15,"type":"AIR"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"DIRT"},{"x":28,"y":15,"type":"DIRT"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"DIRT"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"AIR"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"AIR"},{"x":10,"y":16,"type":"DIRT"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"AIR"},{"x":14,"y":16,"type":"AIR"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"AIR"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"AIR"},{"x":19,"y":16,"type":"AIR"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"DIRT"},{"x":23,"y":16,"type":"AIR"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"AIR"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"DIRT"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"AIR"},{"x":7,"y":17,"type":"AIR"},{"x":8,"y":17,"type":"DIRT"},{"x":9,"y":17,"type":"DIRT"},{"x":10,"y":17,"type":"AIR"},{"x":11,"y":17,"type":"AIR"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"DIRT"},{"x":14,"y":17,"type":"AIR"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"DIRT"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"DIRT"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"AIR"},{"x":22,"y":17,"type":"AIR"},{"x":23,"y":17,"type":"DIRT"},{"x":24,"y":17,"type":"DIRT"},{"x":25,"y":17,"type":"AIR"},{"x":26,"y":17,"type":"AIR"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"AIR"},{"x":6,"y":18,"type":"AIR"},{"x":7,"y":18,"type":"DIRT"},{"x":8,"y":18,"type":"DIRT"},{"x":9,"y":18,"type":"DIRT"},{"x":10,"y":18,"type":"AIR"},{"x":11,"y":18,"type":"AIR"},{"x":12,"y":18,"type":"AIR"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"DIRT"},{"x":15,"y":18,"type":"AIR"},{"x":16,"y":18,"type":"DIRT"},{"x":17,"y":18,"type":"AIR"},{"x":18,"y":18,"type":"DIRT"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"AIR"},{"x":21,"y":18,"type":"AIR"},{"x":22,"y":18,"type":"AIR"},{"x":23,"y":18,"type":"DIRT"},{"x":24,"y":18,"type":"DIRT"},{"x":25,"y":18,"type":"DIRT"},{"x":26,"y":18,"type":"AIR"},{"x":27,"y":18,"type":"AIR"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"AIR"},{"x":3,"y":19,"type":"AIR"},{"x":4,"y":19,"type":"DIRT"},{"x":5,"y":19,"type":"DIRT"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"AIR"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"AIR"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"DIRT"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"AIR"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"DIRT"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"AIR"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"AIR"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"DIRT"},{"x":28,"y":19,"type":"DIRT"},{"x":29,"y":19,"type":"AIR"},{"x":30,"y":19,"type":"AIR"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"DIRT"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"AIR"},{"x":3,"y":20,"type":"AIR"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"DIRT"},{"x":6,"y":20,"type":"DIRT"},{"x":7,"y":20,"type":"AIR"},{"x":8,"y":20,"type":"AIR"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"DIRT"},{"x":11,"y":20,"type":"DIRT"},{"x":12,"y":20,"type":"DIRT"},{"x":13,"y":20,"type":"DIRT"},{"x":14,"y":20,"type":"DIRT"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"AIR"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"DIRT"},{"x":19,"y":20,"type":"DIRT"},{"x":20,"y":20,"type":"DIRT"},{"x":21,"y":20,"type":"DIRT"},{"x":22,"y":20,"type":"DIRT"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"AIR"},{"x":25,"y":20,"type":"AIR"},{"x":26,"y":20,"type":"DIRT"},{"x":27,"y":20,"type":"DIRT"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"AIR"},{"x":30,"y":20,"type":"AIR"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"DIRT"}],[{"x":0,"y":21,"type":"DIRT"},{"x":1,"y":21,"type":"AIR"},{"x":2,"y":21,"type":"AIR"},{"x":3,"y":21,"type":"AIR"},{"x":4,"y":21,"type":"AIR"},{"x":5,"y":21,"type":"DIRT"},{"x":6,"y":21,"type":"DIRT"},{"x":7,"y":21,"type":"DIRT"},{"x":8,"y":21,"type":"AIR"},{"x":9,"y":21,"type":"DIRT"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"DIRT"},{"x":12,"y":21,"type":"DIRT"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"AIR"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"DIRT"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"AIR"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"DIRT"},{"x":21,"y":21,"type":"DIRT"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"DIRT"},{"x":24,"y":21,"type":"AIR"},{"x":25,"y":21,"type":"DIRT"},{"x":26,"y":21,"type":"DIRT"},{"x":27,"y":21,"type":"DIRT"},{"x":28,"y":21,"type":"AIR"},{"x":29,"y":21,"type":"AIR"},{"x":30,"y":21,"type":"AIR"},{"x":31,"y":21,"type":"AIR"},{"x":32,"y":21,"type":"DIRT"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"AIR"},{"x":2,"y":22,"type":"AIR"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"DIRT"},{"x":5,"y":22,"type":"DIRT"},{"x":6,"y":22,"type":"DIRT"},{"x":7,"y":22,"type":"DIRT"},{"x":8,"y":22,"type":"DIRT"},{"x":9,"y":22,"type":"DIRT"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"DIRT"},{"x":13,"y":22,"type":"DIRT"},{"x":14,"y":22,"type":"AIR"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"AIR"},{"x":19,"y":22,"type":"DIRT"},{"x":20,"y":22,"type":"DIRT"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"DIRT"},{"x":24,"y":22,"type":"DIRT"},{"x":25,"y":22,"type":"DIRT"},{"x":26,"y":22,"type":"DIRT"},{"x":27,"y":22,"type":"DIRT"},{"x":28,"y":22,"type":"DIRT"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"AIR"},{"x":31,"y":22,"type":"AIR"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"AIR"},{"x":2,"y":23,"type":"DIRT"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"DIRT"},{"x":5,"y":23,"type":"AIR"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"DIRT"},{"x":8,"y":23,"type":"DIRT"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"DIRT"},{"x":13,"y":23,"type":"AIR"},{"x":14,"y":23,"type":"AIR"},{"x":15,"y":23,"type":"DIRT"},{"x":16,"y":23,"type":"DIRT"},{"x":17,"y":23,"type":"DIRT"},{"x":18,"y":23,"type":"AIR"},{"x":19,"y":23,"type":"AIR"},{"x":20,"y":23,"type":"DIRT"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"DIRT"},{"x":25,"y":23,"type":"DIRT"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"AIR"},{"x":28,"y":23,"type":"DIRT"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"DIRT"},{"x":31,"y":23,"type":"AIR"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"DIRT"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"DIRT"},{"x":5,"y":24,"type":"AIR"},{"x":6,"y":24,"type":"AIR"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"DIRT"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"DIRT"},{"x":11,"y":24,"type":"DIRT"},{"x":12,"y":24,"type":"DIRT"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"DIRT"},{"x":15,"y":24,"type":"DIRT"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"DIRT"},{"x":18,"y":24,"type":"DIRT"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"DIRT"},{"x":21,"y":24,"type":"DIRT"},{"x":22,"y":24,"type":"DIRT"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"DIRT"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"AIR"},{"x":27,"y":24,"type":"AIR"},{"x":28,"y":24,"type":"DIRT"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"DIRT"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"DIRT"},{"x":3,"y":25,"type":"DIRT"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"DIRT"},{"x":6,"y":25,"type":"DIRT"},{"x":7,"y":25,"type":"DIRT"},{"x":8,"y":25,"type":"DIRT"},{"x":9,"y":25,"type":"DIRT"},{"x":10,"y":25,"type":"DIRT"},{"x":11,"y":25,"type":"DIRT"},{"x":12,"y":25,"type":"DIRT"},{"x":13,"y":25,"type":"DIRT"},{"x":14,"y":25,"type":"DIRT"},{"x":15,"y":25,"type":"DIRT"},{"x":16,"y":25,"type":"AIR"},{"x":17,"y":25,"type":"DIRT"},{"x":18,"y":25,"type":"DIRT"},{"x":19,"y":25,"type":"DIRT"},{"x":20,"y":25,"type":"DIRT"},{"x":21,"y":25,"type":"DIRT"},{"x":22,"y":25,"type":"DIRT"},{"x":23,"y":25,"type":"DIRT"},{"x":24,"y":25,"type":"DIRT"},{"x":25,"y":25,"type":"DIRT"},{"x":26,"y":25,"type":"DIRT"},{"x":27,"y":25,"type":"DIRT"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"DIRT"},{"x":30,"y":25,"type":"DIRT"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"DIRT"},{"x":5,"y":26,"type":"DIRT"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"DIRT"},{"x":14,"y":26,"type":"AIR"},{"x":15,"y":26,"type":"AIR"},{"x":16,"y":26,"type":"AIR"},{"x":17,"y":26,"type":"AIR"},{"x":18,"y":26,"type":"AIR"},{"x":19,"y":26,"type":"DIRT"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"DIRT"},{"x":28,"y":26,"type":"DIRT"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"DIRT"},{"x":5,"y":27,"type":"DIRT"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"DIRT"},{"x":14,"y":27,"type":"AIR"},{"x":15,"y":27,"type":"AIR"},{"x":16,"y":27,"type":"DIRT"},{"x":17,"y":27,"type":"AIR"},{"x":18,"y":27,"type":"AIR"},{"x":19,"y":27,"type":"DIRT"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"DIRT"},{"x":28,"y":27,"type":"DIRT"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"DIRT"},{"x":5,"y":28,"type":"DIRT"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"DIRT"},{"x":12,"y":28,"type":"DIRT"},{"x":13,"y":28,"type":"DIRT"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"DIRT"},{"x":16,"y":28,"type":"DIRT"},{"x":17,"y":28,"type":"DIRT"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"DIRT"},{"x":20,"y":28,"type":"DIRT"},{"x":21,"y":28,"type":"DIRT"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"DIRT"},{"x":28,"y":28,"type":"DIRT"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"AIR"},{"x":12,"y":29,"type":"DIRT"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"AIR"},{"x":15,"y":29,"type":"DIRT"},{"x":16,"y":29,"type":"AIR"},{"x":17,"y":29,"type":"DIRT"},{"x":18,"y":29,"type":"AIR"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"DIRT"},{"x":21,"y":29,"type":"AIR"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"AIR"},{"x":12,"y":30,"type":"DIRT"},{"x":13,"y":30,"type":"AIR"},{"x":14,"y":30,"type":"AIR"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"DIRT"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"AIR"},{"x":19,"y":30,"type":"AIR"},{"x":20,"y":30,"type":"DIRT"},{"x":21,"y":30,"type":"AIR"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"AIR"},{"x":9,"y":31,"type":"DIRT"},{"x":10,"y":31,"type":"AIR"},{"x":11,"y":31,"type":"AIR"},{"x":12,"y":31,"type":"AIR"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"DIRT"},{"x":16,"y":31,"type":"DIRT"},{"x":17,"y":31,"type":"DIRT"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"AIR"},{"x":21,"y":31,"type":"AIR"},{"x":22,"y":31,"type":"AIR"},{"x":23,"y":31,"type":"DIRT"},{"x":24,"y":31,"type":"AIR"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"AIR"},{"x":12,"y":32,"type":"AIR"},{"x":13,"y":32,"type":"AIR"},{"x":14,"y":32,"type":"AIR"},{"x":15,"y":32,"type":"DIRT"},{"x":16,"y":32,"type":"AIR"},{"x":17,"y":32,"type":"DIRT"},{"x":18,"y":32,"type":"AIR"},{"x":19,"y":32,"type":"AIR"},{"x":20,"y":32,"type":"AIR"},{"x":21,"y":32,"type":"AIR"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.15.02/B-log.csv b/2019-worms/tests/replays/2019.08.19.21.15.02/B-log.csv
new file mode 100644
index 0000000..04d9660
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.15.02/B-log.csv
@@ -0,0 +1,298 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,8,28,100,8,4,100,31,16
+2,move,"move 9 27",1,121,350,150,9,27,100,8,4,100,31,16
+3,move,"move 9 5",2,126,350,150,9,27,100,9,5,100,31,16
+4,move,"move 30 17",3,131,350,150,9,27,100,9,5,100,30,17
+5,dig,"dig 10 26",1,138,350,150,9,27,100,9,5,100,30,17
+6,dig,"dig 10 6",2,145,350,150,9,27,100,9,5,100,30,17
+7,dig,"dig 29 17",3,152,350,150,9,27,100,9,5,100,30,17
+8,move,"move 10 26",1,157,350,150,10,26,100,9,5,100,30,17
+9,move,"move 10 6",2,162,350,150,10,26,100,10,6,100,30,17
+10,move,"move 29 17",3,167,350,150,10,26,100,10,6,100,29,17
+11,dig,"dig 11 25",1,174,350,150,10,26,100,10,6,100,29,17
+12,dig,"dig 11 7",2,181,350,150,10,26,100,10,6,100,29,17
+13,dig,"dig 28 17",3,188,350,150,10,26,100,10,6,100,29,17
+14,move,"move 11 25",1,193,350,150,11,25,100,10,6,100,29,17
+15,move,"move 11 7",2,198,350,150,11,25,100,11,7,100,29,17
+16,move,"move 28 17",3,203,350,150,11,25,100,11,7,100,28,17
+17,dig,"dig 12 24",1,210,350,150,11,25,100,11,7,100,28,17
+18,dig,"dig 12 8",2,217,350,150,11,25,100,11,7,100,28,17
+19,dig,"dig 27 17",3,224,350,150,11,25,100,11,7,100,28,17
+20,move,"move 12 24",1,229,350,150,12,24,100,11,7,100,28,17
+21,move,"move 12 8",2,234,350,150,12,24,100,12,8,100,28,17
+22,move,"move 27 17",3,239,350,150,12,24,100,12,8,100,27,17
+23,move,"move 13 23",1,244,350,150,13,23,100,12,8,100,27,17
+24,move,"move 13 9",2,249,350,150,13,23,100,13,9,100,27,17
+25,move,"move 26 17",3,254,350,150,13,23,100,13,9,100,26,17
+26,move,"move 14 22",1,259,350,150,14,22,100,13,9,100,26,17
+27,dig,"dig 14 10",2,266,350,150,14,22,100,13,9,100,26,17
+28,move,"move 25 17",3,271,350,150,14,22,100,13,9,100,25,17
+29,move,"move 15 21",1,276,350,150,15,21,100,13,9,100,25,17
+30,move,"move 14 10",2,281,350,150,15,21,100,14,10,100,25,17
+31,dig,"dig 24 17",3,288,350,150,15,21,100,14,10,100,25,17
+32,move,"move 16 20",1,293,350,150,16,20,100,14,10,100,25,17
+33,dig,"dig 14 11",2,300,350,150,16,20,100,14,10,100,25,17
+34,move,"move 24 17",3,305,350,150,16,20,100,14,10,100,24,17
+35,move,"move 17 19",1,310,350,150,17,19,100,14,10,100,24,17
+36,move,"move 14 11",2,315,350,150,17,19,100,14,11,100,24,17
+37,dig,"dig 23 17",3,322,350,150,17,19,100,14,11,100,24,17
+38,dig,"dig 18 18",1,329,350,150,17,19,100,14,11,100,24,17
+39,dig,"dig 14 12",2,336,350,150,17,19,100,14,11,100,24,17
+40,move,"move 23 17",3,341,350,150,17,19,100,14,11,100,23,17
+41,move,"move 18 18",1,346,350,150,18,18,100,14,11,100,23,17
+42,move,"move 14 12",2,351,350,150,18,18,100,14,12,100,23,17
+43,move,"move 22 17",3,356,350,150,18,18,100,14,12,100,22,17
+44,move,"move 18 17",1,365,360,160,18,17,100,14,12,100,22,17
+45,dig,"dig 14 13",2,372,360,160,18,17,100,14,12,100,22,17
+46,dig,"dig 21 16",3,379,360,160,18,17,100,14,12,100,22,17
+47,move,"move 17 16",1,384,360,160,17,16,100,14,12,100,22,17
+48,move,"move 14 13",2,389,360,160,17,16,100,14,13,100,22,17
+49,move,"move 21 16",3,394,360,160,17,16,100,14,13,100,21,16
+50,move,"move 16 15",1,399,360,160,16,15,100,14,13,100,21,16
+51,move,"move 14 14",2,404,360,160,16,15,100,14,14,100,21,16
+52,move,"move 20 15",3,409,360,160,16,15,100,14,14,100,20,15
+53,move,"move 15 15",1,414,360,160,15,15,100,14,14,100,20,15
+54,move,"move 14 15",2,422,370,160,15,15,110,14,15,100,20,15
+55,move,"move 21 14",3,427,370,160,15,15,110,14,15,100,21,14
+56,move,"move 16 14",1,432,370,160,16,14,110,14,15,100,21,14
+57,move,"move 15 14",2,437,370,160,16,14,110,15,14,100,21,14
+58,dig,"dig 22 13",3,444,370,160,16,14,110,15,14,100,21,14
+59,move,"move 17 13",1,449,370,160,17,13,110,15,14,100,21,14
+60,move,"move 16 13",2,454,370,160,17,13,110,16,13,100,21,14
+61,move,"move 22 13",3,459,370,160,17,13,110,16,13,100,22,13
+62,dig,"dig 18 12",1,466,370,160,17,13,110,16,13,100,22,13
+63,move,"move 17 12",2,471,370,160,17,13,110,17,12,100,22,13
+64,dig,"dig 23 12",3,478,370,160,17,13,110,17,12,100,22,13
+65,move,"move 18 12",1,483,370,160,18,12,110,17,12,100,22,13
+66,dig,"dig 18 11",2,490,370,160,18,12,110,17,12,100,22,13
+67,move,"move 23 12",3,495,370,160,18,12,110,17,12,100,23,12
+68,dig,"dig 19 11",1,502,370,160,18,12,110,17,12,100,23,12
+69,move,"move 18 11",2,507,370,160,18,12,110,18,11,100,23,12
+70,dig,"dig 23 11",3,514,370,160,18,12,110,18,11,100,23,12
+71,move,"move 19 11",1,519,370,160,19,11,110,18,11,100,23,12
+72,dig,"dig 19 10",2,526,370,160,19,11,110,18,11,100,23,12
+73,move,"move 24 11",3,531,370,160,19,11,110,18,11,100,24,11
+74,dig,"dig 20 10",1,538,370,160,19,11,110,18,11,100,24,11
+75,move,"move 19 10",2,543,370,160,19,11,110,19,10,100,24,11
+76,dig,"dig 23 10",3,550,370,160,19,11,110,19,10,100,24,11
+77,move,"move 20 10",1,555,370,160,20,10,110,19,10,100,24,11
+78,dig,"dig 20 9",2,562,370,160,20,10,110,19,10,100,24,11
+79,move,"move 24 10",3,567,370,160,20,10,110,19,10,100,24,10
+80,move,"move 21 9",1,572,370,160,21,9,110,19,10,100,24,10
+81,move,"move 20 9",2,577,370,160,21,9,110,20,9,100,24,10
+82,dig,"dig 25 9",3,584,370,160,21,9,110,20,9,100,24,10
+83,dig,"dig 22 8",1,591,370,160,21,9,110,20,9,100,24,10
+84,dig,"dig 21 8",2,598,370,160,21,9,110,20,9,100,24,10
+85,move,"move 25 9",3,603,370,160,21,9,110,20,9,100,25,9
+86,move,"move 22 8",1,608,370,160,22,8,110,20,9,100,25,9
+87,move,"move 21 8",2,613,370,160,22,8,110,21,8,100,25,9
+88,dig,"dig 25 8",3,620,370,160,22,8,110,21,8,100,25,9
+89,move,"move 23 7",1,625,370,160,23,7,110,21,8,100,25,9
+90,move,"move 22 7",2,630,370,160,23,7,110,22,7,100,25,9
+91,move,"move 25 8",3,635,370,160,23,7,110,22,7,100,25,8
+92,dig,"dig 24 6",1,642,370,160,23,7,110,22,7,100,25,8
+93,banana,"banana 24 3",2,703,370,160,23,7,110,22,7,100,25,8
+94,dig,"dig 24 7",3,710,370,160,23,7,110,22,7,100,25,8
+95,move,"move 24 6",1,715,370,160,24,6,110,22,7,100,25,8
+96,banana,"banana 23 4",2,783,370,160,24,6,110,22,7,100,25,8
+97,snowball,"snowball 23 4",3,800,370,160,24,6,110,22,7,100,25,8
+98,move,"move 23 5",1,805,370,160,23,5,110,22,7,100,25,8
+99,banana,"banana 23 4",2,815,357,147,23,5,110,22,7,100,25,8
+100,shoot,"shoot N",1,831,357,147,23,5,110,22,7,100,25,8
+101,shoot,"shoot N",1,847,357,147,23,5,110,22,7,100,25,8
+102,shoot,"shoot N",1,860,349,139,23,5,110,22,7,100,25,8
+103,shoot,"shoot N",1,873,341,131,23,5,110,22,7,100,25,8
+104,shoot,"shoot N",1,889,341,131,23,5,110,22,7,100,25,8
+105,move,"move 23 6",2,894,341,131,23,5,110,23,6,100,25,8
+106,snowball,"snowball 23 4",3,894,341,131,23,5,110,23,6,100,25,8
+107,move,"move 24 4",1,894,341,131,23,5,110,23,6,100,25,8
+108,move,"move 24 5",2,899,341,131,23,5,110,24,5,100,25,8
+109,move,"move 24 7",3,904,341,131,23,5,110,24,5,100,24,7
+110,move,"move 24 4",1,904,341,131,23,5,110,24,5,100,24,7
+111,shoot,"shoot N",2,920,341,131,23,5,110,24,5,100,24,7
+112,snowball,"snowball 24 3",3,937,341,131,23,5,110,24,5,100,24,7
+113,move,"move 24 4",1,942,341,131,24,4,110,24,5,100,24,7
+114,shoot,"shoot NE",2,958,341,131,24,4,110,24,5,100,24,7
+115,dig,"dig 25 6",3,965,341,131,24,4,110,24,5,100,24,7
+116,shoot,"shoot E",1,981,341,131,24,4,110,24,5,100,24,7
+117,shoot,"shoot NE",2,997,341,131,24,4,110,24,5,100,24,7
+118,move,"move 25 6",3,1000,333,123,24,4,110,24,5,100,25,6
+119,shoot,"shoot E",1,1016,333,123,24,4,110,24,5,100,25,6
+120,shoot,"shoot NE",2,1072,333,123,24,4,110,24,5,100,25,6
+121,move,"move 24 7",3,1077,333,123,24,4,110,24,5,100,24,7
+122,nothing,"nothing "Player chose to do nothing"",1,1077,333,123,24,4,110,24,5,100,24,7
+123,move,"move 24 6",2,1082,333,123,24,4,110,24,6,100,24,7
+124,dig,"dig 24 8",3,1089,333,123,24,4,110,24,6,100,24,7
+125,move,"move 24 5",1,1094,333,123,24,5,110,24,6,100,24,7
+126,move,"move 23 7",2,1099,333,123,24,5,110,23,7,100,24,7
+127,dig,"dig 23 8",3,1106,333,123,24,5,110,23,7,100,24,7
+128,move,"move 23 6",1,1111,333,123,23,6,110,23,7,100,24,7
+129,move,"move 22 8",2,1116,333,123,23,6,110,22,8,100,24,7
+130,move,"move 23 8",3,1121,333,123,23,6,110,22,8,100,23,8
+131,move,"move 23 7",1,1126,333,123,23,7,110,22,8,100,23,8
+132,dig,"dig 22 9",2,1133,333,123,23,7,110,22,8,100,23,8
+133,move,"move 22 9",3,1138,333,123,23,7,110,22,8,100,22,9
+134,move,"move 22 7",1,1143,333,123,22,7,110,22,8,100,22,9
+135,move,"move 21 9",2,1148,333,123,22,7,110,21,9,100,22,9
+136,move,"move 21 10",3,1153,333,123,22,7,110,21,9,100,21,10
+137,move,"move 21 8",1,1158,333,123,21,8,110,21,9,100,21,10
+138,invalid,"invalid",2,1154,333,123,21,8,110,21,9,100,21,10
+139,move,"move 21 11",3,1159,333,123,21,8,110,21,9,100,21,11
+140,move,"move 22 9",1,1164,333,123,22,9,110,21,9,100,21,11
+141,dig,"dig 22 10",2,1171,333,123,22,9,110,21,9,100,21,11
+142,move,"move 22 12",3,1176,333,123,22,9,110,21,9,100,22,12
+143,move,"move 23 10",1,1181,333,123,23,10,110,21,9,100,22,12
+144,move,"move 22 10",2,1186,333,123,23,10,110,22,10,100,22,12
+145,dig,"dig 23 13",3,1193,333,123,23,10,110,22,10,100,22,12
+146,move,"move 23 11",1,1198,333,123,23,11,110,22,10,100,22,12
+147,move,"move 21 9",2,1203,333,123,23,11,110,21,9,100,22,12
+148,move,"move 23 13",3,1208,333,123,23,11,110,21,9,100,23,13
+149,move,"move 23 12",1,1213,333,123,23,12,110,21,9,100,23,13
+150,move,"move 22 10",2,1218,333,123,23,12,110,22,10,100,23,13
+151,dig,"dig 23 14",3,1225,333,123,23,12,110,22,10,100,23,13
+152,move,"move 22 13",1,1230,333,123,22,13,110,22,10,100,23,13
+153,move,"move 23 11",2,1235,333,123,22,13,110,23,11,100,23,13
+154,move,"move 23 14",3,1240,333,123,22,13,110,23,11,100,23,14
+155,move,"move 23 13",1,1245,333,123,23,13,110,23,11,100,23,14
+156,move,"move 23 12",2,1250,333,123,23,13,110,23,12,100,23,14
+157,move,"move 23 15",3,1255,333,123,23,13,110,23,12,100,23,15
+158,move,"move 23 14",1,1260,333,123,23,14,110,23,12,100,23,15
+159,move,"move 23 13",2,1265,333,123,23,14,110,23,13,100,23,15
+160,move,"move 24 16",3,1270,333,123,23,14,110,23,13,100,24,16
+161,move,"move 24 15",1,1275,333,123,24,15,110,23,13,100,24,16
+162,move,"move 23 14",2,1280,333,123,24,15,110,23,14,100,24,16
+163,move,"move 23 17",3,1285,333,123,24,15,110,23,14,100,23,17
+164,move,"move 23 16",1,1290,333,123,23,16,110,23,14,100,23,17
+165,move,"move 23 15",2,1292,326,123,23,16,110,23,15,93,23,17
+166,move,"move 23 18",3,1297,326,123,23,16,110,23,15,93,23,18
+167,move,"move 23 17",1,1296,306,116,23,17,110,23,15,80,23,18
+168,move,"move 23 16",2,1301,306,116,23,17,110,23,16,80,23,18
+169,move,"move 23 19",3,1297,279,109,23,17,110,23,16,60,23,19
+170,move,"move 23 18",1,1302,279,109,23,18,110,23,16,60,23,19
+171,move,"move 23 17",2,1307,279,109,23,18,110,23,17,60,23,19
+172,move,"move 23 20",3,1312,279,109,23,18,110,23,17,60,23,20
+173,move,"move 23 19",1,1317,279,109,23,19,110,23,17,60,23,20
+174,move,"move 22 18",2,1322,279,109,23,19,110,22,18,60,23,20
+175,dig,"dig 22 21",3,1329,279,109,23,19,110,22,18,60,23,20
+176,move,"move 22 20",1,1334,279,109,22,20,110,22,18,60,23,20
+177,move,"move 22 19",2,1339,279,109,22,20,110,22,19,60,23,20
+178,move,"move 22 21",3,1344,279,109,22,20,110,22,19,60,22,21
+179,invalid,"invalid",1,1340,279,109,22,20,110,22,19,60,22,21
+180,dig,"dig 21 20",2,1347,279,109,22,20,110,22,19,60,22,21
+181,dig,"dig 21 22",3,1354,279,109,22,20,110,22,19,60,22,21
+182,dig,"dig 21 21",1,1361,279,109,22,20,110,22,19,60,22,21
+183,move,"move 21 20",2,1366,279,109,22,20,110,21,20,60,22,21
+184,move,"move 21 22",3,1371,279,109,22,20,110,21,20,60,21,22
+185,move,"move 21 21",1,1376,279,109,21,21,110,21,20,60,21,22
+186,dig,"dig 20 21",2,1383,279,109,21,21,110,21,20,60,21,22
+187,dig,"dig 20 23",3,1390,279,109,21,21,110,21,20,60,21,22
+188,dig,"dig 20 22",1,1397,279,109,21,21,110,21,20,60,21,22
+189,move,"move 20 21",2,1399,271,109,21,21,110,20,21,52,21,22
+190,shoot,"shoot SW",3,1415,271,109,21,21,110,20,21,52,21,22
+191,move,"move 20 22",1,1417,263,109,20,22,110,20,21,44,21,22
+192,dig,"dig 19 22",2,1424,263,109,20,22,110,20,21,44,21,22
+193,shoot,"shoot SW",3,1438,255,109,20,22,110,20,21,36,21,22
+194,move,"move 19 23",1,1443,255,109,19,23,110,20,21,36,21,22
+195,move,"move 19 22",2,1448,255,109,19,23,110,19,22,36,21,22
+196,move,"move 20 23",3,1453,255,109,19,23,110,19,22,36,20,23
+197,shoot,"shoot W",1,1466,247,109,19,23,102,19,22,36,20,23
+198,shoot,"shoot SW",2,1482,247,109,19,23,102,19,22,36,20,23
+199,invalid,"invalid",3,1475,239,101,19,23,102,19,22,36,20,23
+200,shoot,"shoot W",1,1491,239,101,19,23,102,19,22,36,20,23
+201,shoot,"shoot SW",2,1493,239,101,19,23,102,19,22,36,20,23
+202,move,"move 19 24",3,1498,239,101,19,23,102,19,22,36,19,24
+203,move,"move 18 22",1,1501,231,93,18,22,102,19,22,36,19,24
+204,dig,"dig 19 21",2,1508,231,93,18,22,102,19,22,36,19,24
+205,shoot,"shoot NW",3,1510,231,93,18,22,102,19,22,36,19,24
+206,shoot,"shoot W",1,1526,231,93,18,22,102,19,22,36,19,24
+207,move,"move 18 23",2,1528,223,85,18,22,102,18,23,36,19,24
+208,dig,"dig 18 25",3,1535,223,85,18,22,102,18,23,36,19,24
+209,shoot,"shoot W",1,1548,215,77,18,22,102,18,23,36,19,24
+210,move,"move 17 22",2,1553,215,77,18,22,102,17,22,36,19,24
+211,move,"move 18 23",3,1556,207,77,18,22,94,17,22,36,18,23
+212,move,"move 17 21",1,1561,207,77,17,21,94,17,22,36,18,23
+213,shoot,"shoot W",2,1574,199,69,17,21,94,17,22,36,18,23
+214,move,"move 19 23",3,1579,199,69,17,21,94,17,22,36,19,23
+215,shoot,"shoot SW",1,1576,191,69,17,21,86,17,22,36,19,23
+216,shoot,"shoot W",2,1592,191,69,17,21,86,17,22,36,19,23
+217,move,"move 18 22",3,1595,183,69,17,21,78,17,22,36,18,22
+218,shoot,"shoot SW",1,1595,183,69,17,21,78,17,22,36,18,22
+219,shoot,"shoot W",2,1608,175,69,17,21,70,17,22,36,18,22
+220,invalid,"invalid",3,1604,175,69,17,21,70,17,22,36,18,22
+221,shoot,"shoot SW",1,1604,175,69,17,21,70,17,22,36,18,22
+222,dig,"dig 16 21",2,1611,175,69,17,21,70,17,22,36,18,22
+223,move,"move 18 23",3,1613,167,61,17,21,70,17,22,36,18,23
+224,shoot,"shoot W",1,1613,167,61,17,21,70,17,22,36,18,23
+225,move,"move 16 21",2,1616,159,61,17,21,62,16,21,36,18,23
+226,move,"move 17 22",3,1621,159,61,17,21,62,16,21,36,17,22
+227,move,"move 18 21",1,1623,151,61,18,21,54,16,21,36,17,22
+228,shoot,"shoot W",2,1639,151,61,18,21,54,16,21,36,17,22
+229,dig,"dig 17 23",3,1643,143,61,18,21,46,16,21,36,17,22
+230,move,"move 17 21",1,1646,135,61,17,21,38,16,21,36,17,22
+231,shoot,"shoot W",2,1659,127,61,17,21,30,16,21,36,17,22
+232,invalid,"invalid",3,1652,119,61,17,21,22,16,21,36,17,22
+233,move,"move 18 22",1,1655,111,61,18,22,14,16,21,36,17,22
+234,shoot,"shoot W",2,1711,111,61,18,22,14,16,21,36,17,22
+235,move,"move 17 23",3,1716,111,61,18,22,14,16,21,36,17,23
+236,move,"move 17 21",1,1721,111,61,17,21,14,16,21,36,17,23
+237,move,"move 15 20",2,1726,111,61,17,21,14,15,20,36,17,23
+238,move,"move 16 22",3,1728,103,53,17,21,14,15,20,36,16,22
+239,shoot,"shoot NW",1,1741,95,53,17,21,6,15,20,36,16,22
+240,shoot,"shoot N",2,1755,87,45,17,21,6,15,20,36,16,22
+241,move,"move 15 21",3,1757,79,37,17,21,6,15,20,36,15,21
+242,shoot,"shoot NW",1,1759,79,37,17,21,6,15,20,36,15,21
+243,shoot,"shoot NE",2,1761,79,37,17,21,6,15,20,36,15,21
+244,move,"move 16 20",3,1763,71,29,17,21,6,15,20,36,16,20
+245,shoot,"shoot N",1,1777,63,29,17,21,6,15,20,28,16,20
+246,move,"move 15 21",2,1779,55,21,17,21,6,15,21,28,16,20
+247,shoot,"shoot E",3,1781,55,21,17,21,6,15,21,28,16,20
+248,dig,"dig 18 20",1,1788,55,21,17,21,6,15,21,28,16,20
+249,dig,"dig 14 20",2,1792,47,21,17,21,6,15,21,20,16,20
+250,shoot,"shoot NE",3,1794,47,21,17,21,6,15,21,20,16,20
+251,move,"move 18 20",1,1799,47,21,18,20,6,15,21,20,16,20
+252,move,"move 14 21",2,1802,39,13,18,20,6,14,21,20,16,20
+253,shoot,"shoot NE",3,1804,39,13,18,20,6,14,21,20,16,20
+254,move,"move 17 19",1,1807,33,13,17,19,-2,14,21,20,16,20
+255,move,"move 16 21",3,1809,25,5,17,19,-2,14,21,20,16,21
+256,shoot,"shoot N",1,1811,25,5,17,19,-2,14,21,20,16,21
+257,move,"move 17 20",3,1816,25,5,17,19,-2,14,21,20,17,20
+258,move,"move 18 18",1,1821,25,5,18,18,-2,14,21,20,17,20
+259,move,"move 18 19",3,1824,20,-3,18,18,-2,14,21,20,18,19
+260,shoot,"shoot N",3,1826,20,-3,18,18,-2,14,21,20,18,19
+261,shoot,"shoot NW",3,1828,20,-3,18,18,-2,14,21,20,18,19
+262,shoot,"shoot N",3,1830,20,-3,18,18,-2,14,21,20,18,19
+263,shoot,"shoot NW",3,1832,20,-3,18,18,-2,14,21,20,18,19
+264,shoot,"shoot N",3,1834,20,-3,18,18,-2,14,21,20,18,19
+265,shoot,"shoot NW",3,1836,20,-3,18,18,-2,14,21,20,18,19
+266,shoot,"shoot N",3,1838,20,-3,18,18,-2,14,21,20,18,19
+267,shoot,"shoot W",3,1840,20,-3,18,18,-2,14,21,20,18,19
+268,shoot,"shoot SW",3,1842,20,-3,18,18,-2,14,21,20,18,19
+269,shoot,"shoot W",3,1844,20,-3,18,18,-2,14,21,20,18,19
+270,shoot,"shoot SW",3,1846,20,-3,18,18,-2,14,21,20,18,19
+271,shoot,"shoot W",3,1848,20,-3,18,18,-2,14,21,20,18,19
+272,shoot,"shoot SW",3,1850,20,-3,18,18,-2,14,21,20,18,19
+273,shoot,"shoot W",3,1852,20,-3,18,18,-2,14,21,20,18,19
+274,shoot,"shoot SW",3,1854,20,-3,18,18,-2,14,21,20,18,19
+275,shoot,"shoot W",3,1856,20,-3,18,18,-2,14,21,20,18,19
+276,shoot,"shoot S",3,1858,20,-3,18,18,-2,14,21,20,18,19
+277,shoot,"shoot SW",3,1860,20,-3,18,18,-2,14,21,20,18,19
+278,shoot,"shoot W",3,1862,20,-3,18,18,-2,14,21,20,18,19
+279,shoot,"shoot SW",3,1864,20,-3,18,18,-2,14,21,20,18,19
+280,shoot,"shoot S",3,1866,20,-3,18,18,-2,14,21,20,18,19
+281,shoot,"shoot SW",3,1868,20,-3,18,18,-2,14,21,20,18,19
+282,shoot,"shoot W",3,1870,20,-3,18,18,-2,14,21,20,18,19
+283,shoot,"shoot SW",3,1872,20,-3,18,18,-2,14,21,20,18,19
+284,shoot,"shoot W",3,1874,20,-3,18,18,-2,14,21,20,18,19
+285,shoot,"shoot SW",3,1876,20,-3,18,18,-2,14,21,20,18,19
+286,shoot,"shoot W",3,1878,20,-3,18,18,-2,14,21,20,18,19
+287,shoot,"shoot S",3,1880,20,-3,18,18,-2,14,21,20,18,19
+288,shoot,"shoot W",3,1882,20,-3,18,18,-2,14,21,20,18,19
+289,shoot,"shoot S",3,1884,20,-3,18,18,-2,14,21,20,18,19
+290,shoot,"shoot W",3,1886,20,-3,18,18,-2,14,21,20,18,19
+291,shoot,"shoot S",3,1888,20,-3,18,18,-2,14,21,20,18,19
+292,shoot,"shoot W",3,1890,20,-3,18,18,-2,14,21,20,18,19
+293,shoot,"shoot S",3,1892,20,-3,18,18,-2,14,21,20,18,19
+294,shoot,"shoot W",3,1894,20,-3,18,18,-2,14,21,20,18,19
+295,shoot,"shoot S",3,1895,17,-3,18,18,-2,14,21,17,18,19
+296,shoot,"shoot SW",3,1896,14,-3,18,18,-2,14,21,14,18,19
+297,shoot,"shoot S",3,1897,11,-3,18,18,-2,14,21,11,18,19
diff --git a/2019-worms/tests/replays/2019.08.19.21.31.16/A-init.json b/2019-worms/tests/replays/2019.08.19.21.31.16/A-init.json
new file mode 100644
index 0000000..31546d4
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.31.16/A-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":1,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":2,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"DIRT"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"AIR"},{"x":15,"y":0,"type":"AIR"},{"x":16,"y":0,"type":"DIRT"},{"x":17,"y":0,"type":"AIR"},{"x":18,"y":0,"type":"AIR"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"DIRT"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"DIRT"},{"x":10,"y":1,"type":"DIRT"},{"x":11,"y":1,"type":"DIRT"},{"x":12,"y":1,"type":"DIRT"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"AIR"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"DIRT"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"AIR"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"DIRT"},{"x":21,"y":1,"type":"DIRT"},{"x":22,"y":1,"type":"DIRT"},{"x":23,"y":1,"type":"DIRT"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"DIRT"},{"x":14,"y":2,"type":"DIRT"},{"x":15,"y":2,"type":"AIR"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"AIR"},{"x":18,"y":2,"type":"DIRT"},{"x":19,"y":2,"type":"DIRT"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"AIR"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"DIRT"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"DIRT"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"AIR"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"AIR"},{"x":5,"y":4,"type":"AIR"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"AIR"},{"x":12,"y":4,"type":"AIR"},{"x":13,"y":4,"type":"AIR"},{"x":14,"y":4,"type":"AIR"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"AIR"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"AIR"},{"x":19,"y":4,"type":"AIR"},{"x":20,"y":4,"type":"AIR"},{"x":21,"y":4,"type":"AIR"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"AIR"},{"x":28,"y":4,"type":"AIR"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"AIR"},{"x":5,"y":5,"type":"AIR"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"AIR"},{"x":13,"y":5,"type":"AIR"},{"x":14,"y":5,"type":"AIR"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"AIR"},{"x":19,"y":5,"type":"AIR"},{"x":20,"y":5,"type":"AIR"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"AIR"},{"x":28,"y":5,"type":"AIR"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"AIR"},{"x":4,"y":6,"type":"AIR"},{"x":5,"y":6,"type":"AIR"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"DIRT"},{"x":12,"y":6,"type":"AIR"},{"x":13,"y":6,"type":"AIR"},{"x":14,"y":6,"type":"AIR"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"AIR"},{"x":19,"y":6,"type":"AIR"},{"x":20,"y":6,"type":"AIR"},{"x":21,"y":6,"type":"DIRT"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"AIR"},{"x":28,"y":6,"type":"AIR"},{"x":29,"y":6,"type":"AIR"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"AIR"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"AIR"},{"x":5,"y":7,"type":"DIRT"},{"x":6,"y":7,"type":"AIR"},{"x":7,"y":7,"type":"AIR"},{"x":8,"y":7,"type":"AIR"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"AIR"},{"x":12,"y":7,"type":"AIR"},{"x":13,"y":7,"type":"DIRT"},{"x":14,"y":7,"type":"AIR"},{"x":15,"y":7,"type":"AIR"},{"x":16,"y":7,"type":"AIR"},{"x":17,"y":7,"type":"AIR"},{"x":18,"y":7,"type":"AIR"},{"x":19,"y":7,"type":"DIRT"},{"x":20,"y":7,"type":"AIR"},{"x":21,"y":7,"type":"AIR"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"AIR"},{"x":25,"y":7,"type":"AIR"},{"x":26,"y":7,"type":"AIR"},{"x":27,"y":7,"type":"DIRT"},{"x":28,"y":7,"type":"AIR"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"AIR"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"AIR"},{"x":2,"y":8,"type":"AIR"},{"x":3,"y":8,"type":"DIRT"},{"x":4,"y":8,"type":"DIRT"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"AIR"},{"x":7,"y":8,"type":"AIR"},{"x":8,"y":8,"type":"AIR"},{"x":9,"y":8,"type":"AIR"},{"x":10,"y":8,"type":"AIR"},{"x":11,"y":8,"type":"AIR"},{"x":12,"y":8,"type":"AIR"},{"x":13,"y":8,"type":"DIRT"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"AIR"},{"x":16,"y":8,"type":"AIR"},{"x":17,"y":8,"type":"AIR"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"DIRT"},{"x":20,"y":8,"type":"AIR"},{"x":21,"y":8,"type":"AIR"},{"x":22,"y":8,"type":"AIR"},{"x":23,"y":8,"type":"AIR"},{"x":24,"y":8,"type":"AIR"},{"x":25,"y":8,"type":"AIR"},{"x":26,"y":8,"type":"AIR"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"DIRT"},{"x":29,"y":8,"type":"DIRT"},{"x":30,"y":8,"type":"AIR"},{"x":31,"y":8,"type":"AIR"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"DIRT"},{"x":2,"y":9,"type":"AIR"},{"x":3,"y":9,"type":"DIRT"},{"x":4,"y":9,"type":"DIRT"},{"x":5,"y":9,"type":"AIR"},{"x":6,"y":9,"type":"AIR"},{"x":7,"y":9,"type":"DIRT"},{"x":8,"y":9,"type":"AIR"},{"x":9,"y":9,"type":"AIR"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"AIR"},{"x":12,"y":9,"type":"DIRT"},{"x":13,"y":9,"type":"DIRT"},{"x":14,"y":9,"type":"AIR"},{"x":15,"y":9,"type":"DIRT"},{"x":16,"y":9,"type":"AIR"},{"x":17,"y":9,"type":"DIRT"},{"x":18,"y":9,"type":"AIR"},{"x":19,"y":9,"type":"DIRT"},{"x":20,"y":9,"type":"DIRT"},{"x":21,"y":9,"type":"AIR"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"AIR"},{"x":24,"y":9,"type":"AIR"},{"x":25,"y":9,"type":"DIRT"},{"x":26,"y":9,"type":"AIR"},{"x":27,"y":9,"type":"AIR"},{"x":28,"y":9,"type":"DIRT"},{"x":29,"y":9,"type":"DIRT"},{"x":30,"y":9,"type":"AIR"},{"x":31,"y":9,"type":"DIRT"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"DIRT"},{"x":2,"y":10,"type":"AIR"},{"x":3,"y":10,"type":"AIR"},{"x":4,"y":10,"type":"DIRT"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"DIRT"},{"x":7,"y":10,"type":"DIRT"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"AIR"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"DIRT"},{"x":12,"y":10,"type":"DIRT"},{"x":13,"y":10,"type":"DIRT"},{"x":14,"y":10,"type":"AIR"},{"x":15,"y":10,"type":"AIR"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"AIR"},{"x":18,"y":10,"type":"AIR"},{"x":19,"y":10,"type":"DIRT"},{"x":20,"y":10,"type":"DIRT"},{"x":21,"y":10,"type":"DIRT"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"AIR"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"DIRT"},{"x":26,"y":10,"type":"DIRT"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"DIRT"},{"x":29,"y":10,"type":"AIR"},{"x":30,"y":10,"type":"AIR"},{"x":31,"y":10,"type":"DIRT"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"DIRT"},{"x":3,"y":11,"type":"AIR"},{"x":4,"y":11,"type":"AIR"},{"x":5,"y":11,"type":"AIR"},{"x":6,"y":11,"type":"AIR"},{"x":7,"y":11,"type":"AIR"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"AIR"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"AIR"},{"x":13,"y":11,"type":"DIRT"},{"x":14,"y":11,"type":"AIR"},{"x":15,"y":11,"type":"DIRT"},{"x":16,"y":11,"type":"DIRT"},{"x":17,"y":11,"type":"DIRT"},{"x":18,"y":11,"type":"AIR"},{"x":19,"y":11,"type":"DIRT"},{"x":20,"y":11,"type":"AIR"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"AIR"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"AIR"},{"x":26,"y":11,"type":"AIR"},{"x":27,"y":11,"type":"AIR"},{"x":28,"y":11,"type":"AIR"},{"x":29,"y":11,"type":"AIR"},{"x":30,"y":11,"type":"DIRT"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"DIRT"},{"x":1,"y":12,"type":"DIRT"},{"x":2,"y":12,"type":"DIRT"},{"x":3,"y":12,"type":"DIRT"},{"x":4,"y":12,"type":"DIRT"},{"x":5,"y":12,"type":"AIR"},{"x":6,"y":12,"type":"AIR"},{"x":7,"y":12,"type":"AIR"},{"x":8,"y":12,"type":"DIRT"},{"x":9,"y":12,"type":"DIRT"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"AIR"},{"x":13,"y":12,"type":"AIR"},{"x":14,"y":12,"type":"DIRT"},{"x":15,"y":12,"type":"DIRT"},{"x":16,"y":12,"type":"DIRT"},{"x":17,"y":12,"type":"DIRT"},{"x":18,"y":12,"type":"DIRT"},{"x":19,"y":12,"type":"AIR"},{"x":20,"y":12,"type":"AIR"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"DIRT"},{"x":24,"y":12,"type":"DIRT"},{"x":25,"y":12,"type":"AIR"},{"x":26,"y":12,"type":"AIR"},{"x":27,"y":12,"type":"AIR"},{"x":28,"y":12,"type":"DIRT"},{"x":29,"y":12,"type":"DIRT"},{"x":30,"y":12,"type":"DIRT"},{"x":31,"y":12,"type":"DIRT"},{"x":32,"y":12,"type":"DIRT"}],[{"x":0,"y":13,"type":"DIRT"},{"x":1,"y":13,"type":"DIRT"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"DIRT"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"AIR"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"AIR"},{"x":8,"y":13,"type":"DIRT"},{"x":9,"y":13,"type":"DIRT"},{"x":10,"y":13,"type":"AIR"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"AIR"},{"x":13,"y":13,"type":"AIR"},{"x":14,"y":13,"type":"DIRT"},{"x":15,"y":13,"type":"DIRT"},{"x":16,"y":13,"type":"DIRT"},{"x":17,"y":13,"type":"DIRT"},{"x":18,"y":13,"type":"DIRT"},{"x":19,"y":13,"type":"AIR"},{"x":20,"y":13,"type":"AIR"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"AIR"},{"x":23,"y":13,"type":"DIRT"},{"x":24,"y":13,"type":"DIRT"},{"x":25,"y":13,"type":"AIR"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"AIR"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"DIRT"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"DIRT"},{"x":32,"y":13,"type":"DIRT"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"AIR"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"AIR"},{"x":8,"y":14,"type":"DIRT"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"DIRT"},{"x":11,"y":14,"type":"AIR"},{"x":12,"y":14,"type":"AIR"},{"x":13,"y":14,"type":"AIR"},{"x":14,"y":14,"type":"AIR"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"DIRT"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"AIR"},{"x":19,"y":14,"type":"AIR"},{"x":20,"y":14,"type":"AIR"},{"x":21,"y":14,"type":"AIR"},{"x":22,"y":14,"type":"DIRT"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"DIRT"},{"x":25,"y":14,"type":"AIR"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"AIR"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"AIR"},{"x":5,"y":15,"type":"AIR"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"DIRT"},{"x":9,"y":15,"type":"DIRT"},{"x":10,"y":15,"type":"DIRT"},{"x":11,"y":15,"type":"DIRT"},{"x":12,"y":15,"type":"AIR"},{"x":13,"y":15,"type":"AIR"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"DIRT"},{"x":16,"y":15,"type":"DIRT"},{"x":17,"y":15,"type":"DIRT"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"AIR"},{"x":20,"y":15,"type":"AIR"},{"x":21,"y":15,"type":"DIRT"},{"x":22,"y":15,"type":"DIRT"},{"x":23,"y":15,"type":"DIRT"},{"x":24,"y":15,"type":"DIRT"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"AIR"},{"x":28,"y":15,"type":"AIR"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"DIRT"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"DIRT"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"AIR"},{"x":10,"y":16,"type":"AIR"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"AIR"},{"x":14,"y":16,"type":"AIR"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"DIRT"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"AIR"},{"x":19,"y":16,"type":"AIR"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"AIR"},{"x":23,"y":16,"type":"AIR"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"DIRT"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"DIRT"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"DIRT"},{"x":7,"y":17,"type":"AIR"},{"x":8,"y":17,"type":"AIR"},{"x":9,"y":17,"type":"AIR"},{"x":10,"y":17,"type":"AIR"},{"x":11,"y":17,"type":"DIRT"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"AIR"},{"x":14,"y":17,"type":"AIR"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"DIRT"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"AIR"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"DIRT"},{"x":22,"y":17,"type":"AIR"},{"x":23,"y":17,"type":"AIR"},{"x":24,"y":17,"type":"AIR"},{"x":25,"y":17,"type":"AIR"},{"x":26,"y":17,"type":"DIRT"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"DIRT"},{"x":6,"y":18,"type":"DIRT"},{"x":7,"y":18,"type":"AIR"},{"x":8,"y":18,"type":"AIR"},{"x":9,"y":18,"type":"AIR"},{"x":10,"y":18,"type":"DIRT"},{"x":11,"y":18,"type":"DIRT"},{"x":12,"y":18,"type":"DIRT"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"DIRT"},{"x":15,"y":18,"type":"DIRT"},{"x":16,"y":18,"type":"DIRT"},{"x":17,"y":18,"type":"DIRT"},{"x":18,"y":18,"type":"DIRT"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"DIRT"},{"x":21,"y":18,"type":"DIRT"},{"x":22,"y":18,"type":"DIRT"},{"x":23,"y":18,"type":"AIR"},{"x":24,"y":18,"type":"AIR"},{"x":25,"y":18,"type":"AIR"},{"x":26,"y":18,"type":"DIRT"},{"x":27,"y":18,"type":"DIRT"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"AIR"},{"x":3,"y":19,"type":"DIRT"},{"x":4,"y":19,"type":"DIRT"},{"x":5,"y":19,"type":"AIR"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"DIRT"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"DIRT"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"AIR"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"DIRT"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"AIR"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"DIRT"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"DIRT"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"AIR"},{"x":28,"y":19,"type":"DIRT"},{"x":29,"y":19,"type":"DIRT"},{"x":30,"y":19,"type":"AIR"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"DIRT"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"AIR"},{"x":3,"y":20,"type":"AIR"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"AIR"},{"x":6,"y":20,"type":"AIR"},{"x":7,"y":20,"type":"DIRT"},{"x":8,"y":20,"type":"DIRT"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"AIR"},{"x":11,"y":20,"type":"AIR"},{"x":12,"y":20,"type":"AIR"},{"x":13,"y":20,"type":"AIR"},{"x":14,"y":20,"type":"AIR"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"DIRT"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"AIR"},{"x":19,"y":20,"type":"AIR"},{"x":20,"y":20,"type":"AIR"},{"x":21,"y":20,"type":"AIR"},{"x":22,"y":20,"type":"AIR"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"DIRT"},{"x":25,"y":20,"type":"DIRT"},{"x":26,"y":20,"type":"AIR"},{"x":27,"y":20,"type":"AIR"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"AIR"},{"x":30,"y":20,"type":"AIR"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"DIRT"}],[{"x":0,"y":21,"type":"AIR"},{"x":1,"y":21,"type":"AIR"},{"x":2,"y":21,"type":"AIR"},{"x":3,"y":21,"type":"AIR"},{"x":4,"y":21,"type":"AIR"},{"x":5,"y":21,"type":"AIR"},{"x":6,"y":21,"type":"AIR"},{"x":7,"y":21,"type":"AIR"},{"x":8,"y":21,"type":"DIRT"},{"x":9,"y":21,"type":"DIRT"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"AIR"},{"x":12,"y":21,"type":"AIR"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"DIRT"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"AIR"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"DIRT"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"AIR"},{"x":21,"y":21,"type":"AIR"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"DIRT"},{"x":24,"y":21,"type":"DIRT"},{"x":25,"y":21,"type":"AIR"},{"x":26,"y":21,"type":"AIR"},{"x":27,"y":21,"type":"AIR"},{"x":28,"y":21,"type":"AIR"},{"x":29,"y":21,"type":"AIR"},{"x":30,"y":21,"type":"AIR"},{"x":31,"y":21,"type":"AIR"},{"x":32,"y":21,"type":"AIR"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"AIR"},{"x":2,"y":22,"type":"DIRT"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"AIR"},{"x":5,"y":22,"type":"AIR"},{"x":6,"y":22,"type":"AIR"},{"x":7,"y":22,"type":"AIR"},{"x":8,"y":22,"type":"AIR"},{"x":9,"y":22,"type":"DIRT"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"AIR"},{"x":13,"y":22,"type":"DIRT"},{"x":14,"y":22,"type":"DIRT"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"DIRT"},{"x":19,"y":22,"type":"DIRT"},{"x":20,"y":22,"type":"AIR"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"DIRT"},{"x":24,"y":22,"type":"AIR"},{"x":25,"y":22,"type":"AIR"},{"x":26,"y":22,"type":"AIR"},{"x":27,"y":22,"type":"AIR"},{"x":28,"y":22,"type":"AIR"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"DIRT"},{"x":31,"y":22,"type":"AIR"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"AIR"},{"x":2,"y":23,"type":"DIRT"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"AIR"},{"x":5,"y":23,"type":"AIR"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"AIR"},{"x":8,"y":23,"type":"AIR"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"AIR"},{"x":13,"y":23,"type":"DIRT"},{"x":14,"y":23,"type":"DIRT"},{"x":15,"y":23,"type":"AIR"},{"x":16,"y":23,"type":"DIRT"},{"x":17,"y":23,"type":"AIR"},{"x":18,"y":23,"type":"DIRT"},{"x":19,"y":23,"type":"DIRT"},{"x":20,"y":23,"type":"AIR"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"AIR"},{"x":25,"y":23,"type":"AIR"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"AIR"},{"x":28,"y":23,"type":"AIR"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"DIRT"},{"x":31,"y":23,"type":"AIR"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"AIR"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"AIR"},{"x":5,"y":24,"type":"AIR"},{"x":6,"y":24,"type":"DIRT"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"AIR"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"AIR"},{"x":11,"y":24,"type":"AIR"},{"x":12,"y":24,"type":"AIR"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"AIR"},{"x":15,"y":24,"type":"AIR"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"AIR"},{"x":18,"y":24,"type":"AIR"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"AIR"},{"x":21,"y":24,"type":"AIR"},{"x":22,"y":24,"type":"AIR"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"AIR"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"DIRT"},{"x":27,"y":24,"type":"AIR"},{"x":28,"y":24,"type":"AIR"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"AIR"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"AIR"},{"x":3,"y":25,"type":"AIR"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"AIR"},{"x":6,"y":25,"type":"AIR"},{"x":7,"y":25,"type":"AIR"},{"x":8,"y":25,"type":"AIR"},{"x":9,"y":25,"type":"AIR"},{"x":10,"y":25,"type":"AIR"},{"x":11,"y":25,"type":"AIR"},{"x":12,"y":25,"type":"AIR"},{"x":13,"y":25,"type":"AIR"},{"x":14,"y":25,"type":"AIR"},{"x":15,"y":25,"type":"AIR"},{"x":16,"y":25,"type":"DIRT"},{"x":17,"y":25,"type":"AIR"},{"x":18,"y":25,"type":"AIR"},{"x":19,"y":25,"type":"AIR"},{"x":20,"y":25,"type":"AIR"},{"x":21,"y":25,"type":"AIR"},{"x":22,"y":25,"type":"AIR"},{"x":23,"y":25,"type":"AIR"},{"x":24,"y":25,"type":"AIR"},{"x":25,"y":25,"type":"AIR"},{"x":26,"y":25,"type":"AIR"},{"x":27,"y":25,"type":"AIR"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"AIR"},{"x":30,"y":25,"type":"AIR"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"AIR"},{"x":5,"y":26,"type":"AIR"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"DIRT"},{"x":14,"y":26,"type":"DIRT"},{"x":15,"y":26,"type":"AIR"},{"x":16,"y":26,"type":"DIRT"},{"x":17,"y":26,"type":"AIR"},{"x":18,"y":26,"type":"DIRT"},{"x":19,"y":26,"type":"DIRT"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"AIR"},{"x":28,"y":26,"type":"AIR"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"AIR"},{"x":5,"y":27,"type":"AIR"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"DIRT"},{"x":14,"y":27,"type":"DIRT"},{"x":15,"y":27,"type":"AIR"},{"x":16,"y":27,"type":"AIR"},{"x":17,"y":27,"type":"AIR"},{"x":18,"y":27,"type":"DIRT"},{"x":19,"y":27,"type":"DIRT"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"AIR"},{"x":28,"y":27,"type":"AIR"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"DIRT"},{"x":5,"y":28,"type":"DIRT"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"AIR"},{"x":12,"y":28,"type":"AIR"},{"x":13,"y":28,"type":"DIRT"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"AIR"},{"x":16,"y":28,"type":"AIR"},{"x":17,"y":28,"type":"AIR"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"DIRT"},{"x":20,"y":28,"type":"AIR"},{"x":21,"y":28,"type":"AIR"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"DIRT"},{"x":28,"y":28,"type":"DIRT"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"AIR"},{"x":12,"y":29,"type":"DIRT"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"DIRT"},{"x":15,"y":29,"type":"AIR"},{"x":16,"y":29,"type":"DIRT"},{"x":17,"y":29,"type":"AIR"},{"x":18,"y":29,"type":"DIRT"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"DIRT"},{"x":21,"y":29,"type":"AIR"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"DIRT"},{"x":12,"y":30,"type":"DIRT"},{"x":13,"y":30,"type":"DIRT"},{"x":14,"y":30,"type":"AIR"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"DIRT"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"AIR"},{"x":19,"y":30,"type":"DIRT"},{"x":20,"y":30,"type":"DIRT"},{"x":21,"y":30,"type":"DIRT"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"AIR"},{"x":9,"y":31,"type":"AIR"},{"x":10,"y":31,"type":"AIR"},{"x":11,"y":31,"type":"AIR"},{"x":12,"y":31,"type":"DIRT"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"DIRT"},{"x":16,"y":31,"type":"DIRT"},{"x":17,"y":31,"type":"DIRT"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"DIRT"},{"x":21,"y":31,"type":"AIR"},{"x":22,"y":31,"type":"AIR"},{"x":23,"y":31,"type":"AIR"},{"x":24,"y":31,"type":"AIR"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"AIR"},{"x":12,"y":32,"type":"DIRT"},{"x":13,"y":32,"type":"DIRT"},{"x":14,"y":32,"type":"DIRT"},{"x":15,"y":32,"type":"DIRT"},{"x":16,"y":32,"type":"DIRT"},{"x":17,"y":32,"type":"DIRT"},{"x":18,"y":32,"type":"DIRT"},{"x":19,"y":32,"type":"DIRT"},{"x":20,"y":32,"type":"DIRT"},{"x":21,"y":32,"type":"AIR"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.31.16/A-log.csv b/2019-worms/tests/replays/2019.08.19.21.31.16/A-log.csv
new file mode 100644
index 0000000..14e4b3c
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.31.16/A-log.csv
@@ -0,0 +1,274 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,24,4,100,24,28,100,1,16
+2,move,"move 23 3",1,121,350,150,23,3,100,24,28,100,1,16
+3,move,"move 23 28",2,126,350,150,23,3,100,23,28,100,1,16
+4,move,"move 2 16",3,131,350,150,23,3,100,23,28,100,2,16
+5,dig,"dig 22 3",1,138,350,150,23,3,100,23,28,100,2,16
+6,dig,"dig 22 28",2,145,350,150,23,3,100,23,28,100,2,16
+7,dig,"dig 3 16",3,152,350,150,23,3,100,23,28,100,2,16
+8,move,"move 22 3",1,157,350,150,22,3,100,23,28,100,2,16
+9,move,"move 22 28",2,162,350,150,22,3,100,22,28,100,2,16
+10,move,"move 2 15",3,167,350,150,22,3,100,22,28,100,2,15
+11,move,"move 23 3",1,172,350,150,23,3,100,22,28,100,2,15
+12,move,"move 23 28",2,177,350,150,23,3,100,23,28,100,2,15
+13,move,"move 2 16",3,182,350,150,23,3,100,23,28,100,2,16
+14,move,"move 22 3",1,187,350,150,22,3,100,23,28,100,2,16
+15,move,"move 22 28",2,192,350,150,22,3,100,22,28,100,2,16
+16,move,"move 2 15",3,197,350,150,22,3,100,22,28,100,2,15
+17,move,"move 23 3",1,202,350,150,23,3,100,22,28,100,2,15
+18,move,"move 23 28",2,207,350,150,23,3,100,23,28,100,2,15
+19,move,"move 2 16",3,212,350,150,23,3,100,23,28,100,2,16
+20,move,"move 22 3",1,217,350,150,22,3,100,23,28,100,2,16
+21,move,"move 22 28",2,222,350,150,22,3,100,22,28,100,2,16
+22,move,"move 2 15",3,227,350,150,22,3,100,22,28,100,2,15
+23,move,"move 23 3",1,232,350,150,23,3,100,22,28,100,2,15
+24,move,"move 23 28",2,237,350,150,23,3,100,23,28,100,2,15
+25,move,"move 2 16",3,242,350,150,23,3,100,23,28,100,2,16
+26,move,"move 22 3",1,247,350,150,22,3,100,23,28,100,2,16
+27,move,"move 22 28",2,252,350,150,22,3,100,22,28,100,2,16
+28,move,"move 2 15",3,257,350,150,22,3,100,22,28,100,2,15
+29,move,"move 23 3",1,262,350,150,23,3,100,22,28,100,2,15
+30,move,"move 23 28",2,267,350,150,23,3,100,23,28,100,2,15
+31,move,"move 2 16",3,272,350,150,23,3,100,23,28,100,2,16
+32,move,"move 22 3",1,277,350,150,22,3,100,23,28,100,2,16
+33,move,"move 22 28",2,282,350,150,22,3,100,22,28,100,2,16
+34,move,"move 2 15",3,287,350,150,22,3,100,22,28,100,2,15
+35,move,"move 23 3",1,292,350,150,23,3,100,22,28,100,2,15
+36,move,"move 23 28",2,297,350,150,23,3,100,23,28,100,2,15
+37,move,"move 2 16",3,302,350,150,23,3,100,23,28,100,2,16
+38,move,"move 22 3",1,307,350,150,22,3,100,23,28,100,2,16
+39,move,"move 22 28",2,312,350,150,22,3,100,22,28,100,2,16
+40,move,"move 2 15",3,317,350,150,22,3,100,22,28,100,2,15
+41,move,"move 23 3",1,322,350,150,23,3,100,22,28,100,2,15
+42,move,"move 23 28",2,327,350,150,23,3,100,23,28,100,2,15
+43,move,"move 1 16",3,332,350,150,23,3,100,23,28,100,1,16
+44,dig,"dig 22 2",1,339,350,150,23,3,100,23,28,100,1,16
+45,move,"move 23 27",2,344,350,150,23,3,100,23,27,100,1,16
+46,move,"move 1 15",3,349,350,150,23,3,100,23,27,100,1,15
+47,dig,"dig 22 4",1,356,350,150,23,3,100,23,27,100,1,15
+48,move,"move 22 28",2,361,350,150,23,3,100,22,28,100,1,15
+49,move,"move 2 16",3,366,350,150,23,3,100,22,28,100,2,16
+50,move,"move 22 3",1,371,350,150,22,3,100,22,28,100,2,16
+51,move,"move 23 28",2,376,350,150,22,3,100,23,28,100,2,16
+52,move,"move 2 15",3,381,350,150,22,3,100,23,28,100,2,15
+53,move,"move 23 3",1,386,350,150,23,3,100,23,28,100,2,15
+54,move,"move 22 28",2,391,350,150,23,3,100,22,28,100,2,15
+55,move,"move 2 16",3,396,350,150,23,3,100,22,28,100,2,16
+56,move,"move 22 3",1,401,350,150,22,3,100,22,28,100,2,16
+57,move,"move 23 28",2,406,350,150,22,3,100,23,28,100,2,16
+58,move,"move 2 15",3,411,350,150,22,3,100,23,28,100,2,15
+59,move,"move 23 3",1,416,350,150,23,3,100,23,28,100,2,15
+60,move,"move 22 28",2,421,350,150,23,3,100,22,28,100,2,15
+61,move,"move 2 16",3,426,350,150,23,3,100,22,28,100,2,16
+62,move,"move 22 3",1,431,350,150,22,3,100,22,28,100,2,16
+63,move,"move 23 28",2,436,350,150,22,3,100,23,28,100,2,16
+64,nothing,"nothing "Player chose to do nothing"",3,436,350,150,22,3,100,23,28,100,2,16
+65,move,"move 23 3",1,441,350,150,23,3,100,23,28,100,2,16
+66,move,"move 24 29",2,446,350,150,23,3,100,24,29,100,2,16
+67,move,"move 1 17",3,451,350,150,23,3,100,24,29,100,1,17
+68,move,"move 24 3",1,456,350,150,24,3,100,24,29,100,1,17
+69,dig,"dig 24 30",2,463,350,150,24,3,100,24,29,100,1,17
+70,dig,"dig 1 18",3,470,350,150,24,3,100,24,29,100,1,17
+71,move,"move 23 3",1,475,350,150,23,3,100,24,29,100,1,17
+72,move,"move 25 28",2,480,350,150,23,3,100,25,28,100,1,17
+73,move,"move 1 18",3,485,350,150,23,3,100,25,28,100,1,18
+74,move,"move 22 2",1,490,350,150,22,2,100,25,28,100,1,18
+75,dig,"dig 26 29",2,497,350,150,22,2,100,25,28,100,1,18
+76,move,"move 0 19",3,502,350,150,22,2,100,25,28,100,0,19
+77,nothing,"nothing "Player chose to do nothing"",1,502,350,150,22,2,100,25,28,100,0,19
+78,move,"move 24 29",2,507,350,150,22,2,100,24,29,100,0,19
+79,nothing,"nothing "Player chose to do nothing"",3,507,350,150,22,2,100,24,29,100,0,19
+80,dig,"dig 21 1",1,514,350,150,22,2,100,24,29,100,0,19
+81,move,"move 23 28",2,519,350,150,22,2,100,23,28,100,0,19
+82,dig,"dig 0 20",3,526,350,150,22,2,100,23,28,100,0,19
+83,move,"move 21 1",1,531,350,150,21,1,100,23,28,100,0,19
+84,move,"move 24 27",2,536,350,150,21,1,100,24,27,100,0,19
+85,dig,"dig 0 18",3,543,350,150,21,1,100,24,27,100,0,19
+86,nothing,"nothing "Player chose to do nothing"",1,543,350,150,21,1,100,24,27,100,0,19
+87,dig,"dig 23 26",2,550,350,150,21,1,100,24,27,100,0,19
+88,move,"move 0 20",3,555,350,150,21,1,100,24,27,100,0,20
+89,nothing,"nothing "Player chose to do nothing"",1,555,350,150,21,1,100,24,27,100,0,20
+90,move,"move 24 28",2,560,350,150,21,1,100,24,28,100,0,20
+91,move,"move 1 21",3,565,350,150,21,1,100,24,28,100,1,21
+92,dig,"dig 22 1",1,572,350,150,21,1,100,24,28,100,1,21
+93,move,"move 23 28",2,577,350,150,21,1,100,23,28,100,1,21
+94,move,"move 1 22",3,582,350,150,21,1,100,23,28,100,1,22
+95,dig,"dig 20 2",1,589,350,150,21,1,100,23,28,100,1,22
+96,dig,"dig 22 29",2,596,350,150,21,1,100,23,28,100,1,22
+97,dig,"dig 2 23",3,603,350,150,21,1,100,23,28,100,1,22
+98,dig,"dig 21 2",1,610,350,150,21,1,100,23,28,100,1,22
+99,shoot,"shoot S",1,620,330,130,21,1,100,23,28,100,1,22
+100,shoot,"shoot S",1,633,322,122,21,1,100,23,28,100,1,22
+101,shoot,"shoot S",1,641,299,102,21,1,100,23,28,97,1,22
+102,shoot,"shoot S",1,654,288,94,21,1,100,23,28,94,1,22
+103,shoot,"shoot S",1,662,265,74,21,1,100,23,28,91,1,22
+104,move,"move 22 28",2,662,251,63,21,1,100,22,28,88,1,22
+105,move,"move 2 21",3,663,237,52,21,1,100,22,28,85,2,21
+106,move,"move 22 2",1,667,234,49,22,2,100,22,28,85,2,21
+107,move,"move 23 29",2,672,234,49,22,2,100,23,29,85,2,21
+108,move,"move 3 21",3,677,234,49,22,2,100,23,29,85,3,21
+109,shoot,"shoot S",1,693,234,49,22,2,100,23,29,85,3,21
+110,move,"move 22 28",2,698,234,49,22,2,100,22,28,85,3,21
+111,move,"move 3 20",3,703,234,49,22,2,100,22,28,85,3,20
+112,move,"move 21 2",1,703,234,49,22,2,100,22,28,85,3,20
+113,dig,"dig 22 27",2,710,234,49,22,2,100,22,28,85,3,20
+114,move,"move 4 21",3,714,231,46,22,2,100,22,28,85,4,21
+115,move,"move 21 2",1,713,228,43,22,2,100,22,28,85,4,21
+116,dig,"dig 21 27",2,719,225,40,22,2,100,22,28,85,4,21
+117,move,"move 5 22",3,723,222,37,22,2,100,22,28,85,5,22
+118,shoot,"shoot S",1,722,219,34,22,2,100,22,28,85,5,22
+119,move,"move 21 27",2,726,216,31,22,2,100,21,27,85,5,22
+120,move,"move 6 22",3,730,213,28,22,2,100,21,27,85,6,22
+121,move,"move 21 3",1,729,210,25,22,2,100,21,27,85,6,22
+122,dig,"dig 22 26",2,735,207,22,22,2,100,21,27,85,6,22
+123,move,"move 5 22",3,739,204,19,22,2,100,21,27,85,5,22
+124,move,"move 21 3",1,738,201,16,22,2,100,21,27,85,5,22
+125,nothing,"nothing "Player chose to do nothing"",2,737,198,13,22,2,100,21,27,85,5,22
+126,move,"move 6 21",3,741,195,10,22,2,100,21,27,85,6,21
+127,move,"move 23 3",1,740,192,7,22,2,100,21,27,85,6,21
+128,move,"move 22 26",2,744,189,4,22,2,100,22,26,85,6,21
+129,nothing,"nothing "Player chose to do nothing"",3,743,186,1,22,2,100,22,26,85,6,21
+130,shoot,"shoot S",1,758,185,-10,22,2,100,22,26,85,6,21
+131,move,"move 21 26",2,763,185,-10,22,2,100,21,26,85,6,21
+132,dig,"dig 7 20",3,770,185,-10,22,2,100,21,26,85,6,21
+133,dig,"dig 20 27",2,777,185,-10,22,2,100,21,26,85,6,21
+134,move,"move 5 20",3,782,185,-10,22,2,100,21,26,85,5,20
+135,move,"move 21 25",2,787,185,-10,22,2,100,21,25,85,5,20
+136,move,"move 4 20",3,792,185,-10,22,2,100,21,25,85,4,20
+137,move,"move 20 25",2,797,185,-10,22,2,100,20,25,85,4,20
+138,nothing,"nothing "Player chose to do nothing"",3,797,185,-10,22,2,100,20,25,85,4,20
+139,dig,"dig 19 26",2,804,185,-10,22,2,100,20,25,85,4,20
+140,nothing,"nothing "Player chose to do nothing"",3,804,185,-10,22,2,100,20,25,85,4,20
+141,move,"move 21 25",2,809,185,-10,22,2,100,21,25,85,4,20
+142,nothing,"nothing "Player chose to do nothing"",3,809,185,-10,22,2,100,21,25,85,4,20
+143,move,"move 21 26",2,814,185,-10,22,2,100,21,26,85,4,20
+144,move,"move 5 20",3,819,185,-10,22,2,100,21,26,85,5,20
+145,move,"move 20 27",2,824,185,-10,22,2,100,20,27,85,5,20
+146,move,"move 6 21",3,829,185,-10,22,2,100,20,27,85,6,21
+147,dig,"dig 20 26",2,836,185,-10,22,2,100,20,27,85,6,21
+148,move,"move 5 22",3,841,185,-10,22,2,100,20,27,85,5,22
+149,move,"move 19 26",2,846,185,-10,22,2,100,19,26,85,5,22
+150,move,"move 6 22",3,851,185,-10,22,2,100,19,26,85,6,22
+151,dig,"dig 19 27",2,858,185,-10,22,2,100,19,26,85,6,22
+152,move,"move 7 21",3,863,185,-10,22,2,100,19,26,85,7,21
+153,move,"move 20 25",2,868,185,-10,22,2,100,20,25,85,7,21
+154,move,"move 6 22",3,873,185,-10,22,2,100,20,25,85,6,22
+155,move,"move 19 24",2,878,185,-10,22,2,100,19,24,85,6,22
+156,move,"move 5 21",3,883,185,-10,22,2,100,19,24,85,5,21
+157,nothing,"nothing "Player chose to do nothing"",2,883,185,-10,22,2,100,19,24,85,5,21
+158,move,"move 6 20",3,888,185,-10,22,2,100,19,24,85,6,20
+159,dig,"dig 18 23",2,895,185,-10,22,2,100,19,24,85,6,20
+160,move,"move 5 21",3,900,185,-10,22,2,100,19,24,85,5,21
+161,move,"move 20 23",2,905,185,-10,22,2,100,20,23,85,5,21
+162,move,"move 6 22",3,910,185,-10,22,2,100,20,23,85,6,22
+163,move,"move 20 22",2,915,185,-10,22,2,100,20,22,85,6,22
+164,move,"move 7 21",3,920,185,-10,22,2,100,20,22,85,7,21
+165,move,"move 21 21",2,925,185,-10,22,2,100,21,21,85,7,21
+166,nothing,"nothing "Player chose to do nothing"",3,925,185,-10,22,2,100,21,21,85,7,21
+167,banana,"banana 20 16",2,967,185,-10,22,2,100,21,21,85,7,21
+168,move,"move 6 21",3,972,185,-10,22,2,100,21,21,85,6,21
+169,move,"move 21 20",2,977,185,-10,22,2,100,21,20,85,6,21
+170,nothing,"nothing "Player chose to do nothing"",3,977,185,-10,22,2,100,21,20,85,6,21
+171,banana,"banana 21 15",2,1043,185,-10,22,2,100,21,20,85,6,21
+172,move,"move 6 20",3,1048,185,-10,22,2,100,21,20,85,6,20
+173,banana,"banana 21 16",2,1121,185,-10,22,2,100,21,20,85,6,20
+174,dig,"dig 7 19",3,1128,185,-10,22,2,100,21,20,85,6,20
+175,nothing,"nothing "Player chose to do nothing"",2,1128,185,-10,22,2,100,21,20,85,6,20
+176,move,"move 7 19",3,1133,185,-10,22,2,100,21,20,85,7,19
+177,move,"move 21 21",2,1138,185,-10,22,2,100,21,21,85,7,19
+178,move,"move 8 18",3,1143,185,-10,22,2,100,21,21,85,8,18
+179,move,"move 20 22",2,1148,185,-10,22,2,100,20,22,85,8,18
+180,move,"move 9 17",3,1153,185,-10,22,2,100,20,22,85,9,17
+181,move,"move 20 21",2,1158,185,-10,22,2,100,20,21,85,9,17
+182,move,"move 9 16",3,1163,185,-10,22,2,100,20,21,85,9,16
+183,shoot,"shoot N",2,1179,185,-10,22,2,100,20,21,85,9,16
+184,dig,"dig 10 15",3,1186,185,-10,22,2,100,20,21,85,9,16
+185,shoot,"shoot N",2,1202,185,-10,22,2,100,20,21,85,9,16
+186,move,"move 10 16",3,1207,185,-10,22,2,100,20,21,85,10,16
+187,move,"move 21 21",2,1212,185,-10,22,2,100,21,21,85,10,16
+188,dig,"dig 11 17",3,1219,185,-10,22,2,100,21,21,85,10,16
+189,shoot,"shoot N",2,1235,185,-10,22,2,100,21,21,85,10,16
+190,dig,"dig 11 15",3,1242,185,-10,22,2,100,21,21,85,10,16
+191,shoot,"shoot N",2,1258,185,-10,22,2,100,21,21,85,10,16
+192,dig,"dig 9 15",3,1265,185,-10,22,2,100,21,21,85,10,16
+193,move,"move 22 20",2,1270,185,-10,22,2,100,22,20,85,10,16
+194,dig,"dig 11 16",3,1277,185,-10,22,2,100,22,20,85,10,16
+195,move,"move 23 19",2,1282,185,-10,22,2,100,23,19,85,10,16
+196,move,"move 11 17",3,1287,185,-10,22,2,100,23,19,85,11,17
+197,shoot,"shoot W",2,1303,185,-10,22,2,100,23,19,85,11,17
+198,dig,"dig 12 16",3,1310,185,-10,22,2,100,23,19,85,11,17
+199,move,"move 23 18",2,1315,185,-10,22,2,100,23,18,85,11,17
+200,nothing,"nothing "Player chose to do nothing"",3,1315,185,-10,22,2,100,23,18,85,11,17
+201,move,"move 23 17",2,1320,185,-10,22,2,100,23,17,85,11,17
+202,dig,"dig 12 18",3,1327,185,-10,22,2,100,23,17,85,11,17
+203,shoot,"shoot S",2,1343,185,-10,22,2,100,23,17,85,11,17
+204,dig,"dig 12 17",3,1350,185,-10,22,2,100,23,17,85,11,17
+205,move,"move 24 16",2,1355,185,-10,22,2,100,24,16,85,11,17
+206,move,"move 10 16",3,1360,185,-10,22,2,100,24,16,85,10,16
+207,shoot,"shoot W",2,1376,185,-10,22,2,100,24,16,85,10,16
+208,move,"move 9 15",3,1381,185,-10,22,2,100,24,16,85,9,15
+209,move,"move 23 15",2,1386,185,-10,22,2,100,23,15,85,9,15
+210,dig,"dig 8 15",3,1393,185,-10,22,2,100,23,15,85,9,15
+211,move,"move 22 14",2,1398,185,-10,22,2,100,22,14,85,9,15
+212,move,"move 10 15",3,1403,185,-10,22,2,100,22,14,85,10,15
+213,shoot,"shoot S",2,1419,185,-10,22,2,100,22,14,85,10,15
+214,nothing,"nothing "Player chose to do nothing"",3,1419,185,-10,22,2,100,22,14,85,10,15
+215,move,"move 21 13",2,1424,185,-10,22,2,100,21,13,85,10,15
+216,move,"move 11 15",3,1429,185,-10,22,2,100,21,13,85,11,15
+217,nothing,"nothing "Player chose to do nothing"",2,1429,185,-10,22,2,100,21,13,85,11,15
+218,move,"move 12 15",3,1434,185,-10,22,2,100,21,13,85,12,15
+219,shoot,"shoot S",2,1450,185,-10,22,2,100,21,13,85,12,15
+220,move,"move 12 16",3,1455,185,-10,22,2,100,21,13,85,12,16
+221,move,"move 22 12",2,1460,185,-10,22,2,100,22,12,85,12,16
+222,move,"move 12 17",3,1465,185,-10,22,2,100,22,12,85,12,17
+223,move,"move 22 11",2,1470,185,-10,22,2,100,22,11,85,12,17
+224,dig,"dig 13 18",3,1477,185,-10,22,2,100,22,11,85,12,17
+225,shoot,"shoot S",2,1493,185,-10,22,2,100,22,11,85,12,17
+226,nothing,"nothing "Player chose to do nothing"",3,1493,185,-10,22,2,100,22,11,85,12,17
+227,move,"move 21 11",2,1498,185,-10,22,2,100,21,11,85,12,17
+228,nothing,"nothing "Player chose to do nothing"",3,1498,185,-10,22,2,100,21,11,85,12,17
+229,move,"move 20 10",2,1503,185,-10,22,2,100,20,10,85,12,17
+230,move,"move 11 16",3,1508,185,-10,22,2,100,20,10,85,11,16
+231,nothing,"nothing "Player chose to do nothing"",2,1508,185,-10,22,2,100,20,10,85,11,16
+232,move,"move 10 16",3,1513,185,-10,22,2,100,20,10,85,10,16
+233,move,"move 19 10",2,1518,185,-10,22,2,100,19,10,85,10,16
+234,move,"move 9 17",3,1523,185,-10,22,2,100,19,10,85,9,17
+235,move,"move 18 10",2,1528,185,-10,22,2,100,18,10,85,9,17
+236,move,"move 10 16",3,1533,185,-10,22,2,100,18,10,85,10,16
+237,move,"move 17 10",2,1538,185,-10,22,2,100,17,10,85,10,16
+238,move,"move 9 15",3,1543,185,-10,22,2,100,17,10,85,9,15
+239,shoot,"shoot SE",2,1559,185,-10,22,2,100,17,10,85,9,15
+240,move,"move 10 16",3,1562,177,-10,22,2,92,17,10,85,10,16
+241,shoot,"shoot SE",2,1575,169,-10,22,2,84,17,10,85,10,16
+242,move,"move 11 15",3,1580,169,-10,22,2,84,17,10,85,11,15
+243,shoot,"shoot SE",2,1593,161,-10,22,2,76,17,10,85,11,15
+244,move,"move 11 14",3,1596,153,-10,22,2,68,17,10,85,11,14
+245,shoot,"shoot SE",2,1612,153,-10,22,2,68,17,10,85,11,14
+246,dig,"dig 10 14",3,1616,145,-10,22,2,60,17,10,85,11,14
+247,shoot,"shoot SE",2,1629,137,-10,22,2,52,17,10,85,11,14
+248,move,"move 11 13",3,1634,137,-10,22,2,52,17,10,85,11,13
+249,shoot,"shoot SE",2,1648,129,-10,22,2,44,17,10,85,11,13
+250,move,"move 12 13",3,1650,121,-10,22,2,36,17,10,85,12,13
+251,shoot,"shoot SE",2,1666,121,-10,22,2,36,17,10,85,12,13
+252,move,"move 11 14",3,1668,113,-10,22,2,28,17,10,85,11,14
+253,shoot,"shoot SE",2,1682,105,-10,22,2,20,17,10,85,11,14
+254,move,"move 12 15",3,1687,105,-10,22,2,20,17,10,85,12,15
+255,move,"move 16 10",2,1692,105,-10,22,2,20,16,10,85,12,15
+256,move,"move 13 14",3,1697,105,-10,22,2,20,16,10,85,13,14
+257,dig,"dig 15 11",2,1701,97,-10,22,2,12,16,10,85,13,14
+258,move,"move 12 14",3,1702,86,-10,22,2,1,16,10,85,12,14
+259,shoot,"shoot E",2,1718,85,-10,22,2,-10,16,10,85,12,14
+260,move,"move 13 15",3,1723,85,-10,22,2,-10,16,10,85,13,15
+261,move,"move 13 16",3,1728,85,-10,22,2,-10,16,10,85,13,16
+262,move,"move 12 17",3,1733,85,-10,22,2,-10,16,10,85,12,17
+263,move,"move 12 18",3,1738,85,-10,22,2,-10,16,10,85,12,18
+264,move,"move 13 19",3,1743,85,-10,22,2,-10,16,10,85,13,19
+265,move,"move 14 20",3,1748,85,-10,22,2,-10,16,10,85,14,20
+266,move,"move 15 20",3,1753,85,-10,22,2,-10,16,10,85,15,20
+267,move,"move 16 20",3,1758,85,-10,22,2,-10,16,10,85,16,20
+268,move,"move 15 19",3,1763,85,-10,22,2,-10,16,10,85,15,19
+269,dig,"dig 16 18",3,1770,85,-10,22,2,-10,16,10,85,15,19
+270,move,"move 16 18",3,1775,85,-10,22,2,-10,16,10,85,16,18
+271,move,"move 15 18",3,1780,85,-10,22,2,-10,16,10,85,15,18
+272,move,"move 14 17",3,1785,85,-10,22,2,-10,16,10,85,14,17
+273,shoot,"shoot E",3,1798,77,-10,22,2,-10,16,10,77,14,17
diff --git a/2019-worms/tests/replays/2019.08.19.21.31.16/B-init.json b/2019-worms/tests/replays/2019.08.19.21.31.16/B-init.json
new file mode 100644
index 0000000..373ec6e
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.31.16/B-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":2,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":1,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"DIRT"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"AIR"},{"x":15,"y":0,"type":"AIR"},{"x":16,"y":0,"type":"DIRT"},{"x":17,"y":0,"type":"AIR"},{"x":18,"y":0,"type":"AIR"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"DIRT"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"DIRT"},{"x":10,"y":1,"type":"DIRT"},{"x":11,"y":1,"type":"DIRT"},{"x":12,"y":1,"type":"DIRT"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"AIR"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"DIRT"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"AIR"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"DIRT"},{"x":21,"y":1,"type":"DIRT"},{"x":22,"y":1,"type":"DIRT"},{"x":23,"y":1,"type":"DIRT"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"DIRT"},{"x":14,"y":2,"type":"DIRT"},{"x":15,"y":2,"type":"AIR"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"AIR"},{"x":18,"y":2,"type":"DIRT"},{"x":19,"y":2,"type":"DIRT"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"AIR"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"DIRT"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"DIRT"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"AIR"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"AIR"},{"x":5,"y":4,"type":"AIR"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"AIR"},{"x":12,"y":4,"type":"AIR"},{"x":13,"y":4,"type":"AIR"},{"x":14,"y":4,"type":"AIR"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"AIR"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"AIR"},{"x":19,"y":4,"type":"AIR"},{"x":20,"y":4,"type":"AIR"},{"x":21,"y":4,"type":"AIR"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"AIR"},{"x":28,"y":4,"type":"AIR"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"AIR"},{"x":5,"y":5,"type":"AIR"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"AIR"},{"x":13,"y":5,"type":"AIR"},{"x":14,"y":5,"type":"AIR"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"AIR"},{"x":19,"y":5,"type":"AIR"},{"x":20,"y":5,"type":"AIR"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"AIR"},{"x":28,"y":5,"type":"AIR"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"AIR"},{"x":4,"y":6,"type":"AIR"},{"x":5,"y":6,"type":"AIR"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"DIRT"},{"x":12,"y":6,"type":"AIR"},{"x":13,"y":6,"type":"AIR"},{"x":14,"y":6,"type":"AIR"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"AIR"},{"x":19,"y":6,"type":"AIR"},{"x":20,"y":6,"type":"AIR"},{"x":21,"y":6,"type":"DIRT"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"AIR"},{"x":28,"y":6,"type":"AIR"},{"x":29,"y":6,"type":"AIR"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"AIR"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"AIR"},{"x":5,"y":7,"type":"DIRT"},{"x":6,"y":7,"type":"AIR"},{"x":7,"y":7,"type":"AIR"},{"x":8,"y":7,"type":"AIR"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"AIR"},{"x":12,"y":7,"type":"AIR"},{"x":13,"y":7,"type":"DIRT"},{"x":14,"y":7,"type":"AIR"},{"x":15,"y":7,"type":"AIR"},{"x":16,"y":7,"type":"AIR"},{"x":17,"y":7,"type":"AIR"},{"x":18,"y":7,"type":"AIR"},{"x":19,"y":7,"type":"DIRT"},{"x":20,"y":7,"type":"AIR"},{"x":21,"y":7,"type":"AIR"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"AIR"},{"x":25,"y":7,"type":"AIR"},{"x":26,"y":7,"type":"AIR"},{"x":27,"y":7,"type":"DIRT"},{"x":28,"y":7,"type":"AIR"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"AIR"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"AIR"},{"x":2,"y":8,"type":"AIR"},{"x":3,"y":8,"type":"DIRT"},{"x":4,"y":8,"type":"DIRT"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"AIR"},{"x":7,"y":8,"type":"AIR"},{"x":8,"y":8,"type":"AIR"},{"x":9,"y":8,"type":"AIR"},{"x":10,"y":8,"type":"AIR"},{"x":11,"y":8,"type":"AIR"},{"x":12,"y":8,"type":"AIR"},{"x":13,"y":8,"type":"DIRT"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"AIR"},{"x":16,"y":8,"type":"AIR"},{"x":17,"y":8,"type":"AIR"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"DIRT"},{"x":20,"y":8,"type":"AIR"},{"x":21,"y":8,"type":"AIR"},{"x":22,"y":8,"type":"AIR"},{"x":23,"y":8,"type":"AIR"},{"x":24,"y":8,"type":"AIR"},{"x":25,"y":8,"type":"AIR"},{"x":26,"y":8,"type":"AIR"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"DIRT"},{"x":29,"y":8,"type":"DIRT"},{"x":30,"y":8,"type":"AIR"},{"x":31,"y":8,"type":"AIR"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"DIRT"},{"x":2,"y":9,"type":"AIR"},{"x":3,"y":9,"type":"DIRT"},{"x":4,"y":9,"type":"DIRT"},{"x":5,"y":9,"type":"AIR"},{"x":6,"y":9,"type":"AIR"},{"x":7,"y":9,"type":"DIRT"},{"x":8,"y":9,"type":"AIR"},{"x":9,"y":9,"type":"AIR"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"AIR"},{"x":12,"y":9,"type":"DIRT"},{"x":13,"y":9,"type":"DIRT"},{"x":14,"y":9,"type":"AIR"},{"x":15,"y":9,"type":"DIRT"},{"x":16,"y":9,"type":"AIR"},{"x":17,"y":9,"type":"DIRT"},{"x":18,"y":9,"type":"AIR"},{"x":19,"y":9,"type":"DIRT"},{"x":20,"y":9,"type":"DIRT"},{"x":21,"y":9,"type":"AIR"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"AIR"},{"x":24,"y":9,"type":"AIR"},{"x":25,"y":9,"type":"DIRT"},{"x":26,"y":9,"type":"AIR"},{"x":27,"y":9,"type":"AIR"},{"x":28,"y":9,"type":"DIRT"},{"x":29,"y":9,"type":"DIRT"},{"x":30,"y":9,"type":"AIR"},{"x":31,"y":9,"type":"DIRT"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"DIRT"},{"x":2,"y":10,"type":"AIR"},{"x":3,"y":10,"type":"AIR"},{"x":4,"y":10,"type":"DIRT"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"DIRT"},{"x":7,"y":10,"type":"DIRT"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"AIR"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"DIRT"},{"x":12,"y":10,"type":"DIRT"},{"x":13,"y":10,"type":"DIRT"},{"x":14,"y":10,"type":"AIR"},{"x":15,"y":10,"type":"AIR"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"AIR"},{"x":18,"y":10,"type":"AIR"},{"x":19,"y":10,"type":"DIRT"},{"x":20,"y":10,"type":"DIRT"},{"x":21,"y":10,"type":"DIRT"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"AIR"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"DIRT"},{"x":26,"y":10,"type":"DIRT"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"DIRT"},{"x":29,"y":10,"type":"AIR"},{"x":30,"y":10,"type":"AIR"},{"x":31,"y":10,"type":"DIRT"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"DIRT"},{"x":3,"y":11,"type":"AIR"},{"x":4,"y":11,"type":"AIR"},{"x":5,"y":11,"type":"AIR"},{"x":6,"y":11,"type":"AIR"},{"x":7,"y":11,"type":"AIR"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"AIR"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"AIR"},{"x":13,"y":11,"type":"DIRT"},{"x":14,"y":11,"type":"AIR"},{"x":15,"y":11,"type":"DIRT"},{"x":16,"y":11,"type":"DIRT"},{"x":17,"y":11,"type":"DIRT"},{"x":18,"y":11,"type":"AIR"},{"x":19,"y":11,"type":"DIRT"},{"x":20,"y":11,"type":"AIR"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"AIR"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"AIR"},{"x":26,"y":11,"type":"AIR"},{"x":27,"y":11,"type":"AIR"},{"x":28,"y":11,"type":"AIR"},{"x":29,"y":11,"type":"AIR"},{"x":30,"y":11,"type":"DIRT"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"DIRT"},{"x":1,"y":12,"type":"DIRT"},{"x":2,"y":12,"type":"DIRT"},{"x":3,"y":12,"type":"DIRT"},{"x":4,"y":12,"type":"DIRT"},{"x":5,"y":12,"type":"AIR"},{"x":6,"y":12,"type":"AIR"},{"x":7,"y":12,"type":"AIR"},{"x":8,"y":12,"type":"DIRT"},{"x":9,"y":12,"type":"DIRT"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"AIR"},{"x":13,"y":12,"type":"AIR"},{"x":14,"y":12,"type":"DIRT"},{"x":15,"y":12,"type":"DIRT"},{"x":16,"y":12,"type":"DIRT"},{"x":17,"y":12,"type":"DIRT"},{"x":18,"y":12,"type":"DIRT"},{"x":19,"y":12,"type":"AIR"},{"x":20,"y":12,"type":"AIR"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"DIRT"},{"x":24,"y":12,"type":"DIRT"},{"x":25,"y":12,"type":"AIR"},{"x":26,"y":12,"type":"AIR"},{"x":27,"y":12,"type":"AIR"},{"x":28,"y":12,"type":"DIRT"},{"x":29,"y":12,"type":"DIRT"},{"x":30,"y":12,"type":"DIRT"},{"x":31,"y":12,"type":"DIRT"},{"x":32,"y":12,"type":"DIRT"}],[{"x":0,"y":13,"type":"DIRT"},{"x":1,"y":13,"type":"DIRT"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"DIRT"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"AIR"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"AIR"},{"x":8,"y":13,"type":"DIRT"},{"x":9,"y":13,"type":"DIRT"},{"x":10,"y":13,"type":"AIR"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"AIR"},{"x":13,"y":13,"type":"AIR"},{"x":14,"y":13,"type":"DIRT"},{"x":15,"y":13,"type":"DIRT"},{"x":16,"y":13,"type":"DIRT"},{"x":17,"y":13,"type":"DIRT"},{"x":18,"y":13,"type":"DIRT"},{"x":19,"y":13,"type":"AIR"},{"x":20,"y":13,"type":"AIR"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"AIR"},{"x":23,"y":13,"type":"DIRT"},{"x":24,"y":13,"type":"DIRT"},{"x":25,"y":13,"type":"AIR"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"AIR"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"DIRT"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"DIRT"},{"x":32,"y":13,"type":"DIRT"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"AIR"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"AIR"},{"x":8,"y":14,"type":"DIRT"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"DIRT"},{"x":11,"y":14,"type":"AIR"},{"x":12,"y":14,"type":"AIR"},{"x":13,"y":14,"type":"AIR"},{"x":14,"y":14,"type":"AIR"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"DIRT"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"AIR"},{"x":19,"y":14,"type":"AIR"},{"x":20,"y":14,"type":"AIR"},{"x":21,"y":14,"type":"AIR"},{"x":22,"y":14,"type":"DIRT"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"DIRT"},{"x":25,"y":14,"type":"AIR"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"AIR"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"AIR"},{"x":5,"y":15,"type":"AIR"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"DIRT"},{"x":9,"y":15,"type":"DIRT"},{"x":10,"y":15,"type":"DIRT"},{"x":11,"y":15,"type":"DIRT"},{"x":12,"y":15,"type":"AIR"},{"x":13,"y":15,"type":"AIR"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"DIRT"},{"x":16,"y":15,"type":"DIRT"},{"x":17,"y":15,"type":"DIRT"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"AIR"},{"x":20,"y":15,"type":"AIR"},{"x":21,"y":15,"type":"DIRT"},{"x":22,"y":15,"type":"DIRT"},{"x":23,"y":15,"type":"DIRT"},{"x":24,"y":15,"type":"DIRT"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"AIR"},{"x":28,"y":15,"type":"AIR"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"DIRT"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"DIRT"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"AIR"},{"x":10,"y":16,"type":"AIR"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"AIR"},{"x":14,"y":16,"type":"AIR"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"DIRT"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"AIR"},{"x":19,"y":16,"type":"AIR"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"AIR"},{"x":23,"y":16,"type":"AIR"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"DIRT"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"DIRT"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"DIRT"},{"x":7,"y":17,"type":"AIR"},{"x":8,"y":17,"type":"AIR"},{"x":9,"y":17,"type":"AIR"},{"x":10,"y":17,"type":"AIR"},{"x":11,"y":17,"type":"DIRT"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"AIR"},{"x":14,"y":17,"type":"AIR"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"DIRT"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"AIR"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"DIRT"},{"x":22,"y":17,"type":"AIR"},{"x":23,"y":17,"type":"AIR"},{"x":24,"y":17,"type":"AIR"},{"x":25,"y":17,"type":"AIR"},{"x":26,"y":17,"type":"DIRT"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"DIRT"},{"x":6,"y":18,"type":"DIRT"},{"x":7,"y":18,"type":"AIR"},{"x":8,"y":18,"type":"AIR"},{"x":9,"y":18,"type":"AIR"},{"x":10,"y":18,"type":"DIRT"},{"x":11,"y":18,"type":"DIRT"},{"x":12,"y":18,"type":"DIRT"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"DIRT"},{"x":15,"y":18,"type":"DIRT"},{"x":16,"y":18,"type":"DIRT"},{"x":17,"y":18,"type":"DIRT"},{"x":18,"y":18,"type":"DIRT"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"DIRT"},{"x":21,"y":18,"type":"DIRT"},{"x":22,"y":18,"type":"DIRT"},{"x":23,"y":18,"type":"AIR"},{"x":24,"y":18,"type":"AIR"},{"x":25,"y":18,"type":"AIR"},{"x":26,"y":18,"type":"DIRT"},{"x":27,"y":18,"type":"DIRT"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"AIR"},{"x":3,"y":19,"type":"DIRT"},{"x":4,"y":19,"type":"DIRT"},{"x":5,"y":19,"type":"AIR"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"DIRT"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"DIRT"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"AIR"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"DIRT"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"AIR"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"DIRT"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"DIRT"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"AIR"},{"x":28,"y":19,"type":"DIRT"},{"x":29,"y":19,"type":"DIRT"},{"x":30,"y":19,"type":"AIR"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"DIRT"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"AIR"},{"x":3,"y":20,"type":"AIR"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"AIR"},{"x":6,"y":20,"type":"AIR"},{"x":7,"y":20,"type":"DIRT"},{"x":8,"y":20,"type":"DIRT"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"AIR"},{"x":11,"y":20,"type":"AIR"},{"x":12,"y":20,"type":"AIR"},{"x":13,"y":20,"type":"AIR"},{"x":14,"y":20,"type":"AIR"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"DIRT"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"AIR"},{"x":19,"y":20,"type":"AIR"},{"x":20,"y":20,"type":"AIR"},{"x":21,"y":20,"type":"AIR"},{"x":22,"y":20,"type":"AIR"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"DIRT"},{"x":25,"y":20,"type":"DIRT"},{"x":26,"y":20,"type":"AIR"},{"x":27,"y":20,"type":"AIR"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"AIR"},{"x":30,"y":20,"type":"AIR"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"DIRT"}],[{"x":0,"y":21,"type":"AIR"},{"x":1,"y":21,"type":"AIR"},{"x":2,"y":21,"type":"AIR"},{"x":3,"y":21,"type":"AIR"},{"x":4,"y":21,"type":"AIR"},{"x":5,"y":21,"type":"AIR"},{"x":6,"y":21,"type":"AIR"},{"x":7,"y":21,"type":"AIR"},{"x":8,"y":21,"type":"DIRT"},{"x":9,"y":21,"type":"DIRT"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"AIR"},{"x":12,"y":21,"type":"AIR"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"DIRT"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"AIR"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"DIRT"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"AIR"},{"x":21,"y":21,"type":"AIR"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"DIRT"},{"x":24,"y":21,"type":"DIRT"},{"x":25,"y":21,"type":"AIR"},{"x":26,"y":21,"type":"AIR"},{"x":27,"y":21,"type":"AIR"},{"x":28,"y":21,"type":"AIR"},{"x":29,"y":21,"type":"AIR"},{"x":30,"y":21,"type":"AIR"},{"x":31,"y":21,"type":"AIR"},{"x":32,"y":21,"type":"AIR"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"AIR"},{"x":2,"y":22,"type":"DIRT"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"AIR"},{"x":5,"y":22,"type":"AIR"},{"x":6,"y":22,"type":"AIR"},{"x":7,"y":22,"type":"AIR"},{"x":8,"y":22,"type":"AIR"},{"x":9,"y":22,"type":"DIRT"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"AIR"},{"x":13,"y":22,"type":"DIRT"},{"x":14,"y":22,"type":"DIRT"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"DIRT"},{"x":19,"y":22,"type":"DIRT"},{"x":20,"y":22,"type":"AIR"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"DIRT"},{"x":24,"y":22,"type":"AIR"},{"x":25,"y":22,"type":"AIR"},{"x":26,"y":22,"type":"AIR"},{"x":27,"y":22,"type":"AIR"},{"x":28,"y":22,"type":"AIR"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"DIRT"},{"x":31,"y":22,"type":"AIR"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"AIR"},{"x":2,"y":23,"type":"DIRT"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"AIR"},{"x":5,"y":23,"type":"AIR"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"AIR"},{"x":8,"y":23,"type":"AIR"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"AIR"},{"x":13,"y":23,"type":"DIRT"},{"x":14,"y":23,"type":"DIRT"},{"x":15,"y":23,"type":"AIR"},{"x":16,"y":23,"type":"DIRT"},{"x":17,"y":23,"type":"AIR"},{"x":18,"y":23,"type":"DIRT"},{"x":19,"y":23,"type":"DIRT"},{"x":20,"y":23,"type":"AIR"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"AIR"},{"x":25,"y":23,"type":"AIR"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"AIR"},{"x":28,"y":23,"type":"AIR"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"DIRT"},{"x":31,"y":23,"type":"AIR"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"AIR"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"AIR"},{"x":5,"y":24,"type":"AIR"},{"x":6,"y":24,"type":"DIRT"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"AIR"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"AIR"},{"x":11,"y":24,"type":"AIR"},{"x":12,"y":24,"type":"AIR"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"AIR"},{"x":15,"y":24,"type":"AIR"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"AIR"},{"x":18,"y":24,"type":"AIR"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"AIR"},{"x":21,"y":24,"type":"AIR"},{"x":22,"y":24,"type":"AIR"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"AIR"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"DIRT"},{"x":27,"y":24,"type":"AIR"},{"x":28,"y":24,"type":"AIR"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"AIR"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"AIR"},{"x":3,"y":25,"type":"AIR"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"AIR"},{"x":6,"y":25,"type":"AIR"},{"x":7,"y":25,"type":"AIR"},{"x":8,"y":25,"type":"AIR"},{"x":9,"y":25,"type":"AIR"},{"x":10,"y":25,"type":"AIR"},{"x":11,"y":25,"type":"AIR"},{"x":12,"y":25,"type":"AIR"},{"x":13,"y":25,"type":"AIR"},{"x":14,"y":25,"type":"AIR"},{"x":15,"y":25,"type":"AIR"},{"x":16,"y":25,"type":"DIRT"},{"x":17,"y":25,"type":"AIR"},{"x":18,"y":25,"type":"AIR"},{"x":19,"y":25,"type":"AIR"},{"x":20,"y":25,"type":"AIR"},{"x":21,"y":25,"type":"AIR"},{"x":22,"y":25,"type":"AIR"},{"x":23,"y":25,"type":"AIR"},{"x":24,"y":25,"type":"AIR"},{"x":25,"y":25,"type":"AIR"},{"x":26,"y":25,"type":"AIR"},{"x":27,"y":25,"type":"AIR"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"AIR"},{"x":30,"y":25,"type":"AIR"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"AIR"},{"x":5,"y":26,"type":"AIR"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"DIRT"},{"x":14,"y":26,"type":"DIRT"},{"x":15,"y":26,"type":"AIR"},{"x":16,"y":26,"type":"DIRT"},{"x":17,"y":26,"type":"AIR"},{"x":18,"y":26,"type":"DIRT"},{"x":19,"y":26,"type":"DIRT"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"AIR"},{"x":28,"y":26,"type":"AIR"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"AIR"},{"x":5,"y":27,"type":"AIR"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"DIRT"},{"x":14,"y":27,"type":"DIRT"},{"x":15,"y":27,"type":"AIR"},{"x":16,"y":27,"type":"AIR"},{"x":17,"y":27,"type":"AIR"},{"x":18,"y":27,"type":"DIRT"},{"x":19,"y":27,"type":"DIRT"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"AIR"},{"x":28,"y":27,"type":"AIR"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"DIRT"},{"x":5,"y":28,"type":"DIRT"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"AIR"},{"x":12,"y":28,"type":"AIR"},{"x":13,"y":28,"type":"DIRT"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"AIR"},{"x":16,"y":28,"type":"AIR"},{"x":17,"y":28,"type":"AIR"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"DIRT"},{"x":20,"y":28,"type":"AIR"},{"x":21,"y":28,"type":"AIR"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"DIRT"},{"x":28,"y":28,"type":"DIRT"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"AIR"},{"x":12,"y":29,"type":"DIRT"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"DIRT"},{"x":15,"y":29,"type":"AIR"},{"x":16,"y":29,"type":"DIRT"},{"x":17,"y":29,"type":"AIR"},{"x":18,"y":29,"type":"DIRT"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"DIRT"},{"x":21,"y":29,"type":"AIR"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"DIRT"},{"x":12,"y":30,"type":"DIRT"},{"x":13,"y":30,"type":"DIRT"},{"x":14,"y":30,"type":"AIR"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"DIRT"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"AIR"},{"x":19,"y":30,"type":"DIRT"},{"x":20,"y":30,"type":"DIRT"},{"x":21,"y":30,"type":"DIRT"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"AIR"},{"x":9,"y":31,"type":"AIR"},{"x":10,"y":31,"type":"AIR"},{"x":11,"y":31,"type":"AIR"},{"x":12,"y":31,"type":"DIRT"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"DIRT"},{"x":16,"y":31,"type":"DIRT"},{"x":17,"y":31,"type":"DIRT"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"DIRT"},{"x":21,"y":31,"type":"AIR"},{"x":22,"y":31,"type":"AIR"},{"x":23,"y":31,"type":"AIR"},{"x":24,"y":31,"type":"AIR"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"AIR"},{"x":12,"y":32,"type":"DIRT"},{"x":13,"y":32,"type":"DIRT"},{"x":14,"y":32,"type":"DIRT"},{"x":15,"y":32,"type":"DIRT"},{"x":16,"y":32,"type":"DIRT"},{"x":17,"y":32,"type":"DIRT"},{"x":18,"y":32,"type":"DIRT"},{"x":19,"y":32,"type":"DIRT"},{"x":20,"y":32,"type":"DIRT"},{"x":21,"y":32,"type":"AIR"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.31.16/B-log.csv b/2019-worms/tests/replays/2019.08.19.21.31.16/B-log.csv
new file mode 100644
index 0000000..57ec18e
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.31.16/B-log.csv
@@ -0,0 +1,274 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,8,28,100,8,4,100,31,16
+2,move,"move 9 27",1,121,350,150,9,27,100,8,4,100,31,16
+3,move,"move 9 5",2,126,350,150,9,27,100,9,5,100,31,16
+4,move,"move 30 17",3,131,350,150,9,27,100,9,5,100,30,17
+5,dig,"dig 10 26",1,138,350,150,9,27,100,9,5,100,30,17
+6,dig,"dig 10 6",2,145,350,150,9,27,100,9,5,100,30,17
+7,dig,"dig 29 17",3,152,350,150,9,27,100,9,5,100,30,17
+8,move,"move 10 26",1,157,350,150,10,26,100,9,5,100,30,17
+9,move,"move 10 6",2,162,350,150,10,26,100,10,6,100,30,17
+10,move,"move 29 17",3,167,350,150,10,26,100,10,6,100,29,17
+11,move,"move 11 25",1,172,350,150,11,25,100,10,6,100,29,17
+12,move,"move 11 7",2,177,350,150,11,25,100,11,7,100,29,17
+13,dig,"dig 28 17",3,184,350,150,11,25,100,11,7,100,29,17
+14,move,"move 12 24",1,189,350,150,12,24,100,11,7,100,29,17
+15,move,"move 12 8",2,194,350,150,12,24,100,12,8,100,29,17
+16,move,"move 28 17",3,199,350,150,12,24,100,12,8,100,28,17
+17,dig,"dig 13 23",1,206,350,150,12,24,100,12,8,100,28,17
+18,dig,"dig 13 9",2,213,350,150,12,24,100,12,8,100,28,17
+19,dig,"dig 27 17",3,220,350,150,12,24,100,12,8,100,28,17
+20,move,"move 13 23",1,225,350,150,13,23,100,12,8,100,28,17
+21,move,"move 13 9",2,230,350,150,13,23,100,13,9,100,28,17
+22,move,"move 27 17",3,235,350,150,13,23,100,13,9,100,27,17
+23,dig,"dig 14 22",1,242,350,150,13,23,100,13,9,100,27,17
+24,move,"move 14 10",2,247,350,150,13,23,100,14,10,100,27,17
+25,dig,"dig 26 17",3,254,350,150,13,23,100,14,10,100,27,17
+26,move,"move 14 22",1,259,350,150,14,22,100,14,10,100,27,17
+27,move,"move 14 11",2,264,350,150,14,22,100,14,11,100,27,17
+28,move,"move 26 17",3,269,350,150,14,22,100,14,11,100,26,17
+29,move,"move 15 21",1,274,350,150,15,21,100,14,11,100,26,17
+30,dig,"dig 14 12",2,281,350,150,15,21,100,14,11,100,26,17
+31,move,"move 25 17",3,286,350,150,15,21,100,14,11,100,25,17
+32,dig,"dig 16 20",1,293,350,150,15,21,100,14,11,100,25,17
+33,move,"move 14 12",2,298,350,150,15,21,100,14,12,100,25,17
+34,move,"move 24 17",3,303,350,150,15,21,100,14,12,100,24,17
+35,move,"move 16 20",1,308,350,150,16,20,100,14,12,100,24,17
+36,dig,"dig 14 13",2,315,350,150,16,20,100,14,12,100,24,17
+37,move,"move 23 17",3,320,350,150,16,20,100,14,12,100,23,17
+38,move,"move 17 19",1,325,350,150,17,19,100,14,12,100,23,17
+39,move,"move 14 13",2,330,350,150,17,19,100,14,13,100,23,17
+40,move,"move 22 17",3,335,350,150,17,19,100,14,13,100,22,17
+41,dig,"dig 18 18",1,342,350,150,17,19,100,14,13,100,22,17
+42,move,"move 14 14",2,347,350,150,17,19,100,14,14,100,22,17
+43,dig,"dig 21 17",3,354,350,150,17,19,100,14,14,100,22,17
+44,move,"move 18 18",1,359,350,150,18,18,100,14,14,100,22,17
+45,move,"move 14 15",2,368,360,150,18,18,110,14,15,100,22,17
+46,move,"move 21 17",3,373,360,150,18,18,110,14,15,100,21,17
+47,move,"move 18 17",1,381,370,160,18,17,110,14,15,100,21,17
+48,move,"move 15 14",2,386,370,160,18,17,110,15,14,100,21,17
+49,move,"move 22 16",3,391,370,160,18,17,110,15,14,100,22,16
+50,move,"move 19 16",1,396,370,160,19,16,110,15,14,100,22,16
+51,dig,"dig 16 13",2,403,370,160,19,16,110,15,14,100,22,16
+52,dig,"dig 22 15",3,410,370,160,19,16,110,15,14,100,22,16
+53,move,"move 20 15",1,415,370,160,20,15,110,15,14,100,22,16
+54,move,"move 16 13",2,420,370,160,20,15,110,16,13,100,22,16
+55,dig,"dig 23 15",3,427,370,160,20,15,110,16,13,100,22,16
+56,move,"move 21 14",1,432,370,160,21,14,110,16,13,100,22,16
+57,dig,"dig 17 12",2,439,370,160,21,14,110,16,13,100,22,16
+58,move,"move 22 15",3,444,370,160,21,14,110,16,13,100,22,15
+59,move,"move 22 13",1,449,370,160,22,13,110,16,13,100,22,15
+60,move,"move 17 12",2,454,370,160,22,13,110,17,12,100,22,15
+61,dig,"dig 23 14",3,461,370,160,22,13,110,17,12,100,22,15
+62,dig,"dig 23 12",1,468,370,160,22,13,110,17,12,100,22,15
+63,move,"move 18 11",2,473,370,160,22,13,110,18,11,100,22,15
+64,dig,"dig 22 14",3,480,370,160,22,13,110,18,11,100,22,15
+65,move,"move 22 12",1,485,370,160,22,12,110,18,11,100,22,15
+66,dig,"dig 19 10",2,492,370,160,22,12,110,18,11,100,22,15
+67,move,"move 23 14",3,497,370,160,22,12,110,18,11,100,23,14
+68,move,"move 23 11",1,502,370,160,23,11,110,18,11,100,23,14
+69,move,"move 19 10",2,507,370,160,23,11,110,19,10,100,23,14
+70,dig,"dig 24 13",3,514,370,160,23,11,110,19,10,100,23,14
+71,move,"move 24 10",1,519,370,160,24,10,110,19,10,100,23,14
+72,dig,"dig 20 9",2,526,370,160,24,10,110,19,10,100,23,14
+73,dig,"dig 23 13",3,533,370,160,24,10,110,19,10,100,23,14
+74,move,"move 23 9",1,538,370,160,23,9,110,19,10,100,23,14
+75,move,"move 20 9",2,543,370,160,23,9,110,20,9,100,23,14
+76,move,"move 22 13",3,548,370,160,23,9,110,20,9,100,22,13
+77,move,"move 22 8",1,553,370,160,22,8,110,20,9,100,22,13
+78,move,"move 21 8",2,558,370,160,22,8,110,21,8,100,22,13
+79,move,"move 22 12",3,563,370,160,22,8,110,21,8,100,22,12
+80,move,"move 22 7",1,568,370,160,22,7,110,21,8,100,22,12
+81,move,"move 20 7",2,573,370,160,22,7,110,20,7,100,22,12
+82,move,"move 22 11",3,578,370,160,22,7,110,20,7,100,22,11
+83,dig,"dig 22 6",1,585,370,160,22,7,110,20,7,100,22,11
+84,dig,"dig 21 6",2,592,370,160,22,7,110,20,7,100,22,11
+85,dig,"dig 21 10",3,599,370,160,22,7,110,20,7,100,22,11
+86,move,"move 21 6",1,604,370,160,21,6,110,20,7,100,22,11
+87,dig,"dig 19 8",2,611,370,160,21,6,110,20,7,100,22,11
+88,move,"move 21 10",3,616,370,160,21,6,110,20,7,100,21,10
+89,dig,"dig 21 5",1,623,370,160,21,6,110,20,7,100,21,10
+90,move,"move 21 8",2,628,370,160,21,6,110,21,8,100,21,10
+91,move,"move 21 9",3,633,370,160,21,6,110,21,8,100,21,9
+92,move,"move 21 5",1,638,370,160,21,5,110,21,8,100,21,9
+93,move,"move 21 7",2,643,370,160,21,5,110,21,7,100,21,9
+94,move,"move 21 8",3,648,370,160,21,5,110,21,7,100,21,8
+95,move,"move 21 4",1,653,370,160,21,4,110,21,7,100,21,8
+96,move,"move 21 6",2,658,370,160,21,4,110,21,6,100,21,8
+97,move,"move 21 7",3,663,370,160,21,4,110,21,6,100,21,7
+98,dig,"dig 21 3",1,670,370,160,21,4,110,21,6,100,21,7
+99,banana,"banana 21 1",2,735,362,152,21,4,110,21,6,100,21,7
+100,shoot,"shoot N",1,749,354,144,21,4,110,21,6,100,21,7
+101,banana,"banana 21 1",2,786,346,136,21,4,110,21,6,100,21,7
+102,shoot,"shoot N",1,799,338,128,21,4,110,21,6,100,21,7
+103,banana,"banana 21 1",2,837,330,120,21,4,110,21,6,100,21,7
+104,shoot,"shoot N",1,853,330,120,21,4,110,21,6,100,21,7
+105,shoot,"shoot N",1,869,330,120,21,4,110,21,6,100,21,7
+106,shoot,"shoot N",1,871,330,120,21,4,110,21,6,100,21,7
+107,dig,"dig 22 5",2,878,330,120,21,4,110,21,6,100,21,7
+108,move,"move 22 6",3,883,330,120,21,4,110,21,6,100,22,6
+109,move,"move 22 3",1,885,322,112,22,3,110,21,6,100,22,6
+110,move,"move 22 5",2,890,322,112,22,3,110,22,5,100,22,6
+111,snowball,"snowball 22 2",3,890,322,112,22,3,110,22,5,100,22,6
+112,shoot,"shoot N",1,890,322,112,22,3,110,22,5,100,22,6
+113,move,"move 22 4",2,895,322,112,22,3,110,22,4,100,22,6
+114,move,"move 22 5",3,900,322,112,22,3,110,22,4,100,22,5
+115,shoot,"shoot N",1,900,322,112,22,3,110,22,4,100,22,5
+116,invalid,"invalid",2,896,322,112,22,3,110,22,4,100,22,5
+117,snowball,"snowball 22 2",3,896,322,112,22,3,110,22,4,100,22,5
+118,shoot,"shoot N",1,896,322,112,22,3,110,22,4,100,22,5
+119,move,"move 23 4",2,901,322,112,22,3,110,23,4,100,22,5
+120,move,"move 22 4",3,906,322,112,22,3,110,23,4,100,22,4
+121,shoot,"shoot N",1,906,322,112,22,3,110,23,4,100,22,4
+122,invalid,"invalid",2,902,322,112,22,3,110,23,4,100,22,4
+123,snowball,"snowball 22 2",3,902,322,112,22,3,110,23,4,100,22,4
+124,shoot,"shoot N",1,902,322,112,22,3,110,23,4,100,22,4
+125,move,"move 23 5",2,907,322,112,22,3,110,23,5,100,22,4
+126,move,"move 21 4",3,912,322,112,22,3,110,23,5,100,21,4
+127,shoot,"shoot N",1,912,322,112,22,3,110,23,5,100,21,4
+128,move,"move 22 4",2,916,319,109,22,3,110,22,4,100,21,4
+129,move,"move 20 4",3,920,316,106,22,3,110,22,4,100,20,4
+130,shoot,"shoot N",1,972,305,95,22,3,110,22,4,100,20,4
+131,move,"move 22 5",2,976,302,92,22,3,110,22,5,100,20,4
+132,move,"move 21 5",3,980,299,89,22,3,110,22,5,100,21,5
+133,move,"move 21 4",1,984,296,86,21,4,110,22,5,100,21,5
+134,move,"move 21 6",2,989,296,86,21,4,110,21,6,100,21,5
+135,move,"move 22 4",3,994,296,86,21,4,110,21,6,100,22,4
+136,move,"move 21 5",1,999,296,86,21,5,110,21,6,100,22,4
+137,move,"move 21 7",2,1004,296,86,21,5,110,21,7,100,22,4
+138,move,"move 23 5",3,1009,296,86,21,5,110,21,7,100,23,5
+139,move,"move 20 6",1,1014,296,86,20,6,110,21,7,100,23,5
+140,move,"move 20 8",2,1019,296,86,20,6,110,20,8,100,23,5
+141,move,"move 22 6",3,1024,296,86,20,6,110,20,8,100,22,6
+142,move,"move 21 7",1,1029,296,86,21,7,110,20,8,100,22,6
+143,move,"move 21 9",2,1034,296,86,21,7,110,21,9,100,22,6
+144,move,"move 21 5",3,1039,296,86,21,7,110,21,9,100,21,5
+145,move,"move 21 8",1,1044,296,86,21,8,110,21,9,100,21,5
+146,dig,"dig 20 10",2,1051,296,86,21,8,110,21,9,100,21,5
+147,move,"move 20 6",3,1056,296,86,21,8,110,21,9,100,20,6
+148,move,"move 20 9",1,1061,296,86,20,9,110,21,9,100,20,6
+149,move,"move 20 10",2,1066,296,86,20,9,110,20,10,100,20,6
+150,dig,"dig 19 7",3,1073,296,86,20,9,110,20,10,100,20,6
+151,move,"move 19 10",1,1078,296,86,19,10,110,20,10,100,20,6
+152,dig,"dig 19 11",2,1085,296,86,19,10,110,20,10,100,20,6
+153,move,"move 19 7",3,1090,296,86,19,10,110,20,10,100,19,7
+154,move,"move 20 11",1,1095,296,86,20,11,110,20,10,100,19,7
+155,move,"move 21 11",2,1100,296,86,20,11,110,21,11,100,19,7
+156,move,"move 20 8",3,1105,296,86,20,11,110,21,11,100,20,8
+157,move,"move 19 12",1,1110,296,86,19,12,110,21,11,100,20,8
+158,move,"move 20 12",2,1115,296,86,19,12,110,20,12,100,20,8
+159,dig,"dig 19 9",3,1122,296,86,19,12,110,20,12,100,20,8
+160,move,"move 19 13",1,1127,296,86,19,13,110,20,12,100,20,8
+161,move,"move 20 11",2,1132,296,86,19,13,110,20,11,100,20,8
+162,move,"move 19 9",3,1137,296,86,19,13,110,20,11,100,19,9
+163,move,"move 20 14",1,1142,296,86,20,14,110,20,11,100,19,9
+164,move,"move 20 12",2,1147,296,86,20,14,110,20,12,100,19,9
+165,move,"move 20 10",3,1152,296,86,20,14,110,20,12,100,20,10
+166,dig,"dig 21 15",1,1159,296,86,20,14,110,20,12,100,20,10
+167,move,"move 21 13",2,1162,289,79,20,14,110,21,13,100,20,10
+168,move,"move 20 11",3,1167,289,79,20,14,110,21,13,100,20,11
+169,move,"move 21 15",1,1172,289,79,21,15,110,21,13,100,20,11
+170,move,"move 21 14",2,1177,289,79,21,15,110,21,14,100,20,11
+171,move,"move 21 12",3,1171,256,59,21,15,97,21,14,100,21,12
+172,move,"move 21 16",1,1176,256,59,21,16,97,21,14,100,21,12
+173,move,"move 21 15",2,1170,223,39,21,16,84,21,15,100,21,12
+174,move,"move 21 13",3,1175,223,39,21,16,84,21,15,100,21,13
+175,move,"move 21 17",1,1180,223,39,21,17,84,21,15,100,21,13
+176,move,"move 21 16",2,1185,223,39,21,17,84,21,16,100,21,13
+177,move,"move 21 14",3,1190,223,39,21,17,84,21,16,100,21,14
+178,move,"move 21 18",1,1195,223,39,21,18,84,21,16,100,21,14
+179,move,"move 21 17",2,1200,223,39,21,18,84,21,17,100,21,14
+180,move,"move 21 15",3,1205,223,39,21,18,84,21,17,100,21,15
+181,dig,"dig 20 19",1,1212,223,39,21,18,84,21,17,100,21,15
+182,move,"move 20 18",2,1217,223,39,21,18,84,20,18,100,21,15
+183,move,"move 20 16",3,1219,215,39,21,18,76,20,18,100,20,16
+184,move,"move 20 19",1,1224,215,39,20,19,76,20,18,100,20,16
+185,dig,"dig 21 19",2,1229,207,31,20,19,76,20,18,100,20,16
+186,move,"move 20 17",3,1234,207,31,20,19,76,20,18,100,20,17
+187,shoot,"shoot S",1,1236,207,31,20,19,76,20,18,100,20,17
+188,move,"move 21 19",2,1241,207,31,20,19,76,21,19,100,20,17
+189,move,"move 21 18",3,1243,199,31,20,19,68,21,19,100,21,18
+190,move,"move 21 20",1,1248,199,31,21,20,68,21,19,100,21,18
+191,move,"move 20 18",2,1250,191,23,21,20,68,20,18,100,21,18
+192,move,"move 21 19",3,1255,191,23,21,20,68,20,18,100,21,19
+193,shoot,"shoot S",1,1257,191,23,21,20,68,20,18,100,21,19
+194,invalid,"invalid",2,1253,191,23,21,20,68,20,18,100,21,19
+195,shoot,"shoot SE",3,1255,191,23,21,20,68,20,18,100,21,19
+196,move,"move 22 19",1,1260,191,23,22,19,68,20,18,100,21,19
+197,move,"move 20 17",2,1263,183,15,22,19,68,20,17,100,21,19
+198,move,"move 22 20",3,1268,183,15,22,19,68,20,17,100,22,20
+199,shoot,"shoot E",1,1270,183,15,22,19,68,20,17,100,22,20
+200,move,"move 21 18",2,1275,183,15,22,19,68,21,18,100,22,20
+201,move,"move 23 19",3,1280,183,15,22,19,68,21,18,100,23,19
+202,move,"move 23 18",1,1285,183,15,23,18,68,21,18,100,23,19
+203,move,"move 22 17",2,1287,175,7,23,18,68,22,17,100,23,19
+204,move,"move 22 19",3,1292,175,7,23,18,68,22,17,100,22,19
+205,shoot,"shoot N",1,1294,175,7,23,18,68,22,17,100,22,19
+206,move,"move 23 16",2,1299,175,7,23,18,68,23,16,100,22,19
+207,move,"move 23 20",3,1301,167,7,23,18,60,23,16,100,23,20
+208,move,"move 24 17",1,1306,167,7,24,17,60,23,16,100,23,20
+209,shoot,"shoot E",2,1308,167,7,24,17,60,23,16,100,23,20
+210,dig,"dig 24 19",3,1315,167,7,24,17,60,23,16,100,23,20
+211,move,"move 23 17",1,1320,167,7,23,17,60,23,16,100,23,20
+212,move,"move 22 15",2,1325,167,7,23,17,60,22,15,100,23,20
+213,move,"move 22 19",3,1328,159,7,23,17,52,22,15,100,22,19
+214,move,"move 22 16",1,1333,159,7,22,16,52,22,15,100,22,19
+215,shoot,"shoot N",2,1335,159,7,22,16,52,22,15,100,22,19
+216,move,"move 21 18",3,1340,159,7,22,16,52,22,15,100,21,18
+217,move,"move 21 15",1,1345,159,7,21,15,52,22,15,100,21,18
+218,move,"move 21 14",2,1350,159,7,21,15,52,21,14,100,21,18
+219,move,"move 21 17",3,1352,151,7,21,15,44,21,14,100,21,17
+220,move,"move 20 14",1,1357,151,7,20,14,44,21,14,100,21,17
+221,shoot,"shoot N",2,1359,151,7,20,14,44,21,14,100,21,17
+222,move,"move 20 16",3,1364,151,7,20,14,44,21,14,100,20,16
+223,shoot,"shoot NE",1,1366,151,7,20,14,44,21,14,100,20,16
+224,move,"move 22 13",2,1371,151,7,20,14,44,22,13,100,20,16
+225,move,"move 21 15",3,1373,143,7,20,14,36,22,13,100,21,15
+226,move,"move 21 13",1,1378,143,7,21,13,36,22,13,100,21,15
+227,shoot,"shoot N",2,1380,143,7,21,13,36,22,13,100,21,15
+228,move,"move 21 14",3,1385,143,7,21,13,36,22,13,100,21,14
+229,shoot,"shoot N",1,1387,143,7,21,13,36,22,13,100,21,14
+230,move,"move 21 12",2,1392,143,7,21,13,36,21,12,100,21,14
+231,move,"move 20 13",3,1397,143,7,21,13,36,21,12,100,20,13
+232,move,"move 20 12",1,1402,143,7,20,12,36,21,12,100,20,13
+233,move,"move 20 11",2,1407,143,7,20,12,36,20,11,100,20,13
+234,move,"move 19 12",3,1412,143,7,20,12,36,20,11,100,19,12
+235,move,"move 19 11",1,1417,143,7,19,11,36,20,11,100,19,12
+236,move,"move 19 10",2,1422,143,7,19,11,36,19,10,100,19,12
+237,move,"move 18 11",3,1427,143,7,19,11,36,19,10,100,18,11
+238,move,"move 18 10",1,1432,143,7,18,10,36,19,10,100,18,11
+239,move,"move 20 10",2,1435,135,7,18,10,36,20,10,92,18,11
+240,shoot,"shoot NW",3,1450,132,7,18,10,33,20,10,92,18,11
+241,shoot,"shoot W",1,1462,121,7,18,10,30,20,10,84,18,11
+242,move,"move 19 10",2,1466,118,7,18,10,27,19,10,84,18,11
+243,shoot,"shoot NW",3,1479,110,7,18,10,27,19,10,76,18,11
+244,shoot,"shoot W",1,1495,110,7,18,10,27,19,10,76,18,11
+245,invalid,"invalid",2,1489,102,7,18,10,27,19,10,68,18,11
+246,shoot,"shoot NW",3,1505,102,7,18,10,27,19,10,68,18,11
+247,shoot,"shoot W",1,1517,91,7,18,10,24,19,10,60,18,11
+248,nothing,"nothing "Player chose to do nothing"",2,1516,88,7,18,10,21,19,10,60,18,11
+249,shoot,"shoot NW",3,1528,77,7,18,10,18,19,10,52,18,11
+250,shoot,"shoot W",1,1543,74,7,18,10,15,19,10,52,18,11
+251,nothing,"nothing "Player chose to do nothing"",2,1540,63,7,18,10,12,19,10,44,18,11
+252,shoot,"shoot NW",3,1555,60,7,18,10,9,19,10,44,18,11
+253,shoot,"shoot W",1,1566,46,4,18,10,6,19,10,36,18,11
+254,nothing,"nothing "Player chose to do nothing"",2,1564,40,1,18,10,3,19,10,36,18,11
+255,shoot,"shoot NW",3,1565,36,-2,18,10,0,19,10,36,18,11
+256,move,"move 17 10",3,1570,36,-2,18,10,0,19,10,36,17,10
+257,shoot,"shoot W",3,1585,33,-2,18,10,0,19,10,33,17,10
+258,shoot,"shoot W",3,1600,30,-2,18,10,0,19,10,30,17,10
+259,shoot,"shoot W",3,1652,19,-2,18,10,0,19,10,19,17,10
+260,dig,"dig 16 11",3,1658,16,-2,18,10,0,19,10,16,17,10
+261,move,"move 16 11",3,1662,13,-2,18,10,0,19,10,13,16,11
+262,dig,"dig 15 12",3,1669,13,-2,18,10,0,19,10,13,16,11
+263,move,"move 15 12",3,1674,13,-2,18,10,0,19,10,13,15,12
+264,move,"move 14 13",3,1679,13,-2,18,10,0,19,10,13,14,13
+265,move,"move 13 14",3,1684,13,-2,18,10,0,19,10,13,13,14
+266,move,"move 14 15",3,1689,13,-2,18,10,0,19,10,13,14,15
+267,move,"move 15 16",3,1694,13,-2,18,10,0,19,10,13,15,16
+268,dig,"dig 16 17",3,1701,13,-2,18,10,0,19,10,13,15,16
+269,move,"move 15 17",3,1706,13,-2,18,10,0,19,10,13,15,17
+270,dig,"dig 15 18",3,1713,13,-2,18,10,0,19,10,13,15,17
+271,shoot,"shoot SE",3,1715,13,-2,18,10,0,19,10,13,15,17
+272,shoot,"shoot S",3,1717,13,-2,18,10,0,19,10,13,15,17
+273,shoot,"shoot W",3,1730,5,-2,18,10,0,19,10,5,15,17
diff --git a/2019-worms/tests/replays/2019.08.19.21.57.04/A-init.json b/2019-worms/tests/replays/2019.08.19.21.57.04/A-init.json
new file mode 100644
index 0000000..b8ed313
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.57.04/A-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":1,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":2,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"DIRT"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"AIR"},{"x":15,"y":0,"type":"AIR"},{"x":16,"y":0,"type":"DIRT"},{"x":17,"y":0,"type":"AIR"},{"x":18,"y":0,"type":"AIR"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"DIRT"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"DIRT"},{"x":10,"y":1,"type":"AIR"},{"x":11,"y":1,"type":"DIRT"},{"x":12,"y":1,"type":"DIRT"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"AIR"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"DIRT"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"AIR"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"DIRT"},{"x":21,"y":1,"type":"DIRT"},{"x":22,"y":1,"type":"AIR"},{"x":23,"y":1,"type":"DIRT"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"DIRT"},{"x":14,"y":2,"type":"DIRT"},{"x":15,"y":2,"type":"AIR"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"AIR"},{"x":18,"y":2,"type":"DIRT"},{"x":19,"y":2,"type":"DIRT"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"DIRT"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"AIR"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"AIR"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"DIRT"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"DIRT"},{"x":5,"y":4,"type":"DIRT"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"DIRT"},{"x":12,"y":4,"type":"AIR"},{"x":13,"y":4,"type":"AIR"},{"x":14,"y":4,"type":"AIR"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"AIR"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"AIR"},{"x":19,"y":4,"type":"AIR"},{"x":20,"y":4,"type":"AIR"},{"x":21,"y":4,"type":"DIRT"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"DIRT"},{"x":28,"y":4,"type":"DIRT"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"DIRT"},{"x":5,"y":5,"type":"DIRT"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"DIRT"},{"x":13,"y":5,"type":"DIRT"},{"x":14,"y":5,"type":"AIR"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"AIR"},{"x":19,"y":5,"type":"DIRT"},{"x":20,"y":5,"type":"DIRT"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"DIRT"},{"x":28,"y":5,"type":"DIRT"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"AIR"},{"x":4,"y":6,"type":"DIRT"},{"x":5,"y":6,"type":"DIRT"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"AIR"},{"x":12,"y":6,"type":"AIR"},{"x":13,"y":6,"type":"DIRT"},{"x":14,"y":6,"type":"DIRT"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"DIRT"},{"x":19,"y":6,"type":"DIRT"},{"x":20,"y":6,"type":"AIR"},{"x":21,"y":6,"type":"AIR"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"DIRT"},{"x":28,"y":6,"type":"DIRT"},{"x":29,"y":6,"type":"AIR"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"DIRT"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"DIRT"},{"x":5,"y":7,"type":"DIRT"},{"x":6,"y":7,"type":"DIRT"},{"x":7,"y":7,"type":"AIR"},{"x":8,"y":7,"type":"AIR"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"DIRT"},{"x":12,"y":7,"type":"DIRT"},{"x":13,"y":7,"type":"DIRT"},{"x":14,"y":7,"type":"DIRT"},{"x":15,"y":7,"type":"DIRT"},{"x":16,"y":7,"type":"AIR"},{"x":17,"y":7,"type":"DIRT"},{"x":18,"y":7,"type":"DIRT"},{"x":19,"y":7,"type":"DIRT"},{"x":20,"y":7,"type":"DIRT"},{"x":21,"y":7,"type":"DIRT"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"AIR"},{"x":25,"y":7,"type":"AIR"},{"x":26,"y":7,"type":"DIRT"},{"x":27,"y":7,"type":"DIRT"},{"x":28,"y":7,"type":"DIRT"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"DIRT"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"DIRT"},{"x":2,"y":8,"type":"DIRT"},{"x":3,"y":8,"type":"AIR"},{"x":4,"y":8,"type":"AIR"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"DIRT"},{"x":7,"y":8,"type":"AIR"},{"x":8,"y":8,"type":"AIR"},{"x":9,"y":8,"type":"AIR"},{"x":10,"y":8,"type":"AIR"},{"x":11,"y":8,"type":"DIRT"},{"x":12,"y":8,"type":"DIRT"},{"x":13,"y":8,"type":"AIR"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"AIR"},{"x":16,"y":8,"type":"DIRT"},{"x":17,"y":8,"type":"AIR"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"AIR"},{"x":20,"y":8,"type":"DIRT"},{"x":21,"y":8,"type":"DIRT"},{"x":22,"y":8,"type":"AIR"},{"x":23,"y":8,"type":"AIR"},{"x":24,"y":8,"type":"AIR"},{"x":25,"y":8,"type":"AIR"},{"x":26,"y":8,"type":"DIRT"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"AIR"},{"x":29,"y":8,"type":"AIR"},{"x":30,"y":8,"type":"DIRT"},{"x":31,"y":8,"type":"DIRT"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"DIRT"},{"x":2,"y":9,"type":"DIRT"},{"x":3,"y":9,"type":"AIR"},{"x":4,"y":9,"type":"AIR"},{"x":5,"y":9,"type":"DIRT"},{"x":6,"y":9,"type":"DIRT"},{"x":7,"y":9,"type":"AIR"},{"x":8,"y":9,"type":"AIR"},{"x":9,"y":9,"type":"AIR"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"DIRT"},{"x":12,"y":9,"type":"AIR"},{"x":13,"y":9,"type":"AIR"},{"x":14,"y":9,"type":"DIRT"},{"x":15,"y":9,"type":"AIR"},{"x":16,"y":9,"type":"DIRT"},{"x":17,"y":9,"type":"AIR"},{"x":18,"y":9,"type":"DIRT"},{"x":19,"y":9,"type":"AIR"},{"x":20,"y":9,"type":"AIR"},{"x":21,"y":9,"type":"DIRT"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"AIR"},{"x":24,"y":9,"type":"AIR"},{"x":25,"y":9,"type":"AIR"},{"x":26,"y":9,"type":"DIRT"},{"x":27,"y":9,"type":"DIRT"},{"x":28,"y":9,"type":"AIR"},{"x":29,"y":9,"type":"AIR"},{"x":30,"y":9,"type":"DIRT"},{"x":31,"y":9,"type":"DIRT"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"DIRT"},{"x":2,"y":10,"type":"DIRT"},{"x":3,"y":10,"type":"DIRT"},{"x":4,"y":10,"type":"DIRT"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"AIR"},{"x":7,"y":10,"type":"AIR"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"AIR"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"AIR"},{"x":12,"y":10,"type":"AIR"},{"x":13,"y":10,"type":"AIR"},{"x":14,"y":10,"type":"DIRT"},{"x":15,"y":10,"type":"AIR"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"AIR"},{"x":18,"y":10,"type":"DIRT"},{"x":19,"y":10,"type":"AIR"},{"x":20,"y":10,"type":"AIR"},{"x":21,"y":10,"type":"AIR"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"AIR"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"AIR"},{"x":26,"y":10,"type":"AIR"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"DIRT"},{"x":29,"y":10,"type":"DIRT"},{"x":30,"y":10,"type":"DIRT"},{"x":31,"y":10,"type":"DIRT"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"DIRT"},{"x":3,"y":11,"type":"DIRT"},{"x":4,"y":11,"type":"DIRT"},{"x":5,"y":11,"type":"DIRT"},{"x":6,"y":11,"type":"AIR"},{"x":7,"y":11,"type":"AIR"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"AIR"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"AIR"},{"x":13,"y":11,"type":"AIR"},{"x":14,"y":11,"type":"DIRT"},{"x":15,"y":11,"type":"DIRT"},{"x":16,"y":11,"type":"DIRT"},{"x":17,"y":11,"type":"DIRT"},{"x":18,"y":11,"type":"DIRT"},{"x":19,"y":11,"type":"AIR"},{"x":20,"y":11,"type":"AIR"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"AIR"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"AIR"},{"x":26,"y":11,"type":"AIR"},{"x":27,"y":11,"type":"DIRT"},{"x":28,"y":11,"type":"DIRT"},{"x":29,"y":11,"type":"DIRT"},{"x":30,"y":11,"type":"DIRT"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"DIRT"},{"x":1,"y":12,"type":"DIRT"},{"x":2,"y":12,"type":"AIR"},{"x":3,"y":12,"type":"DIRT"},{"x":4,"y":12,"type":"DIRT"},{"x":5,"y":12,"type":"DIRT"},{"x":6,"y":12,"type":"DIRT"},{"x":7,"y":12,"type":"AIR"},{"x":8,"y":12,"type":"AIR"},{"x":9,"y":12,"type":"AIR"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"DIRT"},{"x":13,"y":12,"type":"DIRT"},{"x":14,"y":12,"type":"AIR"},{"x":15,"y":12,"type":"AIR"},{"x":16,"y":12,"type":"DIRT"},{"x":17,"y":12,"type":"AIR"},{"x":18,"y":12,"type":"AIR"},{"x":19,"y":12,"type":"DIRT"},{"x":20,"y":12,"type":"DIRT"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"AIR"},{"x":24,"y":12,"type":"AIR"},{"x":25,"y":12,"type":"AIR"},{"x":26,"y":12,"type":"DIRT"},{"x":27,"y":12,"type":"DIRT"},{"x":28,"y":12,"type":"DIRT"},{"x":29,"y":12,"type":"DIRT"},{"x":30,"y":12,"type":"AIR"},{"x":31,"y":12,"type":"DIRT"},{"x":32,"y":12,"type":"DIRT"}],[{"x":0,"y":13,"type":"DIRT"},{"x":1,"y":13,"type":"AIR"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"DIRT"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"DIRT"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"AIR"},{"x":8,"y":13,"type":"AIR"},{"x":9,"y":13,"type":"AIR"},{"x":10,"y":13,"type":"AIR"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"DIRT"},{"x":13,"y":13,"type":"DIRT"},{"x":14,"y":13,"type":"AIR"},{"x":15,"y":13,"type":"AIR"},{"x":16,"y":13,"type":"AIR"},{"x":17,"y":13,"type":"AIR"},{"x":18,"y":13,"type":"AIR"},{"x":19,"y":13,"type":"DIRT"},{"x":20,"y":13,"type":"DIRT"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"AIR"},{"x":23,"y":13,"type":"AIR"},{"x":24,"y":13,"type":"AIR"},{"x":25,"y":13,"type":"AIR"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"DIRT"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"DIRT"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"AIR"},{"x":32,"y":13,"type":"DIRT"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"DIRT"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"AIR"},{"x":8,"y":14,"type":"AIR"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"DIRT"},{"x":11,"y":14,"type":"DIRT"},{"x":12,"y":14,"type":"DIRT"},{"x":13,"y":14,"type":"DIRT"},{"x":14,"y":14,"type":"DIRT"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"AIR"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"DIRT"},{"x":19,"y":14,"type":"DIRT"},{"x":20,"y":14,"type":"DIRT"},{"x":21,"y":14,"type":"DIRT"},{"x":22,"y":14,"type":"DIRT"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"AIR"},{"x":25,"y":14,"type":"AIR"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"DIRT"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"DIRT"},{"x":5,"y":15,"type":"AIR"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"DIRT"},{"x":9,"y":15,"type":"AIR"},{"x":10,"y":15,"type":"DIRT"},{"x":11,"y":15,"type":"DIRT"},{"x":12,"y":15,"type":"DIRT"},{"x":13,"y":15,"type":"DIRT"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"AIR"},{"x":16,"y":15,"type":"DIRT"},{"x":17,"y":15,"type":"AIR"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"DIRT"},{"x":20,"y":15,"type":"DIRT"},{"x":21,"y":15,"type":"DIRT"},{"x":22,"y":15,"type":"DIRT"},{"x":23,"y":15,"type":"AIR"},{"x":24,"y":15,"type":"DIRT"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"AIR"},{"x":28,"y":15,"type":"DIRT"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"AIR"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"DIRT"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"DIRT"},{"x":10,"y":16,"type":"DIRT"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"DIRT"},{"x":14,"y":16,"type":"DIRT"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"DIRT"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"DIRT"},{"x":19,"y":16,"type":"DIRT"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"DIRT"},{"x":23,"y":16,"type":"DIRT"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"DIRT"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"AIR"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"DIRT"},{"x":7,"y":17,"type":"DIRT"},{"x":8,"y":17,"type":"AIR"},{"x":9,"y":17,"type":"DIRT"},{"x":10,"y":17,"type":"DIRT"},{"x":11,"y":17,"type":"AIR"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"DIRT"},{"x":14,"y":17,"type":"DIRT"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"AIR"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"DIRT"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"AIR"},{"x":22,"y":17,"type":"DIRT"},{"x":23,"y":17,"type":"DIRT"},{"x":24,"y":17,"type":"AIR"},{"x":25,"y":17,"type":"DIRT"},{"x":26,"y":17,"type":"DIRT"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"DIRT"},{"x":6,"y":18,"type":"DIRT"},{"x":7,"y":18,"type":"DIRT"},{"x":8,"y":18,"type":"DIRT"},{"x":9,"y":18,"type":"AIR"},{"x":10,"y":18,"type":"AIR"},{"x":11,"y":18,"type":"DIRT"},{"x":12,"y":18,"type":"DIRT"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"AIR"},{"x":15,"y":18,"type":"AIR"},{"x":16,"y":18,"type":"AIR"},{"x":17,"y":18,"type":"AIR"},{"x":18,"y":18,"type":"AIR"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"DIRT"},{"x":21,"y":18,"type":"DIRT"},{"x":22,"y":18,"type":"AIR"},{"x":23,"y":18,"type":"AIR"},{"x":24,"y":18,"type":"DIRT"},{"x":25,"y":18,"type":"DIRT"},{"x":26,"y":18,"type":"DIRT"},{"x":27,"y":18,"type":"DIRT"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"DIRT"},{"x":3,"y":19,"type":"AIR"},{"x":4,"y":19,"type":"AIR"},{"x":5,"y":19,"type":"AIR"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"AIR"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"DIRT"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"AIR"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"AIR"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"AIR"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"DIRT"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"AIR"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"AIR"},{"x":28,"y":19,"type":"AIR"},{"x":29,"y":19,"type":"AIR"},{"x":30,"y":19,"type":"DIRT"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"AIR"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"DIRT"},{"x":3,"y":20,"type":"DIRT"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"AIR"},{"x":6,"y":20,"type":"AIR"},{"x":7,"y":20,"type":"AIR"},{"x":8,"y":20,"type":"AIR"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"AIR"},{"x":11,"y":20,"type":"DIRT"},{"x":12,"y":20,"type":"DIRT"},{"x":13,"y":20,"type":"DIRT"},{"x":14,"y":20,"type":"AIR"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"AIR"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"AIR"},{"x":19,"y":20,"type":"DIRT"},{"x":20,"y":20,"type":"DIRT"},{"x":21,"y":20,"type":"DIRT"},{"x":22,"y":20,"type":"AIR"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"AIR"},{"x":25,"y":20,"type":"AIR"},{"x":26,"y":20,"type":"AIR"},{"x":27,"y":20,"type":"AIR"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"DIRT"},{"x":30,"y":20,"type":"DIRT"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"AIR"}],[{"x":0,"y":21,"type":"AIR"},{"x":1,"y":21,"type":"DIRT"},{"x":2,"y":21,"type":"DIRT"},{"x":3,"y":21,"type":"DIRT"},{"x":4,"y":21,"type":"DIRT"},{"x":5,"y":21,"type":"AIR"},{"x":6,"y":21,"type":"AIR"},{"x":7,"y":21,"type":"AIR"},{"x":8,"y":21,"type":"AIR"},{"x":9,"y":21,"type":"AIR"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"DIRT"},{"x":12,"y":21,"type":"DIRT"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"DIRT"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"AIR"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"DIRT"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"DIRT"},{"x":21,"y":21,"type":"DIRT"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"AIR"},{"x":24,"y":21,"type":"AIR"},{"x":25,"y":21,"type":"AIR"},{"x":26,"y":21,"type":"AIR"},{"x":27,"y":21,"type":"AIR"},{"x":28,"y":21,"type":"DIRT"},{"x":29,"y":21,"type":"DIRT"},{"x":30,"y":21,"type":"DIRT"},{"x":31,"y":21,"type":"DIRT"},{"x":32,"y":21,"type":"AIR"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"DIRT"},{"x":2,"y":22,"type":"AIR"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"DIRT"},{"x":5,"y":22,"type":"AIR"},{"x":6,"y":22,"type":"AIR"},{"x":7,"y":22,"type":"AIR"},{"x":8,"y":22,"type":"AIR"},{"x":9,"y":22,"type":"AIR"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"AIR"},{"x":13,"y":22,"type":"AIR"},{"x":14,"y":22,"type":"DIRT"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"DIRT"},{"x":19,"y":22,"type":"AIR"},{"x":20,"y":22,"type":"AIR"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"AIR"},{"x":24,"y":22,"type":"AIR"},{"x":25,"y":22,"type":"AIR"},{"x":26,"y":22,"type":"AIR"},{"x":27,"y":22,"type":"AIR"},{"x":28,"y":22,"type":"DIRT"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"AIR"},{"x":31,"y":22,"type":"DIRT"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"DIRT"},{"x":2,"y":23,"type":"AIR"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"DIRT"},{"x":5,"y":23,"type":"DIRT"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"AIR"},{"x":8,"y":23,"type":"DIRT"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"AIR"},{"x":13,"y":23,"type":"AIR"},{"x":14,"y":23,"type":"DIRT"},{"x":15,"y":23,"type":"AIR"},{"x":16,"y":23,"type":"AIR"},{"x":17,"y":23,"type":"AIR"},{"x":18,"y":23,"type":"DIRT"},{"x":19,"y":23,"type":"AIR"},{"x":20,"y":23,"type":"AIR"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"DIRT"},{"x":25,"y":23,"type":"AIR"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"DIRT"},{"x":28,"y":23,"type":"DIRT"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"AIR"},{"x":31,"y":23,"type":"DIRT"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"DIRT"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"DIRT"},{"x":5,"y":24,"type":"DIRT"},{"x":6,"y":24,"type":"AIR"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"DIRT"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"AIR"},{"x":11,"y":24,"type":"AIR"},{"x":12,"y":24,"type":"AIR"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"DIRT"},{"x":15,"y":24,"type":"DIRT"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"DIRT"},{"x":18,"y":24,"type":"DIRT"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"AIR"},{"x":21,"y":24,"type":"AIR"},{"x":22,"y":24,"type":"AIR"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"DIRT"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"AIR"},{"x":27,"y":24,"type":"DIRT"},{"x":28,"y":24,"type":"DIRT"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"DIRT"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"DIRT"},{"x":3,"y":25,"type":"AIR"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"DIRT"},{"x":6,"y":25,"type":"AIR"},{"x":7,"y":25,"type":"AIR"},{"x":8,"y":25,"type":"DIRT"},{"x":9,"y":25,"type":"DIRT"},{"x":10,"y":25,"type":"AIR"},{"x":11,"y":25,"type":"AIR"},{"x":12,"y":25,"type":"AIR"},{"x":13,"y":25,"type":"AIR"},{"x":14,"y":25,"type":"DIRT"},{"x":15,"y":25,"type":"AIR"},{"x":16,"y":25,"type":"DIRT"},{"x":17,"y":25,"type":"AIR"},{"x":18,"y":25,"type":"DIRT"},{"x":19,"y":25,"type":"AIR"},{"x":20,"y":25,"type":"AIR"},{"x":21,"y":25,"type":"AIR"},{"x":22,"y":25,"type":"AIR"},{"x":23,"y":25,"type":"DIRT"},{"x":24,"y":25,"type":"DIRT"},{"x":25,"y":25,"type":"AIR"},{"x":26,"y":25,"type":"AIR"},{"x":27,"y":25,"type":"DIRT"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"AIR"},{"x":30,"y":25,"type":"DIRT"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"AIR"},{"x":5,"y":26,"type":"AIR"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"AIR"},{"x":14,"y":26,"type":"AIR"},{"x":15,"y":26,"type":"DIRT"},{"x":16,"y":26,"type":"DIRT"},{"x":17,"y":26,"type":"DIRT"},{"x":18,"y":26,"type":"AIR"},{"x":19,"y":26,"type":"AIR"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"AIR"},{"x":28,"y":26,"type":"AIR"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"AIR"},{"x":5,"y":27,"type":"AIR"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"AIR"},{"x":14,"y":27,"type":"AIR"},{"x":15,"y":27,"type":"DIRT"},{"x":16,"y":27,"type":"DIRT"},{"x":17,"y":27,"type":"DIRT"},{"x":18,"y":27,"type":"AIR"},{"x":19,"y":27,"type":"AIR"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"AIR"},{"x":28,"y":27,"type":"AIR"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"AIR"},{"x":5,"y":28,"type":"AIR"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"DIRT"},{"x":12,"y":28,"type":"AIR"},{"x":13,"y":28,"type":"AIR"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"DIRT"},{"x":16,"y":28,"type":"DIRT"},{"x":17,"y":28,"type":"DIRT"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"AIR"},{"x":20,"y":28,"type":"AIR"},{"x":21,"y":28,"type":"DIRT"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"AIR"},{"x":28,"y":28,"type":"AIR"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"DIRT"},{"x":12,"y":29,"type":"AIR"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"DIRT"},{"x":15,"y":29,"type":"DIRT"},{"x":16,"y":29,"type":"DIRT"},{"x":17,"y":29,"type":"DIRT"},{"x":18,"y":29,"type":"DIRT"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"AIR"},{"x":21,"y":29,"type":"DIRT"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"DIRT"},{"x":12,"y":30,"type":"AIR"},{"x":13,"y":30,"type":"AIR"},{"x":14,"y":30,"type":"DIRT"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"AIR"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"DIRT"},{"x":19,"y":30,"type":"AIR"},{"x":20,"y":30,"type":"AIR"},{"x":21,"y":30,"type":"DIRT"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"DIRT"},{"x":9,"y":31,"type":"DIRT"},{"x":10,"y":31,"type":"DIRT"},{"x":11,"y":31,"type":"DIRT"},{"x":12,"y":31,"type":"AIR"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"AIR"},{"x":16,"y":31,"type":"AIR"},{"x":17,"y":31,"type":"AIR"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"AIR"},{"x":21,"y":31,"type":"DIRT"},{"x":22,"y":31,"type":"DIRT"},{"x":23,"y":31,"type":"DIRT"},{"x":24,"y":31,"type":"DIRT"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"DIRT"},{"x":12,"y":32,"type":"DIRT"},{"x":13,"y":32,"type":"DIRT"},{"x":14,"y":32,"type":"AIR"},{"x":15,"y":32,"type":"AIR"},{"x":16,"y":32,"type":"DIRT"},{"x":17,"y":32,"type":"AIR"},{"x":18,"y":32,"type":"AIR"},{"x":19,"y":32,"type":"DIRT"},{"x":20,"y":32,"type":"DIRT"},{"x":21,"y":32,"type":"DIRT"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.57.04/A-log.csv b/2019-worms/tests/replays/2019.08.19.21.57.04/A-log.csv
new file mode 100644
index 0000000..f620e90
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.57.04/A-log.csv
@@ -0,0 +1,214 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,24,4,100,24,28,100,1,16
+2,move,"move 23 3",1,121,350,150,23,3,100,24,28,100,1,16
+3,move,"move 23 28",2,126,350,150,23,3,100,23,28,100,1,16
+4,move,"move 2 16",3,131,350,150,23,3,100,23,28,100,2,16
+5,dig,"dig 22 3",1,138,350,150,23,3,100,23,28,100,2,16
+6,dig,"dig 22 28",2,145,350,150,23,3,100,23,28,100,2,16
+7,dig,"dig 3 16",3,152,350,150,23,3,100,23,28,100,2,16
+8,move,"move 22 3",1,157,350,150,22,3,100,23,28,100,2,16
+9,move,"move 22 28",2,162,350,150,22,3,100,22,28,100,2,16
+10,move,"move 2 15",3,167,350,150,22,3,100,22,28,100,2,15
+11,move,"move 23 3",1,172,350,150,23,3,100,22,28,100,2,15
+12,move,"move 23 28",2,177,350,150,23,3,100,23,28,100,2,15
+13,move,"move 2 16",3,182,350,150,23,3,100,23,28,100,2,16
+14,move,"move 22 3",1,187,350,150,22,3,100,23,28,100,2,16
+15,move,"move 22 28",2,192,350,150,22,3,100,22,28,100,2,16
+16,move,"move 2 15",3,197,350,150,22,3,100,22,28,100,2,15
+17,move,"move 23 3",1,202,350,150,23,3,100,22,28,100,2,15
+18,move,"move 23 28",2,207,350,150,23,3,100,23,28,100,2,15
+19,move,"move 2 16",3,212,350,150,23,3,100,23,28,100,2,16
+20,move,"move 22 3",1,217,350,150,22,3,100,23,28,100,2,16
+21,move,"move 22 28",2,222,350,150,22,3,100,22,28,100,2,16
+22,move,"move 2 15",3,227,350,150,22,3,100,22,28,100,2,15
+23,move,"move 23 3",1,232,350,150,23,3,100,22,28,100,2,15
+24,move,"move 23 28",2,237,350,150,23,3,100,23,28,100,2,15
+25,move,"move 2 16",3,242,350,150,23,3,100,23,28,100,2,16
+26,move,"move 22 3",1,247,350,150,22,3,100,23,28,100,2,16
+27,move,"move 22 28",2,252,350,150,22,3,100,22,28,100,2,16
+28,move,"move 2 15",3,257,350,150,22,3,100,22,28,100,2,15
+29,move,"move 23 3",1,262,350,150,23,3,100,22,28,100,2,15
+30,move,"move 23 28",2,267,350,150,23,3,100,23,28,100,2,15
+31,move,"move 2 16",3,272,350,150,23,3,100,23,28,100,2,16
+32,move,"move 22 3",1,277,350,150,22,3,100,23,28,100,2,16
+33,move,"move 22 28",2,282,350,150,22,3,100,22,28,100,2,16
+34,move,"move 2 15",3,287,350,150,22,3,100,22,28,100,2,15
+35,dig,"dig 22 2",1,294,350,150,22,3,100,22,28,100,2,15
+36,move,"move 23 27",2,299,350,150,22,3,100,23,27,100,2,15
+37,move,"move 1 16",3,304,350,150,22,3,100,23,27,100,1,16
+38,move,"move 22 2",1,309,350,150,22,2,100,23,27,100,1,16
+39,move,"move 22 28",2,314,350,150,22,2,100,22,28,100,1,16
+40,move,"move 2 16",3,319,350,150,22,2,100,22,28,100,2,16
+41,move,"move 23 3",1,324,350,150,23,3,100,22,28,100,2,16
+42,move,"move 23 28",2,329,350,150,23,3,100,23,28,100,2,16
+43,move,"move 2 15",3,334,350,150,23,3,100,23,28,100,2,15
+44,move,"move 22 3",1,339,350,150,22,3,100,23,28,100,2,15
+45,move,"move 22 28",2,344,350,150,22,3,100,22,28,100,2,15
+46,move,"move 2 16",3,349,350,150,22,3,100,22,28,100,2,16
+47,move,"move 23 3",1,354,350,150,23,3,100,22,28,100,2,16
+48,move,"move 23 28",2,359,350,150,23,3,100,23,28,100,2,16
+49,move,"move 3 16",3,364,350,150,23,3,100,23,28,100,3,16
+50,move,"move 23 4",1,369,350,150,23,4,100,23,28,100,3,16
+51,move,"move 24 27",2,374,350,150,23,4,100,24,27,100,3,16
+52,move,"move 2 16",3,379,350,150,23,4,100,24,27,100,2,16
+53,move,"move 23 3",1,384,350,150,23,3,100,24,27,100,2,16
+54,move,"move 23 28",2,389,350,150,23,3,100,23,28,100,2,16
+55,move,"move 2 15",3,394,350,150,23,3,100,23,28,100,2,15
+56,move,"move 22 3",1,399,350,150,22,3,100,23,28,100,2,15
+57,move,"move 22 28",2,404,350,150,22,3,100,22,28,100,2,15
+58,move,"move 2 16",3,409,350,150,22,3,100,22,28,100,2,16
+59,move,"move 23 3",1,414,350,150,23,3,100,22,28,100,2,16
+60,move,"move 23 28",2,419,350,150,23,3,100,23,28,100,2,16
+61,move,"move 2 15",3,424,350,150,23,3,100,23,28,100,2,15
+62,move,"move 22 3",1,429,350,150,22,3,100,23,28,100,2,15
+63,move,"move 22 28",2,434,350,150,22,3,100,22,28,100,2,15
+64,move,"move 2 16",3,439,350,150,22,3,100,22,28,100,2,16
+65,move,"move 23 4",1,444,350,150,23,4,100,22,28,100,2,16
+66,dig,"dig 22 29",2,451,350,150,23,4,100,22,28,100,2,16
+67,dig,"dig 3 15",3,458,350,150,23,4,100,22,28,100,2,16
+68,move,"move 24 4",1,463,350,150,24,4,100,22,28,100,2,16
+69,move,"move 23 28",2,468,350,150,24,4,100,23,28,100,2,16
+70,nothing,"nothing "Player chose to do nothing"",3,468,350,150,24,4,100,23,28,100,2,16
+71,move,"move 25 3",1,473,350,150,25,3,100,23,28,100,2,16
+72,move,"move 23 29",2,478,350,150,25,3,100,23,29,100,2,16
+73,move,"move 1 15",3,483,350,150,25,3,100,23,29,100,1,15
+74,dig,"dig 24 2",1,490,350,150,25,3,100,23,29,100,1,15
+75,move,"move 22 29",2,495,350,150,25,3,100,22,29,100,1,15
+76,dig,"dig 2 14",3,502,350,150,25,3,100,22,29,100,1,15
+77,nothing,"nothing "Player chose to do nothing"",1,502,350,150,25,3,100,22,29,100,1,15
+78,move,"move 23 28",2,507,350,150,25,3,100,23,28,100,1,15
+79,move,"move 2 16",3,512,350,150,25,3,100,23,28,100,2,16
+80,dig,"dig 25 2",1,512,350,150,25,3,100,23,28,100,2,16
+81,move,"move 22 29",2,517,350,150,25,3,100,22,29,100,2,16
+82,move,"move 3 15",3,522,350,150,25,3,100,22,29,100,3,15
+83,move,"move 25 4",1,522,350,150,25,3,100,22,29,100,3,15
+84,move,"move 23 29",2,521,330,130,25,3,100,23,29,100,3,15
+85,dig,"dig 4 14",3,528,330,130,25,3,100,23,29,100,3,15
+86,move,"move 24 4",1,525,322,122,25,3,100,23,29,100,3,15
+87,shoot,"shoot S",1,538,314,114,25,3,100,23,29,100,3,15
+88,shoot,"shoot S",1,552,306,106,25,3,100,23,29,100,3,15
+89,shoot,"shoot S",1,565,298,98,25,3,100,23,29,100,3,15
+90,shoot,"shoot S",1,578,290,90,25,3,100,23,29,100,3,15
+91,move,"move 24 29",2,583,290,90,25,3,100,24,29,100,3,15
+92,shoot,"shoot S",1,593,270,70,25,3,100,24,29,100,3,15
+93,move,"move 25 29",2,598,270,70,25,3,100,25,29,100,3,15
+94,move,"move 2 16",3,600,262,62,25,3,100,25,29,100,2,16
+95,move,"move 24 2",1,593,242,42,25,3,100,25,29,100,2,16
+96,move,"move 24 29",2,598,242,42,25,3,100,24,29,100,2,16
+97,move,"move 2 15",3,601,234,34,25,3,100,24,29,100,2,15
+98,shoot,"shoot S",1,617,234,34,25,3,100,24,29,100,2,15
+99,dig,"dig 24 30",2,624,234,34,25,3,100,24,29,100,2,15
+100,move,"move 2 16",3,626,226,26,25,3,100,24,29,100,2,16
+101,shoot,"shoot S",1,642,226,26,25,3,100,24,29,100,2,16
+102,move,"move 24 28",2,647,226,26,25,3,100,24,28,100,2,16
+103,move,"move 2 15",3,649,218,18,25,3,100,24,28,100,2,15
+104,shoot,"shoot S",1,664,215,15,25,3,100,24,28,100,2,15
+105,move,"move 25 28",2,668,212,12,25,3,100,25,28,100,2,15
+106,move,"move 2 16",3,670,201,1,25,3,100,25,28,100,2,16
+107,shoot,"shoot S",1,685,200,-2,25,3,100,25,28,100,2,16
+108,move,"move 25 27",2,690,200,-2,25,3,100,25,27,100,2,16
+109,dig,"dig 3 17",3,697,200,-2,25,3,100,25,27,100,2,16
+110,move,"move 24 27",2,702,200,-2,25,3,100,24,27,100,2,16
+111,nothing,"nothing "Player chose to do nothing"",3,702,200,-2,25,3,100,24,27,100,2,16
+112,move,"move 25 28",2,707,200,-2,25,3,100,25,28,100,2,16
+113,move,"move 1 17",3,712,200,-2,25,3,100,25,28,100,1,17
+114,move,"move 25 27",2,717,200,-2,25,3,100,25,27,100,1,17
+115,move,"move 2 17",3,722,200,-2,25,3,100,25,27,100,2,17
+116,dig,"dig 24 26",2,729,200,-2,25,3,100,25,27,100,2,17
+117,move,"move 3 17",3,734,200,-2,25,3,100,25,27,100,3,17
+118,move,"move 24 26",2,739,200,-2,25,3,100,24,26,100,3,17
+119,move,"move 3 16",3,744,200,-2,25,3,100,24,26,100,3,16
+120,move,"move 25 25",2,749,200,-2,25,3,100,25,25,100,3,16
+121,move,"move 4 16",3,754,200,-2,25,3,100,25,25,100,4,16
+122,move,"move 26 25",2,759,200,-2,25,3,100,26,25,100,4,16
+123,move,"move 3 17",3,764,200,-2,25,3,100,26,25,100,3,17
+124,move,"move 25 24",2,769,200,-2,25,3,100,25,24,100,3,17
+125,move,"move 2 16",3,774,200,-2,25,3,100,25,24,100,2,16
+126,move,"move 26 24",2,779,200,-2,25,3,100,26,24,100,2,16
+127,move,"move 3 17",3,784,200,-2,25,3,100,26,24,100,3,17
+128,dig,"dig 27 24",2,791,200,-2,25,3,100,26,24,100,3,17
+129,dig,"dig 3 18",3,798,200,-2,25,3,100,26,24,100,3,17
+130,move,"move 25 23",2,803,200,-2,25,3,100,25,23,100,3,17
+131,move,"move 4 16",3,808,200,-2,25,3,100,25,23,100,4,16
+132,move,"move 26 24",2,813,200,-2,25,3,100,26,24,100,4,16
+133,dig,"dig 4 15",3,820,200,-2,25,3,100,26,24,100,4,16
+134,move,"move 26 23",2,825,200,-2,25,3,100,26,23,100,4,16
+135,move,"move 5 15",3,830,200,-2,25,3,100,26,23,100,5,15
+136,move,"move 26 22",2,835,200,-2,25,3,100,26,22,100,5,15
+137,dig,"dig 5 16",3,842,200,-2,25,3,100,26,22,100,5,15
+138,move,"move 25 21",2,847,200,-2,25,3,100,25,21,100,5,15
+139,dig,"dig 5 14",3,854,200,-2,25,3,100,25,21,100,5,15
+140,banana,"banana 25 16",2,937,200,-2,25,3,100,25,21,100,5,15
+141,move,"move 5 14",3,942,200,-2,25,3,100,25,21,100,5,14
+142,banana,"banana 25 16",2,1022,200,-2,25,3,100,25,21,100,5,14
+143,dig,"dig 4 13",3,1029,200,-2,25,3,100,25,21,100,5,14
+144,banana,"banana 26 16",2,1094,200,-2,25,3,100,25,21,100,5,14
+145,nothing,"nothing "Player chose to do nothing"",3,1094,200,-2,25,3,100,25,21,100,5,14
+146,shoot,"shoot N",2,1110,200,-2,25,3,100,25,21,100,5,14
+147,move,"move 5 15",3,1113,192,-2,25,3,92,25,21,100,5,15
+148,shoot,"shoot N",2,1129,192,-2,25,3,92,25,21,100,5,15
+149,move,"move 6 14",3,1134,192,-2,25,3,92,25,21,100,6,14
+150,shoot,"shoot N",2,1147,184,-2,25,3,84,25,21,100,6,14
+151,move,"move 7 14",3,1152,184,-2,25,3,84,25,21,100,7,14
+152,shoot,"shoot N",2,1208,184,-2,25,3,84,25,21,100,7,14
+153,invalid,"invalid",3,1204,184,-2,25,3,84,25,21,100,7,14
+154,shoot,"shoot N",2,1220,184,-2,25,3,84,25,21,100,7,14
+155,move,"move 7 13",3,1222,176,-2,25,3,76,25,21,100,7,13
+156,shoot,"shoot N",2,1238,176,-2,25,3,76,25,21,100,7,13
+157,move,"move 6 14",3,1241,168,-2,25,3,68,25,21,100,6,14
+158,shoot,"shoot N",2,1257,168,-2,25,3,68,25,21,100,6,14
+159,move,"move 5 15",3,1259,160,-2,25,3,60,25,21,100,5,15
+160,shoot,"shoot N",2,1275,160,-2,25,3,60,25,21,100,5,15
+161,move,"move 4 16",3,1277,152,-2,25,3,52,25,21,100,4,16
+162,shoot,"shoot N",2,1293,152,-2,25,3,52,25,21,100,4,16
+163,move,"move 5 15",3,1296,144,-2,25,3,44,25,21,100,5,15
+164,shoot,"shoot N",2,1312,144,-2,25,3,44,25,21,100,5,15
+165,move,"move 5 14",3,1314,136,-2,25,3,36,25,21,100,5,14
+166,shoot,"shoot N",2,1330,136,-2,25,3,36,25,21,100,5,14
+167,move,"move 6 15",3,1335,136,-2,25,3,36,25,21,100,6,15
+168,shoot,"shoot N",2,1348,128,-2,25,3,28,25,21,100,6,15
+169,dig,"dig 6 16",3,1355,128,-2,25,3,28,25,21,100,6,15
+170,shoot,"shoot N",2,1371,128,-2,25,3,28,25,21,100,6,15
+171,move,"move 6 16",3,1374,120,-2,25,3,20,25,21,100,6,16
+172,shoot,"shoot N",2,1390,120,-2,25,3,20,25,21,100,6,16
+173,move,"move 7 16",3,1392,112,-2,25,3,12,25,21,100,7,16
+174,shoot,"shoot N",2,1408,112,-2,25,3,12,25,21,100,7,16
+175,move,"move 8 16",3,1410,104,-2,25,3,4,25,21,100,8,16
+176,shoot,"shoot N",2,1466,104,-2,25,3,4,25,21,100,8,16
+177,nothing,"nothing "Player chose to do nothing"",3,1466,104,-2,25,3,4,25,21,100,8,16
+178,shoot,"shoot N",2,1481,100,-2,25,3,-4,25,21,100,8,16
+179,dig,"dig 7 17",3,1488,100,-2,25,3,-4,25,21,100,8,16
+180,dig,"dig 8 15",3,1495,100,-2,25,3,-4,25,21,100,8,16
+181,move,"move 8 15",3,1500,100,-2,25,3,-4,25,21,100,8,15
+182,move,"move 9 15",3,1505,100,-2,25,3,-4,25,21,100,9,15
+183,dig,"dig 10 15",3,1512,100,-2,25,3,-4,25,21,100,9,15
+184,move,"move 10 15",3,1517,100,-2,25,3,-4,25,21,100,10,15
+185,nothing,"nothing "Player chose to do nothing"",3,1517,100,-2,25,3,-4,25,21,100,10,15
+186,dig,"dig 10 16",3,1524,100,-2,25,3,-4,25,21,100,10,15
+187,dig,"dig 10 14",3,1531,100,-2,25,3,-4,25,21,100,10,15
+188,dig,"dig 11 16",3,1538,100,-2,25,3,-4,25,21,100,10,15
+189,move,"move 9 15",3,1543,100,-2,25,3,-4,25,21,100,9,15
+190,nothing,"nothing "Player chose to do nothing"",3,1543,100,-2,25,3,-4,25,21,100,9,15
+191,move,"move 8 15",3,1548,100,-2,25,3,-4,25,21,100,8,15
+192,move,"move 8 16",3,1553,100,-2,25,3,-4,25,21,100,8,16
+193,move,"move 8 17",3,1558,100,-2,25,3,-4,25,21,100,8,17
+194,move,"move 7 17",3,1563,100,-2,25,3,-4,25,21,100,7,17
+195,dig,"dig 7 18",3,1570,100,-2,25,3,-4,25,21,100,7,17
+196,move,"move 7 18",3,1575,100,-2,25,3,-4,25,21,100,7,18
+197,nothing,"nothing "Player chose to do nothing"",3,1575,100,-2,25,3,-4,25,21,100,7,18
+198,nothing,"nothing "Player chose to do nothing"",3,1575,100,-2,25,3,-4,25,21,100,7,18
+199,move,"move 7 17",3,1580,100,-2,25,3,-4,25,21,100,7,17
+200,move,"move 8 17",3,1585,100,-2,25,3,-4,25,21,100,8,17
+201,move,"move 8 16",3,1590,100,-2,25,3,-4,25,21,100,8,16
+202,snowball,"snowball 11 17",3,1607,100,-2,25,3,-4,25,21,100,8,16
+203,snowball,"snowball 10 16",3,1624,100,-2,25,3,-4,25,21,100,8,16
+204,snowball,"snowball 10 16",3,1641,100,-2,25,3,-4,25,21,100,8,16
+205,move,"move 8 17",3,1646,100,-2,25,3,-4,25,21,100,8,17
+206,dig,"dig 8 18",3,1653,100,-2,25,3,-4,25,21,100,8,17
+207,move,"move 9 18",3,1658,100,-2,25,3,-4,25,21,100,9,18
+208,move,"move 9 19",3,1663,100,-2,25,3,-4,25,21,100,9,19
+209,move,"move 10 19",3,1668,100,-2,25,3,-4,25,21,100,10,19
+210,shoot,"shoot N",3,1681,92,-2,25,3,-4,25,21,92,10,19
+211,shoot,"shoot N",3,1695,84,-2,25,3,-4,25,21,84,10,19
+212,shoot,"shoot N",3,1708,76,-2,25,3,-4,25,21,76,10,19
+213,move,"move 11 18",3,1713,76,-2,25,3,-4,25,21,76,11,18
diff --git a/2019-worms/tests/replays/2019.08.19.21.57.04/B-init.json b/2019-worms/tests/replays/2019.08.19.21.57.04/B-init.json
new file mode 100644
index 0000000..2d71d9a
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.57.04/B-init.json
@@ -0,0 +1 @@
+{"currentRound":1,"maxRounds":400,"pushbackDamage":20,"lavaDamage":3,"mapSize":33,"currentWormId":1,"consecutiveDoNothingCount":0,"myPlayer":{"id":2,"score":116,"health":350,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]},"opponents":[{"id":1,"score":116,"currentWormId":1,"remainingWormSelections":5,"previousCommand":"nothing","worms":[{"id":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"},{"id":2,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"},{"id":3,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}]}],"map":[[{"x":0,"y":0,"type":"DEEP_SPACE"},{"x":1,"y":0,"type":"DEEP_SPACE"},{"x":2,"y":0,"type":"DEEP_SPACE"},{"x":3,"y":0,"type":"DEEP_SPACE"},{"x":4,"y":0,"type":"DEEP_SPACE"},{"x":5,"y":0,"type":"DEEP_SPACE"},{"x":6,"y":0,"type":"DEEP_SPACE"},{"x":7,"y":0,"type":"DEEP_SPACE"},{"x":8,"y":0,"type":"DEEP_SPACE"},{"x":9,"y":0,"type":"DEEP_SPACE"},{"x":10,"y":0,"type":"DEEP_SPACE"},{"x":11,"y":0,"type":"DIRT"},{"x":12,"y":0,"type":"DIRT"},{"x":13,"y":0,"type":"DIRT"},{"x":14,"y":0,"type":"AIR"},{"x":15,"y":0,"type":"AIR"},{"x":16,"y":0,"type":"DIRT"},{"x":17,"y":0,"type":"AIR"},{"x":18,"y":0,"type":"AIR"},{"x":19,"y":0,"type":"DIRT"},{"x":20,"y":0,"type":"DIRT"},{"x":21,"y":0,"type":"DIRT"},{"x":22,"y":0,"type":"DEEP_SPACE"},{"x":23,"y":0,"type":"DEEP_SPACE"},{"x":24,"y":0,"type":"DEEP_SPACE"},{"x":25,"y":0,"type":"DEEP_SPACE"},{"x":26,"y":0,"type":"DEEP_SPACE"},{"x":27,"y":0,"type":"DEEP_SPACE"},{"x":28,"y":0,"type":"DEEP_SPACE"},{"x":29,"y":0,"type":"DEEP_SPACE"},{"x":30,"y":0,"type":"DEEP_SPACE"},{"x":31,"y":0,"type":"DEEP_SPACE"},{"x":32,"y":0,"type":"DEEP_SPACE"}],[{"x":0,"y":1,"type":"DEEP_SPACE"},{"x":1,"y":1,"type":"DEEP_SPACE"},{"x":2,"y":1,"type":"DEEP_SPACE"},{"x":3,"y":1,"type":"DEEP_SPACE"},{"x":4,"y":1,"type":"DEEP_SPACE"},{"x":5,"y":1,"type":"DEEP_SPACE"},{"x":6,"y":1,"type":"DEEP_SPACE"},{"x":7,"y":1,"type":"DEEP_SPACE"},{"x":8,"y":1,"type":"DIRT"},{"x":9,"y":1,"type":"DIRT"},{"x":10,"y":1,"type":"AIR"},{"x":11,"y":1,"type":"DIRT"},{"x":12,"y":1,"type":"DIRT"},{"x":13,"y":1,"type":"AIR"},{"x":14,"y":1,"type":"AIR"},{"x":15,"y":1,"type":"DIRT"},{"x":16,"y":1,"type":"DIRT"},{"x":17,"y":1,"type":"DIRT"},{"x":18,"y":1,"type":"AIR"},{"x":19,"y":1,"type":"AIR"},{"x":20,"y":1,"type":"DIRT"},{"x":21,"y":1,"type":"DIRT"},{"x":22,"y":1,"type":"AIR"},{"x":23,"y":1,"type":"DIRT"},{"x":24,"y":1,"type":"DIRT"},{"x":25,"y":1,"type":"DEEP_SPACE"},{"x":26,"y":1,"type":"DEEP_SPACE"},{"x":27,"y":1,"type":"DEEP_SPACE"},{"x":28,"y":1,"type":"DEEP_SPACE"},{"x":29,"y":1,"type":"DEEP_SPACE"},{"x":30,"y":1,"type":"DEEP_SPACE"},{"x":31,"y":1,"type":"DEEP_SPACE"},{"x":32,"y":1,"type":"DEEP_SPACE"}],[{"x":0,"y":2,"type":"DEEP_SPACE"},{"x":1,"y":2,"type":"DEEP_SPACE"},{"x":2,"y":2,"type":"DEEP_SPACE"},{"x":3,"y":2,"type":"DEEP_SPACE"},{"x":4,"y":2,"type":"DEEP_SPACE"},{"x":5,"y":2,"type":"DEEP_SPACE"},{"x":6,"y":2,"type":"DEEP_SPACE"},{"x":7,"y":2,"type":"DIRT"},{"x":8,"y":2,"type":"DIRT"},{"x":9,"y":2,"type":"DIRT"},{"x":10,"y":2,"type":"DIRT"},{"x":11,"y":2,"type":"DIRT"},{"x":12,"y":2,"type":"DIRT"},{"x":13,"y":2,"type":"DIRT"},{"x":14,"y":2,"type":"DIRT"},{"x":15,"y":2,"type":"AIR"},{"x":16,"y":2,"type":"AIR"},{"x":17,"y":2,"type":"AIR"},{"x":18,"y":2,"type":"DIRT"},{"x":19,"y":2,"type":"DIRT"},{"x":20,"y":2,"type":"DIRT"},{"x":21,"y":2,"type":"DIRT"},{"x":22,"y":2,"type":"DIRT"},{"x":23,"y":2,"type":"DIRT"},{"x":24,"y":2,"type":"DIRT"},{"x":25,"y":2,"type":"DIRT"},{"x":26,"y":2,"type":"DEEP_SPACE"},{"x":27,"y":2,"type":"DEEP_SPACE"},{"x":28,"y":2,"type":"DEEP_SPACE"},{"x":29,"y":2,"type":"DEEP_SPACE"},{"x":30,"y":2,"type":"DEEP_SPACE"},{"x":31,"y":2,"type":"DEEP_SPACE"},{"x":32,"y":2,"type":"DEEP_SPACE"}],[{"x":0,"y":3,"type":"DEEP_SPACE"},{"x":1,"y":3,"type":"DEEP_SPACE"},{"x":2,"y":3,"type":"DEEP_SPACE"},{"x":3,"y":3,"type":"DEEP_SPACE"},{"x":4,"y":3,"type":"DEEP_SPACE"},{"x":5,"y":3,"type":"DEEP_SPACE"},{"x":6,"y":3,"type":"DIRT"},{"x":7,"y":3,"type":"AIR"},{"x":8,"y":3,"type":"AIR"},{"x":9,"y":3,"type":"AIR"},{"x":10,"y":3,"type":"DIRT"},{"x":11,"y":3,"type":"DIRT"},{"x":12,"y":3,"type":"DIRT"},{"x":13,"y":3,"type":"DIRT"},{"x":14,"y":3,"type":"AIR"},{"x":15,"y":3,"type":"AIR"},{"x":16,"y":3,"type":"AIR"},{"x":17,"y":3,"type":"AIR"},{"x":18,"y":3,"type":"AIR"},{"x":19,"y":3,"type":"DIRT"},{"x":20,"y":3,"type":"DIRT"},{"x":21,"y":3,"type":"DIRT"},{"x":22,"y":3,"type":"DIRT"},{"x":23,"y":3,"type":"AIR"},{"x":24,"y":3,"type":"AIR"},{"x":25,"y":3,"type":"AIR"},{"x":26,"y":3,"type":"DIRT"},{"x":27,"y":3,"type":"DEEP_SPACE"},{"x":28,"y":3,"type":"DEEP_SPACE"},{"x":29,"y":3,"type":"DEEP_SPACE"},{"x":30,"y":3,"type":"DEEP_SPACE"},{"x":31,"y":3,"type":"DEEP_SPACE"},{"x":32,"y":3,"type":"DEEP_SPACE"}],[{"x":0,"y":4,"type":"DEEP_SPACE"},{"x":1,"y":4,"type":"DEEP_SPACE"},{"x":2,"y":4,"type":"DEEP_SPACE"},{"x":3,"y":4,"type":"DEEP_SPACE"},{"x":4,"y":4,"type":"DIRT"},{"x":5,"y":4,"type":"DIRT"},{"x":6,"y":4,"type":"DIRT"},{"x":7,"y":4,"type":"AIR"},{"x":8,"y":4,"type":"AIR","occupier":{"id":2,"playerId":2,"health":100,"position":{"x":8,"y":4},"weapon":{"damage":8,"range":4},"bananaBombs":{"damage":20,"range":5,"count":3,"damageRadius":2},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":9,"y":4,"type":"AIR"},{"x":10,"y":4,"type":"DIRT"},{"x":11,"y":4,"type":"DIRT"},{"x":12,"y":4,"type":"AIR"},{"x":13,"y":4,"type":"AIR"},{"x":14,"y":4,"type":"AIR"},{"x":15,"y":4,"type":"AIR"},{"x":16,"y":4,"type":"AIR"},{"x":17,"y":4,"type":"AIR"},{"x":18,"y":4,"type":"AIR"},{"x":19,"y":4,"type":"AIR"},{"x":20,"y":4,"type":"AIR"},{"x":21,"y":4,"type":"DIRT"},{"x":22,"y":4,"type":"DIRT"},{"x":23,"y":4,"type":"AIR"},{"x":24,"y":4,"type":"AIR","occupier":{"id":1,"playerId":1,"health":150,"position":{"x":24,"y":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":25,"y":4,"type":"AIR"},{"x":26,"y":4,"type":"DIRT"},{"x":27,"y":4,"type":"DIRT"},{"x":28,"y":4,"type":"DIRT"},{"x":29,"y":4,"type":"DEEP_SPACE"},{"x":30,"y":4,"type":"DEEP_SPACE"},{"x":31,"y":4,"type":"DEEP_SPACE"},{"x":32,"y":4,"type":"DEEP_SPACE"}],[{"x":0,"y":5,"type":"DEEP_SPACE"},{"x":1,"y":5,"type":"DEEP_SPACE"},{"x":2,"y":5,"type":"DEEP_SPACE"},{"x":3,"y":5,"type":"DEEP_SPACE"},{"x":4,"y":5,"type":"DIRT"},{"x":5,"y":5,"type":"DIRT"},{"x":6,"y":5,"type":"DIRT"},{"x":7,"y":5,"type":"AIR"},{"x":8,"y":5,"type":"AIR"},{"x":9,"y":5,"type":"AIR"},{"x":10,"y":5,"type":"DIRT"},{"x":11,"y":5,"type":"DIRT"},{"x":12,"y":5,"type":"DIRT"},{"x":13,"y":5,"type":"DIRT"},{"x":14,"y":5,"type":"AIR"},{"x":15,"y":5,"type":"AIR"},{"x":16,"y":5,"type":"AIR"},{"x":17,"y":5,"type":"AIR"},{"x":18,"y":5,"type":"AIR"},{"x":19,"y":5,"type":"DIRT"},{"x":20,"y":5,"type":"DIRT"},{"x":21,"y":5,"type":"DIRT"},{"x":22,"y":5,"type":"DIRT"},{"x":23,"y":5,"type":"AIR"},{"x":24,"y":5,"type":"AIR"},{"x":25,"y":5,"type":"AIR"},{"x":26,"y":5,"type":"DIRT"},{"x":27,"y":5,"type":"DIRT"},{"x":28,"y":5,"type":"DIRT"},{"x":29,"y":5,"type":"DEEP_SPACE"},{"x":30,"y":5,"type":"DEEP_SPACE"},{"x":31,"y":5,"type":"DEEP_SPACE"},{"x":32,"y":5,"type":"DEEP_SPACE"}],[{"x":0,"y":6,"type":"DEEP_SPACE"},{"x":1,"y":6,"type":"DEEP_SPACE"},{"x":2,"y":6,"type":"DEEP_SPACE"},{"x":3,"y":6,"type":"AIR"},{"x":4,"y":6,"type":"DIRT"},{"x":5,"y":6,"type":"DIRT"},{"x":6,"y":6,"type":"DIRT"},{"x":7,"y":6,"type":"DIRT"},{"x":8,"y":6,"type":"DIRT"},{"x":9,"y":6,"type":"DIRT"},{"x":10,"y":6,"type":"DIRT"},{"x":11,"y":6,"type":"AIR"},{"x":12,"y":6,"type":"AIR"},{"x":13,"y":6,"type":"DIRT"},{"x":14,"y":6,"type":"DIRT"},{"x":15,"y":6,"type":"AIR"},{"x":16,"y":6,"type":"AIR"},{"x":17,"y":6,"type":"AIR"},{"x":18,"y":6,"type":"DIRT"},{"x":19,"y":6,"type":"DIRT"},{"x":20,"y":6,"type":"AIR"},{"x":21,"y":6,"type":"AIR"},{"x":22,"y":6,"type":"DIRT"},{"x":23,"y":6,"type":"DIRT"},{"x":24,"y":6,"type":"DIRT"},{"x":25,"y":6,"type":"DIRT"},{"x":26,"y":6,"type":"DIRT"},{"x":27,"y":6,"type":"DIRT"},{"x":28,"y":6,"type":"DIRT"},{"x":29,"y":6,"type":"AIR"},{"x":30,"y":6,"type":"DEEP_SPACE"},{"x":31,"y":6,"type":"DEEP_SPACE"},{"x":32,"y":6,"type":"DEEP_SPACE"}],[{"x":0,"y":7,"type":"DEEP_SPACE"},{"x":1,"y":7,"type":"DEEP_SPACE"},{"x":2,"y":7,"type":"DIRT"},{"x":3,"y":7,"type":"AIR"},{"x":4,"y":7,"type":"DIRT"},{"x":5,"y":7,"type":"DIRT"},{"x":6,"y":7,"type":"DIRT"},{"x":7,"y":7,"type":"AIR"},{"x":8,"y":7,"type":"AIR"},{"x":9,"y":7,"type":"AIR"},{"x":10,"y":7,"type":"AIR"},{"x":11,"y":7,"type":"DIRT"},{"x":12,"y":7,"type":"DIRT"},{"x":13,"y":7,"type":"DIRT"},{"x":14,"y":7,"type":"DIRT"},{"x":15,"y":7,"type":"DIRT"},{"x":16,"y":7,"type":"AIR"},{"x":17,"y":7,"type":"DIRT"},{"x":18,"y":7,"type":"DIRT"},{"x":19,"y":7,"type":"DIRT"},{"x":20,"y":7,"type":"DIRT"},{"x":21,"y":7,"type":"DIRT"},{"x":22,"y":7,"type":"AIR"},{"x":23,"y":7,"type":"AIR"},{"x":24,"y":7,"type":"AIR"},{"x":25,"y":7,"type":"AIR"},{"x":26,"y":7,"type":"DIRT"},{"x":27,"y":7,"type":"DIRT"},{"x":28,"y":7,"type":"DIRT"},{"x":29,"y":7,"type":"AIR"},{"x":30,"y":7,"type":"DIRT"},{"x":31,"y":7,"type":"DEEP_SPACE"},{"x":32,"y":7,"type":"DEEP_SPACE"}],[{"x":0,"y":8,"type":"DEEP_SPACE"},{"x":1,"y":8,"type":"DIRT"},{"x":2,"y":8,"type":"DIRT"},{"x":3,"y":8,"type":"AIR"},{"x":4,"y":8,"type":"AIR"},{"x":5,"y":8,"type":"DIRT"},{"x":6,"y":8,"type":"DIRT"},{"x":7,"y":8,"type":"AIR"},{"x":8,"y":8,"type":"AIR"},{"x":9,"y":8,"type":"AIR"},{"x":10,"y":8,"type":"AIR"},{"x":11,"y":8,"type":"DIRT"},{"x":12,"y":8,"type":"DIRT"},{"x":13,"y":8,"type":"AIR"},{"x":14,"y":8,"type":"AIR"},{"x":15,"y":8,"type":"AIR"},{"x":16,"y":8,"type":"DIRT"},{"x":17,"y":8,"type":"AIR"},{"x":18,"y":8,"type":"AIR"},{"x":19,"y":8,"type":"AIR"},{"x":20,"y":8,"type":"DIRT"},{"x":21,"y":8,"type":"DIRT"},{"x":22,"y":8,"type":"AIR"},{"x":23,"y":8,"type":"AIR"},{"x":24,"y":8,"type":"AIR"},{"x":25,"y":8,"type":"AIR"},{"x":26,"y":8,"type":"DIRT"},{"x":27,"y":8,"type":"DIRT"},{"x":28,"y":8,"type":"AIR"},{"x":29,"y":8,"type":"AIR"},{"x":30,"y":8,"type":"DIRT"},{"x":31,"y":8,"type":"DIRT"},{"x":32,"y":8,"type":"DEEP_SPACE"}],[{"x":0,"y":9,"type":"DEEP_SPACE"},{"x":1,"y":9,"type":"DIRT"},{"x":2,"y":9,"type":"DIRT"},{"x":3,"y":9,"type":"AIR"},{"x":4,"y":9,"type":"AIR"},{"x":5,"y":9,"type":"DIRT"},{"x":6,"y":9,"type":"DIRT"},{"x":7,"y":9,"type":"AIR"},{"x":8,"y":9,"type":"AIR"},{"x":9,"y":9,"type":"AIR"},{"x":10,"y":9,"type":"DIRT"},{"x":11,"y":9,"type":"DIRT"},{"x":12,"y":9,"type":"AIR"},{"x":13,"y":9,"type":"AIR"},{"x":14,"y":9,"type":"DIRT"},{"x":15,"y":9,"type":"AIR"},{"x":16,"y":9,"type":"DIRT"},{"x":17,"y":9,"type":"AIR"},{"x":18,"y":9,"type":"DIRT"},{"x":19,"y":9,"type":"AIR"},{"x":20,"y":9,"type":"AIR"},{"x":21,"y":9,"type":"DIRT"},{"x":22,"y":9,"type":"DIRT"},{"x":23,"y":9,"type":"AIR"},{"x":24,"y":9,"type":"AIR"},{"x":25,"y":9,"type":"AIR"},{"x":26,"y":9,"type":"DIRT"},{"x":27,"y":9,"type":"DIRT"},{"x":28,"y":9,"type":"AIR"},{"x":29,"y":9,"type":"AIR"},{"x":30,"y":9,"type":"DIRT"},{"x":31,"y":9,"type":"DIRT"},{"x":32,"y":9,"type":"DEEP_SPACE"}],[{"x":0,"y":10,"type":"DEEP_SPACE"},{"x":1,"y":10,"type":"DIRT"},{"x":2,"y":10,"type":"DIRT"},{"x":3,"y":10,"type":"DIRT"},{"x":4,"y":10,"type":"DIRT"},{"x":5,"y":10,"type":"DIRT"},{"x":6,"y":10,"type":"AIR"},{"x":7,"y":10,"type":"AIR"},{"x":8,"y":10,"type":"AIR"},{"x":9,"y":10,"type":"AIR"},{"x":10,"y":10,"type":"DIRT"},{"x":11,"y":10,"type":"AIR"},{"x":12,"y":10,"type":"AIR"},{"x":13,"y":10,"type":"AIR"},{"x":14,"y":10,"type":"DIRT"},{"x":15,"y":10,"type":"AIR"},{"x":16,"y":10,"type":"AIR"},{"x":17,"y":10,"type":"AIR"},{"x":18,"y":10,"type":"DIRT"},{"x":19,"y":10,"type":"AIR"},{"x":20,"y":10,"type":"AIR"},{"x":21,"y":10,"type":"AIR"},{"x":22,"y":10,"type":"DIRT"},{"x":23,"y":10,"type":"AIR"},{"x":24,"y":10,"type":"AIR"},{"x":25,"y":10,"type":"AIR"},{"x":26,"y":10,"type":"AIR"},{"x":27,"y":10,"type":"DIRT"},{"x":28,"y":10,"type":"DIRT"},{"x":29,"y":10,"type":"DIRT"},{"x":30,"y":10,"type":"DIRT"},{"x":31,"y":10,"type":"DIRT"},{"x":32,"y":10,"type":"DEEP_SPACE"}],[{"x":0,"y":11,"type":"DIRT"},{"x":1,"y":11,"type":"DIRT"},{"x":2,"y":11,"type":"DIRT"},{"x":3,"y":11,"type":"DIRT"},{"x":4,"y":11,"type":"DIRT"},{"x":5,"y":11,"type":"DIRT"},{"x":6,"y":11,"type":"AIR"},{"x":7,"y":11,"type":"AIR"},{"x":8,"y":11,"type":"AIR"},{"x":9,"y":11,"type":"AIR"},{"x":10,"y":11,"type":"AIR"},{"x":11,"y":11,"type":"AIR"},{"x":12,"y":11,"type":"AIR"},{"x":13,"y":11,"type":"AIR"},{"x":14,"y":11,"type":"DIRT"},{"x":15,"y":11,"type":"DIRT"},{"x":16,"y":11,"type":"DIRT"},{"x":17,"y":11,"type":"DIRT"},{"x":18,"y":11,"type":"DIRT"},{"x":19,"y":11,"type":"AIR"},{"x":20,"y":11,"type":"AIR"},{"x":21,"y":11,"type":"AIR"},{"x":22,"y":11,"type":"AIR"},{"x":23,"y":11,"type":"AIR"},{"x":24,"y":11,"type":"AIR"},{"x":25,"y":11,"type":"AIR"},{"x":26,"y":11,"type":"AIR"},{"x":27,"y":11,"type":"DIRT"},{"x":28,"y":11,"type":"DIRT"},{"x":29,"y":11,"type":"DIRT"},{"x":30,"y":11,"type":"DIRT"},{"x":31,"y":11,"type":"DIRT"},{"x":32,"y":11,"type":"DIRT"}],[{"x":0,"y":12,"type":"DIRT"},{"x":1,"y":12,"type":"DIRT"},{"x":2,"y":12,"type":"AIR"},{"x":3,"y":12,"type":"DIRT"},{"x":4,"y":12,"type":"DIRT"},{"x":5,"y":12,"type":"DIRT"},{"x":6,"y":12,"type":"DIRT"},{"x":7,"y":12,"type":"AIR"},{"x":8,"y":12,"type":"AIR"},{"x":9,"y":12,"type":"AIR"},{"x":10,"y":12,"type":"AIR"},{"x":11,"y":12,"type":"AIR"},{"x":12,"y":12,"type":"DIRT"},{"x":13,"y":12,"type":"DIRT"},{"x":14,"y":12,"type":"AIR"},{"x":15,"y":12,"type":"AIR"},{"x":16,"y":12,"type":"DIRT"},{"x":17,"y":12,"type":"AIR"},{"x":18,"y":12,"type":"AIR"},{"x":19,"y":12,"type":"DIRT"},{"x":20,"y":12,"type":"DIRT"},{"x":21,"y":12,"type":"AIR"},{"x":22,"y":12,"type":"AIR"},{"x":23,"y":12,"type":"AIR"},{"x":24,"y":12,"type":"AIR"},{"x":25,"y":12,"type":"AIR"},{"x":26,"y":12,"type":"DIRT"},{"x":27,"y":12,"type":"DIRT"},{"x":28,"y":12,"type":"DIRT"},{"x":29,"y":12,"type":"DIRT"},{"x":30,"y":12,"type":"AIR"},{"x":31,"y":12,"type":"DIRT"},{"x":32,"y":12,"type":"DIRT"}],[{"x":0,"y":13,"type":"DIRT"},{"x":1,"y":13,"type":"AIR"},{"x":2,"y":13,"type":"AIR"},{"x":3,"y":13,"type":"DIRT"},{"x":4,"y":13,"type":"DIRT"},{"x":5,"y":13,"type":"DIRT"},{"x":6,"y":13,"type":"AIR"},{"x":7,"y":13,"type":"AIR"},{"x":8,"y":13,"type":"AIR"},{"x":9,"y":13,"type":"AIR"},{"x":10,"y":13,"type":"AIR"},{"x":11,"y":13,"type":"AIR"},{"x":12,"y":13,"type":"DIRT"},{"x":13,"y":13,"type":"DIRT"},{"x":14,"y":13,"type":"AIR"},{"x":15,"y":13,"type":"AIR"},{"x":16,"y":13,"type":"AIR"},{"x":17,"y":13,"type":"AIR"},{"x":18,"y":13,"type":"AIR"},{"x":19,"y":13,"type":"DIRT"},{"x":20,"y":13,"type":"DIRT"},{"x":21,"y":13,"type":"AIR"},{"x":22,"y":13,"type":"AIR"},{"x":23,"y":13,"type":"AIR"},{"x":24,"y":13,"type":"AIR"},{"x":25,"y":13,"type":"AIR"},{"x":26,"y":13,"type":"AIR"},{"x":27,"y":13,"type":"DIRT"},{"x":28,"y":13,"type":"DIRT"},{"x":29,"y":13,"type":"DIRT"},{"x":30,"y":13,"type":"AIR"},{"x":31,"y":13,"type":"AIR"},{"x":32,"y":13,"type":"DIRT"}],[{"x":0,"y":14,"type":"DIRT"},{"x":1,"y":14,"type":"DIRT"},{"x":2,"y":14,"type":"DIRT"},{"x":3,"y":14,"type":"DIRT"},{"x":4,"y":14,"type":"DIRT"},{"x":5,"y":14,"type":"DIRT"},{"x":6,"y":14,"type":"AIR"},{"x":7,"y":14,"type":"AIR"},{"x":8,"y":14,"type":"AIR"},{"x":9,"y":14,"type":"DIRT"},{"x":10,"y":14,"type":"DIRT"},{"x":11,"y":14,"type":"DIRT"},{"x":12,"y":14,"type":"DIRT"},{"x":13,"y":14,"type":"DIRT"},{"x":14,"y":14,"type":"DIRT"},{"x":15,"y":14,"type":"AIR"},{"x":16,"y":14,"type":"AIR"},{"x":17,"y":14,"type":"AIR"},{"x":18,"y":14,"type":"DIRT"},{"x":19,"y":14,"type":"DIRT"},{"x":20,"y":14,"type":"DIRT"},{"x":21,"y":14,"type":"DIRT"},{"x":22,"y":14,"type":"DIRT"},{"x":23,"y":14,"type":"DIRT"},{"x":24,"y":14,"type":"AIR"},{"x":25,"y":14,"type":"AIR"},{"x":26,"y":14,"type":"AIR"},{"x":27,"y":14,"type":"DIRT"},{"x":28,"y":14,"type":"DIRT"},{"x":29,"y":14,"type":"DIRT"},{"x":30,"y":14,"type":"DIRT"},{"x":31,"y":14,"type":"DIRT"},{"x":32,"y":14,"type":"DIRT"}],[{"x":0,"y":15,"type":"AIR"},{"x":1,"y":15,"type":"AIR"},{"x":2,"y":15,"type":"AIR"},{"x":3,"y":15,"type":"DIRT"},{"x":4,"y":15,"type":"DIRT"},{"x":5,"y":15,"type":"AIR"},{"x":6,"y":15,"type":"AIR"},{"x":7,"y":15,"type":"AIR"},{"x":8,"y":15,"type":"DIRT"},{"x":9,"y":15,"type":"AIR"},{"x":10,"y":15,"type":"DIRT"},{"x":11,"y":15,"type":"DIRT"},{"x":12,"y":15,"type":"DIRT"},{"x":13,"y":15,"type":"DIRT"},{"x":14,"y":15,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":15,"y":15,"type":"AIR"},{"x":16,"y":15,"type":"DIRT"},{"x":17,"y":15,"type":"AIR"},{"x":18,"y":15,"type":"AIR"},{"x":19,"y":15,"type":"DIRT"},{"x":20,"y":15,"type":"DIRT"},{"x":21,"y":15,"type":"DIRT"},{"x":22,"y":15,"type":"DIRT"},{"x":23,"y":15,"type":"AIR"},{"x":24,"y":15,"type":"DIRT"},{"x":25,"y":15,"type":"AIR"},{"x":26,"y":15,"type":"AIR"},{"x":27,"y":15,"type":"AIR"},{"x":28,"y":15,"type":"DIRT"},{"x":29,"y":15,"type":"DIRT"},{"x":30,"y":15,"type":"AIR"},{"x":31,"y":15,"type":"AIR"},{"x":32,"y":15,"type":"AIR"}],[{"x":0,"y":16,"type":"AIR"},{"x":1,"y":16,"type":"AIR","occupier":{"id":3,"playerId":1,"health":100,"position":{"x":1,"y":16},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":2,"y":16,"type":"AIR"},{"x":3,"y":16,"type":"DIRT"},{"x":4,"y":16,"type":"AIR"},{"x":5,"y":16,"type":"DIRT"},{"x":6,"y":16,"type":"DIRT"},{"x":7,"y":16,"type":"AIR"},{"x":8,"y":16,"type":"AIR"},{"x":9,"y":16,"type":"DIRT"},{"x":10,"y":16,"type":"DIRT"},{"x":11,"y":16,"type":"DIRT"},{"x":12,"y":16,"type":"DIRT"},{"x":13,"y":16,"type":"DIRT"},{"x":14,"y":16,"type":"DIRT"},{"x":15,"y":16,"type":"AIR"},{"x":16,"y":16,"type":"DIRT"},{"x":17,"y":16,"type":"AIR"},{"x":18,"y":16,"type":"DIRT"},{"x":19,"y":16,"type":"DIRT"},{"x":20,"y":16,"type":"DIRT"},{"x":21,"y":16,"type":"DIRT"},{"x":22,"y":16,"type":"DIRT"},{"x":23,"y":16,"type":"DIRT"},{"x":24,"y":16,"type":"AIR"},{"x":25,"y":16,"type":"AIR"},{"x":26,"y":16,"type":"DIRT"},{"x":27,"y":16,"type":"DIRT"},{"x":28,"y":16,"type":"AIR"},{"x":29,"y":16,"type":"DIRT"},{"x":30,"y":16,"type":"AIR"},{"x":31,"y":16,"type":"AIR","occupier":{"id":3,"playerId":2,"health":100,"position":{"x":31,"y":16},"weapon":{"damage":8,"range":4},"snowballs":{"freezeDuration":5,"range":5,"count":3,"freezeRadius":1},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Technologist"}},{"x":32,"y":16,"type":"AIR"}],[{"x":0,"y":17,"type":"AIR"},{"x":1,"y":17,"type":"AIR"},{"x":2,"y":17,"type":"AIR"},{"x":3,"y":17,"type":"DIRT"},{"x":4,"y":17,"type":"DIRT"},{"x":5,"y":17,"type":"DIRT"},{"x":6,"y":17,"type":"DIRT"},{"x":7,"y":17,"type":"DIRT"},{"x":8,"y":17,"type":"AIR"},{"x":9,"y":17,"type":"DIRT"},{"x":10,"y":17,"type":"DIRT"},{"x":11,"y":17,"type":"AIR"},{"x":12,"y":17,"type":"DIRT"},{"x":13,"y":17,"type":"DIRT"},{"x":14,"y":17,"type":"DIRT"},{"x":15,"y":17,"type":"AIR"},{"x":16,"y":17,"type":"AIR"},{"x":17,"y":17,"type":"AIR"},{"x":18,"y":17,"type":"AIR","powerup":{"type":"HEALTH_PACK","value":10}},{"x":19,"y":17,"type":"DIRT"},{"x":20,"y":17,"type":"DIRT"},{"x":21,"y":17,"type":"AIR"},{"x":22,"y":17,"type":"DIRT"},{"x":23,"y":17,"type":"DIRT"},{"x":24,"y":17,"type":"AIR"},{"x":25,"y":17,"type":"DIRT"},{"x":26,"y":17,"type":"DIRT"},{"x":27,"y":17,"type":"DIRT"},{"x":28,"y":17,"type":"DIRT"},{"x":29,"y":17,"type":"DIRT"},{"x":30,"y":17,"type":"AIR"},{"x":31,"y":17,"type":"AIR"},{"x":32,"y":17,"type":"AIR"}],[{"x":0,"y":18,"type":"DIRT"},{"x":1,"y":18,"type":"DIRT"},{"x":2,"y":18,"type":"DIRT"},{"x":3,"y":18,"type":"DIRT"},{"x":4,"y":18,"type":"DIRT"},{"x":5,"y":18,"type":"DIRT"},{"x":6,"y":18,"type":"DIRT"},{"x":7,"y":18,"type":"DIRT"},{"x":8,"y":18,"type":"DIRT"},{"x":9,"y":18,"type":"AIR"},{"x":10,"y":18,"type":"AIR"},{"x":11,"y":18,"type":"DIRT"},{"x":12,"y":18,"type":"DIRT"},{"x":13,"y":18,"type":"DIRT"},{"x":14,"y":18,"type":"AIR"},{"x":15,"y":18,"type":"AIR"},{"x":16,"y":18,"type":"AIR"},{"x":17,"y":18,"type":"AIR"},{"x":18,"y":18,"type":"AIR"},{"x":19,"y":18,"type":"DIRT"},{"x":20,"y":18,"type":"DIRT"},{"x":21,"y":18,"type":"DIRT"},{"x":22,"y":18,"type":"AIR"},{"x":23,"y":18,"type":"AIR"},{"x":24,"y":18,"type":"DIRT"},{"x":25,"y":18,"type":"DIRT"},{"x":26,"y":18,"type":"DIRT"},{"x":27,"y":18,"type":"DIRT"},{"x":28,"y":18,"type":"DIRT"},{"x":29,"y":18,"type":"DIRT"},{"x":30,"y":18,"type":"DIRT"},{"x":31,"y":18,"type":"DIRT"},{"x":32,"y":18,"type":"DIRT"}],[{"x":0,"y":19,"type":"AIR"},{"x":1,"y":19,"type":"AIR"},{"x":2,"y":19,"type":"DIRT"},{"x":3,"y":19,"type":"AIR"},{"x":4,"y":19,"type":"AIR"},{"x":5,"y":19,"type":"AIR"},{"x":6,"y":19,"type":"AIR"},{"x":7,"y":19,"type":"AIR"},{"x":8,"y":19,"type":"DIRT"},{"x":9,"y":19,"type":"AIR"},{"x":10,"y":19,"type":"AIR"},{"x":11,"y":19,"type":"DIRT"},{"x":12,"y":19,"type":"DIRT"},{"x":13,"y":19,"type":"AIR"},{"x":14,"y":19,"type":"AIR"},{"x":15,"y":19,"type":"AIR"},{"x":16,"y":19,"type":"AIR"},{"x":17,"y":19,"type":"AIR"},{"x":18,"y":19,"type":"AIR"},{"x":19,"y":19,"type":"AIR"},{"x":20,"y":19,"type":"DIRT"},{"x":21,"y":19,"type":"DIRT"},{"x":22,"y":19,"type":"AIR"},{"x":23,"y":19,"type":"AIR"},{"x":24,"y":19,"type":"DIRT"},{"x":25,"y":19,"type":"AIR"},{"x":26,"y":19,"type":"AIR"},{"x":27,"y":19,"type":"AIR"},{"x":28,"y":19,"type":"AIR"},{"x":29,"y":19,"type":"AIR"},{"x":30,"y":19,"type":"DIRT"},{"x":31,"y":19,"type":"AIR"},{"x":32,"y":19,"type":"AIR"}],[{"x":0,"y":20,"type":"AIR"},{"x":1,"y":20,"type":"AIR"},{"x":2,"y":20,"type":"DIRT"},{"x":3,"y":20,"type":"DIRT"},{"x":4,"y":20,"type":"AIR"},{"x":5,"y":20,"type":"AIR"},{"x":6,"y":20,"type":"AIR"},{"x":7,"y":20,"type":"AIR"},{"x":8,"y":20,"type":"AIR"},{"x":9,"y":20,"type":"AIR"},{"x":10,"y":20,"type":"AIR"},{"x":11,"y":20,"type":"DIRT"},{"x":12,"y":20,"type":"DIRT"},{"x":13,"y":20,"type":"DIRT"},{"x":14,"y":20,"type":"AIR"},{"x":15,"y":20,"type":"AIR"},{"x":16,"y":20,"type":"AIR"},{"x":17,"y":20,"type":"AIR"},{"x":18,"y":20,"type":"AIR"},{"x":19,"y":20,"type":"DIRT"},{"x":20,"y":20,"type":"DIRT"},{"x":21,"y":20,"type":"DIRT"},{"x":22,"y":20,"type":"AIR"},{"x":23,"y":20,"type":"AIR"},{"x":24,"y":20,"type":"AIR"},{"x":25,"y":20,"type":"AIR"},{"x":26,"y":20,"type":"AIR"},{"x":27,"y":20,"type":"AIR"},{"x":28,"y":20,"type":"AIR"},{"x":29,"y":20,"type":"DIRT"},{"x":30,"y":20,"type":"DIRT"},{"x":31,"y":20,"type":"AIR"},{"x":32,"y":20,"type":"AIR"}],[{"x":0,"y":21,"type":"AIR"},{"x":1,"y":21,"type":"DIRT"},{"x":2,"y":21,"type":"DIRT"},{"x":3,"y":21,"type":"DIRT"},{"x":4,"y":21,"type":"DIRT"},{"x":5,"y":21,"type":"AIR"},{"x":6,"y":21,"type":"AIR"},{"x":7,"y":21,"type":"AIR"},{"x":8,"y":21,"type":"AIR"},{"x":9,"y":21,"type":"AIR"},{"x":10,"y":21,"type":"DIRT"},{"x":11,"y":21,"type":"DIRT"},{"x":12,"y":21,"type":"DIRT"},{"x":13,"y":21,"type":"DIRT"},{"x":14,"y":21,"type":"DIRT"},{"x":15,"y":21,"type":"AIR"},{"x":16,"y":21,"type":"AIR"},{"x":17,"y":21,"type":"AIR"},{"x":18,"y":21,"type":"DIRT"},{"x":19,"y":21,"type":"DIRT"},{"x":20,"y":21,"type":"DIRT"},{"x":21,"y":21,"type":"DIRT"},{"x":22,"y":21,"type":"DIRT"},{"x":23,"y":21,"type":"AIR"},{"x":24,"y":21,"type":"AIR"},{"x":25,"y":21,"type":"AIR"},{"x":26,"y":21,"type":"AIR"},{"x":27,"y":21,"type":"AIR"},{"x":28,"y":21,"type":"DIRT"},{"x":29,"y":21,"type":"DIRT"},{"x":30,"y":21,"type":"DIRT"},{"x":31,"y":21,"type":"DIRT"},{"x":32,"y":21,"type":"AIR"}],[{"x":0,"y":22,"type":"DEEP_SPACE"},{"x":1,"y":22,"type":"DIRT"},{"x":2,"y":22,"type":"AIR"},{"x":3,"y":22,"type":"DIRT"},{"x":4,"y":22,"type":"DIRT"},{"x":5,"y":22,"type":"AIR"},{"x":6,"y":22,"type":"AIR"},{"x":7,"y":22,"type":"AIR"},{"x":8,"y":22,"type":"AIR"},{"x":9,"y":22,"type":"AIR"},{"x":10,"y":22,"type":"DIRT"},{"x":11,"y":22,"type":"DIRT"},{"x":12,"y":22,"type":"AIR"},{"x":13,"y":22,"type":"AIR"},{"x":14,"y":22,"type":"DIRT"},{"x":15,"y":22,"type":"AIR"},{"x":16,"y":22,"type":"AIR"},{"x":17,"y":22,"type":"AIR"},{"x":18,"y":22,"type":"DIRT"},{"x":19,"y":22,"type":"AIR"},{"x":20,"y":22,"type":"AIR"},{"x":21,"y":22,"type":"DIRT"},{"x":22,"y":22,"type":"DIRT"},{"x":23,"y":22,"type":"AIR"},{"x":24,"y":22,"type":"AIR"},{"x":25,"y":22,"type":"AIR"},{"x":26,"y":22,"type":"AIR"},{"x":27,"y":22,"type":"AIR"},{"x":28,"y":22,"type":"DIRT"},{"x":29,"y":22,"type":"DIRT"},{"x":30,"y":22,"type":"AIR"},{"x":31,"y":22,"type":"DIRT"},{"x":32,"y":22,"type":"DEEP_SPACE"}],[{"x":0,"y":23,"type":"DEEP_SPACE"},{"x":1,"y":23,"type":"DIRT"},{"x":2,"y":23,"type":"AIR"},{"x":3,"y":23,"type":"DIRT"},{"x":4,"y":23,"type":"DIRT"},{"x":5,"y":23,"type":"DIRT"},{"x":6,"y":23,"type":"AIR"},{"x":7,"y":23,"type":"AIR"},{"x":8,"y":23,"type":"DIRT"},{"x":9,"y":23,"type":"DIRT"},{"x":10,"y":23,"type":"DIRT"},{"x":11,"y":23,"type":"DIRT"},{"x":12,"y":23,"type":"AIR"},{"x":13,"y":23,"type":"AIR"},{"x":14,"y":23,"type":"DIRT"},{"x":15,"y":23,"type":"AIR"},{"x":16,"y":23,"type":"AIR"},{"x":17,"y":23,"type":"AIR"},{"x":18,"y":23,"type":"DIRT"},{"x":19,"y":23,"type":"AIR"},{"x":20,"y":23,"type":"AIR"},{"x":21,"y":23,"type":"DIRT"},{"x":22,"y":23,"type":"DIRT"},{"x":23,"y":23,"type":"DIRT"},{"x":24,"y":23,"type":"DIRT"},{"x":25,"y":23,"type":"AIR"},{"x":26,"y":23,"type":"AIR"},{"x":27,"y":23,"type":"DIRT"},{"x":28,"y":23,"type":"DIRT"},{"x":29,"y":23,"type":"DIRT"},{"x":30,"y":23,"type":"AIR"},{"x":31,"y":23,"type":"DIRT"},{"x":32,"y":23,"type":"DEEP_SPACE"}],[{"x":0,"y":24,"type":"DEEP_SPACE"},{"x":1,"y":24,"type":"DIRT"},{"x":2,"y":24,"type":"DIRT"},{"x":3,"y":24,"type":"DIRT"},{"x":4,"y":24,"type":"DIRT"},{"x":5,"y":24,"type":"DIRT"},{"x":6,"y":24,"type":"AIR"},{"x":7,"y":24,"type":"AIR"},{"x":8,"y":24,"type":"DIRT"},{"x":9,"y":24,"type":"AIR"},{"x":10,"y":24,"type":"AIR"},{"x":11,"y":24,"type":"AIR"},{"x":12,"y":24,"type":"AIR"},{"x":13,"y":24,"type":"AIR"},{"x":14,"y":24,"type":"DIRT"},{"x":15,"y":24,"type":"DIRT"},{"x":16,"y":24,"type":"DIRT"},{"x":17,"y":24,"type":"DIRT"},{"x":18,"y":24,"type":"DIRT"},{"x":19,"y":24,"type":"AIR"},{"x":20,"y":24,"type":"AIR"},{"x":21,"y":24,"type":"AIR"},{"x":22,"y":24,"type":"AIR"},{"x":23,"y":24,"type":"AIR"},{"x":24,"y":24,"type":"DIRT"},{"x":25,"y":24,"type":"AIR"},{"x":26,"y":24,"type":"AIR"},{"x":27,"y":24,"type":"DIRT"},{"x":28,"y":24,"type":"DIRT"},{"x":29,"y":24,"type":"DIRT"},{"x":30,"y":24,"type":"DIRT"},{"x":31,"y":24,"type":"DIRT"},{"x":32,"y":24,"type":"DEEP_SPACE"}],[{"x":0,"y":25,"type":"DEEP_SPACE"},{"x":1,"y":25,"type":"DEEP_SPACE"},{"x":2,"y":25,"type":"DIRT"},{"x":3,"y":25,"type":"AIR"},{"x":4,"y":25,"type":"DIRT"},{"x":5,"y":25,"type":"DIRT"},{"x":6,"y":25,"type":"AIR"},{"x":7,"y":25,"type":"AIR"},{"x":8,"y":25,"type":"DIRT"},{"x":9,"y":25,"type":"DIRT"},{"x":10,"y":25,"type":"AIR"},{"x":11,"y":25,"type":"AIR"},{"x":12,"y":25,"type":"AIR"},{"x":13,"y":25,"type":"AIR"},{"x":14,"y":25,"type":"DIRT"},{"x":15,"y":25,"type":"AIR"},{"x":16,"y":25,"type":"DIRT"},{"x":17,"y":25,"type":"AIR"},{"x":18,"y":25,"type":"DIRT"},{"x":19,"y":25,"type":"AIR"},{"x":20,"y":25,"type":"AIR"},{"x":21,"y":25,"type":"AIR"},{"x":22,"y":25,"type":"AIR"},{"x":23,"y":25,"type":"DIRT"},{"x":24,"y":25,"type":"DIRT"},{"x":25,"y":25,"type":"AIR"},{"x":26,"y":25,"type":"AIR"},{"x":27,"y":25,"type":"DIRT"},{"x":28,"y":25,"type":"DIRT"},{"x":29,"y":25,"type":"AIR"},{"x":30,"y":25,"type":"DIRT"},{"x":31,"y":25,"type":"DEEP_SPACE"},{"x":32,"y":25,"type":"DEEP_SPACE"}],[{"x":0,"y":26,"type":"DEEP_SPACE"},{"x":1,"y":26,"type":"DEEP_SPACE"},{"x":2,"y":26,"type":"DEEP_SPACE"},{"x":3,"y":26,"type":"AIR"},{"x":4,"y":26,"type":"AIR"},{"x":5,"y":26,"type":"AIR"},{"x":6,"y":26,"type":"DIRT"},{"x":7,"y":26,"type":"DIRT"},{"x":8,"y":26,"type":"DIRT"},{"x":9,"y":26,"type":"DIRT"},{"x":10,"y":26,"type":"DIRT"},{"x":11,"y":26,"type":"AIR"},{"x":12,"y":26,"type":"DIRT"},{"x":13,"y":26,"type":"AIR"},{"x":14,"y":26,"type":"AIR"},{"x":15,"y":26,"type":"DIRT"},{"x":16,"y":26,"type":"DIRT"},{"x":17,"y":26,"type":"DIRT"},{"x":18,"y":26,"type":"AIR"},{"x":19,"y":26,"type":"AIR"},{"x":20,"y":26,"type":"DIRT"},{"x":21,"y":26,"type":"AIR"},{"x":22,"y":26,"type":"DIRT"},{"x":23,"y":26,"type":"DIRT"},{"x":24,"y":26,"type":"DIRT"},{"x":25,"y":26,"type":"DIRT"},{"x":26,"y":26,"type":"DIRT"},{"x":27,"y":26,"type":"AIR"},{"x":28,"y":26,"type":"AIR"},{"x":29,"y":26,"type":"AIR"},{"x":30,"y":26,"type":"DEEP_SPACE"},{"x":31,"y":26,"type":"DEEP_SPACE"},{"x":32,"y":26,"type":"DEEP_SPACE"}],[{"x":0,"y":27,"type":"DEEP_SPACE"},{"x":1,"y":27,"type":"DEEP_SPACE"},{"x":2,"y":27,"type":"DEEP_SPACE"},{"x":3,"y":27,"type":"DEEP_SPACE"},{"x":4,"y":27,"type":"AIR"},{"x":5,"y":27,"type":"AIR"},{"x":6,"y":27,"type":"DIRT"},{"x":7,"y":27,"type":"AIR"},{"x":8,"y":27,"type":"AIR"},{"x":9,"y":27,"type":"AIR"},{"x":10,"y":27,"type":"DIRT"},{"x":11,"y":27,"type":"DIRT"},{"x":12,"y":27,"type":"DIRT"},{"x":13,"y":27,"type":"AIR"},{"x":14,"y":27,"type":"AIR"},{"x":15,"y":27,"type":"DIRT"},{"x":16,"y":27,"type":"DIRT"},{"x":17,"y":27,"type":"DIRT"},{"x":18,"y":27,"type":"AIR"},{"x":19,"y":27,"type":"AIR"},{"x":20,"y":27,"type":"DIRT"},{"x":21,"y":27,"type":"DIRT"},{"x":22,"y":27,"type":"DIRT"},{"x":23,"y":27,"type":"AIR"},{"x":24,"y":27,"type":"AIR"},{"x":25,"y":27,"type":"AIR"},{"x":26,"y":27,"type":"DIRT"},{"x":27,"y":27,"type":"AIR"},{"x":28,"y":27,"type":"AIR"},{"x":29,"y":27,"type":"DEEP_SPACE"},{"x":30,"y":27,"type":"DEEP_SPACE"},{"x":31,"y":27,"type":"DEEP_SPACE"},{"x":32,"y":27,"type":"DEEP_SPACE"}],[{"x":0,"y":28,"type":"DEEP_SPACE"},{"x":1,"y":28,"type":"DEEP_SPACE"},{"x":2,"y":28,"type":"DEEP_SPACE"},{"x":3,"y":28,"type":"DEEP_SPACE"},{"x":4,"y":28,"type":"AIR"},{"x":5,"y":28,"type":"AIR"},{"x":6,"y":28,"type":"DIRT"},{"x":7,"y":28,"type":"AIR"},{"x":8,"y":28,"type":"AIR","occupier":{"id":1,"playerId":2,"health":150,"position":{"x":8,"y":28},"weapon":{"damage":8,"range":4},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Commando"}},{"x":9,"y":28,"type":"AIR"},{"x":10,"y":28,"type":"DIRT"},{"x":11,"y":28,"type":"DIRT"},{"x":12,"y":28,"type":"AIR"},{"x":13,"y":28,"type":"AIR"},{"x":14,"y":28,"type":"AIR"},{"x":15,"y":28,"type":"DIRT"},{"x":16,"y":28,"type":"DIRT"},{"x":17,"y":28,"type":"DIRT"},{"x":18,"y":28,"type":"AIR"},{"x":19,"y":28,"type":"AIR"},{"x":20,"y":28,"type":"AIR"},{"x":21,"y":28,"type":"DIRT"},{"x":22,"y":28,"type":"DIRT"},{"x":23,"y":28,"type":"AIR"},{"x":24,"y":28,"type":"AIR","occupier":{"id":2,"playerId":1,"health":100,"position":{"x":24,"y":28},"diggingRange":1,"movementRange":1,"roundsUntilUnfrozen":0,"profession":"Agent"}},{"x":25,"y":28,"type":"AIR"},{"x":26,"y":28,"type":"DIRT"},{"x":27,"y":28,"type":"AIR"},{"x":28,"y":28,"type":"AIR"},{"x":29,"y":28,"type":"DEEP_SPACE"},{"x":30,"y":28,"type":"DEEP_SPACE"},{"x":31,"y":28,"type":"DEEP_SPACE"},{"x":32,"y":28,"type":"DEEP_SPACE"}],[{"x":0,"y":29,"type":"DEEP_SPACE"},{"x":1,"y":29,"type":"DEEP_SPACE"},{"x":2,"y":29,"type":"DEEP_SPACE"},{"x":3,"y":29,"type":"DEEP_SPACE"},{"x":4,"y":29,"type":"DEEP_SPACE"},{"x":5,"y":29,"type":"DEEP_SPACE"},{"x":6,"y":29,"type":"DIRT"},{"x":7,"y":29,"type":"AIR"},{"x":8,"y":29,"type":"AIR"},{"x":9,"y":29,"type":"AIR"},{"x":10,"y":29,"type":"DIRT"},{"x":11,"y":29,"type":"DIRT"},{"x":12,"y":29,"type":"AIR"},{"x":13,"y":29,"type":"DIRT"},{"x":14,"y":29,"type":"DIRT"},{"x":15,"y":29,"type":"DIRT"},{"x":16,"y":29,"type":"DIRT"},{"x":17,"y":29,"type":"DIRT"},{"x":18,"y":29,"type":"DIRT"},{"x":19,"y":29,"type":"DIRT"},{"x":20,"y":29,"type":"AIR"},{"x":21,"y":29,"type":"DIRT"},{"x":22,"y":29,"type":"DIRT"},{"x":23,"y":29,"type":"AIR"},{"x":24,"y":29,"type":"AIR"},{"x":25,"y":29,"type":"AIR"},{"x":26,"y":29,"type":"DIRT"},{"x":27,"y":29,"type":"DEEP_SPACE"},{"x":28,"y":29,"type":"DEEP_SPACE"},{"x":29,"y":29,"type":"DEEP_SPACE"},{"x":30,"y":29,"type":"DEEP_SPACE"},{"x":31,"y":29,"type":"DEEP_SPACE"},{"x":32,"y":29,"type":"DEEP_SPACE"}],[{"x":0,"y":30,"type":"DEEP_SPACE"},{"x":1,"y":30,"type":"DEEP_SPACE"},{"x":2,"y":30,"type":"DEEP_SPACE"},{"x":3,"y":30,"type":"DEEP_SPACE"},{"x":4,"y":30,"type":"DEEP_SPACE"},{"x":5,"y":30,"type":"DEEP_SPACE"},{"x":6,"y":30,"type":"DEEP_SPACE"},{"x":7,"y":30,"type":"DIRT"},{"x":8,"y":30,"type":"DIRT"},{"x":9,"y":30,"type":"DIRT"},{"x":10,"y":30,"type":"DIRT"},{"x":11,"y":30,"type":"DIRT"},{"x":12,"y":30,"type":"AIR"},{"x":13,"y":30,"type":"AIR"},{"x":14,"y":30,"type":"DIRT"},{"x":15,"y":30,"type":"DIRT"},{"x":16,"y":30,"type":"AIR"},{"x":17,"y":30,"type":"DIRT"},{"x":18,"y":30,"type":"DIRT"},{"x":19,"y":30,"type":"AIR"},{"x":20,"y":30,"type":"AIR"},{"x":21,"y":30,"type":"DIRT"},{"x":22,"y":30,"type":"DIRT"},{"x":23,"y":30,"type":"DIRT"},{"x":24,"y":30,"type":"DIRT"},{"x":25,"y":30,"type":"DIRT"},{"x":26,"y":30,"type":"DEEP_SPACE"},{"x":27,"y":30,"type":"DEEP_SPACE"},{"x":28,"y":30,"type":"DEEP_SPACE"},{"x":29,"y":30,"type":"DEEP_SPACE"},{"x":30,"y":30,"type":"DEEP_SPACE"},{"x":31,"y":30,"type":"DEEP_SPACE"},{"x":32,"y":30,"type":"DEEP_SPACE"}],[{"x":0,"y":31,"type":"DEEP_SPACE"},{"x":1,"y":31,"type":"DEEP_SPACE"},{"x":2,"y":31,"type":"DEEP_SPACE"},{"x":3,"y":31,"type":"DEEP_SPACE"},{"x":4,"y":31,"type":"DEEP_SPACE"},{"x":5,"y":31,"type":"DEEP_SPACE"},{"x":6,"y":31,"type":"DEEP_SPACE"},{"x":7,"y":31,"type":"DEEP_SPACE"},{"x":8,"y":31,"type":"DIRT"},{"x":9,"y":31,"type":"DIRT"},{"x":10,"y":31,"type":"DIRT"},{"x":11,"y":31,"type":"DIRT"},{"x":12,"y":31,"type":"AIR"},{"x":13,"y":31,"type":"AIR"},{"x":14,"y":31,"type":"AIR"},{"x":15,"y":31,"type":"AIR"},{"x":16,"y":31,"type":"AIR"},{"x":17,"y":31,"type":"AIR"},{"x":18,"y":31,"type":"AIR"},{"x":19,"y":31,"type":"AIR"},{"x":20,"y":31,"type":"AIR"},{"x":21,"y":31,"type":"DIRT"},{"x":22,"y":31,"type":"DIRT"},{"x":23,"y":31,"type":"DIRT"},{"x":24,"y":31,"type":"DIRT"},{"x":25,"y":31,"type":"DEEP_SPACE"},{"x":26,"y":31,"type":"DEEP_SPACE"},{"x":27,"y":31,"type":"DEEP_SPACE"},{"x":28,"y":31,"type":"DEEP_SPACE"},{"x":29,"y":31,"type":"DEEP_SPACE"},{"x":30,"y":31,"type":"DEEP_SPACE"},{"x":31,"y":31,"type":"DEEP_SPACE"},{"x":32,"y":31,"type":"DEEP_SPACE"}],[{"x":0,"y":32,"type":"DEEP_SPACE"},{"x":1,"y":32,"type":"DEEP_SPACE"},{"x":2,"y":32,"type":"DEEP_SPACE"},{"x":3,"y":32,"type":"DEEP_SPACE"},{"x":4,"y":32,"type":"DEEP_SPACE"},{"x":5,"y":32,"type":"DEEP_SPACE"},{"x":6,"y":32,"type":"DEEP_SPACE"},{"x":7,"y":32,"type":"DEEP_SPACE"},{"x":8,"y":32,"type":"DEEP_SPACE"},{"x":9,"y":32,"type":"DEEP_SPACE"},{"x":10,"y":32,"type":"DEEP_SPACE"},{"x":11,"y":32,"type":"DIRT"},{"x":12,"y":32,"type":"DIRT"},{"x":13,"y":32,"type":"DIRT"},{"x":14,"y":32,"type":"AIR"},{"x":15,"y":32,"type":"AIR"},{"x":16,"y":32,"type":"DIRT"},{"x":17,"y":32,"type":"AIR"},{"x":18,"y":32,"type":"AIR"},{"x":19,"y":32,"type":"DIRT"},{"x":20,"y":32,"type":"DIRT"},{"x":21,"y":32,"type":"DIRT"},{"x":22,"y":32,"type":"DEEP_SPACE"},{"x":23,"y":32,"type":"DEEP_SPACE"},{"x":24,"y":32,"type":"DEEP_SPACE"},{"x":25,"y":32,"type":"DEEP_SPACE"},{"x":26,"y":32,"type":"DEEP_SPACE"},{"x":27,"y":32,"type":"DEEP_SPACE"},{"x":28,"y":32,"type":"DEEP_SPACE"},{"x":29,"y":32,"type":"DEEP_SPACE"},{"x":30,"y":32,"type":"DEEP_SPACE"},{"x":31,"y":32,"type":"DEEP_SPACE"},{"x":32,"y":32,"type":"DEEP_SPACE"}]],"visualizerEvents":[]} \ No newline at end of file
diff --git a/2019-worms/tests/replays/2019.08.19.21.57.04/B-log.csv b/2019-worms/tests/replays/2019.08.19.21.57.04/B-log.csv
new file mode 100644
index 0000000..cc2bfa5
--- /dev/null
+++ b/2019-worms/tests/replays/2019.08.19.21.57.04/B-log.csv
@@ -0,0 +1,214 @@
+Round,LastCommandType,LastCommand,ActiveWorm,Score,Health,Worm1 Health,Worm1 x,Worm1 y,Worm2 Health,Worm2 x,Worm2 y,Worm3 Health,Worm3 x,Worm3 y
+1,null,"null",1,116,350,150,8,28,100,8,4,100,31,16
+2,move,"move 9 27",1,121,350,150,9,27,100,8,4,100,31,16
+3,move,"move 9 5",2,126,350,150,9,27,100,9,5,100,31,16
+4,move,"move 30 17",3,131,350,150,9,27,100,9,5,100,30,17
+5,dig,"dig 10 26",1,138,350,150,9,27,100,9,5,100,30,17
+6,dig,"dig 10 6",2,145,350,150,9,27,100,9,5,100,30,17
+7,dig,"dig 29 17",3,152,350,150,9,27,100,9,5,100,30,17
+8,move,"move 10 26",1,157,350,150,10,26,100,9,5,100,30,17
+9,move,"move 10 6",2,162,350,150,10,26,100,10,6,100,30,17
+10,move,"move 29 17",3,167,350,150,10,26,100,10,6,100,29,17
+11,move,"move 11 25",1,172,350,150,11,25,100,10,6,100,29,17
+12,dig,"dig 11 7",2,179,350,150,11,25,100,10,6,100,29,17
+13,dig,"dig 28 17",3,186,350,150,11,25,100,10,6,100,29,17
+14,move,"move 12 24",1,191,350,150,12,24,100,10,6,100,29,17
+15,move,"move 11 7",2,196,350,150,12,24,100,11,7,100,29,17
+16,move,"move 28 17",3,201,350,150,12,24,100,11,7,100,28,17
+17,move,"move 13 23",1,206,350,150,13,23,100,11,7,100,28,17
+18,dig,"dig 12 8",2,213,350,150,13,23,100,11,7,100,28,17
+19,dig,"dig 27 17",3,220,350,150,13,23,100,11,7,100,28,17
+20,dig,"dig 14 22",1,227,350,150,13,23,100,11,7,100,28,17
+21,move,"move 12 8",2,232,350,150,13,23,100,12,8,100,28,17
+22,move,"move 27 17",3,237,350,150,13,23,100,12,8,100,27,17
+23,move,"move 14 22",1,242,350,150,14,22,100,12,8,100,27,17
+24,move,"move 13 9",2,247,350,150,14,22,100,13,9,100,27,17
+25,dig,"dig 26 17",3,254,350,150,14,22,100,13,9,100,27,17
+26,move,"move 15 21",1,259,350,150,15,21,100,13,9,100,27,17
+27,dig,"dig 14 10",2,266,350,150,15,21,100,13,9,100,27,17
+28,move,"move 26 17",3,271,350,150,15,21,100,13,9,100,26,17
+29,move,"move 16 20",1,276,350,150,16,20,100,13,9,100,26,17
+30,move,"move 14 10",2,281,350,150,16,20,100,14,10,100,26,17
+31,dig,"dig 25 17",3,288,350,150,16,20,100,14,10,100,26,17
+32,move,"move 17 19",1,293,350,150,17,19,100,14,10,100,26,17
+33,dig,"dig 14 11",2,300,350,150,17,19,100,14,10,100,26,17
+34,move,"move 25 17",3,305,350,150,17,19,100,14,10,100,25,17
+35,move,"move 18 18",1,310,350,150,18,18,100,14,10,100,25,17
+36,move,"move 14 11",2,315,350,150,18,18,100,14,11,100,25,17
+37,move,"move 24 17",3,320,350,150,18,18,100,14,11,100,24,17
+38,move,"move 18 17",1,329,360,160,18,17,100,14,11,100,24,17
+39,move,"move 14 12",2,334,360,160,18,17,100,14,12,100,24,17
+40,dig,"dig 23 16",3,341,360,160,18,17,100,14,12,100,24,17
+41,move,"move 17 16",1,346,360,160,17,16,100,14,12,100,24,17
+42,move,"move 14 13",2,351,360,160,17,16,100,14,13,100,24,17
+43,move,"move 23 16",3,356,360,160,17,16,100,14,13,100,23,16
+44,dig,"dig 16 15",1,363,360,160,17,16,100,14,13,100,23,16
+45,dig,"dig 14 14",2,370,360,160,17,16,100,14,13,100,23,16
+46,dig,"dig 22 15",3,377,360,160,17,16,100,14,13,100,23,16
+47,move,"move 16 15",1,382,360,160,16,15,100,14,13,100,23,16
+48,move,"move 14 14",2,387,360,160,16,15,100,14,14,100,23,16
+49,move,"move 22 15",3,392,360,160,16,15,100,14,14,100,22,15
+50,move,"move 15 15",1,397,360,160,15,15,100,14,14,100,22,15
+51,move,"move 14 15",2,405,370,160,15,15,110,14,15,100,22,15
+52,dig,"dig 23 14",3,412,370,160,15,15,110,14,15,100,22,15
+53,move,"move 16 14",1,417,370,160,16,14,110,14,15,100,22,15
+54,move,"move 15 14",2,422,370,160,16,14,110,15,14,100,22,15
+55,move,"move 23 14",3,427,370,160,16,14,110,15,14,100,23,14
+56,move,"move 17 13",1,432,370,160,17,13,110,15,14,100,23,14
+57,move,"move 16 13",2,437,370,160,17,13,110,16,13,100,23,14
+58,move,"move 22 13",3,442,370,160,17,13,110,16,13,100,22,13
+59,move,"move 18 12",1,447,370,160,18,12,110,16,13,100,22,13
+60,move,"move 17 12",2,452,370,160,18,12,110,17,12,100,22,13
+61,move,"move 23 12",3,457,370,160,18,12,110,17,12,100,23,12
+62,move,"move 19 11",1,462,370,160,19,11,110,17,12,100,23,12
+63,dig,"dig 18 11",2,469,370,160,19,11,110,17,12,100,23,12
+64,move,"move 22 11",3,474,370,160,19,11,110,17,12,100,22,11
+65,move,"move 20 10",1,479,370,160,20,10,110,17,12,100,22,11
+66,move,"move 18 11",2,484,370,160,20,10,110,18,11,100,22,11
+67,move,"move 23 10",3,489,370,160,20,10,110,18,11,100,23,10
+68,dig,"dig 21 9",1,496,370,160,20,10,110,18,11,100,23,10
+69,move,"move 19 10",2,501,370,160,20,10,110,19,10,100,23,10
+70,move,"move 24 9",3,506,370,160,20,10,110,19,10,100,24,9
+71,move,"move 21 9",1,511,370,160,21,9,110,19,10,100,24,9
+72,move,"move 20 9",2,516,370,160,21,9,110,20,9,100,24,9
+73,move,"move 25 8",3,521,370,160,21,9,110,20,9,100,25,8
+74,move,"move 22 8",1,526,370,160,22,8,110,20,9,100,25,8
+75,dig,"dig 21 8",2,533,370,160,22,8,110,20,9,100,25,8
+76,snowball,"snowball 25 3",3,550,370,160,22,8,110,20,9,100,25,8
+77,move,"move 23 7",1,555,370,160,23,7,110,20,9,100,25,8
+78,move,"move 21 8",2,560,370,160,23,7,110,21,8,100,25,8
+79,move,"move 25 7",3,565,370,160,23,7,110,21,8,100,25,7
+80,dig,"dig 24 6",1,572,370,160,23,7,110,21,8,100,25,7
+81,move,"move 22 7",2,577,370,160,23,7,110,22,7,100,25,7
+82,snowball,"snowball 25 3",3,594,370,160,23,7,110,22,7,100,25,7
+83,move,"move 24 6",1,599,370,160,24,6,110,22,7,100,25,7
+84,banana,"banana 25 3",2,660,370,160,24,6,110,22,7,100,25,7
+85,dig,"dig 25 6",3,667,370,160,24,6,110,22,7,100,25,7
+86,shoot,"shoot N",3,683,370,160,24,6,110,22,7,100,25,7
+87,shoot,"shoot N",3,696,362,160,24,6,110,22,7,92,25,7
+88,shoot,"shoot N",3,710,354,160,24,6,110,22,7,84,25,7
+89,shoot,"shoot N",3,723,346,160,24,6,110,22,7,76,25,7
+90,shoot,"shoot N",3,736,338,160,24,6,110,22,7,68,25,7
+91,move,"move 25 5",1,741,338,160,25,5,110,22,7,68,25,7
+92,banana,"banana 25 3",2,762,323,145,25,5,110,22,7,68,25,7
+93,snowball,"snowball 25 3",3,779,323,145,25,5,110,22,7,68,25,7
+94,shoot,"shoot N",1,795,323,145,25,5,110,22,7,68,25,7
+95,banana,"banana 25 3",2,819,316,138,25,5,110,22,7,68,25,7
+96,move,"move 25 6",3,824,316,138,25,5,110,22,7,68,25,6
+97,shoot,"shoot N",1,840,316,138,25,5,110,22,7,68,25,6
+98,dig,"dig 23 6",2,844,308,130,25,5,110,22,7,68,25,6
+99,move,"move 25 7",3,849,308,130,25,5,110,22,7,68,25,7
+100,shoot,"shoot N",1,865,308,130,25,5,110,22,7,68,25,7
+101,move,"move 23 6",2,868,300,122,25,5,110,23,6,68,25,7
+102,move,"move 25 6",3,873,300,122,25,5,110,23,6,68,25,6
+103,shoot,"shoot N",1,889,300,122,25,5,110,23,6,68,25,6
+104,move,"move 24 5",2,891,292,114,25,5,110,24,5,68,25,6
+105,move,"move 24 7",3,896,292,114,25,5,110,24,5,68,24,7
+106,shoot,"shoot N",1,912,292,114,25,5,110,24,5,68,24,7
+107,move,"move 25 4",2,914,284,114,25,5,102,25,4,68,24,7
+108,move,"move 25 8",3,919,284,114,25,5,102,25,4,68,25,8
+109,move,"move 25 6",1,924,284,114,25,6,102,25,4,68,25,8
+110,move,"move 25 5",2,929,284,114,25,6,102,25,5,68,25,8
+111,move,"move 24 9",3,934,284,114,25,6,102,25,5,68,24,9
+112,move,"move 24 7",1,939,284,114,24,7,102,25,5,68,24,9
+113,move,"move 25 6",2,944,284,114,24,7,102,25,6,68,24,9
+114,move,"move 25 10",3,949,284,114,24,7,102,25,6,68,25,10
+115,move,"move 25 8",1,954,284,114,25,8,102,25,6,68,25,10
+116,move,"move 25 7",2,959,284,114,25,8,102,25,7,68,25,10
+117,move,"move 25 11",3,964,284,114,25,8,102,25,7,68,25,11
+118,move,"move 25 9",1,969,284,114,25,9,102,25,7,68,25,11
+119,move,"move 24 8",2,974,284,114,25,9,102,24,8,68,25,11
+120,move,"move 24 12",3,979,284,114,25,9,102,24,8,68,24,12
+121,move,"move 25 10",1,984,284,114,25,10,102,24,8,68,24,12
+122,move,"move 25 9",2,989,284,114,25,10,102,25,9,68,24,12
+123,move,"move 25 13",3,994,284,114,25,10,102,25,9,68,25,13
+124,move,"move 26 11",1,999,284,114,26,11,102,25,9,68,25,13
+125,move,"move 25 10",2,1004,284,114,26,11,102,25,10,68,25,13
+126,move,"move 25 14",3,1009,284,114,26,11,102,25,10,68,25,14
+127,dig,"dig 26 12",1,1016,284,114,26,11,102,25,10,68,25,14
+128,move,"move 24 10",2,1021,284,114,26,11,102,24,10,68,25,14
+129,move,"move 26 13",3,1026,284,114,26,11,102,24,10,68,26,13
+130,move,"move 26 12",1,1031,284,114,26,12,102,24,10,68,26,13
+131,move,"move 25 11",2,1036,284,114,26,12,102,25,11,68,26,13
+132,move,"move 25 14",3,1041,284,114,26,12,102,25,11,68,25,14
+133,move,"move 26 13",1,1046,284,114,26,13,102,25,11,68,25,14
+134,move,"move 26 12",2,1051,284,114,26,13,102,26,12,68,25,14
+135,move,"move 26 15",3,1056,284,114,26,13,102,26,12,68,26,15
+136,move,"move 26 14",1,1061,284,114,26,14,102,26,12,68,26,15
+137,move,"move 26 13",2,1066,284,114,26,14,102,26,13,68,26,15
+138,dig,"dig 26 16",3,1073,284,114,26,14,102,26,13,68,26,15
+139,move,"move 25 15",1,1078,284,114,25,15,102,26,13,68,26,15
+140,move,"move 25 14",2,1073,253,101,25,15,95,25,14,57,26,15
+141,move,"move 25 16",3,1078,253,101,25,15,95,25,14,57,25,16
+142,invalid,"invalid",1,1061,213,88,25,15,88,25,14,37,25,16
+143,move,"move 26 14",2,1066,213,88,25,15,88,26,14,37,25,16
+144,move,"move 25 17",3,1061,184,77,25,15,81,26,14,26,25,17
+145,move,"move 25 16",1,1066,184,77,25,16,81,26,14,26,25,17
+146,move,"move 25 15",2,1068,176,77,25,16,81,25,15,18,25,17
+147,shoot,"shoot S",3,1084,176,77,25,16,81,25,15,18,25,17
+148,move,"move 24 15",1,1087,168,77,24,15,81,25,15,10,25,17
+149,move,"move 25 16",2,1092,168,77,24,15,81,25,16,10,25,17
+150,shoot,"shoot S",3,1105,160,77,24,15,81,25,16,2,25,17
+151,move,"move 24 16",1,1110,160,77,24,16,81,25,16,2,25,17
+152,move,"move 24 15",2,1114,158,77,24,16,81,24,15,-6,25,17
+153,move,"move 25 17",1,1119,158,77,25,17,81,24,15,-6,25,17
+154,move,"move 25 16",2,1122,150,69,25,17,81,25,16,-6,25,17
+155,shoot,"shoot S",1,1138,150,69,25,17,81,25,16,-6,25,17
+156,move,"move 24 15",2,1140,142,61,25,17,81,24,15,-6,25,17
+157,shoot,"shoot S",1,1156,142,61,25,17,81,24,15,-6,25,17
+158,move,"move 25 16",2,1158,134,53,25,17,81,25,16,-6,25,17
+159,shoot,"shoot S",1,1174,134,53,25,17,81,25,16,-6,25,17
+160,move,"move 24 15",2,1177,126,45,25,17,81,24,15,-6,25,17
+161,shoot,"shoot S",1,1193,126,45,25,17,81,24,15,-6,25,17
+162,move,"move 25 16",2,1195,118,37,25,17,81,25,16,-6,25,17
+163,shoot,"shoot S",1,1211,118,37,25,17,81,25,16,-6,25,17
+164,move,"move 24 17",2,1213,110,29,25,17,81,24,17,-6,25,17
+165,shoot,"shoot S",1,1229,110,29,25,17,81,24,17,-6,25,17
+166,move,"move 25 18",2,1232,102,29,25,17,73,25,18,-6,25,17
+167,move,"move 26 18",1,1237,102,29,26,18,73,25,18,-6,25,17
+168,shoot,"shoot S",2,1250,94,29,26,18,65,25,18,-6,25,17
+169,move,"move 25 19",1,1255,94,29,25,19,65,25,18,-6,25,17
+170,dig,"dig 24 18",2,1259,86,21,25,19,65,25,18,-6,25,17
+171,shoot,"shoot S",1,1275,86,21,25,19,65,25,18,-6,25,17
+172,move,"move 25 17",2,1278,78,13,25,19,65,25,17,-6,25,17
+173,shoot,"shoot S",1,1294,78,13,25,19,65,25,17,-6,25,17
+174,move,"move 25 18",2,1296,70,5,25,19,65,25,18,-6,25,17
+175,shoot,"shoot S",1,1312,70,5,25,19,65,25,18,-6,25,17
+176,move,"move 24 17",2,1315,65,-3,25,19,65,24,17,-6,25,17
+177,move,"move 25 18",2,1320,65,-3,25,19,65,25,18,-6,25,17
+178,shoot,"shoot S",2,1374,57,-3,25,19,57,25,18,-6,25,17
+179,move,"move 24 17",2,1379,57,-3,25,19,57,24,17,-6,25,17
+180,move,"move 23 16",2,1384,57,-3,25,19,57,23,16,-6,25,17
+181,dig,"dig 22 16",2,1391,57,-3,25,19,57,23,16,-6,25,17
+182,move,"move 22 15",2,1396,57,-3,25,19,57,22,15,-6,25,17
+183,dig,"dig 21 15",2,1403,57,-3,25,19,57,22,15,-6,25,17
+184,move,"move 21 15",2,1408,57,-3,25,19,57,21,15,-6,25,17
+185,dig,"dig 20 15",2,1415,57,-3,25,19,57,21,15,-6,25,17
+186,move,"move 20 15",2,1420,57,-3,25,19,57,20,15,-6,25,17
+187,dig,"dig 19 15",2,1427,57,-3,25,19,57,20,15,-6,25,17
+188,move,"move 19 15",2,1432,57,-3,25,19,57,19,15,-6,25,17
+189,move,"move 18 15",2,1437,57,-3,25,19,57,18,15,-6,25,17
+190,move,"move 17 15",2,1442,57,-3,25,19,57,17,15,-6,25,17
+191,move,"move 16 15",2,1447,57,-3,25,19,57,16,15,-6,25,17
+192,move,"move 15 15",2,1452,57,-3,25,19,57,15,15,-6,25,17
+193,dig,"dig 14 16",2,1459,57,-3,25,19,57,15,15,-6,25,17
+194,move,"move 14 16",2,1464,57,-3,25,19,57,14,16,-6,25,17
+195,dig,"dig 13 17",2,1471,57,-3,25,19,57,14,16,-6,25,17
+196,move,"move 13 17",2,1476,57,-3,25,19,57,13,17,-6,25,17
+197,dig,"dig 12 18",2,1483,57,-3,25,19,57,13,17,-6,25,17
+198,move,"move 12 18",2,1488,57,-3,25,19,57,12,18,-6,25,17
+199,dig,"dig 11 18",2,1495,57,-3,25,19,57,12,18,-6,25,17
+200,move,"move 11 17",2,1500,57,-3,25,19,57,11,17,-6,25,17
+201,dig,"dig 10 17",2,1507,57,-3,25,19,57,11,17,-6,25,17
+202,move,"move 10 16",2,1512,57,-3,25,19,57,10,16,-6,25,17
+203,dig,"dig 9 16",2,1512,57,-3,25,19,57,10,16,-6,25,17
+204,dig,"dig 9 16",2,1512,57,-3,25,19,57,10,16,-6,25,17
+205,dig,"dig 9 16",2,1512,57,-3,25,19,57,10,16,-6,25,17
+206,dig,"dig 9 17",2,1512,57,-3,25,19,57,10,16,-6,25,17
+207,dig,"dig 9 17",2,1512,57,-3,25,19,57,10,16,-6,25,17
+208,dig,"dig 9 17",2,1512,57,-3,25,19,57,10,16,-6,25,17
+209,dig,"dig 9 17",2,1519,57,-3,25,19,57,10,16,-6,25,17
+210,shoot,"shoot S",2,1532,49,-3,25,19,49,10,16,-6,25,17
+211,shoot,"shoot S",2,1545,41,-3,25,19,41,10,16,-6,25,17
+212,shoot,"shoot S",2,1559,33,-3,25,19,33,10,16,-6,25,17
+213,shoot,"shoot S",2,1561,33,-3,25,19,33,10,16,-6,25,17
diff --git a/2019-worms/tests/strategy.rs b/2019-worms/tests/strategy.rs
new file mode 100644
index 0000000..7088099
--- /dev/null
+++ b/2019-worms/tests/strategy.rs
@@ -0,0 +1,27 @@
+use std::path::Path;
+
+use steam_powered_wyrm::game;
+use steam_powered_wyrm::json;
+use steam_powered_wyrm::strategy::{choose_move_with_normalized_perf, ScoreConfig};
+
+#[test]
+fn strategy_is_implemented_symetrically() {
+ let state = game::GameBoard::new(
+ json::read_state_from_json_file(&Path::new(&format!("./tests/example-state.json")))
+ .unwrap(),
+ );
+ let depth = 100;
+
+ let config = ScoreConfig::default();
+ let flipped_state = {
+ let mut flipped_state = state.clone();
+ flipped_state.players[0] = state.players[1].clone();
+ flipped_state.players[1] = state.players[0].clone();
+ flipped_state
+ };
+
+ let position_one_move = choose_move_with_normalized_perf(&state, &config, 0, depth);
+ let position_two_move = choose_move_with_normalized_perf(&flipped_state, &config, 1, depth);
+
+ assert_eq!(position_one_move, position_two_move);
+}
diff --git a/readme.org b/readme.org
new file mode 100644
index 0000000..3d6071f
--- /dev/null
+++ b/readme.org
@@ -0,0 +1,4 @@
+* Entelect Challenge Bots
+
+These are my bots that I've submitted to the [[https://challenge.entelect.co.za/][Entelect Challenge]]. Some
+of them did better than others!