diff --git a/src/main/java/com/bernard/nodecames/HttpController.java b/src/main/java/com/bernard/nodecames/HttpController.java index c6a6050..2a42fde 100644 --- a/src/main/java/com/bernard/nodecames/HttpController.java +++ b/src/main/java/com/bernard/nodecames/HttpController.java @@ -1,5 +1,18 @@ package com.bernard.nodecames; +import static com.bernard.nodecames.model.GridConfig.CardColors.BB; +import static com.bernard.nodecames.model.GridConfig.CardColors.BG; +import static com.bernard.nodecames.model.GridConfig.CardColors.BW; +import static com.bernard.nodecames.model.GridConfig.CardColors.GB; +import static com.bernard.nodecames.model.GridConfig.CardColors.GG; +import static com.bernard.nodecames.model.GridConfig.CardColors.GW; +import static com.bernard.nodecames.model.GridConfig.CardColors.WB; +import static com.bernard.nodecames.model.GridConfig.CardColors.WG; +import static com.bernard.nodecames.model.GridConfig.CardColors.WW; + +import java.util.List; +import java.util.Random; +import java.util.Set; import java.util.UUID; import org.springframework.stereotype.Controller; @@ -8,14 +21,17 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.servlet.view.RedirectView; import com.bernard.nodecames.game.GameManager; +import com.bernard.nodecames.model.GridConfig; +import com.bernard.nodecames.model.GridConfig.CardColors; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; -@AllArgsConstructor +@RequiredArgsConstructor @Controller public class HttpController { - GameManager gm; + private final GameManager gm; + private final Random rand = new Random(); @GetMapping("/") public String index() { @@ -27,9 +43,22 @@ public class HttpController { return "grid"; } + public static final List CLASSICAL25 = List.of( + BB,BW,WB,BG,GB,GG,GG,GG,WG,WG,WG,WG,WG,GW,GW,GW,GW,GW,WW,WW,WW,WW,WW,WW,WW + ); + public static final Set BASE_HINTWORDCOUNT = Set.of( + 0,1,2,3,4,5,6,7,8,9,-1 + ); + public static final GridConfig CODENAMES_CLASSICAL = new GridConfig( + 9, true, true, CLASSICAL25, + 9, BASE_HINTWORDCOUNT, true, + Integer.MAX_VALUE, true, false, + true, false); + @GetMapping("/create-room") public RedirectView createRoom() { - UUID uuid = gm.newGrid(); + Random r = new Random(rand.nextLong()); + UUID uuid = gm.newGrid(CODENAMES_CLASSICAL, r); return new RedirectView("/room/"+uuid.toString()+"/grid"); } } diff --git a/src/main/java/com/bernard/nodecames/frontend/JsonDataService.java b/src/main/java/com/bernard/nodecames/frontend/JsonDataService.java new file mode 100644 index 0000000..328d82d --- /dev/null +++ b/src/main/java/com/bernard/nodecames/frontend/JsonDataService.java @@ -0,0 +1,104 @@ +package com.bernard.nodecames.frontend; + +import java.util.stream.IntStream; + +import org.springframework.stereotype.Service; + +import com.bernard.nodecames.model.Card; +import com.bernard.nodecames.model.GameEvent; +import com.bernard.nodecames.model.Grid; +import com.bernard.nodecames.model.Grid.Phase; +import com.bernard.nodecames.model.Hint; + +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; + +@Service +public class JsonDataService { + + private static final JsonNodeFactory jsn = JsonNodeFactory.instance; + + public JsonNode cardData(Card c, char player) { + ObjectNode cardNode = jsn.objectNode(); + cardNode.set("word", jsn.stringNode(c.getWord())); + cardNode.set("color", jsn.stringNode((player=='A'?c.getColorA():c.getColorB()).name().toLowerCase())); + if((player=='A' && c.isColorBPublic()) || (player=='B' && c.isColorAPublic())) + cardNode.set("otherColor", jsn.stringNode((player=='A'?c.getColorB():c.getColorA()).name().toLowerCase())); + cardNode.set("revealed-a", jsn.booleanNode(c.isColorBPublic())); + cardNode.set("revealed-b", jsn.booleanNode(c.isColorAPublic())); + return cardNode; + } + + public JsonNode eventData(Grid g, GameEvent ge) { + ObjectNode geNode = jsn.objectNode(); + geNode.set("type", jsn.stringNode(ge.getType().name().toLowerCase())); + geNode.set("issuer", jsn.stringNode(Character.toString(ge.getIssuer()))); + switch (ge.getType()) { + case GameEvent.Type.HINT: + Hint hint = (Hint)ge.getData(); + geNode.set("word", jsn.stringNode(hint.getWord())); + geNode.set("wordCount", jsn.numberNode(hint.getWordCount())); + break; + case GameEvent.Type.GUESS: + Card c = (Card)ge.getData(); + Integer pos = IntStream.range(0, g.getCardCount()) + .filter(i -> c == g.getCards()[i]) + .findFirst() + .getAsInt(); + geNode.set("card-index", jsn.numberNode(pos)); + geNode.set("color", jsn.stringNode(((ge.getIssuer()=='A')?c.getColorB():c.getColorA()).name().toLowerCase())); + break; + case GameEvent.Type.END_GUESSING: + geNode.set("manual", jsn.booleanNode((Boolean)ge.getData())); + break; + case GameEvent.Type.GAME_END: + geNode.set("win", jsn.booleanNode((Boolean)ge.getData())); + break; + case GameEvent.Type.SUDDEN_DEATH: + break; + } + return geNode; + } + + public JsonNode phaseData(Phase p) { + ObjectNode out = jsn.objectNode(); + out.set("a-guessing", jsn.booleanNode(p.isAGuessing())); + out.set("b-guessing", jsn.booleanNode(p.isBGuessing())); + out.set("a-hinting", jsn.booleanNode(p.isAHinting())); + out.set("b-hinting", jsn.booleanNode(p.isBHinting())); + out.set("sudden-death", jsn.booleanNode(p.isSuddenDeath())); + out.set("game-ended", jsn.booleanNode(p.isGameEnded())); + return out; + } + + public JsonNode gridData(Grid g, char player) { + + ArrayNode cardsNode = jsn.arrayNode(g.getCardCount()); + for (int i = 0; i < g.getCardCount(); i++) + cardsNode.add(cardData(g.getCards()[i], player)); + + ArrayNode eventsNode = jsn.arrayNode(g.getGameEvents().size()); + for(GameEvent ge : g.getGameEvents()) + eventsNode.add(eventData(g, ge)); + + ObjectNode currentHintNode = jsn.objectNode(); + if(g.getCurrentHint() != null) { + currentHintNode.set("word", jsn.stringNode(g.getCurrentHint().getWord())); + currentHintNode.set("wordCount", jsn.numberNode(g.getCurrentHint().getWordCount())); + } + + ObjectNode out = jsn.objectNode(); + out.set("cards", cardsNode); + out.set("phase", phaseData(g.getPhase())); + out.set("events", eventsNode); + if(g.getCurrentHint() != null) + out.set("current-hint", currentHintNode); + out.set("current-guess-count", jsn.numberNode(g.getCurrentGuessCount())); + out.set("used-hints", jsn.numberNode(g.getUsedHints())); + out.set("used-whites", jsn.numberNode(g.getUsedWhites())); + + return out; + } +} diff --git a/src/main/java/com/bernard/nodecames/frontend/WebSocketController.java b/src/main/java/com/bernard/nodecames/frontend/WebSocketController.java index 1545ef0..0746bc8 100644 --- a/src/main/java/com/bernard/nodecames/frontend/WebSocketController.java +++ b/src/main/java/com/bernard/nodecames/frontend/WebSocketController.java @@ -13,38 +13,37 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import com.bernard.nodecames.game.GameManager; +import com.bernard.nodecames.game.IllegalGameActionException; import com.bernard.nodecames.model.Card; import com.bernard.nodecames.model.GameEvent; import com.bernard.nodecames.model.Grid; -import com.bernard.nodecames.model.Grid.Phase; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.node.JsonNodeFactory; @Controller public class WebSocketController { - private static final JsonNodeFactory jsn = JsonNodeFactory.instance; - private GameManager gm; private SimpMessagingTemplate template; + private JsonDataService json; - public WebSocketController(GameManager gm, SimpMessagingTemplate template) { + public WebSocketController(GameManager gm, SimpMessagingTemplate template, JsonDataService json) { this.gm = gm; this.template = template; + this.json = json; gm.setWsc(this); } public void onNewEvent(Grid g, GameEvent ge) { this.template.convertAndSend("/topic/new-event", - gm.eventData(g, ge) + json.eventData(g, ge) ); } - public void onNewPhase(Grid g, Phase p) { + public void onNewPhase(Grid g) { this.template.convertAndSend( "/topic/new-phase", - gm.phaseData(g.getPhase()) + json.phaseData(g.getPhase()) ); } @@ -52,7 +51,7 @@ public class WebSocketController { Card c = g.getCards()[cardIndex]; this.template.convertAndSend( "/topic/update-card/" + Character.toLowerCase(player), - gm.cardData(c, player), + json.cardData(c, player), Map.of("cardIndex", cardIndex) ); } @@ -61,6 +60,10 @@ public class WebSocketController { publishCardUpdateToPlayer(g, cardIndex, 'B'); } + public void publishError(char issuer, IllegalGameActionException e) { + //TODO open error channel + } + @GetMapping("/room/{id}/game/{player}") public Object grid(@PathVariable("id") String gridId, @PathVariable("player") String playerStr) { char player; @@ -73,7 +76,7 @@ public class WebSocketController { Grid g = gm.findGrid(gridId); - return new ResponseEntity<>(gm.gridData(g, player), HttpStatus.OK); + return new ResponseEntity<>(json.gridData(g, player), HttpStatus.OK); } @MessageMapping("submit-hint") @@ -83,7 +86,12 @@ public class WebSocketController { Grid g = gm.findGrid(roomId); - gm.proposeHint(g, player.charAt(0), hint, wordCount); + try { + gm.proposeHint(g, player.charAt(0), hint, wordCount); + } catch (IllegalGameActionException e) { + publishError(player.charAt(0), e); + e.printStackTrace(); + } } @MessageMapping("point-card") @@ -91,13 +99,23 @@ public class WebSocketController { int cardIndex = content.asObject().get("cardIndex").intValue(); Grid g = gm.findGrid(roomId); - gm.pointCard(g, player.charAt(0), cardIndex); - publishCardUpdate(g, cardIndex); + try { + gm.pointCard(g, player.charAt(0), cardIndex); + publishCardUpdate(g, cardIndex); + } catch (IllegalGameActionException e) { + publishError(player.charAt(0), e); + e.printStackTrace(); + } } @MessageMapping("end-guessing") public void endGuessing(@Header("room") String roomId, @Header("player") String player) { Grid g = gm.findGrid(roomId); - gm.endGuessing(g, player.charAt(0)); + try { + gm.endGuessing(g, player.charAt(0)); + } catch (IllegalGameActionException e) { + publishError(player.charAt(0), e); + e.printStackTrace(); + } } } diff --git a/src/main/java/com/bernard/nodecames/game/DictionnariesService.java b/src/main/java/com/bernard/nodecames/game/DictionnariesService.java new file mode 100644 index 0000000..bd6147d --- /dev/null +++ b/src/main/java/com/bernard/nodecames/game/DictionnariesService.java @@ -0,0 +1,57 @@ +package com.bernard.nodecames.game; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Scanner; +import java.util.Set; + +import javax.management.RuntimeErrorException; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; +import lombok.Setter; + +@Service +public class DictionnariesService { + + @Value("classpath:dictionaries/fr.txt") + @Setter + Resource dictFr; + + //TODO do Spring boot magic to only generate this once + public List allWords() { + try (Scanner wordsScanner = new Scanner(dictFr.getInputStream())) { + List out = new ArrayList<>(); + while (wordsScanner.hasNextLine()) + out.add(wordsScanner.nextLine()); + return Collections.unmodifiableList(out); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String[] randomWords(int count, Random r) { + List allWords = allWords(); + if(count > allWords.size()) + throw new IllegalArgumentException("Not enough words in the dictionary"); + String[] out = new String[count]; + Set already = new HashSet<>(); + int k; + for(int i = 0; i games = new HashMap<>(); + private static Map randoms = new HashMap<>(); @Setter - WebSocketController wsc; + private WebSocketController wsc = null; - @Value("classpath:dictionaries/fr.txt") - Resource dictFr; + private final DictionnariesService dictionnaries; private void newGameEvent(Grid g, GameEvent ge) { g.newGameEvent(ge); @@ -58,170 +47,18 @@ public class GameManager { if(!phase.isAnyoneGuessing()) { g.setCurrentHint(null); } - wsc.onNewPhase(g, phase); + wsc.onNewPhase(g); } - //TODO do Spring boot magic to only generate this once - public List allWords() { - try (Scanner wordsScanner = new Scanner(dictFr.getInputStream())) { - List out = new ArrayList<>(); - while (wordsScanner.hasNextLine()) - out.add(wordsScanner.nextLine()); - return Collections.unmodifiableList(out); - } catch (IOException e) { - throw new IllegalStateException("Could not read internal dictionnary", e); - } - } - - public String[] randomWords(int count) { - List allWords = allWords(); - //TODO seed the games - Random r = new Random(); - if(count > allWords.size()) - throw new IllegalArgumentException("Not enough words in the dictionary"); - String[] out = new String[count]; - Set already = new HashSet<>(); - int k; - for(int i = 0; i cards = new ArrayList<>(25); - int i = 0; - cards.add(new Card(words[i++], Color.BLACK, Color.BLACK)); - for (;i < 4; i++) - cards.add(new Card(words[i], Color.GREEN, Color.GREEN)); - for (;i < 9; i++) - cards.add(new Card(words[i], Color.WHITE, Color.GREEN)); - for (;i < 14; i++) - cards.add(new Card(words[i], Color.GREEN, Color.WHITE)); - cards.add(new Card(words[i++], Color.GREEN, Color.BLACK)); - cards.add(new Card(words[i++], Color.BLACK, Color.GREEN)); - cards.add(new Card(words[i++], Color.WHITE, Color.BLACK)); - cards.add(new Card(words[i++], Color.BLACK, Color.WHITE)); - for (;i < 25; i++) - cards.add(new Card(words[i], Color.WHITE, Color.WHITE)); - - Collections.shuffle(cards); - - Grid g = new Grid(uuid.toString(), (Card[])cards.toArray(new Card[cards.size()])); - - games.put(uuid, g); - - return uuid; - } - - /********************************** - * JSON ACCESSORS * - **********************************/ - - public JsonNode cardData(Card c, char player) { - ObjectNode cardNode = jsn.objectNode(); - cardNode.set("word", jsn.stringNode(c.getWord())); - cardNode.set("color", jsn.stringNode((player=='A'?c.getColorA():c.getColorB()).name().toLowerCase())); - if((player=='A' && c.isColorBPublic()) || (player=='B' && c.isColorAPublic())) - cardNode.set("otherColor", jsn.stringNode((player=='A'?c.getColorB():c.getColorA()).name().toLowerCase())); - cardNode.set("revealed-a", jsn.booleanNode(c.isColorBPublic())); - cardNode.set("revealed-b", jsn.booleanNode(c.isColorAPublic())); - return cardNode; - } - - public JsonNode eventData(Grid g, GameEvent ge) { - ObjectNode geNode = jsn.objectNode(); - geNode.set("type", jsn.stringNode(ge.getType().name().toLowerCase())); - geNode.set("issuer", jsn.stringNode(Character.toString(ge.getIssuer()))); - switch (ge.getType()) { - case GameEvent.Type.HINT: - Hint hint = (Hint)ge.getData(); - geNode.set("word", jsn.stringNode(hint.getWord())); - geNode.set("wordCount", jsn.numberNode(hint.getWordCount())); - break; - case GameEvent.Type.GUESS: - Card c = (Card)ge.getData(); - Integer pos = IntStream.range(0, g.getCardCount()) - .filter(i -> c == g.getCards()[i]) - .findFirst() - .getAsInt(); - geNode.set("card-index", jsn.numberNode(pos)); - geNode.set("color", jsn.stringNode(((ge.getIssuer()=='A')?c.getColorB():c.getColorA()).name().toLowerCase())); - break; - case GameEvent.Type.END_GUESSING: - geNode.set("manual", jsn.booleanNode((Boolean)ge.getData())); - break; - case GameEvent.Type.GAME_END: - geNode.set("win", jsn.booleanNode((Boolean)ge.getData())); - break; - case GameEvent.Type.SUDDEN_DEATH: - break; - } - return geNode; - } - - public JsonNode phaseData(Phase p) { - ObjectNode out = jsn.objectNode(); - out.set("a-guessing", jsn.booleanNode(p.isAGuessing())); - out.set("b-guessing", jsn.booleanNode(p.isBGuessing())); - out.set("a-hinting", jsn.booleanNode(p.isAHinting())); - out.set("b-hinting", jsn.booleanNode(p.isBHinting())); - out.set("sudden-death", jsn.booleanNode(p.isSuddenDeath())); - out.set("game-ended", jsn.booleanNode(p.isGameEnded())); - return out; - } - - public JsonNode gridData(Grid g, char player) { - - ArrayNode cardsNode = jsn.arrayNode(g.getCardCount()); - for (int i = 0; i < g.getCardCount(); i++) - cardsNode.add(cardData(g.getCards()[i], player)); - - ArrayNode eventsNode = jsn.arrayNode(g.getGameEvents().size()); - for(GameEvent ge : g.getGameEvents()) - eventsNode.add(eventData(g, ge)); - - ObjectNode currentHintNode = jsn.objectNode(); - if(g.getCurrentHint() != null) { - currentHintNode.set("word", jsn.stringNode(g.getCurrentHint().getWord())); - currentHintNode.set("wordCount", jsn.numberNode(g.getCurrentHint().getWordCount())); - } - - ObjectNode out = jsn.objectNode(); - out.set("cards", cardsNode); - out.set("phase", phaseData(g.getPhase())); - out.set("events", eventsNode); - if(g.getCurrentHint() != null) - out.set("current-hint", currentHintNode); - out.set("current-guess-count", jsn.numberNode(g.getCurrentGuessCount())); - out.set("used-hints", jsn.numberNode(g.getUsedHints())); - out.set("used-whites", jsn.numberNode(g.getUsedWhites())); - - return out; - } - - /*################################* - * GAME LOGIC * - *################################*/ /** @returns true iff all green cards of this player have been revealed either side */ - public boolean allGreenRevealed(Grid g, char player) { + private boolean allGreenRevealed(Grid g, char player) { for(Card c : g.getCards()) { if (c.getColor(player) == Color.GREEN && !c.isColorPublic(player) && !(c.getColor(other(player)) == Color.GREEN && c.isColorPublic(other(player)))) @@ -229,6 +66,47 @@ public class GameManager { } return true; } + + public Grid findGrid(String gridId) { + return games.get(UUID.fromString(gridId)); + } + + public UUID newGrid(GridConfig gc, Random r) { + UUID uuid = UUID.randomUUID(); + randoms.put(uuid, r); + String[] words = dictionnaries.randomWords(gc.getWordCount(), r); + List cards = new ArrayList<>(gc.getWordCount()); + List distrib = gc.getWordDistribution(); + for (int j = 0; j < distrib.size(); j++) { + cards.add(new Card(words[j], distrib.get(j).getFaceA(), distrib.get(j).getFaceB())); + } + Collections.shuffle(cards, r); + + Phase startPhase; + if(gc.isAnyoneStarts()) { + startPhase = Phase.bothHinting(); + } else { + if(r.nextBoolean()) + startPhase = Phase.hinting('A'); + else + startPhase = Phase.hinting('B'); + } + + Grid g = new Grid(uuid.toString(), + (Card[])cards.toArray(new Card[cards.size()]), + new ArrayList<>(), + null, + 0, + 0, + 0, + startPhase, + Optional.empty(), + gc); + + games.put(uuid, g); + + return uuid; + } /********************************** * GAME ACTIONS * @@ -239,9 +117,9 @@ public class GameManager { newGameEvent(g, GameEvent.newGameEndEvent(win)); } - public void proposeHint(Grid g, char player, String hint, int wordCount) { + public void proposeHint(Grid g, char player, String hint, int wordCount) throws IllegalGameActionException { if (!g.getPhase().isHinting(player)) { - throw new RuntimeException("This action is not allowed now"); + throw new IllegalGameActionException(g, "This action is not allowed now"); } Hint h = new Hint(hint, wordCount); @@ -258,7 +136,7 @@ public class GameManager { */ private void endGuessingRound(Grid g, char player) { // We're done with this round of guesses - if(g.getUsedHints() >= MAX_HINT_COUNT) { + if(g.getUsedHints() >= g.getConfig().getMaxHint()) { // We're entering sudden death newGameEvent(g, GameEvent.newSuddenDeath()); if(allGreenRevealed(g, player)) { @@ -279,9 +157,9 @@ public class GameManager { } } - public Card.Color pointCard(Grid g, char player, int cardIndex) { + public Card.Color pointCard(Grid g, char player, int cardIndex) throws IllegalGameActionException { if (!g.getPhase().isGuessing(player)) { - throw new RuntimeException("This action is not allowed now"); + throw new IllegalGameActionException(g, "This action is not allowed now"); } if (cardIndex<0 || cardIndex>g.getCardCount()) { throw new IllegalArgumentException("The given card index is invalid"); @@ -289,7 +167,7 @@ public class GameManager { Card c = g.getCards()[cardIndex]; if(c.isColorPublic(other(player)) || (c.getColor(player) == Card.Color.GREEN && c.isColorPublic(player))) - throw new RuntimeException("Card has already been revealed"); + throw new IllegalGameActionException(g, "Card has already been revealed"); c.setColorPublic(other(player), true); Card.Color guessedColor = c.getColor(other(player)); @@ -328,9 +206,9 @@ public class GameManager { return guessedColor; } - public void endGuessing(Grid g, char player) { + public void endGuessing(Grid g, char player) throws IllegalGameActionException { if (!g.getPhase().isGuessing(player)) { - throw new RuntimeException("This action is not allowed now"); + throw new IllegalGameActionException(g, "This action is not allowed now"); } endGuessingRound(g, player); newGameEvent(g, GameEvent.newEndGuessingEvent(true, player)); diff --git a/src/main/java/com/bernard/nodecames/game/IllegalGameActionException.java b/src/main/java/com/bernard/nodecames/game/IllegalGameActionException.java new file mode 100644 index 0000000..4d8b376 --- /dev/null +++ b/src/main/java/com/bernard/nodecames/game/IllegalGameActionException.java @@ -0,0 +1,16 @@ +package com.bernard.nodecames.game; + +import com.bernard.nodecames.model.Grid; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class IllegalGameActionException extends Exception { + + private final Grid g; + + private final String message; + +} diff --git a/src/main/java/com/bernard/nodecames/model/Grid.java b/src/main/java/com/bernard/nodecames/model/Grid.java index bdf5250..9a04c67 100644 --- a/src/main/java/com/bernard/nodecames/model/Grid.java +++ b/src/main/java/com/bernard/nodecames/model/Grid.java @@ -1,6 +1,5 @@ package com.bernard.nodecames.model; -import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -30,9 +29,7 @@ public class Grid { Phase phase; Optional won; - public Grid(String name, Card[] cards) { - this(name, cards, new ArrayList<>(), null, 0, 0, 0, Phase.bothHinting(), Optional.empty()); - } + GridConfig config; public void incrementHintCount() { this.setUsedHints(this.getUsedHints()+1); diff --git a/src/main/java/com/bernard/nodecames/model/GridConfig.java b/src/main/java/com/bernard/nodecames/model/GridConfig.java new file mode 100644 index 0000000..cd6070c --- /dev/null +++ b/src/main/java/com/bernard/nodecames/model/GridConfig.java @@ -0,0 +1,54 @@ +package com.bernard.nodecames.model; + +import java.util.List; +import java.util.Set; + +import com.bernard.nodecames.model.Card.Color; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Value; + +@Getter +@AllArgsConstructor +public class GridConfig { + + // Immutable options + // Maximum of maximom number of hints + // This is to trigger end of game + private int maxMaxHint;//TODO + private boolean structuredGrid;//TODO + private boolean anyoneStarts; + private List wordDistribution; + + // Mutable options + private int maxHint;//TODO + private Set availableHintWordCount;//TODO + private boolean oneMoreGuess;//TODO + + private int hintMaxLength;//TODO + private boolean endGuessing;//TODO + private boolean onlyOneGreenGreen;//TODO + private boolean suddenDeath;//TODO + private boolean hintsInARow;//TODO + + public int getWordCount() { + return wordDistribution.size(); + } + + @Value(staticConstructor = "of") + public static final class CardColors { + Color faceA; + Color faceB; + + public static final CardColors WW = CardColors.of(Color.WHITE, Color.WHITE); + public static final CardColors WB = CardColors.of(Color.WHITE, Color.BLACK); + public static final CardColors BW = CardColors.of(Color.BLACK, Color.WHITE); + public static final CardColors WG = CardColors.of(Color.WHITE, Color.GREEN); + public static final CardColors GW = CardColors.of(Color.GREEN, Color.WHITE); + public static final CardColors GG = CardColors.of(Color.GREEN, Color.GREEN); + public static final CardColors GB = CardColors.of(Color.GREEN, Color.BLACK); + public static final CardColors BG = CardColors.of(Color.BLACK, Color.GREEN); + public static final CardColors BB = CardColors.of(Color.BLACK, Color.BLACK); + } +}