Merge pull request 'main' (#4) from React-Group/react-typescript-test:main into main
Reviewed-on: https://interstellardevelopment.org/code/code/Patrick_Pluto/react-typescript-test/pulls/4
This commit is contained in:
commit
a6f1fcccd5
4 changed files with 287 additions and 27 deletions
|
@ -1,4 +1,4 @@
|
|||
/* Luca.css */
|
||||
/* Don't mess with that styling please */
|
||||
.programmer-space {
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
|
@ -10,22 +10,108 @@
|
|||
background-color: #4a90e2; /* Blue */
|
||||
}
|
||||
|
||||
.click-button {
|
||||
padding: 10px 20px;
|
||||
margin-top: 10px;
|
||||
/* Snake game container */
|
||||
.snake-game-container {
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack score and game area vertically */
|
||||
align-items: center; /* Center content horizontally */
|
||||
background-color: #f5f5f5; /* Light background for contrast */
|
||||
border-radius: 12px; /* Rounded corners for a modern touch */
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Snake game area */
|
||||
.snake-game {
|
||||
position: relative;
|
||||
width: 400px; /* 20 cells * 20px */
|
||||
height: 400px; /* 20 cells * 20px */
|
||||
border: 3px solid #4a90e2; /* Accent color border */
|
||||
background-color: #222; /* Dark background for the game area */
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); /* Enhanced shadow for better depth */
|
||||
border-radius: 8px; /* Rounded corners for the game board */
|
||||
margin-top: 20px; /* Space between score and game */
|
||||
}
|
||||
|
||||
/* Snake segments */
|
||||
.snake-segment {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #4caf50; /* Modern green */
|
||||
border-radius: 6px; /* Rounded corners for the snake segments */
|
||||
}
|
||||
|
||||
/* Food */
|
||||
.food {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #f44336; /* Modern red */
|
||||
border-radius: 6px; /* Rounded corners for the food */
|
||||
}
|
||||
|
||||
/* Game Over message */
|
||||
.game-over {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: rgba(0, 0, 0, 0.8); /* Slightly darker overlay for better readability */
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
font-size: 22px;
|
||||
text-align: center;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); /* Enhanced shadow for visibility */
|
||||
}
|
||||
|
||||
/* Score display */
|
||||
.score {
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
color: #4a90e2; /* Accent color */
|
||||
}
|
||||
|
||||
/* Reload Game Button */
|
||||
.button {
|
||||
background-color: #4CAF50; /* Green background */
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
color: #4a90e2;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.click-button:hover {
|
||||
background-color: #e3e3e3;
|
||||
}
|
||||
|
||||
.lorem-ipsum {
|
||||
color: white;
|
||||
padding: 14px 28px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #388E3C; /* Darker green for hover effect */
|
||||
}
|
||||
|
||||
/* Game Explanation Section */
|
||||
.game-explanation {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background-color: #fff; /* Light background for readability */
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.game-explanation h3 {
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5rem;
|
||||
color: #4a90e2; /* Accent color */
|
||||
}
|
||||
|
||||
.game-explanation p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.game-explanation ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,205 @@
|
|||
// Luca.tsx
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import './Luca.css'; // Import specific styles for Luca
|
||||
|
||||
const GRID_WIDTH = 20;
|
||||
const GRID_HEIGHT = 20;
|
||||
const CELL_SIZE = 20;
|
||||
|
||||
const Luca = () => {
|
||||
// State to keep track of the number of clicks
|
||||
// State for tracking clicks
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
|
||||
// Function to handle button click
|
||||
const handleClick = () => {
|
||||
setClickCount(clickCount + 1);
|
||||
// State for Snake game
|
||||
const [snake, setSnake] = useState([{ x: 5, y: 5 }]);
|
||||
const [food, setFood] = useState({ x: 10, y: 10 });
|
||||
const [direction, setDirection] = useState('RIGHT');
|
||||
const [gameOver, setGameOver] = useState(false);
|
||||
const [gameStarted, setGameStarted] = useState(false);
|
||||
const [score, setScore] = useState(0);
|
||||
|
||||
// Resets the game to its initial state
|
||||
const resetGame = () => {
|
||||
setSnake([{ x: 5, y: 5 }]);
|
||||
setFood(generateRandomFood());
|
||||
setDirection('RIGHT');
|
||||
setScore(0);
|
||||
setGameOver(false);
|
||||
};
|
||||
|
||||
// Generates a random position for the food
|
||||
const generateRandomFood = () => ({
|
||||
x: Math.floor(Math.random() * GRID_WIDTH),
|
||||
y: Math.floor(Math.random() * GRID_HEIGHT),
|
||||
});
|
||||
|
||||
// Moves the snake based on the current direction
|
||||
const moveSnake = useCallback(() => {
|
||||
if (gameOver) return;
|
||||
|
||||
let newSnake = [...snake];
|
||||
let head = { ...newSnake[0] };
|
||||
|
||||
// Update head position based on direction
|
||||
switch (direction) {
|
||||
case 'UP': head.y -= 1; break;
|
||||
case 'DOWN': head.y += 1; break;
|
||||
case 'LEFT': head.x -= 1; break;
|
||||
case 'RIGHT': head.x += 1; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Check if the new head position is the food
|
||||
const hasEatenFood = head.x === food.x && head.y === food.y;
|
||||
|
||||
newSnake = [head, ...newSnake];
|
||||
|
||||
if (hasEatenFood) {
|
||||
setFood(generateRandomFood());
|
||||
setScore(prevScore => prevScore + 1);
|
||||
} else {
|
||||
newSnake.pop();
|
||||
}
|
||||
|
||||
// Check for collisions
|
||||
if (
|
||||
head.x < 0 || head.x >= GRID_WIDTH ||
|
||||
head.y < 0 || head.y >= GRID_HEIGHT ||
|
||||
newSnake.slice(1).some(seg => seg.x === head.x && seg.y === head.y)
|
||||
) {
|
||||
setGameOver(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setSnake(newSnake);
|
||||
}, [direction, snake, food, gameOver]);
|
||||
|
||||
// Set up interval to move the snake if the game is started
|
||||
useEffect(() => {
|
||||
if (gameStarted && !gameOver) {
|
||||
const interval = setInterval(moveSnake, 200);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [moveSnake, gameStarted, gameOver]);
|
||||
|
||||
// Handle key presses to change the snake's direction
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(e.code)) {
|
||||
e.preventDefault(); // Prevent default behavior for movement keys
|
||||
}
|
||||
|
||||
switch (e.code) {
|
||||
case 'KeyW': if (direction !== 'DOWN') setDirection('UP'); break;
|
||||
case 'KeyS': if (direction !== 'UP') setDirection('DOWN'); break;
|
||||
case 'KeyA': if (direction !== 'RIGHT') setDirection('LEFT'); break;
|
||||
case 'KeyD': if (direction !== 'LEFT') setDirection('RIGHT'); break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [direction]);
|
||||
|
||||
return (
|
||||
<div className="programmer-space luca">
|
||||
<h2>Luca's Space</h2>
|
||||
<p>Welcome, Luca! This is your programming space.</p>
|
||||
|
||||
{/* Button to count clicks */}
|
||||
<button onClick={handleClick} className="click-button">
|
||||
<button onClick={() => setClickCount(clickCount + 1)} className="button">
|
||||
Click me
|
||||
</button>
|
||||
<p>You've clicked the button {clickCount} times!</p>
|
||||
|
||||
{/* Lorem Ipsum text */}
|
||||
<div className="lorem-ipsum">
|
||||
{/* Start Game Button */}
|
||||
{!gameStarted && !gameOver && (
|
||||
<button onClick={() => setGameStarted(true)} className="button" style={{ marginTop: '20px' }}>
|
||||
Start Game
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Snake game container */}
|
||||
{gameStarted && (
|
||||
<div className="snake-game-container">
|
||||
{/* Score display */}
|
||||
<div className="score">
|
||||
<p>Score: {score}</p>
|
||||
</div>
|
||||
|
||||
{/* Snake game */}
|
||||
<div className="snake-game">
|
||||
{snake.map((segment, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="snake-segment"
|
||||
style={{
|
||||
left: segment.x * CELL_SIZE,
|
||||
top: segment.y * CELL_SIZE,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="food"
|
||||
style={{
|
||||
left: food.x * CELL_SIZE,
|
||||
top: food.y * CELL_SIZE,
|
||||
}}
|
||||
/>
|
||||
{gameOver && (
|
||||
<div className="game-over">
|
||||
Game Over
|
||||
<button onClick={resetGame} className="button" style={{ marginTop: '20px' }}>
|
||||
Reload Game
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Explanation section */}
|
||||
<div className="game-explanation">
|
||||
<h3>Game Explanation</h3>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
<strong>Disclaimer:</strong> The game controls can be sensitive to rapid direction changes. If you press two directional keys in quick succession (e.g., moving right and then quickly pressing up or left), the snake may change direction instantly. This can sometimes lead to unintended behavior, including the possibility of the snake colliding with itself or the walls and causing an immediate game over. Please be mindful of this issue while playing.
|
||||
</p>
|
||||
<p>
|
||||
<strong>How the Game Works:</strong>
|
||||
<br />
|
||||
- Use the <strong>W</strong> key to move the snake up, the <strong>S</strong> key to move it down, the{' '}
|
||||
<strong>A</strong> key to move it left, and the <strong>D</strong> key to move it right.
|
||||
<br />
|
||||
- The snake will grow longer each time it eats the red food that appears on the game board.
|
||||
<br />
|
||||
- The game ends if the snake runs into the walls of the game area or into itself.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Code Structure:</strong>
|
||||
<br />
|
||||
- <strong>State Management:</strong>
|
||||
<br />
|
||||
- <strong>`useState`:</strong> Manages the snake's position, food position, direction, score, and game status.
|
||||
<br />
|
||||
- <strong>Movement Logic:</strong>
|
||||
<br />
|
||||
- <strong>`moveSnake`:</strong> Updates the snake's position, checks for food consumption, and handles collisions.
|
||||
<br />
|
||||
- <strong>Collision Detection:</strong>
|
||||
<br />
|
||||
- Within <strong>`moveSnake`</strong>, checks if the snake's head collides with walls or itself.
|
||||
<br />
|
||||
- <strong>Event Handling:</strong>
|
||||
<br />
|
||||
- <strong>`handleKeyDown`:</strong> Listens for key presses to change the snake's direction and prevents reversing.
|
||||
<br />
|
||||
- <strong>Game Start and Restart:</strong>
|
||||
<br />
|
||||
- <strong>Start Game Button:</strong> Starts the game loop.
|
||||
<br />
|
||||
- <strong>Game Over and Restart Button:</strong> Resets the game state for a new game.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Patrick.css */
|
||||
/* Don't mess with that styling please */
|
||||
.programmer-space {
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Yasin.css */
|
||||
/* Don't mess with that styling please */
|
||||
.programmer-space {
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
|
|
Loading…
Reference in a new issue