├── .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 | Wood League 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 |
  1. It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
  2. 76 |
  3. It charges a special power of your choice, i.e. increases its energy level by 1.
  4. 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 | torpedo range 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 | explosion range 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 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 |
Your actionShown to opponentComment
MOVE N TORPEDOMOVE NThe opponent will not see which power is charged.
SURFACESURFACE 3The opponent will see which sector (in this case: 3) you surfaced in.
TORPEDO 3 5TORPEDO 3 5The opponent will see the same order you issued.
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 |
    145 |
  • Don't respond in time.
  • 146 |
  • Output an ill-formed order.
  • 147 |
  • Reach 0 hit points or lower (while your opponent still has more than you).
  • 148 |
149 |
150 |
151 |
152 |
153 |
154 |

155 |   156 | Expert Rules 157 |

158 | 159 |
160 | The source code can be found here: https://github.com/CodinGameCommunity/ocean-of-code. 161 |
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 |
    222 |
  • TORPEDO
  • 223 |
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 | Wood League 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 |
  1. It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
  2. 75 |
  3. It charges a special power of your choice, i.e. increases its energy level by 1.
  4. 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 | torpedo range 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 | explosion range 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 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 144 | 145 | 146 | 147 | 148 | 151 | 152 | 153 | 154 | 155 |
Your actionShown to opponentComment
MOVE N TORPEDOMOVE NThe opponent will not see which power is charged.
SURFACESURFACE 3The opponent will see which sector (in this case: 3) you surfaced in.
TORPEDO 3 5TORPEDO 3 5The opponent will see the same order you issued.
SONAR 4SONAR 4The opponent will see the same order you issued.
SILENCE N 4SILENCEThe opponent will see that you moved silently—but not where or how far.
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 |
    172 |
  • Don't respond in time.
  • 173 |
  • Output an ill-formed order.
  • 174 |
  • Reach 0 hit points or lower (while your opponent still has more than you).
  • 175 |
176 |
177 |
178 |
179 |
180 |
181 |

182 |   183 | Expert Rules 184 |

185 | 186 |
187 | The source code can be found here: https://github.com/CodinGameCommunity/ocean-of-code. 188 |
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 | Wood League 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 |
  1. It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
  2. 75 |
  3. It charges a special power of your choice, i.e. increases its energy level by 1.
  4. 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 | torpedo range 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 | explosion range 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 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 166 | 167 | 168 | 169 | 170 | 173 | 174 | 175 | 176 | 177 |
Your actionShown to opponentComment
MOVE N TORPEDOMOVE NThe opponent will not see which power is charged.
SURFACESURFACE 3The opponent will see which sector (in this case: 3) you surfaced in.
TORPEDO 3 5TORPEDO 3 5The opponent will see the same order you issued.
SONAR 4SONAR 4The opponent will see the same order you issued.
SILENCE N 4SILENCEThe opponent will see that you moved silently—but not where or how far.
MINE EMINEThe opponent will see that you placed a mine—but not in which direction.
TRIGGER 3 5TRIGGER 3 5The opponent will see the same order you issued.
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 |
    194 |
  • Don't respond in time.
  • 195 |
  • Output an ill-formed order.
  • 196 |
  • Reach 0 hit points or lower (while your opponent still has more than you).
  • 197 |
198 |
199 |
200 |
201 |
202 |
203 |

204 |   205 | Expert Rules 206 |

207 | 208 |
209 | The source code can be found here: https://github.com/CodinGameCommunity/ocean-of-code. 210 |
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 |
  1. It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
  2. 58 |
  3. It charges a special power of your choice, i.e. increases its energy level by 1.
  4. 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 | torpedo range 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 | explosion range 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 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
Your actionShown to opponentComment
MOVE N TORPEDOMOVE NThe opponent will not see which power is charged.
SURFACESURFACE 3The opponent will see which sector (in this case: 3) you surfaced in.
TORPEDO 3 5TORPEDO 3 5The opponent will see the same order you issued.
SONAR 4SONAR 4The opponent will see the same order you issued.
SILENCE N 4SILENCEThe opponent will see that you moved silently—but not where or how far.
MINE EMINEThe opponent will see that you placed a mine—but not in which direction.
TRIGGER 3 5TRIGGER 3 5The opponent will see that you triggered the mine at (3,5).
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 |
    167 |
  • Don't respond in time.
  • 168 |
  • Output an ill-formed order.
  • 169 |
  • Reach 0 hit points or lower (while your opponent still has more than you).
  • 170 |
171 |
172 |
173 |
174 |
175 |
176 |

177 |   178 | Expert Rules 179 |

180 | 181 |
182 | The source code can be found here: https://github.com/CodinGameCommunity/ocean-of-code. 183 |
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 | Wood League 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 |
  1. It moves your submarine by 1 cell in a chosen direction (north, east, south, west).
  2. 94 |
  3. It charges a special power of your choice, i.e. increases its energy level by 1.
  4. 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 | torpedo range 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 | explosion range 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 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 216 | 217 | 218 | 219 | 220 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 244 | 245 | 246 | 247 | 248 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
Your actionShown to opponentComment
MOVE N TORPEDOMOVE NThe opponent will not see which power is charged.
SURFACESURFACE 3The opponent will see which sector (in this case: 3) you surfaced in.
TORPEDO 3 5TORPEDO 3 5The opponent will see the same order you issued.
SONAR 4SONAR 4The opponent will see the same order you issued.
SILENCE N 4SILENCEThe opponent will see that you moved silently—but not where or how far.
SONAR 4SONAR 4The opponent will see the same order you issued.
SILENCE N 4SILENCEThe opponent will see that you moved silently—but not where or how far.
MINE EMINEThe opponent will see that you placed a mine—but not in which direction.
TRIGGER 3 5TRIGGER 3 5The opponent will see the same order you issued.
MINE EMINEThe opponent will see that you placed a mine—but not in which direction.
TRIGGER 3 5TRIGGER 3 5The opponent will see that you triggered the mine at (3,5).
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 |
    285 |
  • Don't respond in time.
  • 286 |
  • Output an ill-formed order.
  • 287 |
  • Reach 0 hit points or lower (while your opponent still has more than you).
  • 288 |
289 |
290 |
291 |
292 |
293 |
294 |

295 |   296 | Expert Rules 297 |

298 | 299 |
300 | The source code can be found here: https://github.com/CodinGameCommunity/ocean-of-code. 301 |
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 --------------------------------------------------------------------------------