Amélioration intéraction, game over + check cases numéro
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 159 B |
BIN
public/sHighlight.png
Normal file
BIN
public/sHighlight.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 B |
@@ -1,31 +1,40 @@
|
||||
type CellGrid = string[][] // type CellGrid (grille de jeu)
|
||||
type MineGrid = number[][] // type MineGrid (grille de bombes)
|
||||
type Directions = number[][] // type Directions (directions possibles)
|
||||
|
||||
function getDirections(x: number, y: number): Directions {
|
||||
return [[x-1, y-1], [x-1, y], [x-1, y+1], [x, y-1], [x, y+1], [x+1, y-1], [x+1, y], [x+1, y+1]]
|
||||
}
|
||||
|
||||
function genCellGrid(width: number, length: number): CellGrid { // fonction genCellGrid (génération de la grille de jeu)
|
||||
return Array.from({ length: width }, () => Array.from({ length: length }, () => "/sUnknown.png"))
|
||||
}
|
||||
|
||||
function genMineGrid(width: number, length: number, nbMines: number): MineGrid { // fonction genMineGrid (génération des bombes)
|
||||
function genMineGrid(width: number, length: number, nbMines: number, fx: number, fy: number): MineGrid { // fonction genMineGrid (génération des bombes)
|
||||
const mineGrid: MineGrid = [] // initialisation de la grille de bombes
|
||||
const directions = getDirections(fx, fy)
|
||||
|
||||
while (mineGrid.length < nbMines) { // boucle pour placer les bombes
|
||||
const x = Math.floor(Math.random() * length) // position x de la bombe
|
||||
const y = Math.floor(Math.random() * width) // position y de la bombe
|
||||
const indice = [y, x] // id de la bombe
|
||||
if (!mineGrid.includes(indice)) mineGrid.push(indice) // si la bombe n'est pas déjà placée, on la place
|
||||
const indice = [x, y]
|
||||
|
||||
if (directions.some(d => d[0] === x && d[1] === y) || (x === fx && y === fy)) continue // vérifie si la bombe n'est pas déjà placée et si ce n'est pas le premier clic
|
||||
if (!mineGrid.some(m => m[0] === x && m[1] === y)) mineGrid.push(indice) // vérifie si la bombe n'est pas déjà placée et si ce n'est pas le premier clic
|
||||
}
|
||||
return mineGrid; // retour de la grille de bombes
|
||||
mineGrid.sort((a, b) => a[0] - b[0] || a[1] - b[1]) // Trier les bombes par ordre x puis y
|
||||
return mineGrid // retour de la grille de bombes
|
||||
}
|
||||
|
||||
function cliqueGauche(cellGrid: CellGrid, mineGrid: MineGrid, x: number, y: number) { // fonction main (début du jeu)
|
||||
//const indice = [y, x] // id de la case cliquée
|
||||
//if (mineGrid.includes(indice)) bombe() // si la case cliquée est une bombe, on va dans la fonction bombe
|
||||
//else if (champDeMines[indice] === 0) {caseVide(indice);} // si la case cliquée est vide, on va dans la fonction caseVide
|
||||
//else if (champDeMines[indice] !== 0) {decouvreCase(indice);} // si la case cliquée est un numéro, on va dans la fonction decouvreCase
|
||||
//decouvreCase(indice);
|
||||
if (cellGrid[y][x] === '/sClick.png') return cellGrid[y][x] = '/sEmpty.png' // modification de l'image de la case
|
||||
|
||||
function cliqueGauche(gameStatus: number, cellGrid: CellGrid, mineGrid: MineGrid, x: number, y: number) {
|
||||
const checkDrop = ['/sEmpty.png', '/sUnknown.png', '/sFlag.png', '/sQuestion.png']
|
||||
if (checkDrop.includes(cellGrid[y][x])) return
|
||||
else if (cellGrid[y][x] === '/sClick.png') checkCell(gameStatus, cellGrid, mineGrid, x, y)
|
||||
else revealCell(gameStatus, cellGrid, mineGrid, x, y)
|
||||
}
|
||||
|
||||
function cliqueDroit(cellGrid: string[][], x: number, y: number): void { // fonction cliqueDroit (flag d'une case) // modification de l'image de la case
|
||||
function cliqueDroit(cellGrid: string[][], x: number, y: number) { // fonction cliqueDroit (flag d'une case) // modification de l'image de la case
|
||||
switch (cellGrid[y][x]) {
|
||||
case '/sUnknown.png':
|
||||
cellGrid[y][x] = "/sFlag.png"
|
||||
@@ -39,30 +48,58 @@ function cliqueDroit(cellGrid: string[][], x: number, y: number): void {
|
||||
}
|
||||
}
|
||||
|
||||
function checkCell(gameStatus: number, cellGrid: CellGrid, mineGrid: MineGrid, x: number, y: number) {
|
||||
if (mineGrid.some(m => m[0] === x && m[1] === y)) return gameOver(gameStatus, cellGrid, mineGrid, x, y)
|
||||
|
||||
/*
|
||||
const directions: number[] = [-11, -10, -9, -1, 1, 9, 10, 11]; // directions possibles pour découvrir les cases
|
||||
const champDeMines: number[] = []; // champ de mines (0 = case vide, 10 = bombe, 1 à 8 = nombre de bombes adjacentes)
|
||||
for (let i = 0; i < 100; i++) {champDeMines.push(0);} // initialisation des cases à 0
|
||||
const casesDecouvertes: number[] = []; // cases découvertes (0 = case non découverte, 1 = case découverte, 2 = case flag)
|
||||
for (let i = 0; i < 100; i++) {casesDecouvertes.push(0);} // initialisation des cases à 0
|
||||
|
||||
function bombe(): void { // fonction bombe (game over)
|
||||
for (let i = 0; i < casesDecouvertes.length; i++) {casesDecouvertes[i] = 1;}
|
||||
let minesAdjacentes = 0
|
||||
const directions = getDirections(x, y)
|
||||
for (const [dx, dy] of directions) {
|
||||
if (dx < 0 || dx >= cellGrid[0].length || dy < 0 || dy >= cellGrid.length) continue
|
||||
if (mineGrid.some(m => m[0] === dx && m[1] === dy)) minesAdjacentes++
|
||||
}
|
||||
|
||||
function caseVide(indice: number): void { // fonction caseVide (découverte des cases adjacentes à une case vide)
|
||||
for (let i = 0; i < directions.length; i++) { // parcours des directions
|
||||
if (champDeMines[indice + directions[i]] !== 0) {decouvreCase(indice + directions[i]); return; } // si la case est un numéro, on ne fait rien
|
||||
else if (casesDecouvertes[indice + directions[i]] === 2) {decouvreCase(indice + directions[i]); return;} // si la case est un drapeau, on ne fait rien
|
||||
else {decouvreCase(indice + directions[i]); caseVide(indice + directions[i]);} // si la case est vide, on va dans la fonction caseVide
|
||||
if (minesAdjacentes > 0) cellGrid[y][x] = `/s${minesAdjacentes}.png`
|
||||
else {
|
||||
cellGrid[y][x] = '/sEmpty.png'
|
||||
for (const [dx, dy] of directions) {
|
||||
if (dx >= 0 && dx < cellGrid[0].length && dy >= 0 && dy < cellGrid.length && cellGrid[dy][dx] === '/sUnknown.png') {
|
||||
checkCell(gameStatus, cellGrid, mineGrid, dx, dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function decouvreCase(indice: number): void { // fonction decouvreCase (découverte d'une case)
|
||||
casesDecouvertes[indice] = 1; // modification dans le tableau des cases découvertes
|
||||
function revealCell(gameStatus: number, cellGrid: CellGrid, mineGrid: MineGrid, x: number, y: number) {
|
||||
const directions = getDirections(x, y)
|
||||
let flagCount = 0
|
||||
for (const [dx, dy] of directions) {
|
||||
if (dx >= 0 && dx < cellGrid[0].length && dy >= 0 && dy < cellGrid.length && cellGrid[dy][dx] === '/sFlag.png') flagCount++
|
||||
}
|
||||
const cellValue = parseInt(cellGrid[y][x].match(/\/s(\d)\.png$/)?.[1] || '0')
|
||||
if (flagCount === cellValue) {
|
||||
for (const [dx, dy] of directions) {
|
||||
if (dx >= 0 && dx < cellGrid[0].length && dy >= 0 && dy < cellGrid.length && cellGrid[dy][dx] === '/sHighlight.png') {
|
||||
cellGrid[dy][dx] = '/sUnknown.png'
|
||||
checkCell(gameStatus, cellGrid, mineGrid, dx, dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
export { genCellGrid, genMineGrid, cliqueGauche, cliqueDroit } // export des variables et fonctions
|
||||
export type { CellGrid, MineGrid } // export des types
|
||||
function gameOver(gameStatus: number, cellGrid: CellGrid, mineGrid: MineGrid, x: number, y: number) {
|
||||
for (const [mx, my] of mineGrid) {
|
||||
if (cellGrid[my][mx] === '/sUnknown.png') cellGrid[my][mx] = '/sMine.png'
|
||||
}
|
||||
// Vérifier l'emplacement de tous les flags dans toute la grille et les remplacer par '/sFlagWrong.png' s'il y en a un qui n'est pas sur une bombe
|
||||
for (let y = 0; y < cellGrid.length; y++) {
|
||||
for (let x = 0; x < cellGrid[y].length; x++) {
|
||||
if (cellGrid[y][x] === '/sFlag.png') if (!mineGrid.some(m => m[0] === x && m[1] === y)) cellGrid[y][x] = '/sFlagWrong.png'
|
||||
}
|
||||
}
|
||||
|
||||
cellGrid[y][x] = '/sExploded.png'
|
||||
gameStatus = 3
|
||||
}
|
||||
|
||||
export { getDirections, genCellGrid, genMineGrid, cliqueGauche, cliqueDroit } // export des fonctions
|
||||
export type { Directions, CellGrid, MineGrid } // export des types
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { genCellGrid, genMineGrid, cliqueGauche, cliqueDroit } from '@/utils/game'
|
||||
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||
import { getDirections, genCellGrid, genMineGrid, cliqueGauche, cliqueDroit } from '@/utils/game'
|
||||
import type { CellGrid, MineGrid } from '@/utils/game'
|
||||
|
||||
// Variables du jeu
|
||||
const width = 10
|
||||
const length = 15
|
||||
const nbMines = 25
|
||||
@@ -13,48 +14,111 @@ cellGrid.value = genCellGrid(width, length)
|
||||
|
||||
// Création grille des mines
|
||||
let mineGrid: MineGrid = []
|
||||
mineGrid = genMineGrid(width, length, nbMines)
|
||||
|
||||
// État du jeu
|
||||
const gameStatus = ref(0) // 0: vide, 1: en cours, 2: gagné, 3: perdu
|
||||
const timer = ref(0)
|
||||
let timerInterval: number | undefined = undefined
|
||||
|
||||
watch(gameStatus, async (status) => {
|
||||
if (status === 1) {
|
||||
timerInterval = setInterval(() => timer.value++, 1000)
|
||||
} else {
|
||||
if (timerInterval !== null) clearInterval(timerInterval)
|
||||
if (status === 2) alert('Vous avez gagné !')
|
||||
if (status === 3) alert('Vous avez perdu !')
|
||||
}
|
||||
})
|
||||
|
||||
// État pour gérer le clic et la position de la cellule
|
||||
const isMouseDown = ref(false)
|
||||
const currentCell = ref<{ rowIndex: number, cellIndex: number } | null>(null)
|
||||
|
||||
// État pour suivre les cases surlignées
|
||||
const highlightedCells = ref<number[][]>([])
|
||||
|
||||
// Gestion des événements de clic et de la position de la cellule
|
||||
const handleMouseDown = (rowIndex: number, cellIndex: number) => {
|
||||
isMouseDown.value = true
|
||||
currentCell.value = { rowIndex, cellIndex }
|
||||
if (cellGrid.value[rowIndex][cellIndex] === '/sUnknown.png') cellGrid.value[rowIndex][cellIndex] = '/sClick.png'
|
||||
const cellValue = cellGrid.value[rowIndex][cellIndex]
|
||||
|
||||
if (cellValue === '/sUnknown.png') cellGrid.value[rowIndex][cellIndex] = '/sClick.png' // Si la cellule est inconnue, on la vérifie
|
||||
else if (/\/s\d\.png$/.test(cellValue)) highlightAdjacentUnknownCells(cellIndex, rowIndex) // Vérifie si la cellule contient un chiffre
|
||||
|
||||
}
|
||||
|
||||
const handleMouseUp = (rowIndex: number, cellIndex: number) => {
|
||||
// Gestion des événements de relâchement de clic et de la position de la cellule
|
||||
const handleMouseUp = async (rowIndex: number, cellIndex: number) => {
|
||||
isMouseDown.value = false
|
||||
cliqueGauche(cellGrid.value, mineGrid, cellIndex, rowIndex)
|
||||
currentCell.value = null
|
||||
if (!mineGrid.length) {
|
||||
mineGrid = genMineGrid(width, length, nbMines, cellIndex, rowIndex)
|
||||
gameStatus.value = 1
|
||||
}
|
||||
|
||||
cliqueGauche(gameStatus.value, cellGrid.value, mineGrid, cellIndex, rowIndex)
|
||||
currentCell.value = null
|
||||
|
||||
// Réinitialiser les cases surlignées
|
||||
for (const [dx, dy] of highlightedCells.value) { if (cellGrid.value[dy][dx] === '/sHighlight.png') cellGrid.value[dy][dx] = '/sUnknown.png' }
|
||||
highlightedCells.value = []
|
||||
}
|
||||
|
||||
// Gestion des événements de déplacement de la souris et de la position de la cellule
|
||||
const handleMouseMove = (rowIndex: number, cellIndex: number) => {
|
||||
if (isMouseDown.value && currentCell.value) {
|
||||
const { rowIndex: prevRowIndex, cellIndex: prevCellIndex } = currentCell.value
|
||||
|
||||
if (prevRowIndex !== rowIndex || prevCellIndex !== cellIndex) {
|
||||
if (cellGrid.value[prevRowIndex][prevCellIndex] === '/sClick.png') cellGrid.value[prevRowIndex][prevCellIndex] = '/sUnknown.png' // Réinitialiser l'image précédente
|
||||
if (cellGrid.value[rowIndex][cellIndex] === '/sUnknown.png') cellGrid.value[rowIndex][cellIndex] = '/sClick.png' // Changer l'image actuelle
|
||||
const cellValue = cellGrid.value[rowIndex][cellIndex]
|
||||
|
||||
// Réinitialiser les cases surlignées
|
||||
for (const [dx, dy] of highlightedCells.value) { cellGrid.value[dy][dx] = '/sUnknown.png' }
|
||||
highlightedCells.value = []
|
||||
|
||||
// Changer l'image actuelle
|
||||
if (cellValue === '/sUnknown.png') cellGrid.value[rowIndex][cellIndex] = '/sClick.png'
|
||||
else if (/\/s\d\.png$/.test(cellValue)) highlightAdjacentUnknownCells(cellIndex, rowIndex)
|
||||
|
||||
// Réinitialiser l'image précédente
|
||||
const prevCellValue = cellGrid.value[prevRowIndex][prevCellIndex]
|
||||
if (prevCellValue === '/sClick.png') cellGrid.value[prevRowIndex][prevCellIndex] = '/sUnknown.png'
|
||||
|
||||
currentCell.value = { rowIndex, cellIndex }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion des événements de déplacement de la souris et de la position de la cellule
|
||||
const handleGlobalMouseMove = (event: MouseEvent) => {
|
||||
if (isMouseDown.value && currentCell.value) {
|
||||
const target = event.target as HTMLElement
|
||||
|
||||
if (!target.classList.contains('cell')) {
|
||||
const { rowIndex, cellIndex } = currentCell.value
|
||||
if (cellGrid.value[rowIndex][cellIndex] === '/sClick.png') {
|
||||
cellGrid.value[rowIndex][cellIndex] = '/sUnknown.png'
|
||||
}
|
||||
|
||||
if (cellGrid.value[rowIndex][cellIndex] === '/sClick.png') cellGrid.value[rowIndex][cellIndex] = '/sUnknown.png'
|
||||
currentCell.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction pour surligner toutes les cases adjacentes inconnues
|
||||
const highlightAdjacentUnknownCells = (cellIndex: number, rowIndex: number) => {
|
||||
const directions = getDirections(cellIndex, rowIndex)
|
||||
|
||||
highlightedCells.value = [] // Réinitialiser la liste des cases surlignées
|
||||
|
||||
for (const [dx, dy] of directions) {
|
||||
if (dx < 0 || dx >= cellGrid.value[0].length || dy < 0 || dy >= cellGrid.value.length) continue
|
||||
if (cellGrid.value[dy][dx] === '/sUnknown.png') {
|
||||
cellGrid.value[dy][dx] = '/sHighlight.png'
|
||||
highlightedCells.value.push([dx, dy])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Écoute des événements de déplacement de la souris
|
||||
onMounted(() => document.addEventListener('mousemove', handleGlobalMouseMove))
|
||||
onUnmounted(() => document.removeEventListener('mousemove', handleGlobalMouseMove))
|
||||
</script>
|
||||
@@ -62,6 +126,7 @@ onUnmounted(() => document.removeEventListener('mousemove', handleGlobalMouseMov
|
||||
<template>
|
||||
<div class="main">
|
||||
<h1>Solo</h1>
|
||||
<div class="timer" :gameStatus>{{ timer }}</div>
|
||||
<div class="grid">
|
||||
<div v-for="(row, rowIndex) in cellGrid" :key="rowIndex" class="row">
|
||||
<div v-for="(cell, cellIndex) in row" :key="cellIndex">
|
||||
@@ -77,6 +142,13 @@ onUnmounted(() => document.removeEventListener('mousemove', handleGlobalMouseMov
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div v-for="(row, rowIndex) in cellGrid" :key="rowIndex" class="row">
|
||||
<div v-for="(cell, cellIndex) in row" :key="cellIndex">
|
||||
<img class="cell" :src="mineGrid.some(mine => mine[0] === cellIndex && mine[1] === rowIndex) ? '/sMine.png' : '/sEmpty.png'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user