From: JBM Date: Wed, 20 May 2020 13:21:22 +0000 (+0200) Subject: Initial commit X-Git-Url: https://troll.desast.re/troll.git/commitdiff_plain/33d43e0a726d9cb3256489d0b39feebbbb3304a4?ds=sidebyside Initial commit --- 33d43e0a726d9cb3256489d0b39feebbbb3304a4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54e1393 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea/ +target/ +.settings +.classpath +.project +.vscode +bin \ No newline at end of file diff --git a/config/Boss.java b/config/Boss.java new file mode 100644 index 0000000..d3c297d --- /dev/null +++ b/config/Boss.java @@ -0,0 +1,20 @@ +import java.util.Random; +import java.util.Scanner; + +public class Player2 { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Random random = new Random(0); + + int roadLength = in.nextInt(); + int initialStones = in.nextInt(); + + while (true) { + int trollDistance = in.nextInt(); + int stones = in.nextInt(); + int opponentStones = in.nextInt(); + + System.out.println(random.nextInt(stones+1)); + } + } +} diff --git a/config/Boss.sh b/config/Boss.sh new file mode 100644 index 0000000..0b3832f --- /dev/null +++ b/config/Boss.sh @@ -0,0 +1 @@ +exec yes 1 diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 0000000..2002eb1 --- /dev/null +++ b/config/config.ini @@ -0,0 +1,4 @@ +title=Castle Troll +min_players=2 +max_players=2 +type=multi \ No newline at end of file diff --git a/config/statement_en.html b/config/statement_en.html new file mode 100644 index 0000000..83c0e4e --- /dev/null +++ b/config/statement_en.html @@ -0,0 +1,22 @@ +
+ +
+

+   + The Goal +

+
+ Don't get destroyed by the troll. +
+
+ +
+

+   + Rules +

+
+ In the classic version, you have to shoot at least one stone per turn. This isn't enforced yet. +
+
+
diff --git a/config/stub.txt b/config/stub.txt new file mode 100644 index 0000000..88c3d38 --- /dev/null +++ b/config/stub.txt @@ -0,0 +1,17 @@ +read roadLength:int initialStones:int +gameloop +read trollDistance:int stones:int opponentStones:int +write 1 + +STATEMENT +Keep the troll out of your castle! + +INPUT +roadLength: the length of the road between both castles. The troll starts in the middle. +initialStones: the number of stones each castle starts with +trollDistance: distance between the troll and your castle. Keep it above zero! +stones: number of stones left +opponentStones: number of stones left to your opponent + +OUTPUT +number of stones to shoot at the troll diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8c64961 --- /dev/null +++ b/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + + com.codingame.game + castle-troll + 0.5-git + + + 3.13.1 + 1.8 + 1.8 + + + + + com.codingame.gameengine + core + ${gamengine.version} + + + + com.codingame.gameengine + module-entities + ${gamengine.version} + + + + com.codingame.gameengine + runner + ${gamengine.version} + + + diff --git a/src/main/java/com/codingame/game/InvalidAction.java b/src/main/java/com/codingame/game/InvalidAction.java new file mode 100644 index 0000000..5681dd9 --- /dev/null +++ b/src/main/java/com/codingame/game/InvalidAction.java @@ -0,0 +1,10 @@ +package com.codingame.game; + +public class InvalidAction extends Exception { + private static final long serialVersionUID = -8185589153224401564L; + + public InvalidAction(String message) { + super(message); + } + +} diff --git a/src/main/java/com/codingame/game/Player.java b/src/main/java/com/codingame/game/Player.java new file mode 100644 index 0000000..2716b17 --- /dev/null +++ b/src/main/java/com/codingame/game/Player.java @@ -0,0 +1,70 @@ +package com.codingame.game; + +import com.codingame.gameengine.core.AbstractMultiplayerPlayer; +import com.codingame.gameengine.module.entities.Group; +import com.codingame.gameengine.module.entities.Text; +import com.codingame.gameengine.module.entities.Sprite; + +public class Player extends AbstractMultiplayerPlayer { + Group hud; + Text stoneCounter; + + Sprite castle; + Text stone; + + + private int castlePosition; + public int getCastlePosition() { + return castlePosition; + } + public void setCastlePosition(int pos) { + castlePosition = pos; + } + + private int stones; + public int getStones() + { + return stones; + } + public void consumeStones(int n) throws InvalidAction { + if (n > stones) { + throw new InvalidAction("attempted to throw more stones than they had."); + } + setStones(stones - n); + } + public void setStones(int n) { + stones = n; + if (stones <= 0) { + stoneCounter.setText("No stones!"); + stoneCounter.setFillColor(0xff7777); + } + else if (stones == 1) { + stoneCounter.setText("1 stone"); + stoneCounter.setFillColor(0xffbb77); + } + else { + stoneCounter.setText(stones + " stones"); + } + } + + private int multiplier; + public int getMultiplier() { + return multiplier; + } + public void setMultiplier(int m){ + multiplier = m; + } + + public void adjustScore(int trollPosition) { + setScore(Math.abs(castlePosition - trollPosition)); + } + + @Override + public int getExpectedOutputLines() { + return 1; + } + + public int getAction() throws TimeoutException, NumberFormatException { + return Integer.parseInt(getOutputs().get(0)); + } +} diff --git a/src/main/java/com/codingame/game/Referee.java b/src/main/java/com/codingame/game/Referee.java new file mode 100644 index 0000000..4b1315b --- /dev/null +++ b/src/main/java/com/codingame/game/Referee.java @@ -0,0 +1,291 @@ +package com.codingame.game; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import com.codingame.gameengine.core.AbstractPlayer.TimeoutException; +import com.codingame.gameengine.core.AbstractReferee; +import com.codingame.gameengine.core.GameManager; +import com.codingame.gameengine.core.MultiplayerGameManager; +import com.codingame.gameengine.module.entities.GraphicEntityModule; +import com.codingame.gameengine.module.entities.Rectangle; +import com.codingame.gameengine.module.entities.Sprite; +import com.codingame.gameengine.module.entities.Text; +import com.codingame.gameengine.module.entities.Curve; +import com.google.inject.Inject; +import com.google.inject.Provider; + +public class Referee extends AbstractReferee { + @Inject private MultiplayerGameManager gameManager; + @Inject private GraphicEntityModule graphicEntityModule; + + int roadLength; + int initialStones; + int trollPosition; + Player p0, p1; + Sprite troll; + Text trollPositionGauge; + + @Override + public void init() { + switch (new Random(gameManager.getSeed()).nextInt(4)) { + case 0: + roadLength = 7; + initialStones = 15; + break; + case 1: + roadLength = 7; + initialStones = 30; + break; + case 2: + roadLength = 15; + initialStones = 30; + break; + case 3: + roadLength = 15; + initialStones = 50; + break; + } + + trollPosition = (roadLength - 1) / 2; + + p0 = gameManager.getPlayer(0); + p0.setCastlePosition(0); + p0.setMultiplier(1); + p0.adjustScore(trollPosition); + p0.sendInputLine(String.format("%d %d", roadLength - 1, initialStones)); + + p1 = gameManager.getPlayer(1); + p1.setCastlePosition(roadLength-1); + p1.setMultiplier(-1); + p1.adjustScore(trollPosition); + p1.sendInputLine(String.format("%d %d", roadLength - 1, initialStones)); + + drawBackground(); + drawPlayer(); + drawTroll(); + + // result in text display, so do last: + p0.setStones(initialStones); + p1.setStones(initialStones); + + gameManager.setFrameDuration(2000); + } + + private void drawBackground() { + graphicEntityModule.createSprite() + .setImage("background.png") + .setAnchor(0); + } + + private void drawPlayer() { + for (Player player : gameManager.getPlayers()) { + boolean p0 = player.getIndex() == 0; + int x = p0 ? 280 : 1920 - 280; + int y = 220; + + Rectangle border1 = graphicEntityModule + .createRectangle() + .setWidth(140) + .setHeight(140) + .setX(x - 70) + .setY(y - 70) + .setLineWidth(0) + .setFillColor(player.getColorToken()); + + Rectangle border2 = graphicEntityModule + .createRectangle() + .setWidth(120) + .setHeight(120) + .setX(x - 60) + .setY(y - 60) + .setLineWidth(0) + .setFillColor(0xffffff); + + Text text = graphicEntityModule.createText(player.getNicknameToken()) + .setX(x) + .setY(y + 120) + .setZIndex(20) + .setFontSize(40) + .setFillColor(0x7f3f00) + .setAnchor(0.5); + + Sprite avatar = graphicEntityModule.createSprite() + .setX(x) + .setY(y) + .setZIndex(20) + .setImage(player.getAvatarToken()) + .setAnchor(0.5) + .setBaseHeight(116) + .setBaseWidth(116); + + Text stoneCounter = graphicEntityModule.createText("S") + .setX(x) + .setY(y+200) + .setZIndex(20) + .setFontSize(40) + .setFillColor(0x7f3f00) + .setAnchor(0.5); + + player.castle = graphicEntityModule.createSprite() + .setImage("castle.png") + .setX(p0 ? 160 : 1920-160) + .setY(p0 ? 890 : 880) + .setZIndex(1) + .setAnchorX(0.5) + .setAnchorY(1); + + player.stone = graphicEntityModule.createText() + .setZIndex(3) + .setFontSize(150) + .setFillColor(0x12322a) + .setAnchor(0.5) + .setAlpha(0); + + player.hud = graphicEntityModule.createGroup(border1, border2, text, avatar, stoneCounter, player.castle); + player.stoneCounter = stoneCounter; + } + } + + private void drawTroll() { + troll = graphicEntityModule.createSprite() + .setImage("troll.png") + .setAnchorX(0.5) + .setAnchorY(1) + .setX(1920/2) + .setY(880) + .setZIndex(2); + trollPositionGauge = graphicEntityModule.createText() + .setZIndex(2) + .setAnchor(0.5) + .setFontSize(40) + .setX(1980/2) + .setY(980) + .setFillColor(0xffffff); + } + + private void moveTroll() { + graphicEntityModule.commitEntityState(0.5, troll, trollPositionGauge); + int x0 = p0.castle.getX(), x1 = p1.castle.getX(); + int y0 = p0.castle.getY(), y1 = p1.castle.getY(); + troll.setX(x0 + trollPosition * (x1-x0) / (roadLength - 1), + Curve.ELASTIC); + troll.setY(y0 + trollPosition * (y1-y0) / (roadLength - 1), + Curve.ELASTIC); + + trollPositionGauge.setX((trollPositionGauge.getX() + troll.getX()) / 2); + int delta = trollPosition - (roadLength - 1) / 2; + if (delta < 0) { + trollPositionGauge.setText("← " + Math.abs(delta)); + } + else if (delta > 0) { + trollPositionGauge.setText(Math.abs(delta) + " →"); + } + else { + trollPositionGauge.setText("↔"); + } + graphicEntityModule.commitEntityState(0.75, trollPositionGauge); + trollPositionGauge.setX(troll.getX()); + } + + @Override + public void gameTurn(int turn) { + + p0.sendInputLine(String.format("%d %d %d", p0.getScore(), p0.getStones(), p1.getStones())); + p1.sendInputLine(String.format("%d %d %d", p1.getScore(), p1.getStones(), p0.getStones())); + + for (Player player : gameManager.getActivePlayers()) { + player.execute(); + } + + int delta = 0; + for (Player player : gameManager.getActivePlayers()) { + try { + final int stones = player.getAction(); + player.consumeStones(stones); + gameManager.addToGameSummary(String.format("%s throws %d stone%s at the troll.", player.getNicknameToken(), stones, stones == 1 ? "" : "s")); + delta += player.getMultiplier() * stones; + + if (stones > 0) { + player.stone.setX(player.castle.getX()); + player.stone.setY(player.castle.getY() - 100); + player.stone.setText(new Integer(stones).toString()); + player.stone.setAlpha(1); + graphicEntityModule.commitEntityState(0, player.stone); + + int peakX = (player.castle.getX() + troll.getX()) / 2; + int peakY = 540; + player.stone.setX(peakX); + player.stone.setY(peakY, Curve.EASE_OUT); + graphicEntityModule.commitEntityState(0.25, player.stone, + player.stoneCounter); + + player.stone.setX(troll.getX()); + player.stone.setY(troll.getY() - 50, Curve.EASE_IN); + player.stone.setAlpha(0, Curve.EASE_IN); + graphicEntityModule.commitEntityState(0.5, player.stone); + } + } + catch (InvalidAction e) { + player.deactivate(player.getNicknameToken() + " INVALID"); + gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " " + e.getMessage())); + player.setScore(-1); + endGame(); + } + catch (NumberFormatException e) { + player.deactivate(player.getNicknameToken() + " ERROR"); + gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " provided malformed input!")); + player.setScore(-1); + endGame(); + } + catch (TimeoutException e) { + gameManager.addToGameSummary(player.getNicknameToken() + " timed out!"); + player.deactivate(player.getNicknameToken() + " T/O"); + player.setScore(-1); + endGame(); + } + } + + if (delta > 0) { + gameManager.addToGameSummary("Troll walks right."); + trollPosition++; + moveTroll(); + } + else if (delta < 0) { + gameManager.addToGameSummary("Troll walks left."); + trollPosition--; + moveTroll(); + } + else { + gameManager.addToGameSummary("Troll stands still."); + } + + for (Player player : gameManager.getActivePlayers()) { + player.adjustScore(trollPosition); + if (trollPosition == player.getCastlePosition()) { + gameManager.addToGameSummary(GameManager.formatErrorMessage("Troll destroys " + player.getNicknameToken())); + endGame(); + } + } + } + + private void endGame() { + gameManager.endGame(); + + if (p0.getScore() > p1.getScore()) { + gameManager.addToGameSummary(GameManager.formatSuccessMessage(p0.getNicknameToken() + " wins")); + p1.hud.setAlpha(0.3); + } + else if (p0.getScore() < p1.getScore()) { + gameManager.addToGameSummary(GameManager.formatSuccessMessage(p1.getNicknameToken() + " wins")); + p0.hud.setAlpha(0.3); + } + else { + gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!")); + p0.hud.setAlpha(0.3); + p1.hud.setAlpha(0.3); + } + } +} diff --git a/src/main/resources/view/assets/background.png b/src/main/resources/view/assets/background.png new file mode 100644 index 0000000..4b31ba2 Binary files /dev/null and b/src/main/resources/view/assets/background.png differ diff --git a/src/main/resources/view/assets/castle.png b/src/main/resources/view/assets/castle.png new file mode 100644 index 0000000..22d4c08 Binary files /dev/null and b/src/main/resources/view/assets/castle.png differ diff --git a/src/main/resources/view/assets/game.xcf b/src/main/resources/view/assets/game.xcf new file mode 100644 index 0000000..8384687 Binary files /dev/null and b/src/main/resources/view/assets/game.xcf differ diff --git a/src/main/resources/view/assets/troll.png b/src/main/resources/view/assets/troll.png new file mode 100644 index 0000000..be7d5d5 Binary files /dev/null and b/src/main/resources/view/assets/troll.png differ diff --git a/src/main/resources/view/config.js b/src/main/resources/view/config.js new file mode 100644 index 0000000..50b1654 --- /dev/null +++ b/src/main/resources/view/config.js @@ -0,0 +1,12 @@ +import { GraphicEntityModule } from './entity-module/GraphicEntityModule.js'; + +export const gameName = 'Castle-Troll'; + +export const playerColors = [ + '#f2b213', // yellow + '#22a1e4' // curious blue +]; + +export const modules = [ + GraphicEntityModule +]; diff --git a/src/main/resources/view/demo.js b/src/main/resources/view/demo.js new file mode 100644 index 0000000..7c3c397 --- /dev/null +++ b/src/main/resources/view/demo.js @@ -0,0 +1,2 @@ +export const demo = +{"views":["KEY_FRAME 0\n{\"global\":{\"entitymodule\":{\"width\":1920,\"height\":1080}},\"frame\":{\"duration\":2000,\"entitymodule\":\"CS;R;R;T;S;T;S;T;G;R;R;T;S;T;S;T;G;S\\nU15 1 i castle.png ay 1 v 1 ax 0.5 x 1640 y 880 z 1;2 1 f -1 v 1 w 140 x 210 y 150 W 0 h 140;16 1 f 1192490 ay 0.5 v 1 ax 0.5 a 0 s 150 z 3;1 1 i background.png ay 0 v 1 ax 0;7 1 i castle.png ay 1 v 1 ax 0.5 x 280 y 887 z 1;6 1 f 16777215 ay 0.5 v 1 ax 0.5 x 280 y 420 s 40 T '15 stones' z 20;14 1 f 16777215 ay 0.5 v 1 ax 0.5 x 1640 y 420 s 40 T '15 stones' z 20;3 1 f 16777215 v 1 w 120 x 220 y 160 W 0 h 120;17 1 ch 15,13,12,10,14,11 v 1;13 1 bw 116 i $1 ay 0.5 v 1 ax 0.5 x 1640 y 220 bh 116 z 20;8 1 f 1192490 ay 0.5 v 1 ax 0.5 a 0 s 150 z 3;12 1 f 16777215 ay 0.5 v 1 ax 0.5 x 1640 y 340 s 40 T $1 z 20;18 1 i troll.png ay 1 v 1 ax 0.5 x 960 y 880 z 2;5 1 bw 116 i $0 ay 0.5 v 1 ax 0.5 x 280 y 220 bh 116 z 20;9 1 ch 3,2,7,5,6,4 v 1;10 1 f -2 v 1 w 140 x 1570 y 150 W 0 h 140;4 1 f 16777215 ay 0.5 v 1 ax 0.5 x 280 y 340 s 40 T $0 z 20;11 1 f 16777215 v 1 w 120 x 1580 y 160 W 0 h 120\"}}\n","INTERMEDIATE_FRAME 1\n","KEY_FRAME 2\n{\"entitymodule\":\"U16 0 a 1 x 1640 y 780 T 11;8 0 a 1 x 280 y 787 T 1;16 0.25 x 1300 y 540 🙒;8 0.25 x 620 y 540 🙒;16 0.5 a 0 🙖 x 960 y 830 🙖;8 0.5 a 0 🙖 x 960 y 830 🙖;18 0.5;18 1 x 713 ~;6 1 T '14 stones';14 1 T '4 stones'\"}\n","INTERMEDIATE_FRAME 3\n","KEY_FRAME 4\n{\"entitymodule\":\"U16 0 a 1 x 1640 y 780 T 3;8 0 a 1 x 280 y 787;16 0.25 x 1176 y 540 🙒;8 0.25 x 496 y 540 🙒;16 0.5 a 0 🙖 x 713 y 830 🙖;8 0.5 a 0 🙖 x 713 y 830 🙖;18 0.5;18 1 x 466 ~;6 1 T '13 stones';14 1 f 16759671 T '1 stone'\"}\n","INTERMEDIATE_FRAME 5\n","KEY_FRAME 6\n{\"entitymodule\":\"U8 0 a 1 x 280 y 787;8 0.25 x 373 y 540 🙒;8 0.5 a 0 🙖 x 466 y 830 🙖;18 0.5;18 1 x 713 ~;6 1 T '12 stones'\"}\n","INTERMEDIATE_FRAME 7\n","KEY_FRAME 8\n{\"entitymodule\":\"U16 0 a 1 x 1640 y 780 T 1;8 0 a 1 x 280 y 787;16 0.25 x 1176 y 540 🙒;8 0.25 x 496 y 540 🙒;16 0.5 a 0 🙖 x 713 y 830 🙖;8 0.5 a 0 🙖 x 713 y 830 🙖;6 1 T '11 stones';14 1 f 16742263 T 'No stones!'\"}\n","INTERMEDIATE_FRAME 9\n","KEY_FRAME 10\n{\"entitymodule\":\"U8 0 a 1 x 280 y 787;8 0.25 x 496 y 540 🙒;8 0.5 a 0 🙖 x 713 y 830 🙖;18 0.5;18 1 x 960 ~;6 1 T '10 stones'\"}\n","INTERMEDIATE_FRAME 11\n","KEY_FRAME 12\n{\"entitymodule\":\"U8 0 a 1 x 280 y 787;8 0.25 x 620 y 540 🙒;8 0.5 a 0 🙖 x 960 y 830 🙖;18 0.5;18 1 x 1206 ~;6 1 T '9 stones'\"}\n","INTERMEDIATE_FRAME 13\n","KEY_FRAME 14\n{\"entitymodule\":\"U8 0 a 1 x 280 y 787;8 0.25 x 743 y 540 🙒;8 0.5 a 0 🙖 x 1206 y 830 🙖;18 0.5;18 1 x 1453 ~;6 1 T '8 stones'\"}\n","INTERMEDIATE_FRAME 15\n","KEY_FRAME 16\n{\"entitymodule\":\"U8 0 a 1 x 280 y 787;8 0.25 x 866 y 540 🙒;8 0.5 a 0 🙖 x 1453 y 830 🙖;18 0.5;17 1 a 0.3;18 1 x 1700 ~;6 1 T '7 stones'\"}\n"],"agents":[{"index":0,"name":"Player 0","avatar":"https://static.codingame.com/servlet/fileservlet?id=16085713250612&format=viewer_avatar","agentId":0},{"index":1,"name":"Player 1","avatar":"https://static.codingame.com/servlet/fileservlet?id=16085756802960&format=viewer_avatar","agentId":1}]}; diff --git a/src/test/java/Main.java b/src/test/java/Main.java new file mode 100644 index 0000000..956794c --- /dev/null +++ b/src/test/java/Main.java @@ -0,0 +1,19 @@ +import com.codingame.gameengine.runner.MultiplayerGameRunner; + +public class Main { + public static void main(String[] args) { + + MultiplayerGameRunner gameRunner = new MultiplayerGameRunner(); + gameRunner.addAgent(Player1.class); + gameRunner.addAgent(Player2.class); + + // gameRunner.addAgent("python3 /home/user/player.py"); + + // The first league is classic tic-tac-toe + gameRunner.setLeagueLevel(1); + // The second league is ultimate tic-tac-toe + // gameRunner.setLeagueLevel(2); + + gameRunner.start(); + } +} diff --git a/src/test/java/Player1.java b/src/test/java/Player1.java new file mode 100644 index 0000000..bc78cb2 --- /dev/null +++ b/src/test/java/Player1.java @@ -0,0 +1,21 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Scanner; + +public class Player1 { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + + int roadLength = in.nextInt(); + int initialStones = in.nextInt(); + + while (true) { + int trollDistance = in.nextInt(); + int stones = in.nextInt(); + int opponentStones = in.nextInt(); + + System.out.println(1); + } + } +} diff --git a/src/test/java/Player2.java b/src/test/java/Player2.java new file mode 100644 index 0000000..d3c297d --- /dev/null +++ b/src/test/java/Player2.java @@ -0,0 +1,20 @@ +import java.util.Random; +import java.util.Scanner; + +public class Player2 { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Random random = new Random(0); + + int roadLength = in.nextInt(); + int initialStones = in.nextInt(); + + while (true) { + int trollDistance = in.nextInt(); + int stones = in.nextInt(); + int opponentStones = in.nextInt(); + + System.out.println(random.nextInt(stones+1)); + } + } +} diff --git a/src/test/resources/log4j2.properties b/src/test/resources/log4j2.properties new file mode 100644 index 0000000..4ca7574 --- /dev/null +++ b/src/test/resources/log4j2.properties @@ -0,0 +1,11 @@ +# Configuration +name = PropertiesConfig +status = WARN + +appender.console.type = Console +appender.console.name = CONSOLE +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = [%d{yyyy/MM/dd HH:mm:ss}][GF] %-5p : %c{1} - %m%n + +rootLogger.level = warn +rootLogger.appenderRef.console.ref = CONSOLE