Spaces:
Sleeping
Sleeping
| <template> | |
| <div class="mcts-statistics-panel"> | |
| <div v-if="!hasData" class="no-data">No data</div> | |
| <div v-else class="stats-content"> | |
| <div class="stats-summary"> | |
| <div class="stat-item"> | |
| <span class="stat-label">Total visits:</span> | |
| <span class="stat-value">{{ totalVisits }}</span> | |
| </div> | |
| </div> | |
| <div class="stats-options"> | |
| <label class="option-label">Display:</label> | |
| <div class="radio-group"> | |
| <label class="radio-option"> | |
| <input | |
| type="radio" | |
| value="N" | |
| :checked="selectedStat === 'N'" | |
| @change="onStatChange" | |
| /> | |
| <span>Visit Count (N)</span> | |
| </label> | |
| <label class="radio-option"> | |
| <input | |
| type="radio" | |
| value="pi" | |
| :checked="selectedStat === 'pi'" | |
| @change="onStatChange" | |
| /> | |
| <span>Policy (π)</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="top-moves-table"> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>#</th> | |
| <th>Pos</th> | |
| <th>N</th> | |
| <th>%</th> | |
| <th>π</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr | |
| v-for="(move, idx) in topMoves" | |
| :key="move.actionKey" | |
| :class="{ selected: isSelected(move.actionKey) }" | |
| @click="onMoveClick(move.actionKey)" | |
| class="move-row" | |
| > | |
| <td>{{ idx + 1 }}</td> | |
| <td>{{ formatPosition(move.position) }}</td> | |
| <td>{{ move.N }}</td> | |
| <td>{{ ((move.N / totalVisits) * 100).toFixed(1) }}%</td> | |
| <td>{{ move.pi.toFixed(3) }}</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <script setup lang="ts"> | |
| import { computed } from "vue"; | |
| import { useMCTSStore } from "@/stores/mctsStore"; | |
| import { formatPosition } from "@/utils/mctsDataParser"; | |
| import type { MCTSStatisticType } from "@/types/mcts"; | |
| const mctsStore = useMCTSStore(); | |
| const hasData = computed(() => mctsStore.hasData); | |
| const totalVisits = computed(() => mctsStore.totalVisits); | |
| const topMoves = computed(() => mctsStore.topMoves); | |
| const selectedStat = computed(() => mctsStore.selectedStatistic); | |
| function isSelected(actionKey: string): boolean { | |
| return mctsStore.selectedActionKey === actionKey; | |
| } | |
| function onMoveClick(actionKey: string) { | |
| mctsStore.selectAction(actionKey); | |
| } | |
| function onStatChange(event: Event) { | |
| const target = event.target as HTMLInputElement; | |
| mctsStore.setSelectedStatistic(target.value as MCTSStatisticType); | |
| } | |
| </script> | |
| <style scoped lang="scss"> | |
| .mcts-statistics-panel { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .no-data { | |
| text-align: center; | |
| color: #606060; | |
| font-size: 12px; | |
| padding: 20px; | |
| } | |
| .stats-content { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| .stats-summary { | |
| padding: 10px; | |
| background-color: #1a1a1a; | |
| border-radius: 4px; | |
| } | |
| .stat-item { | |
| display: flex; | |
| justify-content: space-between; | |
| font-size: 13px; | |
| } | |
| .stat-label { | |
| color: #a0a0a0; | |
| } | |
| .stat-value { | |
| color: #e0e0e0; | |
| font-weight: 500; | |
| } | |
| .stats-options { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .option-label { | |
| font-size: 12px; | |
| color: #a0a0a0; | |
| font-weight: 500; | |
| } | |
| .radio-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .radio-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 12px; | |
| color: #c0c0c0; | |
| cursor: pointer; | |
| input[type="radio"] { | |
| cursor: pointer; | |
| } | |
| } | |
| .top-moves-table { | |
| max-height: 300px; | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 12px; | |
| thead { | |
| position: sticky; | |
| top: 0; | |
| background-color: #2a2a2a; | |
| z-index: 1; | |
| th { | |
| padding: 8px 6px; | |
| text-align: left; | |
| color: #4a90e2; | |
| font-weight: 600; | |
| border-bottom: 1px solid #404040; | |
| } | |
| } | |
| tbody { | |
| tr { | |
| cursor: pointer; | |
| transition: background-color 0.2s; | |
| &:hover { | |
| background-color: #3a3a3a; | |
| } | |
| &.selected { | |
| background-color: #4a3030; | |
| td { | |
| color: #e94560; | |
| font-weight: 500; | |
| } | |
| } | |
| td { | |
| padding: 8px 6px; | |
| color: #c0c0c0; | |
| border-bottom: 1px solid #303030; | |
| &:first-child { | |
| color: #808080; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </style> | |