From 424174766971a11af0e953a6d5a328d466b8f68b Mon Sep 17 00:00:00 2001 From: JBM Date: Fri, 22 May 2020 17:32:41 +0200 Subject: [PATCH 1/1] Major model/view split. Compiles and runs, but viewer is sub-par. --- src/main/java/com/codingame/game/Model.java | 77 +++++ src/main/java/com/codingame/game/Player.java | 44 +-- src/main/java/com/codingame/game/Referee.java | 280 +++++------------- src/main/java/com/codingame/game/View.java | 180 ++++++++++- 4 files changed, 337 insertions(+), 244 deletions(-) diff --git a/src/main/java/com/codingame/game/Model.java b/src/main/java/com/codingame/game/Model.java index b310fdd..4cd1280 100644 --- a/src/main/java/com/codingame/game/Model.java +++ b/src/main/java/com/codingame/game/Model.java @@ -2,11 +2,54 @@ package com.codingame.game; import java.util.Random; +import com.codingame.gameengine.core.MultiplayerGameManager; +import com.google.inject.Inject; + class Model { + @Inject private MultiplayerGameManager gameManager; Random random; int roadLength; int initialStones; int trollPosition; + Player p0, p1; + + class Player { + com.codingame.game.Player gp; + int index; + + Player(int i) { index = i; } + + private int castlePosition; + public int getCastlePosition() { return castlePosition; } + public void setCastlePosition(int pos) { castlePosition = pos; } + + private int multiplier; + public int getMultiplier() { return multiplier; } + public void setMultiplier(int m){ multiplier = m; } + + 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; + } + public int getOppStones() { + return p0.stones + p1.stones - stones; + } + + public void adjustScore(int trollPosition) { + gp.setScore(Math.abs(castlePosition - trollPosition)); + } + + public int getTrollDistance() { + return Math.abs(castlePosition - trollPosition); + } + } void init(long seed) { random = new Random(seed); @@ -30,5 +73,39 @@ class Model { } trollPosition = roadLength / 2; + + p0 = new Player(0); + p0.gp = gameManager.getPlayer(0); + p0.setCastlePosition(0); + p0.setMultiplier(1); + p0.adjustScore(trollPosition); + p0.setStones(initialStones); + + p1 = new Player(1); + p1.gp = gameManager.getPlayer(1); + p1.setCastlePosition(roadLength); + p1.setMultiplier(-1); + p1.adjustScore(trollPosition); + p1.setStones(initialStones); + } + + private int winner; + boolean haveWinner() { + if (trollPosition == 0) { + winner = 1; + return true; + } + else if (trollPosition == roadLength) { + winner = 0; + return true; + } + else { + return false; + } + } + int getWinner() { return winner; } + + boolean exhausted() { + return p0.getStones() <= 0 && p1.getStones() <= 0; } } diff --git a/src/main/java/com/codingame/game/Player.java b/src/main/java/com/codingame/game/Player.java index db51bb4..ac96489 100644 --- a/src/main/java/com/codingame/game/Player.java +++ b/src/main/java/com/codingame/game/Player.java @@ -1,5 +1,7 @@ package com.codingame.game; +import java.util.List; + import java.util.regex.Pattern; import java.util.Scanner; import java.util.InputMismatchException; @@ -11,50 +13,12 @@ import com.codingame.gameengine.module.entities.Text; import com.codingame.gameengine.module.entities.Sprite; public class Player extends AbstractMultiplayerPlayer { - Group avatar; - Text stoneCounter; - Text message; - Sprite castle; - Text stone; + Model.Player model; + View.Player view; private String messageString = ""; public String getMessageString() { return messageString; } - 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; diff --git a/src/main/java/com/codingame/game/Referee.java b/src/main/java/com/codingame/game/Referee.java index edcafe2..b56cade 100644 --- a/src/main/java/com/codingame/game/Referee.java +++ b/src/main/java/com/codingame/game/Referee.java @@ -22,157 +22,51 @@ public class Referee extends AbstractReferee { @Inject private GraphicEntityModule graphicEntityModule; @Inject private View view; - - Model model = new Model(); - - // Hybrid :-( - Player p0, p1; + @Inject private Model model; @Override public void init() { model.init(gameManager.getSeed()); + gameManager.getPlayer(0).model = model.p0; + gameManager.getPlayer(1).model = model.p1; - p0 = gameManager.getPlayer(0); - p0.setCastlePosition(0); - p0.setMultiplier(1); - p0.adjustScore(model.trollPosition); - p0.sendInputLine(String.format("%d %d", model.roadLength, model.initialStones)); - - p1 = gameManager.getPlayer(1); - p1.setCastlePosition(model.roadLength); - p1.setMultiplier(-1); - p1.adjustScore(model.trollPosition); - p1.sendInputLine(String.format("%d %d", model.roadLength, model.initialStones)); - - view.init(); - drawPlayer(); - - // result in text display, so do last: - p0.setStones(model.initialStones); - p1.setStones(model.initialStones); - - gameManager.setFrameDuration(2000); // XXX - } - - 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); - - Sprite avatarSprite = graphicEntityModule.createSprite() - .setX(x) - .setY(y) - .setZIndex(20) - .setImage(player.getAvatarToken()) - .setAnchor(0.5) - .setBaseHeight(116) - .setBaseWidth(116); - - player.avatar = graphicEntityModule.createGroup(border1, border2, avatarSprite); - - Text text = graphicEntityModule.createText(player.getNicknameToken()) - .setX(x) - .setY(y + 120) - .setZIndex(20) - .setFontSize(40) - .setFillColor(0x7f3f00) - .setAnchor(0.5); - - player.stoneCounter = graphicEntityModule.createText("S") - .setX(x) - .setY(y+200) - .setZIndex(20) - .setFontSize(40) - .setFillColor(0x7f3f00) - .setAnchor(0.5); - - player.message = graphicEntityModule.createText() - .setX(p0 ? 15 : 1920-15) - .setY(680) - .setZIndex(1) - .setFontSize(40) - .setStrokeColor(0x000000) - .setFillColor(0xffbf7f) - .setAnchorX(p0 ? 0 : 1) - .setAnchorY(1); - - player.castle = graphicEntityModule.createSprite() - .setImage("castle.png") - .setTint(player.getColorToken()) - .setX(p0 ? 160 : 1920-160) - .setY(p0 ? 890 : 880) - .setZIndex(1) - .setAnchorX(0.5) - .setAnchorY(1) - .setScaleX(p0 ? 1 : -1); - - player.stone = graphicEntityModule.createText() - .setZIndex(3) - .setFontSize(150) - .setFillColor(0x12322a) - .setAnchor(0.5) - .setAlpha(0); + for (Player p : gameManager.getPlayers()) { + p.sendInputLine(String.format("%d %d", model.roadLength, model.initialStones)); } - } - private void moveTroll() { - graphicEntityModule.commitEntityState(0.5, view.troll, view.trollPositionGauge); - int x0 = p0.castle.getX(), x1 = p1.castle.getX(); - int y0 = p0.castle.getY(), y1 = p1.castle.getY(); - view.troll.setX(x0 + model.trollPosition * (x1-x0) / model.roadLength, - Curve.ELASTIC); - view.troll.setY(y0 + model.trollPosition * (y1-y0) / model.roadLength, - Curve.ELASTIC); + view.init(model); + gameManager.getPlayer(0).view = view.p0; + gameManager.getPlayer(1).view = view.p1; - view.trollPositionGauge.setX((view.trollPositionGauge.getX() + view.troll.getX()) / 2); - int delta = model.trollPosition - model.roadLength / 2; - if (delta < 0) { - view.trollPositionGauge.setText("← " + Math.abs(delta)); - } - else if (delta > 0) { - view.trollPositionGauge.setText(Math.abs(delta) + " →"); - } - else { - view.trollPositionGauge.setText("↔"); - } - graphicEntityModule.commitEntityState(0.75, view.trollPositionGauge); - view.trollPositionGauge.setX(view.troll.getX()); + gameManager.setFrameDuration(2000); // XXX } @Override public void gameTurn(int turn) { + // System.err.println("Starting turn " + 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())); + boolean disqual = false; + boolean victory = false; + boolean exhausted = false; + int delta = 0; for (Player player : gameManager.getActivePlayers()) { + Model.Player p = player.model; + { + int trollDistance = p.getTrollDistance(); + int stones = p.getStones(); + int oppStones = p.getOppStones(); + player.sendInputLine(String.format("%d %d %d", trollDistance, stones, oppStones)); + } player.execute(); } - int delta = 0; for (Player player : gameManager.getActivePlayers()) { + Model.Player p = player.model; + try { int stones = player.getAction(); - if (stones == 0 && player.getStones() > 0) { + if (stones == 0 && p.getStones() > 0) { if (model.random.nextInt(10) > 0) { gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " tried not throwing stones. Fixing that for them because I'm in a good mood today.")); stones = 1; @@ -181,116 +75,96 @@ public class Referee extends AbstractReferee { throw new InvalidAction("tried not throwing any stone. They were then eaten by a grue."); } } - player.consumeStones(stones); + p.consumeStones(stones); gameManager.addToGameSummary(String.format("%s throws %d stone%s at the troll.", player.getNicknameToken(), stones, stones == 1 ? "" : "s")); - delta += player.getMultiplier() * stones; + delta += player.model.getMultiplier() * stones; if (stones < 0) { player.deactivate(player.getNicknameToken() + " CHEAT"); gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " cheated. Banning account.")); player.setScore(-1); - endGame(); + disqual = true; } else 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() + view.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(view.troll.getX()); - player.stone.setY(view.troll.getY() - 50, Curve.EASE_IN); - player.stone.setAlpha(0, Curve.EASE_IN); - graphicEntityModule.commitEntityState(0.5, player.stone); + player.view.animateStones(stones); } } catch (InvalidAction e) { player.deactivate(player.getNicknameToken() + " INVALID"); gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " " + e.getMessage())); player.setScore(-1); - endGame(); + disqual = true; } catch (NumberFormatException e) { player.deactivate(player.getNicknameToken() + " ERROR"); gameManager.addToGameSummary(GameManager.formatErrorMessage(player.getNicknameToken() + " provided malformed input!")); player.setScore(-1); - endGame(); + disqual = true; } catch (TimeoutException e) { gameManager.addToGameSummary(player.getNicknameToken() + " timed out!"); player.deactivate(player.getNicknameToken() + " T/O"); player.setScore(-1); - endGame(); + disqual = true; } - player.message - .setText(player.getMessageString()); - // .setAnchorX(/*player == p0 ? 0 : */ 1); - graphicEntityModule.commitEntityState(0, player.message); + player.view.displayMessage(player.getMessageString()); } - if (delta > 0) { - gameManager.addToGameSummary("Troll walks right."); - model.trollPosition++; - moveTroll(); - } - else if (delta < 0) { - gameManager.addToGameSummary("Troll walks left."); - model.trollPosition--; - moveTroll(); - } - else { - gameManager.addToGameSummary("Troll stands still."); - } + if (! disqual) { + if (delta > 0) { + gameManager.addToGameSummary("Troll walks right."); + model.trollPosition++; + } + else if (delta < 0) { + gameManager.addToGameSummary("Troll walks left."); + model.trollPosition--; + } + else { + gameManager.addToGameSummary("Troll stands still."); + // XXX animate + } + view.moveTroll(); - for (Player player : gameManager.getActivePlayers()) { - player.adjustScore(model.trollPosition); - if (model.trollPosition == player.getCastlePosition()) { - gameManager.addToGameSummary(GameManager.formatErrorMessage("Troll destroys " + player.getNicknameToken())); - endGame(); + for (Player player : gameManager.getActivePlayers()) { + player.model.adjustScore(model.trollPosition); } - } - if (p0.getStones() <= 0 && p1.getStones() <= 0) { - endGame(); + if (model.haveWinner()) { + int winner = model.getWinner(); + gameManager.addToGameSummary(GameManager.formatErrorMessage("Troll destroys " + gameManager.getPlayer(winner).getNicknameToken())); + victory = true; + } + else if (model.exhausted()) exhausted = true; } - } - - private void destroyPlayer(Player player) { - player.avatar.setRotation(170*Math.PI/180, Curve.ELASTIC); - graphicEntityModule.commitEntityState(0.5, player.castle); - player.castle.setX(player.castle.getX(), Curve.ELASTIC); - player.castle.setScaleY(-0.2, Curve.EASE_IN); + if (disqual || victory || exhausted) endGame(); } private void endGame() { - if (! gameManager.isGameEnd()) { - gameManager.endGame(); + gameManager.endGame(); - if (p0.getScore() > p1.getScore()) { - gameManager.addToGameSummary(GameManager.formatSuccessMessage(p0.getNicknameToken() + " wins")); - destroyPlayer(p1); - } - else if (p0.getScore() < p1.getScore()) { - gameManager.addToGameSummary(GameManager.formatSuccessMessage(p1.getNicknameToken() + " wins")); - destroyPlayer(p0); - } - else if (p0.getScore() < 0) { - gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!")); - destroyPlayer(p0); - destroyPlayer(p1); - } - else { - gameManager.addToGameSummary("Draw."); - } + Player p0 = gameManager.getPlayer(0); + Player p1 = gameManager.getPlayer(1); + + int s0 = p0.getScore(); + int s1 = p1.getScore(); + + if (s0 > s1) { + gameManager.addToGameSummary(GameManager.formatSuccessMessage(p0.getNicknameToken() + " wins")); + p1.view.destroy(); + } + else if (s0 < s1) { + gameManager.addToGameSummary(GameManager.formatSuccessMessage(p1.getNicknameToken() + " wins")); + p0.view.destroy(); + } + else if (s0 < 0) { + gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!")); + p0.view.destroy(); + p1.view.destroy(); + } + else { + gameManager.addToGameSummary("Draw."); } } } diff --git a/src/main/java/com/codingame/game/View.java b/src/main/java/com/codingame/game/View.java index 38dcac1..65bacea 100644 --- a/src/main/java/com/codingame/game/View.java +++ b/src/main/java/com/codingame/game/View.java @@ -2,19 +2,173 @@ package com.codingame.game; import java.util.Random; +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.Group; +import com.codingame.gameengine.module.entities.Curve; import com.google.inject.Inject; class View { + @Inject private MultiplayerGameManager gameManager; @Inject private GraphicEntityModule graphicEntityModule; + class Player { + Model.Player model; + int colorToken; + String nicknameToken; + String avatarToken; + + Group avatar; + Text stoneCounter; + Text message; + Sprite castle; + Text stone; + + void init(com.codingame.game.Player p) { + model = p.model; + colorToken = p.getColorToken(); + nicknameToken = p.getNicknameToken(); + avatarToken = p.getAvatarToken(); + + boolean p0 = model.index == 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(colorToken); + + Rectangle border2 = graphicEntityModule + .createRectangle() + .setWidth(120) + .setHeight(120) + .setX(x - 60) + .setY(y - 60) + .setLineWidth(0) + .setFillColor(0xffffff); + + Sprite avatarSprite = graphicEntityModule.createSprite() + .setX(x) + .setY(y) + .setZIndex(20) + .setImage(avatarToken) + .setAnchor(0.5) + .setBaseHeight(116) + .setBaseWidth(116); + + avatar = graphicEntityModule.createGroup(border1, border2, avatarSprite); + + Text text = graphicEntityModule.createText(nicknameToken) + .setX(x) + .setY(y + 120) + .setZIndex(20) + .setFontSize(40) + .setFillColor(0x7f3f00) + .setAnchor(0.5); + + stoneCounter = graphicEntityModule.createText("S") + .setX(x) + .setY(y+200) + .setZIndex(20) + .setFontSize(40) + .setFillColor(0x7f3f00) + .setAnchor(0.5); + + message = graphicEntityModule.createText() + .setX(p0 ? 15 : 1920-15) + .setY(680) + .setZIndex(1) + .setFontSize(40) + .setStrokeColor(0x000000) + .setFillColor(0xffbf7f) + .setAnchorX(p0 ? 0 : 1) + .setAnchorY(1); + + castle = graphicEntityModule.createSprite() + .setImage("castle.png") + .setTint(colorToken) + .setX(p0 ? 160 : 1920-160) + .setY(p0 ? 890 : 880) + .setZIndex(1) + .setAnchorX(0.5) + .setAnchorY(1) + .setScaleX(p0 ? 1 : -1); + + stone = graphicEntityModule.createText() + .setZIndex(3) + .setFontSize(150) + .setFillColor(0x12322a) + .setAnchor(0.5) + .setAlpha(0); + } + + void setStone() { + int stones = model.getStones(); + 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"); + } + } + void animateStones(int stones) { + stone.setX(castle.getX()); + stone.setY(castle.getY() - 100); + stone.setText(new Integer(stones).toString()); + stone.setAlpha(1); + graphicEntityModule.commitEntityState(0, stone); + + int peakX = (castle.getX() + troll.getX()) / 2; + int peakY = 540; + stone.setX(peakX); + stone.setY(peakY, Curve.EASE_OUT); + graphicEntityModule.commitEntityState(0.25, + stone, + stoneCounter); + + stone.setX(troll.getX()); + stone.setY(troll.getY() - 50, Curve.EASE_IN); + stone.setAlpha(0, Curve.EASE_IN); + graphicEntityModule.commitEntityState(0.5, stone); + } + void displayMessage(String msg) { + message.setText(msg); + graphicEntityModule.commitEntityState(0, message); + } + + void destroy() { + avatar.setRotation(170*Math.PI/180, Curve.ELASTIC); + + graphicEntityModule.commitEntityState(0.5, castle); + castle.setX(castle.getX(), Curve.ELASTIC); + castle.setScaleY(-0.2, Curve.EASE_IN); + } + } + + Model model; Random random = new Random(); Sprite troll; Text trollPositionGauge; + Player p0 = new Player(), p1 = new Player(); + + void init(Model m) { + model = m; + p0.init(gameManager.getPlayer(0)); + p1.init(gameManager.getPlayer(1)); - void init() { drawBackground(); drawTroll(); } @@ -42,4 +196,28 @@ class View { .setFillColor(0xffffff); } + 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 + model.trollPosition * (x1-x0) / model.roadLength, + Curve.ELASTIC); + troll.setY(y0 + model.trollPosition * (y1-y0) / model.roadLength, + Curve.ELASTIC); + + trollPositionGauge.setX((trollPositionGauge.getX() + troll.getX()) / 2); + int delta = model.trollPosition - model.roadLength / 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()); + } + } -- 2.30.2