diff --git a/assets/css/custom.css b/assets/css/custom.css
index 50e0502..dc8bbf2 100644
--- a/assets/css/custom.css
+++ b/assets/css/custom.css
@@ -1,302 +1,316 @@
+:root {
+ --bg-dark: #0f172a;
+ --bg-mesh: #020617;
+ --card-bg: rgba(30, 41, 59, 0.7);
+ --accent: #38bdf8;
+ --accent-hover: #7dd3fc;
+ --text-main: #f8fafc;
+ --text-muted: #94a3b8;
+ --snake-head: #4ade80;
+ --snake-body: #166534;
+ --apple: #ef4444;
+ --wall: #475569;
+ --font-main: 'Inter', sans-serif;
+ --font-mono: 'JetBrains Mono', monospace;
+ --shadow-sm: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
+}
+
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ -webkit-tap-highlight-color: transparent;
+}
+
body {
- background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
- background-size: 400% 400%;
- animation: gradient 15s ease infinite;
- color: #212529;
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
- font-size: 14px;
- margin: 0;
- min-height: 100vh;
+ font-family: var(--font-main);
+ background-color: var(--bg-dark);
+ color: var(--text-main);
+ line-height: 1.5;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow-x: hidden;
+ position: relative;
}
-.main-wrapper {
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 100vh;
- width: 100%;
- padding: 20px;
- box-sizing: border-box;
- position: relative;
- z-index: 1;
+.background-mesh {
+ position: fixed;
+ inset: 0;
+ z-index: -1;
+ background-color: var(--bg-mesh);
+ background-image:
+ radial-gradient(at 0% 0%, rgba(56, 189, 248, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 0%, rgba(139, 92, 246, 0.15) 0px, transparent 50%),
+ radial-gradient(at 100% 100%, rgba(236, 72, 153, 0.1) 0px, transparent 50%),
+ radial-gradient(at 0% 100%, rgba(56, 189, 248, 0.1) 0px, transparent 50%);
}
-@keyframes gradient {
- 0% {
- background-position: 0% 50%;
- }
- 50% {
- background-position: 100% 50%;
- }
- 100% {
- background-position: 0% 50%;
- }
+.container {
+ width: 100%;
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 0 1rem;
}
-.chat-container {
- width: 100%;
- max-width: 600px;
- background: rgba(255, 255, 255, 0.85);
- border: 1px solid rgba(255, 255, 255, 0.3);
- border-radius: 20px;
- display: flex;
- flex-direction: column;
- height: 85vh;
- box-shadow: 0 20px 40px rgba(0,0,0,0.2);
- backdrop-filter: blur(15px);
- -webkit-backdrop-filter: blur(15px);
- overflow: hidden;
+.main-header {
+ padding: 1.5rem 0;
+ text-align: center;
}
-.chat-header {
- padding: 1.5rem;
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- background: rgba(255, 255, 255, 0.5);
- font-weight: 700;
- font-size: 1.1rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
+.main-header h1 {
+ font-size: 2.25rem;
+ font-weight: 800;
+ letter-spacing: -0.025em;
+ margin-bottom: 0.25rem;
+ background: linear-gradient(to right, var(--accent), #818cf8);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
}
-.chat-messages {
- flex: 1;
- overflow-y: auto;
- padding: 1.5rem;
- display: flex;
- flex-direction: column;
- gap: 1.25rem;
+.badge {
+ font-size: 0.75rem;
+ padding: 0.2rem 0.6rem;
+ background: var(--accent);
+ border-radius: 9999px;
+ -webkit-text-fill-color: var(--bg-dark);
+ vertical-align: middle;
+ margin-left: 0.4rem;
+ text-transform: uppercase;
+ font-weight: 700;
}
-/* Custom Scrollbar */
-::-webkit-scrollbar {
- width: 6px;
+.subtitle {
+ color: var(--text-muted);
+ font-size: 0.95rem;
}
-::-webkit-scrollbar-track {
- background: transparent;
+.main-layout {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ padding-bottom: 2rem;
}
-::-webkit-scrollbar-thumb {
- background: rgba(255, 255, 255, 0.3);
- border-radius: 10px;
+@media (min-width: 968px) {
+ .main-layout {
+ display: grid;
+ grid-template-columns: 1fr 340px;
+ }
}
-::-webkit-scrollbar-thumb:hover {
- background: rgba(255, 255, 255, 0.5);
+.card {
+ background: var(--card-bg);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 1.25rem;
+ padding: 1.25rem;
+ box-shadow: var(--shadow-xl);
+ margin-bottom: 1rem;
}
-.message {
- max-width: 85%;
- padding: 0.85rem 1.1rem;
- border-radius: 16px;
- line-height: 1.5;
- font-size: 0.95rem;
- box-shadow: 0 4px 15px rgba(0,0,0,0.05);
- animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+.card h3 {
+ font-size: 1.125rem;
+ margin-bottom: 1rem;
+ font-weight: 600;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+ padding-bottom: 0.5rem;
+ color: var(--accent);
}
-@keyframes fadeIn {
- from { opacity: 0; transform: translateY(20px) scale(0.95); }
- to { opacity: 1; transform: translateY(0) scale(1); }
+.canvas-wrapper {
+ position: relative;
+ background: #000;
+ border-radius: 1.25rem;
+ overflow: hidden;
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1);
+ aspect-ratio: 1 / 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ max-width: 600px;
+ margin: 0 auto;
}
-.message.visitor {
- align-self: flex-end;
- background: linear-gradient(135deg, #212529 0%, #343a40 100%);
- color: #fff;
- border-bottom-right-radius: 4px;
+#gameCanvas {
+ width: 100%;
+ height: 100%;
+ touch-action: none;
}
-.message.bot {
- align-self: flex-start;
- background: #ffffff;
- color: #212529;
- border-bottom-left-radius: 4px;
+.overlay {
+ position: absolute;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.9);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10;
+ transition: opacity 0.3s ease;
+ backdrop-filter: blur(8px);
}
-.chat-input-area {
- padding: 1.25rem;
- background: rgba(255, 255, 255, 0.5);
- border-top: 1px solid rgba(0, 0, 0, 0.05);
+.overlay.hidden {
+ opacity: 0;
+ pointer-events: none;
}
-.chat-input-area form {
- display: flex;
- gap: 0.75rem;
+.overlay-content h2 {
+ font-size: 2.5rem;
+ color: var(--snake-head);
+ margin-bottom: 0.5rem;
+ text-shadow: 0 0 15px rgba(74, 222, 128, 0.3);
}
-.chat-input-area input {
- flex: 1;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 12px;
- padding: 0.75rem 1rem;
- outline: none;
- background: rgba(255, 255, 255, 0.9);
- transition: all 0.3s ease;
+.stats-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0.75rem;
}
-.chat-input-area input:focus {
- border-color: #23a6d5;
- box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
+.stat-item {
+ background: rgba(15, 23, 42, 0.5);
+ padding: 0.75rem;
+ border-radius: 1rem;
+ text-align: center;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ transition: transform 0.2s;
}
-.chat-input-area button {
- background: #212529;
- color: #fff;
- border: none;
- padding: 0.75rem 1.5rem;
- border-radius: 12px;
- cursor: pointer;
- font-weight: 600;
- transition: all 0.3s ease;
+.stat-item:hover {
+ transform: translateY(-2px);
+ border-color: rgba(56, 189, 248, 0.2);
}
-.chat-input-area button:hover {
- background: #000;
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
+.stat-item .label {
+ display: block;
+ font-size: 0.65rem;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ font-weight: 700;
+ margin-bottom: 0.2rem;
+ letter-spacing: 0.05em;
}
-/* Background Animations */
-.bg-animations {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 0;
- overflow: hidden;
- pointer-events: none;
+.stat-item .value {
+ display: block;
+ font-size: 1.25rem;
+ font-family: var(--font-mono);
+ font-weight: 700;
+ color: var(--text-main);
}
-.blob {
- position: absolute;
- width: 500px;
- height: 500px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 50%;
- filter: blur(80px);
- animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
+.control-group {
+ margin-bottom: 1.25rem;
}
-.blob-1 {
- top: -10%;
- left: -10%;
- background: rgba(238, 119, 82, 0.4);
+.control-group label {
+ display: block;
+ margin-bottom: 0.75rem;
+ color: var(--text-muted);
+ font-size: 0.8rem;
+ font-weight: 600;
}
-.blob-2 {
- bottom: -10%;
- right: -10%;
- background: rgba(35, 166, 213, 0.4);
- animation-delay: -7s;
- width: 600px;
- height: 600px;
+input[type="range"] {
+ width: 100%;
+ accent-color: var(--accent);
+ height: 6px;
+ border-radius: 3px;
+ cursor: pointer;
}
-.blob-3 {
- top: 40%;
- left: 30%;
- background: rgba(231, 60, 126, 0.3);
- animation-delay: -14s;
- width: 450px;
- height: 450px;
+.range-labels {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 0.5rem;
+ font-size: 0.7rem;
+ color: var(--text-muted);
+ font-weight: 600;
}
-@keyframes move {
- 0% { transform: translate(0, 0) rotate(0deg) scale(1); }
- 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
- 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
- 100% { transform: translate(0, 0) rotate(360deg) scale(1); }
+.button-group {
+ display: flex;
+ gap: 0.75rem;
}
-.admin-link {
- font-size: 14px;
- color: #fff;
- text-decoration: none;
- background: rgba(0, 0, 0, 0.2);
- padding: 0.5rem 1rem;
- border-radius: 8px;
- transition: all 0.3s ease;
+.btn {
+ flex: 1;
+ padding: 0.75rem 1rem;
+ border-radius: 0.85rem;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.2s;
+ border: none;
+ font-size: 0.85rem;
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
}
-.admin-link:hover {
- background: rgba(0, 0, 0, 0.4);
- text-decoration: none;
+.btn-primary {
+ background: linear-gradient(135deg, var(--accent), #6366f1);
+ color: white;
+ box-shadow: 0 4px 15px rgba(56, 189, 248, 0.3);
}
-/* Admin Styles */
-.admin-container {
- max-width: 900px;
- margin: 3rem auto;
- padding: 2.5rem;
- background: rgba(255, 255, 255, 0.85);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
- border-radius: 24px;
- box-shadow: 0 20px 50px rgba(0,0,0,0.15);
- border: 1px solid rgba(255, 255, 255, 0.4);
- position: relative;
- z-index: 1;
+.btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(56, 189, 248, 0.4);
}
-.admin-container h1 {
- margin-top: 0;
- color: #212529;
- font-weight: 800;
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.05);
+ color: var(--text-main);
+ border: 1px solid rgba(255, 255, 255, 0.1);
}
-.table {
- width: 100%;
- border-collapse: separate;
- border-spacing: 0 8px;
- margin-top: 1.5rem;
+.btn-secondary:hover {
+ background: rgba(255, 255, 255, 0.1);
}
-.table th {
- background: transparent;
- border: none;
+.info-card p {
+ font-size: 0.85rem;
+ color: var(--text-muted);
+ line-height: 1.6;
+}
+
+.info-card strong {
+ color: var(--accent);
+}
+
+.main-footer {
+ margin-top: auto;
+ padding: 2rem 0;
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 0.8rem;
+}
+
+@media (max-width: 640px) {
+ .main-header h1 {
+ font-size: 1.75rem;
+ }
+
+ .canvas-wrapper {
+ border-radius: 1rem;
+ }
+
+ .card {
padding: 1rem;
- color: #6c757d;
- font-weight: 600;
- text-transform: uppercase;
- font-size: 0.75rem;
- letter-spacing: 1px;
+ }
+
+ .overlay-content h2 {
+ font-size: 2rem;
+ }
}
-.table td {
- background: #fff;
- padding: 1rem;
- border: none;
-}
-
-.table tr td:first-child { border-radius: 12px 0 0 12px; }
-.table tr td:last-child { border-radius: 0 12px 12px 0; }
-
-.form-group {
- margin-bottom: 1.25rem;
-}
-
-.form-group label {
- display: block;
- margin-bottom: 0.5rem;
- font-weight: 600;
- font-size: 0.9rem;
-}
-
-.form-control {
- width: 100%;
- padding: 0.75rem 1rem;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 12px;
- background: #fff;
- transition: all 0.3s ease;
- box-sizing: border-box;
-}
-
-.form-control:focus {
- outline: none;
- border-color: #23a6d5;
- box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
+@media (max-height: 700px) and (orientation: landscape) {
+ .main-layout {
+ flex-direction: row;
+ }
+ .canvas-wrapper {
+ max-width: 400px;
+ }
}
\ No newline at end of file
diff --git a/assets/js/main.js b/assets/js/main.js
index d349598..a1fe791 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1,39 +1,599 @@
-document.addEventListener('DOMContentLoaded', () => {
- const chatForm = document.getElementById('chat-form');
- const chatInput = document.getElementById('chat-input');
- const chatMessages = document.getElementById('chat-messages');
+/**
+ * Auto-Snake: AI-Powered Snake Game
+ * Implements Advanced Pathfinding (BFS + Longest Path Survival)
+ * to ensure the snake fills the grid and avoids infinite loops.
+ */
- const appendMessage = (text, sender) => {
- const msgDiv = document.createElement('div');
- msgDiv.classList.add('message', sender);
- msgDiv.textContent = text;
- chatMessages.appendChild(msgDiv);
- chatMessages.scrollTop = chatMessages.scrollHeight;
- };
+const CONFIG = {
+ GRID_SIZE: 20,
+ TILE_SIZE: 0,
+ COLORS: {
+ BG: '#020617',
+ SNAKE_HEAD: '#4ade80',
+ SNAKE_BODY: '#22c55e',
+ SNAKE_BODY_GRADIENT: '#166534',
+ APPLE: '#ef4444',
+ APPLE_SHINE: '#fca5a5',
+ WALL: '#475569',
+ GRID: 'rgba(56, 189, 248, 0.05)'
+ }
+};
- chatForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- const message = chatInput.value.trim();
- if (!message) return;
+class Game {
+ constructor() {
+ this.canvas = document.getElementById('gameCanvas');
+ this.ctx = this.canvas.getContext('2d');
+
+ this.winCount = parseInt(localStorage.getItem('winCount') || '0');
+ this.bestScore = parseInt(localStorage.getItem('bestScore') || '0');
+ this.lastWinTime = parseInt(localStorage.getItem('lastWinTime') || Date.now());
+
+ this.speedRange = document.getElementById('speedRange');
+ this.winDisplay = document.getElementById('winCount');
+ this.timerDisplay = document.getElementById('timer');
+ this.scoreDisplay = document.getElementById('currentScore');
+ this.bestScoreDisplay = document.getElementById('bestScore');
+ this.resetBtn = document.getElementById('resetBtn');
+ this.toggleMazeBtn = document.getElementById('toggleMazeBtn');
+ this.overlay = document.getElementById('gameOverlay');
+
+ this.snake = [];
+ this.apple = { x: 0, y: 0 };
+ this.mazeEnabled = false;
+ this.walls = [];
+ this.isGameOver = false;
+ this.isWin = false;
+ this.frameCount = 0;
+
+ this.lastFrameTime = 0;
+
+ this.init();
+ this.setupEventListeners();
+ this.reset();
+ this.gameLoop(0);
+ }
- appendMessage(message, 'visitor');
- chatInput.value = '';
+ init() {
+ const resize = () => {
+ const container = this.canvas.parentElement;
+ const size = Math.min(container.clientWidth, 600);
+ this.canvas.width = size;
+ this.canvas.height = size;
+ CONFIG.TILE_SIZE = size / CONFIG.GRID_SIZE;
+ };
+ window.addEventListener('resize', resize);
+ resize();
+
+ this.winDisplay.textContent = this.winCount;
+ this.bestScoreDisplay.textContent = this.bestScore;
+ this.updateTimer();
+ setInterval(() => this.updateTimer(), 1000);
+ }
- try {
- const response = await fetch('api/chat.php', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ message })
- });
- const data = await response.json();
-
- // Artificial delay for realism
- setTimeout(() => {
- appendMessage(data.reply, 'bot');
- }, 500);
- } catch (error) {
- console.error('Error:', error);
- appendMessage("Sorry, something went wrong. Please try again.", 'bot');
+ setupEventListeners() {
+ this.resetBtn.addEventListener('click', () => {
+ this.winCount = 0;
+ this.bestScore = 0;
+ localStorage.setItem('winCount', '0');
+ localStorage.setItem('bestScore', '0');
+ this.winDisplay.textContent = '0';
+ this.bestScoreDisplay.textContent = '0';
+ this.reset();
+ });
+
+ this.toggleMazeBtn.addEventListener('click', () => {
+ this.mazeEnabled = !this.mazeEnabled;
+ this.reset();
+ });
+ }
+
+ reset() {
+ const mid = Math.floor(CONFIG.GRID_SIZE / 2);
+ this.snake = [
+ { x: mid, y: mid },
+ { x: mid, y: mid + 1 },
+ { x: mid, y: mid + 2 }
+ ];
+ this.isGameOver = false;
+ this.isWin = false;
+ this.overlay.classList.add('hidden');
+
+ this.generateMaze();
+ this.spawnApple();
+ this.scoreDisplay.textContent = '0';
+ }
+
+ generateMaze() {
+ this.walls = [];
+ if (!this.mazeEnabled) return;
+
+ const padding = 4;
+ const length = 8;
+ for (let i = padding; i < padding + length; i++) {
+ this.walls.push({ x: i, y: padding });
+ this.walls.push({ x: CONFIG.GRID_SIZE - 1 - i, y: CONFIG.GRID_SIZE - 1 - padding });
+ this.walls.push({ x: padding, y: CONFIG.GRID_SIZE - 1 - i });
+ this.walls.push({ x: CONFIG.GRID_SIZE - 1 - padding, y: i });
}
- });
-});
+ }
+
+ spawnApple() {
+ let possible = [];
+ for (let y = 0; y < CONFIG.GRID_SIZE; y++) {
+ for (let x = 0; x < CONFIG.GRID_SIZE; x++) {
+ if (!this.isOccupied(x, y)) possible.push({ x, y });
+ }
+ }
+
+ if (possible.length === 0) {
+ this.handleWin();
+ return;
+ }
+ this.apple = possible[Math.floor(Math.random() * possible.length)];
+ }
+
+ isOccupied(x, y, ignoreTail = false) {
+ if (x < 0 || x >= CONFIG.GRID_SIZE || y < 0 || y >= CONFIG.GRID_SIZE) return true;
+ const snakeToCheck = ignoreTail ? this.snake.slice(0, -1) : this.snake;
+ if (snakeToCheck.some(s => s.x === x && s.y === y)) return true;
+ if (this.walls.some(w => w.x === x && w.y === y)) return true;
+ return false;
+ }
+
+ updateTimer() {
+ const diff = Math.floor((Date.now() - this.lastWinTime) / 1000);
+ const mins = Math.floor(diff / 60).toString().padStart(2, '0');
+ const secs = (diff % 60).toString().padStart(2, '0');
+ this.timerDisplay.textContent = `${mins}:${secs}`;
+ }
+
+ handleWin() {
+ this.isWin = true;
+ this.winCount++;
+ this.lastWinTime = Date.now();
+ localStorage.setItem('winCount', this.winCount);
+ localStorage.setItem('lastWinTime', this.lastWinTime);
+ this.winDisplay.textContent = this.winCount;
+ this.overlay.classList.remove('hidden');
+ document.getElementById('overlayTitle').textContent = 'VICTORY!';
+ document.getElementById('overlayMessage').textContent = 'Perfect cycle complete. Restarting...';
+ setTimeout(() => this.reset(), 5000);
+ }
+
+ handleGameOver() {
+ this.isGameOver = true;
+ this.overlay.classList.remove('hidden');
+ document.getElementById('overlayTitle').textContent = 'RECALCULATING';
+ document.getElementById('overlayMessage').textContent = 'Wait, adjusting strategy...';
+ setTimeout(() => this.reset(), 1000);
+ }
+
+ bfs(start, target, ignoreTail = false) {
+ const queue = [[start]];
+ const visited = new Set();
+ visited.add(`${start.x},${start.y}`);
+
+ while (queue.length > 0) {
+ const path = queue.shift();
+ const curr = path[path.length - 1];
+ if (curr.x === target.x && curr.y === target.y) return path;
+
+ const neighbors = [
+ { x: curr.x, y: curr.y - 1 }, { x: curr.x, y: curr.y + 1 },
+ { x: curr.x - 1, y: curr.y }, { x: curr.x + 1, y: curr.y }
+ ];
+
+ for (const n of neighbors) {
+ if (!this.isOccupied(n.x, n.y, ignoreTail) && !visited.has(`${n.x},${n.y}`)) {
+ visited.add(`${n.x},${n.y}`);
+ queue.push([...path, n]);
+ }
+ }
+ }
+ return null;
+ }
+
+ getLongestPath(start, target) {
+ let path = this.bfs(start, target, true);
+ if (!path) return null;
+
+ let extended = true;
+ while (extended) {
+ extended = false;
+ for (let i = 0; i < path.length - 1; i++) {
+ const p1 = path[i];
+ const p2 = path[i+1];
+
+ const dirs = [
+ { x: 0, y: -1 }, { x: 0, y: 1 },
+ { x: -1, y: 0 }, { x: 1, y: 0 }
+ ];
+
+ for (const d of dirs) {
+ const n1 = { x: p1.x + d.x, y: p1.y + d.y };
+ const n2 = { x: p2.x + d.x, y: p2.y + d.y };
+
+ if (!this.isOccupied(n1.x, n1.y, true) && !this.isOccupied(n2.x, n2.y, true) &&
+ !path.some(p => p.x === n1.x && p.y === n1.y) &&
+ !path.some(p => p.x === n2.x && p.y === n2.y)) {
+
+ path.splice(i + 1, 0, n1, n2);
+ extended = true;
+ break;
+ }
+ }
+ if (extended) break;
+ }
+ }
+ return path;
+ }
+
+ canReachTail(virtualSnake) {
+ if (virtualSnake.length < 2) return true;
+ const head = virtualSnake[0];
+ const tail = virtualSnake[virtualSnake.length - 1];
+
+ const originalSnake = this.snake;
+ this.snake = virtualSnake;
+ const path = this.bfs(head, tail, true);
+ this.snake = originalSnake;
+ return !!path;
+ }
+
+ update() {
+ if (this.isGameOver || this.isWin) return;
+
+ const head = this.snake[0];
+ const tail = this.snake[this.snake.length - 1];
+
+ // 1. Path to apple
+ let path = this.bfs(head, this.apple, true);
+ let nextMove = null;
+
+ if (path && path.length > 1) {
+ const virtualSnake = [path[1], ...this.snake];
+ if (!(path[1].x === this.apple.x && path[1].y === this.apple.y)) {
+ virtualSnake.pop();
+ }
+
+ if (this.canReachTail(virtualSnake)) {
+ nextMove = path[1];
+ }
+ }
+
+ // 2. Fallback: Longest path to tail
+ if (!nextMove) {
+ const pathToTail = this.getLongestPath(head, tail);
+ if (pathToTail && pathToTail.length > 1) {
+ nextMove = pathToTail[1];
+ } else {
+ nextMove = this.getSurvivalMove();
+ }
+ }
+
+ if (!nextMove) {
+ this.handleGameOver();
+ return;
+ }
+
+ this.snake.unshift(nextMove);
+ if (nextMove.x === this.apple.x && nextMove.y === this.apple.y) {
+ const score = this.snake.length - 3;
+ this.scoreDisplay.textContent = score;
+ if (score > this.bestScore) {
+ this.bestScore = score;
+ this.bestScoreDisplay.textContent = score;
+ localStorage.setItem('bestScore', score);
+ }
+ this.spawnApple();
+ } else {
+ this.snake.pop();
+ }
+ this.frameCount++;
+ }
+
+ getSurvivalMove() {
+ const head = this.snake[0];
+ const neighbors = [
+ { x: head.x, y: head.y - 1 }, { x: head.x, y: head.y + 1 },
+ { x: head.x - 1, y: head.y }, { x: head.x + 1, y: head.y }
+ ].filter(n => !this.isOccupied(n.x, n.y));
+
+ if (neighbors.length === 0) return null;
+ return neighbors.sort((a, b) => this.getReachableArea(b) - this.getReachableArea(a))[0];
+ }
+
+ getReachableArea(pos) {
+ const visited = new Set();
+ const stack = [pos];
+ visited.add(`${pos.x},${pos.y}`);
+ let count = 0;
+
+ while (stack.length > 0) {
+ const curr = stack.pop();
+ count++;
+
+ const neighbors = [
+ { x: curr.x, y: curr.y - 1 }, { x: curr.x, y: curr.y + 1 },
+ { x: curr.x - 1, y: curr.y }, { x: curr.x + 1, y: curr.y }
+ ];
+
+ for (const n of neighbors) {
+ if (!this.isOccupied(n.x, n.y) && !visited.has(`${n.x},${n.y}`)) {
+ visited.add(`${n.x},${n.y}`);
+ stack.push(n);
+ }
+ }
+ }
+ return count;
+ }
+
+ draw() {
+ const { ctx, canvas } = this;
+ const ts = CONFIG.TILE_SIZE;
+
+ ctx.fillStyle = CONFIG.COLORS.BG;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Grid
+ ctx.strokeStyle = CONFIG.COLORS.GRID;
+ ctx.lineWidth = 0.5;
+ for (let i = 0; i <= CONFIG.GRID_SIZE; i++) {
+ ctx.beginPath();
+ ctx.moveTo(i * ts, 0); ctx.lineTo(i * ts, canvas.height); ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(0, i * ts); ctx.lineTo(canvas.width, i * ts); ctx.stroke();
+ }
+
+ // Walls
+ this.walls.forEach(w => {
+ const x = w.x * ts, y = w.y * ts;
+ ctx.fillStyle = CONFIG.COLORS.WALL;
+ this.drawTexturedRect(ctx, x, y, ts, ts, 'wall');
+ });
+
+ // Apple
+ const ax = this.apple.x * ts + ts/2, ay = this.apple.y * ts + ts/2;
+ this.drawApple(ctx, ax, ay, ts);
+
+ // Snake
+ for (let i = this.snake.length - 1; i >= 0; i--) {
+ const s = this.snake[i];
+ const x = s.x * ts, y = s.y * ts;
+ if (i === 0) {
+ this.drawSnakeHead(ctx, x, y, ts);
+ } else if (i === this.snake.length - 1) {
+ this.drawSnakeTail(ctx, x, y, ts, i);
+ } else {
+ this.drawSnakeBody(ctx, x, y, ts, i);
+ }
+ }
+ }
+
+ drawApple(ctx, x, y, ts) {
+ const radius = ts / 2 - 2;
+ ctx.fillStyle = 'rgba(0,0,0,0.3)';
+ ctx.beginPath();
+ ctx.ellipse(x, y + ts/4, radius, radius/2, 0, 0, Math.PI*2);
+ ctx.fill();
+
+ const grad = ctx.createRadialGradient(x - ts/6, y - ts/6, ts/10, x, y, ts/2);
+ grad.addColorStop(0, '#ff4d4d');
+ grad.addColorStop(1, '#8b0000');
+ ctx.fillStyle = grad;
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.fillStyle = 'rgba(255,255,255,0.4)';
+ ctx.beginPath();
+ ctx.arc(x - ts/5, y - ts/5, ts/8, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.fillStyle = '#22c55e';
+ ctx.beginPath();
+ ctx.ellipse(x + 2, y - ts/2.5, ts/4, ts/8, Math.PI/4, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ drawSnakeHead(ctx, x, y, ts) {
+ const next = this.snake[1];
+ let rotation = 0;
+ if (next) {
+ if (next.x < this.snake[0].x) rotation = Math.PI / 2;
+ else if (next.x > this.snake[0].x) rotation = -Math.PI / 2;
+ else if (next.y < this.snake[0].y) rotation = Math.PI;
+ else if (next.y > this.snake[0].y) rotation = 0;
+ }
+
+ ctx.save();
+ ctx.translate(x + ts/2, y + ts/2);
+ ctx.rotate(rotation);
+
+ const grad = ctx.createRadialGradient(0, 0, ts/4, 0, 0, ts/2);
+ grad.addColorStop(0, CONFIG.COLORS.SNAKE_HEAD);
+ grad.addColorStop(1, CONFIG.COLORS.SNAKE_BODY);
+ ctx.fillStyle = grad;
+
+ this.drawRoundedRect(ctx, -ts/2 + 1, -ts/2 + 1, ts - 2, ts + 2, 12);
+
+ // Apple tracking eyes
+ const dx = this.apple.x - this.snake[0].x;
+ const dy = this.apple.y - this.snake[0].y;
+ const angle = Math.atan2(dy, dx) - rotation + Math.PI/2;
+ const eyeOffset = ts * 0.05;
+ const ex = Math.cos(angle) * eyeOffset;
+ const ey = Math.sin(angle) * eyeOffset;
+
+ ctx.fillStyle = '#fff';
+ ctx.beginPath();
+ ctx.arc(-ts*0.22, -ts*0.2, ts*0.14, 0, Math.PI*2);
+ ctx.arc(ts*0.22, -ts*0.2, ts*0.14, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.arc(-ts*0.22 + ex, -ts*0.2 + ey, ts*0.07, 0, Math.PI*2);
+ ctx.arc(ts*0.22 + ex, -ts*0.2 + ey, ts*0.07, 0, Math.PI*2);
+ ctx.fill();
+
+ // Nostrils
+ ctx.fillStyle = 'rgba(0,0,0,0.3)';
+ ctx.beginPath();
+ ctx.arc(-ts*0.1, -ts*0.4, 2, 0, Math.PI*2);
+ ctx.arc(ts*0.1, -ts*0.4, 2, 0, Math.PI*2);
+ ctx.fill();
+
+ if (this.frameCount % 20 < 10) {
+ ctx.strokeStyle = '#ef4444';
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ ctx.moveTo(0, -ts/2);
+ ctx.lineTo(0, -ts/2 - 10);
+ ctx.lineTo(-4, -ts/2 - 14);
+ ctx.moveTo(0, -ts/2 - 10);
+ ctx.lineTo(4, -ts/2 - 14);
+ ctx.stroke();
+ }
+
+ ctx.restore();
+ }
+
+ drawSnakeBody(ctx, x, y, ts, index) {
+ const curr = this.snake[index];
+ const next = this.snake[index - 1];
+ const prev = this.snake[index + 1];
+
+ if (!next || !prev) return;
+
+ ctx.save();
+ ctx.translate(x + ts/2, y + ts/2);
+
+ const grad = ctx.createLinearGradient(-ts/2, -ts/2, ts/2, ts/2);
+ grad.addColorStop(0, CONFIG.COLORS.SNAKE_BODY);
+ grad.addColorStop(1, CONFIG.COLORS.SNAKE_BODY_GRADIENT);
+ ctx.fillStyle = grad;
+
+ const dirNext = { x: next.x - curr.x, y: next.y - curr.y };
+ const dirPrev = { x: prev.x - curr.x, y: prev.y - curr.y };
+
+ const isHorizontal = next.y === prev.y;
+ const isVertical = next.x === prev.x;
+
+ if (isHorizontal) {
+ this.drawRoundedRect(ctx, -ts/2, -ts/2 + 2, ts, ts - 4, 4);
+ } else if (isVertical) {
+ this.drawRoundedRect(ctx, -ts/2 + 2, -ts/2, ts - 4, ts, 4);
+ } else {
+ // Corner - smooth arc connection
+ ctx.beginPath();
+ if ((dirNext.x === 1 && dirPrev.y === 1) || (dirNext.y === 1 && dirPrev.x === 1)) {
+ // Bottom-right
+ ctx.arc(ts/2, ts/2, ts - 2, Math.PI, Math.PI * 1.5);
+ ctx.lineTo(ts/2, ts/2);
+ } else if ((dirNext.x === -1 && dirPrev.y === 1) || (dirNext.y === 1 && dirPrev.x === -1)) {
+ // Bottom-left
+ ctx.arc(-ts/2, ts/2, ts - 2, Math.PI * 1.5, 0);
+ ctx.lineTo(-ts/2, ts/2);
+ } else if ((dirNext.x === 1 && dirPrev.y === -1) || (dirNext.y === -1 && dirPrev.x === 1)) {
+ // Top-right
+ ctx.arc(ts/2, -ts/2, ts - 2, Math.PI * 0.5, Math.PI);
+ ctx.lineTo(ts/2, -ts/2);
+ } else {
+ // Top-left
+ ctx.arc(-ts/2, -ts/2, ts - 2, 0, Math.PI * 0.5);
+ ctx.lineTo(-ts/2, -ts/2);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ // Animated scales
+ if ((index + Math.floor(this.frameCount/2)) % 6 === 0) {
+ ctx.fillStyle = 'rgba(255,255,255,0.12)';
+ ctx.beginPath();
+ ctx.arc(0, 0, ts/5, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ ctx.restore();
+ }
+
+ drawSnakeTail(ctx, x, y, ts, index) {
+ const curr = this.snake[index];
+ const prev = this.snake[index - 1];
+ if (!prev) return;
+
+ let rotation = 0;
+ if (prev.x < curr.x) rotation = Math.PI / 2;
+ else if (prev.x > curr.x) rotation = -Math.PI / 2;
+ else if (prev.y < curr.y) rotation = Math.PI;
+ else if (prev.y > curr.y) rotation = 0;
+
+ ctx.save();
+ ctx.translate(x + ts/2, y + ts/2);
+ ctx.rotate(rotation);
+
+ const grad = ctx.createLinearGradient(-ts/2, -ts/2, ts/2, ts/2);
+ grad.addColorStop(0, CONFIG.COLORS.SNAKE_BODY);
+ grad.addColorStop(1, CONFIG.COLORS.SNAKE_BODY_GRADIENT);
+ ctx.fillStyle = grad;
+
+ // Tapered tail
+ ctx.beginPath();
+ ctx.moveTo(-ts/2 + 4, -ts/2);
+ ctx.lineTo(ts/2 - 4, -ts/2);
+ ctx.quadraticCurveTo(0, ts/2, 0, ts/2 + 6);
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.restore();
+ }
+
+ drawTexturedRect(ctx, x, y, w, h, type) {
+ if (type === 'wall') {
+ const grad = ctx.createLinearGradient(x, y, x + w, y + h);
+ grad.addColorStop(0, '#475569');
+ grad.addColorStop(1, '#1e293b');
+ ctx.fillStyle = grad;
+ this.drawRoundedRect(ctx, x + 2, y + 2, w - 4, h - 4, 6);
+
+ ctx.strokeStyle = 'rgba(255,255,255,0.1)';
+ ctx.strokeRect(x + 6, y + 6, w - 12, h - 12);
+ }
+ }
+
+ drawRoundedRect(ctx, x, y, w, h, r) {
+ ctx.beginPath();
+ if (ctx.roundRect) {
+ ctx.roundRect(x, y, w, h, r);
+ } else {
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
+ ctx.lineTo(x + r, y + h);
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
+ ctx.lineTo(x, y + r);
+ ctx.quadraticCurveTo(x, y, x + r, y);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ gameLoop(timestamp) {
+ const speed = parseInt(this.speedRange.value);
+ const interval = 1000 / speed;
+
+ if (timestamp - this.lastFrameTime >= interval) {
+ this.update();
+ this.draw();
+ this.lastFrameTime = timestamp;
+ }
+
+ requestAnimationFrame((t) => this.gameLoop(t));
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => new Game());
\ No newline at end of file
diff --git a/assets/pasted-20260303-004347-83d8b12e.jpg b/assets/pasted-20260303-004347-83d8b12e.jpg
new file mode 100644
index 0000000..7f9cca0
Binary files /dev/null and b/assets/pasted-20260303-004347-83d8b12e.jpg differ
diff --git a/assets/pasted-20260303-004846-1f6b05b6.jpg b/assets/pasted-20260303-004846-1f6b05b6.jpg
new file mode 100644
index 0000000..32462b8
Binary files /dev/null and b/assets/pasted-20260303-004846-1f6b05b6.jpg differ
diff --git a/assets/pasted-20260303-005216-02bf6ec0.jpg b/assets/pasted-20260303-005216-02bf6ec0.jpg
new file mode 100644
index 0000000..a42a752
Binary files /dev/null and b/assets/pasted-20260303-005216-02bf6ec0.jpg differ
diff --git a/assets/pasted-20260303-005641-21839e8c.jpg b/assets/pasted-20260303-005641-21839e8c.jpg
new file mode 100644
index 0000000..76ed4a7
Binary files /dev/null and b/assets/pasted-20260303-005641-21839e8c.jpg differ
diff --git a/assets/pasted-20260303-011135-6f2f6c05.jpg b/assets/pasted-20260303-011135-6f2f6c05.jpg
new file mode 100644
index 0000000..0cd6a78
Binary files /dev/null and b/assets/pasted-20260303-011135-6f2f6c05.jpg differ
diff --git a/index.php b/index.php
index 7205f3d..e021ecf 100644
--- a/index.php
+++ b/index.php
@@ -6,145 +6,118 @@ declare(strict_types=1);
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
+
+// SEO Meta Tags
+$title = "Auto-Snake: The Infinite Loop";
+$description = "Watch an AI-powered snake navigate mazes and eat apples in an infinite, perfect loop. A mesmerizing demonstration of pathfinding.";
+$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ = htmlspecialchars($title) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
+
+
Auto-Snake AI Bot
+
Smart, smooth, and never-ending.
+
+
+
+
+
+
+
+
+
VICTORY!
+
The grid is full. Restarting...
+
+
+
+
+
+
-