From e0da785e68b21f4906f4f0bc849bb7bbab1abeb8 Mon Sep 17 00:00:00 2001 From: JBM Date: Thu, 28 May 2020 17:35:23 +0200 Subject: [PATCH] Funky loss visuals. A cheater doesn't lose to timeout/illegal anymore. --- config/statement_en.html | 2 +- src/main/java/com/codingame/game/Referee.java | 98 ++++++++++--------- src/main/java/com/codingame/game/View.java | 70 +++++++++++-- src/test/java/Main.java | 2 +- src/test/java/PlayerCheat.java | 20 ++++ .../java/{Player2.java => PlayerRand.java} | 2 +- src/test/java/PlayerStupid.java | 20 ++++ src/test/java/PlayerTimeout.java | 18 ++++ 8 files changed, 174 insertions(+), 58 deletions(-) create mode 100644 src/test/java/PlayerCheat.java rename src/test/java/{Player2.java => PlayerRand.java} (97%) create mode 100644 src/test/java/PlayerStupid.java create mode 100644 src/test/java/PlayerTimeout.java diff --git a/config/statement_en.html b/config/statement_en.html index b1efb7c..d4896dd 100644 --- a/config/statement_en.html +++ b/config/statement_en.html @@ -137,7 +137,7 @@

This draft's last change is: - restore cheat throw visuals. + a cheater doesn't win face to a timeout.

diff --git a/src/main/java/com/codingame/game/Referee.java b/src/main/java/com/codingame/game/Referee.java index 6e291be..b15b4b0 100644 --- a/src/main/java/com/codingame/game/Referee.java +++ b/src/main/java/com/codingame/game/Referee.java @@ -68,10 +68,12 @@ public class Referee extends AbstractReferee { switch (player.type) { case Timeout: disqualify(player, "T/O", "timed out!"); + player.view.markTimeout(); disqual = true; break; case Invalid: disqualify(player, "INVALID", "provided an ill-formed action"); + player.view.markIllegal(); disqual = true; break; case Throw: @@ -83,6 +85,7 @@ public class Referee extends AbstractReferee { } else { disqualify(player, "ILLEGAL", "tried to throw more stones than they had. They went into debt trying to provide. The economy tanked, recession and famine ensued; even the troll wouldn't have wanted to bash them anymore. But that's no victory."); + player.view.markIllegal(); disqual = true; } } @@ -114,59 +117,60 @@ public class Referee extends AbstractReferee { int delta = 0; boolean victory = false; boolean exhausted = false; - if (! disqual) { - for (Player player : gameManager.getActivePlayers()) { - player.view.throwStones(player.stoneThrow); - delta += player.model.getMultiplier() * player.stoneThrow; - - if (player.stoneThrow < 0) { - disqualify(player, "CHEAT", "cheated. Banning account."); - disqual = true; - } - if (player.stoneThrow != 0) { - player.view.animateStones(player.stoneThrow); - player.view.updateStoneCounter(); - } - } + for (Player player : gameManager.getActivePlayers()) { + player.view.throwStones(player.stoneThrow); + delta += player.model.getMultiplier() * player.stoneThrow; - /* If a player cheated, delta is unusable as is. - * (Consider the case the player on the right sent - * INT_MIN. INT_MIN * (-1) = INT_MIN, so that player - * would both glean the stones *and* push the troll away. - * It would be unfair to have a cheating player "win" - * (earn the opponent castle destruction animation) this - * way. - */ - boolean cheat0 = gameManager.getPlayer(0).stoneThrow < 0; - boolean cheat1 = gameManager.getPlayer(1).stoneThrow < 0; - if (cheat0 && cheat1); // here we can actually keep delta's value - else if (cheat0) delta = -1; - else if (cheat1) delta = 1; - - if (delta > 0) { - model.trollPosition++; - view.moveTroll(View.Dir.RIGHT); - } - else if (delta < 0) { - model.trollPosition--; - view.moveTroll(View.Dir.LEFT); + if (player.stoneThrow < 0) { + disqualify(player, "CHEAT", "cheated. Banning account."); + player.view.markCheat(); + disqual = true; } - else { - view.moveTroll(View.Dir.STILL); - // XXX animate + if (player.stoneThrow != 0) { + player.view.animateStones(player.stoneThrow); + player.view.updateStoneCounter(); } + } - for (Player player : gameManager.getActivePlayers()) { - player.model.adjustScore(model.trollPosition); - } + /* If a player cheated, delta is unusable as is. + * (Consider the case the player on the right sent + * INT_MIN. INT_MIN * (-1) = INT_MIN, so that player + * would both glean the stones *and* push the troll away. + * It would be unfair to have a cheating player "win" + * (earn the opponent castle destruction animation) this + * way. + */ + boolean cheat0 = gameManager.getPlayer(0).isActive() + && gameManager.getPlayer(0).stoneThrow < 0; + boolean cheat1 = gameManager.getPlayer(1).isActive() + && gameManager.getPlayer(1).stoneThrow < 0; + if (cheat0 && cheat1); // here we can actually keep delta's value + else if (cheat0) delta = -1; + else if (cheat1) delta = 1; + + if (delta > 0) { + model.trollPosition++; + view.moveTroll(View.Dir.RIGHT); + } + else if (delta < 0) { + model.trollPosition--; + view.moveTroll(View.Dir.LEFT); + } + else { + view.moveTroll(View.Dir.STILL); + // XXX animate + } - if (model.haveWinner()) { - int loser = model.getLoser(); - gameManager.getPlayer(loser).view.destroy(); - victory = true; - } - else if (model.exhausted()) exhausted = true; + for (Player player : gameManager.getActivePlayers()) { + player.model.adjustScore(model.trollPosition); + } + + if (model.haveWinner()) { + int loser = model.getLoser(); + gameManager.getPlayer(loser).view.destroy(); + victory = true; } + else if (model.exhausted()) exhausted = true; if (disqual || victory || exhausted) endGame(); } diff --git a/src/main/java/com/codingame/game/View.java b/src/main/java/com/codingame/game/View.java index 804cda7..b59c0a8 100644 --- a/src/main/java/com/codingame/game/View.java +++ b/src/main/java/com/codingame/game/View.java @@ -11,6 +11,7 @@ import com.codingame.gameengine.module.entities.Rectangle; import com.codingame.gameengine.module.entities.Sprite; import com.codingame.gameengine.module.entities.SpriteAnimation; import com.codingame.gameengine.module.entities.Text; +import com.codingame.gameengine.module.entities.TextBasedEntity; import com.codingame.gameengine.module.entities.Group; import com.codingame.gameengine.module.entities.Curve; import com.codingame.gameengine.module.toggle.ToggleModule; @@ -127,10 +128,13 @@ class View { .setX(p0 ? x + 100 : x - 100) .setY(y) .setZIndex(20) - .setFontSize(75) - .setFillColor(0x3f3f3f) + .setFontSize(80) + .setFontFamily("monospace") + .setStrokeColor(0xff0080) + .setFillColor(0xff0080) .setAnchorX(p0 ? 0 : 1) .setAnchorY(0.5); + toggleModule.displayOnToggleState(stoneReminder, "debug", true); } void updateStoneCounter() { @@ -170,9 +174,7 @@ class View { graphicEntityModule.commitEntityState(0.5, stone); stoneReminder.setText(stonesString); - graphicEntityModule.commitEntityState(0.25, stoneReminder); - stoneReminder.setAlpha(1); - graphicEntityModule.commitEntityState(0.5, stoneReminder); + graphicEntityModule.commitEntityState(0, stoneReminder); } void displayMessage(String msg) { @@ -194,12 +196,16 @@ class View { } void startTurn() { - stoneReminder.setAlpha(0); graphicEntityModule.commitEntityState(0, stoneReminder); } void victory() { gameManager.addToGameSummary(GameManager.formatSuccessMessage(nicknameToken + " wins.")); + graphicEntityModule.commitEntityState(0.5, avatar); + avatar.setScaleX(1.5, Curve.EASE_OUT); + avatar.setScaleY(1.5, Curve.EASE_OUT); + avatar.setRotation((random.nextDouble() - 0.5) * Math.PI / 18, + Curve.ELASTIC); } void throwStones(int stones) { @@ -213,6 +219,18 @@ class View { void failedToThrowStonesAndShouldHave() { gameManager.addToGameSummary(GameManager.formatErrorMessage(nicknameToken + " tried not throwing any stones. Fixing that for them because I'm in a good mood today.")); } + + void markTimeout() { + animateLoss(avatar.getX(), avatar.getY(), 100, "SLOW\nPOKE"); + } + + void markIllegal() { + animateLoss(avatar.getX(), avatar.getY(), 100, "STUPID"); + } + + void markCheat() { + animateLoss(avatar.getX(), avatar.getY(), 100, "CHEATER"); + } } Model model; @@ -399,7 +417,7 @@ class View { SpriteAnimation debugMode = graphicEntityModule.createSpriteAnimation() .setImages(debugModePngs) .setX(1920 / 2) - .setY(80) + .setY(60) .setAnchorX(0.5) .setLoop(true); toggleModule.displayOnToggleState(debugMode, "debug", true); @@ -408,7 +426,7 @@ class View { .setAnchorX(0.5) .setAnchorY(0) .setX(1920 / 2) - .setY(280) + .setY(260) .setStrokeColor(0xff0080) .setFillColor(0xff0080) .setFontFamily("monospace") @@ -418,11 +436,47 @@ class View { animateTurnCounter(); } + void animateLoss(int x, int y, int size, String message) { + int startX; + if (x < 1920/2) { startX = 1920; } + else if (x > 1920/2) { startX = 1920; } + else { startX = 1920 * random.nextInt(2); } + + Text msg = graphicEntityModule.createText(message) + .setX(startX) + .setY(1080) + .setAnchorX(0.5) + .setAnchorY(0.5) + .setScaleX(3*random.nextDouble() - 1) + .setScaleY(3*random.nextDouble() - 1) + .setSkewX(2*random.nextDouble() - 1) + .setSkewY(2*random.nextDouble() - 1) + .setRotation(4*Math.PI * (1 + random.nextDouble()) + * (random.nextInt(2) == 0 ? 1 : -1)) + .setFontSize(0) + .setStrokeColor(0xff7f7f) + .setFillColor(0xff7f7f) + .setFontWeight(Text.FontWeight.BOLD) + .setTextAlign(TextBasedEntity.TextAlign.CENTER); + graphicEntityModule.commitEntityState(0.25, msg); + Curve curve = Curve.ELASTIC; + msg.setX(x, Curve.EASE_OUT) + .setY(y, Curve.ELASTIC) + .setScaleX(1, curve) + .setScaleY(1, curve) + .setSkewX(0, curve) + .setSkewY(0, curve) + .setRotation(2*Math.PI * (random.nextDouble() - 0.5), Curve.LINEAR) + .setFontSize(size, curve); + } + void doubleDefeat() { gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!")); + animateLoss(1920/2, 680, 150, "L0SERZ!"); } void draw() { gameManager.addToGameSummary("Draw."); + animateLoss(1920/2, 680, 200, "DRAW"); } } diff --git a/src/test/java/Main.java b/src/test/java/Main.java index 956794c..250f028 100644 --- a/src/test/java/Main.java +++ b/src/test/java/Main.java @@ -5,7 +5,7 @@ public class Main { MultiplayerGameRunner gameRunner = new MultiplayerGameRunner(); gameRunner.addAgent(Player1.class); - gameRunner.addAgent(Player2.class); + gameRunner.addAgent(Player1.class); // gameRunner.addAgent("python3 /home/user/player.py"); diff --git a/src/test/java/PlayerCheat.java b/src/test/java/PlayerCheat.java new file mode 100644 index 0000000..0f98d3c --- /dev/null +++ b/src/test/java/PlayerCheat.java @@ -0,0 +1,20 @@ +import java.util.Random; +import java.util.Scanner; + +public class PlayerCheat { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Random random = new Random(); + + 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(-42); + } + } +} diff --git a/src/test/java/Player2.java b/src/test/java/PlayerRand.java similarity index 97% rename from src/test/java/Player2.java rename to src/test/java/PlayerRand.java index 5dbb9b8..a58d673 100644 --- a/src/test/java/Player2.java +++ b/src/test/java/PlayerRand.java @@ -1,7 +1,7 @@ import java.util.Random; import java.util.Scanner; -public class Player2 { +public class PlayerRand { private final static String[] messages = { "meta⁵ @YannT ∷ (a → b) → f1 (f2 (f3 a)) → f1 (f2 (f3 b))", "By the power of Grayskull!", diff --git a/src/test/java/PlayerStupid.java b/src/test/java/PlayerStupid.java new file mode 100644 index 0000000..8cb1ed2 --- /dev/null +++ b/src/test/java/PlayerStupid.java @@ -0,0 +1,20 @@ +import java.util.Random; +import java.util.Scanner; + +public class PlayerStupid { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Random random = new Random(); + + 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(10); + } + } +} diff --git a/src/test/java/PlayerTimeout.java b/src/test/java/PlayerTimeout.java new file mode 100644 index 0000000..ba337b3 --- /dev/null +++ b/src/test/java/PlayerTimeout.java @@ -0,0 +1,18 @@ +import java.util.Random; +import java.util.Scanner; + +public class PlayerTimeout { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + Random random = new Random(); + + int roadLength = in.nextInt(); + int initialStones = in.nextInt(); + + while (true) { + int trollDistance = in.nextInt(); + int stones = in.nextInt(); + int opponentStones = in.nextInt(); + } + } +} -- 2.30.2