├── .gitignore ├── README.md ├── config ├── Boss.py ├── config.ini ├── level1 │ └── Boss.py ├── level2 │ ├── Boss.py │ ├── welcome_en.html │ └── welcome_fr.html ├── level3 │ ├── Boss.py │ ├── Tuto_6.jpg │ ├── welcome_en.html │ └── welcome_fr.html ├── level4 │ ├── Tuto_7.jpg │ ├── welcome_en.html │ └── welcome_fr.html ├── statement_en.html.tpl ├── statement_fr.html.tpl └── stub.txt ├── full_rules_en.pdf ├── full_rules_fr.pdf ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── codingame │ │ │ ├── endscreen │ │ │ └── EndScreenModule.java │ │ │ ├── event │ │ │ ├── Animation.java │ │ │ ├── AnimationData.java │ │ │ └── EventData.java │ │ │ ├── game │ │ │ ├── Closest.java │ │ │ ├── Collision.java │ │ │ ├── CommandManager.java │ │ │ ├── Drone.java │ │ │ ├── Entity.java │ │ │ ├── Fish.java │ │ │ ├── FishType.java │ │ │ ├── Game.java │ │ │ ├── GameSummaryManager.java │ │ │ ├── Player.java │ │ │ ├── Referee.java │ │ │ ├── Scan.java │ │ │ ├── Serializer.java │ │ │ ├── Ugly.java │ │ │ ├── Vector.java │ │ │ ├── action │ │ │ │ ├── Action.java │ │ │ │ ├── ActionException.java │ │ │ │ ├── ActionType.java │ │ │ │ ├── MoveAction.java │ │ │ │ └── WaitAction.java │ │ │ └── exception │ │ │ │ ├── GameException.java │ │ │ │ └── InvalidInputException.java │ │ │ └── view │ │ │ ├── FrameViewData.java │ │ │ ├── GameDataProvider.java │ │ │ ├── GlobalViewData.java │ │ │ ├── PlayerDto.java │ │ │ └── ViewModule.java │ └── resources │ │ └── view │ │ ├── .eslintrc.js │ │ ├── assets │ │ ├── Background-derriere.jpg │ │ ├── Background-devant-D.png │ │ ├── Background-devant-G.png │ │ ├── Background-plantes.png │ │ ├── blue.json │ │ ├── blue.png │ │ ├── dial_arrow.png │ │ ├── dial_mid.png │ │ ├── dial_side.png │ │ ├── disc_black.png │ │ ├── feedback.json │ │ ├── feedback.png │ │ ├── green.json │ │ ├── green.png │ │ ├── logo.png │ │ ├── other.json │ │ ├── other.png │ │ ├── pink.json │ │ ├── pink.png │ │ ├── scan_green.json │ │ ├── scan_green.png │ │ ├── uglies.json │ │ ├── uglies.png │ │ ├── yellow.json │ │ └── yellow.png │ │ ├── config.js │ │ ├── demo.js │ │ ├── endscreen-module │ │ ├── EndScreenModule.js │ │ └── errors │ │ │ └── MissingImageError.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── ts │ │ ├── Deserializer.ts │ │ ├── MessageBoxes.ts │ │ ├── TooltipManager.ts │ │ ├── ViewModule.ts │ │ ├── assetConstants.ts │ │ ├── darkness.ts │ │ ├── events.ts │ │ ├── gameConstants.ts │ │ ├── global │ │ │ ├── core.d.ts │ │ │ └── global.d.ts │ │ ├── types.ts │ │ └── utils.ts │ │ └── tsconfig.json └── test │ ├── java │ └── Fall2023Main.java │ └── resources │ └── log4j2.properties ├── starterAIs ├── README.md ├── SS_Starter.java ├── SS_Starter.js ├── SS_Starter.py ├── SS_Starter.rb └── SS_Starter.ts └── typescript ├── polyfill.js ├── readline.js └── readline └── index.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.project 3 | /.classpath 4 | /bin/ 5 | .factorypath 6 | /.settings/ 7 | .pydevproject 8 | src/main/resources/view/node_modules/ 9 | src/main/resources/view/graphics/ 10 | statement_en.html 11 | statement_fr.html 12 | JulienMain.java -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FallChallenge2023-SeabedSecurity 2 | 3 | Source code for CodinGame's Fall Challenge 2023 event. 4 | 5 | https://www.codingame.com/contests/fall-challenge-2023 6 | 7 | Community starter AIs are located here: 8 | 9 | https://github.com/CodinGame/FallChallenge2023-SeabedSecurity/tree/main/starterAIs 10 | -------------------------------------------------------------------------------- /config/Boss.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('WAIT 0') 3 | -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | min_players=2 2 | max_players=2 3 | type=multi -------------------------------------------------------------------------------- /config/level1/Boss.py: -------------------------------------------------------------------------------- 1 | redacted 2 | -------------------------------------------------------------------------------- /config/level2/Boss.py: -------------------------------------------------------------------------------- 1 | redacted 2 | -------------------------------------------------------------------------------- /config/level2/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league.

3 |
4 | The game is now played in darkness, your drones can only see fish in their 5 | light radius. 6 |
7 |
See the updated statement for details.
8 |
-------------------------------------------------------------------------------- /config/level2/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous venez d'atteindre la ligue supérieure.

3 |
4 | Le jeu se déroule maintenant dans l'obscurité, vos drones ne peuvent voir que les poissons 5 | dans leur rayon de lumière. 6 |
7 |
Consultez l'énoncé mis à jour pour plus de détails.
8 |
-------------------------------------------------------------------------------- /config/level3/Boss.py: -------------------------------------------------------------------------------- 1 | redacted 2 | -------------------------------------------------------------------------------- /config/level3/Tuto_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/config/level3/Tuto_6.jpg -------------------------------------------------------------------------------- /config/level3/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league.

3 |
Creatures will now flee from the noise of drone engines!
4 |
You will now control a team of two drones.
5 |
6 |
See the updated statement for details.
7 |
-------------------------------------------------------------------------------- /config/level3/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous venez d'atteindre la ligue supérieure.

3 |
Les créatures fuieront le son des moteurs des drones!
4 |
Vous controllez maintenant une équipe de deux drones.
5 |
6 |
Consultez l'énoncé mis à jour pour plus de détails.
7 |
-------------------------------------------------------------------------------- /config/level4/Tuto_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/config/level4/Tuto_7.jpg -------------------------------------------------------------------------------- /config/level4/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league.

3 |
Sea monsters will attack drones that shine their light too close!
4 |
5 |
See the updated statement for details.
6 |
-------------------------------------------------------------------------------- /config/level4/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous venez d'atteindre la ligue supérieure.

3 |
Des monstres marins s'attaqueront à vos drones s'ils dirigent leur lumière trop près !
4 |
5 |
Consultez l'énoncé mis à jour pour plus de détails.
6 |
-------------------------------------------------------------------------------- /config/stub.txt: -------------------------------------------------------------------------------- 1 | STATEMENT 2 | Score points by scanning valuable fish faster than your opponent. 3 | 4 | read creatureCount:int 5 | loop creatureCount read creatureId:int color:int type:int 6 | 7 | gameloop 8 | 9 | read myScore:int 10 | read foeScore:int 11 | 12 | read myScanCount:int 13 | loop myScanCount read creatureId:int 14 | read foeScanCount:int 15 | loop foeScanCount read creatureId:int 16 | 17 | read myDroneCount:int 18 | loop myDroneCount read droneId:int droneX:int droneY:int emergency:int battery:int 19 | 20 | read foeDroneCount:int 21 | loop foeDroneCount read droneId:int droneX:int droneY:int emergency:int battery:int 22 | 23 | read droneScanCount:int 24 | loop droneScanCount read droneId:int creatureId:int 25 | 26 | read visibleCreatureCount:int 27 | loop visibleCreatureCount read creatureId:int creatureX:int creatureY:int creatureVx:int creatureVy:int 28 | 29 | read radarBlipCount:int 30 | loop radarBlipCount read droneId:int creatureId:int radar:word(2) 31 | 32 | loop myDroneCount write WAIT 1 33 | 34 | OUTPUT 35 | MOVE | WAIT -------------------------------------------------------------------------------- /full_rules_en.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/full_rules_en.pdf -------------------------------------------------------------------------------- /full_rules_fr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/full_rules_fr.pdf -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.codingame.game 6 | fall-2023-fish 7 | 1.0-SNAPSHOT 8 | 9 | 10 | 4.4.2 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 | runner 25 | ${gamengine.version} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/endscreen/EndScreenModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.endscreen; 2 | 3 | import com.codingame.gameengine.core.AbstractPlayer; 4 | import com.codingame.gameengine.core.GameManager; 5 | import com.codingame.gameengine.core.Module; 6 | import com.google.inject.Inject; 7 | import com.google.inject.Singleton; 8 | 9 | /** 10 | * The EndScreen takes care of displaying and animating an end screen with the scores of the players at the end of the game. 11 | * 12 | */ 13 | @Singleton 14 | public class EndScreenModule implements Module { 15 | 16 | private GameManager gameManager; 17 | private int[] scores; 18 | private String[] displayedText; 19 | private String titleRankingsSprite = "logo.png"; 20 | 21 | @Inject 22 | EndScreenModule(GameManager gameManager) { 23 | this.gameManager = gameManager; 24 | gameManager.registerModule(this); 25 | } 26 | 27 | /** 28 | * Send scores to the module 29 | * 30 | * @param scores 31 | * the scores of the different players, the index matches the player.getIndex() 32 | */ 33 | public void setScores(int[] scores) { 34 | this.scores = scores; 35 | } 36 | 37 | /** 38 | * Send scores to the module 39 | * 40 | * @param scores 41 | * the scores of the different players, the index matches the player.getIndex() 42 | * @param displayedText 43 | * the text displayed instead of the score of a player, if null or empty string for a player the score will still be displayed 44 | * 45 | */ 46 | public void setScores(int[] scores, String[] displayedText) { 47 | this.scores = scores; 48 | this.displayedText = displayedText; 49 | } 50 | 51 | /** 52 | * Allows you to set the sprite used as the title of the ranking board 53 | * 54 | * @param spriteName 55 | * the name of the sprite you want to use default is "logo.png" 56 | */ 57 | public void setTitleRankingsSprite(String spriteName) { 58 | this.titleRankingsSprite = spriteName; 59 | } 60 | 61 | /** 62 | * 63 | * @return the name of the sprite that will be used as the title of the ranking board 64 | */ 65 | public String getTitleRankingsSprite() { 66 | return titleRankingsSprite; 67 | } 68 | 69 | @Override 70 | public final void onGameInit() { 71 | } 72 | 73 | @Override 74 | public final void onAfterGameTurn() { 75 | } 76 | 77 | @Override 78 | public final void onAfterOnEnd() { 79 | Object[] data = { scores, titleRankingsSprite, displayedText }; 80 | gameManager.setViewData("endScreen", data); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/main/java/com/codingame/event/Animation.java: -------------------------------------------------------------------------------- 1 | package com.codingame.event; 2 | 3 | import java.util.List; 4 | 5 | import com.google.inject.Singleton; 6 | 7 | @Singleton 8 | public class Animation { 9 | public static final int HUNDREDTH = 10; 10 | public static final int TWENTIETH = 50; 11 | public static final int TENTH = 100; 12 | public static final int THIRD = 300; 13 | public static final int HALF = 500; 14 | public static final int WHOLE = 1000; 15 | 16 | private int frameTime; 17 | private int endTime; 18 | 19 | public void reset() { 20 | frameTime = 0; 21 | endTime = 0; 22 | } 23 | 24 | public int wait(int time) { 25 | return frameTime += time; 26 | } 27 | 28 | public int getFrameTime() { 29 | return frameTime; 30 | } 31 | 32 | public void startAnim(List animData, int duration) { 33 | animData.add(new AnimationData(frameTime, duration)); 34 | endTime = Math.max(endTime, frameTime + duration); 35 | } 36 | 37 | public void waitForAnim(List animData, int duration) { 38 | animData.add(new AnimationData(frameTime, duration)); 39 | frameTime += duration; 40 | endTime = Math.max(endTime, frameTime); 41 | } 42 | 43 | public void chainAnims(int count, List animData, int duration, int separation) { 44 | chainAnims(count, animData, duration, separation, true); 45 | } 46 | 47 | public void chainAnims(int count, List animData, int duration, int separation, boolean waitForEnd) { 48 | for (int i = 0; i < count; ++i) { 49 | animData.add(new AnimationData(frameTime, duration)); 50 | 51 | if (i < count - 1) { 52 | frameTime += separation; 53 | } 54 | } 55 | endTime = Math.max(endTime, frameTime + duration); 56 | if (waitForEnd && count > 0) { 57 | frameTime += duration; 58 | } 59 | } 60 | 61 | public void setFrameTime(int startTime) { 62 | this.frameTime = startTime; 63 | } 64 | 65 | public int getEndTime() { 66 | return endTime; 67 | } 68 | 69 | public void catchUp() { 70 | frameTime = endTime; 71 | } 72 | 73 | public int computeEvents() { 74 | int minTime = 1000; 75 | 76 | catchUp(); 77 | 78 | int frameTime = Math.max( 79 | getFrameTime(), 80 | minTime 81 | ); 82 | return frameTime; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/event/AnimationData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.event; 2 | 3 | public class AnimationData { 4 | public int start, end; 5 | 6 | public AnimationData(int start, int duration) { 7 | this.start = start; 8 | this.end = start + duration; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/event/EventData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.event; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.codingame.game.Vector; 7 | 8 | public class EventData { 9 | public static final int BUILD = 0; 10 | public static final int MOVE = 1; 11 | 12 | public int type; 13 | public List animData; 14 | 15 | public EventData() { 16 | animData = new ArrayList<>(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Closest.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.List; 4 | 5 | public class Closest { 6 | 7 | List list; 8 | double distance; 9 | 10 | public Closest(List list, double distance) { 11 | this.list = list; 12 | this.distance = distance; 13 | } 14 | 15 | public T get() { 16 | if (hasOne()) { 17 | return list.get(0); 18 | } 19 | return null; 20 | 21 | } 22 | 23 | public boolean hasOne() { 24 | return list.size() == 1; 25 | } 26 | 27 | public Vector getPos() { 28 | if (!hasOne()) { 29 | return null; 30 | } 31 | 32 | return list.get(0).getPos(); 33 | } 34 | 35 | public Vector getMeanPos() { 36 | if (hasOne()) { 37 | return getPos(); 38 | } 39 | double x = 0; 40 | double y = 0; 41 | 42 | for (Entity entity : list) { 43 | x += entity.getPos().getX(); 44 | y += entity.getPos().getY(); 45 | } 46 | return new Vector(x / list.size(), y / list.size()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Collision.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public class Collision { 4 | final static Collision NONE = new Collision(-1); 5 | 6 | double t; 7 | Entity a; 8 | Entity b; 9 | 10 | Collision(double t) { 11 | this(t, null, null); 12 | } 13 | 14 | Collision(double t, Entity a) { 15 | this(t, a, null); 16 | } 17 | 18 | Collision(double t, Entity a, Entity b) { 19 | this.t = t; 20 | this.a = a; 21 | this.b = b; 22 | } 23 | 24 | public boolean happened() { 25 | return t >= 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/CommandManager.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.List; 4 | import java.util.regex.Matcher; 5 | 6 | import com.codingame.game.action.Action; 7 | import com.codingame.game.action.ActionType; 8 | import com.codingame.game.action.MoveAction; 9 | import com.codingame.game.action.WaitAction; 10 | import com.codingame.game.exception.InvalidInputException; 11 | import com.codingame.gameengine.core.GameManager; 12 | import com.codingame.gameengine.core.MultiplayerGameManager; 13 | import com.google.inject.Inject; 14 | import com.google.inject.Singleton; 15 | 16 | @Singleton 17 | public class CommandManager { 18 | 19 | @Inject private MultiplayerGameManager gameManager; 20 | 21 | public void parseCommands(Player player, List lines) { 22 | 23 | try { 24 | Matcher match; 25 | int droneIdx = 0; 26 | for (String command : lines) { 27 | boolean found = false; 28 | try { 29 | for (ActionType actionType : ActionType.values()) { 30 | match = actionType.getPattern().matcher(command.trim()); 31 | if (match.matches()) { 32 | Action action; 33 | switch (actionType) { 34 | case MOVE: { 35 | int x = Integer.parseInt(match.group("x")); 36 | int y = Integer.parseInt(match.group("y")); 37 | int light = Integer.parseInt(match.group("light")); 38 | action = new MoveAction(x, y, light); 39 | 40 | Drone drone = player.drones.get(droneIdx); 41 | drone.move = new Vector(x, y); 42 | drone.lightSwitch = light == 1; 43 | matchMessage(drone, match); 44 | break; 45 | } 46 | case WAIT: { 47 | int light = Integer.parseInt(match.group("light")); 48 | action = new WaitAction(light); 49 | Drone drone = player.drones.get(droneIdx); 50 | drone.lightSwitch = light == 1; 51 | matchMessage(drone, match); 52 | break; 53 | } 54 | default: 55 | // Impossibru 56 | action = null; 57 | break; 58 | } 59 | // player.addAction(action); 60 | 61 | found = true; 62 | break; 63 | } 64 | } 65 | } catch (Exception e) { 66 | throw new InvalidInputException(Game.getExpected(command), e.toString()); 67 | } 68 | 69 | if (!found) { 70 | throw new InvalidInputException(Game.getExpected(command), command); 71 | } 72 | droneIdx++; 73 | } 74 | 75 | } catch (InvalidInputException e) { 76 | deactivatePlayer(player, e.getMessage()); 77 | gameManager.addToGameSummary(e.getMessage()); 78 | gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + ": disqualified!")); 79 | } 80 | } 81 | 82 | public void deactivatePlayer(Player player, String message) { 83 | player.deactivate(escapeHTMLEntities(message)); 84 | player.setScore(-1); 85 | } 86 | 87 | private void matchMessage(Drone drone, Matcher match) { 88 | String message = match.group("message"); 89 | if (message != null) { 90 | if (Game.ALLOW_EMOJI) { 91 | drone.setMessage(message); 92 | } else { 93 | String characterFilter = "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]"; 94 | String messageWithoutEmojis = message.replaceAll(characterFilter, ""); 95 | drone.setMessage(messageWithoutEmojis); 96 | } 97 | } 98 | } 99 | 100 | private String escapeHTMLEntities(String message) { 101 | return message 102 | .replace("<", "<") 103 | .replace(">", ">"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Drone.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Drone implements Entity { 7 | 8 | Vector pos; 9 | Vector move; 10 | Vector speed; 11 | 12 | int light; 13 | int battery; 14 | List scans; 15 | List fishesScannedThisTurn; 16 | boolean lightSwitch; 17 | boolean lightOn; 18 | boolean dying; 19 | boolean dead; 20 | boolean didReport; 21 | int id; 22 | double dieAt; 23 | String message; 24 | Player owner; 25 | 26 | /* stats */ 27 | int maxTurnsSpentWithScan; 28 | int turnsSpentWithScan; 29 | int maxY; 30 | 31 | public Drone(double x, int y, int id, Player owner) { 32 | this.id = id; 33 | this.owner = owner; 34 | pos = new Vector(x, y); 35 | battery = Game.DRONE_MAX_BATTERY; 36 | light = 0; 37 | scans = new ArrayList<>(); 38 | lightSwitch = false; 39 | speed = Vector.ZERO; 40 | dying = false; 41 | dead = false; 42 | didReport = false; 43 | lightOn = false; 44 | fishesScannedThisTurn = new ArrayList<>(); 45 | message = ""; 46 | } 47 | 48 | @Override 49 | public Vector getPos() { 50 | return pos; 51 | } 52 | 53 | @Override 54 | public Vector getSpeed() { 55 | return speed; 56 | } 57 | 58 | public boolean isEngineOn() { 59 | return move != null; 60 | } 61 | 62 | public boolean isLightOn() { 63 | return lightOn; 64 | } 65 | 66 | 67 | public void drainBattery() { 68 | battery -= Game.LIGHT_BATTERY_COST; 69 | } 70 | 71 | public void rechargeBattery() { 72 | if (battery < Game.DRONE_MAX_BATTERY) { 73 | battery += Game.DRONE_BATTERY_REGEN; 74 | if (battery >= Game.DRONE_MAX_BATTERY) { 75 | battery = Game.DRONE_MAX_BATTERY; 76 | } 77 | } 78 | 79 | } 80 | 81 | public boolean isDeadOrDying() { 82 | return dying || dead; 83 | } 84 | 85 | public double getX() { 86 | return pos.getX(); 87 | } 88 | 89 | public double getY() { 90 | return pos.getY(); 91 | } 92 | 93 | public String scanSlotToString(int i) { 94 | if (scans.size() > i) { 95 | Scan scan = scans.get(i); 96 | return scan.toInputString(); 97 | } 98 | return "-1 -1"; 99 | } 100 | 101 | public void setMessage(String message) { 102 | this.message = message; 103 | if (message != null && message.length() > 48) { 104 | this.message = message.substring(0, 46) + "..."; 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Entity.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public interface Entity { 4 | Vector getPos(); 5 | 6 | Vector getSpeed(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Fish.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public class Fish implements Entity { 4 | FishType type; 5 | Vector pos; 6 | int color; 7 | double startY; 8 | Vector speed; 9 | int id; 10 | int lowY, highY; 11 | boolean isFleeing; 12 | /* stats */ 13 | Integer fleeingFromPlayer; 14 | 15 | public Fish(double x, double y, FishType type, int color, int id, int lowY, int highY) { 16 | this.id = id; 17 | this.pos = new Vector(x, y); 18 | this.type = type; 19 | this.color = color; 20 | this.lowY = lowY; 21 | this.highY = highY; 22 | this.speed = Vector.ZERO; 23 | } 24 | 25 | @Override 26 | public Vector getPos() { 27 | return pos; 28 | } 29 | 30 | @Override 31 | public Vector getSpeed() { 32 | return speed; 33 | } 34 | 35 | public double getX() { 36 | return pos.getX(); 37 | } 38 | 39 | public double getY() { 40 | return pos.getY(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/FishType.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public enum FishType { 4 | JELLY, FISH, CRAB; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/GameSummaryManager.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.stream.Collectors; 8 | 9 | import com.google.inject.Singleton; 10 | 11 | @Singleton 12 | public class GameSummaryManager { 13 | private List lines; 14 | private Map> playersErrors; 15 | private Map> playersSummary; 16 | 17 | public GameSummaryManager() { 18 | this.lines = new ArrayList<>(); 19 | this.playersErrors = new HashMap<>(); 20 | this.playersSummary = new HashMap<>(); 21 | } 22 | 23 | public String getSummary() { 24 | return toString(); 25 | } 26 | 27 | public void clear() { 28 | this.lines.clear(); 29 | this.playersErrors.clear(); 30 | this.playersSummary.clear(); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return formatErrors() + "\n" + formatSummary() + "\n" + lines.stream().collect(Collectors.joining("\n")); 36 | } 37 | 38 | public void addError(Player player, String error) { 39 | String key = player.getNicknameToken(); 40 | if (!playersErrors.containsKey(key)) { 41 | playersErrors.put(key, new ArrayList()); 42 | } 43 | playersErrors.get(key).add(error); 44 | } 45 | 46 | public void addPlayerSummary(String key, String summary) { 47 | if (!playersSummary.containsKey(key)) { 48 | playersSummary.put(key, new ArrayList()); 49 | } 50 | playersSummary.get(key).add(summary); 51 | } 52 | 53 | private String formatErrors() { 54 | return playersErrors.entrySet().stream().map(errorMap -> { 55 | List errors = errorMap.getValue(); 56 | String additionnalErrorsMessage = errors.size() > 1 ? " + " + (errors.size() - 1) + " other error" + (errors.size() > 2 ? "s" : "") : ""; 57 | return errorMap.getKey() + ": " + errors.get(0) + additionnalErrorsMessage; 58 | }).collect(Collectors.joining("\n")); 59 | } 60 | 61 | public String formatSummary() { 62 | return playersSummary.entrySet().stream().flatMap(summaryMap -> { 63 | return summaryMap.getValue().stream(); 64 | }).collect(Collectors.joining("\n")); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Player.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | import com.codingame.gameengine.core.AbstractMultiplayerPlayer; 9 | 10 | public class Player extends AbstractMultiplayerPlayer { 11 | 12 | String message; 13 | List drones; 14 | Set scans; 15 | Set visibleFishes; 16 | 17 | List countFishSaved; 18 | int points = 0; 19 | 20 | public Player() { 21 | drones = new ArrayList<>(); 22 | visibleFishes = new LinkedHashSet<>(); 23 | scans = new LinkedHashSet<>(); 24 | countFishSaved = new ArrayList(); 25 | } 26 | 27 | @Override 28 | public int getExpectedOutputLines() { 29 | return drones.size(); 30 | } 31 | 32 | public String getMessage() { 33 | return message; 34 | } 35 | 36 | public void reset() { 37 | message = null; 38 | drones.forEach(d -> { 39 | d.move = null; 40 | d.fishesScannedThisTurn.clear(); 41 | d.didReport = false; 42 | d.message = ""; 43 | }); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Referee.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import com.codingame.gameengine.core.AbstractPlayer.TimeoutException; 4 | import com.codingame.gameengine.core.AbstractReferee; 5 | import com.codingame.gameengine.core.MultiplayerGameManager; 6 | import com.google.inject.Inject; 7 | import com.google.inject.Singleton; 8 | 9 | @Singleton 10 | public class Referee extends AbstractReferee { 11 | 12 | @Inject private MultiplayerGameManager gameManager; 13 | @Inject private CommandManager commandManager; 14 | @Inject private Game game; 15 | 16 | @Override 17 | public void init() { 18 | try { 19 | 20 | int leagueLevel = gameManager.getLeagueLevel(); 21 | if (leagueLevel == 1) { 22 | Game.ENABLE_UGLIES = false; 23 | Game.FISH_WILL_FLEE = false; 24 | Game.DRONES_PER_PLAYER = 1; 25 | Game.SIMPLE_SCANS = true; 26 | Game.FISH_WILL_MOVE = true; 27 | } else if (leagueLevel == 2) { 28 | Game.ENABLE_UGLIES = false; 29 | Game.FISH_WILL_FLEE = false; 30 | Game.DRONES_PER_PLAYER = 1; 31 | } else if (leagueLevel == 3) { 32 | Game.ENABLE_UGLIES = false; 33 | } else { 34 | 35 | } 36 | 37 | game.init(); 38 | sendGlobalInfo(); 39 | 40 | gameManager.setFrameDuration(500); 41 | gameManager.setMaxTurns(Game.MAX_TURNS); 42 | gameManager.setTurnMaxTime(50); 43 | gameManager.setFirstTurnMaxTime(1000); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | System.err.println("Referee failed to initialize"); 47 | abort(); 48 | } 49 | } 50 | 51 | private void abort() { 52 | gameManager.endGame(); 53 | 54 | } 55 | 56 | private void sendGlobalInfo() { 57 | // Give input to players 58 | for (Player player : gameManager.getActivePlayers()) { 59 | for (String line : Serializer.serializeGlobalInfoFor(player, game)) { 60 | player.sendInputLine(line); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void gameTurn(int turn) { 67 | game.resetGameTurnData(); 68 | 69 | // Give input to players 70 | for (Player player : gameManager.getActivePlayers()) { 71 | for (String line : Serializer.serializeFrameInfoFor(player, game)) { 72 | player.sendInputLine(line); 73 | } 74 | player.execute(); 75 | } 76 | // Get output from players 77 | handlePlayerCommands(); 78 | 79 | game.performGameUpdate(turn); 80 | 81 | if (gameManager.getActivePlayers().size() < 2) { 82 | abort(); 83 | } 84 | } 85 | 86 | private void handlePlayerCommands() { 87 | 88 | for (Player player : gameManager.getActivePlayers()) { 89 | try { 90 | commandManager.parseCommands(player, player.getOutputs()); 91 | } catch (TimeoutException e) { 92 | player.deactivate("Timeout!"); 93 | gameManager.addToGameSummary(player.getNicknameToken() + " has not provided " + player.getExpectedOutputLines() + " lines in time"); 94 | } 95 | } 96 | 97 | } 98 | 99 | @Override 100 | public void onEnd() { 101 | game.onEnd(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Scan.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.Objects; 4 | 5 | public class Scan { 6 | int fishId; 7 | FishType type; 8 | int color; 9 | 10 | public Scan(Fish fish) { 11 | this.fishId = fish.id; 12 | this.type = fish.type; 13 | this.color = fish.color; 14 | } 15 | 16 | public Scan(FishType type, int color) { 17 | this.type = type; 18 | this.color = color; 19 | } 20 | 21 | @Override 22 | public int hashCode() { 23 | return Objects.hash(color, type); 24 | } 25 | 26 | @Override 27 | public boolean equals(Object obj) { 28 | if (this == obj) return true; 29 | if (obj == null) return false; 30 | if (getClass() != obj.getClass()) return false; 31 | Scan other = (Scan) obj; 32 | return color == other.color && type == other.type; 33 | } 34 | 35 | public String toInputString() { 36 | return Serializer.join(fishId); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return String.format( 42 | "%s (%d) %s (%d)", 43 | Game.COLORS[color], 44 | color, 45 | type.name().toLowerCase(), 46 | type.ordinal() 47 | ); 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.function.Function; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | public class Serializer { 11 | public static final String MAIN_SEPARATOR = ";"; 12 | 13 | static public String serialize(List list) { 14 | return list.stream().map(String::valueOf).collect(Collectors.joining(" ")); 15 | } 16 | 17 | static public String serialize(Vector coord) { 18 | return coord.getX() + " " + coord.getY(); 19 | } 20 | 21 | static public String join(Object... args) { 22 | return Stream.of(args) 23 | .map(String::valueOf) 24 | .collect(Collectors.joining(" ")); 25 | } 26 | 27 | public static String serializeGlobalData(Game game) { 28 | List lines = new ArrayList<>(); 29 | lines.add(Game.FISH_WILL_FLEE ? 1 : 0); 30 | lines.add(Game.WIDTH); 31 | lines.add(Game.HEIGHT); 32 | lines.add(Game.DRONES_PER_PLAYER); 33 | lines.add(game.fishes.size()); 34 | 35 | for (Fish fish : game.fishes) { 36 | lines.add(join(fish.id, fish.color, fish.type.ordinal())); 37 | } 38 | lines.add(game.uglies.size()); 39 | for (Ugly ugly : game.uglies) { 40 | lines.add(join(ugly.id)); 41 | } 42 | lines.add(Game.SIMPLE_SCANS ? 1 : 0); 43 | 44 | return lines.stream() 45 | .map(String::valueOf) 46 | .collect(Collectors.joining(MAIN_SEPARATOR)); 47 | } 48 | 49 | public static String serializeFrameData(Game game) { 50 | List lines = new ArrayList<>(); 51 | lines.add(game.fishes.size()); 52 | for (Fish fish : game.fishes) { 53 | lines.add(join(fish.id, fish.getPos().toIntString(), fish.getSpeed().toIntString(), fish.isFleeing ? 1 : 0)); 54 | } 55 | 56 | for (Ugly ugly : game.uglies) { 57 | lines.add(join(ugly.id, ugly.getPos().toIntString(), ugly.getSpeed().toIntString(), ugly.foundTarget ? 1 : 0)); 58 | } 59 | for (Player player : game.players) { 60 | lines.add(player.points); 61 | for (Drone drone : player.drones) { 62 | lines.add( 63 | join( 64 | drone.id, 65 | drone.pos.toIntString(), 66 | drone.move == null ? "x x" : drone.move.toIntString(), 67 | drone.battery, 68 | drone.isLightOn() ? 1 : 0, 69 | drone.dead ? 1 : 0, 70 | (int) (drone.dieAt * 100), 71 | drone.didReport ? 1 : 0, 72 | drone.message 73 | ) 74 | ); 75 | lines.add(drone.fishesScannedThisTurn.size()); 76 | for (int id : drone.fishesScannedThisTurn) { 77 | lines.add(id); 78 | } 79 | 80 | lines.add(drone.scans.size()); 81 | for (Scan scan : drone.scans) { 82 | lines.add( 83 | join( 84 | scan.color, scan.type.ordinal() 85 | ) 86 | ); 87 | } 88 | } 89 | lines.add(getScanSummary(player, game)); 90 | } 91 | return lines.stream() 92 | .map(String::valueOf) 93 | .collect(Collectors.joining(MAIN_SEPARATOR)); 94 | } 95 | 96 | private static final int SCAN_CODE_UNSCANNED = 0; 97 | private static final int SCAN_CODE_BUFFERED = 1; 98 | private static final int SCAN_CODE_SAVED = 2; 99 | private static final int SCAN_CODE_LOST = 3; 100 | 101 | public static String getScanSummary(Player player, Game game) { 102 | List summary = new ArrayList<>(19); 103 | for (int col = 0; col < Game.COLORS_PER_FISH; col++) { 104 | for (FishType type : FishType.values()) { 105 | Scan scan = new Scan(type, col); 106 | summary.add(getScanSummaryCode(player, scan, game)); 107 | summary.add(game.hasFirstToScanBonus(player, scan) ? "@" : "_"); 108 | } 109 | summary.add(game.hasFirstToScanAllFishOfColor(player, col) ? "@" : "_"); 110 | } 111 | 112 | for (FishType type : FishType.values()) { 113 | summary.add(game.hasFirstToScanAllFishOfType(player, type) ? "@" : "_"); 114 | } 115 | 116 | return summary.stream() 117 | .map(String::valueOf) 118 | .collect(Collectors.joining(" ")); 119 | } 120 | 121 | private static int getScanSummaryCode(Player player, Scan scan, Game game) { 122 | boolean saved = game.playerScanned(player, scan); 123 | if (saved) { 124 | return SCAN_CODE_SAVED; 125 | } 126 | 127 | boolean buffered = player.drones.stream() 128 | .flatMap(d -> d.scans.stream()) 129 | .anyMatch(scanned -> scanned.equals(scan)); 130 | if (buffered) { 131 | return SCAN_CODE_BUFFERED; 132 | } 133 | 134 | Optional optFish = game.fishes.stream().filter(fish -> new Scan(fish).equals(scan)).findFirst(); 135 | if (!optFish.isPresent()) { 136 | return SCAN_CODE_LOST; 137 | } 138 | 139 | return SCAN_CODE_UNSCANNED; 140 | } 141 | 142 | public static List serializeGlobalInfoFor(Player player, Game game) { 143 | List lines = new ArrayList<>(); 144 | 145 | // Preliminary scan (all possible fish) 146 | lines.add(game.fishes.size() + game.uglies.size()); 147 | game.fishes 148 | .forEach(fish -> { 149 | lines.add( 150 | Serializer.join(fish.id, fish.color, fish.type.ordinal()) 151 | ); 152 | }); 153 | game.uglies.forEach(ugly -> { 154 | lines.add( 155 | Serializer.join(ugly.id, -1, -1) 156 | ); 157 | }); 158 | 159 | return lines.stream() 160 | .map(String::valueOf) 161 | .collect(Collectors.toList()); 162 | } 163 | 164 | public static List serializeFrameInfoFor(Player player, Game game) { 165 | List lines = new ArrayList<>(); 166 | 167 | // Player scores 168 | 169 | List players = game.players; 170 | List uglies = game.uglies; 171 | List fishes = game.fishes; 172 | 173 | Player other = players 174 | .stream() 175 | .filter(p -> p != player) 176 | .findFirst().get(); 177 | 178 | lines.add(String.valueOf(player.points)); 179 | lines.add(String.valueOf(other.points)); 180 | 181 | Stream.of(player, other) 182 | .forEach(p -> { 183 | lines.add(String.valueOf(p.scans.size())); 184 | for (Scan scan : p.scans) { 185 | lines.add(scan.toInputString()); 186 | } 187 | }); 188 | 189 | List allDrones = Stream.of(player.drones.stream(), other.drones.stream()) 190 | .flatMap(Function.identity()) 191 | .collect(Collectors.toList()); 192 | 193 | // Drone states 194 | lines.add(String.valueOf(player.drones.size())); 195 | player.drones.forEach(drone -> { 196 | lines.add( 197 | Serializer.join( 198 | drone.id, 199 | drone.getPos().toIntString(), 200 | drone.dead ? 1 : 0, 201 | drone.battery 202 | ) 203 | ); 204 | }); 205 | lines.add(String.valueOf(other.drones.size())); 206 | other.drones.forEach(drone -> { 207 | lines.add( 208 | Serializer.join( 209 | drone.id, 210 | drone.getPos().toIntString(), 211 | drone.dead ? 1 : 0, 212 | drone.battery 213 | ) 214 | ); 215 | }); 216 | 217 | // Drone scans 218 | lines.add( 219 | String.valueOf(allDrones.stream().mapToInt(drone -> drone.scans.size()).sum()) 220 | ); 221 | 222 | allDrones.forEach(drone -> { 223 | drone.scans.forEach(scan -> { 224 | lines.add( 225 | Serializer.join( 226 | drone.id, 227 | scan.fishId 228 | ) 229 | ); 230 | }); 231 | }); 232 | 233 | List visibleUglies = uglies.stream() 234 | .filter( 235 | ugly -> player.drones.stream() 236 | .anyMatch( 237 | drone -> ugly.pos.inRange(drone.pos, Game.UGLY_EAT_RANGE + (drone.isLightOn() ? Game.LIGHT_SCAN_RANGE : Game.DARK_SCAN_RANGE)) 238 | ) 239 | ) 240 | .collect(Collectors.toList()); 241 | 242 | // Visible fish 243 | lines.add(String.valueOf(player.visibleFishes.size() + visibleUglies.size())); 244 | 245 | player.visibleFishes.stream() 246 | .forEach(fish -> { 247 | lines.add( 248 | Serializer.join(fish.id, fish.getPos().toIntString(), fish.speed.toIntString()) 249 | ); 250 | }); 251 | visibleUglies.forEach(ugly -> { 252 | lines.add( 253 | Serializer.join(ugly.id, ugly.getPos().toIntString(), ugly.speed.toIntString()) 254 | ); 255 | }); 256 | 257 | // Radar blips 258 | lines.add(String.valueOf(Game.DRONES_PER_PLAYER * (fishes.size() + uglies.size()))); 259 | 260 | for (Drone drone : player.drones) { 261 | fishes.stream() 262 | .forEach(fish -> { 263 | String direction = ""; 264 | if (fish.getY() > drone.getY()) { 265 | direction += "B"; 266 | } else { 267 | direction += "T"; 268 | } 269 | if (fish.getX() > drone.getX()) { 270 | direction += "R"; 271 | } else { 272 | direction += "L"; 273 | } 274 | 275 | lines.add(Serializer.join(drone.id, fish.id, direction)); 276 | }); 277 | uglies.stream() 278 | .forEach(ugly -> { 279 | String direction = ""; 280 | if (ugly.getY() > drone.getY()) { 281 | direction += "B"; 282 | } else { 283 | direction += "T"; 284 | } 285 | if (ugly.getX() > drone.getX()) { 286 | direction += "R"; 287 | } else { 288 | direction += "L"; 289 | } 290 | 291 | lines.add(Serializer.join(drone.id, ugly.id, direction)); 292 | }); 293 | } 294 | 295 | return lines.stream() 296 | .collect(Collectors.toList()); 297 | } 298 | 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Ugly.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public class Ugly implements Entity { 4 | Vector pos; 5 | Vector speed; 6 | Vector target; 7 | int id; 8 | boolean foundTarget; 9 | 10 | public Ugly(double x, double y, int id) { 11 | this.id = id; 12 | this.pos = new Vector(x, y); 13 | this.speed = Vector.ZERO; 14 | this.target = null; 15 | } 16 | 17 | @Override 18 | public Vector getPos() { 19 | return pos; 20 | } 21 | 22 | @Override 23 | public Vector getSpeed() { 24 | return speed; 25 | } 26 | 27 | public double getX() { 28 | return pos.getX(); 29 | } 30 | 31 | public double getY() { 32 | return pos.getY(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Vector.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | public class Vector { 4 | public static final Vector ZERO = new Vector(0, 0); 5 | private final double x, y; 6 | 7 | @Override 8 | public int hashCode() { 9 | final int prime = 31; 10 | int result = 1; 11 | long temp; 12 | temp = Double.doubleToLongBits(x); 13 | result = prime * result + (int) (temp ^ (temp >>> 32)); 14 | temp = Double.doubleToLongBits(y); 15 | result = prime * result + (int) (temp ^ (temp >>> 32)); 16 | return result; 17 | } 18 | 19 | @Override 20 | public boolean equals(Object obj) { 21 | if (this == obj) return true; 22 | if (obj == null) return false; 23 | if (getClass() != obj.getClass()) return false; 24 | Vector other = (Vector) obj; 25 | if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) return false; 26 | if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) return false; 27 | return true; 28 | } 29 | 30 | public Vector(double x, double y) { 31 | this.x = x; 32 | this.y = y; 33 | } 34 | 35 | public Vector(Vector a, Vector b) { 36 | this.x = b.x - a.x; 37 | this.y = b.y - a.y; 38 | } 39 | 40 | public Vector(double angle) { 41 | this.x = Math.cos(angle); 42 | this.y = Math.sin(angle); 43 | } 44 | 45 | public Vector rotate(double angle) { 46 | double nx = (x * Math.cos(angle)) - (y * Math.sin(angle)); 47 | double ny = (x * Math.sin(angle)) + (y * Math.cos(angle)); 48 | 49 | return new Vector(nx, ny); 50 | }; 51 | 52 | public boolean equals(Vector v) { 53 | return v.getX() == x && v.getY() == y; 54 | } 55 | 56 | public Vector round() { 57 | return new Vector((int) Math.round(this.x), (int) Math.round(this.y)); 58 | } 59 | 60 | public Vector truncate() { 61 | return new Vector((int) this.x, (int) this.y); 62 | } 63 | 64 | public double getX() { 65 | return x; 66 | } 67 | 68 | public double getY() { 69 | return y; 70 | } 71 | 72 | public double distance(Vector v) { 73 | return Math.sqrt((v.x - x) * (v.x - x) + (v.y - y) * (v.y - y)); 74 | } 75 | 76 | public boolean inRange(Vector v, double range) { 77 | return (v.x - x) * (v.x - x) + (v.y - y) * (v.y - y) <= range * range; 78 | } 79 | 80 | public Vector add(Vector v) { 81 | return new Vector(x + v.x, y + v.y); 82 | } 83 | 84 | public Vector mult(double factor) { 85 | return new Vector(x * factor, y * factor); 86 | } 87 | 88 | public Vector sub(Vector v) { 89 | return new Vector(this.x - v.x, this.y - v.y); 90 | } 91 | 92 | public double length() { 93 | return Math.sqrt(x * x + y * y); 94 | } 95 | 96 | public double lengthSquared() { 97 | return x * x + y * y; 98 | } 99 | 100 | public Vector normalize() { 101 | double length = length(); 102 | if (length == 0) 103 | return new Vector(0, 0); 104 | return new Vector(x / length, y / length); 105 | } 106 | 107 | public double dot(Vector v) { 108 | return x * v.x + y * v.y; 109 | } 110 | 111 | public double angle() { 112 | return Math.atan2(y, x); 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | return "[" + x + ", " + y + "]"; 118 | } 119 | 120 | public String toIntString() { 121 | return (int) x + " " + (int) y; 122 | } 123 | 124 | public Vector project(Vector force) { 125 | Vector normalize = this.normalize(); 126 | return normalize.mult(normalize.dot(force)); 127 | } 128 | 129 | public final Vector cross(double s) { 130 | return new Vector(-s * y, s * x); 131 | } 132 | 133 | public Vector hsymmetric(double center) { 134 | return new Vector(2 * center - this.x, this.y); 135 | } 136 | 137 | public Vector vsymmetric(double center) { 138 | return new Vector(this.x, 2 * center - this.y); 139 | } 140 | 141 | public Vector vsymmetric() { 142 | return new Vector(this.x, -this.y); 143 | } 144 | 145 | public Vector hsymmetric() { 146 | return new Vector(-this.x, this.y); 147 | } 148 | 149 | public Vector symmetric() { 150 | return symmetric(new Vector(0, 0)); 151 | } 152 | 153 | public Vector symmetric(Vector center) { 154 | return new Vector(center.x * 2 - this.x, center.y * 2 - this.y); 155 | } 156 | 157 | public boolean withinBounds(double minx, double miny, double maxx, double maxy) { 158 | return x >= minx && x < maxx && y >= miny && y < maxy; 159 | } 160 | 161 | public boolean isZero() { 162 | return x == 0 && y == 0; 163 | } 164 | 165 | public Vector symmetricTruncate(Vector origin) { 166 | return sub(origin).truncate().add(origin); 167 | } 168 | 169 | public Vector symmetricTruncate() { 170 | return new Vector( 171 | (x < Game.CENTER.x) ? Math.floor(x) : Math.ceil(x), 172 | (y < Game.CENTER.y) ? Math.floor(y) : Math.ceil(y) 173 | ); 174 | } 175 | 176 | public double euclideanTo(double x, double y) { 177 | return Math.sqrt(sqrEuclideanTo(x, y)); 178 | } 179 | 180 | public double sqrEuclideanTo(double x, double y) { 181 | return Math.pow(x - this.x, 2) + Math.pow(y - this.y, 2); 182 | } 183 | 184 | public double sqrEuclideanTo(Vector other) { 185 | return sqrEuclideanTo(other.x, other.y); 186 | } 187 | 188 | public Vector add(double x, double y) { 189 | return new Vector(this.x + x, this.y + y); 190 | } 191 | 192 | public double manhattanTo(Vector other) { 193 | return manhattanTo(other.x, other.y); 194 | } 195 | 196 | public double chebyshevTo(double x, double y) { 197 | return Math.max(Math.abs(x - this.x), Math.abs(y - this.y)); 198 | } 199 | 200 | public double manhattanTo(double x, double y) { 201 | return Math.abs(x - this.x) + Math.abs(y - this.y); 202 | } 203 | 204 | public double euclideanTo(Vector pos) { 205 | return euclideanTo(pos.x, pos.y); 206 | } 207 | 208 | public Vector epsilonRound() { 209 | return new Vector( 210 | Math.round(x * 10000000.0) / 10000000.0, 211 | Math.round(y * 10000000.0) / 10000000.0 212 | ); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/Action.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public class Action { 4 | final ActionType type; 5 | private String message; 6 | 7 | public Action(ActionType type) { 8 | this.type = type; 9 | } 10 | 11 | public ActionType getType() { 12 | return type; 13 | } 14 | 15 | public String getMessage() { 16 | return message; 17 | } 18 | 19 | public void setMessage(String message) { 20 | this.message = message; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/ActionException.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | @SuppressWarnings("serial") 4 | public class ActionException extends Exception { 5 | 6 | public ActionException(String message) { 7 | super(message); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/ActionType.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | public enum ActionType { 6 | 7 | MOVE( 8 | "^MOVE (?-?\\d+) (?-?\\d+) (?1|0)(?:\\s+(?.+))?" 9 | ), 10 | WAIT( 11 | "^WAIT (?1|0)(?:\\s+(?.+))?" 12 | ); 13 | 14 | private Pattern pattern; 15 | 16 | ActionType(String pattern) { 17 | this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); 18 | } 19 | 20 | public Pattern getPattern() { 21 | return pattern; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/MoveAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public class MoveAction extends Action { 4 | 5 | private String message; 6 | int x, y, light; 7 | 8 | public MoveAction(int x, int y, int light) { 9 | super(ActionType.MOVE); 10 | this.x = x; 11 | this.y = y; 12 | this.light = light; 13 | } 14 | 15 | public String getMessage() { 16 | return message; 17 | } 18 | 19 | public void setMessage(String message) { 20 | this.message = message; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/WaitAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public class WaitAction extends Action { 4 | 5 | private String message; 6 | int light; 7 | 8 | public WaitAction(int light) { 9 | super(ActionType.WAIT); 10 | this.light = light; 11 | } 12 | 13 | public String getMessage() { 14 | return message; 15 | } 16 | 17 | public void setMessage(String message) { 18 | this.message = message; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/exception/GameException.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class GameException extends Exception { 5 | 6 | public GameException(String string) { 7 | super(string); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/exception/InvalidInputException.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class InvalidInputException extends Exception { 5 | private final String expected; 6 | private final String got; 7 | 8 | public InvalidInputException(String expected, String got) { 9 | super("Invalid Input: Expected " + expected + " but got '" + got + "'"); 10 | this.expected = expected; 11 | this.got = got; 12 | } 13 | 14 | public String getExpected() { 15 | return expected; 16 | } 17 | 18 | public String getGot() { 19 | return got; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/FrameViewData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.List; 4 | 5 | import com.codingame.event.EventData; 6 | 7 | public class FrameViewData { 8 | // public List recyclers; 9 | public List players; 10 | public List events; 11 | 12 | } -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/GameDataProvider.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.stream.Collectors; 4 | 5 | import com.codingame.game.Game; 6 | import com.codingame.game.Player; 7 | import com.codingame.gameengine.core.MultiplayerGameManager; 8 | import com.google.inject.Inject; 9 | import com.google.inject.Singleton; 10 | 11 | @Singleton 12 | public class GameDataProvider { 13 | @Inject private Game game; 14 | @Inject private MultiplayerGameManager gameManager; 15 | 16 | public GlobalViewData getGlobalData() { 17 | GlobalViewData data = new GlobalViewData(); 18 | data.width = 16000; 19 | data.height = 900; 20 | 21 | return data; 22 | } 23 | 24 | public FrameViewData getCurrentFrameData() { 25 | FrameViewData data = new FrameViewData(); 26 | 27 | data.players = gameManager.getPlayers().stream() 28 | .map(PlayerDto::new) 29 | .collect(Collectors.toList()); 30 | 31 | data.events = game.getViewerEvents(); 32 | 33 | return data; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/GlobalViewData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.List; 4 | 5 | public class GlobalViewData { 6 | 7 | public int width; 8 | public int height; 9 | public int fishCount; 10 | public int uglyCount; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/PlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import com.codingame.game.Player; 4 | 5 | public class PlayerDto { 6 | String message; 7 | 8 | public PlayerDto(Player player) { 9 | message = player.getMessage(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/ViewModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import com.codingame.game.Game; 4 | import com.codingame.game.Serializer; 5 | import com.codingame.gameengine.core.AbstractPlayer; 6 | import com.codingame.gameengine.core.GameManager; 7 | import com.codingame.gameengine.core.Module; 8 | import com.google.inject.Inject; 9 | import com.google.inject.Singleton; 10 | 11 | @Singleton 12 | public class ViewModule implements Module { 13 | 14 | private GameManager gameManager; 15 | // private GameDataProvider gameDataProvider; 16 | private Game game; 17 | 18 | @Inject 19 | ViewModule(GameManager gameManager, Game game) { 20 | this.gameManager = gameManager; 21 | this.game = game; 22 | gameManager.registerModule(this); 23 | } 24 | 25 | @Override 26 | public final void onGameInit() { 27 | sendGlobalData(); 28 | sendFrameData(); 29 | } 30 | 31 | private void sendFrameData() { 32 | gameManager.setViewData("graphics", Serializer.serializeFrameData(game)); 33 | } 34 | 35 | private void sendGlobalData() { 36 | gameManager.setViewGlobalData("graphics", Serializer.serializeGlobalData(game)); 37 | 38 | } 39 | 40 | @Override 41 | public final void onAfterGameTurn() { 42 | sendFrameData(); 43 | } 44 | 45 | @Override 46 | public final void onAfterOnEnd() { 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/view/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | jest: true 6 | }, 7 | extends: [ 8 | 'standard', 'standard-jsx' 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly' 13 | }, 14 | parser: '@typescript-eslint/parser', 15 | parserOptions: { 16 | ecmaFeatures: { 17 | jsx: true 18 | }, 19 | ecmaVersion: 2018, 20 | sourceType: 'module', 21 | project: './tsconfig.json' 22 | }, 23 | plugins: [ 24 | '@typescript-eslint' 25 | ], 26 | rules: { 27 | '@typescript-eslint/member-delimiter-style': ['error', { multiline: { delimiter: 'none' }, singleline: { delimiter: 'comma' } }], 28 | '@typescript-eslint/no-misused-promises': ['error', { checksVoidReturn: false }], 29 | '@typescript-eslint/no-useless-constructor': 'error', 30 | '@typescript-eslint/type-annotation-spacing': 'error', 31 | 'no-unused-vars': 'off', 32 | 'no-undef': 'off', 33 | 'no-useless-constructor': 'off' 34 | }, 35 | ignorePatterns: ['.eslintrc.js', 'config.js', 'demo.js'] 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/view/assets/Background-derriere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/Background-derriere.jpg -------------------------------------------------------------------------------- /src/main/resources/view/assets/Background-devant-D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/Background-devant-D.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/Background-devant-G.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/Background-devant-G.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/Background-plantes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/Background-plantes.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/blue.json: -------------------------------------------------------------------------------- 1 | {"frames":{"crabe4anime0001":{"frame":{"x":0,"y":0,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0003":{"frame":{"x":0,"y":85,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0005":{"frame":{"x":105,"y":0,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0007":{"frame":{"x":105,"y":85,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0009":{"frame":{"x":0,"y":170,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0011":{"frame":{"x":105,"y":170,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0013":{"frame":{"x":210,"y":0,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0015":{"frame":{"x":210,"y":85,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0017":{"frame":{"x":210,"y":170,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0019":{"frame":{"x":0,"y":255,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0021":{"frame":{"x":105,"y":255,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0023":{"frame":{"x":210,"y":255,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0025":{"frame":{"x":315,"y":0,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0027":{"frame":{"x":315,"y":85,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0029":{"frame":{"x":315,"y":170,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0031":{"frame":{"x":315,"y":255,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0033":{"frame":{"x":0,"y":340,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0035":{"frame":{"x":105,"y":340,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0037":{"frame":{"x":210,"y":340,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0039":{"frame":{"x":315,"y":340,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0041":{"frame":{"x":0,"y":425,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0043":{"frame":{"x":105,"y":425,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"crabe4anime0045":{"frame":{"x":210,"y":425,"w":104,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":84},"sourceSize":{"w":104,"h":84}},"pieuvre4anime0001":{"frame":{"x":514,"y":440,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0003":{"frame":{"x":0,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0005":{"frame":{"x":63,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0007":{"frame":{"x":126,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0009":{"frame":{"x":189,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0011":{"frame":{"x":252,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0013":{"frame":{"x":315,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0015":{"frame":{"x":378,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0017":{"frame":{"x":441,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0019":{"frame":{"x":504,"y":565,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0021":{"frame":{"x":608,"y":0,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0023":{"frame":{"x":608,"y":68,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0025":{"frame":{"x":608,"y":136,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0027":{"frame":{"x":608,"y":204,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0029":{"frame":{"x":608,"y":272,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0031":{"frame":{"x":608,"y":340,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0033":{"frame":{"x":608,"y":408,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0035":{"frame":{"x":608,"y":476,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0037":{"frame":{"x":608,"y":544,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0039":{"frame":{"x":0,"y":633,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0041":{"frame":{"x":63,"y":633,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0043":{"frame":{"x":126,"y":633,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"pieuvre4anime0045":{"frame":{"x":189,"y":633,"w":62,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":62,"h":67},"sourceSize":{"w":62,"h":67}},"poisson4anime0001":{"frame":{"x":315,"y":425,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0003":{"frame":{"x":420,"y":0,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0005":{"frame":{"x":420,"y":55,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0007":{"frame":{"x":420,"y":110,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0009":{"frame":{"x":420,"y":165,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0011":{"frame":{"x":420,"y":220,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0013":{"frame":{"x":420,"y":275,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0015":{"frame":{"x":420,"y":330,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0017":{"frame":{"x":420,"y":385,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0019":{"frame":{"x":420,"y":440,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0021":{"frame":{"x":0,"y":510,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0023":{"frame":{"x":94,"y":510,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0025":{"frame":{"x":188,"y":510,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0027":{"frame":{"x":282,"y":510,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0029":{"frame":{"x":376,"y":510,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0031":{"frame":{"x":514,"y":0,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0033":{"frame":{"x":514,"y":55,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0035":{"frame":{"x":514,"y":110,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0037":{"frame":{"x":514,"y":165,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0039":{"frame":{"x":514,"y":220,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0041":{"frame":{"x":514,"y":275,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0043":{"frame":{"x":514,"y":330,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}},"poisson4anime0045":{"frame":{"x":514,"y":385,"w":93,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":93,"h":54},"sourceSize":{"w":93,"h":54}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"blue.png","size":{"w":670,"h":700},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/blue.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/dial_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/dial_arrow.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/dial_mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/dial_mid.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/dial_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/dial_side.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/disc_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/disc_black.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/feedback.json: -------------------------------------------------------------------------------- 1 | {"frames":{"FeedBack_Scan0003":{"frame":{"x":0,"y":0,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0005":{"frame":{"x":0,"y":108,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0007":{"frame":{"x":112,"y":0,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0009":{"frame":{"x":112,"y":108,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0011":{"frame":{"x":0,"y":216,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0013":{"frame":{"x":112,"y":216,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0015":{"frame":{"x":224,"y":0,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0017":{"frame":{"x":224,"y":108,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0019":{"frame":{"x":224,"y":216,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0021":{"frame":{"x":0,"y":324,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0023":{"frame":{"x":112,"y":324,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0025":{"frame":{"x":224,"y":324,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0027":{"frame":{"x":336,"y":0,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0029":{"frame":{"x":336,"y":108,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0031":{"frame":{"x":336,"y":216,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0033":{"frame":{"x":336,"y":324,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0035":{"frame":{"x":0,"y":432,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0037":{"frame":{"x":112,"y":432,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0039":{"frame":{"x":224,"y":432,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0041":{"frame":{"x":336,"y":432,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0043":{"frame":{"x":448,"y":0,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0045":{"frame":{"x":448,"y":108,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0047":{"frame":{"x":448,"y":216,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0049":{"frame":{"x":448,"y":324,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0051":{"frame":{"x":448,"y":432,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0053":{"frame":{"x":0,"y":540,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0055":{"frame":{"x":112,"y":540,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}},"FeedBack_Scan0057":{"frame":{"x":224,"y":540,"w":111,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":111,"h":107},"sourceSize":{"w":111,"h":107}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"feedback.png","size":{"w":559,"h":647},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/feedback.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/green.json: -------------------------------------------------------------------------------- 1 | {"frames":{"crabe3anime0001":{"frame":{"x":0,"y":0,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0003":{"frame":{"x":0,"y":46,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0005":{"frame":{"x":0,"y":92,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0007":{"frame":{"x":0,"y":138,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0009":{"frame":{"x":92,"y":0,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0011":{"frame":{"x":92,"y":46,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0013":{"frame":{"x":92,"y":92,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0015":{"frame":{"x":92,"y":138,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0017":{"frame":{"x":0,"y":184,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0019":{"frame":{"x":92,"y":184,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0021":{"frame":{"x":0,"y":230,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0023":{"frame":{"x":92,"y":230,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0025":{"frame":{"x":184,"y":0,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0027":{"frame":{"x":184,"y":46,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0029":{"frame":{"x":184,"y":92,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0031":{"frame":{"x":184,"y":138,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0033":{"frame":{"x":184,"y":184,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0035":{"frame":{"x":184,"y":230,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0037":{"frame":{"x":0,"y":276,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0039":{"frame":{"x":92,"y":276,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0041":{"frame":{"x":184,"y":276,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0043":{"frame":{"x":0,"y":322,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"crabe3anime0045":{"frame":{"x":92,"y":322,"w":91,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":91,"h":45},"sourceSize":{"w":91,"h":45}},"pieuvre3anime0001":{"frame":{"x":276,"y":0,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0003":{"frame":{"x":276,"y":67,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0005":{"frame":{"x":276,"y":134,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0007":{"frame":{"x":276,"y":201,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0009":{"frame":{"x":276,"y":268,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0011":{"frame":{"x":352,"y":0,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0013":{"frame":{"x":352,"y":67,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0015":{"frame":{"x":352,"y":134,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0017":{"frame":{"x":352,"y":201,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0019":{"frame":{"x":352,"y":268,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0021":{"frame":{"x":0,"y":368,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0023":{"frame":{"x":76,"y":368,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0025":{"frame":{"x":152,"y":368,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0027":{"frame":{"x":228,"y":368,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0029":{"frame":{"x":304,"y":368,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0031":{"frame":{"x":0,"y":435,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0033":{"frame":{"x":76,"y":435,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0035":{"frame":{"x":152,"y":435,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0037":{"frame":{"x":228,"y":435,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0039":{"frame":{"x":304,"y":435,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0041":{"frame":{"x":428,"y":0,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0043":{"frame":{"x":428,"y":67,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"pieuvre3anime0045":{"frame":{"x":428,"y":134,"w":75,"h":66},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":66},"sourceSize":{"w":75,"h":66}},"poisson3anime0001":{"frame":{"x":428,"y":201,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0003":{"frame":{"x":428,"y":273,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0005":{"frame":{"x":428,"y":345,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0007":{"frame":{"x":428,"y":417,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0009":{"frame":{"x":0,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0011":{"frame":{"x":73,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0013":{"frame":{"x":146,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0015":{"frame":{"x":219,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0017":{"frame":{"x":292,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0019":{"frame":{"x":365,"y":502,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0021":{"frame":{"x":504,"y":0,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0023":{"frame":{"x":504,"y":72,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0025":{"frame":{"x":504,"y":144,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0027":{"frame":{"x":504,"y":216,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0029":{"frame":{"x":504,"y":288,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0031":{"frame":{"x":504,"y":360,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0033":{"frame":{"x":504,"y":432,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0035":{"frame":{"x":0,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0037":{"frame":{"x":73,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0039":{"frame":{"x":146,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0041":{"frame":{"x":219,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0043":{"frame":{"x":292,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}},"poisson3anime0045":{"frame":{"x":365,"y":574,"w":72,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":71},"sourceSize":{"w":72,"h":71}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"green.png","size":{"w":576,"h":645},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/green.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/logo.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/other.json: -------------------------------------------------------------------------------- 1 | {"frames":{"Crabe_1":{"frame":{"x":140,"y":0,"w":56,"h":43},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":56,"h":43},"sourceSize":{"w":56,"h":43}},"Crabe_2":{"frame":{"x":0,"y":47,"w":54,"h":46},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":54,"h":46},"sourceSize":{"w":54,"h":46}},"Crabe_3":{"frame":{"x":337,"y":0,"w":72,"h":36},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":36},"sourceSize":{"w":72,"h":36}},"Crabe_4":{"frame":{"x":275,"y":108,"w":60,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":60,"h":54},"sourceSize":{"w":60,"h":54}},"Drone_Orange_Damaged":{"frame":{"x":0,"y":312,"w":140,"h":108},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":108},"sourceSize":{"w":140,"h":108}},"Drone_Orange_Normal":{"frame":{"x":141,"y":312,"w":139,"h":108},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":139,"h":108},"sourceSize":{"w":139,"h":108}},"Drone_Orange_Scan":{"frame":{"x":197,"y":186,"w":139,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":139,"h":107},"sourceSize":{"w":139,"h":107}},"Drone_Violet_Damaged":{"frame":{"x":0,"y":203,"w":140,"h":108},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":108},"sourceSize":{"w":140,"h":108}},"Drone_Violet_Normal":{"frame":{"x":0,"y":94,"w":139,"h":108},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":139,"h":108},"sourceSize":{"w":139,"h":108}},"Drone_Violet_Scan":{"frame":{"x":197,"y":0,"w":139,"h":107},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":139,"h":107},"sourceSize":{"w":139,"h":107}},"Fish_1":{"frame":{"x":141,"y":203,"w":50,"h":42},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":50,"h":42},"sourceSize":{"w":50,"h":42}},"Fish_2":{"frame":{"x":140,"y":91,"w":56,"h":44},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":56,"h":44},"sourceSize":{"w":56,"h":44}},"Fish_3":{"frame":{"x":141,"y":246,"w":37,"h":47},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":37,"h":47},"sourceSize":{"w":37,"h":47}},"Fish_4":{"frame":{"x":140,"y":136,"w":52,"h":36},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":52,"h":36},"sourceSize":{"w":52,"h":36}},"Medaille-Crabe":{"frame":{"x":337,"y":37,"w":59,"h":59},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":59,"h":59},"sourceSize":{"w":59,"h":59}},"Medaille-Poissons":{"frame":{"x":337,"y":165,"w":59,"h":59},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":59,"h":59},"sourceSize":{"w":59,"h":59}},"Medaille-Poulpe":{"frame":{"x":337,"y":361,"w":59,"h":59},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":59,"h":59},"sourceSize":{"w":59,"h":59}},"PERDU":{"frame":{"x":197,"y":108,"w":77,"h":77},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":77,"h":77},"sourceSize":{"w":77,"h":77}},"Pieuvre_1":{"frame":{"x":281,"y":312,"w":49,"h":45},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":45},"sourceSize":{"w":49,"h":45}},"Pieuvre_2":{"frame":{"x":281,"y":358,"w":43,"h":61},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":43,"h":61},"sourceSize":{"w":43,"h":61}},"Pieuvre_3":{"frame":{"x":0,"y":0,"w":50,"h":46},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":50,"h":46},"sourceSize":{"w":50,"h":46}},"Pieuvre_4":{"frame":{"x":140,"y":44,"w":52,"h":46},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":52,"h":46},"sourceSize":{"w":52,"h":46}},"Star":{"frame":{"x":55,"y":68,"w":24,"h":23},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":24,"h":23},"sourceSize":{"w":24,"h":23}},"Trophee_Bleu":{"frame":{"x":55,"y":0,"w":71,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":71,"h":67},"sourceSize":{"w":71,"h":67}},"Trophee_Jaune":{"frame":{"x":337,"y":225,"w":71,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":71,"h":67},"sourceSize":{"w":71,"h":67}},"Trophee_Rose":{"frame":{"x":337,"y":97,"w":71,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":71,"h":67},"sourceSize":{"w":71,"h":67}},"Trophee_Vert":{"frame":{"x":337,"y":293,"w":71,"h":67},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":71,"h":67},"sourceSize":{"w":71,"h":67}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"other.png","size":{"w":409,"h":420},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/other.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/pink.json: -------------------------------------------------------------------------------- 1 | {"frames":{"crabe1anime0001":{"frame":{"x":0,"y":360,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0003":{"frame":{"x":85,"y":360,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0005":{"frame":{"x":170,"y":360,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0007":{"frame":{"x":255,"y":360,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0009":{"frame":{"x":370,"y":0,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0011":{"frame":{"x":370,"y":71,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0013":{"frame":{"x":370,"y":142,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0015":{"frame":{"x":370,"y":213,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0017":{"frame":{"x":370,"y":284,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0019":{"frame":{"x":370,"y":355,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0021":{"frame":{"x":0,"y":431,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0023":{"frame":{"x":85,"y":431,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0025":{"frame":{"x":170,"y":431,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0027":{"frame":{"x":255,"y":431,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0029":{"frame":{"x":340,"y":431,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0031":{"frame":{"x":455,"y":0,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0033":{"frame":{"x":455,"y":71,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0035":{"frame":{"x":455,"y":142,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0037":{"frame":{"x":455,"y":213,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0039":{"frame":{"x":455,"y":284,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0041":{"frame":{"x":455,"y":355,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0043":{"frame":{"x":455,"y":426,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"crabe1anime0045":{"frame":{"x":0,"y":502,"w":84,"h":70},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":70},"sourceSize":{"w":84,"h":70}},"pieuvre10001":{"frame":{"x":0,"y":0,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10003":{"frame":{"x":0,"y":72,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10005":{"frame":{"x":74,"y":0,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10007":{"frame":{"x":74,"y":72,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10009":{"frame":{"x":0,"y":144,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10011":{"frame":{"x":74,"y":144,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10013":{"frame":{"x":148,"y":0,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10015":{"frame":{"x":148,"y":72,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10017":{"frame":{"x":148,"y":144,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10019":{"frame":{"x":0,"y":216,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10021":{"frame":{"x":74,"y":216,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10023":{"frame":{"x":148,"y":216,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10025":{"frame":{"x":222,"y":0,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10027":{"frame":{"x":222,"y":72,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10029":{"frame":{"x":222,"y":144,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10031":{"frame":{"x":222,"y":216,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10033":{"frame":{"x":0,"y":288,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10035":{"frame":{"x":74,"y":288,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10037":{"frame":{"x":148,"y":288,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10039":{"frame":{"x":222,"y":288,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10041":{"frame":{"x":296,"y":0,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10043":{"frame":{"x":296,"y":72,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"pieuvre10045":{"frame":{"x":296,"y":144,"w":73,"h":71},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":73,"h":71},"sourceSize":{"w":73,"h":71}},"poisson1anime0001":{"frame":{"x":85,"y":502,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0003":{"frame":{"x":161,"y":502,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0005":{"frame":{"x":237,"y":502,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0007":{"frame":{"x":313,"y":502,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0009":{"frame":{"x":389,"y":502,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0011":{"frame":{"x":540,"y":0,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0013":{"frame":{"x":540,"y":55,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0015":{"frame":{"x":540,"y":110,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0017":{"frame":{"x":540,"y":165,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0019":{"frame":{"x":540,"y":220,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0021":{"frame":{"x":540,"y":275,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0023":{"frame":{"x":540,"y":330,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0025":{"frame":{"x":540,"y":385,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0027":{"frame":{"x":540,"y":440,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0029":{"frame":{"x":540,"y":495,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0031":{"frame":{"x":0,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0033":{"frame":{"x":76,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0035":{"frame":{"x":152,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0037":{"frame":{"x":228,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0039":{"frame":{"x":304,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0041":{"frame":{"x":380,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0043":{"frame":{"x":456,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}},"poisson1anime0045":{"frame":{"x":532,"y":573,"w":75,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":75,"h":54},"sourceSize":{"w":75,"h":54}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"pink.png","size":{"w":615,"h":627},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/pink.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/scan_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/scan_green.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/uglies.json: -------------------------------------------------------------------------------- 1 | {"frames":{"monstre0001":{"frame":{"x":0,"y":762,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0003":{"frame":{"x":149,"y":137,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0005":{"frame":{"x":447,"y":274,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0007":{"frame":{"x":0,"y":137,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0009":{"frame":{"x":564,"y":762,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0011":{"frame":{"x":596,"y":137,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0013":{"frame":{"x":596,"y":254,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0015":{"frame":{"x":282,"y":645,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0017":{"frame":{"x":423,"y":645,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0019":{"frame":{"x":439,"y":508,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0021":{"frame":{"x":0,"y":391,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0023":{"frame":{"x":282,"y":762,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0025":{"frame":{"x":141,"y":645,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0027":{"frame":{"x":564,"y":645,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0029":{"frame":{"x":282,"y":391,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0031":{"frame":{"x":298,"y":137,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0033":{"frame":{"x":0,"y":645,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0035":{"frame":{"x":141,"y":762,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0037":{"frame":{"x":298,"y":508,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0039":{"frame":{"x":141,"y":391,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0041":{"frame":{"x":745,"y":137,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0043":{"frame":{"x":447,"y":391,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstre0045":{"frame":{"x":423,"y":762,"w":140,"h":116},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":140,"h":116},"sourceSize":{"w":140,"h":116}},"monstreanimeaggro0001":{"frame":{"x":745,"y":391,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0003":{"frame":{"x":298,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0005":{"frame":{"x":447,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0007":{"frame":{"x":745,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0009":{"frame":{"x":149,"y":254,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0011":{"frame":{"x":596,"y":371,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0013":{"frame":{"x":447,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0015":{"frame":{"x":0,"y":254,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0017":{"frame":{"x":596,"y":508,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0019":{"frame":{"x":596,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0021":{"frame":{"x":745,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0023":{"frame":{"x":447,"y":137,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0025":{"frame":{"x":298,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0027":{"frame":{"x":149,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0029":{"frame":{"x":0,"y":0,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0031":{"frame":{"x":0,"y":508,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0033":{"frame":{"x":596,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0035":{"frame":{"x":149,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0037":{"frame":{"x":149,"y":508,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0039":{"frame":{"x":298,"y":254,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0041":{"frame":{"x":745,"y":528,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0043":{"frame":{"x":0,"y":879,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}},"monstreanimeaggro0045":{"frame":{"x":745,"y":254,"w":148,"h":136},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":148,"h":136},"sourceSize":{"w":148,"h":136}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"uglies.png","size":{"w":893,"h":1015},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/uglies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/uglies.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/yellow.json: -------------------------------------------------------------------------------- 1 | {"frames":{"crabe2anime0001":{"frame":{"x":0,"y":330,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0003":{"frame":{"x":82,"y":330,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0005":{"frame":{"x":164,"y":330,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0007":{"frame":{"x":246,"y":330,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0009":{"frame":{"x":340,"y":0,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0011":{"frame":{"x":340,"y":63,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0013":{"frame":{"x":340,"y":126,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0015":{"frame":{"x":340,"y":189,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0017":{"frame":{"x":340,"y":252,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0019":{"frame":{"x":340,"y":315,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0021":{"frame":{"x":0,"y":393,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0023":{"frame":{"x":82,"y":393,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0025":{"frame":{"x":164,"y":393,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0027":{"frame":{"x":246,"y":393,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0029":{"frame":{"x":328,"y":393,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0031":{"frame":{"x":422,"y":0,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0033":{"frame":{"x":422,"y":63,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0035":{"frame":{"x":422,"y":126,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0037":{"frame":{"x":422,"y":189,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0039":{"frame":{"x":422,"y":252,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0041":{"frame":{"x":422,"y":315,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0043":{"frame":{"x":422,"y":378,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"crabe2anime0045":{"frame":{"x":0,"y":456,"w":81,"h":62},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":81,"h":62},"sourceSize":{"w":81,"h":62}},"pieuvre2anime0001":{"frame":{"x":504,"y":0,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0003":{"frame":{"x":504,"y":74,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0005":{"frame":{"x":504,"y":148,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0007":{"frame":{"x":504,"y":222,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0009":{"frame":{"x":504,"y":296,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0011":{"frame":{"x":504,"y":370,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0013":{"frame":{"x":504,"y":444,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0015":{"frame":{"x":0,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0017":{"frame":{"x":52,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0019":{"frame":{"x":104,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0021":{"frame":{"x":156,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0023":{"frame":{"x":208,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0025":{"frame":{"x":260,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0027":{"frame":{"x":312,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0029":{"frame":{"x":364,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0031":{"frame":{"x":416,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0033":{"frame":{"x":468,"y":519,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0035":{"frame":{"x":556,"y":0,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0037":{"frame":{"x":556,"y":74,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0039":{"frame":{"x":556,"y":148,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0041":{"frame":{"x":556,"y":222,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0043":{"frame":{"x":556,"y":296,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0045":{"frame":{"x":556,"y":370,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"pieuvre2anime0047":{"frame":{"x":556,"y":444,"w":51,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":51,"h":73},"sourceSize":{"w":51,"h":73}},"poisson2anime0001":{"frame":{"x":0,"y":0,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0003":{"frame":{"x":0,"y":55,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0005":{"frame":{"x":0,"y":110,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0007":{"frame":{"x":85,"y":0,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0009":{"frame":{"x":85,"y":55,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0011":{"frame":{"x":85,"y":110,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0013":{"frame":{"x":0,"y":165,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0015":{"frame":{"x":85,"y":165,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0017":{"frame":{"x":170,"y":0,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0019":{"frame":{"x":170,"y":55,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0021":{"frame":{"x":170,"y":110,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0023":{"frame":{"x":170,"y":165,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0025":{"frame":{"x":0,"y":220,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0027":{"frame":{"x":85,"y":220,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0029":{"frame":{"x":170,"y":220,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0031":{"frame":{"x":0,"y":275,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0033":{"frame":{"x":85,"y":275,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0035":{"frame":{"x":170,"y":275,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0037":{"frame":{"x":255,"y":0,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0039":{"frame":{"x":255,"y":55,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0041":{"frame":{"x":255,"y":110,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0043":{"frame":{"x":255,"y":165,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}},"poisson2anime0045":{"frame":{"x":255,"y":220,"w":84,"h":54},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":84,"h":54},"sourceSize":{"w":84,"h":54}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"yellow.png","size":{"w":607,"h":592},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2023-SeabedSecurity/cd73b1ff2e80a34114eb1c402cc94b24080a5bed/src/main/resources/view/assets/yellow.png -------------------------------------------------------------------------------- /src/main/resources/view/config.js: -------------------------------------------------------------------------------- 1 | // import { GraphicEntityModule } from './entity-module/GraphicEntityModule.js' 2 | import { ViewModule, api} from './graphics/ViewModule.js'; 3 | import { EndScreenModule } from './endscreen-module/EndScreenModule.js'; 4 | 5 | // List of viewer modules that you want to use in your game 6 | export const modules = [ 7 | ViewModule, 8 | EndScreenModule 9 | ] 10 | 11 | export const playerColors = [ 12 | '#f6832c', // Cadmium orange 13 | '#b130c0', // Barney purple 14 | ] 15 | 16 | export const options = [ 17 | { 18 | title: 'DEBUG MODE', 19 | get: function () { 20 | return api.options.debugMode 21 | }, 22 | set: function (value) { 23 | api.options.debugMode = value 24 | }, 25 | values: { 26 | 'ON': true, 27 | 'OFF': false 28 | }, 29 | }, { 30 | title: 'SHOW FISH HABITAT', 31 | get: function () { 32 | return api.options.showFishHabitat 33 | }, 34 | set: function (value) { 35 | api.options.showFishHabitat = value 36 | }, 37 | values: { 38 | 'ON': true, 39 | 'OFF': false 40 | } 41 | }, { 42 | title: 'MY MESSAGES', 43 | get: function () { 44 | return api.options.showMyMessages 45 | }, 46 | set: function (value) { 47 | api.options.showMyMessages = value 48 | }, 49 | enabled: function () { 50 | return api.options.meInGame 51 | }, 52 | values: { 53 | 'ON': true, 54 | 'OFF': false 55 | } 56 | }, { 57 | title: 'OTHERS\' MESSAGES', 58 | get: function () { 59 | return api.options.showOthersMessages 60 | }, 61 | set: function (value) { 62 | api.options.showOthersMessages = value 63 | }, 64 | 65 | values: { 66 | 'ON': true, 67 | 'OFF': false 68 | } 69 | }, { 70 | title: 'ENABLE DARKNESS', 71 | get: function () { 72 | return api.options.darkness 73 | }, 74 | set: function (value) { 75 | api.options.darkness = value 76 | }, 77 | enabled: function () { 78 | return api.options.enableDarkness 79 | }, 80 | values: { 81 | 'ON': true, 82 | 'ORANGE': 0, 83 | 'PURPLE': 1, 84 | 'OFF': false 85 | } 86 | } 87 | 88 | ] 89 | 90 | 91 | export const gameName = 'FC2023' 92 | 93 | export const stepByStepAnimateSpeed = 3 94 | -------------------------------------------------------------------------------- /src/main/resources/view/endscreen-module/EndScreenModule.js: -------------------------------------------------------------------------------- 1 | import { WIDTH, HEIGHT } from '../core/constants.js' 2 | import { lerp, unlerp, fitAspectRatio } from '../core/utils.js' 3 | import { ErrorLog } from '../core/ErrorLog.js' 4 | import { MissingImageError } from './errors/MissingImageError.js' 5 | 6 | /* global PIXI */ 7 | 8 | export class EndScreenModule { 9 | constructor (assets) { 10 | this.states = [] 11 | this.scores = [] 12 | this.globalData = {} 13 | this.atEnd = false 14 | } 15 | 16 | static get moduleName () { 17 | return 'endScreen' 18 | } 19 | 20 | updateScene (previousData, currentData, progress) { 21 | if (currentData.scores && progress === 1) { 22 | this.atEnd = true 23 | } else { 24 | this.atEnd = false 25 | } 26 | } 27 | 28 | handleFrameData (frameInfo, data) { 29 | let scores = null 30 | let spriteName = null 31 | let displayedText = null 32 | if (data) { 33 | scores = data[0] 34 | spriteName = data[1] 35 | displayedText = data[2] 36 | } 37 | const state = { 38 | number: frameInfo.number, 39 | scores, 40 | spriteName, 41 | displayedText 42 | } 43 | if (scores) { 44 | this.scores = scores 45 | } 46 | if (spriteName) { 47 | this.spriteName = spriteName 48 | } 49 | if (displayedText) { 50 | this.displayedText = displayedText 51 | } 52 | this.states.push(state) 53 | return state 54 | } 55 | 56 | reinitScene (container, canvasData) { 57 | this.container = container 58 | this.endLayer = this.createEndScene(this) 59 | if (this.atEnd) { 60 | this.initEndScene() 61 | } 62 | this.container.addChild(this.endLayer) 63 | } 64 | 65 | animateScene (delta) { 66 | const step = Math.min(32, delta) 67 | 68 | if (this.atEnd) { 69 | if (!this.animationEnded) { 70 | this.renderEndScene(step) 71 | } 72 | } else { 73 | if (this.endTime > 0) { 74 | this.destroyEndScene() 75 | } 76 | this.endTime = 0 77 | } 78 | } 79 | 80 | destroyEndScene () { 81 | this.animationEnded = false 82 | this.endLayer.visible = false 83 | } 84 | 85 | initEndScene () { 86 | this.animationEnded = false 87 | this.endLayer.visible = true 88 | } 89 | 90 | renderEndScene (step) { 91 | var endOfEnd = 10000 92 | if (this.endTime === 0) { 93 | this.initEndScene() 94 | } 95 | 96 | var backS = 0 97 | var backD = 400 98 | var backP = unlerp(backS, backS + backD, this.endTime) 99 | this.endLayer.backgroundRanking.alpha = backP * 0.9 100 | 101 | var logoS = 400 102 | var logoD = 600 103 | var logoP = unlerp(logoS, logoS + logoD, this.endTime) 104 | 105 | this.endLayer.titleRanking.scale.set(lerp(this.targetTitleScale * 10, this.targetTitleScale, logoP)) 106 | this.endLayer.titleRanking.visible = !!logoP 107 | 108 | var rankS = 1000 109 | var rankD = 300 110 | for (let i = 0; i < this.finishers.length; ++i) { 111 | var p = unlerp(rankS + rankD * i, rankS + rankD * i + rankD, this.endTime) 112 | this.finishers[i].alpha = p 113 | } 114 | 115 | this.endTime += step 116 | 117 | if (this.endTime >= endOfEnd) { 118 | this.animationEnded = true 119 | } 120 | } 121 | 122 | handleGlobalData (players, globalData) { 123 | this.globalData = { 124 | players: players, 125 | playerCount: players.length 126 | } 127 | } 128 | 129 | fitTextInWidth (text, width) { 130 | let currText = text.text 131 | while (text.width > width) { 132 | currText = currText.slice(0, -1) 133 | text.text = currText + '...' 134 | } 135 | } 136 | 137 | generateText (text, size, align, color, maxWidth = null) { 138 | var textEl 139 | textEl = new PIXI.Text(text, { 140 | fontSize: Math.round(size / 1.2) + 'px', 141 | fontFamily: 'Lato', 142 | fontWeight: 'bold', 143 | fill: color 144 | }) 145 | textEl.lineHeight = Math.round(size / 1.2) 146 | if (align === 'right') { 147 | textEl.anchor.x = 1 148 | } else if (align === 'center') { 149 | textEl.anchor.x = 0.5 150 | } 151 | if (maxWidth !== null) { 152 | this.fitTextInWidth(textEl, maxWidth) 153 | } 154 | 155 | return textEl 156 | } 157 | 158 | createFinisher (finisher) { 159 | var layer = new PIXI.Container() 160 | 161 | var avatarContainer = new PIXI.Container() 162 | avatarContainer.y = 0 163 | avatarContainer.x = 0 164 | 165 | var backgroundAvatar = new PIXI.Graphics() 166 | backgroundAvatar.beginFill(0xffffff) 167 | backgroundAvatar.alpha = 0.1 168 | backgroundAvatar.drawRect(0, 0, 240, 120) 169 | avatarContainer.addChild(backgroundAvatar) 170 | 171 | var avatarBorder = new PIXI.Graphics() 172 | avatarBorder.lineStyle(1, 0xffffff) 173 | avatarBorder.alpha = 0.5 174 | avatarBorder.drawRect(0, 0, 120, 120) 175 | avatarContainer.addChild(avatarBorder) 176 | 177 | var avatar = new PIXI.Sprite(finisher.player.avatar) 178 | avatar.width = avatar.height = 120 179 | 180 | var rank = this.generateText(finisher.rank.toString(), 76, 'center', finisher.player.color) 181 | rank.anchor.y = 0.5 182 | rank.position.x = 160 183 | rank.position.y = 56 184 | avatarContainer.addChild(rank) 185 | 186 | let rankChars = 'TH' 187 | if (finisher.rank < 4) { 188 | rankChars = ['ST', 'ND', 'RD'][finisher.rank - 1] 189 | } 190 | var rankLetter = this.generateText(rankChars.toString(), 34, 'left', finisher.player.color) 191 | rankLetter.position.x = 184 192 | rankLetter.position.y = 32 193 | avatarContainer.addChild(rankLetter) 194 | 195 | var hudAvatar = new PIXI.Container() 196 | hudAvatar.addChild(avatar) 197 | 198 | avatarContainer.addChild(hudAvatar) 199 | 200 | let maxTextWidth 201 | if (this.globalData.playerCount <= 4) { 202 | maxTextWidth = 1500 203 | } else { 204 | maxTextWidth = 500 205 | } 206 | var name = this.generateText(finisher.player.name.toUpperCase(), 50, 'left', finisher.player.color, maxTextWidth) 207 | 208 | const scoreText = finisher.text || ((finisher.score >= 0) ? finisher.score.toString() + ' points' : '-') 209 | var scoreLabel = this.generateText(scoreText, 64, 'left', finisher.player.color, maxTextWidth) 210 | 211 | name.x = 330 212 | name.y = -4 213 | scoreLabel.x = 330 214 | scoreLabel.y = 50 215 | 216 | layer.addChild(avatarContainer) 217 | layer.addChild(name) 218 | layer.addChild(scoreLabel) 219 | 220 | return layer 221 | } 222 | 223 | createEndScene () { 224 | var layer = new PIXI.Container() 225 | 226 | var background = new PIXI.Graphics() 227 | background.beginFill(0, 0.85) 228 | background.drawRect(360, 0, WIDTH - 360 * 2, HEIGHT) 229 | background.endFill() 230 | 231 | layer.backgroundRanking = background 232 | 233 | const sprite = this.spriteName 234 | var titleRanking 235 | if (PIXI.utils.TextureCache[sprite]) { 236 | titleRanking = PIXI.Sprite.from(sprite) 237 | } else { 238 | ErrorLog.push(new MissingImageError(sprite)) 239 | titleRanking = new PIXI.Sprite(PIXI.Texture.EMPTY) 240 | } 241 | this.targetTitleScale = fitAspectRatio(titleRanking.texture.width, titleRanking.texture.height, 2 * WIDTH / 3, HEIGHT / 4) 242 | titleRanking.anchor.x = titleRanking.anchor.y = 0.5 243 | layer.titleRanking = titleRanking 244 | 245 | titleRanking.position.x = WIDTH / 2 246 | titleRanking.position.y = 230 247 | 248 | var podium = [] 249 | for (var i = 0; i < this.globalData.playerCount; ++i) { 250 | podium.push({ 251 | score: this.scores[i], 252 | text: (this.displayedText) ? this.displayedText[i] : null, 253 | player: this.globalData.players[i], 254 | rank: 0 255 | }) 256 | } 257 | podium.sort(function (a, b) { 258 | return b.score - a.score 259 | }) 260 | 261 | this.finishers = [] 262 | var finishers = new PIXI.Container() 263 | 264 | var elem 265 | for (i = 0; i < podium.length; ++i) { 266 | podium[i].rank = podium.filter(p => p.score > podium[i].score).length + 1 267 | elem = this.createFinisher(podium[i]) 268 | finishers.addChild(elem) 269 | this.finishers.push(elem) 270 | } 271 | 272 | if (this.finishers.length <= 4) { 273 | const maxFinisherWidth = Math.max(...this.finishers.map(f => f.width)) 274 | for (i = 0; i < this.finishers.length; ++i) { 275 | this.finishers[i].position.x = (WIDTH - maxFinisherWidth) / 2 276 | this.finishers[i].position.y = i * 150 277 | } 278 | } else { 279 | const margin = 50 280 | const middle = Math.ceil(this.finishers.length / 2) 281 | const maxFinisherWidth = Math.max(...this.finishers.slice(0, middle).map(f => f.width)) 282 | for (i = 0; i < this.finishers.length; ++i) { 283 | if (i < middle) { 284 | this.finishers[i].position.x = WIDTH / 2 - (maxFinisherWidth + margin) 285 | } else { 286 | this.finishers[i].position.x = WIDTH / 2 + margin 287 | } 288 | this.finishers[i].position.y = (i % middle) * 150 289 | } 290 | } 291 | finishers.y = 400 292 | 293 | layer.addChild(background) 294 | layer.addChild(titleRanking) 295 | layer.addChild(finishers) 296 | 297 | layer.visible = false 298 | return layer 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/main/resources/view/endscreen-module/errors/MissingImageError.js: -------------------------------------------------------------------------------- 1 | export class MissingImageError extends Error { 2 | constructor (image, cause) { 3 | super('Could not find image: "' + image + '". Make sure it is in your assets folder and is spelled correctly.') 4 | this.image = image 5 | this.cause = cause 6 | this.name = 'MissingImageError' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/view/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "view", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "config.js", 6 | "dependencies": { 7 | "pixi.js": "^5.0.0" 8 | }, 9 | "devDependencies": { 10 | "@types/pixi.js": "^5.0.0", 11 | "@typescript-eslint/eslint-plugin": "2.28.0", 12 | "@typescript-eslint/parser": "2.28.0", 13 | "eslint": "6.8.0", 14 | "eslint-config-standard": "14.1.1", 15 | "eslint-config-standard-jsx": "8.1.0", 16 | "eslint-loader": "3.0.4", 17 | "eslint-plugin-import": "2.20.2", 18 | "eslint-plugin-node": "11.1.0", 19 | "eslint-plugin-promise": "4.2.1", 20 | "eslint-plugin-react": "7.19.0", 21 | "eslint-plugin-react-hooks": "2.5.1", 22 | "eslint-plugin-standard": "4.0.1", 23 | "typescript": "3.8.3" 24 | }, 25 | "scripts": { 26 | "test": "echo \"Error: no test specified\" && exit 1", 27 | "start": "tsc -w" 28 | }, 29 | "author": "", 30 | "license": "ISC" 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/Deserializer.ts: -------------------------------------------------------------------------------- 1 | import { HEIGHT } from '../core/constants.js' 2 | import { SCAN_CODE_LOST, SCAN_CODE_SAVED, SCAN_CODE_UNSCANNED } from './gameConstants.js' 3 | import { DroneDto, FishDto, FishGlobalDto, FrameDataDTO, GlobalData, GlobalDataDTO, PlayerDto, ScanSummaryDto, UglyDto } from './types.js' 4 | 5 | const MAIN_SEPARATOR = ';' 6 | 7 | function splitLine (str) { 8 | return str.length === 0 ? [] : str.split(' ') 9 | } 10 | 11 | export function parseData (unsplit: string, globalData: GlobalData): FrameDataDTO { 12 | const raw = unsplit.split(MAIN_SEPARATOR) 13 | 14 | let idx = 0 15 | const fishCount = +raw[idx++] 16 | const fishes: FishDto[] = [] 17 | const fishMap: Record = {} 18 | for (let i = 0; i < fishCount; ++i) { 19 | const rawFish = splitLine(raw[idx++]) 20 | const fish: FishDto = { 21 | id: +rawFish[0], 22 | x: +rawFish[1], 23 | y: +rawFish[2], 24 | vx: +rawFish[3], 25 | vy: +rawFish[4], 26 | isFleeing: rawFish[5] === '1' 27 | } 28 | fishes.push(fish) 29 | fishMap[fish.id] = fish 30 | } 31 | 32 | const uglies: UglyDto[] = [] 33 | const uglyMap: Record = {} 34 | for (let i = 0; i < globalData.uglies.length; ++i) { 35 | const rawFish = splitLine(raw[idx++]) 36 | const fish: UglyDto = { 37 | id: +rawFish[0], 38 | x: +rawFish[1], 39 | y: +rawFish[2], 40 | vx: +rawFish[3], 41 | vy: +rawFish[4], 42 | foundTarget: rawFish[5] === '1' 43 | } 44 | uglies.push(fish) 45 | uglyMap[fish.id] = fish 46 | } 47 | 48 | const scans: ScanSummaryDto[][][] = [] 49 | const scores: number[] = [] 50 | const drones: DroneDto[][] = [[], []] 51 | 52 | for (let i = 0; i < globalData.playerCount; ++i) { 53 | scores.push(+raw[idx++]) 54 | for (let k = 0; k < globalData.dronesPerPlayer; ++k) { 55 | const rawDrone = splitLine(raw[idx++]) 56 | const drone: DroneDto = { 57 | id: +rawDrone[0], 58 | x: +rawDrone[1], 59 | y: +rawDrone[2], 60 | targetX: rawDrone[3] === 'x' ? null : +rawDrone[3], 61 | targetY: rawDrone[4] === 'x' ? null : +rawDrone[4], 62 | battery: +rawDrone[5], 63 | light: rawDrone[6] === '1', 64 | dead: rawDrone[7] === '1', 65 | dieAt: (+rawDrone[8]) / 100, 66 | scans: [], 67 | fishesScannedThisTurn: [], 68 | didReport: rawDrone[9] === '1', 69 | message: rawDrone.slice(10).join(' ') 70 | } 71 | const fishesScannedThisTurnCount = +raw[idx++] 72 | for (let j = 0; j < fishesScannedThisTurnCount; ++j) { 73 | drone.fishesScannedThisTurn.push(+raw[idx++]) 74 | } 75 | const scanCount = +raw[idx++] 76 | for (let j = 0; j < scanCount; ++j) { 77 | const rawScan = raw[idx++].split(' ') 78 | const scan = { 79 | color: +rawScan[0], 80 | type: +rawScan[1] 81 | } 82 | drone.scans.push(scan) 83 | } 84 | drones[i].push(drone) 85 | } 86 | let scanIdx = 0 87 | const rawScans = splitLine(raw[idx++]) 88 | const playerScans: ScanSummaryDto[][] = [] 89 | for (let row = 0; row < 4; ++row) { 90 | const line: ScanSummaryDto[] = [] 91 | for (let col = 0; col < 3; ++col) { 92 | const code = +rawScans[scanIdx++] 93 | const hasBonus = rawScans[scanIdx++] === '@' 94 | line.push({ code, hasBonus }) 95 | } 96 | line.push({ 97 | code: getComboCode(line.map(({ code }) => code)), 98 | hasBonus: rawScans[scanIdx++] === '@' 99 | }) 100 | playerScans.push(line) 101 | } 102 | const lastLine = [] 103 | for (let col = 0; col < 3; ++col) { 104 | const column = playerScans.map(line => line[col].code) 105 | lastLine.push({ 106 | code: getComboCode(column), 107 | hasBonus: rawScans[scanIdx++] === '@' 108 | }) 109 | } 110 | playerScans.push(lastLine) 111 | scans.push(playerScans) 112 | } 113 | 114 | return { 115 | scans, 116 | fishes, 117 | fishMap, 118 | uglies, 119 | uglyMap, 120 | scores, 121 | drones 122 | } 123 | 124 | // const events: EventDto[] = [] 125 | // const eventCount = +raw[idx++] 126 | // for (let i = 0; i < eventCount; ++i) { 127 | // const playerIndex = +raw[idx++] 128 | // const amount = +raw[idx++] 129 | // const rawCoord = splitLine(raw[idx++]) 130 | // const coord = { 131 | // x: +rawCoord[0], 132 | // y: +rawCoord[1] 133 | // } 134 | // const rawTarget = splitLine(raw[idx++]) 135 | // const target = { 136 | // x: +rawTarget[0], 137 | // y: +rawTarget[1] 138 | // } 139 | // const type = +raw[idx++] 140 | // const start = +raw[idx++] 141 | // const end = +raw[idx++] 142 | // const animData = { start, end } 143 | 144 | // events.push({ 145 | // playerIndex, 146 | // amount, 147 | // coord, 148 | // target, 149 | // type, 150 | // animData 151 | // }) 152 | // } 153 | } 154 | 155 | function getComboCode (line: number[]) { 156 | if (line.every(n => n === SCAN_CODE_SAVED)) { 157 | return SCAN_CODE_SAVED 158 | } 159 | if (line.some(n => n === SCAN_CODE_LOST)) { 160 | return SCAN_CODE_LOST 161 | } 162 | return SCAN_CODE_UNSCANNED 163 | } 164 | 165 | export function parseGlobalData (unsplit: string): GlobalDataDTO { 166 | const raw = unsplit.split(MAIN_SEPARATOR) 167 | let idx = 0 168 | 169 | const rawIsDemo = raw[idx] 170 | let isDemo = false 171 | if (rawIsDemo === 'X') { 172 | isDemo = true 173 | ++idx 174 | } 175 | const fishWillFlee = raw[idx++] === '1' 176 | const width = +raw[idx++] 177 | const height = +raw[idx++] 178 | const dronesPerPlayer = +raw[idx++] 179 | const fishCount = +raw[idx++] 180 | 181 | const fishes: FishGlobalDto[] = [] 182 | const fishMap: Record = {} 183 | 184 | for (let i = 0; i < fishCount; ++i) { 185 | const rawFish = splitLine(raw[idx++]) 186 | const fish: FishGlobalDto = { 187 | id: +rawFish[0], 188 | color: +rawFish[1], 189 | type: +rawFish[2] 190 | } 191 | fishes.push(fish) 192 | fishMap[fish.id] = fish 193 | } 194 | 195 | const uglyCount = +raw[idx++] 196 | 197 | const uglies: number[] = [] 198 | for (let i = 0; i < uglyCount; ++i) { 199 | uglies.push(+raw[idx++]) 200 | } 201 | 202 | const simpleScans = raw[idx++] === '1' 203 | 204 | return { 205 | width, 206 | height, 207 | dronesPerPlayer, 208 | fishes, 209 | fishMap, 210 | uglies, 211 | ratio: HEIGHT / 10800, 212 | fishWillFlee, 213 | isDemo, 214 | simpleScans 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/MessageBoxes.ts: -------------------------------------------------------------------------------- 1 | import { flagForDestructionOnReinit } from '../core/rendering.js' 2 | import { ViewModule } from './ViewModule.js' 3 | import { PlayerInfo } from './types.js' 4 | import { fit } from './utils.js' 5 | import * as utils from '../core/utils.js' 6 | 7 | export function renderMessageContainer (this: ViewModule, messageContainer: MessageContainer, i, step: number) { 8 | const playerInfo = this.globalData.players 9 | const options = this.api.options 10 | const stepFactor = Math.pow(0.99, step) 11 | const targetMessageAlpha = (options.showMyMessages && playerInfo[i].isMe) || (options.showOthersMessages && !playerInfo[i].isMe) ? 1 12 | : 0 13 | messageContainer.alpha = messageContainer.alpha * stepFactor + targetMessageAlpha * (1 - stepFactor) 14 | }; 15 | 16 | export const messageBox = { 17 | width: 150, 18 | offset: { 19 | x: 70, 20 | y: -60 21 | } 22 | } 23 | 24 | export type MessageContainer = PIXI.Container & { 25 | messageText: PIXI.Text 26 | updateText: (text: string, x: number, y: number) => void 27 | } 28 | 29 | export function initMessages (layer) { 30 | const self: ViewModule = this 31 | self.messages = [] 32 | for (let i = 0; i < self.globalData.playerCount; ++i) { 33 | self.messages[i] = [] 34 | const messageGroup = new PIXI.Container() 35 | for (let k = 0; k < self.globalData.dronesPerPlayer; ++k) { 36 | const messageContainer = new PIXI.Container() as MessageContainer 37 | 38 | const baseScale = 0.5699481865284974 39 | const bubble = new PIXI.Container() 40 | const bubbleLeft = PIXI.Sprite.from('dial_side.png') 41 | const bubbleRight = PIXI.Sprite.from('dial_side.png') 42 | const bubbleMid = PIXI.Sprite.from('dial_mid.png') 43 | const bubbleArrow = PIXI.Sprite.from('dial_arrow.png') 44 | 45 | bubbleLeft.anchor.y = 0.5 46 | bubbleRight.anchor.y = 0.5 47 | bubbleMid.anchor.y = 0.5 48 | 49 | bubbleLeft.scale.set(-baseScale, baseScale) 50 | bubbleRight.scale.set(baseScale, baseScale) 51 | bubbleMid.scale.set(baseScale, baseScale) 52 | bubbleArrow.scale.set(baseScale, baseScale) 53 | bubble.y = -18 54 | 55 | bubbleArrow.y = bubbleMid.height / 2 - 5 56 | bubbleArrow.x = -25 57 | 58 | bubble.addChild(bubbleLeft) 59 | bubble.addChild(bubbleRight) 60 | bubble.addChild(bubbleMid) 61 | bubble.addChild(bubbleArrow) 62 | 63 | const textStyle: Partial = { 64 | fontFamily: 'Arial', 65 | fontWeight: '700', 66 | fontSize: 22, 67 | fill: self.globalData.players[i].color, 68 | // lineHeight: 28, 69 | align: 'center', 70 | // strokeThickness: 2, 71 | wordWrap: true, 72 | wordWrapWidth: 180 73 | } 74 | const messageText = new PIXI.Text('', textStyle) 75 | 76 | messageText.anchor.x = 0.5 77 | messageText.anchor.y = 0.5 78 | 79 | messageText.y = -20 80 | 81 | flagForDestructionOnReinit(messageText) 82 | 83 | messageContainer.messageText = messageText 84 | 85 | messageContainer.updateText = (text, x, y) => { 86 | bubbleArrow.visible = false 87 | bubble.visible = (!!text) 88 | messageText.text = text 89 | const maxHeight = 70 90 | if (messageText.height > maxHeight) { 91 | while (messageText.text.length > 3 && messageText.height > maxHeight) { 92 | messageText.text = messageText.text.slice(0, -4) + '...' 93 | } 94 | } 95 | const w = messageText.width 96 | bubbleLeft.x = -w / 2 97 | bubbleRight.x = w / 2 98 | bubbleMid.x = -w / 2 99 | bubbleMid.width = w 100 | 101 | messageContainer.position.set(x, y) 102 | messageContainer.y += messageBox.offset.y 103 | messageContainer.x += messageContainer.width / 2 104 | 105 | bubble.height = messageText.height * 1.2 106 | 107 | bubbleArrow.x = -messageContainer.width / 2 108 | bubbleArrow.visible = true 109 | 110 | if (messageText.height > 50) { 111 | bubble.y = -18 112 | messageText.y = -20 113 | } else { 114 | bubble.y = 0 115 | messageText.y = 0 116 | } 117 | } 118 | 119 | messageContainer.addChild(bubble) 120 | messageContainer.addChild(messageText) 121 | 122 | self.messages[i].push(messageContainer) 123 | messageGroup.addChild(messageContainer) 124 | } 125 | layer.addChild(messageGroup) 126 | } 127 | }; 128 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/TooltipManager.ts: -------------------------------------------------------------------------------- 1 | import { HEIGHT } from '../core/constants.js' 2 | import { ScanDto } from './types.js' 3 | import { fit, getFishPng } from './utils.js' 4 | import * as utils from '../core/utils.js' 5 | 6 | /* global PIXI */ 7 | const MAX_FISH_HEIGHT = 61 8 | const MAX_FISH_WIDTH = 72 9 | 10 | const PADDING = 5 11 | const CURSOR_WIDTH = 20 12 | const LINE_HEIGHT = 36 13 | const SCANS_RECT = { 14 | x: 0, 15 | y: 180, 16 | w: 232 17 | } 18 | const SCANS_SEP = 65 19 | const SCANS_HEIGHT = MAX_FISH_HEIGHT 20 | 21 | function generateText (text, size, color, align): PIXI.Text { 22 | var textEl = new PIXI.Text(text, { 23 | fontSize: Math.round(size / 1.2) + 'px', 24 | fontFamily: 'Lato', 25 | fontWeight: 'bold', 26 | fill: color, 27 | lineHeight: size 28 | }) 29 | 30 | if (align === 'right') { 31 | textEl.anchor.x = 1 32 | } else if (align === 'center') { 33 | textEl.anchor.x = 0.5 34 | } 35 | 36 | return textEl 37 | }; 38 | 39 | export class TooltipManager { 40 | tooltip: PIXI.Container 41 | tooltipContainer: PIXI.Container 42 | tooltipLabel: PIXI.Text 43 | tooltipBackground: PIXI.Graphics 44 | tooltipOffset: number 45 | scansContainer: PIXI.Container 46 | debug: PIXI.Graphics 47 | scans: PIXI.Sprite[] 48 | registry: {element: PIXI.DisplayObject, getText: () => string, getScans: () => ScanDto[]}[] 49 | inside: {[id: number]: boolean} 50 | getGlobalText: (data: PIXI.InteractionData) => string 51 | lastEvent: PIXI.InteractionEvent 52 | 53 | reinit () { 54 | const container = new PIXI.Container() 55 | const tooltip = new PIXI.Container() 56 | const background = new PIXI.Graphics() 57 | const label = generateText('DEFAULT', 36, 0xFFFFFF, 'left') 58 | 59 | label.position.x = PADDING 60 | label.position.y = PADDING 61 | 62 | tooltip.visible = false 63 | tooltip.addChild(background) 64 | tooltip.addChild(label) 65 | this.tooltipBackground = background 66 | this.tooltipLabel = label 67 | this.tooltipContainer = container 68 | this.tooltip = tooltip 69 | this.registry = [] 70 | this.inside = {} 71 | this.tooltipOffset = 0 72 | this.getGlobalText = null 73 | 74 | this.scansContainer = new PIXI.Container() 75 | this.scans = [] 76 | for (let i = 0; i < 12; ++i) { 77 | const scan = PIXI.Sprite.from('Pieuvre_1') 78 | scan.visible = false 79 | 80 | scan.anchor.set(0.5) 81 | scan.x = (i % 4) * SCANS_SEP 82 | scan.y = Math.floor(i / 4) * SCANS_HEIGHT 83 | scan.x += MAX_FISH_WIDTH / 2 84 | scan.y += MAX_FISH_HEIGHT / 2 85 | 86 | this.scansContainer.addChild(scan) 87 | this.scans.push(scan) 88 | } 89 | 90 | container.addChild(this.tooltip) 91 | container.addChild(this.scansContainer) 92 | // this.debug = new PIXI.Graphics() 93 | // container.addChild(this.debug) 94 | return container 95 | } 96 | 97 | clear () { 98 | this.inside = {} 99 | } 100 | 101 | registerGlobal (getText: (data: PIXI.InteractionData) => string) { 102 | this.getGlobalText = getText 103 | } 104 | 105 | register (element: PIXI.DisplayObject, getText: () => string, getScans: () => ScanDto[]) { 106 | this.registry.push({ element, getText, getScans }) 107 | } 108 | 109 | showTooltip (text: string) { 110 | this.setTooltipText(this.tooltip, text) 111 | } 112 | 113 | setTooltipText (tooltip: PIXI.Container, text: string) { 114 | this.tooltipLabel.text = text 115 | 116 | const width = this.tooltipLabel.width + PADDING * 2 117 | const height = this.tooltipLabel.height + PADDING * 2 118 | 119 | this.tooltipOffset = -width 120 | 121 | this.tooltipBackground.clear() 122 | this.tooltipBackground.beginFill(0x0, 0.9) 123 | this.tooltipBackground.drawRect(0, 0, width, height) 124 | this.tooltipBackground.endFill() 125 | 126 | tooltip.visible = true 127 | } 128 | 129 | updateGlobalText () { 130 | if (this.lastEvent != null) { 131 | this.moveTooltip(this.lastEvent) 132 | } 133 | } 134 | 135 | // Iterate over all the elements in the registry and set true/false in the inside map 136 | updateHovers () { 137 | for (let i = 0; i < this.registry.length; ++i) { 138 | const { element } = this.registry[i] 139 | const mousePosition = this.lastEvent.data.getLocalPosition(element) 140 | let inside 141 | if (element.hitArea != null) { 142 | inside = element.hitArea.contains(mousePosition.x, mousePosition.y) 143 | } else { 144 | inside = pointWithinBound(mousePosition, element.getLocalBounds()) 145 | } 146 | if (inside) { 147 | this.inside[i] = true 148 | } else { 149 | delete this.inside[i] 150 | } 151 | } 152 | } 153 | 154 | moveTooltip (event: PIXI.InteractionEvent) { 155 | this.lastEvent = event 156 | 157 | const newPosition = event.data.getLocalPosition(this.tooltipContainer) 158 | 159 | this.updateHovers() 160 | 161 | let xOffset = this.tooltipOffset - 20 162 | let yOffset = +40 163 | 164 | if (newPosition.x + xOffset < 0) { 165 | xOffset = CURSOR_WIDTH 166 | } 167 | 168 | if (newPosition.y + this.tooltip.height + yOffset > HEIGHT) { 169 | yOffset = HEIGHT - newPosition.y - this.tooltip.height 170 | } 171 | 172 | this.tooltip.position.x = newPosition.x + xOffset 173 | this.tooltip.position.y = newPosition.y + yOffset 174 | 175 | this.scansContainer.visible = false 176 | 177 | const textBlocks = [] 178 | const insideKeys = Object.keys(this.inside) 179 | 180 | for (const key of insideKeys) { 181 | const registryIdx = parseInt(key) 182 | const { getText, getScans } = this.registry[registryIdx] 183 | 184 | let text = getText() 185 | 186 | const scansToShow = getScans?.() ?? [] 187 | if (scansToShow.length > 0) { 188 | if (insideKeys.length === 1) { 189 | // The space where the icons will go 190 | text += '\nscans:\n ' 191 | 192 | if (scansToShow.length > 4) { 193 | text += '\n' 194 | } 195 | if (scansToShow.length > 8) { 196 | text += '\n' 197 | } 198 | 199 | let showIdx = 0 200 | 201 | let scanRectH = LINE_HEIGHT 202 | if (scansToShow.length > 4) { 203 | scanRectH = LINE_HEIGHT * 2 204 | } 205 | if (scansToShow.length > 8) { 206 | scanRectH = LINE_HEIGHT * 3 207 | } 208 | // Space above the underscores 209 | scanRectH += 30 210 | 211 | for (const scan of this.scans) { 212 | scan.visible = false 213 | if (showIdx < scansToShow.length) { 214 | const { color, type } = scansToShow[showIdx] 215 | scan.texture = PIXI.Texture.from(getFishPng(color, type)) 216 | scan.visible = true 217 | } 218 | ++showIdx 219 | 220 | this.scansContainer.position.set( 221 | this.tooltip.position.x + SCANS_RECT.x, 222 | this.tooltip.position.y + SCANS_RECT.y 223 | ) 224 | } 225 | this.scansContainer.visible = true 226 | this.scansContainer.scale.set(1) 227 | 228 | const scanMaxWidth = Math.min(4, scansToShow.length) * SCANS_SEP 229 | const scale = Math.min(1, utils.fitAspectRatio(scanMaxWidth, this.scansContainer.height, SCANS_RECT.w, scanRectH)) 230 | this.scansContainer.scale.set(scale) 231 | } else { 232 | text += '\nscans:\n' 233 | let idx = 0 234 | for (const scan of scansToShow) { 235 | text += `(${scan.color}, ${scan.type})` 236 | if (idx % 4 === 3) { 237 | text += '\n' 238 | } else { 239 | text += ' ' 240 | } 241 | ++idx 242 | } 243 | } 244 | } 245 | 246 | if (text != null && text.length > 0) { 247 | textBlocks.push(text) 248 | } 249 | } 250 | 251 | if (this.getGlobalText != null) { 252 | const text = this.getGlobalText(event.data) 253 | if (text != null && text.length > 0) { 254 | textBlocks.push(text) 255 | } 256 | } 257 | if (textBlocks.length > 0) { 258 | this.showTooltip(textBlocks.join('\n________\n')) 259 | } else { 260 | this.hideTooltip() 261 | } 262 | } 263 | 264 | hideTooltip () { 265 | this.tooltip.visible = false 266 | } 267 | } 268 | 269 | function pointWithinBound (position: PIXI.IPoint, bounds: PIXI.Rectangle) { 270 | return position.x <= bounds.x + bounds.width && 271 | position.y <= bounds.y + bounds.height && 272 | position.x >= bounds.x && 273 | position.y >= bounds.y 274 | } 275 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/assetConstants.ts: -------------------------------------------------------------------------------- 1 | export const BACKGROUND = 'background.jpg' 2 | export const GAME_ZONE_OFFSET_Y = 300 3 | export const FISH_HABITAT_SIZE = 2500 4 | export const BEAM_LENGTH = 250 5 | 6 | export const REPORTING_FRAMES = [ 7 | 'Reporting0001', 8 | 'Reporting0002', 9 | 'Reporting0003', 10 | 'Reporting0004', 11 | 'Reporting0005', 12 | 'Reporting0006', 13 | 'Reporting0007', 14 | 'Reporting0008', 15 | 'Reporting0009', 16 | 'Reporting0010', 17 | 'Reporting0011', 18 | 'Reporting0012', 19 | 'Reporting0013', 20 | 'Reporting0014', 21 | 'Reporting0015', 22 | 'Reporting0016', 23 | 'Reporting0017', 24 | 'Reporting0018', 25 | 'Reporting0019', 26 | 'Reporting0020', 27 | 'Reporting0021', 28 | 'Reporting0022', 29 | 'Reporting0023', 30 | 'Reporting0024', 31 | 'Reporting0025', 32 | 'Reporting0026', 33 | 'Reporting0027', 34 | 'Reporting0028', 35 | 'Reporting0029', 36 | 'Reporting0030', 37 | 'Reporting0031', 38 | 'Reporting0032', 39 | 'Reporting0033', 40 | 'Reporting0034', 41 | 'Reporting0035', 42 | 'Reporting0036', 43 | 'Reporting0037', 44 | 'Reporting0038', 45 | 'Reporting0039', 46 | 'Reporting0040', 47 | 'Reporting0041', 48 | 'Reporting0042', 49 | 'Reporting0043', 50 | 'Reporting0044', 51 | 'Reporting0045', 52 | 'Reporting0046', 53 | 'Reporting0047', 54 | 'Reporting0048', 55 | 'Reporting0049', 56 | 'Reporting0050', 57 | 'Reporting0051', 58 | 'Reporting0052', 59 | 'Reporting0053', 60 | 'Reporting0054', 61 | 'Reporting0055', 62 | 'Reporting0056', 63 | 'Reporting0057', 64 | 'Reporting0058', 65 | 'Reporting0059', 66 | 'Reporting0060', 67 | 'Reporting0061', 68 | 'Reporting0062', 69 | 'Reporting0063', 70 | 'Reporting0064', 71 | 'Reporting0065', 72 | 'Reporting0066', 73 | 'Reporting0067', 74 | 'Reporting0068', 75 | 'Reporting0069', 76 | 'Reporting0070', 77 | 'Reporting0071', 78 | 'Reporting0072', 79 | 'Reporting0073', 80 | 'Reporting0074', 81 | 'Reporting0075', 82 | 'Reporting0076', 83 | 'Reporting0077', 84 | 'Reporting0078', 85 | 'Reporting0079', 86 | 'Reporting0080', 87 | 'Reporting0081', 88 | 'Reporting0082', 89 | 'Reporting0083', 90 | 'Reporting0084', 91 | 'Reporting0085', 92 | 'Reporting0086', 93 | 'Reporting0087' 94 | ] 95 | export const DRONE_SCAN_FRAMES = [ 96 | 'Anime_Scan_Drone0002', 97 | 'Anime_Scan_Drone0003', 98 | 'Anime_Scan_Drone0004', 99 | 'Anime_Scan_Drone0005', 100 | 'Anime_Scan_Drone0006', 101 | 'Anime_Scan_Drone0007', 102 | 'Anime_Scan_Drone0008', 103 | 'Anime_Scan_Drone0009', 104 | 'Anime_Scan_Drone0010', 105 | 'Anime_Scan_Drone0011', 106 | 'Anime_Scan_Drone0012', 107 | 'Anime_Scan_Drone0013', 108 | 'Anime_Scan_Drone0014', 109 | 'Anime_Scan_Drone0015', 110 | 'Anime_Scan_Drone0016', 111 | 'Anime_Scan_Drone0017', 112 | 'Anime_Scan_Drone0018', 113 | 'Anime_Scan_Drone0019', 114 | 'Anime_Scan_Drone0020', 115 | 'Anime_Scan_Drone0021', 116 | 'Anime_Scan_Drone0022', 117 | 'Anime_Scan_Drone0023', 118 | 'Anime_Scan_Drone0024', 119 | 'Anime_Scan_Drone0025', 120 | 'Anime_Scan_Drone0026', 121 | 'Anime_Scan_Drone0027', 122 | 'Anime_Scan_Drone0028', 123 | 'Anime_Scan_Drone0029', 124 | 'Anime_Scan_Drone0030', 125 | 'Anime_Scan_Drone0031', 126 | 'Anime_Scan_Drone0032', 127 | 'Anime_Scan_Drone0033', 128 | 'Anime_Scan_Drone0034', 129 | 'Anime_Scan_Drone0035', 130 | 'Anime_Scan_Drone0036', 131 | 'Anime_Scan_Drone0037', 132 | 'Anime_Scan_Drone0038', 133 | 'Anime_Scan_Drone0039', 134 | 'Anime_Scan_Drone0040', 135 | 'Anime_Scan_Drone0041', 136 | 'Anime_Scan_Drone0042', 137 | 'Anime_Scan_Drone0043', 138 | 'Anime_Scan_Drone0044', 139 | 'Anime_Scan_Drone0045', 140 | 'Anime_Scan_Drone0046', 141 | 'Anime_Scan_Drone0047', 142 | 'Anime_Scan_Drone0048', 143 | 'Anime_Scan_Drone0049', 144 | 'Anime_Scan_Drone0050', 145 | 'Anime_Scan_Drone0051', 146 | 'Anime_Scan_Drone0052', 147 | 'Anime_Scan_Drone0053', 148 | 'Anime_Scan_Drone0054', 149 | 'Anime_Scan_Drone0055', 150 | 'Anime_Scan_Drone0056', 151 | 'Anime_Scan_Drone0057', 152 | 'Anime_Scan_Drone0058', 153 | 'Anime_Scan_Drone0059', 154 | 'Anime_Scan_Drone0060', 155 | 'Anime_Scan_Drone0061', 156 | 'Anime_Scan_Drone0062', 157 | 'Anime_Scan_Drone0063', 158 | 'Anime_Scan_Drone0064', 159 | 'Anime_Scan_Drone0065', 160 | 'Anime_Scan_Drone0066', 161 | 'Anime_Scan_Drone0067', 162 | 'Anime_Scan_Drone0068' 163 | ] 164 | 165 | export const BEAM_FRAMES = [ 166 | 'Special_Beam_Canon0001', 167 | 'Special_Beam_Canon0002', 168 | 'Special_Beam_Canon0003', 169 | 'Special_Beam_Canon0004', 170 | 'Special_Beam_Canon0005', 171 | 'Special_Beam_Canon0006', 172 | 'Special_Beam_Canon0007', 173 | 'Special_Beam_Canon0008', 174 | 'Special_Beam_Canon0009', 175 | 'Special_Beam_Canon0010', 176 | 'Special_Beam_Canon0011', 177 | 'Special_Beam_Canon0012', 178 | 'Special_Beam_Canon0013', 179 | 'Special_Beam_Canon0014', 180 | 'Special_Beam_Canon0015', 181 | 'Special_Beam_Canon0016', 182 | 'Special_Beam_Canon0017', 183 | 'Special_Beam_Canon0018', 184 | 'Special_Beam_Canon0019', 185 | 'Special_Beam_Canon0020', 186 | 'Special_Beam_Canon0021', 187 | 'Special_Beam_Canon0022', 188 | 'Special_Beam_Canon0023', 189 | 'Special_Beam_Canon0024', 190 | 'Special_Beam_Canon0025', 191 | 'Special_Beam_Canon0026', 192 | 'Special_Beam_Canon0027', 193 | 'Special_Beam_Canon0028', 194 | 'Special_Beam_Canon0029', 195 | 'Special_Beam_Canon0030', 196 | 'Special_Beam_Canon0031', 197 | 'Special_Beam_Canon0032', 198 | 'Special_Beam_Canon0033', 199 | 'Special_Beam_Canon0034', 200 | 'Special_Beam_Canon0035', 201 | 'Special_Beam_Canon0036', 202 | 'Special_Beam_Canon0037', 203 | 'Special_Beam_Canon0038', 204 | 'Special_Beam_Canon0039', 205 | 'Special_Beam_Canon0040', 206 | 'Special_Beam_Canon0041', 207 | 'Special_Beam_Canon0042', 208 | 'Special_Beam_Canon0043', 209 | 'Special_Beam_Canon0044', 210 | 'Special_Beam_Canon0045', 211 | 'Special_Beam_Canon0046', 212 | 'Special_Beam_Canon0047', 213 | 'Special_Beam_Canon0048', 214 | 'Special_Beam_Canon0049', 215 | 'Special_Beam_Canon0050' 216 | ] 217 | 218 | export const FEEDBACK_SCAN_FRAMES = [ 219 | 'FeedBack_Scan0003', 220 | 'FeedBack_Scan0005', 221 | 'FeedBack_Scan0007', 222 | 'FeedBack_Scan0009', 223 | 'FeedBack_Scan0011', 224 | 'FeedBack_Scan0013', 225 | 'FeedBack_Scan0015', 226 | 'FeedBack_Scan0017', 227 | 'FeedBack_Scan0019', 228 | 'FeedBack_Scan0021', 229 | 'FeedBack_Scan0023', 230 | 'FeedBack_Scan0025', 231 | 'FeedBack_Scan0027', 232 | 'FeedBack_Scan0029', 233 | 'FeedBack_Scan0031', 234 | 'FeedBack_Scan0033', 235 | 'FeedBack_Scan0035', 236 | 'FeedBack_Scan0037', 237 | 'FeedBack_Scan0039', 238 | 'FeedBack_Scan0041', 239 | 'FeedBack_Scan0043', 240 | 'FeedBack_Scan0045', 241 | 'FeedBack_Scan0047', 242 | 'FeedBack_Scan0049', 243 | 'FeedBack_Scan0051', 244 | 'FeedBack_Scan0053', 245 | 'FeedBack_Scan0055', 246 | 'FeedBack_Scan0057' 247 | ] 248 | 249 | const crabe1 = [ 250 | 'crabe1anime0001', 251 | 'crabe1anime0003', 252 | 'crabe1anime0005', 253 | 'crabe1anime0007', 254 | 'crabe1anime0009', 255 | 'crabe1anime0011', 256 | 'crabe1anime0013', 257 | 'crabe1anime0015', 258 | 'crabe1anime0017', 259 | 'crabe1anime0019', 260 | 'crabe1anime0021', 261 | 'crabe1anime0023', 262 | 'crabe1anime0025', 263 | 'crabe1anime0027', 264 | 'crabe1anime0029', 265 | 'crabe1anime0031', 266 | 'crabe1anime0033', 267 | 'crabe1anime0035', 268 | 'crabe1anime0037', 269 | 'crabe1anime0039', 270 | 'crabe1anime0041', 271 | 'crabe1anime0043', 272 | 'crabe1anime0045' 273 | ] 274 | 275 | const crabe2 = [ 276 | 'crabe2anime0001', 277 | 'crabe2anime0003', 278 | 'crabe2anime0005', 279 | 'crabe2anime0007', 280 | 'crabe2anime0009', 281 | 'crabe2anime0011', 282 | 'crabe2anime0013', 283 | 'crabe2anime0015', 284 | 'crabe2anime0017', 285 | 'crabe2anime0019', 286 | 'crabe2anime0021', 287 | 'crabe2anime0023', 288 | 'crabe2anime0025', 289 | 'crabe2anime0027', 290 | 'crabe2anime0029', 291 | 'crabe2anime0031', 292 | 'crabe2anime0033', 293 | 'crabe2anime0035', 294 | 'crabe2anime0037', 295 | 'crabe2anime0039', 296 | 'crabe2anime0041', 297 | 'crabe2anime0043', 298 | 'crabe2anime0045' 299 | ] 300 | 301 | const crabe3 = [ 302 | 'crabe3anime0001', 303 | 'crabe3anime0003', 304 | 'crabe3anime0005', 305 | 'crabe3anime0007', 306 | 'crabe3anime0009', 307 | 'crabe3anime0011', 308 | 'crabe3anime0013', 309 | 'crabe3anime0015', 310 | 'crabe3anime0017', 311 | 'crabe3anime0019', 312 | 'crabe3anime0021', 313 | 'crabe3anime0023', 314 | 'crabe3anime0025', 315 | 'crabe3anime0027', 316 | 'crabe3anime0029', 317 | 'crabe3anime0031', 318 | 'crabe3anime0033', 319 | 'crabe3anime0035', 320 | 'crabe3anime0037', 321 | 'crabe3anime0039', 322 | 'crabe3anime0041', 323 | 'crabe3anime0043', 324 | 'crabe3anime0045' 325 | ] 326 | 327 | const crabe4 = [ 328 | 'crabe4anime0001', 329 | 'crabe4anime0003', 330 | 'crabe4anime0005', 331 | 'crabe4anime0007', 332 | 'crabe4anime0009', 333 | 'crabe4anime0011', 334 | 'crabe4anime0013', 335 | 'crabe4anime0015', 336 | 'crabe4anime0017', 337 | 'crabe4anime0019', 338 | 'crabe4anime0021', 339 | 'crabe4anime0023', 340 | 'crabe4anime0025', 341 | 'crabe4anime0027', 342 | 'crabe4anime0029', 343 | 'crabe4anime0031', 344 | 'crabe4anime0033', 345 | 'crabe4anime0035', 346 | 'crabe4anime0037', 347 | 'crabe4anime0039', 348 | 'crabe4anime0041', 349 | 'crabe4anime0043', 350 | 'crabe4anime0045' 351 | ] 352 | 353 | const monstreaggro = [ 354 | 'monstreanimeaggro0001', 355 | 'monstreanimeaggro0003', 356 | 'monstreanimeaggro0005', 357 | 'monstreanimeaggro0007', 358 | 'monstreanimeaggro0009', 359 | 'monstreanimeaggro0011', 360 | 'monstreanimeaggro0013', 361 | 'monstreanimeaggro0015', 362 | 'monstreanimeaggro0017', 363 | 'monstreanimeaggro0019', 364 | 'monstreanimeaggro0021', 365 | 'monstreanimeaggro0023', 366 | 'monstreanimeaggro0025', 367 | 'monstreanimeaggro0027', 368 | 'monstreanimeaggro0029', 369 | 'monstreanimeaggro0031', 370 | 'monstreanimeaggro0033', 371 | 'monstreanimeaggro0035', 372 | 'monstreanimeaggro0037', 373 | 'monstreanimeaggro0039', 374 | 'monstreanimeaggro0041', 375 | 'monstreanimeaggro0043', 376 | 'monstreanimeaggro0045' 377 | ] 378 | 379 | const monstre = [ 380 | 'monstre0001', 381 | 'monstre0003', 382 | 'monstre0005', 383 | 'monstre0007', 384 | 'monstre0009', 385 | 'monstre0011', 386 | 'monstre0013', 387 | 'monstre0015', 388 | 'monstre0017', 389 | 'monstre0019', 390 | 'monstre0021', 391 | 'monstre0023', 392 | 'monstre0025', 393 | 'monstre0027', 394 | 'monstre0029', 395 | 'monstre0031', 396 | 'monstre0033', 397 | 'monstre0035', 398 | 'monstre0037', 399 | 'monstre0039', 400 | 'monstre0041', 401 | 'monstre0043', 402 | 'monstre0045' 403 | ] 404 | 405 | const pieuvre1 = [ 406 | 'pieuvre10001', 407 | 'pieuvre10003', 408 | 'pieuvre10005', 409 | 'pieuvre10007', 410 | 'pieuvre10009', 411 | 'pieuvre10011', 412 | 'pieuvre10013', 413 | 'pieuvre10015', 414 | 'pieuvre10017', 415 | 'pieuvre10019', 416 | 'pieuvre10021', 417 | 'pieuvre10023', 418 | 'pieuvre10025', 419 | 'pieuvre10027', 420 | 'pieuvre10029', 421 | 'pieuvre10031', 422 | 'pieuvre10033', 423 | 'pieuvre10035', 424 | 'pieuvre10037', 425 | 'pieuvre10039', 426 | 'pieuvre10041', 427 | 'pieuvre10043', 428 | 'pieuvre10045' 429 | ] 430 | 431 | const pieuvre2 = [ 432 | 'pieuvre2anime0001', 433 | 'pieuvre2anime0003', 434 | 'pieuvre2anime0005', 435 | 'pieuvre2anime0007', 436 | 'pieuvre2anime0009', 437 | 'pieuvre2anime0011', 438 | 'pieuvre2anime0013', 439 | 'pieuvre2anime0015', 440 | 'pieuvre2anime0017', 441 | 'pieuvre2anime0019', 442 | 'pieuvre2anime0021', 443 | 'pieuvre2anime0023', 444 | 'pieuvre2anime0025', 445 | 'pieuvre2anime0027', 446 | 'pieuvre2anime0029', 447 | 'pieuvre2anime0031', 448 | 'pieuvre2anime0033', 449 | 'pieuvre2anime0035', 450 | 'pieuvre2anime0037', 451 | 'pieuvre2anime0039', 452 | 'pieuvre2anime0041', 453 | 'pieuvre2anime0043', 454 | 'pieuvre2anime0045', 455 | 'pieuvre2anime0047' 456 | ] 457 | 458 | const pieuvre3 = [ 459 | 'pieuvre3anime0001', 460 | 'pieuvre3anime0003', 461 | 'pieuvre3anime0005', 462 | 'pieuvre3anime0007', 463 | 'pieuvre3anime0009', 464 | 'pieuvre3anime0011', 465 | 'pieuvre3anime0013', 466 | 'pieuvre3anime0015', 467 | 'pieuvre3anime0017', 468 | 'pieuvre3anime0019', 469 | 'pieuvre3anime0021', 470 | 'pieuvre3anime0023', 471 | 'pieuvre3anime0025', 472 | 'pieuvre3anime0027', 473 | 'pieuvre3anime0029', 474 | 'pieuvre3anime0031', 475 | 'pieuvre3anime0033', 476 | 'pieuvre3anime0035', 477 | 'pieuvre3anime0037', 478 | 'pieuvre3anime0039', 479 | 'pieuvre3anime0041', 480 | 'pieuvre3anime0043', 481 | 'pieuvre3anime0045' 482 | ] 483 | 484 | const pieuvre4 = [ 485 | 'pieuvre4anime0001', 486 | 'pieuvre4anime0003', 487 | 'pieuvre4anime0005', 488 | 'pieuvre4anime0007', 489 | 'pieuvre4anime0009', 490 | 'pieuvre4anime0011', 491 | 'pieuvre4anime0013', 492 | 'pieuvre4anime0015', 493 | 'pieuvre4anime0017', 494 | 'pieuvre4anime0019', 495 | 'pieuvre4anime0021', 496 | 'pieuvre4anime0023', 497 | 'pieuvre4anime0025', 498 | 'pieuvre4anime0027', 499 | 'pieuvre4anime0029', 500 | 'pieuvre4anime0031', 501 | 'pieuvre4anime0033', 502 | 'pieuvre4anime0035', 503 | 'pieuvre4anime0037', 504 | 'pieuvre4anime0039', 505 | 'pieuvre4anime0041', 506 | 'pieuvre4anime0043', 507 | 'pieuvre4anime0045' 508 | ] 509 | 510 | const poisson1 = [ 511 | 'poisson1anime0001', 512 | 'poisson1anime0003', 513 | 'poisson1anime0005', 514 | 'poisson1anime0007', 515 | 'poisson1anime0009', 516 | 'poisson1anime0011', 517 | 'poisson1anime0013', 518 | 'poisson1anime0015', 519 | 'poisson1anime0017', 520 | 'poisson1anime0019', 521 | 'poisson1anime0021', 522 | 'poisson1anime0023', 523 | 'poisson1anime0025', 524 | 'poisson1anime0027', 525 | 'poisson1anime0029', 526 | 'poisson1anime0031', 527 | 'poisson1anime0033', 528 | 'poisson1anime0035', 529 | 'poisson1anime0037', 530 | 'poisson1anime0039', 531 | 'poisson1anime0041', 532 | 'poisson1anime0043', 533 | 'poisson1anime0045' 534 | ] 535 | 536 | const poisson2 = [ 537 | 'poisson2anime0001', 538 | 'poisson2anime0003', 539 | 'poisson2anime0005', 540 | 'poisson2anime0007', 541 | 'poisson2anime0009', 542 | 'poisson2anime0011', 543 | 'poisson2anime0013', 544 | 'poisson2anime0015', 545 | 'poisson2anime0017', 546 | 'poisson2anime0019', 547 | 'poisson2anime0021', 548 | 'poisson2anime0023', 549 | 'poisson2anime0025', 550 | 'poisson2anime0027', 551 | 'poisson2anime0029', 552 | 'poisson2anime0031', 553 | 'poisson2anime0033', 554 | 'poisson2anime0035', 555 | 'poisson2anime0037', 556 | 'poisson2anime0039', 557 | 'poisson2anime0041', 558 | 'poisson2anime0043', 559 | 'poisson2anime0045' 560 | ] 561 | 562 | const poisson3 = [ 563 | 'poisson3anime0001', 564 | 'poisson3anime0003', 565 | 'poisson3anime0005', 566 | 'poisson3anime0007', 567 | 'poisson3anime0009', 568 | 'poisson3anime0011', 569 | 'poisson3anime0013', 570 | 'poisson3anime0015', 571 | 'poisson3anime0017', 572 | 'poisson3anime0019', 573 | 'poisson3anime0021', 574 | 'poisson3anime0023', 575 | 'poisson3anime0025', 576 | 'poisson3anime0027', 577 | 'poisson3anime0029', 578 | 'poisson3anime0031', 579 | 'poisson3anime0033', 580 | 'poisson3anime0035', 581 | 'poisson3anime0037', 582 | 'poisson3anime0039', 583 | 'poisson3anime0041', 584 | 'poisson3anime0043', 585 | 'poisson3anime0045' 586 | ] 587 | 588 | const poisson4 = [ 589 | 'poisson4anime0001', 590 | 'poisson4anime0003', 591 | 'poisson4anime0005', 592 | 'poisson4anime0007', 593 | 'poisson4anime0009', 594 | 'poisson4anime0011', 595 | 'poisson4anime0013', 596 | 'poisson4anime0015', 597 | 'poisson4anime0017', 598 | 'poisson4anime0019', 599 | 'poisson4anime0021', 600 | 'poisson4anime0023', 601 | 'poisson4anime0025', 602 | 'poisson4anime0027', 603 | 'poisson4anime0029', 604 | 'poisson4anime0031', 605 | 'poisson4anime0033', 606 | 'poisson4anime0035', 607 | 'poisson4anime0037', 608 | 'poisson4anime0039', 609 | 'poisson4anime0041', 610 | 'poisson4anime0043', 611 | 'poisson4anime0045' 612 | ] 613 | 614 | export const FISH_FRAMES = [ 615 | [pieuvre1, pieuvre2, pieuvre3, pieuvre4], 616 | [poisson1, poisson2, poisson3, poisson4], 617 | [crabe1, crabe2, crabe3, crabe4] 618 | ] 619 | export const UGLY_FRAMES = [ 620 | monstre, 621 | monstreaggro 622 | ] 623 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/darkness.ts: -------------------------------------------------------------------------------- 1 | import { ViewModule } from './ViewModule.js' 2 | import { getRenderer, flagForDestructionOnReinit } from '../core/rendering.js' 3 | import { HEIGHT, WIDTH } from '../core/constants.js' 4 | import { DARK_SCAN_RANGE, LIGHT_SCAN_RANGE } from './gameConstants.js' 5 | import { fit } from './utils.js' 6 | 7 | export const DISC_RADIUS = 218 8 | const FOG_SCALE = 0.4 9 | 10 | export function initDarkness (this: ViewModule, layer: PIXI.Container) { 11 | if (this.canvasMode) { 12 | return 13 | } 14 | 15 | const darkness = new PIXI.Sprite(PIXI.Texture.WHITE) 16 | darkness.width = WIDTH 17 | darkness.height = HEIGHT 18 | darkness.tint = 0x0 19 | darkness.alpha = 0.33 20 | // darkness.alpha = 1 21 | 22 | const buffer = new PIXI.Container() 23 | 24 | const backdrop = new PIXI.Sprite(PIXI.Texture.WHITE) 25 | backdrop.width = WIDTH 26 | backdrop.height = HEIGHT 27 | 28 | const discLayer = new PIXI.Container() 29 | const discs = [[], []] 30 | const texture = PIXI.RenderTexture.create({ width: WIDTH * FOG_SCALE, height: HEIGHT * FOG_SCALE }) 31 | // TODO: flag texture for destruction using flagForDestructionOnReinit(backdrop) 32 | const mask = new PIXI.Sprite(texture) 33 | 34 | buffer.scale.set(FOG_SCALE) 35 | 36 | const options = this.api.options 37 | for (let i = 0; i < this.globalData.playerCount; ++i) { 38 | for (let k = 0; k < this.globalData.dronesPerPlayer; ++k) { 39 | const disc = this.asLayer(initDisc(LIGHT_SCAN_RANGE)) 40 | discs[i][k] = disc 41 | discLayer.addChild(disc) 42 | 43 | Object.defineProperty(disc, 'visible', { get: () => options.darkness === true || options.darkness === i }) 44 | } 45 | } 46 | Object.defineProperty(darkness, 'visible', { get: () => options.darkness !== false && !this.canvasMode }) 47 | 48 | buffer.addChild(backdrop) 49 | buffer.addChild(discLayer) 50 | 51 | mask.scale.set(1 / FOG_SCALE) 52 | 53 | const renderFog = () => { 54 | if (!(texture as any)._destroyed) { 55 | getRenderer().render(buffer, texture) 56 | } 57 | } 58 | this.api.renderFog = renderFog 59 | 60 | this.discs = discs 61 | 62 | layer.addChild(darkness) 63 | layer.addChild(mask) 64 | darkness.mask = mask 65 | 66 | const xOffset = WIDTH / 2 - this.globalData.width / 2 * this.globalData.ratio 67 | discLayer.x = xOffset 68 | discLayer.y = 300 * this.globalData.ratio 69 | } 70 | 71 | function initDisc (radius) { 72 | return (layer) => { 73 | const normal = PIXI.Sprite.from('disc_black.png') 74 | layer.addChild(normal) 75 | normal.anchor.set(0.5) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/events.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | BUILD: 0, 3 | MOVE: 1 4 | } 5 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/gameConstants.ts: -------------------------------------------------------------------------------- 1 | export const DRONE_HIT_RANGE = 200 2 | export const DRONES_PER_PLAYER = 2 3 | export const DRONE_MAX_BATTERY = 30 4 | export const LIGHT_BATTERY_COST = 5 5 | export const DRONE_BATTERY_REGEN = 1 6 | export const EAT_RADIUS = 300 7 | 8 | export const DARK_SCAN_RANGE = 800 9 | export const LIGHT_SCAN_RANGE = 2000 10 | export const FISH_HEARING_RANGE = (DARK_SCAN_RANGE + LIGHT_SCAN_RANGE) / 2 11 | 12 | export const SCAN_CODE_UNSCANNED = 0 13 | export const SCAN_CODE_BUFFERED = 1 14 | export const SCAN_CODE_SAVED = 2 15 | export const SCAN_CODE_LOST = 3 16 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/global/core.d.ts: -------------------------------------------------------------------------------- 1 | declare module '../core/*.js' { 2 | export const WIDTH: number 3 | export const HEIGHT: number 4 | export const BASE_FRAME_DURATION: number 5 | export function bell (t: number): number 6 | export function elastic (t: number): number 7 | export function ease (t: number): number 8 | export function easeIn (t: number): number 9 | export function easeOut (t: number): number 10 | export function randInt (a: number, b?: number): number 11 | export function lerp (a: number, b: number, u: number): number 12 | export function unlerpUnclamped (a: number, b: number, v: number): number 13 | export function lerpAngle (start: number, end: number, amount: number, maxDelta?: number): number 14 | export function lerpPosition (from: {x: number, y: number}, to: {x: number, y: number}, p: number): {x: number, y: number} 15 | export function lerpColor (start: number, end: number, amount: number): number 16 | export function unlerp (a: number, b: number, v): number 17 | export function pushAll (self: {push: (item: T) => void}, arr: T[]): void 18 | export function fitAspectRatio (srcWidth: number, srcHeight: number, maxWidth: number, maxHeight: number, padding?: number): number 19 | export function paddingString (word: string, width: number, char: string): string 20 | export function flagForDestructionOnReinit (destroyable: any): void 21 | export function getRenderer(): PIXI.Renderer 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/global/global.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | debug: any 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/types.ts: -------------------------------------------------------------------------------- 1 | export type ContainerConsumer = (layer: PIXI.Container) => void 2 | 3 | /** 4 | * Given by the SDK 5 | */ 6 | export interface FrameInfo { 7 | number: number 8 | frameDuration: number 9 | date: number 10 | } 11 | /** 12 | * Given by the SDK 13 | */ 14 | export interface CanvasInfo { 15 | width: number 16 | height: number 17 | oversampling: number 18 | } 19 | /** 20 | * Given by the SDK 21 | */ 22 | export interface PlayerInfo { 23 | name: string 24 | avatar: PIXI.Texture 25 | color: number 26 | index: number 27 | isMe: boolean 28 | number: number 29 | type?: string 30 | } 31 | 32 | export interface EventDto { 33 | type: number 34 | animData: AnimData 35 | } 36 | 37 | export interface PlayerDto { 38 | message: string 39 | } 40 | 41 | export interface FrameDataDTO { 42 | scans: ScanSummaryDto[][][] 43 | fishes: FishDto[] 44 | uglies: UglyDto[] 45 | scores: number[] 46 | drones: DroneDto[][] 47 | fishMap: Record 48 | uglyMap: Record 49 | } 50 | 51 | export interface FrameData extends FrameDataDTO { 52 | previous: FrameData 53 | frameInfo: FrameInfo 54 | } 55 | 56 | export interface CoordDto { 57 | x: number 58 | y: number 59 | } 60 | 61 | export interface GlobalDataDTO { 62 | width: number 63 | height: number 64 | fishes: FishGlobalDto[] 65 | fishMap: Record 66 | uglies: number[] 67 | ratio: number 68 | dronesPerPlayer: number 69 | fishWillFlee: boolean 70 | isDemo: boolean 71 | simpleScans: boolean 72 | } 73 | export interface GlobalData extends GlobalDataDTO { 74 | players: PlayerInfo[] 75 | playerCount: number 76 | } 77 | 78 | export interface AnimData { 79 | start: number 80 | end: number 81 | } 82 | 83 | export interface Effect { 84 | busy: boolean 85 | display: PIXI.DisplayObject 86 | } 87 | 88 | /* View entities */ 89 | export interface FishGlobalDto { 90 | id: number 91 | color: number 92 | type: number 93 | } 94 | 95 | export interface EntityDto { 96 | id: number 97 | x: number 98 | y: number 99 | vx?: number 100 | vy?: number 101 | } 102 | export interface FishDto extends EntityDto { 103 | isFleeing: boolean 104 | } 105 | 106 | export interface UglyDto extends EntityDto { 107 | foundTarget: boolean 108 | } 109 | export interface ScanDto { 110 | color: number 111 | type: number 112 | } 113 | export interface DroneDto extends EntityDto { 114 | targetX: number 115 | targetY: number 116 | battery: number 117 | light: boolean 118 | dead: boolean 119 | dieAt: number 120 | message: string 121 | scans: ScanDto[] 122 | didReport: boolean 123 | fishesScannedThisTurn: number[] 124 | } 125 | 126 | export interface ScanSummaryDto { 127 | code: number 128 | hasBonus: boolean 129 | } 130 | 131 | /* View entities */ 132 | export interface Entity { 133 | container: PIXI.Container 134 | sprite: PIXI.Sprite 135 | id: number 136 | targetScaleX: number 137 | scaler: PIXI.Container 138 | } 139 | 140 | export interface Fish extends Entity { 141 | } 142 | 143 | export interface Drone extends Entity { 144 | container: PIXI.Container 145 | lightRadius: PIXI.Graphics 146 | radius: PIXI.Graphics 147 | engineRadius: PIXI.Graphics 148 | reportAnimation: PIXI.AnimatedSprite 149 | isDeadNow: boolean 150 | scanAnimation: PIXI.AnimatedSprite 151 | } 152 | 153 | export interface Ugly extends Entity { 154 | container: PIXI.Container 155 | aggroSprite: PIXI.AnimatedSprite 156 | } 157 | 158 | export interface SquidGrid { 159 | array: SquidGridCell[][] 160 | container: PIXI.Container 161 | } 162 | 163 | export interface SquidGridCell { 164 | sprite: PIXI.Sprite 165 | bonusIcon: PIXI.DisplayObject 166 | lostIcon: PIXI.DisplayObject 167 | feedbackScanAnimation: PIXI.AnimatedSprite 168 | } 169 | -------------------------------------------------------------------------------- /src/main/resources/view/ts/utils.ts: -------------------------------------------------------------------------------- 1 | import * as utils from '../core/utils.js' 2 | 3 | export function setAnimationProgress (fx: PIXI.AnimatedSprite, progress: number): number { 4 | let idx = Math.floor(progress * fx.totalFrames) 5 | idx = Math.min(fx.totalFrames - 1, idx) 6 | fx.gotoAndStop(idx) 7 | return idx 8 | } 9 | 10 | export function distance (a, b) { 11 | return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) 12 | } 13 | 14 | export function fit (entity: PIXI.DisplayObject & {width: number, height: number}, maxWidth: number, maxHeight: number): void { 15 | entity.scale.set(utils.fitAspectRatio(entity.width, entity.height, maxWidth, maxHeight)) 16 | } 17 | 18 | export interface Point { 19 | x: number 20 | y: number 21 | } 22 | 23 | export function setSize (sprite: PIXI.Sprite | PIXI.Container, size: number): void { 24 | sprite.width = size 25 | sprite.height = size 26 | } 27 | 28 | export function bounce (t: number): number { 29 | return 1 + (Math.sin(t * 10) * 0.5 * Math.cos(t * 3.14 / 2)) * (1 - t) * (1 - t) 30 | } 31 | 32 | export function generateText (text: string | number, color: number, size: number, strokeThickness = 4): PIXI.Text { 33 | const drawnText = new PIXI.Text(typeof text === 'number' ? '' + text : text, { 34 | fontSize: Math.round(size) + 'px', 35 | fontFamily: 'Arial', 36 | fontWeight: 'bold', 37 | fill: color, 38 | stroke: 0x0, 39 | strokeThickness, 40 | lineHeight: Math.round(size) 41 | }) 42 | drawnText.anchor.x = 0.5 43 | drawnText.anchor.y = 0.5 44 | return drawnText 45 | } 46 | 47 | export function last (arr: T[]): T { 48 | return arr[arr.length - 1] 49 | } 50 | 51 | export function keyOf (x: number, y: number): string { 52 | return `${x},${y}` 53 | } 54 | export function angleDiff (a: number, b: number): number { 55 | return Math.abs(utils.lerpAngle(a, b, 0) - utils.lerpAngle(a, b, 1)) 56 | } 57 | export function pad (text: string|number, n: number, char: string = '0'): string { 58 | let out = text.toString() 59 | while (out.length < n) { 60 | out = char + out 61 | } 62 | return out 63 | } 64 | 65 | export function randomChoice (rand: number, coeffs: number[]): number { 66 | const total = coeffs.reduce((a, b) => a + b, 0) 67 | const b = 1 / total 68 | const weights = coeffs.map(v => v * b) 69 | let cur = 0 70 | for (let i = 0; i < weights.length; ++i) { 71 | cur += weights[i] 72 | if (cur >= rand) { 73 | return i 74 | } 75 | } 76 | return 0 77 | } 78 | 79 | export function sum (arr: number[]): number { 80 | return arr.reduce((a, b) => a + b, 0) 81 | } 82 | 83 | const TYPE_NAMES = ['Pieuvre', 'Fish', 'Crabe'] 84 | export function getFishPng (colorIdx: number, typeIdx: number): string { 85 | const typeName = TYPE_NAMES[typeIdx] 86 | return `${typeName}_${colorIdx + 1}` 87 | } 88 | -------------------------------------------------------------------------------- /src/main/resources/view/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "checkJs": false, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "isolatedModules": true, 9 | "jsx": "react", 10 | "lib": [ 11 | "dom", 12 | "dom.iterable", 13 | "esnext" 14 | ], 15 | "module": "es2015", 16 | "moduleResolution": "node", 17 | "noEmit": false, 18 | "noImplicitReturns": true, 19 | "noImplicitAny": false, 20 | "resolveJsonModule": false, 21 | "skipLibCheck": true, 22 | "sourceMap": false, 23 | "target": "es6", 24 | "outDir": "graphics", 25 | "types": ["pixi.js"] 26 | }, 27 | "include": [ 28 | "ts" 29 | ] 30 | } -------------------------------------------------------------------------------- /src/test/java/Fall2023Main.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | 4 | import com.codingame.gameengine.runner.MultiplayerGameRunner; 5 | import com.google.common.io.Files; 6 | 7 | public class Fall2023Main { 8 | public static void main(String[] args) throws IOException, InterruptedException { 9 | 10 | MultiplayerGameRunner gameRunner = new MultiplayerGameRunner(); 11 | // gameRunner.setSeed(-8358938852454912011l); 12 | gameRunner.addAgent("python3 config/Boss.py", "TestBoss_1"); 13 | gameRunner.addAgent("python3 config/Boss.py", "TestBoss_2"); 14 | gameRunner.setLeagueLevel(4); 15 | 16 | gameRunner.start(); 17 | } 18 | 19 | private static String compile(String botFile) throws IOException, InterruptedException { 20 | 21 | File outFolder = Files.createTempDir(); 22 | 23 | System.out.println("Compiling Boss.java... " + botFile); 24 | Process compileProcess = Runtime.getRuntime() 25 | .exec(new String[] { "bash", "-c", "javac " + botFile + " -d " + outFolder.getAbsolutePath() }); 26 | compileProcess.waitFor(); 27 | return "java -cp " + outFolder + " Player"; 28 | } 29 | 30 | private static String[] compileTS(String botFile) throws IOException, InterruptedException { 31 | 32 | System.out.println("Compiling ... " + botFile); 33 | 34 | Process compileProcess = Runtime.getRuntime().exec( 35 | new String[] { "bash", "-c", "npx tsc --target ES2018 --inlineSourceMap --types ./typescript/readline/ " 36 | + botFile + " --outFile /tmp/Boss.js" } 37 | ); 38 | compileProcess.waitFor(); 39 | 40 | return new String[] { "bash", "-c", "node -r ./typescript/polyfill.js /tmp/Boss.js" }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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{HH:mm:ss}] %-5p : %c{1} - %m%n 9 | 10 | rootLogger.level = info 11 | rootLogger.appenderRef.console.ref = CONSOLE 12 | -------------------------------------------------------------------------------- /starterAIs/README.md: -------------------------------------------------------------------------------- 1 | # Starter AIs 2 | 3 | These starter AIs are meant to help new players to parse the input and have a small structure to begin with. 4 | 5 | A few lines of logic on top of these should be enough to beat the first bosses. 6 | 7 | Feel free to contribute your own starter AI in another language to help other players by making a PR. We'll be happy to add your starter AI to the list. 8 | 9 | Thank you! 10 | -------------------------------------------------------------------------------- /starterAIs/SS_Starter.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | 3 | // Define the data structures as records 4 | record Vector(int x, int y) {} 5 | 6 | record FishDetail(int color, int type) {} 7 | 8 | record Fish(int fishId, Vector pos, Vector speed, FishDetail detail) {} 9 | 10 | record Drone(int droneId, Vector pos, boolean dead, int battery, List scans) {} 11 | 12 | record RadarBlip(int fishId, String dir) {} 13 | 14 | class Player { 15 | public static void main(String[] args) { 16 | Scanner in = new Scanner(System.in); 17 | 18 | Map fishDetails = new HashMap<>(); 19 | 20 | int fishCount = in.nextInt(); 21 | for (int i = 0; i < fishCount; i++) { 22 | int fishId = in.nextInt(); 23 | int color = in.nextInt(); 24 | int type = in.nextInt(); 25 | fishDetails.put(fishId, new FishDetail(color, type)); 26 | } 27 | 28 | // game loop 29 | while (true) { 30 | List myScans = new ArrayList<>(); 31 | List foeScans = new ArrayList<>(); 32 | Map droneById = new HashMap<>(); 33 | List myDrones = new ArrayList<>(); 34 | List foeDrones = new ArrayList<>(); 35 | List visibleFishes = new ArrayList<>(); 36 | Map> myRadarBlips = new HashMap<>(); 37 | 38 | int myScore = in.nextInt(); 39 | int foeScore = in.nextInt(); 40 | 41 | int myScanCount = in.nextInt(); 42 | for (int i = 0; i < myScanCount; i++) { 43 | int fishId = in.nextInt(); 44 | myScans.add(fishId); 45 | } 46 | 47 | int foeScanCount = in.nextInt(); 48 | for (int i = 0; i < foeScanCount; i++) { 49 | int fishId = in.nextInt(); 50 | foeScans.add(fishId); 51 | } 52 | 53 | 54 | 55 | int myDroneCount = in.nextInt(); 56 | for (int i = 0; i < myDroneCount; i++) { 57 | int droneId = in.nextInt(); 58 | int droneX = in.nextInt(); 59 | int droneY = in.nextInt(); 60 | boolean dead = in.nextInt() == 1; 61 | int battery = in.nextInt(); 62 | Vector pos = new Vector(droneX, droneY); 63 | Drone drone = new Drone(droneId, pos, dead, battery, new ArrayList<>()); 64 | droneById.put(droneId, drone); 65 | myDrones.add(drone); 66 | myRadarBlips.put(droneId, new ArrayList<>()); 67 | } 68 | 69 | int foeDroneCount = in.nextInt(); 70 | for (int i = 0; i < foeDroneCount; i++) { 71 | int droneId = in.nextInt(); 72 | int droneX = in.nextInt(); 73 | int droneY = in.nextInt(); 74 | boolean dead = in.nextInt() == 1; 75 | int battery = in.nextInt(); 76 | Vector pos = new Vector(droneX, droneY); 77 | Drone drone = new Drone(droneId, pos, dead, battery, new ArrayList<>()); 78 | droneById.put(droneId, drone); 79 | foeDrones.add(drone); 80 | } 81 | 82 | int droneScanCount = in.nextInt(); 83 | for (int i = 0; i < droneScanCount; i++) { 84 | int droneId = in.nextInt(); 85 | int fishId = in.nextInt(); 86 | droneById.get(droneId).scans().add(fishId); 87 | } 88 | 89 | int visibleFishCount = in.nextInt(); 90 | for (int i = 0; i < visibleFishCount; i++) { 91 | int fishId = in.nextInt(); 92 | int fishX = in.nextInt(); 93 | int fishY = in.nextInt(); 94 | int fishVx = in.nextInt(); 95 | int fishVy = in.nextInt(); 96 | Vector pos = new Vector(fishX, fishY); 97 | Vector speed = new Vector(fishVx, fishVy); 98 | FishDetail detail = fishDetails.get(fishId); 99 | visibleFishes.add(new Fish(fishId, pos, speed, detail)); 100 | } 101 | 102 | int myRadarBlipCount = in.nextInt(); 103 | for (int i = 0; i < myRadarBlipCount; i++) { 104 | int droneId = in.nextInt(); 105 | int fishId = in.nextInt(); 106 | String radar = in.next(); 107 | myRadarBlips.get(droneId).add(new RadarBlip(fishId, radar)); 108 | } 109 | 110 | for (Drone drone : myDrones) { 111 | int x = drone.pos().x(); 112 | int y = drone.pos().y(); 113 | // TODO: Implement logic on where to move here 114 | int targetX = 5000; 115 | int targetY = 5000; 116 | int light = 1; 117 | 118 | System.out.println(String.format("MOVE %d %d %d", targetX, targetY, light)); 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /starterAIs/SS_Starter.js: -------------------------------------------------------------------------------- 1 | // Define the data structures as classes 2 | class Vector { 3 | constructor(x, y) { 4 | this.x = x; 5 | this.y = y; 6 | } 7 | } 8 | 9 | class FishDetail { 10 | constructor(color, type) { 11 | this.color = color; 12 | this.type = type; 13 | } 14 | } 15 | 16 | class Fish { 17 | constructor(fishId, pos, speed, detail) { 18 | this.fishId = fishId; 19 | this.pos = pos; 20 | this.speed = speed; 21 | this.detail = detail; 22 | } 23 | } 24 | 25 | class Drone { 26 | constructor(dronedId, pos, dead, battery, scans) { 27 | this.dronedId = dronedId; 28 | this.pos = pos; 29 | this.dead = dead; 30 | this.battery = battery; 31 | this.scans = scans; 32 | } 33 | } 34 | 35 | class RadarBlip { 36 | constructor(fishId, dir) { 37 | this.fishId = fishId; 38 | this.dir = dir; 39 | } 40 | } 41 | 42 | const fishDetails = new Map(); 43 | 44 | const fishCount = parseInt(readline()); 45 | for (let i = 0; i < fishCount; i++) { 46 | const [fishId, color, type] = readline().split(' ').map(Number); 47 | fishDetails.set(fishId, new FishDetail(color, type)); 48 | } 49 | 50 | // game loop 51 | while (true) { 52 | const myScans = []; 53 | const foeScans = []; 54 | const droneById = new Map(); 55 | const myDrones = []; 56 | const foeDrones = []; 57 | const visibleFishes = []; 58 | const myRadarBlips = new Map(); 59 | 60 | const myScore = parseInt(readline()); 61 | const foeScore = parseInt(readline()); 62 | 63 | const myScanCount = parseInt(readline()); 64 | for (let i = 0; i < myScanCount; i++) { 65 | const fish_id = parseInt(readline()); 66 | myScans.push(fish_id); 67 | } 68 | 69 | const foeScanCount = parseInt(readline()); 70 | for (let i = 0; i < foeScanCount; i++) { 71 | const fish_id = parseInt(readline()); 72 | foeScans.push(fish_id); 73 | } 74 | 75 | const myDroneCount = parseInt(readline()); 76 | for (let i = 0; i < myDroneCount; i++) { 77 | const [droneId, droneX, droneY, dead, battery] = readline().split(' ').map(Number); 78 | const pos = new Vector(droneX, droneY); 79 | const drone = new Drone(droneId, pos, dead, battery, []); 80 | droneById.set(droneId, drone); 81 | myDrones.push(drone); 82 | myRadarBlips.set(droneId, []); 83 | } 84 | 85 | const foeDroneCount = parseInt(readline()); 86 | for (let i = 0; i < foeDroneCount; i++) { 87 | const [droneId, droneX, droneY, dead, battery] = readline().split(' ').map(Number); 88 | const pos = new Vector(droneX, droneY); 89 | const drone = new Drone(droneId, pos, dead, battery, []); 90 | droneById.set(droneId, drone); 91 | foeDrones.push(drone); 92 | } 93 | 94 | const droneScanCount = parseInt(readline()); 95 | for (let i = 0; i < droneScanCount; i++) { 96 | const [droneId, fish_id] = readline().split(' ').map(Number); 97 | droneById.get(droneId).scans.push(fish_id); 98 | } 99 | 100 | 101 | const visibleFishCount = parseInt(readline()); 102 | for (let i = 0; i < visibleFishCount; i++) { 103 | const [fishId, fishX, fishY, fishVx, fishVy] = readline().split(' ').map(Number); 104 | const pos = new Vector(fishX, fishY); 105 | const speed = new Vector(fishVx, fishVy); 106 | visibleFishes.push(new Fish(fishId, pos, speed, fishDetails.get(fishId))); 107 | } 108 | 109 | const myRadarBlipCount = parseInt(readline()); 110 | for (let i = 0; i < myRadarBlipCount; i++) { 111 | const [_droneId, _fishId, dir] = readline().split(' '); 112 | const droneId = parseInt(_droneId); 113 | const fishId = parseInt(_fishId); 114 | myRadarBlips.get(droneId).push(new RadarBlip(fishId, dir)); 115 | } 116 | 117 | for (const drone of myDrones) { 118 | const x = drone.pos.x; 119 | const y = drone.pos.y; 120 | // TODO: Implement logic on where to move here 121 | const targetX = 5000; 122 | const targetY = 5000; 123 | const light = 1; 124 | 125 | console.log(`MOVE ${targetX} ${targetY} ${light}`); 126 | } 127 | } -------------------------------------------------------------------------------- /starterAIs/SS_Starter.py: -------------------------------------------------------------------------------- 1 | from typing import List, NamedTuple, Dict 2 | 3 | # Define the data structures as namedtuples 4 | class Vector(NamedTuple): 5 | x: int 6 | y: int 7 | 8 | class FishDetail(NamedTuple): 9 | color: int 10 | type: int 11 | 12 | class Fish(NamedTuple): 13 | fish_id: int 14 | pos: Vector 15 | speed: Vector 16 | detail: FishDetail 17 | 18 | class RadarBlip(NamedTuple): 19 | fish_id: int 20 | dir: str 21 | 22 | class Drone(NamedTuple): 23 | drone_id: int 24 | pos: Vector 25 | dead: bool 26 | battery: int 27 | scans: List[int] 28 | 29 | fish_details: Dict[int, FishDetail] = {} 30 | 31 | fish_count = int(input()) 32 | for _ in range(fish_count): 33 | fish_id, color, _type = map(int, input().split()) 34 | fish_details[fish_id] = FishDetail(color, _type) 35 | 36 | # game loop 37 | while True: 38 | my_scans: List[int] = [] 39 | foe_scans: List[int] = [] 40 | drone_by_id: Dict[int, Drone] = {} 41 | my_drones: List[Drone] = [] 42 | foe_drones: List[Drone] = [] 43 | visible_fish: List[Fish] = [] 44 | my_radar_blips: Dict[int, List[RadarBlip]] = {} 45 | 46 | my_score = int(input()) 47 | foe_score = int(input()) 48 | 49 | my_scan_count = int(input()) 50 | for _ in range(my_scan_count): 51 | fish_id = int(input()) 52 | my_scans.append(fish_id) 53 | 54 | foe_scan_count = int(input()) 55 | for _ in range(foe_scan_count): 56 | fish_id = int(input()) 57 | foe_scans.append(fish_id) 58 | 59 | my_drone_count = int(input()) 60 | for _ in range(my_drone_count): 61 | drone_id, drone_x, drone_y, dead, battery = map(int, input().split()) 62 | pos = Vector(drone_x, drone_y) 63 | drone = Drone(drone_id, pos, dead == '1', battery, []) 64 | drone_by_id[drone_id] = drone 65 | my_drones.append(drone) 66 | my_radar_blips[drone_id] = [] 67 | 68 | foe_drone_count = int(input()) 69 | for _ in range(foe_drone_count): 70 | drone_id, drone_x, drone_y, dead, battery = map(int, input().split()) 71 | pos = Vector(drone_x, drone_y) 72 | drone = Drone(drone_id, pos, dead == '1', battery, []) 73 | drone_by_id[drone_id] = drone 74 | foe_drones.append(drone) 75 | 76 | drone_scan_count = int(input()) 77 | for _ in range(drone_scan_count): 78 | drone_id, fish_id = map(int, input().split()) 79 | drone_by_id[drone_id].scans.append(fish_id) 80 | 81 | visible_fish_count = int(input()) 82 | for _ in range(visible_fish_count): 83 | fish_id, fish_x, fish_y, fish_vx, fish_vy = map(int, input().split()) 84 | pos = Vector(fish_x, fish_y) 85 | speed = Vector(fish_vx, fish_vy) 86 | visible_fish.append(Fish(fish_id, pos, speed, fish_details[fish_id])) 87 | 88 | my_radar_blip_count = int(input()) 89 | for _ in range(my_radar_blip_count): 90 | drone_id, fish_id, dir = input().split() 91 | drone_id = int(drone_id) 92 | fish_id = int(fish_id) 93 | my_radar_blips[drone_id].append(RadarBlip(fish_id, dir)) 94 | 95 | for drone in my_drones: 96 | x = drone.pos.x 97 | y = drone.pos.y 98 | # TODO: Implement logic on where to move here 99 | target_x = 5000 100 | target_y = 5000 101 | light = 1 102 | 103 | print(f"MOVE {target_x} {target_y} {light}") -------------------------------------------------------------------------------- /starterAIs/SS_Starter.rb: -------------------------------------------------------------------------------- 1 | STDOUT.sync = true # DO NOT REMOVE 2 | 3 | # Define the data structures as structs 4 | Vector = Struct.new(:x, :y) 5 | FishDetail = Struct.new(:color, :type) 6 | Fish = Struct.new(:fish_id, :pos, :speed, :detail) 7 | Drone = Struct.new(:drone_id, :pos, :dead, :battery, :scans) 8 | RadarBlip = Struct.new(:fish_id, :dir) 9 | 10 | fish_details = {} 11 | 12 | fish_count = gets.to_i 13 | fish_count.times do 14 | fish_id, color, type = gets.split.map(&:to_i) 15 | fish_details[fish_id] = FishDetail.new(color, type) 16 | end 17 | 18 | # game loop 19 | loop do 20 | my_scans = [] 21 | foe_scans = [] 22 | drone_by_id = {} 23 | my_drones = [] 24 | foe_drones = [] 25 | visible_fishes = [] 26 | my_radar_blips = {} 27 | 28 | my_score = gets.to_i 29 | foe_score = gets.to_i 30 | 31 | my_scan_count = gets.to_i 32 | my_scan_count.times do 33 | fish_id = gets.to_i 34 | my_scans << fish_id 35 | end 36 | 37 | foe_scan_count = gets.to_i 38 | foe_scan_count.times do 39 | fish_id = gets.to_i 40 | foe_scans << fish_id 41 | end 42 | 43 | my_drone_count = gets.to_i 44 | my_drone_count.times do 45 | drone_id, drone_x, drone_y, dead, battery = gets.split.map(&:to_i) 46 | pos = Vector.new(drone_x, drone_y) 47 | drone = Drone.new(drone_id, pos, dead, battery, []) 48 | drone_by_id[drone_id] = drone 49 | my_drones << drone 50 | my_radar_blips[drone_id] ||= [] 51 | end 52 | 53 | foe_drone_count = gets.to_i 54 | foe_drone_count.times do 55 | drone_id, drone_x, drone_y, dead, battery = gets.split.map(&:to_i) 56 | pos = Vector.new(drone_x, drone_y) 57 | drone = Drone.new(drone_id, pos, dead, battery, []) 58 | drone_by_id[drone_id] = drone 59 | foe_drones << drone 60 | end 61 | 62 | drone_scan_count = gets.to_i 63 | drone_scan_count.times do 64 | drone_id, fish_id = gets.split.map(&:to_i) 65 | drone_by_id[drone_id].scans.push(fish_id) 66 | end 67 | 68 | visible_fish_count = gets.to_i 69 | visible_fish_count.times do 70 | fish_id, fish_x, fish_y, fish_vx, fish_vy = gets.split.map(&:to_i) 71 | pos = Vector.new(fish_x, fish_y) 72 | speed = Vector.new(fish_vx, fish_vy) 73 | visible_fishes << Fish.new(fish_id, pos, speed, fish_details[fish_id]) 74 | end 75 | 76 | my_radar_blip_count = gets.to_i 77 | my_radar_blip_count.times do 78 | drone_id, fish_id, dir = gets.split 79 | drone_id = drone_id.to_i 80 | my_radar_blips[drone_id] << RadarBlip.new(fish_id.to_i, dir) 81 | end 82 | 83 | my_drones.each do |drone| 84 | x = drone.pos.x 85 | y = drone.pos.y 86 | # TODO: Implement logic on where to move here 87 | target_x = 5000 88 | target_y = 5000 89 | light = 1 90 | 91 | puts "MOVE #{target_x} #{target_y} #{light}" 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /starterAIs/SS_Starter.ts: -------------------------------------------------------------------------------- 1 | interface Vector { 2 | x: number 3 | y: number 4 | } 5 | 6 | interface FishDetail { 7 | color: number 8 | type: number 9 | } 10 | 11 | interface Fish { 12 | fishId: number 13 | pos: Vector 14 | speed: Vector 15 | detail: FishDetail 16 | } 17 | 18 | interface Drone { 19 | droneId: number 20 | pos: Vector 21 | dead: number 22 | battery: number 23 | scans: number[] 24 | } 25 | 26 | interface RadarBlip { 27 | fishId: number 28 | dir: string 29 | } 30 | 31 | 32 | const fishDetails = new Map() 33 | 34 | const fishCount = parseInt(readline()) 35 | for (let i = 0; i < fishCount; i++) { 36 | const [fishId, color, type] = readline().split(' ').map(Number) 37 | fishDetails.set(fishId, { color, type }) 38 | } 39 | 40 | // game loop 41 | while (true) { 42 | const myScans: number[] = [] 43 | const foeScans: number[] = [] 44 | const droneById = new Map() 45 | const myDrones: Drone[] = [] 46 | const foeDrones: Drone[] = [] 47 | const visibleFish: Fish[] = [] 48 | const myRadarBlips = new Map() 49 | 50 | const myScore = parseInt(readline()) 51 | const foeScore = parseInt(readline()) 52 | 53 | const myScanCount = parseInt(readline()) 54 | for (let i = 0; i < myScanCount; i++) { 55 | const fishId = parseInt(readline()) 56 | myScans.push(fishId) 57 | } 58 | 59 | const foeScanCount = parseInt(readline()) 60 | for (let i = 0; i < foeScanCount; i++) { 61 | const fishId = parseInt(readline()) 62 | foeScans.push(fishId) 63 | } 64 | 65 | const myDroneCount = parseInt(readline()) 66 | for (let i = 0; i < myDroneCount; i++) { 67 | const [droneId, droneX, droneY, dead, battery] = readline().split(' ').map(Number) 68 | const pos = { x: droneX, y: droneY } 69 | const drone = { droneId, pos, dead, battery, scans: [] } 70 | droneById.set(droneId, drone) 71 | myDrones.push(drone) 72 | myRadarBlips.set(droneId, []) 73 | } 74 | 75 | const foeDroneCount = parseInt(readline()) 76 | for (let i = 0; i < foeDroneCount; i++) { 77 | const [droneId, droneX, droneY, dead, battery] = readline().split(' ').map(Number) 78 | const pos = { x: droneX, y: droneY } 79 | const drone = { droneId, pos, dead, battery, scans: [] } 80 | droneById.set(droneId, drone) 81 | foeDrones.push(drone) 82 | } 83 | 84 | 85 | const droneScanCount = parseInt(readline()) 86 | for (let i = 0; i < droneScanCount; i++) { 87 | const [droneId, fishId] = readline().split(' ').map(Number) 88 | droneById.get(droneId)!.scans.push(fishId) 89 | } 90 | 91 | const visibleFishCount = parseInt(readline()) 92 | for (let i = 0; i < visibleFishCount; i++) { 93 | const [fishId, fishX, fishY, fishVx, fishVy] = readline().split(' ').map(Number) 94 | const pos = { x: fishX, y: fishY } 95 | const speed = { x: fishVx, y: fishVy } 96 | visibleFish.push({fishId, pos, speed, detail: fishDetails.get(fishId)! }) 97 | } 98 | 99 | const myRadarBlipCount = parseInt(readline()) 100 | for (let i = 0; i < myRadarBlipCount; i++) { 101 | const [_droneId, _fishId, dir] = readline().split(' ') 102 | const droneId = parseInt(_droneId) 103 | const fishId = parseInt(_fishId) 104 | myRadarBlips.get(droneId)!.push({ fishId, dir }) 105 | } 106 | 107 | for (const drone of myDrones) { 108 | const x = drone.pos.x 109 | const y = drone.pos.y 110 | // TODO: Implement logic on where to move here 111 | const targetX = 5000 112 | const targetY = 5000 113 | const light = 1 114 | 115 | console.log(`MOVE ${targetX} ${targetY} ${light}`) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /typescript/polyfill.js: -------------------------------------------------------------------------------- 1 | // Add print, printErr and readline functions 2 | 3 | const { readline } = require('./readline.js') 4 | 5 | // Polyfill missing SpiderMonkey functions: 6 | global.print = console.log 7 | global.printErr = console.warn 8 | global.readline = readline 9 | global.putstr = process.stdout.write 10 | -------------------------------------------------------------------------------- /typescript/readline.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | // Variable used to store the remain of stdin (happen if several lines have been read at once by getStdin) 4 | let stdin = '' 5 | // File descriptor for stdin 6 | let fd = null 7 | 8 | // Read stdin until endByte of end of file is reached 9 | // Beware that endByte is not necessarly the last character! 10 | function getStdin (endByte) { 11 | const BUFSIZE = 256 12 | let buf = Buffer.allocUnsafe(BUFSIZE) 13 | let totalBuf = Buffer.allocUnsafe(0) 14 | let bytesRead 15 | let endBytePos 16 | 17 | if (fd === null) { 18 | fd = fs.openSync('/dev/stdin', 'rs') 19 | } 20 | 21 | do { 22 | bytesRead = fs.readSync(fd, buf, 0, BUFSIZE, null) 23 | totalBuf = Buffer.concat([totalBuf, buf], totalBuf.length + bytesRead) 24 | endBytePos = buf.indexOf(endByte) 25 | } while (bytesRead > 0 && (endBytePos < 0 || endBytePos >= bytesRead)) 26 | 27 | return totalBuf 28 | } 29 | 30 | function readline () { 31 | if (stdin.indexOf('\n') === -1) { 32 | stdin = stdin + getStdin('\n'.charCodeAt(0)).toString('utf-8') 33 | } 34 | 35 | // If still empty then EOF reached. Return null to keep the same behaviour as SpiderMonkey. 36 | if (stdin.length === 0) { 37 | return null 38 | } 39 | 40 | // At this point, either stdin contains '\n' or it's the end of the file: we have something to return 41 | const newline = stdin.indexOf('\n') 42 | if (newline !== -1) { 43 | const line = stdin.slice(0, newline) 44 | 45 | stdin = stdin.slice(newline + 1) 46 | return line 47 | } else { 48 | const line = stdin 49 | 50 | stdin = '' 51 | return line 52 | } 53 | } 54 | 55 | module.exports = { 56 | readline 57 | } 58 | -------------------------------------------------------------------------------- /typescript/readline/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function readline(): string 2 | --------------------------------------------------------------------------------