Init
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<h1 class="title">Generator Labiryntu</h1>
|
||||
|
||||
<div class="input-group">
|
||||
<input v-model="seed" @keyup.enter="generateMaze" type="text" class="input"
|
||||
placeholder="Wprowadź seed (ziarno)">
|
||||
<button @click="generateMaze" class="button" type="button">Generuj</button>
|
||||
</div>
|
||||
|
||||
<div v-if="maze.length" class="maze-container">
|
||||
<div v-for="(row, y) in maze" :key="y" class="maze-row">
|
||||
<div v-for="(cell, x) in row" :key="x" :class="['maze-cell',
|
||||
{
|
||||
'wall': cell === 1,
|
||||
'visited': isVisited(x, y),
|
||||
'start': x === 1 && y === 1,
|
||||
'end': x === maze[0].length - 3 && y === maze.length - 3,
|
||||
'player': x === playerX && y === playerY
|
||||
}]">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-controls" v-if="isMobile">
|
||||
<button @click="moveUp" class="control-button">↑</button>
|
||||
<div class="horizontal-controls">
|
||||
<button @click="moveLeft" class="control-button">←</button>
|
||||
<button @click="moveDown" class="control-button">↓</button>
|
||||
<button @click="moveRight" class="control-button">→</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-counter">
|
||||
Liczba kroków: {{ stepCount }}
|
||||
</div>
|
||||
|
||||
<div v-if="completed">
|
||||
<p class="completion-message mt-2">Gratulacje! Przeszedłeś labirynt.</p>
|
||||
<p class="completion-message">Chcesz spróbować jeszcze raz?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, Ref, onMounted, watch } from 'vue';
|
||||
|
||||
const seed = ref('');
|
||||
const maze = ref<number[][]>([]);
|
||||
|
||||
const playerX = ref(1);
|
||||
const playerY = ref(1);
|
||||
const stepCount = ref(0);
|
||||
|
||||
const completed = ref(false);
|
||||
const visitedCells: Ref<Set<string>> = ref(new Set<string>());
|
||||
|
||||
const isMobile = ref(window.innerWidth < 640);
|
||||
|
||||
const updateIsMobile = () => {
|
||||
isMobile.value = window.innerWidth < 640;
|
||||
};
|
||||
|
||||
window.addEventListener('resize', updateIsMobile);
|
||||
|
||||
const generateMaze = () => {
|
||||
const seedValue = seed.value || Math.random().toString(36).substring(7);
|
||||
const rows = 50;
|
||||
const cols = 50;
|
||||
|
||||
visitedCells.value = new Set();
|
||||
stepCount.value = 0;
|
||||
|
||||
const random = (() => {
|
||||
let seed = hash(seedValue);
|
||||
return () => {
|
||||
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
||||
return seed / 0x7fffffff;
|
||||
};
|
||||
})();
|
||||
|
||||
maze.value = Array(rows).fill(0).map(() => Array(cols).fill(1));
|
||||
|
||||
const stack: [number, number][] = [[1, 1]];
|
||||
maze.value[1][1] = 0;
|
||||
|
||||
while (stack.length > 0) {
|
||||
const [x, y] = stack[stack.length - 1];
|
||||
const directions = [[0, -1], [1, 0], [0, 1], [-1, 0]].sort(() => random() - 0.5);
|
||||
|
||||
let moved = false;
|
||||
for (const [dx, dy] of directions) {
|
||||
const nx = x + dx * 2;
|
||||
const ny = y + dy * 2;
|
||||
if (nx > 0 && nx < cols - 1 && ny > 0 && ny < rows - 1 && maze.value[ny][nx] === 1) {
|
||||
maze.value[ny][nx] = 0;
|
||||
maze.value[y + dy][x + dx] = 0;
|
||||
stack.push([nx, ny]);
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!moved)
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
playerX.value = 1;
|
||||
playerY.value = 1;
|
||||
completed.value = false;
|
||||
};
|
||||
|
||||
const movePlayer = (e: KeyboardEvent) => {
|
||||
if (completed.value) return;
|
||||
|
||||
const key = e.key;
|
||||
let newX = playerX.value;
|
||||
let newY = playerY.value;
|
||||
|
||||
if (key === 'ArrowUp') newY--;
|
||||
else if (key === 'ArrowDown') newY++;
|
||||
else if (key === 'ArrowLeft') newX--;
|
||||
else if (key === 'ArrowRight') newX++;
|
||||
|
||||
moveTo(newX, newY);
|
||||
};
|
||||
|
||||
const moveTo = (newX: number, newY: number) => {
|
||||
if (newX >= 0 && newX < maze.value[0].length && newY >= 0 && newY < maze.value.length && maze.value[newY][newX] === 0) {
|
||||
playerX.value = newX;
|
||||
playerY.value = newY;
|
||||
stepCount.value++;
|
||||
visitedCells.value.add(`${newX},${newY}`);
|
||||
|
||||
if (newX === maze.value[0].length - 3 && newY === maze.value.length - 3)
|
||||
completed.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const moveUp = () => moveTo(playerX.value, playerY.value - 1);
|
||||
const moveDown = () => moveTo(playerX.value, playerY.value + 1);
|
||||
const moveLeft = () => moveTo(playerX.value - 1, playerY.value);
|
||||
const moveRight = () => moveTo(playerX.value + 1, playerY.value);
|
||||
|
||||
const hash = (s: string) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', movePlayer);
|
||||
generateMaze();
|
||||
});
|
||||
|
||||
watch(completed, (newValue) => {
|
||||
if (newValue)
|
||||
window.removeEventListener('keydown', movePlayer);
|
||||
else
|
||||
window.addEventListener('keydown', movePlayer);
|
||||
});
|
||||
|
||||
const isVisited = (x: number, y: number) => {
|
||||
if (
|
||||
(x == playerX.value && y == playerY.value) ||
|
||||
(x == 1 && y == 1) ||
|
||||
(x == maze.value[0].length - 3 && y == maze.value.length - 3)
|
||||
)
|
||||
return false;
|
||||
return visitedCells.value.has(`${x},${y}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
@apply flex justify-center items-center min-h-screen bg-gray-100 min-w-full p-4;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply bg-white p-4 sm:p-8 rounded-lg shadow-lg max-w-full;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-2xl sm:text-3xl font-bold text-center mb-4 sm:mb-6;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@apply flex mb-4;
|
||||
}
|
||||
|
||||
.input {
|
||||
@apply flex-grow px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500;
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500;
|
||||
}
|
||||
|
||||
.maze-container {
|
||||
@apply inline-block border-2 border-black max-w-full overflow-auto;
|
||||
}
|
||||
|
||||
.maze-row {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
.maze-cell {
|
||||
@apply w-2 h-2 min-w-0.5 min-h-0.5;
|
||||
}
|
||||
|
||||
.wall {
|
||||
@apply bg-black;
|
||||
}
|
||||
|
||||
.player {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
|
||||
.start {
|
||||
@apply bg-green-500;
|
||||
}
|
||||
|
||||
.end {
|
||||
@apply bg-yellow-500;
|
||||
}
|
||||
|
||||
.visited {
|
||||
@apply bg-blue-200;
|
||||
}
|
||||
|
||||
.step-counter {
|
||||
@apply mt-4 text-center text-base sm:text-lg font-semibold;
|
||||
}
|
||||
|
||||
.completion-message {
|
||||
@apply text-center text-base sm:text-lg font-semibold text-green-600;
|
||||
}
|
||||
|
||||
/* Nowe style dla przycisków na urządzeniach mobilnych */
|
||||
.mobile-controls {
|
||||
@apply mt-4 flex flex-col items-center;
|
||||
}
|
||||
|
||||
.horizontal-controls {
|
||||
@apply flex mt-2 mb-2;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded-full m-1;
|
||||
@apply hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user