Added a config page
This commit is contained in:
@@ -1,15 +1,25 @@
|
|||||||
package com.bernard.nodecames;
|
package com.bernard.nodecames;
|
||||||
|
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
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.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import com.bernard.nodecames.frontend.GridConfigEdit;
|
||||||
import com.bernard.nodecames.game.GameManager;
|
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;
|
||||||
|
import com.bernard.nodecames.model.GridConfig.MutableOption;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
@@ -30,6 +40,30 @@ public class HttpController {
|
|||||||
return "grid";
|
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<MutableOption, Object> 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")
|
@GetMapping("/create-room")
|
||||||
|
|||||||
@@ -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<Integer> getAvailableHintWordCount() {
|
||||||
|
return Stream.of(this.getAvailableHintWordCountStr().split(","))
|
||||||
|
.map(Integer::parseInt)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<MutableOption, Object> 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<MutableOption, Object> getModifications(GridConfig orig) {
|
||||||
|
Map<MutableOption, Object> out = new EnumMap<>(MutableOption.class);
|
||||||
|
Map<MutableOption, Object> selfOpts = this.getMutableOptions();
|
||||||
|
for(Entry<MutableOption, Object> e : orig.getMutableOptions().entrySet()) {
|
||||||
|
Object newValue = selfOpts.get(e.getKey());
|
||||||
|
if(!newValue.equals(e.getValue())) {
|
||||||
|
out.put(e.getKey(), newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -305,10 +305,43 @@ public class GameManager {
|
|||||||
/**
|
/**
|
||||||
* UPDATE GAME CONFIG
|
* 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<Integer>)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 {
|
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");
|
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);
|
changeConfig(g, MutableOption.MAX_HINT, newHintCount);
|
||||||
if(g.getPhase().isNeedMoreHints() || g.getPhase().isNeedMoreHintsOrSuddenDeath()) {
|
if(g.getPhase().isNeedMoreHints() || g.getPhase().isNeedMoreHintsOrSuddenDeath()) {
|
||||||
setPhase(g, Phase.hinting(g.getPhase().getHinting()));
|
setPhase(g, Phase.hinting(g.getPhase().getHinting()));
|
||||||
@@ -326,7 +359,7 @@ public class GameManager {
|
|||||||
|
|
||||||
public void unlockOneMoreGuess(Grid g) throws IllegalGameActionException {
|
public void unlockOneMoreGuess(Grid g) throws IllegalGameActionException {
|
||||||
if(g.getConfig().isOneMoreGuess())
|
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);
|
changeConfig(g, MutableOption.ONE_MORE_GUESS, true);
|
||||||
if(g.getPhase().isWaitingForOneMore()) {
|
if(g.getPhase().isWaitingForOneMore()) {
|
||||||
setPhase(g, Phase.guessing(g.getPhase().getGuessing()));
|
setPhase(g, Phase.guessing(g.getPhase().getGuessing()));
|
||||||
@@ -342,13 +375,13 @@ public class GameManager {
|
|||||||
|
|
||||||
public void unlockEndGuessing(Grid g) throws IllegalGameActionException {
|
public void unlockEndGuessing(Grid g) throws IllegalGameActionException {
|
||||||
if(g.getConfig().isEndGuessing())
|
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);
|
changeConfig(g, MutableOption.END_GUESSING, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlockOnlyOneGreenGreen(Grid g) throws IllegalGameActionException {
|
public void unlockOnlyOneGreenGreen(Grid g) throws IllegalGameActionException {
|
||||||
if(g.getConfig().isOnlyOneGreenGreen())
|
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);
|
changeConfig(g, MutableOption.ONLY_ONE_GREEN_GREEN, true);
|
||||||
if(g.getPhase() == Phase.WIN_IF_GREEN_GREEN) {
|
if(g.getPhase() == Phase.WIN_IF_GREEN_GREEN) {
|
||||||
endGame(g, true);
|
endGame(g, true);
|
||||||
@@ -357,7 +390,7 @@ public class GameManager {
|
|||||||
|
|
||||||
public void unlockSuddenDeath(Grid g) throws IllegalGameActionException {
|
public void unlockSuddenDeath(Grid g) throws IllegalGameActionException {
|
||||||
if(g.getConfig().isSuddenDeath())
|
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);
|
changeConfig(g, MutableOption.SUDDEN_DEATH,true);
|
||||||
if (g.getPhase().isNeedMoreHintsOrSuddenDeath()) {
|
if (g.getPhase().isNeedMoreHintsOrSuddenDeath()) {
|
||||||
setPhase(g, Phase.needMoreHints(g.getPhase().getHinting()));
|
setPhase(g, Phase.needMoreHints(g.getPhase().getHinting()));
|
||||||
@@ -369,7 +402,7 @@ public class GameManager {
|
|||||||
|
|
||||||
public void unlockHintsInARow(Grid g) throws IllegalGameActionException {
|
public void unlockHintsInARow(Grid g) throws IllegalGameActionException {
|
||||||
if(g.getConfig().isHintsInARow())
|
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);
|
changeConfig(g, MutableOption.HINTS_IN_A_ROW,true);
|
||||||
if ((g.getPhase() == Phase.HINTING_A || g.getPhase() == Phase.HINTING_B)
|
if ((g.getPhase() == Phase.HINTING_A || g.getPhase() == Phase.HINTING_B)
|
||||||
&& !allGreenRevealed(g, 'A', g.getConfig().isOnlyOneGreenGreen())
|
&& !allGreenRevealed(g, 'A', g.getConfig().isOnlyOneGreenGreen())
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
package com.bernard.nodecames.model;
|
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.BB;
|
||||||
import static com.bernard.nodecames.model.GridConfig.CardColors.BG;
|
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.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.WG;
|
||||||
import static com.bernard.nodecames.model.GridConfig.CardColors.WW;
|
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
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class GridConfig {
|
public class GridConfig {
|
||||||
@@ -107,6 +106,23 @@ public class GridConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<MutableOption, Object> 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")
|
@Value(staticConstructor = "of")
|
||||||
public static final class CardColors {
|
public static final class CardColors {
|
||||||
Color faceA;
|
Color faceA;
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ function onUpdateCard(m) {
|
|||||||
|
|
||||||
function onConfigChange(m) {
|
function onConfigChange(m) {
|
||||||
data = JSON.parse(m.body)
|
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)
|
console.log("New config option : ", key, "=", value)
|
||||||
gc[key] = value
|
gc[key] = value
|
||||||
switch(key) {
|
switch(key) {
|
||||||
@@ -320,8 +320,6 @@ function onConfigChange(m) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cards[i] = data
|
|
||||||
updateCard(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<div th:replace="~{html-head}"/>
|
||||||
|
<link rel="stylesheet" th:href="@{/css/grid.css}"/>
|
||||||
|
<script th:src="@{/js/jquery-4.0.0.min.js}" type="text/javascript"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.6.1/sockjs.min.js" integrity="sha512-1QvjE7BtotQjkq8PxLeF6P46gEpBRXuskzIVgjFpekzFVF4yjRgrQvTG1MTOJ3yQgvTteKAcO7DSZI92+u/yZw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js" integrity="sha512-iKDtgDyTHjAitUDdLljGhenhPwrbBfqTKWO1mkhSFH3A7blITC9MhYon6SjnMhp4o0rADGw9yAC6EW4t5a4K3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script th:src="@{/js/grid.js}" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<form method="POST"
|
||||||
|
action="" th:object="${gridConfig}">
|
||||||
|
<ul th:if="${#fields.hasErrors('global')}">
|
||||||
|
<li th:each="error : ${#fields.errors('global')}" th:text="${error}">error</li>
|
||||||
|
</ul>
|
||||||
|
<label th:field="maxHint" for="field-max-hint">Maximum number of hints</label>
|
||||||
|
<input th:field="*{maxHint}" id="field-max-hint"/> <br/>
|
||||||
|
<label th:field="availableHintWordCountStr" for="field-available-hint-word-count">Available hint's word counts<label>
|
||||||
|
<input th:field="*{availableHintWordCountStr}" id="field-available-hint-word-count"/> <br/>
|
||||||
|
<label th:field="oneMoreGuess" for="field-one-more-guess">Can do one more guess than hint word count</label>
|
||||||
|
<input type="checkbox" th:field="*{oneMoreGuess}" id="field-one-more-guess"/> <br/>
|
||||||
|
<label th:field="hintMaxLength" for="field-hint-max-length">Maximum length of hint words</label>
|
||||||
|
<input th:field="*{hintMaxLength}" id="field-hint-max-length"/> <br/>
|
||||||
|
<label th:field="endGuessing" for="field-end-guessing">Can press end guessing before the number of words</label>
|
||||||
|
<input type="checkbox" th:field="*{endGuessing}" id="field-end-guessing"/> <br/>
|
||||||
|
<label th:field="onlyOneGreenGreen" for="field-only-one-green-green">Green-Green cards only have to be pressed by one side</label>
|
||||||
|
<input type="checkbox" th:field="*{onlyOneGreenGreen}" id="field-only-one-green-green"/> <br/>
|
||||||
|
<label th:field="suddenDeath" for="field-sudden-death">Before losing beccause no more hints, goes to sudden death</label>
|
||||||
|
<input type="checkbox" th:field="*{suddenDeath}" id="field-sudden-death"/> <br/>
|
||||||
|
<label th:field="hintsInARow" for="field-hints-in-a-row">Someone can propose two hints in a row</label>
|
||||||
|
<input type="checkbox" th:field="*{hintsInARow}" id="field-hints-in-a-row"/> <br/>
|
||||||
|
<input type="submit" value="Change config" />
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user