diff --git a/src/main/java/com/bernard/nodecames/HttpController.java b/src/main/java/com/bernard/nodecames/HttpController.java index d936bc5..96e2f27 100644 --- a/src/main/java/com/bernard/nodecames/HttpController.java +++ b/src/main/java/com/bernard/nodecames/HttpController.java @@ -1,15 +1,25 @@ package com.bernard.nodecames; +import java.util.Map.Entry; import java.util.Random; import java.util.UUID; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.view.RedirectView; +import com.bernard.nodecames.frontend.GridConfigEdit; import com.bernard.nodecames.game.GameManager; +import com.bernard.nodecames.game.IllegalGameActionException; +import com.bernard.nodecames.model.Grid; import com.bernard.nodecames.model.GridConfig; +import com.bernard.nodecames.model.GridConfig.MutableOption; import lombok.RequiredArgsConstructor; @@ -30,6 +40,30 @@ public class HttpController { return "grid"; } + @GetMapping("/room/{id}/config") + public String gridConfig(@PathVariable("id") String id, Model model) { + GridConfig gc = gm.findGrid(id).getConfig(); + model.addAttribute("gridConfig", GridConfigEdit.of(gc)); + return "grid-config"; + } + @PostMapping("/room/{id}/config") + public String setGridConfig(@PathVariable("id") String id, + @ModelAttribute("gridConfig") GridConfigEdit gce, + BindingResult br, + Model model + ) { + Grid g = gm.findGrid(id); + for(Entry e : gce.getModifications(g.getConfig()).entrySet()) { + try { + gm.unlock(g, e.getKey(), e.getValue()); + } catch (IllegalGameActionException ige) { + br.addError(new ObjectError(e.getKey().name(), ige.getMessage())); + } + } + model.addAttribute("contactForm", gce); + return "grid-config"; + } + @GetMapping("/create-room") diff --git a/src/main/java/com/bernard/nodecames/frontend/GridConfigEdit.java b/src/main/java/com/bernard/nodecames/frontend/GridConfigEdit.java new file mode 100644 index 0000000..2f322fc --- /dev/null +++ b/src/main/java/com/bernard/nodecames/frontend/GridConfigEdit.java @@ -0,0 +1,80 @@ +package com.bernard.nodecames.frontend; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.bernard.nodecames.model.GridConfig; +import com.bernard.nodecames.model.GridConfig.MutableOption; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class GridConfigEdit { + + private int maxHint; + private String availableHintWordCountStr; // Comma separated integers + private boolean oneMoreGuess; + private int hintMaxLength; + private boolean endGuessing; + private boolean onlyOneGreenGreen; + private boolean suddenDeath; + private boolean hintsInARow; + + public static GridConfigEdit of(GridConfig gc) { + return new GridConfigEdit( + gc.getMaxHint(), + gc.getAvailableHintWordCount() + .stream() + .sorted() + .map(Object::toString) + .collect(Collectors.joining(",")), + gc.isOneMoreGuess(), + gc.getHintMaxLength(), + gc.isEndGuessing(), + gc.isOnlyOneGreenGreen(), + gc.isSuddenDeath(), + gc.isHintsInARow() + ); + } + + public Set getAvailableHintWordCount() { + return Stream.of(this.getAvailableHintWordCountStr().split(",")) + .map(Integer::parseInt) + .collect(Collectors.toSet()); + } + + public Map getMutableOptions() { + return Map.of( + MutableOption.MAX_HINT, this.getMaxHint(), + MutableOption.AVAILABLE_HINT_WORD_COUNT, this.getAvailableHintWordCount(), + MutableOption.ONE_MORE_GUESS, this.isOneMoreGuess(), + MutableOption.HINT_MAX_LENGTH, this.getHintMaxLength(), + MutableOption.END_GUESSING, this.isEndGuessing(), + MutableOption.ONLY_ONE_GREEN_GREEN, this.isOnlyOneGreenGreen(), + MutableOption.SUDDEN_DEATH, this.isSuddenDeath(), + MutableOption.HINTS_IN_A_ROW, this.isHintsInARow() + ); + } + + /** + * Returns the list of modified in this edit compared to the GridConfig + */ + public Map getModifications(GridConfig orig) { + Map out = new EnumMap<>(MutableOption.class); + Map selfOpts = this.getMutableOptions(); + for(Entry e : orig.getMutableOptions().entrySet()) { + Object newValue = selfOpts.get(e.getKey()); + if(!newValue.equals(e.getValue())) { + out.put(e.getKey(), newValue); + } + } + return out; + } + +} diff --git a/src/main/java/com/bernard/nodecames/game/GameManager.java b/src/main/java/com/bernard/nodecames/game/GameManager.java index bf9b6d1..dc66c27 100644 --- a/src/main/java/com/bernard/nodecames/game/GameManager.java +++ b/src/main/java/com/bernard/nodecames/game/GameManager.java @@ -305,10 +305,43 @@ public class GameManager { /** * UPDATE GAME CONFIG */ + public void unlock(Grid g, MutableOption opt, Object o) throws IllegalGameActionException { + switch(opt) { + case MutableOption.MAX_HINT: + unlockHintCount(g, (Integer)o); + break; + case MutableOption.AVAILABLE_HINT_WORD_COUNT: + unlockNewWordCounts(g, (Set)o); + break; + case MutableOption.ONE_MORE_GUESS: + unlockOneMoreGuess(g); + break; + case MutableOption.HINT_MAX_LENGTH: + unlockHintMaxLength(g, (Integer)o); + break; + case MutableOption.END_GUESSING: + unlockEndGuessing(g); + break; + case MutableOption.ONLY_ONE_GREEN_GREEN: + unlockOnlyOneGreenGreen(g); + break; + case MutableOption.SUDDEN_DEATH: + unlockSuddenDeath(g); + break; + case MutableOption.HINTS_IN_A_ROW: + unlockHintsInARow(g); + break; + default: + throw new IllegalStateException("Unknown MutableOption to edit"); + } + } public void unlockHintCount(Grid g, int newHintCount) throws IllegalGameActionException { - if(newHintCount > g.getConfig().getMaxMaxHint() || newHintCount <= g.getConfig().getMaxHint()) { + if(newHintCount > g.getConfig().getMaxMaxHint()) { throw new IllegalGameActionException(g, "Illegal hint count"); } + if(newHintCount <= g.getConfig().getMaxHint()) { + throw new IllegalGameActionException(g, "Cannot lower hint count"); + } changeConfig(g, MutableOption.MAX_HINT, newHintCount); if(g.getPhase().isNeedMoreHints() || g.getPhase().isNeedMoreHintsOrSuddenDeath()) { setPhase(g, Phase.hinting(g.getPhase().getHinting())); @@ -326,7 +359,7 @@ public class GameManager { public void unlockOneMoreGuess(Grid g) throws IllegalGameActionException { if(g.getConfig().isOneMoreGuess()) - throw new IllegalGameActionException(g, "Cannot set value, already set"); + throw new IllegalGameActionException(g, "Cannot unlock oneMoreGuess as it is already unlocked"); changeConfig(g, MutableOption.ONE_MORE_GUESS, true); if(g.getPhase().isWaitingForOneMore()) { setPhase(g, Phase.guessing(g.getPhase().getGuessing())); @@ -342,13 +375,13 @@ public class GameManager { public void unlockEndGuessing(Grid g) throws IllegalGameActionException { if(g.getConfig().isEndGuessing()) - throw new IllegalGameActionException(g, "Cannot set value, already set"); + throw new IllegalGameActionException(g, "Cannot unlock endGuessing as it is already unlocked"); changeConfig(g, MutableOption.END_GUESSING, true); } public void unlockOnlyOneGreenGreen(Grid g) throws IllegalGameActionException { if(g.getConfig().isOnlyOneGreenGreen()) - throw new IllegalGameActionException(g, "Cannot set value, already set"); + throw new IllegalGameActionException(g, "Cannot unlock onlyOneGreenGreen as it is already unlocked"); changeConfig(g, MutableOption.ONLY_ONE_GREEN_GREEN, true); if(g.getPhase() == Phase.WIN_IF_GREEN_GREEN) { endGame(g, true); @@ -357,7 +390,7 @@ public class GameManager { public void unlockSuddenDeath(Grid g) throws IllegalGameActionException { if(g.getConfig().isSuddenDeath()) - throw new IllegalGameActionException(g, "Cannot set value, already set"); + throw new IllegalGameActionException(g, "Cannot unlock suddenDeath as it is already unlocked"); changeConfig(g, MutableOption.SUDDEN_DEATH,true); if (g.getPhase().isNeedMoreHintsOrSuddenDeath()) { setPhase(g, Phase.needMoreHints(g.getPhase().getHinting())); @@ -369,7 +402,7 @@ public class GameManager { public void unlockHintsInARow(Grid g) throws IllegalGameActionException { if(g.getConfig().isHintsInARow()) - throw new IllegalGameActionException(g, "Cannot set value, already set"); + throw new IllegalGameActionException(g, "Cannot unlock hintsInARow as it is already unlocked"); changeConfig(g, MutableOption.HINTS_IN_A_ROW,true); if ((g.getPhase() == Phase.HINTING_A || g.getPhase() == Phase.HINTING_B) && !allGreenRevealed(g, 'A', g.getConfig().isOnlyOneGreenGreen()) diff --git a/src/main/java/com/bernard/nodecames/model/GridConfig.java b/src/main/java/com/bernard/nodecames/model/GridConfig.java index 68a6341..dbf946b 100644 --- a/src/main/java/com/bernard/nodecames/model/GridConfig.java +++ b/src/main/java/com/bernard/nodecames/model/GridConfig.java @@ -1,17 +1,5 @@ package com.bernard.nodecames.model; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.bernard.nodecames.model.Card.Color; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; -import lombok.Value; -import lombok.With; - 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; @@ -22,6 +10,17 @@ 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.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.bernard.nodecames.model.Card.Color; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Value; + @Getter @AllArgsConstructor public class GridConfig { @@ -107,6 +106,23 @@ public class GridConfig { } } + public Map getMutableOptions() { + return Map.of( + MutableOption.MAX_HINT, this.getMaxHint(), + MutableOption.AVAILABLE_HINT_WORD_COUNT, this.getAvailableHintWordCount(), + MutableOption.ONE_MORE_GUESS, this.isOneMoreGuess(), + MutableOption.HINT_MAX_LENGTH, this.getHintMaxLength(), + MutableOption.END_GUESSING, this.isEndGuessing(), + MutableOption.ONLY_ONE_GREEN_GREEN, this.isOnlyOneGreenGreen(), + MutableOption.SUDDEN_DEATH, this.isSuddenDeath(), + MutableOption.HINTS_IN_A_ROW, this.isHintsInARow() + ); + } + + public static final boolean isGenericallyValidHintWordCount(int i) { + return 0 <= i || i == -1; + } + @Value(staticConstructor = "of") public static final class CardColors { Color faceA; diff --git a/src/main/resources/static/js/grid.js b/src/main/resources/static/js/grid.js index 65242d3..691048b 100644 --- a/src/main/resources/static/js/grid.js +++ b/src/main/resources/static/js/grid.js @@ -300,7 +300,7 @@ function onUpdateCard(m) { function onConfigChange(m) { data = JSON.parse(m.body) - for(const [key, value] of data.entries()) { + for(const [key, value] of Object.entries(data)) { console.log("New config option : ", key, "=", value) gc[key] = value switch(key) { @@ -320,8 +320,6 @@ function onConfigChange(m) { break; } } - cards[i] = data - updateCard(i) } function initialize() { diff --git a/src/main/resources/templates/grid-config.html b/src/main/resources/templates/grid-config.html new file mode 100644 index 0000000..cfbbdb0 --- /dev/null +++ b/src/main/resources/templates/grid-config.html @@ -0,0 +1,38 @@ + + + +
+ + + + + + + +
+
+
    +
  • error
  • +
+ +
+
+ + \ No newline at end of file