221 lines
5.1 KiB
Vue
221 lines
5.1 KiB
Vue
<template>
|
|
<AppHeader />
|
|
|
|
<!-- Number to find -->
|
|
<PlaquetteBox
|
|
class="p-1 mx-auto mb-4 w-fit"
|
|
:class="{
|
|
'text-blue-400': difficultyLevel === 1,
|
|
'text-purple-500': difficultyLevel === 2,
|
|
}">
|
|
<span class="text-4xl">{{ result }}</span>
|
|
</PlaquetteBox>
|
|
|
|
<!-- Start button -->
|
|
<!-- TODO: fix animation -->
|
|
<Transition name="zero_height">
|
|
<div
|
|
class="text-center"
|
|
v-if="gameState === GameState.Waiting">
|
|
<div>
|
|
Combinez les nombres imposés afin de trouver le résultat ci-dessus, ou
|
|
de vous en approcher le plus possible.<br>
|
|
Le compteur démarre quand vous cliquez sur le bouton.<br>Partagez vos
|
|
meilleurs temps !
|
|
</div>
|
|
|
|
<button
|
|
@click="startGame"
|
|
class="py-2 px-4 mt-4 btn">
|
|
{{ t('startGame') }}
|
|
</button>
|
|
</div>
|
|
</Transition>
|
|
|
|
<!-- PLAQUETTES -->
|
|
<PlaquettesList
|
|
:plaquettes="shownPlaquettes"
|
|
@click-number="selectNumber" />
|
|
|
|
<Transition name="slide_up">
|
|
<div v-if="gameIsRunning">
|
|
<!-- OPERATORS -->
|
|
<OperatorsList @click="selectOperator" />
|
|
|
|
<!-- Divider -->
|
|
<div class="my-4 mx-auto max-w-sm border-b" />
|
|
|
|
<!-- List of Operations -->
|
|
<OperationsList v-show="gameIsRunning" />
|
|
</div>
|
|
</Transition>
|
|
|
|
<Transition name="slide_up">
|
|
<div
|
|
v-if="isEndGame"
|
|
class="flex flex-row justify-evenly items-center mx-auto mt-8 max-w-sm">
|
|
<span
|
|
v-if="isResultPerfect"
|
|
v-html="t('endGame.victoryLabel')" />
|
|
<span
|
|
v-else
|
|
v-html="t('endGame.failureLabel')" />
|
|
<button
|
|
class="p-2 rounded border"
|
|
@click="reboot">
|
|
{{ t('playAgain') }}
|
|
</button>
|
|
</div>
|
|
</Transition>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
import {
|
|
getEmptyOperation,
|
|
isOperationReady,
|
|
isOperationResultValid,
|
|
isSolvable,
|
|
operate,
|
|
} from '@/algo'
|
|
import AppHeader from '@/components/AppHeader.vue'
|
|
import PlaquetteBox from '@/components/common/PlaquetteBox.vue'
|
|
import OperationsList from '@/components/OperationsList.vue'
|
|
import OperatorsList from '@/components/OperatorsList.vue'
|
|
import PlaquettesList from '@/components/PlaquettesList.vue'
|
|
import {
|
|
currentOperation,
|
|
gameIsRunning,
|
|
gameState,
|
|
isEndGame,
|
|
isResultPerfect,
|
|
operations,
|
|
plaquettes,
|
|
result,
|
|
} from '@/composables/game-state'
|
|
import { GameState, pools } from '@/globals'
|
|
import { OperatorType, Plaquette } from '@/types'
|
|
import { randItem, randRange } from '@/utils'
|
|
|
|
const { t } = useI18n() // call `useI18n`, and spread `t` from `useI18n` returning
|
|
|
|
/*
|
|
* Computed & refs
|
|
*/
|
|
|
|
const difficultyLevel = ref<1 | 2>(1)
|
|
|
|
const isDaily = computed(() => useRoute().name === 'daily')
|
|
const shownPlaquettes = computed(() =>
|
|
gameIsRunning.value ? plaquettes.value : [],
|
|
)
|
|
|
|
/*
|
|
* Watchers
|
|
*/
|
|
|
|
watch(
|
|
currentOperation,
|
|
op => {
|
|
if (isOperationReady(op) && isOperationResultValid(op) && !op.result) {
|
|
op.result = {
|
|
free: true,
|
|
value: operate(op.operator!, op.left!.value, op.right!.value),
|
|
}
|
|
if (operations.length < 5 && !isEndGame.value) {
|
|
plaquettes.value.push(op.result)
|
|
operations.push(getEmptyOperation())
|
|
}
|
|
}
|
|
},
|
|
{ deep: true },
|
|
)
|
|
|
|
function startGame(): void {
|
|
gameState.value = GameState.Loading
|
|
setTimeout(() => (gameState.value = GameState.Playing), 500)
|
|
}
|
|
|
|
function selectNumber(p: Plaquette): void {
|
|
if (isEndGame.value) return
|
|
|
|
const op = currentOperation.value
|
|
if (!p.free) return
|
|
|
|
if (!op.left) {
|
|
op.left = p
|
|
p.free = false
|
|
}
|
|
else if (!op.right) {
|
|
op.right = p
|
|
p.free = false
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Functions
|
|
*/
|
|
|
|
function selectOperator(o: OperatorType): void {
|
|
if (isEndGame.value) return
|
|
currentOperation.value.operator = o
|
|
}
|
|
|
|
function reboot(): void {
|
|
gameState.value = GameState.Playing
|
|
do {
|
|
// Find a problem
|
|
// result.value = randRange(101, 1000)
|
|
difficultyLevel.value = randItem([1, 1, 1, 1, 2])
|
|
result.value = (() => {
|
|
switch (difficultyLevel.value) {
|
|
case 1:
|
|
return randRange(101, 1000)
|
|
case 2:
|
|
return randRange(101, 1000)
|
|
}
|
|
})()
|
|
// result.value = 29
|
|
// Reset Operations list
|
|
operations.splice(0)
|
|
operations.push(getEmptyOperation())
|
|
|
|
// Generate result and plaquettes
|
|
plaquettes.value = []
|
|
const poolCopy = [...pools[difficultyLevel.value]]
|
|
for (let i = 0; i < 6; ++i) {
|
|
const random = Math.floor(Math.random() * poolCopy.length)
|
|
const el = poolCopy.splice(random, 1)[0]
|
|
plaquettes.value.push({ value: el, free: true })
|
|
}
|
|
// plaquettes.value = [4, 8, 10, 25, 50, 100].map(value => ({
|
|
// free: true,
|
|
// value,
|
|
// }))
|
|
// Solve it
|
|
} while (
|
|
!isSolvable(
|
|
result.value,
|
|
plaquettes.value.map(p => p.value),
|
|
)
|
|
)
|
|
plaquettes.value.sort((a, b) => a.value - b.value)
|
|
}
|
|
|
|
/*
|
|
* Hooks
|
|
*/
|
|
|
|
onMounted(() => {
|
|
reboot()
|
|
gameState.value = GameState.Waiting
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
gameState.value = GameState.Waiting
|
|
})
|
|
</script>
|