feat: counting used tools to sort

This commit is contained in:
Julien Calixte
2025-08-09 17:12:32 +02:00
parent 9350bc7bb2
commit 23cc172f77
9 changed files with 210 additions and 75 deletions

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { useBoardGameStore } from '@/modules/5s/board-game-store'
import { toDuration, toSeconds } from '@/modules/5s/utils'
import { getNatural } from '@/utils'
import { ref } from 'vue'
const boardGameStore = useBoardGameStore()
const duration = ref<string | null>(null)
setInterval(() => {
duration.value = boardGameStore.meta.start
? toDuration(
new Date(boardGameStore.meta.start),
boardGameStore.meta.end ? new Date(boardGameStore.meta.end) : new Date()
)
: null
}, 1000)
</script>
<template>
<div class="board-game-performance">
<p class="numeric">{{ duration }}</p>
<template v-if="boardGameStore.meta.perfs.length > 0">
<h3>Last performances</h3>
<table class="table">
<thead>
<tr>
<th>Round</th>
<th>Duration</th>
<th>Board Games</th>
<th>Time / board game</th>
</tr>
</thead>
<tbody>
<tr v-for="(perf, index) in boardGameStore.meta.perfs">
<td>{{ index + 1 }}</td>
<td class="numeric">
{{ toDuration(new Date(perf.start), new Date(perf.end)) }}
</td>
<td class="numeric">
{{ perf.totalGames }}
</td>
<td class="numeric">
{{
getNatural(
toSeconds(new Date(perf.start), new Date(perf.end)),
perf.totalGames
)
}}s
</td>
</tr>
</tbody>
</table>
{{ boardGameStore.countUsedTools }}
</template>
</div>
</template>
<style scoped lang="scss">
.board-game-performance {
.numeric {
text-align: right;
}
}
</style>

View File

@@ -4,14 +4,36 @@ import { shuffleArray } from '@/utils'
import { computed } from 'vue' import { computed } from 'vue'
const boardGameStore = useBoardGameStore() const boardGameStore = useBoardGameStore()
const isSeiriActivated = computed(() => boardGameStore.sUsed.includes('seiri'))
const isSeitonActivated = computed(() => const isSeitonActivated = computed(() =>
boardGameStore.sUsed.includes('seiton') boardGameStore.sUsed.includes('seiton')
) )
// const isSeisoActivated = computed(() => boardGameStore.sUsed.includes('seiso'))
const rawTools = computed(() => const neededTools = computed(
shuffleArray( () =>
boardGameStore.tools.map((t) => `${t.name} (ref: ${t.reference})`) new Set(
).join(', ') boardGameStore.boardGames
.flatMap((g) => g.parts)
.flatMap((p) => p.tasks)
.flatMap((t) => t.tools)
.map((t) => t.id)
)
)
const tools = computed(() => {
if (isSeiriActivated.value) {
return boardGameStore.tools.filter((t) => neededTools.value.has(t.id))
} else {
return boardGameStore.tools
}
})
const toolsToDisplay = computed(() =>
shuffleArray(tools.value.map((t) => `${t.name} (ref: ${t.reference})`)).join(
', '
)
) )
</script> </script>
@@ -23,18 +45,20 @@ const rawTools = computed(() =>
<tr> <tr>
<th>Tool</th> <th>Tool</th>
<th>Reference</th> <th>Reference</th>
<th>Used</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="tool in boardGameStore.tools" :key="tool.reference"> <tr v-for="tool in tools" :key="tool.reference">
<td>{{ tool.name }}</td> <td>{{ tool.name }}</td>
<td>{{ tool.reference }}</td> <td class="numeric">{{ tool.reference }}</td>
<td>{{ boardGameStore.countUsedTools[tool.id] }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div v-else> <div v-else>
{{ rawTools }} {{ toolsToDisplay }}
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,30 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBoardGameStore } from '@/modules/5s/board-game-store' import { useBoardGameStore } from '@/modules/5s/board-game-store'
import BoardGamePerformance from '@/modules/5s/BoardGamePerformance.vue'
import BoardGameToolbox from '@/modules/5s/BoardGameToolbox.vue' import BoardGameToolbox from '@/modules/5s/BoardGameToolbox.vue'
import { _5S, is5S } from '@/modules/5s/types/5s' import { _5S, is5S } from '@/modules/5s/types/5s'
import { toDuration } from '@/modules/5s/utils'
import { onMounted, ref, toValue } from 'vue' import { onMounted, ref, toValue } from 'vue'
const userInput = ref('') const userInput = ref('')
const mode = ref<_5S | null>(null) const mode = ref<_5S | null>(null)
const boardGameStore = useBoardGameStore() const boardGameStore = useBoardGameStore()
const duration = ref<string | null>(null)
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
onMounted(() => { onMounted(() => {
boardGameStore.initGame() boardGameStore.initGame()
}) })
} }
setInterval(() => {
duration.value = boardGameStore.meta.start
? toDuration(
new Date(boardGameStore.meta.start),
boardGameStore.meta.end ? new Date(boardGameStore.meta.end) : new Date()
)
: null
}, 1000)
const submit = () => { const submit = () => {
const lastInput = toValue(userInput) const lastInput = toValue(userInput)
userInput.value = '' userInput.value = ''
@@ -49,6 +38,11 @@ const submit = () => {
boardGameStore.activateS(command) boardGameStore.activateS(command)
return return
} }
// d for debug
if (command === 'd') {
boardGameStore.increment()
}
} }
</script> </script>
@@ -151,18 +145,7 @@ const submit = () => {
</div> </div>
<aside class="performance prose"> <aside class="performance prose">
<h2>Performance</h2> <h2>Performance</h2>
<BoardGamePerformance />
<p class="duration numeric">{{ duration }}</p>
<template v-if="boardGameStore.meta.perfs.length > 0">
<h3>Last performances</h3>
<ul>
<li v-for="[start, end] in boardGameStore.meta.perfs">
{{ toDuration(new Date(start), new Date(end)) }}
</li>
</ul>
</template>
</aside> </aside>
</div> </div>
</template> </template>
@@ -171,12 +154,6 @@ const submit = () => {
@import url('https://fonts.googleapis.com/css2?family=Google+Sans+Code&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Google+Sans+Code&display=swap');
.board-game-workshop { .board-game-workshop {
flex: 1;
font-family: 'Google Sans Code', monospace;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
font-size: 14px;
display: flex; display: flex;
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
@@ -218,7 +195,7 @@ aside,
margin: 1rem; margin: 1rem;
} }
.duration { .card-title {
text-align: right; justify-content: center;
} }
</style> </style>

View File

@@ -3,7 +3,7 @@ import { boardGames } from '@/modules/5s/types/board-games'
import { tools } from '@/modules/5s/types/tools' import { tools } from '@/modules/5s/types/tools'
import { BoardGame, Part, Task, Tool } from '@/modules/5s/types/workshop' import { BoardGame, Part, Task, Tool } from '@/modules/5s/types/workshop'
import { toDuration } from '@/modules/5s/utils' import { toDuration } from '@/modules/5s/utils'
import { randomAlias } from '@/utils' import { accumulate, randomAlias } from '@/utils'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
type State = { type State = {
@@ -17,14 +17,20 @@ type State = {
meta: { meta: {
start: string | null start: string | null
end: string | null end: string | null
perfs: Array<[string, string]> perfs: Array<{
start: string
end: string
boardGames: BoardGame[]
countGames: Record<string, number>
totalGames: number
}>
} }
} }
const firstDemands = [ const firstDemands = [
boardGames[0],
boardGames[0] boardGames[0]
// boardGames[0], // boardGames[0],
// boardGames[0],
// boardGames[0] // boardGames[0]
] ]
@@ -113,7 +119,17 @@ export const useBoardGameStore = defineStore('board-game', {
// All board games complete // All board games complete
this.meta.end = new Date().toISOString() this.meta.end = new Date().toISOString()
this.meta.perfs = [...this.meta.perfs, [this.meta.start, this.meta.end]] const countGames = accumulate(this.boardGames.map((b) => b.name))
this.meta.perfs = [
...this.meta.perfs,
{
start: this.meta.start,
end: this.meta.end,
boardGames: [...this.boardGames],
countGames,
totalGames: this.boardGames.length
}
]
this.currentBoardGameIndex = null this.currentBoardGameIndex = null
this.currentPartIndex = null this.currentPartIndex = null
this.currentTaskIndex = null this.currentTaskIndex = null
@@ -162,6 +178,50 @@ export const useBoardGameStore = defineStore('board-game', {
} }
return toDuration(new Date(this.meta.start), new Date(this.meta.end)) return toDuration(new Date(this.meta.start), new Date(this.meta.end))
},
countUsedTools(): Record<string, number> {
const metaToolIds = this.meta.perfs
.flatMap((p) => p.boardGames)
.flatMap((b) => b.parts)
.flatMap((p) => p.tasks)
.flatMap((t) => t.tools)
.map((t) => t.id)
if (
!this.meta.start ||
!this.currentTask ||
!this.currentPart ||
!this.currentBoardGame ||
this.currentTaskIndex === null ||
this.currentPartIndex === null ||
this.currentBoardGameIndex === null
) {
return accumulate(metaToolIds)
}
const currentBoardGameIndex = this.currentBoardGameIndex
const currentPartIndex = this.currentPartIndex
const currentTaskIndex = this.currentTaskIndex
const toolIds = this.boardGames
.filter((_, i) => i <= currentBoardGameIndex)
.flatMap((b, boardIndex) =>
boardIndex === currentBoardGameIndex
? b.parts.filter((_, i) => i <= currentPartIndex)
: b.parts
)
.flatMap((p, partIndex) =>
partIndex === currentPartIndex
? p.tasks.filter((_, i) => i <= currentTaskIndex)
: p.tasks
)
.flatMap((t, taskIndex) =>
taskIndex === currentTaskIndex
? this.usedTools
: t.tools.map((t) => t.id)
)
return accumulate([...metaToolIds, ...toolIds])
} }
} }
}) })

View File

@@ -10,7 +10,6 @@ import { NonEmptyArray } from '@/modules/5s/types/tools'
export type Tool = { export type Tool = {
name: string name: string
alias: string alias: string
cooldown: number
} }
export type Task = { export type Task = {
@@ -29,14 +28,14 @@ export type BoardGame = {
} }
export const tools: Tool[] = [ export const tools: Tool[] = [
{ name: 'Card Printer', alias: 'card-printer', cooldown: 5 }, { name: 'Card Printer', alias: 'card-printer' },
{ name: 'Miniature Mold', alias: 'mini-mold', cooldown: 15 }, { name: 'Miniature Mold', alias: 'mini-mold' },
{ name: 'Dice Engraver', alias: 'dice-engraver', cooldown: 10 }, { name: 'Dice Engraver', alias: 'dice-engraver' },
{ name: 'Board Cutter', alias: 'board-cutter', cooldown: 8 }, { name: 'Board Cutter', alias: 'board-cutter' },
{ name: 'Rulebook Designer', alias: 'rulebook-dzn', cooldown: 6 }, { name: 'Rulebook Designer', alias: 'rulebook-dzn' },
{ name: 'Box Assembler', alias: 'box-asm', cooldown: 4 }, { name: 'Box Assembler', alias: 'box-asm' },
{ name: 'Component Painter', alias: 'painter', cooldown: 12 }, { name: 'Component Painter', alias: 'painter' },
{ name: 'Lamination Machine', alias: 'laminator', cooldown: 7 } { name: 'Lamination Machine', alias: 'laminator' }
] ]
export type NonEmptyArray<T> = [T, ...T[]] export type NonEmptyArray<T> = [T, ...T[]]

View File

@@ -1,53 +1,47 @@
import { Tool } from '@/modules/5s/types/workshop' import { Tool } from '@/modules/5s/types/workshop'
export const tools: Tool[] = [ export const tools: Tool[] = [
{ name: 'Card Printer', id: 'card-printer', reference: '', cooldown: 5 }, { name: 'Card Printer', id: 'card-printer', reference: '' },
{ name: 'Miniature Mold', id: 'mini-mold', reference: '', cooldown: 15 }, { name: 'Miniature Mold', id: 'mini-mold', reference: '' },
{ name: 'Dice Engraver', id: 'dice-engraver', reference: '', cooldown: 10 }, { name: 'Dice Engraver', id: 'dice-engraver', reference: '' },
{ name: 'Cutter', id: 'cutter', reference: '', cooldown: 8 }, { name: 'Cutter', id: 'cutter', reference: '' },
{ name: 'Rulebook Designer', id: 'rulebook-dzn', reference: '', cooldown: 6 }, { name: 'Rulebook Designer', id: 'rulebook-dzn', reference: '' },
{ name: 'Box Assembler', id: 'box-asm', reference: '', cooldown: 4 }, { name: 'Box Assembler', id: 'box-asm', reference: '' },
{ name: 'Component Painter', id: 'painter', reference: '', cooldown: 12 }, { name: 'Component Painter', id: 'painter', reference: '' },
{ name: 'Lamination Machine', id: 'laminator', reference: '', cooldown: 7 }, { name: 'Lamination Machine', id: 'laminator', reference: '' },
// Additional realistic tools // Additional realistic tools
{ {
name: 'Shrink Wrap Machine', name: 'Shrink Wrap Machine',
id: 'shrink-wrap', id: 'shrink-wrap',
reference: '', reference: ''
cooldown: 9
}, },
{ {
name: 'Punch Board Cutter', name: 'Punch Board Cutter',
id: 'punch-cutter', id: 'punch-cutter',
reference: '', reference: ''
cooldown: 11
}, },
{ {
name: 'Sticker Applicator', name: 'Sticker Applicator',
id: 'sticker-applicator', id: 'sticker-applicator',
reference: '', reference: ''
cooldown: 6
}, },
{ {
name: 'Foil Stamping Press', name: 'Foil Stamping Press',
id: 'foil-stamp', id: 'foil-stamp',
reference: '', reference: ''
cooldown: 13
}, },
{ {
name: 'Scanner', name: 'Scanner',
id: 'scanner', id: 'scanner',
reference: '', reference: ''
cooldown: 5
}, },
{ {
name: 'Instruction Sheet Folder', name: 'Instruction Sheet Folder',
id: 'sheet-folder', id: 'sheet-folder',
reference: '', reference: ''
cooldown: 7
}, },
{ name: 'Plastic Bag Sealer', id: 'bag-sealer', reference: '', cooldown: 4 }, { name: 'Plastic Bag Sealer', id: 'bag-sealer', reference: '' },
{ name: 'Barcode Printer', id: 'barcode-printer', reference: '', cooldown: 8 } { name: 'Barcode Printer', id: 'barcode-printer', reference: '' }
] ]
export type NonEmptyArray<T> = [T, ...T[]] export type NonEmptyArray<T> = [T, ...T[]]

View File

@@ -4,7 +4,6 @@ export type Tool = {
name: string name: string
id: string id: string
reference: string reference: string
cooldown: number
} }
export type Task = { export type Task = {

View File

@@ -22,3 +22,7 @@ export const toDuration = (startDate: Date, endDate: Date = new Date()) => {
return parts.join(' ') return parts.join(' ')
} }
export const toSeconds = (start: Date, endDate: Date): number => {
return Math.floor((endDate.getTime() - start.getTime()) / 1000)
}

View File

@@ -21,6 +21,9 @@ export const getMean = (data: number[]) =>
export const getRound = (data: number, total: number) => export const getRound = (data: number, total: number) =>
(Math.round(100 * (data / total)) / 100 || 0).toFixed(2) (Math.round(100 * (data / total)) / 100 || 0).toFixed(2)
export const getNatural = (data: number, total: number) =>
(Math.round(100 * (data / total)) / 100 || 0).toFixed(0)
export const shuffleArray = <T>(array: T[]) => { export const shuffleArray = <T>(array: T[]) => {
let currentIndex = array.length, let currentIndex = array.length,
randomIndex randomIndex
@@ -37,6 +40,13 @@ export const shuffleArray = <T>(array: T[]) => {
return array return array
} }
export const accumulate = (array: string[]) => {
return array.reduce<Record<string, number>>((acc, toolId) => {
acc[toolId] = (acc[toolId] || 0) + 1
return acc
}, {})
}
export const popNElement = <T>(array: T[], numberOfElements: number) => { export const popNElement = <T>(array: T[], numberOfElements: number) => {
const poppedElements: T[] = [] const poppedElements: T[] = []