├── .gitignore
├── LICENSE
├── README.md
├── config
├── config.ini
├── level1
│ └── statement_en.html
├── level2
│ └── statement_en.html
├── level3
│ └── statement_en.html
├── level4
│ └── statement_en.html
├── statement_en.html.tpl
└── stub.txt
├── explosion.png
├── pom.xml
├── src
├── main
│ ├── java
│ │ └── com
│ │ │ └── codingame
│ │ │ └── game
│ │ │ ├── GameException.java
│ │ │ ├── Player.java
│ │ │ ├── Referee.java
│ │ │ ├── ViewController.java
│ │ │ └── model
│ │ │ ├── BFS.java
│ │ │ ├── Direction.java
│ │ │ ├── Game.java
│ │ │ ├── GridModel.java
│ │ │ ├── IPlayerManager.java
│ │ │ ├── Mine.java
│ │ │ ├── PlayerAction.java
│ │ │ ├── PlayerModel.java
│ │ │ ├── Utils.java
│ │ │ └── commands
│ │ │ ├── ICommand.java
│ │ │ ├── IPower.java
│ │ │ ├── MessageCommand.java
│ │ │ ├── MinePower.java
│ │ │ ├── MoveCommand.java
│ │ │ ├── SilencePower.java
│ │ │ ├── SonarPower.java
│ │ │ ├── SurfaceCommand.java
│ │ │ ├── TorpedoPower.java
│ │ │ └── TriggerCommand.java
│ └── resources
│ │ ├── assembly.xml
│ │ ├── log4j2.properties
│ │ └── view
│ │ ├── assets
│ │ ├── background.jpg
│ │ ├── explosion
│ │ │ ├── 0.png
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── font.fnt
│ │ ├── font.png
│ │ ├── island
│ │ │ ├── 01111111.png
│ │ │ ├── 11011111.png
│ │ │ ├── 11110111.png
│ │ │ ├── 11111101.png
│ │ │ ├── 11111111.png
│ │ │ ├── 1111x0x1.png
│ │ │ ├── 11x0x0x1.png
│ │ │ ├── 11x0x111.png
│ │ │ ├── x0x0x111.png
│ │ │ ├── x0x11111.png
│ │ │ ├── x0x111x0.png
│ │ │ ├── x11111x0.png
│ │ │ └── x111x0x0.png
│ │ ├── logo.png
│ │ ├── mine0.png
│ │ ├── mine1.png
│ │ ├── mine2.png
│ │ └── sub.png
│ │ ├── config.js
│ │ └── demo.js
└── test
│ ├── java
│ ├── DummyPlayer.java
│ ├── Main.java
│ └── UnitTests.java
│ └── resources
│ └── log4j2.properties
└── torpedoRange.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | target/
3 |
4 | # Compiled class file
5 | *.class
6 |
7 | # Log file
8 | *.log
9 |
10 | # BlueJ files
11 | *.ctxt
12 |
13 | # Mobile Tools for Java (J2ME)
14 | .mtj.tmp/
15 |
16 | # Package Files #
17 | *.jar
18 | *.war
19 | *.nar
20 | *.ear
21 | *.zip
22 | *.tar.gz
23 | *.rar
24 |
25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
26 | hs_err_pid*
27 | captainsonar.iml
28 | src/main/resources/.DS_Store
29 | src/main/resources/ai/.DS_Store
30 | src/main/resources/view/assets/.DS_Store
31 | src/main/resources/view/assets/.DS_Store
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Erik Kvanli
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ocean of Code
2 | Community contest for CodingGame, see [https://www.codingame.com/contests/ocean-of-code](https://www.codingame.com/contests/ocean-of-code)
3 |
4 |
5 | ## Run locally
6 |
7 | ### Prerequisites:
8 |
9 | * JDK 1.8 (or higher).
10 |
11 | * Maven 3.6.x.
12 |
13 | ### Follow the next steps respectively to run the game locally:
14 |
15 | 1. Run `mvn clean install`. This bundles the game dependencies and tests in one jar file inside the target directory.
16 |
17 | 1. Run `java -jar ./target/captainsonar-1.0-SNAPSHOT-fat-tests.jar "bot-1 exec command" "bot-2 exec command"`.
18 |
19 | > Note: The executable jar expects two cli arguments, which are the two exec commands for the first and second player bots. If a cli argument is missing, the DummyPlayer bot `src/test/java/DummyPlayer.java` will be used as a replacement.
20 |
21 | Here is an example on how to run two java bots against each:
22 | ```bash
23 | java -jar ./target/captainsonar-1.0-SNAPSHOT-fat-tests.jar "java -cp target/test-classes/ DummyPlayer" "java -cp target/test-classes/ DummyPlayer"
24 | ```
25 |
26 | ### Output:
27 |
28 | Running the executable jar will output two things:
29 |
30 | * A full dump of the game summary to the stdout.
31 |
32 | * A web page to see the game in action (e.g. http://localhost:8888/test.html).
33 |
34 |
35 | Click to expand a sample game summary output.
36 |
37 | ```
38 | WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
39 | WARNING: An illegal reflective access operation has occurred
40 | WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$2
41 | WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
42 | WARNING: All illegal access operations will be denied in a future release
43 | [2020/03/30 20:21:01][GF] INFO : GameManager - Init
44 | 1337
45 | [2020/03/30 20:21:01][GF] INFO : GameManager - Turn 1
46 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 2]
47 | KEY_FRAME 0
48 | {"global":{"entitymodule":{"width":1920,"height":1080}},"frame":{"entitymodule":"CS;R;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;S;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;R;T;T;C;C;C;C;C;C;T;S;R;T;C;C;C;C;C;C;T;S;R\nU6 0 bw 60 i island/11x0x0x1.png v 1 x 630 y 210 bh 60;16 0 bw 60 i island/11111111.png v 1 x 510 y 870 bh 60;41 0 bw 60 i island/x11111x0.png v 1 x 1050 y 930 bh 60;238 0 v 1 x 165 w 205 y 45 F 0 c -1 W 10 z 1 h 205;46 0 bw 60 i island/x0x0x111.png v 1 x 1110 y 810 bh 60;243 0 f -2 v 1 a 0.8 x 1480 y 115 c 0 R 12 W 4;5 0 bw 60 i island/11111111.png v 1 x 510 y 210 bh 60;26 0 bw 60 i island/11111111.png v 1 x 570 y 930 bh 60;45 0 bw 60 i island/x0x0x111.png v 1 x 810 y 450 bh 60;244 0 f -2 v 1 a 0.8 x 1480 y 75 c 0 R 12 W 4;21 0 bw 60 i island/x0x111x0.png v 1 x 1110 y 270 bh 60;37 0 bw 60 i island/11x0x111.png v 1 x 1110 y 870 bh 60;38 0 bw 60 i island/x11111x0.png v 1 x 750 y 510 bh 60;47 0 bw 60 i island/11x0x0x1.png v 1 x 1290 y 870 bh 60;32 0 bw 60 i island/11x0x111.png v 1 x 810 y 510 bh 60;236 0 f -1 v 1 ff Arial ax 0.5 x 270 y 270 s 60 T $0 z 20 maxWidth 400;240 0 f -2 v 1 a 0.8 x 1480 y 235 c 0 R 12 W 4;22 0 bw 60 i island/x0x11111.png v 1 x 1170 y 270 bh 60;231 0 f -1 v 1 a 0.8 x 440 y 195 c 0 R 12 W 4;42 0 bw 60 i island/1111x0x1.png v 1 x 510 y 270 bh 60;242 0 f -2 v 1 a 0.8 x 1480 y 155 c 0 R 12 W 4;246 0 f -2 v 1 ff Arial ax 0.5 x 1650 y 270 s 60 T $1 z 20 maxWidth 400;19 0 bw 60 i island/x0x11111.png v 1 x 510 y 810 bh 60;237 0 bw 200 i $0 v 1 ax 0.5 x 270 y 50 bh 200;39 0 bw 60 i island/11x0x0x1.png v 1 x 810 y 570 bh 60;35 0 bw 60 i island/11x0x111.png v 1 x 1110 y 930 bh 60;28 0 bw 60 i island/x0x11111.png v 1 x 570 y 810 bh 60;48 0 bw 60 i island/x111x0x0.png v 1 x 1230 y 870 bh 60;30 0 bw 60 i island/11111111.png v 1 x 570 y 90 bh 60;43 0 bw 60 i island/x0x111x0.png v 1 x 750 y 450 bh 60;31 0 bw 60 i island/11110111.png v 1 x 570 y 210 bh 60;25 0 bw 60 i island/x0x0x111.png v 1 x 630 y 570 bh 60;233 0 f -1 v 1 a 0.8 x 440 y 115 c 0 R 12 W 4;40 0 bw 60 i island/x11111x0.png v 1 x 1050 y 870 bh 60;20 0 bw 60 i island/x0x0x111.png v 1 x 630 y 810 bh 60;29 0 bw 60 i island/x111x0x0.png v 1 x 570 y 630 bh 60;8 0 bw 60 i island/11111111.png v 1 x 510 y 150 bh 60;15 0 bw 60 i island/1111x0x1.png v 1 x 1170 y 330 bh 60;245 0 f -2 v 1 a 0.8 x 1480 y 35 c 0 R 12 W 4;18 0 bw 60 i island/11x0x0x1.png v 1 x 1230 y 330 bh 60;12 0 bw 60 i island/x111x0x0.png v 1 x 1110 y 330 bh 60;34 0 bw 60 i island/x0x111x0.png v 1 x 1230 y 810 bh 60;3 0 bw 60 i island/11111111.png v 1 x 510 y 90 bh 60;23 0 bw 60 i island/11x0x0x1.png v 1 x 630 y 630 bh 60;24 0 bw 60 i island/11111111.png v 1 x 570 y 150 bh 60;27 0 bw 60 i island/11111111.png v 1 x 570 y 870 bh 60;11 0 bw 60 i island/x0x111x0.png v 1 x 570 y 570 bh 60;235 0 f -1 v 1 a 0.8 x 440 y 35 c 0 R 12 W 4;9 0 bw 60 i island/11x0x111.png v 1 x 630 y 150 bh 60;14 0 bw 60 i island/11x0x111.png v 1 x 630 y 930 bh 60;36 0 bw 60 i island/x0x0x111.png v 1 x 1290 y 810 bh 60;248 0 v 1 x 1550 w 205 y 45 F 0 c -2 W 10 z 1 h 205;33 0 bw 60 i island/x111x0x0.png v 1 x 750 y 570 bh 60;10 0 bw 60 i island/x0x0x111.png v 1 x 1230 y 270 bh 60;234 0 f -1 v 1 a 0.8 x 440 y 75 c 0 R 12 W 4;232 0 f -1 v 1 a 0.8 x 440 y 155 c 0 R 12 W 4;13 0 bw 60 i island/11111111.png v 1 x 510 y 930 bh 60;7 0 bw 60 i island/11x0x0x1.png v 1 x 570 y 270 bh 60;230 0 f -1 v 1 a 0.8 x 440 y 235 c 0 R 12 W 4;247 0 bw 200 i $1 v 1 ax 0.5 x 1650 y 50 bh 200;44 0 bw 60 i island/x0x111x0.png v 1 x 1050 y 810 bh 60;4 0 bw 60 i island/11x0x111.png v 1 x 630 y 90 bh 60;17 0 bw 60 i island/11x0x111.png v 1 x 630 y 870 bh 60;241 0 f -2 v 1 a 0.8 x 1480 y 195 c 0 R 12 W 4;106 1 v 1 a 0 x 810 w 60 y 810 h 60;167 1 v 1 a 0 x 1110 w 60 y 150 h 60;174 1 v 1 a 0 x 1110 w 60 y 690 h 60;192 1 v 1 a 0 x 1230 w 60 y 390 h 60;51 1 v 1 a 0 x 510 w 60 y 450 h 60;135 1 v 1 a 0 x 930 w 60 y 750 h 60;113 1 v 1 a 0 x 870 w 60 y 330 h 60;199 1 v 1 a 0 x 1230 w 60 y 930 h 60;82 1 v 1 a 0 x 690 w 60 y 810 h 60;119 1 v 1 a 0 x 870 w 60 y 690 h 60;84 1 v 1 a 0 x 690 w 60 y 930 h 60;147 1 v 1 a 0 x 990 w 60 y 570 h 60;77 1 v 1 a 0 x 690 w 60 y 510 h 60;217 1 v 1 a 0 x 1350 w 60 y 330 h 60;69 1 v 1 a 0 x 630 w 60 y 750 h 60;146 1 v 1 a 0 x 990 w 60 y 510 h 60;179 1 v 1 a 0 x 1170 w 60 y 390 h 60;162 1 v 1 a 0 x 1050 w 60 y 570 h 60;197 1 v 1 a 0 x 1230 w 60 y 690 h 60;76 1 v 1 a 0 x 690 w 60 y 450 h 60;194 1 v 1 a 0 x 1230 w 60 y 510 h 60;203 1 v 1 a 0 x 1290 w 60 y 270 h 60;151 1 v 1 a 0 x 990 w 60 y 810 h 60;177 1 v 1 a 0 x 1170 w 60 y 150 h 60;55 1 v 1 a 0 x 510 w 60 y 690 h 60;121 1 v 1 a 0 x 870 w 60 y 810 h 60;131 1 v 1 a 0 x 930 w 60 y 510 h 60;216 1 v 1 a 0 x 1350 w 60 y 270 h 60;226 1 v 1 a 0 x 1350 w 60 y 870 h 60;176 1 v 1 a 0 x 1170 w 60 y 90 h 60;185 1 v 1 a 0 x 1170 w 60 y 750 h 60;80 1 v 1 a 0 x 690 w 60 y 690 h 60;1 1 i background.jpg ay 0 v 1 ax 0;70 1 v 1 a 0 x 690 w 60 y 90 h 60;98 1 v 1 a 0 x 810 w 60 y 150 h 60;153 1 v 1 a 0 x 990 w 60 y 930 h 60;184 1 v 1 a 0 x 1170 w 60 y 690 h 60;189 1 v 1 a 0 x 1230 w 60 y 90 h 60;150 1 v 1 a 0 x 990 w 60 y 750 h 60;68 1 v 1 a 0 x 630 w 60 y 690 h 60;97 1 v 1 a 0 x 810 w 60 y 90 h 60;204 1 v 1 a 0 x 1290 w 60 y 330 h 60;218 1 v 1 a 0 x 1350 w 60 y 390 h 60;227 1 v 1 a 0 x 1350 w 60 y 930 h 60;90 1 v 1 a 0 x 750 w 60 y 390 h 60;114 1 v 1 a 0 x 870 w 60 y 390 h 60;93 1 v 1 a 0 x 750 w 60 y 750 h 60;206 1 v 1 a 0 x 1290 w 60 y 450 h 60;212 1 v 1 a 0 x 1290 w 60 y 930 h 60;67 1 v 1 a 0 x 630 w 60 y 510 h 60;50 1 v 1 a 0 x 510 w 60 y 390 h 60;120 1 v 1 a 0 x 870 w 60 y 750 h 60;133 1 v 1 a 0 x 930 w 60 y 630 h 60;56 1 v 1 a 0 x 510 w 60 y 750 h 60;163 1 v 1 a 0 x 1050 w 60 y 630 h 60;142 1 v 1 a 0 x 990 w 60 y 270 h 60;202 1 v 1 a 0 x 1290 w 60 y 210 h 60;96 1 v 1 a 0 x 750 w 60 y 930 h 60;222 1 v 1 a 0 x 1350 w 60 y 630 h 60;158 1 v 1 a 0 x 1050 w 60 y 330 h 60;125 1 v 1 a 0 x 930 w 60 y 150 h 60;138 1 v 1 a 0 x 930 w 60 y 930 h 60;73 1 v 1 a 0 x 690 w 60 y 270 h 60;165 1 v 1 a 0 x 1050 w 60 y 750 h 60;239 1 f 16777215 v 1 ff Arial ax 1 x 1890 y 370 s 30 T maxWidth 400 z 100;155 1 v 1 a 0 x 1050 w 60 y 150 h 60;213 1 v 1 a 0 x 1350 w 60 y 90 h 60;186 1 v 1 a 0 x 1170 w 60 y 810 h 60;63 1 v 1 a 0 x 630 w 60 y 270 h 60;130 1 v 1 a 0 x 930 w 60 y 450 h 60;164 1 v 1 a 0 x 1050 w 60 y 690 h 60;59 1 v 1 a 0 x 570 w 60 y 450 h 60;74 1 v 1 a 0 x 690 w 60 y 330 h 60;161 1 v 1 a 0 x 1050 w 60 y 510 h 60;173 1 v 1 a 0 x 1110 w 60 y 630 h 60;228 1 ay 0.5 v 1 ff Arial ax 0.5 x 960 S 2 y 1040 s 40 T sc 0;122 1 v 1 a 0 x 870 w 60 y 870 h 60;143 1 v 1 a 0 x 990 w 60 y 330 h 60;140 1 v 1 a 0 x 990 w 60 y 150 h 60;94 1 v 1 a 0 x 750 w 60 y 810 h 60;209 1 v 1 a 0 x 1290 w 60 y 630 h 60;104 1 v 1 a 0 x 810 w 60 y 690 h 60;107 1 v 1 a 0 x 810 w 60 y 870 h 60;198 1 v 1 a 0 x 1230 w 60 y 750 h 60;52 1 v 1 a 0 x 510 w 60 y 510 h 60;116 1 v 1 a 0 x 870 w 60 y 510 h 60;136 1 v 1 a 0 x 930 w 60 y 810 h 60;101 1 v 1 a 0 x 810 w 60 y 330 h 60;91 1 v 1 a 0 x 750 w 60 y 630 h 60;141 1 v 1 a 0 x 990 w 60 y 210 h 60;81 1 v 1 a 0 x 690 w 60 y 750 h 60;124 1 v 1 a 0 x 930 w 60 y 90 h 60;160 1 v 1 a 0 x 1050 w 60 y 450 h 60;175 1 v 1 a 0 x 1110 w 60 y 750 h 60;95 1 v 1 a 0 x 750 w 60 y 870 h 60;149 1 v 1 a 0 x 990 w 60 y 690 h 60;200 1 v 1 a 0 x 1290 w 60 y 90 h 60;103 1 v 1 a 0 x 810 w 60 y 630 h 60;134 1 v 1 a 0 x 930 w 60 y 690 h 60;110 1 v 1 a 0 x 870 w 60 y 150 h 60;183 1 v 1 a 0 x 1170 w 60 y 630 h 60;225 1 v 1 a 0 x 1350 w 60 y 810 h 60;123 1 v 1 a 0 x 870 w 60 y 930 h 60;79 1 v 1 a 0 x 690 w 60 y 630 h 60;65 1 v 1 a 0 x 630 w 60 y 390 h 60;92 1 v 1 a 0 x 750 w 60 y 690 h 60;87 1 v 1 a 0 x 750 w 60 y 210 h 60;75 1 v 1 a 0 x 690 w 60 y 390 h 60;137 1 v 1 a 0 x 930 w 60 y 870 h 60;223 1 v 1 a 0 x 1350 w 60 y 690 h 60;62 1 v 1 a 0 x 570 w 60 y 750 h 60;182 1 v 1 a 0 x 1170 w 60 y 570 h 60;115 1 v 1 a 0 x 870 w 60 y 450 h 60;139 1 v 1 a 0 x 990 w 60 y 90 h 60;220 1 v 1 a 0 x 1350 w 60 y 510 h 60;159 1 v 1 a 0 x 1050 w 60 y 390 h 60;205 1 v 1 a 0 x 1290 w 60 y 390 h 60;89 1 v 1 a 0 x 750 w 60 y 330 h 60;60 1 v 1 a 0 x 570 w 60 y 510 h 60;219 1 v 1 a 0 x 1350 w 60 y 450 h 60;112 1 v 1 a 0 x 870 w 60 y 270 h 60;190 1 v 1 a 0 x 1230 w 60 y 150 h 60;214 1 v 1 a 0 x 1350 w 60 y 150 h 60;144 1 v 1 a 0 x 990 w 60 y 390 h 60;53 1 v 1 a 0 x 510 w 60 y 570 h 60;168 1 v 1 a 0 x 1110 w 60 y 210 h 60;156 1 v 1 a 0 x 1050 w 60 y 210 h 60;57 1 v 1 a 0 x 570 w 60 y 330 h 60;191 1 v 1 a 0 x 1230 w 60 y 210 h 60;193 1 v 1 a 0 x 1230 w 60 y 450 h 60;211 1 v 1 a 0 x 1290 w 60 y 750 h 60;195 1 v 1 a 0 x 1230 w 60 y 570 h 60;169 1 v 1 a 0 x 1110 w 60 y 390 h 60;99 1 v 1 a 0 x 810 w 60 y 210 h 60;66 1 v 1 a 0 x 630 w 60 y 450 h 60;187 1 v 1 a 0 x 1170 w 60 y 870 h 60;201 1 v 1 a 0 x 1290 w 60 y 150 h 60;109 1 v 1 a 0 x 870 w 60 y 90 h 60;72 1 v 1 a 0 x 690 w 60 y 210 h 60;64 1 v 1 a 0 x 630 w 60 y 330 h 60;215 1 v 1 a 0 x 1350 w 60 y 210 h 60;166 1 v 1 a 0 x 1110 w 60 y 90 h 60;88 1 v 1 a 0 x 750 w 60 y 270 h 60;224 1 v 1 a 0 x 1350 w 60 y 750 h 60;128 1 v 1 a 0 x 930 w 60 y 330 h 60;157 1 v 1 a 0 x 1050 w 60 y 270 h 60;196 1 v 1 a 0 x 1230 w 60 y 630 h 60;207 1 v 1 a 0 x 1290 w 60 y 510 h 60;181 1 v 1 a 0 x 1170 w 60 y 510 h 60;2 1 v 1 x 507 w 906 y 87 F 0 c 15724527 W 10 z 99 h 906;145 1 v 1 a 0 x 990 w 60 y 450 h 60;171 1 v 1 a 0 x 1110 w 60 y 510 h 60;229 1 f 16777215 v 1 ff Arial x 30 y 370 s 30 T maxWidth 400 z 100;221 1 v 1 a 0 x 1350 w 60 y 570 h 60;100 1 v 1 a 0 x 810 w 60 y 270 h 60;105 1 v 1 a 0 x 810 w 60 y 750 h 60;108 1 v 1 a 0 x 810 w 60 y 930 h 60;54 1 v 1 a 0 x 510 w 60 y 630 h 60;129 1 v 1 a 0 x 930 w 60 y 390 h 60;170 1 v 1 a 0 x 1110 w 60 y 450 h 60;188 1 v 1 a 0 x 1170 w 60 y 930 h 60;71 1 v 1 a 0 x 690 w 60 y 150 h 60;178 1 v 1 a 0 x 1170 w 60 y 210 h 60;61 1 v 1 a 0 x 570 w 60 y 690 h 60;117 1 v 1 a 0 x 870 w 60 y 570 h 60;126 1 v 1 a 0 x 930 w 60 y 210 h 60;102 1 v 1 a 0 x 810 w 60 y 390 h 60;83 1 v 1 a 0 x 690 w 60 y 870 h 60;210 1 v 1 a 0 x 1290 w 60 y 690 h 60;152 1 v 1 a 0 x 990 w 60 y 870 h 60;127 1 v 1 a 0 x 930 w 60 y 270 h 60;154 1 v 1 a 0 x 1050 w 60 y 90 h 60;58 1 v 1 a 0 x 570 w 60 y 390 h 60;85 1 v 1 a 0 x 750 w 60 y 90 h 60;208 1 v 1 a 0 x 1290 w 60 y 570 h 60;172 1 v 1 a 0 x 1110 w 60 y 570 h 60;78 1 v 1 a 0 x 690 w 60 y 570 h 60;180 1 v 1 a 0 x 1170 w 60 y 450 h 60;148 1 v 1 a 0 x 990 w 60 y 630 h 60;86 1 v 1 a 0 x 750 w 60 y 150 h 60;118 1 v 1 a 0 x 870 w 60 y 630 h 60;49 1 v 1 a 0 x 510 w 60 y 330 h 60;111 1 v 1 a 0 x 870 w 60 y 210 h 60;132 1 v 1 a 0 x 930 w 60 y 570 h 60","tooltips":[{"3":"Island\nx = 0\ny = 0","4":"Island\nx = 2\ny = 0","5":"Island\nx = 0\ny = 2","6":"Island\nx = 2\ny = 2","7":"Island\nx = 1\ny = 3","8":"Island\nx = 0\ny = 1","9":"Island\nx = 2\ny = 1","10":"Island\nx = 12\ny = 3","11":"Island\nx = 1\ny = 8","12":"Island\nx = 10\ny = 4","13":"Island\nx = 0\ny = 14","14":"Island\nx = 2\ny = 14","15":"Island\nx = 11\ny = 4","16":"Island\nx = 0\ny = 13","17":"Island\nx = 2\ny = 13","18":"Island\nx = 12\ny = 4","19":"Island\nx = 0\ny = 12","20":"Island\nx = 2\ny = 12","21":"Island\nx = 10\ny = 3","22":"Island\nx = 11\ny = 3","23":"Island\nx = 2\ny = 9","24":"Island\nx = 1\ny = 1","25":"Island\nx = 2\ny = 8","26":"Island\nx = 1\ny = 14","27":"Island\nx = 1\ny = 13","28":"Island\nx = 1\ny = 12","29":"Island\nx = 1\ny = 9","30":"Island\nx = 1\ny = 0","31":"Island\nx = 1\ny = 2","32":"Island\nx = 5\ny = 7","33":"Island\nx = 4\ny = 8","34":"Island\nx = 12\ny = 12","35":"Island\nx = 10\ny = 14","36":"Island\nx = 13\ny = 12","37":"Island\nx = 10\ny = 13","38":"Island\nx = 4\ny = 7","39":"Island\nx = 5\ny = 8","40":"Island\nx = 9\ny = 13","41":"Island\nx = 9\ny = 14","42":"Island\nx = 0\ny = 3","43":"Island\nx = 4\ny = 6","44":"Island\nx = 9\ny = 12","45":"Island\nx = 5\ny = 6","46":"Island\nx = 10\ny = 12","47":"Island\nx = 13\ny = 13","48":"Island\nx = 12\ny = 13","49":"Water\nx = 0\ny = 4","50":"Water\nx = 0\ny = 5","51":"Water\nx = 0\ny = 6","52":"Water\nx = 0\ny = 7","53":"Water\nx = 0\ny = 8","54":"Water\nx = 0\ny = 9","55":"Water\nx = 0\ny = 10","56":"Water\nx = 0\ny = 11","57":"Water\nx = 1\ny = 4","58":"Water\nx = 1\ny = 5","59":"Water\nx = 1\ny = 6","60":"Water\nx = 1\ny = 7","61":"Water\nx = 1\ny = 10","62":"Water\nx = 1\ny = 11","63":"Water\nx = 2\ny = 3","64":"Water\nx = 2\ny = 4","65":"Water\nx = 2\ny = 5","66":"Water\nx = 2\ny = 6","67":"Water\nx = 2\ny = 7","68":"Water\nx = 2\ny = 10","69":"Water\nx = 2\ny = 11","70":"Water\nx = 3\ny = 0","71":"Water\nx = 3\ny = 1","72":"Water\nx = 3\ny = 2","73":"Water\nx = 3\ny = 3","74":"Water\nx = 3\ny = 4","75":"Water\nx = 3\ny = 5","76":"Water\nx = 3\ny = 6","77":"Water\nx = 3\ny = 7","78":"Water\nx = 3\ny = 8","79":"Water\nx = 3\ny = 9","80":"Water\nx = 3\ny = 10","81":"Water\nx = 3\ny = 11","82":"Water\nx = 3\ny = 12","83":"Water\nx = 3\ny = 13","84":"Water\nx = 3\ny = 14","85":"Water\nx = 4\ny = 0","86":"Water\nx = 4\ny = 1","87":"Water\nx = 4\ny = 2","88":"Water\nx = 4\ny = 3","89":"Water\nx = 4\ny = 4","90":"Water\nx = 4\ny = 5","91":"Water\nx = 4\ny = 9","92":"Water\nx = 4\ny = 10","93":"Water\nx = 4\ny = 11","94":"Water\nx = 4\ny = 12","95":"Water\nx = 4\ny = 13","96":"Water\nx = 4\ny = 14","97":"Water\nx = 5\ny = 0","98":"Water\nx = 5\ny = 1","99":"Water\nx = 5\ny = 2","100":"Water\nx = 5\ny = 3","101":"Water\nx = 5\ny = 4","102":"Water\nx = 5\ny = 5","103":"Water\nx = 5\ny = 9","104":"Water\nx = 5\ny = 10","105":"Water\nx = 5\ny = 11","106":"Water\nx = 5\ny = 12","107":"Water\nx = 5\ny = 13","108":"Water\nx = 5\ny = 14","109":"Water\nx = 6\ny = 0","110":"Water\nx = 6\ny = 1","111":"Water\nx = 6\ny = 2","112":"Water\nx = 6\ny = 3","113":"Water\nx = 6\ny = 4","114":"Water\nx = 6\ny = 5","115":"Water\nx = 6\ny = 6","116":"Water\nx = 6\ny = 7","117":"Water\nx = 6\ny = 8","118":"Water\nx = 6\ny = 9","119":"Water\nx = 6\ny = 10","120":"Water\nx = 6\ny = 11","121":"Water\nx = 6\ny = 12","122":"Water\nx = 6\ny = 13","123":"Water\nx = 6\ny = 14","124":"Water\nx = 7\ny = 0","125":"Water\nx = 7\ny = 1","126":"Water\nx = 7\ny = 2","127":"Water\nx = 7\ny = 3","128":"Water\nx = 7\ny = 4","129":"Water\nx = 7\ny = 5","130":"Water\nx = 7\ny = 6","131":"Water\nx = 7\ny = 7","132":"Water\nx = 7\ny = 8","133":"Water\nx = 7\ny = 9","134":"Water\nx = 7\ny = 10","135":"Water\nx = 7\ny = 11","136":"Water\nx = 7\ny = 12","137":"Water\nx = 7\ny = 13","138":"Water\nx = 7\ny = 14","139":"Water\nx = 8\ny = 0","140":"Water\nx = 8\ny = 1","141":"Water\nx = 8\ny = 2","142":"Water\nx = 8\ny = 3","143":"Water\nx = 8\ny = 4","144":"Water\nx = 8\ny = 5","145":"Water\nx = 8\ny = 6","146":"Water\nx = 8\ny = 7","147":"Water\nx = 8\ny = 8","148":"Water\nx = 8\ny = 9","149":"Water\nx = 8\ny = 10","150":"Water\nx = 8\ny = 11","151":"Water\nx = 8\ny = 12","152":"Water\nx = 8\ny = 13","153":"Water\nx = 8\ny = 14","154":"Water\nx = 9\ny = 0","155":"Water\nx = 9\ny = 1","156":"Water\nx = 9\ny = 2","157":"Water\nx = 9\ny = 3","158":"Water\nx = 9\ny = 4","159":"Water\nx = 9\ny = 5","160":"Water\nx = 9\ny = 6","161":"Water\nx = 9\ny = 7","162":"Water\nx = 9\ny = 8","163":"Water\nx = 9\ny = 9","164":"Water\nx = 9\ny = 10","165":"Water\nx = 9\ny = 11","166":"Water\nx = 10\ny = 0","167":"Water\nx = 10\ny = 1","168":"Water\nx = 10\ny = 2","169":"Water\nx = 10\ny = 5","170":"Water\nx = 10\ny = 6","171":"Water\nx = 10\ny = 7","172":"Water\nx = 10\ny = 8","173":"Water\nx = 10\ny = 9","174":"Water\nx = 10\ny = 10","175":"Water\nx = 10\ny = 11","176":"Water\nx = 11\ny = 0","177":"Water\nx = 11\ny = 1","178":"Water\nx = 11\ny = 2","179":"Water\nx = 11\ny = 5","180":"Water\nx = 11\ny = 6","181":"Water\nx = 11\ny = 7","182":"Water\nx = 11\ny = 8","183":"Water\nx = 11\ny = 9","184":"Water\nx = 11\ny = 10","185":"Water\nx = 11\ny = 11","186":"Water\nx = 11\ny = 12","187":"Water\nx = 11\ny = 13","188":"Water\nx = 11\ny = 14","189":"Water\nx = 12\ny = 0","190":"Water\nx = 12\ny = 1","191":"Water\nx = 12\ny = 2","192":"Water\nx = 12\ny = 5","193":"Water\nx = 12\ny = 6","194":"Water\nx = 12\ny = 7","195":"Water\nx = 12\ny = 8","196":"Water\nx = 12\ny = 9","197":"Water\nx = 12\ny = 10","198":"Water\nx = 12\ny = 11","199":"Water\nx = 12\ny = 14","200":"Water\nx = 13\ny = 0","201":"Water\nx = 13\ny = 1","202":"Water\nx = 13\ny = 2","203":"Water\nx = 13\ny = 3","204":"Water\nx = 13\ny = 4","205":"Water\nx = 13\ny = 5","206":"Water\nx = 13\ny = 6","207":"Water\nx = 13\ny = 7","208":"Water\nx = 13\ny = 8","209":"Water\nx = 13\ny = 9","210":"Water\nx = 13\ny = 10","211":"Water\nx = 13\ny = 11","212":"Water\nx = 13\ny = 14","213":"Water\nx = 14\ny = 0","214":"Water\nx = 14\ny = 1","215":"Water\nx = 14\ny = 2","216":"Water\nx = 14\ny = 3","217":"Water\nx = 14\ny = 4","218":"Water\nx = 14\ny = 5","219":"Water\nx = 14\ny = 6","220":"Water\nx = 14\ny = 7","221":"Water\nx = 14\ny = 8","222":"Water\nx = 14\ny = 9","223":"Water\nx = 14\ny = 10","224":"Water\nx = 14\ny = 11","225":"Water\nx = 14\ny = 12","226":"Water\nx = 14\ny = 13","227":"Water\nx = 14\ny = 14"}]}}
49 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[NEXT_PLAYER_INPUT] 16]
50 | 15 15 0
51 | xxx............
52 | xxx............
53 | xxx............
54 | xx........xxx..
55 | ..........xxx..
56 | ...............
57 | ....xx.........
58 | ....xx.........
59 | .xx.xx.........
60 | .xx............
61 | ...............
62 | ...............
63 | xxx......xx.xx.
64 | xxx......xx.xx.
65 | xxx......xx....
66 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === Read from player
67 | [2020/03/30 20:21:01][GF] INFO : GameRunner - 7 14
68 |
69 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === End Player
70 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 1]
71 | INTERMEDIATE_FRAME 1
72 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[NEXT_PLAYER_INPUT] 16]
73 | 15 15 1
74 | xxx............
75 | xxx............
76 | xxx............
77 | xx........xxx..
78 | ..........xxx..
79 | ...............
80 | ....xx.........
81 | ....xx.........
82 | .xx.xx.........
83 | .xx............
84 | ...............
85 | ...............
86 | xxx......xx.xx.
87 | xxx......xx.xx.
88 | xxx......xx....
89 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === Read from player
90 | [2020/03/30 20:21:01][GF] INFO : GameRunner - 7 14
91 |
92 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === End Player
93 | [2020/03/30 20:21:01][GF] INFO : GameManager - Turn 2
94 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 2]
95 | KEY_FRAME 2
96 | {"duration":10,"entitymodule":"CS;G;S;G;R;R;T;C;C;C;T;C;C;C;T;C;C;C;C;C;C;T;C;C;C;C;C;C;T;C;C;C;C;T;C;C;C;C;T;C;C;C;T;C;C;C\nU289 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 105 y 910 c 0 R 20 W 5;260 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1870 y 550 c 0 R 20 W 5;263 0 f 15724527 ay 0.5 v 1 ff Arial ax 1 x 1890 y 740 s 30 T SILENCE fw bold z 5;251 0 sx 1 i sub.png sy 1.5 ay 0.5 v 1 ax 0.5 t -2;293 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1815 y 910 c 0 R 20 W 5;270 0 f 15724527 ay 0.5 v 1 ff Arial ax 0 x 30 y 740 s 30 T SILENCE fw bold z 5;276 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1595 y 790 c 0 R 20 W 5;271 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1870 y 790 c 0 R 20 W 5;268 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 270 y 790 c 0 R 20 W 5;282 0 f 15724527 ay 0.5 v 1 ff Arial ax 0 x 30 y 620 s 30 T SONAR fw bold z 5;286 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1705 y 670 c 0 R 20 W 5;265 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 105 y 790 c 0 R 20 W 5;287 0 f 15724527 ay 0.5 v 1 ff Arial ax 1 x 1890 y 860 s 30 T MINE fw bold z 5;250 0 x 960 y 960 v 1 ch 249 z 100;267 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 215 y 790 c 0 R 20 W 5;284 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1815 y 670 c 0 R 20 W 5;283 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1870 y 670 c 0 R 20 W 5;249 0 sx 1 i sub.png sy 1.5 ay 0.5 v 1 ax 0.5 t -1;288 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 50 y 910 c 0 R 20 W 5;285 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1760 y 670 c 0 R 20 W 5;290 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 160 y 910 c 0 R 20 W 5;269 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 325 y 790 c 0 R 20 W 5;277 0 f 15724527 ay 0.5 v 1 ff Arial ax 1 x 1890 y 620 s 30 T SONAR fw bold z 5;291 0 f 15724527 ay 0.5 v 1 ff Arial ax 0 x 30 y 860 s 30 T MINE fw bold z 5;274 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1705 y 790 c 0 R 20 W 5;258 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 160 y 550 c 0 R 20 W 5;272 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1815 y 790 c 0 R 20 W 5;279 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 105 y 670 c 0 R 20 W 5;262 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1760 y 550 c 0 R 20 W 5;266 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 160 y 790 c 0 R 20 W 5;280 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 160 y 670 c 0 R 20 W 5;261 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1815 y 550 c 0 R 20 W 5;264 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 50 y 790 c 0 R 20 W 5;294 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1760 y 910 c 0 R 20 W 5;256 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 50 y 550 c 0 R 20 W 5;273 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1760 y 790 c 0 R 20 W 5;257 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 105 y 550 c 0 R 20 W 5;252 0 x 960 y 960 v 1 ch 251 z 100;292 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1870 y 910 c 0 R 20 W 5;255 0 f 15724527 ay 0.5 v 1 ff Arial ax 1 x 1890 y 500 s 30 T TORPEDO fw bold z 5;281 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 215 y 670 c 0 R 20 W 5;259 0 f 15724527 ay 0.5 v 1 ff Arial ax 0 x 30 y 500 s 30 T TORPEDO fw bold z 5;275 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 1650 y 790 c 0 R 20 W 5;278 0 f 5592405 sx 0.5 sy 0.5 v 1 a 0.8 x 50 y 670 c 0 R 20 W 5;253 1 f -1 v 1 a 0.5 x 930 w 60 y 930 A 0 h 60 z 1;254 1 f -2 v 1 a 0.5 x 930 w 60 y 930 A 0 h 60 z 0","tooltips":[{"249":"Player 0","251":"Player 1"}]}
97 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[NEXT_PLAYER_INPUT] 3]
98 | 7 14 6 6 3 4 6 3
99 | NA
100 | NA
101 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === Read from player
102 | [2020/03/30 20:21:01][GF] INFO : GameRunner - MINE N|TRIGGER 7 9
103 |
104 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === End Player
105 | [2020/03/30 20:21:01][GF] INFO : GameManager - Turn 3
106 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 2]
107 | KEY_FRAME 3
108 | {"duration":500,"entitymodule":"CC\nU295 0 f -1 sx 0 sy 0 v 1 x 960 y 960 F 0 c -1 R 20;228 0 f -1 T 'MINE N > TRIGGER 7 9';295 0.2 sx 1 ~ sy 1 ~ F 0.5;250 0.5 sx 2 sy 2;250 1 sx 1 sy 1;235 1 f 5592405 sx 0.5 sy 0.5;295 1 sx 10 sy 10 a 0","tooltips":[{}]}
109 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[NEXT_PLAYER_INPUT] 3]
110 | 7 14 6 5 3 4 6 3
111 | NA
112 | SURFACE 8
113 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === Read from player
114 | [2020/03/30 20:21:01][GF] INFO : GameRunner - MINE N|TRIGGER 7 9
115 |
116 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === End Player
117 | [2020/03/30 20:21:01][GF] INFO : GameManager - Turn 4
118 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 2]
119 | KEY_FRAME 4
120 | {"entitymodule":"CC\nU228 0 f -2;296 0 f -2 sx 0 sy 0 v 1 x 960 y 960 F 0 c -2 R 20;296 0.2 sx 1 ~ sy 1 ~ F 0.5;252 0.5 sx 2 sy 2;252 1 sx 1 sy 1;245 1 f 5592405 sx 0.5 sy 0.5;296 1 sx 10 sy 10 a 0","tooltips":[{}]}
121 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[NEXT_PLAYER_INPUT] 3]
122 | 7 14 5 5 3 4 6 3
123 | NA
124 | SURFACE 8
125 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === Read from player
126 | [2020/03/30 20:21:01][GF] INFO : GameRunner - MOVE N MSG|MSG WAZZAP 5?!
127 |
128 | [2020/03/30 20:21:01][GF] INFO : GameRunner - === End Player
129 | [2020/03/30 20:21:01][GF] INFO : GameManager - End
130 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[VIEW] 2]
131 | KEY_FRAME 5
132 | {"entitymodule":"U228 0 f -1 T 'MOVE N MSG';229 0 T 'WAZZAP 5?!';233 1 f 5592405 sx 0.5 sy 0.5;234 1 f 5592405 sx 0.5 sy 0.5;232 1 f 5592405 sx 0.5 sy 0.5;231 1 f 5592405 sx 0.5 sy 0.5;230 1 f 5592405 sx 0.5 sy 0.5","tooltips":[{}],"endScreen":[[-1,5],"logo.png",["-1 life","5 lives"]]}
133 | [2020/03/30 20:21:01][GF] INFO : GameManager - [[UINPUT] 1]
134 | [2020/03/30 20:21:01][GF] INFO : GameManager - seed=1337
135 | http://localhost:8888/test.html
136 | Exposed web server dir: /private/var/folders/9_/4l2_p7_108z0_p050d4ngn8w0000gn/T/codingame
137 | [2020/03/30 20:21:02][GF] INFO : xnio - XNIO version 3.3.8.Final
138 | [2020/03/30 20:21:02][GF] INFO : nio - XNIO NIO Implementation Version 3.3.8.Final
139 | ```
140 |
141 |
--------------------------------------------------------------------------------
/config/config.ini:
--------------------------------------------------------------------------------
1 | #Tue Jun 26 17:23:24 CEST 2018
2 | max_players=2
3 | title=Captain Sonar
4 | type=multi
5 | min_players=2
6 |
--------------------------------------------------------------------------------
/config/level1/statement_en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |

12 |
13 |
14 | This is a league-based challenge.
15 |
16 |
17 | Wood leagues should be considered as a tutorial which lets players discover the different rules of the game.
18 | In Bronze league, all rules will be unlocked and the real challenge will begin.
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | The Goal
27 |
28 |
29 | This is a two-player game based on the board game Captain Sonar.
30 | You pilot a submarine. You know that an enemy is present nearby because you are listening to its radio frequency communication.
31 | You don't know exactly where it is but you can hear all of its orders.
32 | You and your opponent start with 6 hit points. When a player's hit points reach 0, that player loses.
33 |
34 |
35 |
36 |
37 |
38 |
39 | Rules
40 |
41 |
42 |
Environment
43 |
44 | - The map is a square tiling: 15 cells wide and 15 cells high. The NW corner is (0,0); the SW corner is (0,14).
45 | - Each map cell is either open water or an island. Islands are obstacles. Submarines cannot move to or through islands. Nor can torpedoes or mines.
46 | - Submarines move on the open water cells of the map. Each player controls one submarine; both submarines move independently. They can share the same water cell without colliding.
47 | - The map is split in 9 sectors. Each sector encompasses a 5×5 block of 25 cells. You can find the sectors' numbering in the viewer; the top left sector is number 1 and the bottom right sector is number 9.
48 |
49 |
50 |
Game start
51 |
52 | At the beginning of the game, you'll receive a full 15×15 map that indicates the position of the islands.
53 | You will then decide and reply where you want your submarine's starting position to be.
54 |
55 |
56 |
Turns
57 |
This is a turn-based game: each player plays a turn one after the other. The whole game lasts 299 turns per player. The player with id 0 begins.
58 |
During your turn, thanks to your radio frequency analysis, you will receive an indication of what your opponent has done.
59 | For example, you can receive that it moved to the north. It's up to you to use this valuable information to detect where the opponent actually is.
60 |
61 |
62 | During your turn, you must perform at least one action.
63 |
64 |
65 |
Actions
66 |
67 | Actions are your turn's output. They are provided sequentially, on a single line. You can chain several of them using a pipe symbol |.
68 |
But you can use each type of action only once per turn. For example you can MOVE once per turn, and no more.
69 | If you fail to output a valid action, your action will be forced to SURFACE.
70 |
71 |
72 |
Move
73 |
A move action has two effects:
74 |
75 | - It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
76 | - It charges a special power of your choice, i.e. increases its energy level by 1.
77 |
78 |
When you move, you must respect the following rules:
79 |
- You cannot move through islands.
80 | - You cannot move to a cell you have visited before.
81 |
82 |
83 |
Powers
84 |
For each move action you take, you have to decide what special power to charge. The special powers are performed by different devices, who require a different amount of energy to be ready.
85 |
The energy levels are separate per device, not shared. The amounts of energy needed to activate a device are also distinct per device.
86 | A device's energy is fully consumed when that device's power is used. The energy level cannot grow above that device's requirement for one use of the power.
87 |
88 | In this league you only have access to the torpedo.
89 |
90 |
Surface
91 | The surface action will reset your path of visited cells, so that you can freely move to a cell that you have previously visited.
92 | But surfacing has major impacts: your opponent will know in which sector you are surfacing, and you will lose
1 hit point.
93 |
94 |
Torpedo
95 | The torpedo action requires an energy level of
3.
96 | When fully charged, the torpedo can be fired at an arbitrary water position within a water range of
4 cells.
97 | This allows the torpedo's path to contain corners and go around islands, but not through them.
98 | The following image illustrates the range of a torpedo:
99 |

100 |
101 |
Explosions
102 |
When a torpedo or a mine explodes on a water cell, any submarine present on that cell and its 8 neighbors suffers a loss of hit points. The damage is 2 hit points on the explosion cell itself and 1 hit point on its neighbor cells—this includes diagonals.
103 |

104 |
105 |
Explosions damage your opponent's submarine and yours indiscriminately. They have no effect on anything else (mines, islands).
106 |
107 |
108 |
109 |
Orders' visibility
110 | The following table shows how different actions will be shown to your opponent:
111 |
112 | Your action | Shown to opponent | Comment |
113 |
114 | MOVE N TORPEDO |
115 | MOVE N |
116 | The opponent will not see which power is charged. |
117 |
118 |
119 | SURFACE |
120 | SURFACE 3 |
121 | The opponent will see which sector (in this case: 3) you surfaced in. |
122 |
123 |
124 | TORPEDO 3 5 |
125 | TORPEDO 3 5 |
126 | The opponent will see the same order you issued. |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
Victory Conditions
136 |
Have more hit points left than your enemy at the end of the game.
137 |
138 |
139 |
140 |
141 |
142 |
143 |
Loss Conditions
144 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | Expert Rules
157 |
158 |
159 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | Game Input
169 |
170 |
171 |
172 |
Inputs of the first turn
173 |
Line 1:
174 |
width height myId, space-separated.
175 |
- width and height indicate the size of the grid in cells (15 15)
176 | - myId indicates your player ID (0 or 1). The player with ID 0 begins.
177 |
178 |
Next height lines: a string of
width chars representing the content of each cells of this row:
x for an island,
. for an empty cell.
179 |
180 |
181 |
182 |
183 |
Output for the first turn
184 |
2 space-separated integers x y which represent the coordinates of your starting position. The cell at the specified coordinates should be empty which means no island, but the opponent can choose the same cell!
185 |
186 |
187 |
188 |
189 |
Inputs for each turn
190 |
Line 1: 8 space-separated integers:
191 |
x y myLife oppLife torpedoCooldown sonarCooldown silenceCooldown mineCooldown
192 |
193 | - x and y represent your current position.
194 | - myLife and oppLife give the number of hit points you and your opponent have left
195 | - torpedoCooldown sonarCooldown silenceCooldown mineCooldown represent the number of charges each device still needs before it can be used.
196 | Devices unavailable in your league will have a cooldown value of -1.
197 |
198 |
199 |
Line 2: a string
sonarResult which gives you the result of your
SONAR action:
Y for yes,
N for no,
NA, if you didn't use the sonar on the previous turn.
200 |
Line 3: opponentOrders, a summary of your opponent's actions (separated by a pipe symbol
|) during its turn.
201 | Example:
MOVE N|TORPEDO 3 5
202 | This example indicates that your opponent moved to the north and then fired a torpedo to (3,5).
203 | On the starting player's first turn, this will be the string
NA, since the opponent has no previous orders at that point.
204 |
205 |
206 |
207 |
208 |
Output for each next turns
209 |
One or multiple commands separated by a pipe symbol
|.
210 | Example:
MOVE N TORPEDO | TORPEDO 3 5
211 | These commands move your submarine to the north and then fire a torpedo at (3,5).
212 |
213 | Here are the different available actions:
214 |
215 | - MOVE direction power
216 | - SURFACE
217 | - TORPEDO X Y
218 | - MSG message
219 |
220 | Here are the different available powers to charge:
221 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | Constraints
231 |
232 |
233 | Response time first turn ≤ 1000 ms
234 |
Response time per turn ≤ 50 ms
235 |
A game ends after 300 turns per player
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/config/level2/statement_en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |

12 |
13 |
14 | Welcome to the Wood1 league!
15 |
16 |
17 | In Wood 1, you can now use SONAR and SILENCE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | The Goal
26 |
27 |
28 | This is a two-player game based on the board game Captain Sonar.
29 | You pilot a submarine. You know that an enemy is present nearby because you are listening to its radio frequency communication.
30 | You don't know exactly where it is but you can hear all of its orders.
31 | You and your opponent start with 6 hit points. When a player's hit points reach 0, that player loses.
32 |
33 |
34 |
35 |
36 |
37 |
38 | Rules
39 |
40 |
41 |
Environment
42 |
43 | - The map is a square tiling: 15 cells wide and 15 cells high. The NW corner is (0,0); the SW corner is (0,14).
44 | - Each map cell is either open water or an island. Islands are obstacles. Submarines cannot move to or through islands. Nor can torpedoes or mines.
45 | - Submarines move on the open water cells of the map. Each player controls one submarine; both submarines move independently. They can share the same water cell without colliding.
46 | - The map is split in 9 sectors. Each sector encompasses a 5×5 block of 25 cells. You can find the sectors' numbering in the viewer; the top left sector is number 1 and the bottom right sector is number 9.
47 |
48 |
49 |
Game start
50 |
51 | At the beginning of the game, you'll receive a full 15×15 map that indicates the position of the islands.
52 | You will then decide and reply where you want your submarine's starting position to be.
53 |
54 |
55 |
Turns
56 |
This is a turn-based game: each player plays a turn one after the other. The whole game lasts 299 turns per player. The player with id 0 begins.
57 |
During your turn, thanks to your radio frequency analysis, you will receive an indication of what your opponent has done.
58 | For example, you can receive that it moved to the north. It's up to you to use this valuable information to detect where the opponent actually is.
59 |
60 |
61 | During your turn, you must perform at least one action.
62 |
63 |
64 |
Actions
65 |
66 | Actions are your turn's output. They are provided sequentially, on a single line. You can chain several of them using a pipe symbol |.
67 |
But you can use each type of action only once per turn. For example you can MOVE once per turn, and no more.
68 | If you fail to output a valid action, your action will be forced to SURFACE.
69 |
70 |
71 |
Move
72 |
A move action has two effects:
73 |
74 | - It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
75 | - It charges a special power of your choice, i.e. increases its energy level by 1.
76 |
77 |
When you move, you must respect the following rules:
78 |
- You cannot move through islands.
79 | - You cannot move to a cell you have visited before.
80 |
81 |
82 |
Powers
83 |
For each move action you take, you have to decide what special power to charge. The special powers are performed by different devices, who require a different amount of energy to be ready.
84 |
The energy levels are separate per device, not shared. The amounts of energy needed to activate a device are also distinct per device.
85 | A device's energy is fully consumed when that device's power is used. The energy level cannot grow above that device's requirement for one use of the power.
86 |
87 | In this league you can charge the torpedo, the sonar and the silence mode.
88 |
89 |
Surface
90 | The surface action will reset your path of visited cells, so that you can freely move to a cell that you have previously visited.
91 | But surfacing has major impacts: your opponent will know in which sector you are surfacing, and you will lose
1 hit point.
92 |
93 |
Torpedo
94 | The torpedo action requires an energy level of
3.
95 | When fully charged, the torpedo can be fired at an arbitrary water position within a water range of
4 cells.
96 | This allows the torpedo's path to contain corners and go around islands, but not through them.
97 | The following image illustrates the range of a torpedo:
98 |

99 |
100 |
Explosions
101 |
When a torpedo or a mine explodes on a water cell, any submarine present on that cell and its 8 neighbors suffers a loss of hit points. The damage is 2 hit points on the explosion cell itself and 1 hit point on its neighbor cells—this includes diagonals.
102 |

103 |
104 |
Explosions damage your opponent's submarine and yours indiscriminately. They have no effect on anything else (mines, islands).
105 |
106 |
109 |
Sonar
110 |
The sonar action requires an energy level of 4.
111 | When fully charged, it allows you to check whether the opponent's submarine is in a chosen sector.
112 |
The response will be provided to you in your next turn's input.
113 | Careful! Said response is in respect to the time you issued the order, so your opponent will have issued and performed orders of its own by the time you receive it.
114 |
115 |
Silence
116 | The silence action requires an energy level of
6.
117 | When fully charged, it allows you to move
0 to
4 cells in a chosen direction. Same as with the Move action, you may not visit an island or an already visited cell, be it as a final action destination or a waypoint.
118 | Your opponent will not know in which direction or how far you've moved. It
will know that you used the silence action, though.
119 |
120 |
121 |
122 |
Orders' visibility
123 | The following table shows how different actions will be shown to your opponent:
124 |
125 | Your action | Shown to opponent | Comment |
126 |
127 | MOVE N TORPEDO |
128 | MOVE N |
129 | The opponent will not see which power is charged. |
130 |
131 |
132 | SURFACE |
133 | SURFACE 3 |
134 | The opponent will see which sector (in this case: 3) you surfaced in. |
135 |
136 |
137 | TORPEDO 3 5 |
138 | TORPEDO 3 5 |
139 | The opponent will see the same order you issued. |
140 |
141 |
144 | SONAR 4 |
145 | SONAR 4 |
146 | The opponent will see the same order you issued. |
147 |
148 |
151 | SILENCE N 4 |
152 | SILENCE |
153 | The opponent will see that you moved silently—but not where or how far. |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
Victory Conditions
163 |
Have more hit points left than your enemy at the end of the game.
164 |
165 |
166 |
167 |
168 |
169 |
170 |
Loss Conditions
171 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 | Expert Rules
184 |
185 |
186 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | Game Input
196 |
197 |
198 |
199 |
Inputs of the first turn
200 |
Line 1:
201 |
width height myId, space-separated.
202 |
- width and height indicate the size of the grid in cells (15 15)
203 | - myId indicates your player ID (0 or 1). The player with ID 0 begins.
204 |
205 |
Next height lines: a string of
width chars representing the content of each cells of this row:
x for an island,
. for an empty cell.
206 |
207 |
208 |
209 |
210 |
Output for the first turn
211 |
2 space-separated integers x y which represent the coordinates of your starting position. The cell at the specified coordinates should be empty which means no island, but the opponent can choose the same cell!
212 |
213 |
214 |
215 |
216 |
Inputs for each turn
217 |
Line 1: 8 space-separated integers:
218 |
x y myLife oppLife torpedoCooldown sonarCooldown silenceCooldown mineCooldown
219 |
220 | - x and y represent your current position.
221 | - myLife and oppLife give the number of hit points you and your opponent have left
222 | - torpedoCooldown sonarCooldown silenceCooldown mineCooldown represent the number of charges each device still needs before it can be used.
223 | Devices unavailable in your league will have a cooldown value of -1.
224 |
225 |
226 |
Line 2: a string
sonarResult which gives you the result of your
SONAR action:
Y for yes,
N for no,
NA, if you didn't use the sonar on the previous turn.
227 |
Line 3: opponentOrders, a summary of your opponent's actions (separated by a pipe symbol
|) during its turn.
228 | Example:
MOVE N|TORPEDO 3 5
229 | This example indicates that your opponent moved to the north and then fired a torpedo to (3,5).
230 | On the starting player's first turn, this will be the string
NA, since the opponent has no previous orders at that point.
231 |
232 |
233 |
234 |
235 |
Output for each next turns
236 |
One or multiple commands separated by a pipe symbol
|.
237 | Example:
MOVE N TORPEDO | TORPEDO 3 5
238 | These commands move your submarine to the north and then fire a torpedo at (3,5).
239 |
240 | Here are the different available actions:
241 |
242 | - MOVE direction power
243 | - SURFACE
244 | - TORPEDO X Y
245 | - SONAR
248 | - SILENCE direction distance
251 | - MSG message
252 |
253 | Here are the different available powers to charge:
254 |
255 | - TORPEDO
256 | - SONAR
257 | - SILENCE
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | Constraints
266 |
267 |
268 | Response time first turn ≤ 1000 ms
269 |
Response time per turn ≤ 50 ms
270 |
A game ends after 300 turns per player
271 |
272 |
273 |
274 |
275 |
--------------------------------------------------------------------------------
/config/level3/statement_en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |

12 |
13 |
14 | Welcome to the Bronze league!
15 |
16 |
17 | In Bronze, you can now use MINES. There won't be any additional rules.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | The Goal
26 |
27 |
28 | This is a two-player game based on the board game Captain Sonar.
29 | You pilot a submarine. You know that an enemy is present nearby because you are listening to its radio frequency communication.
30 | You don't know exactly where it is but you can hear all of its orders.
31 | You and your opponent start with 6 hit points. When a player's hit points reach 0, that player loses.
32 |
33 |
34 |
35 |
36 |
37 |
38 | Rules
39 |
40 |
41 |
Environment
42 |
43 | - The map is a square tiling: 15 cells wide and 15 cells high. The NW corner is (0,0); the SW corner is (0,14).
44 | - Each map cell is either open water or an island. Islands are obstacles. Submarines cannot move to or through islands. Nor can torpedoes or mines.
45 | - Submarines move on the open water cells of the map. Each player controls one submarine; both submarines move independently. They can share the same water cell without colliding.
46 | - The map is split in 9 sectors. Each sector encompasses a 5×5 block of 25 cells. You can find the sectors' numbering in the viewer; the top left sector is number 1 and the bottom right sector is number 9.
47 |
48 |
49 |
Game start
50 |
51 | At the beginning of the game, you'll receive a full 15×15 map that indicates the position of the islands.
52 | You will then decide and reply where you want your submarine's starting position to be.
53 |
54 |
55 |
Turns
56 |
This is a turn-based game: each player plays a turn one after the other. The whole game lasts 299 turns per player. The player with id 0 begins.
57 |
During your turn, thanks to your radio frequency analysis, you will receive an indication of what your opponent has done.
58 | For example, you can receive that it moved to the north. It's up to you to use this valuable information to detect where the opponent actually is.
59 |
60 |
61 | During your turn, you must perform at least one action.
62 |
63 |
64 |
Actions
65 |
66 | Actions are your turn's output. They are provided sequentially, on a single line. You can chain several of them using a pipe symbol |.
67 |
But you can use each type of action only once per turn. For example you can MOVE once per turn, and no more.
68 | If you fail to output a valid action, your action will be forced to SURFACE.
69 |
70 |
71 |
Move
72 |
A move action has two effects:
73 |
74 | - It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
75 | - It charges a special power of your choice, i.e. increases its energy level by 1.
76 |
77 |
When you move, you must respect the following rules:
78 |
- You cannot move through islands.
79 | - You cannot move to a cell you have visited before.
80 |
81 |
82 |
Powers
83 |
For each move action you take, you have to decide what special power to charge. The special powers are performed by different devices, who require a different amount of energy to be ready.
84 |
The energy levels are separate per device, not shared. The amounts of energy needed to activate a device are also distinct per device.
85 | A device's energy is fully consumed when that device's power is used. The energy level cannot grow above that device's requirement for one use of the power.
86 |
87 | You can charge the torpedo, the sonar, the silence mode and mines.
88 |
89 |
Surface
90 | The surface action will reset your path of visited cells, so that you can freely move to a cell that you have previously visited.
91 | But surfacing has major impacts: your opponent will know in which sector you are surfacing, and you will lose
1 hit point.
92 |
93 |
Torpedo
94 | The torpedo action requires an energy level of
3.
95 | When fully charged, the torpedo can be fired at an arbitrary water position within a water range of
4 cells.
96 | This allows the torpedo's path to contain corners and go around islands, but not through them.
97 | The following image illustrates the range of a torpedo:
98 |

99 |
100 |
Explosions
101 |
When a torpedo or a mine explodes on a water cell, any submarine present on that cell and its 8 neighbors suffers a loss of hit points. The damage is 2 hit points on the explosion cell itself and 1 hit point on its neighbor cells—this includes diagonals.
102 |

103 |
104 |
Explosions damage your opponent's submarine and yours indiscriminately. They have no effect on anything else (mines, islands).
105 |
106 |
Sonar
107 |
The sonar action requires an energy level of 4.
108 | When fully charged, it allows you to check whether the opponent's submarine is in a chosen sector.
109 |
The response will be provided to you in your next turn's input.
110 | Careful! Said response is in respect to the time you issued the order, so the opponent will have issued and performed orders of its own by the time you receive it.
111 |
112 |
Silence
113 | The silence action requires an energy level of
6.
114 | When fully charged, it allows you to move
0 to
4 cells in a chosen direction. Same as with the Move action, you may not visit an island or an already visited cell, be it as a final action destination or a waypoint.
115 | Your opponent will not know in which direction or how far you've moved. It
will know that you used the silence action, though.
116 |
117 |
120 |
Mine
121 | The mine action requires an energy level of
3.
122 | The mine can be placed on any cell next to you (north, east, south, west).
123 | You can't place two own mines on the same cell. However it's possible to place your own mine on an opponent mine or the opponent's submarine itself.
124 | Mines are only detonated by using the trigger action, not by a submarine moving onto them.
125 |
126 |
Trigger
127 | The trigger action will cause a mine to explode. You can only trigger your own mines.
128 | You can't trigger multiple mines in the same turn. You can't place a mine and trigger it during the same turn.
129 |
130 |
131 | Mines' explosions behave exactly as torpedoes'.
132 |
133 |
134 |
Orders' visibility
135 | The following table shows how different actions will be shown to your opponent:
136 |
137 | Your action | Shown to opponent | Comment |
138 |
139 | MOVE N TORPEDO |
140 | MOVE N |
141 | The opponent will not see which power is charged. |
142 |
143 |
144 | SURFACE |
145 | SURFACE 3 |
146 | The opponent will see which sector (in this case: 3) you surfaced in. |
147 |
148 |
149 | TORPEDO 3 5 |
150 | TORPEDO 3 5 |
151 | The opponent will see the same order you issued. |
152 |
153 |
154 | SONAR 4 |
155 | SONAR 4 |
156 | The opponent will see the same order you issued. |
157 |
158 |
159 | SILENCE N 4 |
160 | SILENCE |
161 | The opponent will see that you moved silently—but not where or how far. |
162 |
163 |
166 | MINE E |
167 | MINE |
168 | The opponent will see that you placed a mine—but not in which direction. |
169 |
170 |
173 | TRIGGER 3 5 |
174 | TRIGGER 3 5 |
175 | The opponent will see the same order you issued. |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
Victory Conditions
185 |
Have more hit points left than your enemy at the end of the game.
186 |
187 |
188 |
189 |
190 |
191 |
192 |
Loss Conditions
193 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | Expert Rules
206 |
207 |
208 |
211 |
212 |
213 |
214 |
215 |
216 |
217 | Game Input
218 |
219 |
220 |
221 |
Inputs of the first turn
222 |
Line 1:
223 |
width height myId, space-separated.
224 |
- width and height indicate the size of the grid in cells (15 15)
225 | - myId indicates your player ID (0 or 1). The player with ID 0 begins.
226 |
227 |
Next height lines: a string of
width chars representing the content of each cells of this row:
x for an island,
. for an empty cell.
228 |
229 |
230 |
231 |
232 |
Output for the first turn
233 |
2 space-separated integers x y which represent the coordinates of your starting position. The cell at the specified coordinates should be empty which means no island, but the opponent can choose the same cell!
234 |
235 |
236 |
237 |
238 |
Inputs for each turn
239 |
Line 1: 8 space-separated integers:
240 |
x y myLife oppLife torpedoCooldown sonarCooldown silenceCooldown mineCooldown
241 |
242 | - x and y represent your current position.
243 | - myLife and oppLife give the number of hit points you and your opponent have left
244 | - torpedoCooldown sonarCooldown silenceCooldown mineCooldown represent the number of charges each device still needs before it can be used.
245 |
246 |
247 |
Line 2: a string
sonarResult which gives you the result of your
SONAR action:
Y for yes,
N for no,
NA, if you didn't use the sonar on the previous turn.
248 |
Line 3: opponentOrders, a summary of your opponent's actions (separated by a pipe symbol
|) during its turn.
249 | Example:
MOVE N|TORPEDO 3 5
250 | This example indicates that your opponent moved to the north and then fired a torpedo to (3,5).
251 | On the starting player's first turn, this will be the string
NA, since the opponent has no previous orders at that point.
252 |
253 |
254 |
255 |
256 |
Output for each next turns
257 |
One or multiple commands separated by a pipe symbol
|.
258 | Example:
MOVE N TORPEDO | TORPEDO 3 5
259 | These commands move your submarine to the north and then fire a torpedo at (3,5).
260 |
261 | Here are the different available actions:
262 |
263 | - MOVE direction power
264 | - SURFACE
265 | - TORPEDO X Y
266 | - SONAR
267 | - SILENCE direction distance
268 | - MINE direction
271 | - TRIGGER X Y
274 | - MSG message
275 |
276 | Here are the different available powers to charge:
277 |
278 | - TORPEDO
279 | - SONAR
280 | - SILENCE
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | Constraints
289 |
290 |
291 | Response time first turn ≤ 1000 ms
292 |
Response time per turn ≤ 50 ms
293 |
A game ends after 300 turns per player
294 |
295 |
296 |
297 |
298 |
--------------------------------------------------------------------------------
/config/level4/statement_en.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | The Goal
9 |
10 |
11 | This is a two-player game based on the board game Captain Sonar.
12 | You pilot a submarine. You know that an enemy is present nearby because you are listening to its radio frequency communication.
13 | You don't know exactly where it is but you can hear all of its orders.
14 | You and your opponent start with 6 hit points. When a player's hit points reach 0, that player loses.
15 |
16 |
17 |
18 |
19 |
20 |
21 | Rules
22 |
23 |
24 |
Environment
25 |
26 | - The map is a square tiling: 15 cells wide and 15 cells high. The NW corner is (0,0); the SW corner is (0,14).
27 | - Each map cell is either open water or an island. Islands are obstacles. Submarines cannot move to or through islands. Nor can torpedoes or mines.
28 | - Submarines move on the open water cells of the map. Each player controls one submarine; both submarines move independently. They can share the same water cell without colliding.
29 | - The map is split in 9 sectors. Each sector encompasses a 5×5 block of 25 cells. You can find the sectors' numbering in the viewer; the top left sector is number 1 and the bottom right sector is number 9.
30 |
31 |
32 |
Game start
33 |
34 | At the beginning of the game, you'll receive a full 15×15 map that indicates the position of the islands.
35 | You will then decide and reply where you want your submarine's starting position to be.
36 |
37 |
38 |
Turns
39 |
This is a turn-based game: each player plays a turn one after the other. The whole game lasts 299 turns per player. The player with id 0 begins.
40 |
During your turn, thanks to your radio frequency analysis, you will receive an indication of what your opponent has done.
41 | For example, you can receive that it moved to the north. It's up to you to use this valuable information to detect where the opponent actually is.
42 |
43 |
44 | During your turn, you must perform at least one action.
45 |
46 |
47 |
Actions
48 |
49 | Actions are your turn's output. They are provided sequentially, on a single line. You can chain several of them using a pipe symbol |.
50 |
But you can use each type of action only once per turn. For example you can MOVE once per turn, and no more.
51 | If you fail to output a valid action, your action will be forced to SURFACE.
52 |
53 |
54 |
Move
55 |
A move action has two effects:
56 |
57 | - It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
58 | - It charges a special power of your choice, i.e. increases its energy level by 1.
59 |
60 |
When you move, you must respect the following rules:
61 |
- You cannot move through islands.
62 | - You cannot move to a cell you have visited before.
63 |
64 |
65 |
Powers
66 |
For each move action you take, you have to decide what special power to charge. The special powers are performed by different devices, who require a different amount of energy to be ready.
67 |
The energy levels are separate per device, not shared. The amounts of energy needed to activate a device are also distinct per device.
68 | A device's energy is fully consumed when that device's power is used. The energy level cannot grow above that device's requirement for one use of the power.
69 |
70 | You can charge the torpedo, the sonar, the silence mode and mines.
71 |
72 |
Surface
73 | The surface action will reset your path of visited cells, so that you can freely move to a cell that you have previously visited.
74 | But surfacing has major impacts: your opponent will know in which sector you are surfacing, and you will lose
1 hit point.
75 |
76 |
Torpedo
77 | The torpedo action requires an energy level of
3.
78 | When fully charged, the torpedo can be fired at an arbitrary water position within a water range of
4 cells.
79 | This allows the torpedo's path to contain corners and go around islands, but not through them.
80 | The following image illustrates the range of a torpedo:
81 |

82 |
83 |
Explosions
84 |
When a torpedo or a mine explodes on a water cell, any submarine present on that cell and its 8 neighbors suffers a loss of hit points. The damage is 2 hit points on the explosion cell itself and 1 hit point on its neighbor cells—this includes diagonals.
85 |

86 |
87 |
Explosions damage your opponent's submarine and yours indiscriminately. They have no effect on anything else (mines, islands).
88 |
89 |
Sonar
90 |
The sonar action requires an energy level of 4.
91 | When fully charged, it allows you to check whether the opponent's submarine is in a chosen sector.
92 |
The response will be provided to you in your next turn's input.
93 | Careful! Said response is in respect to the time you issued the order, so the opponent will have issued and performed orders of its own by the time you receive it.
94 |
95 |
Silence
96 | The silence action requires an energy level of
6.
97 | When fully charged, it allows you to move
0 to
4 cells in a chosen direction. Same as with the Move action, you may not visit an island or an already visited cell, be it as a final action destination or a waypoint.
98 | Your opponent will not know in which direction or how far you've moved. It
will know that you used the silence action, though.
99 |
100 |
Mine
101 | The mine action requires an energy level of
3.
102 | The mine can be placed on any cell next to you (north, east, south, west).
103 | You can't place two own mines on the same cell. However it's possible to place your own mine on an opponent mine or the opponent's submarine itself.
104 | Mines are only detonated by using the trigger action, not by a submarine moving onto them.
105 |
106 |
Trigger
107 | The trigger action will cause a mine to explode. You can only trigger your own mines.
108 | You can't trigger multiple mines in the same turn. You can't place a mine and trigger it during the same turn.
109 |
110 |
111 |
Orders' visibility
112 | The following table shows how different actions will be shown to your opponent:
113 |
114 | Your action | Shown to opponent | Comment |
115 |
116 | MOVE N TORPEDO |
117 | MOVE N |
118 | The opponent will not see which power is charged. |
119 |
120 |
121 | SURFACE |
122 | SURFACE 3 |
123 | The opponent will see which sector (in this case: 3) you surfaced in. |
124 |
125 |
126 | TORPEDO 3 5 |
127 | TORPEDO 3 5 |
128 | The opponent will see the same order you issued. |
129 |
130 |
131 | SONAR 4 |
132 | SONAR 4 |
133 | The opponent will see the same order you issued. |
134 |
135 |
136 | SILENCE N 4 |
137 | SILENCE |
138 | The opponent will see that you moved silently—but not where or how far. |
139 |
140 |
141 | MINE E |
142 | MINE |
143 | The opponent will see that you placed a mine—but not in which direction. |
144 |
145 |
146 | TRIGGER 3 5 |
147 | TRIGGER 3 5 |
148 | The opponent will see that you triggered the mine at (3,5). |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
Victory Conditions
158 |
Have more hit points left than your enemy at the end of the game.
159 |
160 |
161 |
162 |
163 |
164 |
165 |
Loss Conditions
166 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | Expert Rules
179 |
180 |
181 |
184 |
185 |
186 |
187 |
188 |
189 |
190 | Game Input
191 |
192 |
193 |
194 |
Inputs of the first turn
195 |
Line 1:
196 |
width height myId, space-separated.
197 |
- width and height indicate the size of the grid in cells (15 15)
198 | - myId indicates your player ID (0 or 1). The player with ID 0 begins.
199 |
200 |
Next height lines: a string of
width chars representing the content of each cells of this row:
x for an island,
. for an empty cell.
201 |
202 |
203 |
204 |
205 |
Output for the first turn
206 |
2 space-separated integers x y which represent the coordinates of your starting position. The cell at the specified coordinates should be empty which means no island, but the opponent can choose the same cell!
207 |
208 |
209 |
210 |
211 |
Inputs for each turn
212 |
Line 1: 8 space-separated integers:
213 |
x y myLife oppLife torpedoCooldown sonarCooldown silenceCooldown mineCooldown
214 |
215 | - x and y represent your current position.
216 | - myLife and oppLife give the number of hit points you and your opponent have left
217 | - torpedoCooldown sonarCooldown silenceCooldown mineCooldown represent the number of charges each device still needs before it can be used.
218 |
219 |
220 |
Line 2: a string
sonarResult which gives you the result of your
SONAR action:
Y for yes,
N for no,
NA, if you didn't use the sonar on the previous turn.
221 |
Line 3: opponentOrders, a summary of your opponent's actions (separated by a pipe symbol
|) during its turn.
222 | Example:
MOVE N|TORPEDO 3 5
223 | This example indicates that your opponent moved to the north and then fired a torpedo to (3,5).
224 | On the starting player's first turn, this will be the string
NA, since the opponent has no previous orders at that point.
225 |
226 |
227 |
228 |
229 |
Output for each next turns
230 |
One or multiple commands separated by a pipe symbol
|.
231 | Example:
MOVE N TORPEDO | TORPEDO 3 5
232 | These commands move your submarine to the north and then fire a torpedo at (3,5).
233 |
234 | Here are the different available actions:
235 |
236 | - MOVE direction power
237 | - SURFACE
238 | - TORPEDO X Y
239 | - SONAR
240 | - SILENCE direction distance
241 | - MINE direction
242 | - TRIGGER X Y
243 | - MSG message
244 |
245 | Here are the different available powers to charge:
246 |
247 | - TORPEDO
248 | - SONAR
249 | - SILENCE
250 | - MINE
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 | Constraints
259 |
260 |
261 | Response time first turn ≤ 1000 ms
262 |
Response time per turn ≤ 50 ms
263 |
A game ends after 300 turns per player
264 |
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------
/config/statement_en.html.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |

13 |
14 |
15 |
16 | This is a league-based challenge.
17 |
18 |
19 | Welcome to the Wood1 league!
20 |
21 |
22 | Welcome to the Bronze league!
23 |
24 |
25 |
26 |
27 | Wood leagues should be considered as a tutorial which lets players discover the different rules of the game.
28 | In Bronze league, all rules will be unlocked and the real challenge will begin.
29 |
30 |
31 | In Wood 1, you can now use SONAR and SILENCE
32 |
33 |
34 | In Bronze, you can now use MINES. There won't be any additional rules.
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | The Goal
45 |
46 |
47 | This is a two-player game based on the board game Captain Sonar.
48 | You pilot a submarine. You know that an enemy is present nearby because you are listening to its radio frequency communication.
49 | You don't know exactly where it is but you can hear all of its orders.
50 | You and your opponent start with 6 hit points. When a player's hit points reach 0, that player loses.
51 |
52 |
53 |
54 |
55 |
56 |
57 | Rules
58 |
59 |
60 |
Environment
61 |
62 | - The map is a square tiling: 15 cells wide and 15 cells high. The NW corner is (0,0); the SW corner is (0,14).
63 | - Each map cell is either open water or an island. Islands are obstacles. Submarines cannot move to or through islands. Nor can torpedoes or mines.
64 | - Submarines move on the open water cells of the map. Each player controls one submarine; both submarines move independently. They can share the same water cell without colliding.
65 | - The map is split in 9 sectors. Each sector encompasses a 5×5 block of 25 cells. You can find the sectors' numbering in the viewer; the top left sector is number 1 and the bottom right sector is number 9.
66 |
67 |
68 |
Game start
69 |
70 | At the beginning of the game, you'll receive a full 15×15 map that indicates the position of the islands.
71 | You will then decide and reply where you want your submarine's starting position to be.
72 |
73 |
74 |
Turns
75 |
This is a turn-based game: each player plays a turn one after the other. The whole game lasts 299 turns per player. The player with id 0 begins.
76 |
During your turn, thanks to your radio frequency analysis, you will receive an indication of what your opponent has done.
77 | For example, you can receive that it moved to the north. It's up to you to use this valuable information to detect where the opponent actually is.
78 |
79 |
80 | During your turn, you must perform at least one action.
81 |
82 |
83 |
Actions
84 |
85 | Actions are your turn's output. They are provided sequentially, on a single line. You can chain several of them using a pipe symbol |.
86 |
But you can use each type of action only once per turn. For example you can MOVE once per turn, and no more.
87 | If you fail to output a valid action, your action will be forced to SURFACE.
88 |
89 |
90 |
Move
91 |
A move action has two effects:
92 |
93 | - It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
94 | - It charges a special power of your choice, i.e. increases its energy level by 1.
95 |
96 |
When you move, you must respect the following rules:
97 |
- You cannot move through islands.
98 | - You cannot move to a cell you have visited before.
99 |
100 |
101 |
Powers
102 |
For each move action you take, you have to decide what special power to charge. The special powers are performed by different devices, who require a different amount of energy to be ready.
103 |
The energy levels are separate per device, not shared. The amounts of energy needed to activate a device are also distinct per device.
104 | A device's energy is fully consumed when that device's power is used. The energy level cannot grow above that device's requirement for one use of the power.
105 |
106 |
107 | In this league you only have access to the torpedo.
108 |
109 |
110 | In this league you can charge the torpedo, the sonar and the silence mode.
111 |
112 |
113 | You can charge the torpedo, the sonar, the silence mode and mines.
114 |
115 |
116 |
Surface
117 | The surface action will reset your path of visited cells, so that you can freely move to a cell that you have previously visited.
118 | But surfacing has major impacts: your opponent will know in which sector you are surfacing, and you will lose
1 hit point.
119 |
120 |
Torpedo
121 | The torpedo action requires an energy level of
3.
122 | When fully charged, the torpedo can be fired at an arbitrary water position within a water range of
4 cells.
123 | This allows the torpedo's path to contain corners and go around islands, but not through them.
124 | The following image illustrates the range of a torpedo:
125 |

126 |
127 |
Explosions
128 |
When a torpedo or a mine explodes on a water cell, any submarine present on that cell and its 8 neighbors suffers a loss of hit points. The damage is 2 hit points on the explosion cell itself and 1 hit point on its neighbor cells—this includes diagonals.
129 |

130 |
131 |
Explosions damage your opponent's submarine and yours indiscriminately. They have no effect on anything else (mines, islands).
132 |
133 |
134 |
137 |
Sonar
138 |
The sonar action requires an energy level of 4.
139 | When fully charged, it allows you to check whether the opponent's submarine is in a chosen sector.
140 |
The response will be provided to you in your next turn's input.
141 | Careful! Said response is in respect to the time you issued the order, so your opponent will have issued and performed orders of its own by the time you receive it.
142 |
143 |
Silence
144 | The silence action requires an energy level of
6.
145 | When fully charged, it allows you to move
0 to
4 cells in a chosen direction. Same as with the Move action, you may not visit an island or an already visited cell, be it as a final action destination or a waypoint.
146 | Your opponent will not know in which direction or how far you've moved. It
will know that you used the silence action, though.
147 |
148 |
149 |
150 |
Sonar
151 |
The sonar action requires an energy level of 4.
152 | When fully charged, it allows you to check whether the opponent's submarine is in a chosen sector.
153 |
The response will be provided to you in your next turn's input.
154 | Careful! Said response is in respect to the time you issued the order, so the opponent will have issued and performed orders of its own by the time you receive it.
155 |
156 |
Silence
157 | The silence action requires an energy level of
6.
158 | When fully charged, it allows you to move
0 to
4 cells in a chosen direction. Same as with the Move action, you may not visit an island or an already visited cell, be it as a final action destination or a waypoint.
159 | Your opponent will not know in which direction or how far you've moved. It
will know that you used the silence action, though.
160 |
161 |
162 |
163 |
166 |
Mine
167 | The mine action requires an energy level of
3.
168 | The mine can be placed on any cell next to you (north, east, south, west).
169 | You can't place two own mines on the same cell. However it's possible to place your own mine on an opponent mine or the opponent's submarine itself.
170 | Mines are only detonated by using the trigger action, not by a submarine moving onto them.
171 |
172 |
Trigger
173 | The trigger action will cause a mine to explode. You can only trigger your own mines.
174 | You can't trigger multiple mines in the same turn. You can't place a mine and trigger it during the same turn.
175 |
176 |
177 | Mines' explosions behave exactly as torpedoes'.
178 |
179 |
180 |
181 |
Mine
182 | The mine action requires an energy level of
3.
183 | The mine can be placed on any cell next to you (north, east, south, west).
184 | You can't place two own mines on the same cell. However it's possible to place your own mine on an opponent mine or the opponent's submarine itself.
185 | Mines are only detonated by using the trigger action, not by a submarine moving onto them.
186 |
187 |
Trigger
188 | The trigger action will cause a mine to explode. You can only trigger your own mines.
189 | You can't trigger multiple mines in the same turn. You can't place a mine and trigger it during the same turn.
190 |
191 |
192 |
193 |
Orders' visibility
194 | The following table shows how different actions will be shown to your opponent:
195 |
196 | Your action | Shown to opponent | Comment |
197 |
198 | MOVE N TORPEDO |
199 | MOVE N |
200 | The opponent will not see which power is charged. |
201 |
202 |
203 | SURFACE |
204 | SURFACE 3 |
205 | The opponent will see which sector (in this case: 3) you surfaced in. |
206 |
207 |
208 | TORPEDO 3 5 |
209 | TORPEDO 3 5 |
210 | The opponent will see the same order you issued. |
211 |
212 |
213 |
216 | SONAR 4 |
217 | SONAR 4 |
218 | The opponent will see the same order you issued. |
219 |
220 |
223 | SILENCE N 4 |
224 | SILENCE |
225 | The opponent will see that you moved silently—but not where or how far. |
226 |
227 |
228 |
229 |
230 | SONAR 4 |
231 | SONAR 4 |
232 | The opponent will see the same order you issued. |
233 |
234 |
235 | SILENCE N 4 |
236 | SILENCE |
237 | The opponent will see that you moved silently—but not where or how far. |
238 |
239 |
240 |
241 |
244 | MINE E |
245 | MINE |
246 | The opponent will see that you placed a mine—but not in which direction. |
247 |
248 |
251 | TRIGGER 3 5 |
252 | TRIGGER 3 5 |
253 | The opponent will see the same order you issued. |
254 |
255 |
256 |
257 |
258 | MINE E |
259 | MINE |
260 | The opponent will see that you placed a mine—but not in which direction. |
261 |
262 |
263 | TRIGGER 3 5 |
264 | TRIGGER 3 5 |
265 | The opponent will see that you triggered the mine at (3,5). |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
Victory Conditions
276 |
Have more hit points left than your enemy at the end of the game.
277 |
278 |
279 |
280 |
281 |
282 |
283 |
Loss Conditions
284 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | Expert Rules
297 |
298 |
299 |
302 |
303 |
304 |
305 |
306 |
307 |
308 | Game Input
309 |
310 |
311 |
312 |
Inputs of the first turn
313 |
Line 1:
314 |
width height myId, space-separated.
315 |
- width and height indicate the size of the grid in cells (15 15)
316 | - myId indicates your player ID (0 or 1). The player with ID 0 begins.
317 |
318 |
Next height lines: a string of
width chars representing the content of each cells of this row:
x for an island,
. for an empty cell.
319 |
320 |
321 |
322 |
323 |
Output for the first turn
324 |
2 space-separated integers x y which represent the coordinates of your starting position. The cell at the specified coordinates should be empty which means no island, but the opponent can choose the same cell!
325 |
326 |
327 |
328 |
329 |
Inputs for each turn
330 |
Line 1: 8 space-separated integers:
331 |
x y myLife oppLife torpedoCooldown sonarCooldown silenceCooldown mineCooldown
332 |
333 | - x and y represent your current position.
334 | - myLife and oppLife give the number of hit points you and your opponent have left
335 | - torpedoCooldown sonarCooldown silenceCooldown mineCooldown represent the number of charges each device still needs before it can be used.
336 |
337 | Devices unavailable in your league will have a cooldown value of -1.
338 |
339 |
340 |
341 |
Line 2: a string
sonarResult which gives you the result of your
SONAR action:
Y for yes,
N for no,
NA, if you didn't use the sonar on the previous turn.
342 |
Line 3: opponentOrders, a summary of your opponent's actions (separated by a pipe symbol
|) during its turn.
343 | Example:
MOVE N|TORPEDO 3 5
344 | This example indicates that your opponent moved to the north and then fired a torpedo to (3,5).
345 | On the starting player's first turn, this will be the string
NA, since the opponent has no previous orders at that point.
346 |
347 |
348 |
349 |
350 |
Output for each next turns
351 |
One or multiple commands separated by a pipe symbol
|.
352 | Example:
MOVE N TORPEDO | TORPEDO 3 5
353 | These commands move your submarine to the north and then fire a torpedo at (3,5).
354 |
355 | Here are the different available actions:
356 |
357 | - MOVE direction power
358 | - SURFACE
359 | - TORPEDO X Y
360 |
361 | - SONAR
364 | - SILENCE direction distance
367 |
368 |
369 | - SONAR
370 | - SILENCE direction distance
371 |
372 |
373 | - MINE direction
376 | - TRIGGER X Y
379 |
380 |
381 | - MINE direction
382 | - TRIGGER X Y
383 |
384 | - MSG message
385 |
386 | Here are the different available powers to charge:
387 |
388 | - TORPEDO
389 |
390 | - SONAR
391 | - SILENCE
392 |
393 |
394 | - MINE
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 | Constraints
404 |
405 |
406 | Response time first turn ≤ 1000 ms
407 |
Response time per turn ≤ 50 ms
408 |
A game ends after 300 turns per player
409 |
410 |
411 |
412 |
413 |
--------------------------------------------------------------------------------
/config/stub.txt:
--------------------------------------------------------------------------------
1 | read width:int height:int myId:int
2 | loop height read line:string(width)
3 |
4 | write 7 7
5 |
6 | gameloop
7 | read x:int y:int myLife:int oppLife:int torpedoCooldown:int sonarCooldown:int silenceCooldown:int mineCooldown:int
8 | read sonarResult:word(3)
9 | read opponentOrders:string(200)
10 |
11 | write MOVE N TORPEDO
--------------------------------------------------------------------------------
/explosion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/explosion.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromecance.codingame
6 | captainsonar
7 | 1.0-SNAPSHOT
8 |
9 |
10 | 3.13.0
11 | 1.8
12 | 1.8
13 |
14 |
15 |
16 |
17 | com.codingame.gameengine
18 | core
19 | ${gamengine.version}
20 |
21 |
22 |
23 | com.codingame.gameengine
24 | module-entities
25 | ${gamengine.version}
26 |
27 |
28 |
29 | com.codingame.gameengine
30 | runner
31 | ${gamengine.version}
32 |
33 |
34 |
35 | com.codingame.gameengine
36 | module-tooltip
37 | ${gamengine.version}
38 |
39 |
40 |
41 | com.codingame.gameengine
42 | module-endscreen
43 | ${gamengine.version}
44 |
45 |
46 |
47 |
48 |
49 |
50 | maven-assembly-plugin
51 | 2.3
52 |
53 | src/main/resources/assembly.xml
54 |
55 |
56 |
57 | make-assembly
58 | package
59 |
60 | single
61 |
62 |
63 |
64 |
65 | Main
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/GameException.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game;
2 |
3 | public class GameException extends Exception {
4 |
5 | public GameException(String message) {
6 | super(message);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/Player.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game;
2 | import com.codingame.gameengine.core.AbstractMultiplayerPlayer;
3 |
4 | public class Player extends AbstractMultiplayerPlayer {
5 | @Override
6 | public int getExpectedOutputLines() {
7 | return 1;
8 | }
9 | }
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/Referee.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Random;
5 |
6 | import com.codingame.game.model.Game;
7 | import com.codingame.game.model.IPlayerManager;
8 | import com.codingame.gameengine.core.AbstractPlayer.TimeoutException;
9 | import com.codingame.gameengine.core.AbstractReferee;
10 | import com.codingame.gameengine.core.MultiplayerGameManager;
11 | import com.codingame.gameengine.module.endscreen.EndScreenModule;
12 | import com.codingame.gameengine.module.entities.GraphicEntityModule;
13 | import com.codingame.gameengine.module.tooltip.TooltipModule;
14 | import com.google.inject.Inject;
15 |
16 | public class Referee extends AbstractReferee implements IPlayerManager {
17 | @Inject
18 | private MultiplayerGameManager gameManager;
19 | @Inject
20 | private GraphicEntityModule entityManager;
21 | @Inject
22 | private EndScreenModule endScreenModule;
23 | @Inject
24 | private TooltipModule tooltipModule;
25 | private ViewController viewController;
26 | private Random random;
27 | private Game game;
28 |
29 |
30 | @Override
31 | public void init() {
32 | gameManager.setTurnMaxTime(50);
33 | gameManager.setMaxTurns(1000);
34 |
35 | System.err.println(Long.parseLong(gameManager.getGameParameters().get("seed").toString()));
36 | long seed = gameManager.getSeed();
37 |
38 | random = new Random(seed);
39 | entityManager.createSprite().setImage("background.jpg").setAnchor(0);
40 |
41 | int diff = 87;
42 | addFrame(1920/2-1080/2+diff, diff, 1080-diff*2, 1080-diff*2, 99);
43 | game = new Game(this, random);
44 | viewController = new ViewController(this, game, entityManager, new ArrayList<>(gameManager.getPlayers()), tooltipModule);
45 | viewController.preInitialize();
46 | }
47 |
48 | public int getLeague() {
49 | return gameManager.getLeagueLevel();
50 | }
51 |
52 | private String getLifeString(int amount){
53 | if(Math.abs(amount)==1) return " life";
54 | return " lives";
55 | }
56 | @Override
57 | public void onEnd() {
58 | int[] scores = gameManager.getPlayers().stream().mapToInt(p -> p.getScore()).toArray();
59 | String[] texts = new String[]{ gameManager.getPlayer(0).getScore()+getLifeString(gameManager.getPlayer(0).getScore()), gameManager.getPlayer(1).getScore()+getLifeString(gameManager.getPlayer(1).getScore())};
60 | endScreenModule.setScores(scores, texts);
61 | endScreenModule.setTitleRankingsSprite("logo.png");
62 | }
63 |
64 | private void firstTurn() {
65 | game.initialize(getLeague());
66 | viewController.initialize();
67 | }
68 |
69 | private void classicTurn() {
70 | game.onRound();
71 | viewController.onRound();
72 | }
73 |
74 | @Override
75 | public void gameTurn(int turn) {
76 | try{
77 | if(turn==1){
78 | gameManager.setFrameDuration(10);
79 | firstTurn();
80 | }else{
81 | gameManager.setFrameDuration(500);
82 | classicTurn();
83 | }
84 | }
85 | catch (Exception e){
86 | if(game.activePlayer != null){
87 | disablePlayer(game.activePlayer.index, e.getMessage());
88 | gameManager.addToGameSummary(e.getMessage());
89 | gameManager.addTooltip(gameManager.getPlayers().get(game.activePlayer.index), e.getMessage());
90 | }
91 | }
92 | }
93 |
94 | @Override
95 | public String getOutputs(int playerId) throws TimeoutException {
96 | return gameManager.getPlayer(playerId).getOutputs().get(0);
97 | }
98 |
99 | @Override
100 | public void sendData(String[] lines, int playerId) {
101 | for(String line : lines){
102 | gameManager.getPlayer(playerId).sendInputLine(line);
103 | }
104 | }
105 |
106 | @Override
107 | public void execute(int playerId) {
108 | gameManager.getPlayer(playerId).execute();
109 | }
110 |
111 | @Override
112 | public void updateScore(int playerId, int score) {
113 | gameManager.getPlayer(playerId).setScore(score);
114 | }
115 |
116 | @Override
117 | public void disablePlayer(int playerId, String reason) {
118 | gameManager.getPlayer(playerId).deactivate(reason);
119 | gameManager.getPlayer(playerId).setScore(-1);
120 | gameManager.endGame();
121 | }
122 |
123 | @Override
124 | public void endGame() {
125 | gameManager.endGame();
126 | }
127 |
128 | @Override
129 | public void addTooltip(int playerId, String message) {
130 | gameManager.addTooltip(gameManager.getPlayer(playerId), message);
131 | }
132 |
133 | @Override
134 | public void addGameSummary(int playerId, String message) {
135 | gameManager.addToGameSummary(message);
136 | }
137 |
138 | private void addFrame(int x, int y, int width, int height, int z){
139 | entityManager.createRectangle()
140 | .setLineColor(0xefefef)
141 | .setLineWidth(10)
142 | .setZIndex(z)
143 | .setX(x)
144 | .setY(y)
145 | .setWidth(width)
146 | .setHeight(height)
147 | .setFillAlpha(0);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/ViewController.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game;
2 |
3 | import com.codingame.game.model.Game;
4 | import com.codingame.game.model.GridModel;
5 | import com.codingame.game.model.Mine;
6 | import com.codingame.game.model.PlayerModel;
7 | import com.codingame.game.model.commands.*;
8 | import com.codingame.gameengine.module.entities.*;
9 | import com.codingame.gameengine.module.entities.Rectangle;
10 | import com.codingame.gameengine.module.tooltip.TooltipModule;
11 |
12 | import java.awt.*;
13 | import java.util.ArrayList;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | public class ViewController {
18 | private static final int CELL_WIDTH = 60;
19 | public static final int GRID_SIZE = 15;
20 | private static final int GRID_ORIGIN_Y = (int) Math.round(1080 / 2 - CELL_WIDTH * GRID_SIZE / 2);
21 | private static final int GRID_ORIGIN_X = (int) Math.round(1920 / 2 - CELL_WIDTH * GRID_SIZE / 2);
22 |
23 | private Referee referee;
24 | private Game game;
25 | private GraphicEntityModule entityManager;
26 | private ArrayList players;
27 | private TooltipModule tooltipModule;
28 | private ArrayList parts = new ArrayList<>();
29 |
30 | public ViewController(Referee referee, Game game, GraphicEntityModule entityManager, ArrayList players, TooltipModule tooltipModule){
31 | this.referee = referee;
32 | this.game = game;
33 | this.entityManager = entityManager;
34 | this.players = players;
35 | this.tooltipModule = tooltipModule;
36 | }
37 |
38 | public void preInitialize(){
39 | setupMap();
40 | parts.add(new ActionMessagePart(game));
41 | parts.add(new ExplosionPart());
42 | setupPlayer(game.players[0]);
43 | setupPlayer(game.players[1]);
44 | }
45 |
46 | public void initialize(){
47 | parts.add(new SubmarinePart(game.players[0]));
48 | parts.add(new SubmarinePart(game.players[1]));
49 | parts.add(new PathMapPart(game.players[0]));
50 | parts.add(new PathMapPart(game.players[1]));
51 | for(int i = 0; i < game.players[0].powers.size(); i++){
52 | parts.add(new PowerPart(game.players[0], players.get(0), i));
53 | parts.add(new PowerPart(game.players[1], players.get(1), i));
54 | }
55 | }
56 |
57 | public void onRound(){
58 | for(ViewPart part : parts){
59 | part.update();
60 | }
61 | }
62 |
63 | private int convertX(double unit) {
64 | return (int) (GRID_ORIGIN_X + unit * CELL_WIDTH);
65 | }
66 |
67 | private int convertY(double unit) {
68 | return (int) (GRID_ORIGIN_Y + unit * CELL_WIDTH);
69 | }
70 |
71 | private void setupMap(){
72 | boolean[][] grid = game.gridModel.grid;
73 | Map tiles = game.gridModel.tiles;
74 | for (Map.Entry entry : tiles.entrySet()) {
75 | Sprite sprite = entityManager.createSprite().setImage(entry.getValue())
76 | .setBaseWidth(60)
77 | .setBaseHeight(60)
78 | .setX(convertX(entry.getKey().x))
79 | .setY(convertY(entry.getKey().y));
80 | entityManager.commitEntityState(0, sprite);
81 | tooltipModule.setTooltipText(sprite, "Island\nx = " + (int) entry.getKey().getX() + "\ny = " + (int) entry.getKey().getY());
82 | }
83 |
84 | // set tooltips for water tiles
85 | for (int x = 0; x < GRID_SIZE; x++) {
86 | for (int y = 0; y < GRID_SIZE; y++) {
87 | if (!grid[x][y]) {
88 | Rectangle rectangle = entityManager.createRectangle().setX(convertX(x)).setY(convertY(y)).setWidth(CELL_WIDTH).setHeight(CELL_WIDTH).setAlpha(0);
89 | tooltipModule.setTooltipText(rectangle, "Water\nx = " + x + "\ny = " + y);
90 | }
91 | }
92 | }
93 | }
94 |
95 | public Circle createCircle(Point point, int color) {
96 | return entityManager.createCircle().setX(convertX(point.x) + CELL_WIDTH / 2)
97 | .setY(convertY(point.y) + CELL_WIDTH / 2).setRadius(CELL_WIDTH / 3).setFillColor(color)
98 | .setLineColor(color);
99 | }
100 |
101 | private void setupPlayer(PlayerModel playerModel){
102 | Player player = players.get(playerModel.index);
103 | parts.add(new MessagePart(playerModel));
104 | parts.add(new LifePart(playerModel, player));
105 | parts.add(new MinePart(playerModel));
106 | createNickname(player);
107 | createPlayerFrame(player);
108 |
109 | }
110 |
111 | private void createPlayerFrame(Player player){
112 | Sprite sprite = entityManager.createSprite()
113 | .setImage(player.getAvatarToken()).setAnchorX(0.5).setBaseHeight(200).setBaseWidth(200).setY(50).setX(1080/4);
114 |
115 | addFrame(1080/4-5-100, 45, 205, 205, 1, player);
116 |
117 | if (player.getIndex() == 1) {
118 | sprite.setX(1920-sprite.getX());
119 | }
120 | entityManager.commitEntityState(0, sprite);
121 | }
122 |
123 | private void addFrame(int x, int y, int width, int height, int z, Player player){
124 | Rectangle rect = entityManager.createRectangle()
125 | .setLineColor(player.getColorToken())
126 | .setLineWidth(10)
127 | .setZIndex(z)
128 | .setX(x)
129 | .setY(y)
130 | .setWidth(width)
131 | .setHeight(height)
132 | .setFillAlpha(0);
133 |
134 | if (player.getIndex() == 1) {
135 | rect.setX(1920-rect.getX()-rect.getWidth());
136 | }
137 |
138 | entityManager.commitEntityState(0, rect);
139 | }
140 |
141 | private void createNickname(Player player) {
142 | Text nickname = entityManager.createText(player.getNicknameToken())
143 | .setFontFamily("Arial")
144 | .setX(1080/4)
145 | .setY(270)
146 | .setZIndex(20)
147 | .setAnchorX(0.5)
148 | .setMaxWidth(400)
149 | .setFontSize(60)
150 | .setFillColor(player.getColorToken());
151 |
152 | if (player.getIndex() == 1) {
153 | nickname.setX(1920-nickname.getX());
154 | }
155 | entityManager.commitEntityState(0, nickname);
156 | }
157 |
158 | private void addExplosion(Point position, PlayerModel playerModel){
159 | Player player = players.get(playerModel.index);
160 | String file = "explosion";
161 | int animation = 5;
162 | Rectangle rect = entityManager.createRectangle()
163 | .setWidth(CELL_WIDTH*3)
164 | .setHeight(CELL_WIDTH*3)
165 | .setX(convertX(position.x)-CELL_WIDTH)
166 | .setY(convertY(position.y)-CELL_WIDTH)
167 | .setAlpha(0)
168 | .setFillColor(player.getColorToken())
169 | .setLineAlpha(0)
170 | ;
171 |
172 | entityManager.commitEntityState(0, rect);
173 | rect.setAlpha(0.3, Curve.ELASTIC);
174 | entityManager.commitEntityState(0.2, rect);
175 | rect.setAlpha(0, Curve.ELASTIC);
176 |
177 | Sprite entity = entityManager.createSprite().setImage(file + "/0.png")
178 | .setX(convertX(position.x) + CELL_WIDTH / 2).setY(convertY(position.y) + CELL_WIDTH / 2).setScale(1.5)
179 | .setAnchor(0.5);
180 |
181 | for (int i = 0; i < animation; i++) {
182 | entity.setImage(file + "/" + i + ".png");
183 | entityManager.commitEntityState(i / (animation - 1.0), entity);
184 | }
185 |
186 | entity.setScale(0.5);
187 | }
188 |
189 | public Line createLine(Point p1, Point p2, int color) {
190 | return entityManager.createLine().setX(convertX(p1.x) + CELL_WIDTH / 2).setY(convertY(p1.y) + CELL_WIDTH / 2)
191 | .setX2(convertX(p2.x) + CELL_WIDTH / 2).setY2(convertY(p2.y) + CELL_WIDTH / 2).setLineColor(color)
192 | .setLineWidth(3);
193 | }
194 |
195 |
196 | public abstract class ViewPart {
197 | public abstract void update();
198 | }
199 |
200 | public class ExplosionPart extends ViewPart{
201 | @Override
202 | public void update() {
203 | if(game.activePlayer != null && game.activePlayer.lastCommand != null && game.activePlayer.lastCommand.activator.equals(TorpedoPower.NAME)){
204 | String[] coords = game.activePlayer.lastCommand.command.split(" ");
205 | int x = Integer.parseInt(coords[1]);
206 | int y = Integer.parseInt(coords[2]);
207 | explode(new Point(x, y), game.activePlayer);
208 | }
209 | }
210 |
211 | private void explode(Point point, PlayerModel playerModel){
212 | Line line = createLine(playerModel.position, point, players.get(playerModel.index).getColorToken());
213 | entityManager.commitEntityState(0.2, line);
214 | line.setAlpha(0);
215 | entityManager.commitEntityState(1, line);
216 | addExplosion(point, playerModel);
217 | }
218 | }
219 |
220 | public class MinePart extends ViewPart{
221 | private PlayerModel playerModel;
222 | private Map mines = new HashMap<>();
223 |
224 | public MinePart(PlayerModel playerModel){
225 | this.playerModel = playerModel;
226 | }
227 |
228 | @Override
229 | public void update() {
230 | if(game.activePlayer != playerModel) return;
231 | for(Mine mine : playerModel.mines){
232 | if(!mine.isActive && !mines.containsKey(mine)){
233 | addMine(mine);
234 | }else if(mine.isBlown && mines.containsKey(mine)){
235 | mines.get(mine).setAlpha(0);
236 | tooltipModule.setTooltipText(mines.get(mine), "");
237 | entityManager.commitEntityState(0.4, mines.get(mine));
238 | mines.remove(mine);
239 | addExplosion(mine.point, playerModel);
240 | }
241 | }
242 | }
243 |
244 | private void addMine(Mine mine){
245 | SpriteAnimation mineAnimation = entityManager.createSpriteAnimation()
246 | .setImages(new String[]{ "mine0.png", "mine1.png", "mine0.png", "mine2.png"})
247 | .setZIndex(5)
248 | .setAnchor(0.5)
249 | .setTint(players.get(playerModel.index).getColorToken())
250 | .setX(convertX(playerModel.position.x) + CELL_WIDTH / 2 )
251 | .setY(convertY(playerModel.position.y) + CELL_WIDTH / 2)
252 | .setDuration(1000)
253 | .setLoop(true);
254 |
255 | mines.put(mine, mineAnimation);
256 | tooltipModule.setTooltipText(mineAnimation, "Mine\nOwner = " + playerModel.index);
257 | entityManager.commitEntityState(0, mineAnimation);
258 | mineAnimation.setX(convertX(mine.point.x)+ CELL_WIDTH / 2 - 10 + playerModel.index*20)
259 | .setY(convertY(mine.point.y)+ CELL_WIDTH / 2 - 10 + playerModel.index*20)
260 | .setZIndex(5);
261 | mineAnimation.setPlaying(true);
262 | }
263 | }
264 |
265 | public class PathMapPart extends ViewPart{
266 | private PlayerModel playerModel;
267 | private Entity>[][] grid = new Entity>[GridModel.GRID_SIZE][GridModel.GRID_SIZE];
268 |
269 | public PathMapPart(PlayerModel playerModel)
270 | {
271 | this.playerModel = playerModel;
272 | grid[playerModel.position.x][playerModel.position.y] = fillCell(playerModel.position, players.get(playerModel.index));
273 | }
274 |
275 | @Override
276 | public void update() {
277 | if(game.activePlayer != playerModel) return;
278 | for (int i = 0; i < grid.length; i++) {
279 | for (int j = 0; j < grid.length; j++) {
280 | Entity> path = grid[i][j];
281 | if(path == null && playerModel.grid[i][j]){
282 | grid[i][j] = fillCell(new Point(i, j), players.get(playerModel.index));
283 | continue;
284 | }
285 | else if(path != null && playerModel.grid[i][j]){
286 | continue;
287 | }
288 |
289 | if (path != null && !playerModel.grid[i][j]) {
290 | path.setAlpha(0);
291 | }
292 |
293 | grid[i][j] = null;
294 | }
295 | }
296 | }
297 |
298 | public Entity fillCell(Point p, Player player) {
299 | return entityManager.createRectangle().setX(convertX(p.x)).setY(convertY(p.y)).setWidth(CELL_WIDTH).setHeight(CELL_WIDTH)
300 | .setAlpha(0.5).setFillColor(player.getColorToken()).setLineAlpha(0).setZIndex(player.getIndex()*-1+1);
301 | }
302 | }
303 |
304 | public class SubmarinePart extends ViewPart{
305 | private PlayerModel playerModel;
306 | private Group group;
307 | private Sprite sub;
308 |
309 | public SubmarinePart(PlayerModel playerModel){
310 | this.playerModel = playerModel;
311 | sub = entityManager.createSprite().setImage("sub.png").setTint(players.get(playerModel.index).getColorToken()).setScale(1).setScaleY(1.5).setAnchor(0.5);
312 | group = entityManager.createGroup(sub).setZIndex(100)
313 | .setX(convertX(playerModel.position.x) + CELL_WIDTH / 2)
314 | .setY(convertY(playerModel.position.y) + CELL_WIDTH / 2);
315 | tooltipModule.setTooltipText(sub, "Player " + playerModel.index);
316 | entityManager.commitEntityState(0, group, sub);
317 | }
318 |
319 | @Override
320 | public void update() {
321 | if(game.activePlayer != playerModel) return;
322 | Sprite image = sub;
323 | handleSonar();
324 | handleSurface();
325 |
326 | int newX = convertX(playerModel.position.x) + CELL_WIDTH / 2;
327 | int newY = convertY(playerModel.position.y) + CELL_WIDTH / 2;
328 |
329 | double rot = image.getRotation();
330 | if (newX - group.getX() < 0) {
331 | image.setRotation(Math.PI);
332 | } else if (newX - group.getX() > 0) {
333 | image.setRotation(0);
334 | } else if (newY - group.getY() < 0) {
335 | image.setRotation(Math.PI/2*3);
336 | } else if (newY - group.getY() > 0) {
337 | image.setRotation(Math.PI/2);
338 | }else{
339 | handleSilence(); // 0 dist
340 | return;
341 | }
342 |
343 | if(Math.abs(rot-image.getRotation()) > 0.0001){
344 | entityManager.commitEntityState(0, image);
345 | }
346 |
347 | handleSilence();
348 |
349 | group.setX(newX).setY(newY);
350 | }
351 |
352 | private void handleSilence(){
353 | if(game.activePlayer == playerModel && playerModel.lastCommand != null && playerModel.lastCommand.activator.equals(SilencePower.NAME)){
354 | sub.setAlpha(0.7, Curve.LINEAR);
355 | entityManager.commitEntityState(0.5, sub);
356 | sub.setAlpha(1.0, Curve.LINEAR);
357 | }
358 | }
359 | private void handleSonar(){
360 | if(game.activePlayer == playerModel && playerModel.lastCommand != null && playerModel.lastCommand.activator.equals(SonarPower.NAME)){
361 | int grid = Integer.parseInt(playerModel.lastCommand.command.split(" ")[1]);
362 | grid--;
363 | int x = grid % 3 * 5 + 2;
364 | int y = grid / 3 * 5 + 2;
365 | Group sonar = entityManager.createGroup()
366 | .setX(convertX(x) + CELL_WIDTH / 2)
367 | .setY(convertY(y) + CELL_WIDTH / 2)
368 | .setZIndex(1000);
369 |
370 | Circle c1;
371 | sonar.add(c1 = entityManager.createCircle()
372 | .setFillAlpha(0)
373 | .setLineAlpha(1)
374 | .setRadius(CELL_WIDTH*3)
375 | .setLineColor(players.get(playerModel.index).getColorToken())
376 | .setLineWidth(6));
377 |
378 | Circle c2;
379 | sonar.add(c2 = entityManager.createCircle()
380 | .setFillAlpha(0)
381 | .setLineAlpha(1)
382 | .setRadius(CELL_WIDTH*2)
383 | .setLineColor(players.get(playerModel.index).getColorToken())
384 | .setLineWidth(6));
385 |
386 | c2.setScale(0);
387 | c1.setScale(0);
388 | entityManager.commitEntityState(0, c1, c2, sonar);
389 | c2.setScale(1, Curve.EASE_OUT);
390 | c1.setScale(1, Curve.EASE_OUT);
391 | entityManager.commitEntityState(0.3, c1, c2);
392 | c2.setScale(0.7, Curve.EASE_IN);
393 | c1.setScale(0.7, Curve.EASE_IN);
394 | entityManager.commitEntityState(0.6, c1, c2);
395 | c2.setScale(1.3, Curve.EASE_OUT);
396 | c1.setScale(1.3, Curve.EASE_OUT);
397 | entityManager.commitEntityState(0.8, c1, c2, sonar);
398 | //sonar.setScale(0, Curve.EASE_IN_AND_OUT);
399 | sonar.setAlpha(0, Curve.ELASTIC);
400 | c2.setScale(0, Curve.EASE_IN_AND_OUT);
401 | c1.setScale(0, Curve.EASE_IN_AND_OUT);
402 | }
403 | }
404 |
405 | private void handleSurface(){
406 | Player player = players.get(playerModel.index);
407 | if(game.activePlayer == playerModel && playerModel.lastCommand != null && playerModel.lastCommand.command.equals(SurfaceCommand.NAME)){
408 | Circle circle = createCircle(playerModel.position, player.getColorToken());
409 | circle.setFillAlpha(0).setScale(0);
410 |
411 | entityManager.commitEntityState(0, circle);
412 | circle.setFillAlpha(0.5).setScale(1, Curve.ELASTIC);
413 | entityManager.commitEntityState(0.2, circle);
414 |
415 | circle.setScale(10);
416 | circle.setAlpha(0);
417 | entityManager.commitEntityState(1, circle);
418 |
419 | group.setScale(2);
420 | entityManager.commitEntityState(0.5, group);
421 | group.setScale(1);
422 | entityManager.commitEntityState(1, group);
423 | }
424 | }
425 | }
426 |
427 | public class LifePart extends ViewPart{
428 | private int lastLife;
429 | private PlayerModel playerModel;
430 | private ArrayList graphicLife = new ArrayList();
431 |
432 | public LifePart(PlayerModel playerModel, Player player){
433 | lastLife = playerModel.life;
434 | this.playerModel = playerModel;
435 | for (int i = 0; i < playerModel.life; i++) {
436 |
437 | int x = 1080/4+1080/6-10;
438 | int y = 35 + (playerModel.life-i-1) * 40;
439 |
440 | if (playerModel.index == 1) {
441 | x = 1920 - x;
442 | }
443 |
444 | graphicLife.add(entityManager.createCircle().setX(x).setY(y).setFillColor(player.getColorToken()).setRadius(12)
445 | .setAlpha(0.8).setLineColor(0x000000)
446 | .setLineWidth(4));
447 | entityManager.commitEntityState(0, graphicLife.get(graphicLife.size()-1));
448 | }
449 | }
450 |
451 | @Override
452 | public void update() {
453 | for(int i = lastLife-1; i >= Math.max(0, playerModel.life); i--){
454 | graphicLife.get(i).setFillColor(0x555555).setScale(0.5);
455 | }
456 |
457 | lastLife = playerModel.life;
458 | }
459 | }
460 |
461 | public class PowerPart extends ViewPart{
462 | private int charges;
463 | private PlayerModel playerModel;
464 | private Player player;
465 | private IPower power;
466 | private ArrayList graphicCharges = new ArrayList();
467 |
468 | public PowerPart(PlayerModel playerModel, Player player, int index){
469 | this.playerModel = playerModel;
470 | this.player = player;
471 | power = playerModel.powers.get(index);
472 |
473 | int y = 550 + 120 * power.getPowerIndex();
474 |
475 | entityManager.commitEntityState(0, entityManager
476 | .createText(power.getName())
477 | .setFontFamily("Arial")
478 | .setX(player.getIndex() == 1 ? 30 : 1890)
479 | .setY(y-50)
480 | .setAnchorX(player.getIndex() == 1? 0 : 1)
481 | .setFontSize(30)
482 | .setAnchorY(0.5)
483 | .setZIndex(5)
484 | .setFontWeight(Text.FontWeight.BOLD)
485 | .setFillColor(0xefefef));
486 |
487 | for (int i = 0; i < power.getMaxValue(); i++) {
488 |
489 | int x = 50 + 55 * i;
490 |
491 | if (player.getIndex() == 1) {
492 | x = 1920 - (50 + 55 * i);
493 | }
494 |
495 | graphicCharges.add(entityManager.createCircle()
496 | .setX(x)
497 | .setY(y)
498 | .setFillColor(0x555555)
499 | .setAlpha(0.8)
500 | .setRadius(20)
501 | .setScale(0.5)
502 | .setLineWidth(5)
503 | .setLineColor(0x000000));
504 |
505 | entityManager.commitEntityState(0, graphicCharges.get(graphicCharges.size()-1));
506 | }
507 | }
508 |
509 | @Override
510 | public void update() {
511 | if(game.activePlayer != playerModel) return;
512 | if(charges < power.getCharges()){
513 | for (int i = charges; i < power.getCharges(); i++) {
514 | graphicCharges.get(i).setScale(1.0, Curve.ELASTIC).setFillColor(player.getColorToken());
515 | }
516 | }else if(charges > power.getCharges()){
517 | for (int i = charges-1; i >= power.getCharges(); i--) {
518 | graphicCharges.get(i).setScale(0.5, Curve.ELASTIC).setFillColor(0x555555);
519 | }
520 | }
521 |
522 | charges = power.getCharges();
523 | }
524 | }
525 |
526 | public class MessagePart extends ViewPart{
527 | private PlayerModel playerModel;
528 | private Text messageText;
529 |
530 | public MessagePart(PlayerModel playerModel){
531 | this.playerModel = playerModel;
532 | createMessageText();
533 | }
534 |
535 | private void createMessageText(){
536 | messageText = entityManager.createText("")
537 | .setFontFamily("Arial")
538 | .setFillColor(0xffffff)
539 | .setX(playerModel.index==0?30:1890)
540 | .setMaxWidth(400)
541 | .setY(370)
542 | .setFontSize(30)
543 | .setZIndex(100);
544 |
545 | if (playerModel.index == 1) {
546 | messageText.setAnchorX(1);
547 | }
548 | }
549 | @Override
550 | public void update() {
551 | if(!messageText.getText().equals(playerModel.message)){
552 | messageText.setText(playerModel.message);
553 | entityManager.commitEntityState(0, messageText);
554 | }
555 | }
556 | }
557 |
558 | public class ActionMessagePart extends ViewPart{
559 | private Text actionText;
560 | private Game game;
561 |
562 | public ActionMessagePart(Game game){
563 | this.game = game;
564 |
565 | actionText = entityManager.createText("")
566 | .setX(1920 / 2)
567 | .setAnchor(0.5)
568 | .setStrokeColor(0x000000)
569 | .setStrokeThickness(2)
570 | .setY(1040)
571 | .setFontFamily("Arial")
572 | .setFontSize(40);
573 | }
574 | @Override
575 | public void update() {
576 | actionText.setText(game.activePlayer.actionMessage).setFillColor(players.get(game.activePlayer.index).getColorToken());
577 |
578 | entityManager.commitEntityState(0, actionText);
579 | }
580 | }
581 | }
582 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/BFS.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import java.awt.Point;
4 | import java.util.LinkedList;
5 | import java.util.Queue;
6 |
7 | public class BFS {
8 |
9 | public static class Node {
10 | int x;
11 | int y;
12 | Node parent;
13 |
14 | public Node(int x, int y, Node parent) {
15 | this.x = x;
16 | this.y = y;
17 | this.parent = parent;
18 | }
19 |
20 | public Node getParent() {
21 | return this.parent;
22 | }
23 |
24 | public Point getNextPoint() {
25 | Point p = new Point(x, y);
26 |
27 | Node parent = this;
28 | while (parent.getParent() != null) {
29 | p = new Point(parent.x, parent.y);
30 | parent = parent.getParent();
31 | }
32 |
33 | return p;
34 | }
35 |
36 | public String toString() {
37 | return "x = " + x + " y = " + y;
38 | }
39 |
40 | public int getPathLength() {
41 | Node p = this;
42 |
43 | int result = 0;
44 | while (p.getParent() != null) {
45 | p = p.getParent();
46 | result++;
47 | }
48 |
49 | return result;
50 | }
51 | }
52 |
53 | public static Node getPathBFS(boolean[][] maze, Point src, Point dst) {
54 | if(!isFree(maze, dst.x, dst.y)){
55 | return null;
56 | }
57 |
58 | Queue q = new LinkedList();
59 | maze = Utils.copyGrid(maze);
60 | maze[src.x][src.y] = true;
61 | q.add(new Node(src.x, src.y, null));
62 |
63 | while (!q.isEmpty()) {
64 | Node p = q.remove();
65 |
66 | if (p.x == dst.x && p.y == dst.y) {
67 | return p;
68 | }
69 |
70 | if (isFree(maze, p.x + 1, p.y)) {
71 | maze[p.x + 1][p.y] = true;
72 | Node nextP = new Node(p.x + 1, p.y, p);
73 | q.add(nextP);
74 | }
75 |
76 | if (isFree(maze, p.x - 1, p.y)) {
77 | maze[p.x - 1][p.y] = true;
78 | Node nextP = new Node(p.x - 1, p.y, p);
79 | q.add(nextP);
80 | }
81 |
82 | if (isFree(maze, p.x, p.y + 1)) {
83 | maze[p.x][p.y + 1] = true;
84 | Node nextP = new Node(p.x, p.y + 1, p);
85 | q.add(nextP);
86 | }
87 |
88 | if (isFree(maze, p.x, p.y - 1)) {
89 | maze[p.x][p.y - 1] = true;
90 | Node nextP = new Node(p.x, p.y - 1, p);
91 | q.add(nextP);
92 | }
93 | }
94 |
95 | return null;
96 | }
97 |
98 | public static boolean isFree(boolean[][] maze, int x, int y) {
99 | if ((x >= 0 && x < maze.length) && (y >= 0 && y < maze[x].length) && !maze[x][y]) {
100 | return true;
101 | }
102 | return false;
103 | }
104 | }
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/Direction.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import com.codingame.game.GameException;
4 |
5 | public class Direction {
6 | public int Dx, Dy;
7 | public String Name;
8 | public Direction(int dx, int dy, String name){
9 | Dx = dx;
10 | Dy = dy;
11 | Name = name;
12 | }
13 |
14 | private static Direction[] directions = {
15 | new Direction(-1, 0, "W"),
16 | new Direction(1, 0, "E"),
17 | new Direction(0, -1, "N"),
18 | new Direction(0, 1, "S"),
19 | };
20 |
21 | public static Direction getDir(String dir) throws GameException {
22 | for(Direction d : directions){
23 | if(d.Name.equals(dir)) {
24 | return d;
25 | }
26 | }
27 | throw new GameException("Invalid direction: " + dir + ". Legal values are: N, S, E or W.");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/Game.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.gameengine.core.AbstractPlayer;
5 |
6 | import java.awt.*;
7 | import java.util.Random;
8 |
9 | public class Game {
10 | public PlayerModel[] players;
11 | private IPlayerManager playerManager;
12 | private Random rand;
13 | private final int maxTurns = 598;
14 |
15 | public GridModel gridModel;
16 | public PlayerModel activePlayer;
17 | public int turn = 0;
18 |
19 | public Game(IPlayerManager playerManager, Random rand){
20 | this.playerManager = playerManager;
21 | this.rand = rand;
22 | gridModel = new GridModel();
23 | players = new PlayerModel[]{ new PlayerModel(0, this, gridModel), new PlayerModel(1, this, gridModel)};
24 | gridModel.initialize(rand);
25 | }
26 |
27 | public void initialize(int league){
28 | updateScore();
29 | playerManager.sendData(getInitialInput(players[0]), 0);
30 | playerManager.sendData(getInitialInput(players[1]), 1);
31 | initializePlayer(players[0], league);
32 | initializePlayer(players[1], league);
33 | }
34 |
35 | public void onRound(){
36 | if(isGameOver()){
37 | playerManager.endGame();
38 | return;
39 | }
40 |
41 | try{
42 | if(turn == 0){
43 | turn++;
44 | activePlayer = players[0];
45 | handleNewRound(activePlayer);
46 | updateScore();
47 | return;
48 | }
49 |
50 | if (activePlayer.hasMoreActions()){
51 | activePlayer.performRoundAction();
52 | updateScore();
53 | return;
54 | }
55 |
56 | turn++;
57 | if(turn > maxTurns){
58 | playerManager.endGame();
59 | return;
60 | }
61 |
62 | activePlayer = players[1-activePlayer.index];
63 | handleNewRound(activePlayer);
64 | updateScore();
65 | }
66 | catch (AbstractPlayer.TimeoutException e){
67 | playerManager.disablePlayer(activePlayer.index, "Timeout!");
68 | activePlayer.life=-1;
69 | }
70 | catch (GameException e){
71 | playerManager.disablePlayer(activePlayer.index, e.getMessage());
72 | activePlayer.life=-1;
73 | }
74 | catch (Exception e){
75 | playerManager.disablePlayer(activePlayer.index, "provided invalid input");
76 | activePlayer.life=-1;
77 | }
78 | }
79 |
80 | private void handleNewRound(PlayerModel playerModel) throws AbstractPlayer.TimeoutException, GameException {
81 | playerManager.sendData(getRoundInput(playerModel), playerModel.index);
82 | playerManager.execute(playerModel.index);
83 | playerModel.handleRoundInput(playerManager.getOutputs(playerModel.index));
84 | }
85 |
86 | private String[] getRoundInput(PlayerModel playerModel){
87 | PlayerModel opponent = playerModel.getOpponent();
88 | String[] input = new String[3];
89 | input[0] = (String.format("%d %d %d %d %s", playerModel.position.x, playerModel.position.y,
90 | playerModel.life, opponent.life, playerModel.getCooldowns()));
91 | input[1] = playerModel.sonarResult;
92 | input[2] = (String.format("%s", opponent.getSummaries()));
93 | return input;
94 | }
95 |
96 | private String[] getInitialInput(PlayerModel playerModel){
97 | boolean[][] grid = gridModel.grid;
98 |
99 | String[] input = new String[GridModel.GRID_SIZE + 1];
100 | input[0] = String.format("%d %d %d", GridModel.GRID_SIZE, GridModel.GRID_SIZE, playerModel.index);
101 |
102 | // send the grid
103 | for (int y = 0; y < GridModel.GRID_SIZE; y++) {
104 | StringBuffer line = new StringBuffer();
105 | for (int x = 0; x < GridModel.GRID_SIZE; x++) {
106 | line.append(grid[x][y] ? "x" : ".");
107 | }
108 | input[y + 1] = (line.toString());
109 | }
110 |
111 | return input;
112 | }
113 |
114 | private void initializePlayer(PlayerModel playerModel, int league){
115 | try
116 | {
117 | playerManager.execute(playerModel.index);
118 | playerModel.initialize(playerManager.getOutputs(playerModel.index), league);
119 | }
120 | catch (AbstractPlayer.TimeoutException e){
121 | playerManager.disablePlayer(playerModel.index, "Timeout!");
122 | playerModel.life=-1;
123 | }
124 | catch (GameException e){
125 | playerManager.disablePlayer(playerModel.index, e.getMessage());
126 | playerModel.life=-1;
127 | }
128 | catch (Exception e){
129 | playerManager.disablePlayer(playerModel.index, "provided invalid input");
130 | playerModel.life=-1;
131 | }
132 | }
133 |
134 | public void addTooltip(PlayerModel playerModel, String tooltip){
135 | playerManager.addTooltip(playerModel.index, tooltip);
136 | }
137 |
138 | public void addError(PlayerModel playerModel, String error){
139 | playerManager.addGameSummary(playerModel.index, error);
140 | }
141 |
142 | private void updateScore(){
143 | playerManager.updateScore(0, players[0].life);
144 | playerManager.updateScore(1, players[1].life);
145 | }
146 |
147 | private boolean isGameOver(){
148 | return players[0].life <= 0 || players[1].life <= 0 || turn > maxTurns;
149 | }
150 |
151 | public void explode(Point target){
152 | for(PlayerModel playerModel : players) {
153 | if (playerModel.position.equals(target)) {
154 | playerModel.reduceLife(2);
155 | continue;
156 | }
157 |
158 | if (Math.abs(playerModel.position.x - target.x) <= 1 && Math.abs(playerModel.position.y - target.y) <= 1) {
159 | playerModel.reduceLife(1);
160 | }
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/GridModel.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import java.awt.*;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.Random;
7 |
8 | public class GridModel {
9 | public static final int GRID_SIZE = 15;
10 | public boolean[][] grid = new boolean[GRID_SIZE][GRID_SIZE];
11 | public Map tiles;
12 |
13 | public boolean isEmpty(Point p) {
14 | return p.x>=0 && p.x=0 && p.y= 0) {
42 | grid[x][y - 1] = true;
43 | }
44 |
45 | if (x + 1 < GRID_SIZE) {
46 | grid[x + 1][y] = true;
47 |
48 | if (y - 1 >= 0) {
49 | grid[x + 1][y - 1] = true;
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | do {
57 | tiles = checkError(grid);
58 | } while (tiles == null);
59 | }
60 |
61 | private Map checkError(boolean[][] grid) {
62 | String[] tiles = new String[] { "11.0.111", ".11111.0", ".0.0.111", "11.0.0.1", ".111.0.0", ".0.111.0",
63 | "1111.0.1", ".0.11111", "11111111", "11111101", "11110111", "11011111", "01111111" };
64 |
65 | Map tileSprites = new HashMap();
66 |
67 | for (int y = 0; y < GRID_SIZE; y++) {
68 | for (int x = 0; x < GRID_SIZE; x++) {
69 | boolean island = grid[x][y];
70 |
71 | if (!island)
72 | continue;
73 |
74 | // create map
75 | StringBuilder map = new StringBuilder();
76 | map.append(y - 1 < 0 || x - 1 < 0 || grid[x - 1][y - 1] ? "1" : "0");
77 | map.append(y - 1 < 0 || grid[x][y - 1] ? "1" : "0");
78 | map.append(y - 1 < 0 || x + 1 >= GRID_SIZE || grid[x + 1][y - 1] ? "1" : "0");
79 | map.append(x + 1 >= GRID_SIZE || grid[x + 1][y] ? "1" : "0");
80 | map.append(y + 1 >= GRID_SIZE || x + 1 >= GRID_SIZE || grid[x + 1][y + 1] ? "1" : "0");
81 | map.append(y + 1 >= GRID_SIZE || grid[x][y + 1] ? "1" : "0");
82 | map.append(y + 1 >= GRID_SIZE || x - 1 < 0 || grid[x - 1][y + 1] ? "1" : "0");
83 | map.append(x - 1 < 0 || grid[x - 1][y] ? "1" : "0");
84 |
85 | String sprite = null;
86 | for (int i = 0; i < tiles.length; i++) {
87 | if (map.toString().matches(tiles[i])) {
88 | sprite = String.format("island/%s.png", tiles[i].replaceAll("\\.", "x"));
89 | tileSprites.put(new Point(x, y), sprite);
90 | break;
91 | }
92 | }
93 |
94 | if (sprite == null) {
95 | // fix error
96 | grid[Math.max(x - 1, 0)][Math.max(0, y - 1)] = true;
97 | grid[x][Math.max(0, y - 1)] = true;
98 | grid[Math.min(x + 1, GRID_SIZE - 1)][Math.max(0, y - 1)] = true;
99 | grid[Math.max(x - 1, 0)][Math.max(0, y)] = true;
100 | grid[Math.min(x + 1, GRID_SIZE - 1)][Math.max(0, y)] = true;
101 | grid[Math.max(x - 1, 0)][Math.min(y + 1, GRID_SIZE - 1)] = true;
102 | grid[x][Math.min(y + 1, GRID_SIZE - 1)] = true;
103 | grid[Math.min(x + 1, GRID_SIZE - 1)][Math.min(y + 1, GRID_SIZE - 1)] = true;
104 |
105 | return null;
106 | }
107 | }
108 | }
109 |
110 | return tileSprites;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/IPlayerManager.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import com.codingame.gameengine.core.AbstractPlayer;
4 |
5 | public interface IPlayerManager {
6 | String getOutputs(int playerId) throws AbstractPlayer.TimeoutException;
7 | void sendData(String[] lines, int playerId);
8 | void execute(int playerId);
9 | void updateScore(int playerId, int score);
10 | void disablePlayer(int playerId, String reason);
11 | void endGame();
12 | void addTooltip(int playerId, String message);
13 | void addGameSummary(int playerId, String message);
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/Mine.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import java.awt.*;
4 |
5 | public class Mine {
6 | public final Point point;
7 | public boolean isActive, isBlown;
8 |
9 | public Mine(Point point) {
10 | this.point = point;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/PlayerAction.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.commands.MessageCommand;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashSet;
8 |
9 | public class PlayerAction {
10 | public String activator;
11 | public String command;
12 |
13 | public PlayerAction(String activator, String command){
14 | this.activator = activator;
15 | this.command = command;
16 | }
17 |
18 | public static ArrayList cleanActions(String command) throws GameException {
19 | String[] commandStr = command.split("\\|");
20 | HashSet keys = new HashSet<>();
21 | ArrayList actions = new ArrayList<>();
22 | for(int i = 0;i < commandStr.length; i++){
23 | String cStr = commandStr[i].trim();
24 | if(cStr.equals("")) continue;
25 | int activatorSize = cStr.indexOf(" ");
26 | if(activatorSize < 0){
27 | activatorSize = cStr.length();
28 | }
29 | String activator = cStr.substring(0, activatorSize);
30 | if(!keys.add(activator) ){
31 | if(!activator.equals(MessageCommand.NAME)){
32 | throw new GameException("Two equal commands executed.");
33 | }
34 | }
35 |
36 | actions.add(new PlayerAction(activator, cStr));
37 | }
38 |
39 | return actions;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/PlayerModel.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.commands.*;
5 |
6 | import java.awt.*;
7 | import java.util.ArrayList;
8 |
9 | public class PlayerModel {
10 | private MessageCommand messageCommand = new MessageCommand(this);
11 | private final PlayerAction fallbackAction = new PlayerAction("SURFACE", "SURFACE");
12 | private ArrayList actions = new ArrayList<>();
13 |
14 | public ArrayList powers = new ArrayList<>();
15 | public ArrayList commands = new ArrayList<>();
16 | public Game game;
17 | public GridModel gridModel;
18 | public int life = 6;
19 | public int index;
20 | public Point position;
21 | public ArrayList mines = new ArrayList<>();
22 | public String message = "";
23 | public boolean[][] grid = new boolean[GridModel.GRID_SIZE][GridModel.GRID_SIZE];
24 | public ArrayList summaries = new ArrayList<>();
25 | public String sonarResult = "NA";
26 | public PlayerAction lastCommand;
27 | public String actionMessage = "";
28 |
29 | public PlayerModel(int index, Game game, GridModel gridModel){
30 | this.index = index;
31 | this.game = game;
32 | this.gridModel = gridModel;
33 | }
34 |
35 | public int getSector() {
36 | return (int) (Math.ceil((position.x + 1) / 5.0) + Math.floor(position.y / 5.0) * 3);
37 | }
38 |
39 | public void initialize(String output, int league) throws GameException {
40 | if (!output.matches("^[0-9]{1,2} [0-9]{1,2}$"))
41 | throw new GameException("Cannot read initial coordinates. Expected format: 'X Y'");
42 | String[] coordinates = output.split(" ");
43 | position = new Point(Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]));
44 |
45 | if (!gridModel.isEmpty(position))
46 | throw new GameException("Invalid coordinates!");
47 | grid[position.x][position.y] = true;
48 |
49 | commands.add(new MoveCommand(this));
50 | commands.add(new SurfaceCommand(this));
51 | addPower(new TorpedoPower(this));
52 |
53 | if (league >= 2) {
54 | addPower(new SilencePower(this));
55 | addPower(new SonarPower(this));
56 | }
57 |
58 | if (league >= 3) {
59 | addPower(new MinePower(this));
60 | commands.add(new TriggerCommand(this));
61 | }
62 | }
63 |
64 | private void addPower(IPower power){
65 | commands.add(power);
66 | powers.add(power);
67 | }
68 |
69 | public boolean hasMoreActions(){
70 | return actions.size() > 0;
71 | }
72 |
73 | public void handleRoundInput(String input) throws GameException{
74 | updateMines();
75 | lastCommand = null;
76 | sonarResult = "NA";
77 | summaries.clear();
78 | actions = PlayerAction.cleanActions(input);
79 | ArrayList actionMessages = new ArrayList<>();
80 | for(PlayerAction action : actions){
81 | if(messageCommand.canExecute(action)) messageCommand.execute(action);
82 | else{
83 | actionMessages.add(action.command);
84 | }
85 | }
86 | actionMessage = String.join(" > ", actionMessages);
87 |
88 | if(!hasValidCommandAndRemoveInvalid()){
89 | actions.add(fallbackAction);
90 | }
91 |
92 | performRoundAction();
93 | }
94 |
95 | public void performRoundAction() throws GameException{
96 | PlayerAction action = actions.get(0);
97 | lastCommand = action;
98 | actions.remove(0);
99 | for(ICommand command : commands){
100 | if(!command.getName().equals(action.activator)) continue;
101 | command.execute(action);
102 | break;
103 | }
104 |
105 | hasValidCommandAndRemoveInvalid(); // clear trailing invalid commands on the right player if at the end.
106 | }
107 |
108 | private boolean hasValidCommandAndRemoveInvalid() throws GameException{
109 | while(actions.size() > 0){
110 | PlayerAction action = actions.get(0);
111 | for(ICommand command : commands){
112 | if(!command.getName().equals(action.activator)) continue;
113 | if(!command.canExecute(action)){
114 | actions.remove(0);
115 | break;
116 | }else{
117 | return true;
118 | }
119 | }
120 | if(messageCommand.canExecute(action)){
121 | messageCommand.execute(action);
122 | actions.remove(0);
123 | }else if(actions.contains(action)){
124 | throw new GameException("Unknown command: " + action.command);
125 | }
126 | }
127 |
128 | return false;
129 | }
130 |
131 | public PlayerModel getOpponent(){ return game.players[1-index]; }
132 |
133 | public void surface(){
134 | grid = new boolean[GridModel.GRID_SIZE][GridModel.GRID_SIZE];
135 | grid[position.x][position.y] = true;
136 | reduceLife(1);
137 | }
138 |
139 | public void reduceLife(int amount){
140 | life-=amount;
141 | if(life < 0){
142 | life = 0;
143 | }
144 |
145 | if(amount > 1)
146 | game.addTooltip(this, " lost " + amount + " lives");
147 | else
148 | game.addTooltip(this, " lost a life");
149 | }
150 |
151 | public void move(Direction direction) throws GameException {
152 | int x = position.x + direction.Dx;
153 | int y = position.y + direction.Dy;
154 | if(!gridModel.isEmpty(new Point(x, y))){
155 | throw new GameException("Invalid move. Tried to move outside map or onto island");
156 | }
157 | if(grid[x][y]){
158 | throw new GameException("Can't visit same location twice. Use surface.");
159 | }
160 |
161 | position = new Point(x, y);
162 | grid[x][y] = true;
163 | }
164 |
165 | public String getCooldowns(){
166 | return getCooldown(TorpedoPower.NAME)
167 | + " " + getCooldown(SonarPower.NAME)
168 | + " " + getCooldown(SilencePower.NAME)
169 | + " " + getCooldown(MinePower.NAME);
170 | }
171 |
172 | public int getCooldown(String commandName){
173 | for(IPower power : powers){
174 | if(power.getName().equals(commandName)){
175 | return power.getMaxValue() - power.getCharges();
176 | }
177 | }
178 |
179 | return -1;
180 | }
181 |
182 | public boolean hasPower(String commandName){
183 | for(IPower power : powers){
184 | if(power.getName().equals(commandName)){
185 | return true;
186 | }
187 | }
188 |
189 | return false;
190 | }
191 |
192 | public void chargePower(String name){
193 | for(IPower power : powers){
194 | if(power.getName().equals(name)){
195 | if(power.getMaxValue() > power.getCharges()){
196 | power.increaseCharges();
197 | }
198 | return;
199 | }
200 | }
201 | }
202 |
203 | private void updateMines(){
204 | for(int i = mines.size()-1; i >= 0; i--){
205 | if(mines.get(i).isBlown){
206 | mines.remove(i);
207 | }
208 | }
209 |
210 | for(Mine mine : mines){
211 | mine.isActive = true;
212 | }
213 | }
214 |
215 | public String getSummaries() {
216 | return summaries.size() > 0 ? String.join("|", summaries) : "NA";
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/Utils.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model;
2 |
3 | public class Utils {
4 | public static boolean[][] copyGrid(boolean[][] source) {
5 | boolean[][] result = new boolean[source.length][source.length];
6 |
7 | for (int i = 0; i < source.length; i++)
8 | for (int j = 0; j < source[i].length; j++)
9 | result[i][j] = source[i][j];
10 |
11 | return result;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/ICommand.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.PlayerAction;
5 |
6 | public interface ICommand {
7 | String getName();
8 | boolean canExecute(PlayerAction action) throws GameException;
9 | void execute(PlayerAction action) throws GameException;
10 | String getActionWindowMessage(PlayerAction action) throws GameException;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/IPower.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.PlayerAction;
5 |
6 | public interface IPower extends ICommand {
7 | int getMaxValue();
8 | int getCharges();
9 | int getPowerIndex();
10 | void increaseCharges();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/MessageCommand.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.PlayerAction;
5 | import com.codingame.game.model.PlayerModel;
6 |
7 | public class MessageCommand implements ICommand {
8 | public static String NAME = "MSG";
9 | private PlayerModel playerModel;
10 |
11 | public MessageCommand(PlayerModel playerModel){
12 | this.playerModel = playerModel;
13 | }
14 |
15 | public String getName() { return NAME; }
16 |
17 | public boolean canExecute(PlayerAction action) throws GameException {
18 | return action.activator.equals(NAME);
19 | }
20 |
21 | public void execute(PlayerAction action) {
22 | if(action.command.length() < 5){
23 | return;
24 | }
25 |
26 | playerModel.message = action.command.substring(4, action.command.length());
27 | }
28 |
29 | public String getActionWindowMessage(PlayerAction action) {
30 | return null;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/MinePower.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.Direction;
5 | import com.codingame.game.model.Mine;
6 | import com.codingame.game.model.PlayerAction;
7 | import com.codingame.game.model.PlayerModel;
8 |
9 | import java.awt.*;
10 |
11 | public class MinePower implements IPower {
12 | private int charges;
13 | public static String NAME = "MINE";
14 | private PlayerModel playerModel;
15 |
16 | public MinePower(PlayerModel playerModel){
17 | this.playerModel = playerModel;
18 | }
19 |
20 | @Override
21 | public boolean canExecute(PlayerAction action) throws GameException {
22 | if(getMaxValue() > charges){
23 | playerModel.game.addError(playerModel, "Not enough charges of " + NAME);
24 | return false;
25 | }
26 |
27 | String[] commands = action.command.split(" ");
28 | if(commands.length > 2){
29 | throw new GameException("Invalid amount of params for MINE, should be MINE direction");
30 | }
31 |
32 | Point target = getTarget(action.command);
33 | if(!playerModel.gridModel.isEmpty(target)){
34 | playerModel.game.addError(playerModel, "Mine target outside map or on obstacle");
35 | return false;
36 | }
37 |
38 | for(Mine mine : playerModel.mines){
39 | if(!mine.isBlown && mine.point.equals(target)){
40 | playerModel.game.addError(playerModel, "Two mines on the same location is not allowed.");
41 | return false;
42 | }
43 | }
44 |
45 | return true;
46 | }
47 |
48 | public String getName() {
49 | return NAME;
50 | }
51 | public int getCharges() { return charges; }
52 | public int getMaxValue() {
53 | return 3;
54 | }
55 | public int getPowerIndex() {
56 | return 3;
57 | }
58 | public void increaseCharges() {
59 | charges = Math.min(getMaxValue(), charges+1);
60 | }
61 |
62 | private Point getTarget(String command) throws GameException{
63 | try{
64 | String[] commands = command.split(" ");
65 | Direction dir = Direction.getDir(commands[1]);
66 | Point target = new Point(playerModel.position.x+dir.Dx, playerModel.position.y+dir.Dy);
67 | return target;
68 | }
69 | catch (GameException e){
70 | throw new GameException(e.getMessage());
71 | }
72 | catch (Exception e){
73 | throw new GameException("Invalid mine command: " + command);
74 | }
75 | }
76 |
77 | public void execute(PlayerAction action) throws GameException {
78 | Point target = getTarget(action.command);
79 | playerModel.mines.add(new Mine(target));
80 | playerModel.summaries.add("MINE");
81 | //playerModel.game.addTooltip(playerModel, "MINE");
82 | charges = 0;
83 | }
84 |
85 | public String getActionWindowMessage(PlayerAction action) throws GameException{
86 | return action.command;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/MoveCommand.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 |
4 | import com.codingame.game.GameException;
5 | import com.codingame.game.model.Direction;
6 | import com.codingame.game.model.PlayerAction;
7 | import com.codingame.game.model.PlayerModel;
8 |
9 | public class MoveCommand implements ICommand {
10 | public static String NAME = "MOVE";
11 | private PlayerModel playerModel;
12 |
13 | public MoveCommand(PlayerModel playerModel) {
14 | this.playerModel = playerModel;
15 | }
16 |
17 | public String getName() { return NAME; }
18 |
19 | @Override
20 | public boolean canExecute(PlayerAction action) throws GameException {
21 | String[] commands = action.command.split(" ");
22 | if(commands.length > 3){
23 | throw new GameException("Invalid amount of parameters to MOVE. Should be MOVE direction power");
24 | }
25 |
26 | getDirection(action.command);
27 | getPower(action.command);
28 | return true;
29 | }
30 |
31 | @Override
32 | public void execute(PlayerAction action) throws GameException {
33 | Direction direction = getDirection(action.command);
34 | String power = getPower(action.command);
35 | playerModel.move(direction);
36 | playerModel.chargePower(power);
37 | playerModel.summaries.add(String.format("MOVE %s", direction.Name));
38 | }
39 |
40 | @Override
41 | public String getActionWindowMessage(PlayerAction action) throws GameException {
42 | return action.command;
43 | }
44 |
45 | private String getPower(String command) throws GameException{
46 | String[] commands = command.split(" ");
47 | if(commands.length != 3) return "";
48 | String power = commands[2].trim();
49 | if (!playerModel.hasPower(power)){
50 | throw new GameException("Invalid power to charge: " + power);
51 | }
52 | return power;
53 | }
54 |
55 | private Direction getDirection(String command) throws GameException{
56 | try{
57 | String[] commands = command.split(" ");
58 | Direction dir = Direction.getDir(commands[1]);
59 | return dir;
60 | }
61 | catch (GameException e){
62 | throw new GameException(e.getMessage());
63 | }
64 | catch (Exception e){
65 | throw new GameException("Invalid move command: " + command);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/SilencePower.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.Direction;
5 | import com.codingame.game.model.PlayerAction;
6 | import com.codingame.game.model.PlayerModel;
7 |
8 | public class SilencePower implements IPower {
9 | private int charges;
10 | public static String NAME = "SILENCE";
11 | private PlayerModel playerModel;
12 |
13 | public SilencePower(PlayerModel playerModel){
14 | this.playerModel = playerModel;
15 | }
16 |
17 | @Override
18 | public String getName() {
19 | return NAME;
20 | }
21 |
22 | @Override
23 | public boolean canExecute(PlayerAction action) throws GameException {
24 | if(getMaxValue() > charges){
25 | playerModel.game.addError(playerModel, "Not enough charges of " + NAME);
26 | return false;
27 | }
28 |
29 | String[] commands = action.command.split(" ");
30 | if(commands.length != 3){
31 | throw new GameException("Invalid amount of params for Silence, should be: SILENCE direction steps");
32 | }
33 |
34 | getDirection(action.command);
35 | getDistance(action.command);
36 | return true;
37 | }
38 |
39 | @Override
40 | public void execute(PlayerAction action) throws GameException {
41 | Direction dir = getDirection(action.command);
42 | int dist = getDistance(action.command);
43 | for(int i = 0; i < dist;i++){
44 | playerModel.move(dir);
45 | }
46 | playerModel.summaries.add(NAME);
47 | //playerModel.game.addTooltip(playerModel, NAME);
48 | charges = 0;
49 | }
50 |
51 | @Override
52 | public String getActionWindowMessage(PlayerAction action) throws GameException {
53 | return action.command;
54 | }
55 |
56 | @Override
57 | public int getMaxValue() {
58 | return 6;
59 | }
60 |
61 | @Override
62 | public int getCharges() {
63 | return charges;
64 | }
65 |
66 | @Override
67 | public int getPowerIndex() {
68 | return 2;
69 | }
70 |
71 | @Override
72 | public void increaseCharges() { charges = Math.min(getMaxValue(), charges+1); }
73 |
74 | private int getDistance(String command) throws GameException{
75 | try{
76 | String[] commands = command.split(" ");
77 | Integer distance = Integer.parseInt(commands[2]);
78 | if(distance < 0 || distance > 4) {
79 | throw new GameException("Invalid SILENCE distance");
80 | }
81 |
82 | return distance;
83 | }catch (Exception e){
84 | throw new GameException("Invalid distance on SILENCE: " + command);
85 | }
86 | }
87 |
88 | private Direction getDirection(String command) throws GameException{
89 | try{
90 | String[] commands = command.split(" ");
91 | Direction dir = Direction.getDir(commands[1]);
92 | return dir;
93 | }
94 | catch (GameException e){
95 | throw new GameException(e.getMessage());
96 | }
97 | catch (Exception e){
98 | throw new GameException("Invalid move command: " + command);
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/SonarPower.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.PlayerAction;
5 | import com.codingame.game.model.PlayerModel;
6 |
7 | public class SonarPower implements IPower {
8 | private int charges;
9 | public static String NAME = "SONAR";
10 | private PlayerModel playerModel;
11 |
12 | public SonarPower(PlayerModel playerModel){
13 |
14 | this.playerModel = playerModel;
15 | }
16 |
17 | public String getName() { return NAME; }
18 |
19 | @Override
20 | public boolean canExecute(PlayerAction action) throws GameException {
21 | if(getMaxValue() > charges){
22 | playerModel.game.addError(playerModel, "Not enough charges of " + NAME);
23 | return false;
24 | }
25 |
26 | String[] commands = action.command.split(" ");
27 | if(commands.length != 2){
28 | throw new GameException("Invalid amount of params for SONAR, should be: SONAR sectorId");
29 | }
30 |
31 | getSector(action.command);
32 | return true;
33 | }
34 |
35 | @Override
36 | public void execute(PlayerAction action) throws GameException {
37 | int sector = getSector(action.command);
38 | playerModel.sonarResult = playerModel.getOpponent().getSector() == sector ? "Y" : "N";
39 | playerModel.summaries.add(String.format("SONAR %d", sector));
40 | playerModel.game.addTooltip(playerModel, String.format("SONAR %d", sector));
41 | charges = 0;
42 | }
43 |
44 | @Override
45 | public String getActionWindowMessage(PlayerAction action) throws GameException {
46 | return action.command;
47 | }
48 |
49 | @Override
50 | public int getMaxValue() {
51 | return 4;
52 | }
53 |
54 | @Override
55 | public int getCharges() {
56 | return charges;
57 | }
58 |
59 | @Override
60 | public int getPowerIndex() {
61 | return 1;
62 | }
63 |
64 | @Override
65 | public void increaseCharges() { charges = Math.min(getMaxValue(), charges+1);}
66 |
67 | private int getSector(String command) throws GameException {
68 | try {
69 | String[] commands = command.split(" ");
70 | Integer sector = Integer.parseInt(commands[1]);
71 | if (sector < 1 || sector > 9){
72 | throw new GameException("Invalid sector to scan. Must be a number between 1 and 9");
73 | }
74 |
75 | return sector;
76 | }
77 | catch(Exception e){
78 | throw new GameException("Invalid sector on SONAR: " + command);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/SurfaceCommand.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.PlayerAction;
5 | import com.codingame.game.model.PlayerModel;
6 |
7 | public class SurfaceCommand implements ICommand {
8 |
9 | public static String NAME = "SURFACE";
10 | private PlayerModel playerModel;
11 |
12 | public SurfaceCommand(PlayerModel playerModel){
13 | this.playerModel = playerModel;
14 | }
15 |
16 | public String getName() { return NAME; }
17 |
18 | public boolean canExecute(PlayerAction action) throws GameException {
19 | return true;
20 | }
21 |
22 | public void execute(PlayerAction action) throws GameException {
23 | playerModel.game.addTooltip(playerModel, NAME);
24 | playerModel.surface();
25 | playerModel.summaries.add(String.format("SURFACE %d", playerModel.getSector()));
26 | }
27 |
28 | public String getActionWindowMessage(PlayerAction action) throws GameException {
29 | return action.activator;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/TorpedoPower.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import java.awt.Point;
4 |
5 | import com.codingame.game.model.BFS;
6 | import com.codingame.game.model.BFS.Node;
7 | import com.codingame.game.GameException;
8 | import com.codingame.game.model.PlayerAction;
9 | import com.codingame.game.model.PlayerModel;
10 |
11 | public class TorpedoPower implements IPower {
12 | private int charges;
13 | public static String NAME = "TORPEDO";
14 | private PlayerModel playerModel;
15 |
16 | public TorpedoPower(PlayerModel playerModel){
17 | this.playerModel = playerModel;
18 | }
19 |
20 | @Override
21 | public String getName() {
22 | return NAME;
23 | }
24 |
25 | @Override
26 | public boolean canExecute(PlayerAction action) throws GameException {
27 | if(getMaxValue() > charges){
28 | playerModel.game.addError(playerModel, "Not enough charges of " + NAME);
29 | return false;
30 | }
31 |
32 | String[] commands = action.command.split(" ");
33 | if(commands.length != 3){
34 | throw new GameException("Invalid amount of params for TORPEDO, should be TORPEDO x y");
35 | }
36 |
37 | Point target = getTarget(action.command);
38 | Node path = BFS.getPathBFS(playerModel.gridModel.grid, playerModel.position, target);
39 |
40 | if (path == null || path.getPathLength() > 4) {
41 | playerModel.game.addError(playerModel,"Tried to fire out of range");
42 | return false;
43 | }
44 |
45 | return true;
46 | }
47 |
48 | @Override
49 | public void execute(PlayerAction action) throws GameException {
50 | playerModel.game.addTooltip(playerModel, action.command);
51 | Point target = getTarget(action.command);
52 | playerModel.game.explode(target);
53 | playerModel.summaries.add(String.format("%s %d %d", NAME, target.x, target.y));
54 | charges = 0;
55 | }
56 |
57 | public String getActionWindowMessage(PlayerAction action) throws GameException {
58 | return action.command;
59 | }
60 |
61 | public int getMaxValue() {
62 | return 3;
63 | }
64 |
65 | public int getCharges() { return charges; }
66 |
67 | public int getPowerIndex() {
68 | return 0;
69 | }
70 |
71 | public void increaseCharges() { charges = Math.min(getMaxValue(), charges+1);}
72 |
73 | private Point getTarget(String command) throws GameException{
74 | try{
75 | String[] commands = command.split(" ");
76 | Point target = new Point(Integer.parseInt(commands[1]), Integer.parseInt(commands[2]));
77 | return target;
78 | }
79 | catch (Exception e){
80 | throw new GameException("Invalid params to Torpedo. Should be TORPEDO x y");
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/codingame/game/model/commands/TriggerCommand.java:
--------------------------------------------------------------------------------
1 | package com.codingame.game.model.commands;
2 |
3 | import com.codingame.game.GameException;
4 | import com.codingame.game.model.Mine;
5 | import com.codingame.game.model.PlayerAction;
6 | import com.codingame.game.model.PlayerModel;
7 |
8 | import java.awt.*;
9 |
10 | public class TriggerCommand implements ICommand {
11 |
12 | public static String NAME = "TRIGGER";
13 | private PlayerModel playerModel;
14 |
15 | public TriggerCommand(PlayerModel playerModel){
16 | this.playerModel = playerModel;
17 | }
18 |
19 | @Override
20 | public String getName() { return NAME; }
21 |
22 | @Override
23 | public boolean canExecute(PlayerAction action) throws GameException {
24 | String[] commands = action.command.split(" ");
25 | if(commands.length > 3){
26 | throw new GameException("Invalid amount of params for TRIGGER, should be TRIGGER x y");
27 | }
28 |
29 | Point target = getTarget(action.command);
30 | for(Mine mine : playerModel.mines){
31 | if (mine.isBlown) continue;
32 | if (!mine.isActive) continue;
33 | if (mine.point.equals(target)){
34 | return true;
35 | }
36 | }
37 |
38 | return false;
39 | }
40 |
41 | @Override
42 | public void execute(PlayerAction action) throws GameException {
43 | playerModel.game.addTooltip(playerModel, action.command);
44 | Point target = getTarget(action.command);
45 | for(Mine mine : playerModel.mines){
46 | if (mine.isBlown) continue;
47 | if (!mine.isActive) continue;
48 | if (!mine.point.equals(target)) continue;
49 | mine.isBlown = true;
50 | playerModel.game.explode(target);
51 | playerModel.summaries.add(String.format("%s %d %d", NAME, target.x, target.y));
52 | return;
53 | }
54 | }
55 |
56 | @Override
57 | public String getActionWindowMessage(PlayerAction action) throws GameException {
58 | return action.command;
59 | }
60 |
61 | private Point getTarget(String command) throws GameException{
62 | try{
63 | String[] commands = command.split(" ");
64 | Point target = new Point(Integer.parseInt(commands[1]), Integer.parseInt(commands[2]));
65 | return target;
66 | }
67 | catch (Exception e){
68 | throw new GameException("Invalid params to Trigger. Should be Trigger x y");
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/resources/assembly.xml:
--------------------------------------------------------------------------------
1 |
5 | fat-tests
6 |
7 | jar
8 |
9 | false
10 |
11 |
12 | /
13 | true
14 | true
15 | test
16 |
17 |
18 |
19 |
20 | ${project.build.directory}/test-classes
21 | /
22 |
23 | **/*.class
24 |
25 | true
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/main/resources/log4j2.properties:
--------------------------------------------------------------------------------
1 | # Configuration
2 | name = PropertiesConfig
3 | status = WARN
4 |
5 | appender.console.type = Console
6 | appender.console.name = CONSOLE
7 | appender.console.layout.type = PatternLayout
8 | appender.console.layout.pattern = [%d{yyyy/MM/dd HH:mm:ss}][GF] %-5p : %c{1} - %m%n
9 |
10 | rootLogger.level = info
11 | rootLogger.appenderRef.console.ref = CONSOLE
12 |
--------------------------------------------------------------------------------
/src/main/resources/view/assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/background.jpg
--------------------------------------------------------------------------------
/src/main/resources/view/assets/explosion/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/explosion/0.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/explosion/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/explosion/1.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/explosion/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/explosion/2.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/explosion/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/explosion/3.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/explosion/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/explosion/4.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/font.fnt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/main/resources/view/assets/font.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/font.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/01111111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/01111111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11011111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11011111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11110111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11110111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11111101.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11111101.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11111111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11111111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/1111x0x1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/1111x0x1.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11x0x0x1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11x0x0x1.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/11x0x111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/11x0x111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/x0x0x111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/x0x0x111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/x0x11111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/x0x11111.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/x0x111x0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/x0x111x0.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/x11111x0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/x11111x0.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/island/x111x0x0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/island/x111x0x0.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/logo.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/mine0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/mine0.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/mine1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/mine1.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/mine2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/mine2.png
--------------------------------------------------------------------------------
/src/main/resources/view/assets/sub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/src/main/resources/view/assets/sub.png
--------------------------------------------------------------------------------
/src/main/resources/view/config.js:
--------------------------------------------------------------------------------
1 | import { GraphicEntityModule } from './entity-module/GraphicEntityModule.js';
2 | import { EndScreenModule } from './endscreen-module/EndScreenModule.js';
3 | import { TooltipModule } from './tooltip-module/TooltipModule.js';
4 |
5 | export const modules = [
6 | GraphicEntityModule,
7 | TooltipModule,
8 | EndScreenModule
9 | ];
10 |
11 | export const playerColors = [
12 | '#ff1d5c',
13 | '#ff8f16'
14 | ];
--------------------------------------------------------------------------------
/src/test/java/DummyPlayer.java:
--------------------------------------------------------------------------------
1 | import java.util.Random;
2 |
3 | public class DummyPlayer {
4 |
5 | public static void main(String[] args) {
6 | System.out.println("7 14");
7 | System.out.println("MINE N|TRIGGER 7 9");
8 | System.out.println("MOVE N MSG|MSG WAZZAP 5?!");
9 | System.out.println("MOVE N MSG|MSG WAZZAP 5?!");
10 | System.out.println("MOVE N MSG|MSG WAZZAP 5?!");
11 | System.out.println("MINE N|TRIGGER 7 9");
12 | System.out.println("TRIGGER 7 10");
13 | System.out.println("SURFACE");
14 | System.out.println("MOVE N MINE");
15 | System.out.println("MOVE N MINE");
16 | System.out.println("MOVE N MINE");
17 | System.out.println("SURFACE");
18 | System.out.println("MOVE N MINE");
19 | System.out.println("MOVE N MINE");
20 | System.out.println("MOVE N MINE");
21 | System.out.println("MOVE N MINE");
22 | System.out.println("MOVE N MINE");
23 | System.out.println("MOVE N MINE");
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/java/Main.java:
--------------------------------------------------------------------------------
1 | import com.codingame.gameengine.runner.MultiplayerGameRunner;
2 |
3 | public class Main {
4 |
5 | public static void main(String[] args) {
6 | // read cli args
7 | Options opts = new Options().read(args);
8 | String cli1 = opts.getCommandLine1();
9 | String cli2 = opts.getCommandLine2();
10 |
11 | MultiplayerGameRunner gameRunner = new MultiplayerGameRunner();
12 | // Set league, remove before push.
13 | gameRunner.setLeagueLevel(3);
14 | gameRunner.setSeed(1337L);
15 | // add player 1
16 | if (null != cli1) {
17 | gameRunner.addAgent(cli1);
18 | } else {
19 | gameRunner.addAgent(DummyPlayer.class);
20 | }
21 | // add player 2
22 | if (null != cli2) {
23 | gameRunner.addAgent(cli2);
24 | } else {
25 | gameRunner.addAgent(DummyPlayer.class);
26 | }
27 | //gameRunner.addAgent(Player2.class);
28 |
29 | //gameRunner.addAgent("python3 bot.py");
30 | //gameRunner.addAgent("python3 bot.py");
31 | //gameRunner.addAgent("java -cp target/test-classes/ Player"); //-agentlib:jdwp=transport=dt_socket,server=y,address=8681,suspend=y,quiet=y
32 | //gameRunner.addAgent("java -cp target/test-classes/ Player2"); //-agentlib:jdwp=transport=dt_socket,server=y,address=8682,suspend=y,quiet=y
33 |
34 | // gameRunner.addCommandLinePlayer("python src/main/resources/ai/ai.py");
35 |
36 | gameRunner.start();
37 | }
38 | }
39 |
40 | class Options {
41 | private String commandLine1 = null;
42 | private String commandLine2 = null;
43 |
44 | public Options read(String[] args) {
45 | if (args == null) {
46 | return this;
47 | }
48 |
49 | switch (args.length) {
50 | case 1: {
51 | this.commandLine1 = args[0];
52 | break;
53 | }
54 | case 2: {
55 | this.commandLine1 = args[0];
56 | this.commandLine2 = args[1];
57 | break;
58 | }
59 | }
60 |
61 | return this;
62 | }
63 |
64 | public String getCommandLine1() {
65 | return this.commandLine1;
66 | }
67 |
68 | public String getCommandLine2() {
69 | return this.commandLine2;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/resources/log4j2.properties:
--------------------------------------------------------------------------------
1 | # Configuration
2 | name = PropertiesConfig
3 | status = WARN
4 |
5 | appender.console.type = Console
6 | appender.console.name = CONSOLE
7 | appender.console.layout.type = PatternLayout
8 | appender.console.layout.pattern = [%d{yyyy/MM/dd HH:mm:ss}][GF] %-5p : %c{1} - %m%n
9 |
10 | rootLogger.level = info
11 | rootLogger.appenderRef.console.ref = CONSOLE
--------------------------------------------------------------------------------
/torpedoRange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodinGameCommunity/ocean-of-code/a84dcfb5ddc9bb13429eda3da775bc2b0c1ddb98/torpedoRange.png
--------------------------------------------------------------------------------