awacke1's picture
Update app.py
18b24cd verified
raw
history blame
8.28 kB
import streamlit as st
# Set wide layout
st.set_page_config(layout="wide", page_title="Galaxian Snake Game", initial_sidebar_state="expanded")
# Sidebar instructions
st.sidebar.markdown("## Galaxian Snake Game Controls")
st.sidebar.markdown(
"""
- **Controls:**
- **Q:** Move Up–Left
- **W:** Move Up
- **E:** Move Up–Right
- **A:** Move Left
- **S:** (Center) Continue current direction
- **D:** Move Right
- **Z:** Move Down–Left
- **X:** Move Down
- **C:** Move Down–Right
- **Rules:**
- The snake moves on a grid and grows when it eats the alien food (👾).
- You must avoid colliding with the walls or yourself.
- Press **R** to restart after a game over.
Have fun and enjoy the retro Galaxian vibe!
"""
)
# p5.js snake game embedded as an HTML string
html_code = r"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Galaxian Snake Game</title>
<!-- Include p5.js (with WEBGL disabled because we use 2D grid) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
<style>
body { margin: 0; padding: 0; overflow: hidden; background: black; }
canvas { display: block; margin: 0 auto; }
</style>
</head>
<body>
<script>
/*
Galaxian Snake Game
- The snake moves on a grid.
- Controls: Q, W, E, A, S, D, Z, X, C (with S meaning “no turn”).
- Food is represented as a retro alien (👾).
- A starfield background evokes classic Galaxian style.
*/
// Game configuration
let tileSize = 20;
let cols, rows;
let snake;
let direction; // {x: dx, y: dy} in grid units
let food;
let score;
let gameOver;
let moveCounter = 0;
let moveDelay = 8; // lower => faster snake
// Starfield for background
let stars = [];
const numStars = 150;
function setup() {
createCanvas(windowWidth, windowHeight);
frameRate(30);
cols = floor(width / tileSize);
rows = floor(height / tileSize);
resetGame();
// Create starfield
for (let i = 0; i < numStars; i++) {
stars.push({
x: random(width),
y: random(height),
speed: random(0.5, 2),
size: random(1, 3)
});
}
}
function resetGame() {
snake = [];
// Start snake in the middle with length 3
let startX = floor(cols / 2);
let startY = floor(rows / 2);
snake.push({x: startX, y: startY});
snake.push({x: startX - 1, y: startY});
snake.push({x: startX - 2, y: startY});
// Initial direction: moving right
direction = {x: 1, y: 0};
placeFood();
score = 0;
gameOver = false;
moveCounter = 0;
}
// Place food (alien) at a random location not occupied by the snake
function placeFood() {
let valid = false;
let newFood;
while (!valid) {
newFood = {
x: floor(random(cols)),
y: floor(random(rows))
};
valid = true;
for (let seg of snake) {
if (seg.x === newFood.x && seg.y === newFood.y) {
valid = false;
break;
}
}
}
food = newFood;
}
function draw() {
// Draw Galaxian–style starfield background
background(0);
noStroke();
fill(255);
for (let s of stars) {
ellipse(s.x, s.y, s.size);
s.y += s.speed;
if (s.y > height) {
s.y = 0;
s.x = random(width);
}
}
// Draw grid boundaries (optional: for visual style)
// stroke(40);
// for (let i = 0; i < cols; i++) {
// line(i * tileSize, 0, i * tileSize, height);
// }
// for (let j = 0; j < rows; j++) {
// line(0, j * tileSize, width, j * tileSize);
// }
// Game update if not over
if (!gameOver) {
moveCounter++;
if (moveCounter >= moveDelay) {
updateSnake();
moveCounter = 0;
}
} else {
// Display Game Over message
textAlign(CENTER, CENTER);
textSize(64);
fill(255, 50, 50);
text("GAME OVER", width/2, height/2);
textSize(32);
text("Score: " + score + " (Press R to Restart)", width/2, height/2 + 50);
}
// Draw food as a retro alien emoji
textSize(tileSize);
textAlign(CENTER, CENTER);
text("👾", food.x * tileSize + tileSize/2, food.y * tileSize + tileSize/2);
// Draw snake as neon blocks
for (let i = 0; i < snake.length; i++) {
let seg = snake[i];
if (i == 0) {
fill(0, 255, 0); // head bright green
} else {
fill(0, 180, 0);
}
rect(seg.x * tileSize, seg.y * tileSize, tileSize, tileSize);
}
// Draw score in top-left corner
fill(255);
textSize(16);
textAlign(LEFT, TOP);
text("Score: " + score, 10, 10);
}
// Update snake position and check collisions
function updateSnake() {
// Determine new head position
let head = snake[0];
let newHead = { x: head.x + direction.x, y: head.y + direction.y };
// Check wall collisions
if (newHead.x < 0 || newHead.x >= cols || newHead.y < 0 || newHead.y >= rows) {
gameOver = true;
return;
}
// Check self-collision
for (let seg of snake) {
if (seg.x === newHead.x && seg.y === newHead.y) {
gameOver = true;
return;
}
}
// Add new head
snake.unshift(newHead);
// Check food collision
if (newHead.x === food.x && newHead.y === food.y) {
score += 10;
placeFood();
// (Do not remove tail: snake grows)
} else {
// Remove tail (snake moves forward)
snake.pop();
}
}
// Map key presses to direction changes using the 3x3 layout:
// Q: (-1,-1), W: (0,-1), E: (1,-1)
// A: (-1, 0), S: (0,0) [no change], D: (1,0)
// Z: (-1, 1), X: (0,1), C: (1,1)
function keyPressed() {
if (gameOver && key.toLowerCase() === 'r') {
resetGame();
return;
}
// Only process if game is not over
if (gameOver) return;
let newDir = null;
let k = key.toLowerCase();
if (k === 'q') newDir = {x: -1, y: -1};
else if (k === 'w') newDir = {x: 0, y: -1};
else if (k === 'e') newDir = {x: 1, y: -1};
else if (k === 'a') newDir = {x: -1, y: 0};
else if (k === 's') newDir = {x: 0, y: 0}; // center: no change
else if (k === 'd') newDir = {x: 1, y: 0};
else if (k === 'z') newDir = {x: -1, y: 1};
else if (k === 'x') newDir = {x: 0, y: 1};
else if (k === 'c') newDir = {x: 1, y: 1};
// If a valid key was pressed and newDir is not the "no-turn" (0,0) command,
// update direction. Also disallow reversing (only if snake length > 1).
if (newDir) {
if (newDir.x === 0 && newDir.y === 0) {
// "S" was pressed: no turn, so do nothing.
return;
}
if (snake.length > 1) {
// Prevent reversing (i.e., newDir should not be exactly opposite of current)
if (newDir.x === -direction.x && newDir.y === -direction.y) {
return;
}
}
direction = newDir;
}
}
// Resize canvas on window resize
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
cols = floor(width / tileSize);
rows = floor(height / tileSize);
}
</script>
</body>
</html>
"""
st.components.v1.html(html_code, height=700, scrolling=False)