trigo / trigo-web /app /src /components /mcts /MCTSMoveNavigation.vue
k-l-lambda's picture
Update trigo-web with VS People multiplayer mode
15f353f
<template>
<div class="mcts-move-navigation">
<div class="nav-buttons">
<button @click="mctsStore.firstMove()" :disabled="isFirst" class="nav-btn">⏮ First</button>
<button @click="mctsStore.prevMove()" :disabled="isFirst" class="nav-btn">◀ Prev</button>
<button @click="mctsStore.nextMove()" :disabled="isLast" class="nav-btn">Next ▶</button>
<button @click="mctsStore.lastMove()" :disabled="isLast" class="nav-btn">Last ⏭</button>
</div>
<div class="move-slider">
<input
type="range"
:min="0"
:max="maxMove"
:value="currentMove"
@input="onSliderChange"
class="slider"
/>
<span class="move-label">Move {{ currentMove + 1 }} / {{ maxMove + 1 }}</span>
</div>
<div class="move-info">
<span>Player: <strong>{{ currentPlayer }}</strong></span>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useMCTSStore } from "@/stores/mctsStore";
const mctsStore = useMCTSStore();
const currentMove = computed(() => mctsStore.currentMoveIndex);
const maxMove = computed(() => mctsStore.maxMoveIndex);
const isFirst = computed(() => currentMove.value === 0);
const isLast = computed(() => currentMove.value === maxMove.value);
const currentPlayer = computed(() => {
const moveData = mctsStore.currentMoveData;
return moveData ? moveData.player : "-";
});
function onSliderChange(event: Event) {
const target = event.target as HTMLInputElement;
mctsStore.setCurrentMoveIndex(parseInt(target.value, 10));
}
</script>
<style scoped lang="scss">
.mcts-move-navigation {
display: flex;
flex-direction: column;
gap: 15px;
}
.nav-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.nav-btn {
padding: 8px 12px;
background-color: #3a3a3a;
border: 1px solid #505050;
border-radius: 4px;
color: #e0e0e0;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
&:hover:not(:disabled) {
background-color: #4a4a4a;
border-color: #4a90e2;
}
&:active:not(:disabled) {
background-color: #2a2a2a;
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.move-slider {
display: flex;
flex-direction: column;
gap: 8px;
}
.slider {
width: 100%;
height: 6px;
border-radius: 3px;
background-color: #3a3a3a;
outline: none;
-webkit-appearance: none;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #4a90e2;
cursor: pointer;
&:hover {
background-color: #357abd;
}
}
&::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #4a90e2;
cursor: pointer;
border: none;
&:hover {
background-color: #357abd;
}
}
}
.move-label {
font-size: 13px;
color: #c0c0c0;
text-align: center;
}
.move-info {
padding: 8px;
background-color: #1a1a1a;
border-radius: 4px;
font-size: 12px;
color: #a0a0a0;
strong {
color: #e0e0e0;
text-transform: capitalize;
}
}
</style>