Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ data class CreateBotGameRequest(
val startFen: String?,
val openingMode: OpeningMode = OpeningMode.BY_FREQUENCY,
val variant: Variant = Variant.XIANGQI,
val timeControlBase: Int? = null,
val timeControlIncrement: Int? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ class PlayerVsBotGameService(
throw BadRequestException("You must be authenticated in to play with depth greater than 6")
}

request.timeControlBase?.let { base ->
if (base <= 0) {
throw BadRequestException("Time control base must be positive")
}
}
request.timeControlIncrement?.let { increment ->
if (increment < 0) {
throw BadRequestException("Time control increment must be non-negative")
}
if (request.timeControlBase == null) {
throw BadRequestException("Time control increment requires a base time")
}
}

// Manchu variant requires Engine only opening mode
if (request.variant == Variant.MANCHU && request.openingMode != OpeningMode.ENGINE_ONLY) {
throw BadRequestException("Variants require engine-only opening mode")
Expand Down
132 changes: 130 additions & 2 deletions webapp/src/main/resources/modals/play-bot.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div id="play-bot-modal" class="modal-content" data-nosnippet>
<h2>Play Bot</h2>
<div class="new-game-main-container">
<h2 id="play-bot-modal-title">Play Bot</h2>
<div id="play-bot-main-panel" class="new-game-main-container">
<div class="container-with-margin">
<table class="new-game-option-table">
<tr>
Expand Down Expand Up @@ -68,6 +68,21 @@ <h2>Play Bot</h2>
</div>
</td>
</tr>
<tr>
<td class="icons-cells">
<div class="center-content-div">
<img src="/images/icons/shuttle.png"
class="time-control-icons"
alt="time control">
</div>
</td>
<td class="button-cells" colspan="2">
<div id="play-bot-time-control-button"
class="option-button-div">
<span id="play-bot-time-control-label">No time control</span>
</div>
</td>
</tr>
</table>
<div id="play-bot-depth-box">
<h3 class="add-asterisk">Depth</h3>
Expand Down Expand Up @@ -134,7 +149,120 @@ <h3>Starting Position</h3>
</div>
</div>
</div>
<div id="play-bot-time-control-panel" class="new-game-main-container" style="display: none;">
<div class="container-with-margin">
<div class="small-text-explanation">
Times indicated below represent <b>total game time</b> given to each player.
</div>
<table class="new-game-option-table">
<tr>
<td class="icons-cells">
<div class="center-content-div">
<img src="/images/icons/contrast-circle-symbol.png"
class="time-control-icons"
alt="no time control">
</div>
</td>
<td class="button-cells" colspan="2">
<div id="bot-tc-none"
class="option-button-div bot-tc-option-button-div option-button-div-selected"
data-tc-base=""
data-tc-increment=""
data-tc-label="No time control">
No time control
</div>
</td>
</tr>
<tr>
<td class="icons-cells">
<div class="center-content-div">
<img src="/images/icons/shuttle.png"
class="time-control-icons"
alt="bullet">
</div>
</td>
<td class="button-cells">
<div id="bot-tc-60"
class="option-button-div bot-tc-option-button-div"
data-tc-base="60"
data-tc-increment=""
data-tc-label="1m">
1m
</div>
</td>
<td class="button-cells">
<div id="bot-tc-120-1"
class="option-button-div bot-tc-option-button-div"
data-tc-base="120"
data-tc-increment="1"
data-tc-label="2m +1s">
2m +1s
</div>
</td>
</tr>
<tr>
<td class="icons-cells">
<div class="center-content-div">
<img src="/images/icons/flash.png"
class="time-control-icons blitz-time-control-icons"
alt="blitz">
</div>
</td>
<td class="button-cells">
<div id="bot-tc-180"
class="option-button-div bot-tc-option-button-div"
data-tc-base="180"
data-tc-increment=""
data-tc-label="3m">
3m
</div>
</td>
<td class="button-cells">
<div id="bot-tc-300"
class="option-button-div bot-tc-option-button-div"
data-tc-base="300"
data-tc-increment=""
data-tc-label="5m">
5m
</div>
</td>
</tr>
<tr>
<td class="icons-cells">
<div class="center-content-div">
<img src="/images/icons/run.png"
class="time-control-icons"
alt="rapid">
</div>
</td>
<td class="button-cells">
<div id="bot-tc-600"
class="option-button-div bot-tc-option-button-div"
data-tc-base="600"
data-tc-increment=""
data-tc-label="10m">
10m
</div>
</td>
<td class="button-cells">
<div id="bot-tc-1800"
class="option-button-div bot-tc-option-button-div"
data-tc-base="1800"
data-tc-increment=""
data-tc-label="30m">
30m
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="modal-buttons-panel">
<input id="play-bot-time-control-back-button"
type="button"
class="app-buttons"
value="back"
style="display: none;">
<input id="play-bot-button" type="button" class="app-buttons" value="play">
</div>
<div class="center-content-div">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ const BOT_OPTION_BUTTON_DISABLED_CLASS = 'option-button-div-disabled';
const BOT_VARIANT_OPTION_GROUP = 'bot-variant-option-button-div';
const BOT_COLOR_OPTION_GROUP = 'bot-color-option-button-div';
const BOT_ENGINE_OPTION_GROUP = 'bot-engine-option-button-div';
const BOT_TIME_CONTROL_OPTION_GROUP = 'bot-tc-option-button-div';

class PlayBotModalHandler extends ModalHandler {

#exclusionGroups = [BOT_VARIANT_OPTION_GROUP, BOT_COLOR_OPTION_GROUP, BOT_ENGINE_OPTION_GROUP];
#exclusionGroups = [
BOT_VARIANT_OPTION_GROUP,
BOT_COLOR_OPTION_GROUP,
BOT_ENGINE_OPTION_GROUP,
BOT_TIME_CONTROL_OPTION_GROUP,
];
#optionDivs;

#depthInput = document.getElementById('play-bot-depth');
Expand All @@ -45,6 +51,13 @@ class PlayBotModalHandler extends ModalHandler {

#playBotButton = document.getElementById('play-bot-button');

#mainPanel = document.getElementById('play-bot-main-panel');
#timeControlPanel = document.getElementById('play-bot-time-control-panel');
#timeControlButton = document.getElementById('play-bot-time-control-button');
#timeControlLabel = document.getElementById('play-bot-time-control-label');
#timeControlBackButton = document.getElementById('play-bot-time-control-back-button');
#modalTitle = document.getElementById('play-bot-modal-title');

constructor() {
super();
this.#optionDivs = getElementsByClassNameArray(BOT_OPTION_BUTTON_CLASS);
Expand Down Expand Up @@ -85,6 +98,21 @@ class PlayBotModalHandler extends ModalHandler {
this.#handleCreateGameClickEvent();
});

this.#timeControlButton.addEventListener('click', () => {
this.#showTimeControlPanel();
});
this.#timeControlBackButton.addEventListener('click', () => {
this.#showMainPanel();
});
this.#optionDivs
.filter(item => item.classList.contains(BOT_TIME_CONTROL_OPTION_GROUP))
.forEach(item => {
item.addEventListener('click', () => {
this.#updateTimeControlLabel();
this.#showMainPanel();
});
});

// Auto-detect Manchu variant from FEN: if piece section contains an 'M', select Manchu
this.#startFenInput.addEventListener('input', () => {
const piecePart = this.#startFenInput.value.split(' ')[0];
Expand Down Expand Up @@ -140,13 +168,16 @@ class PlayBotModalHandler extends ModalHandler {
validateStartFen(startFenValue);
}
this.#startFenInputSetValid(true);
const {base: timeControlBase, increment: timeControlIncrement} = this.#getSelectedTimeControl();
const body = {
'color': color,
'depth': depth,
'engine': engine,
'startFen': startFenValue,
'openingMode': openingMode,
'variant': variant,
'timeControlBase': timeControlBase,
'timeControlIncrement': timeControlIncrement,
};
postAndHandle('/api/botgame/create', body, json => {
window.open('/playbot?id=' + json.gameId, '_self');
Expand Down Expand Up @@ -283,4 +314,46 @@ class PlayBotModalHandler extends ModalHandler {
}
}

#showTimeControlPanel() {
this.#mainPanel.style.display = 'none';
this.#timeControlPanel.style.display = '';
this.#playBotButton.style.display = 'none';
this.#timeControlBackButton.style.display = '';
this.#modalTitle.innerText = 'Time control';
}

#showMainPanel() {
this.#mainPanel.style.display = '';
this.#timeControlPanel.style.display = 'none';
this.#playBotButton.style.display = '';
this.#timeControlBackButton.style.display = 'none';
this.#modalTitle.innerText = 'Play Bot';
}

#getSelectedTimeControlButton() {
return this.#optionDivs
.filter(item => item.classList.contains(BOT_TIME_CONTROL_OPTION_GROUP))
.find(item => item.classList.contains(BOT_OPTION_BUTTON_SELECTED_CLASS));
}

#updateTimeControlLabel() {
const selected = this.#getSelectedTimeControlButton();
if (selected != null) {
this.#timeControlLabel.innerText = selected.dataset.tcLabel;
}
}

/**
* @return {{base: number|null, increment: number|null}}
*/
#getSelectedTimeControl() {
const selected = this.#getSelectedTimeControlButton();
if (selected == null) {
return {base: null, increment: null};
}
const base = selected.dataset.tcBase === '' ? null : Number(selected.dataset.tcBase);
const increment = selected.dataset.tcIncrement === '' ? null : Number(selected.dataset.tcIncrement);
return {base, increment};
}

}
Loading