awacke1 commited on
Commit
18b24cd
·
verified ·
1 Parent(s): 04d3523

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -133
app.py CHANGED
@@ -1,153 +1,272 @@
1
  import streamlit as st
2
 
3
- def create_app():
4
- st.title("3D Python Snake Matrix Code ❤️🐍")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- html_code = r"""
7
- <!DOCTYPE html>
8
- <html>
9
- <head>
10
- <meta charset="UTF-8">
11
- <title>3D Python Snake Matrix Code ❤️</title>
12
- <!-- Load p5.js (with WEBGL support) from a CDN -->
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
14
- <style>
15
- /* Remove margins and set a black background */
16
- body { margin: 0; padding: 0; overflow: hidden; background: black; }
17
- #sketch-container { display: flex; justify-content: center; align-items: center; }
18
- </style>
19
- </head>
20
- <body>
21
- <div id="sketch-container"></div>
22
- <script>
23
- // ❤️ 3D Python Snake Matrix Code with Heart Emojis ❤️
24
- // Global variables for the Matrix rain overlay
25
- let matrixColumns = [];
26
- let fontSize = 16;
27
- let numColumns;
28
-
29
- // Global variables for the snake
30
- let snake = []; // Array to hold 3D positions of the snake segments
31
- const maxSnakeLength = 50; // Maximum number of segments
32
- let t = 0; // Time variable for snake movement
33
-
34
- function setup() {
35
- // Create a WEBGL canvas that fills the window and attach it to our container
36
- let canvas = createCanvas(windowWidth, windowHeight, WEBGL);
37
- canvas.parent('sketch-container');
38
- textFont('monospace');
39
- textSize(fontSize);
40
- noStroke();
41
-
42
- // Setup Matrix rain columns (we work in 2D for the overlay)
43
- numColumns = floor(width / fontSize);
44
- for (let i = 0; i < numColumns; i++) {
45
- matrixColumns[i] = random(-1000, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
-
48
- // Initialize the snake: start with all segments at the center (0,0,0)
49
- for (let i = 0; i < maxSnakeLength; i++) {
50
- snake.push(createVector(0, 0, 0));
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
  }
53
 
54
- function draw() {
55
- background(0);
56
-
57
- // Set up some lights for 3D shading
58
- ambientLight(50);
59
- directionalLight(255, 255, 255, 0, -1, 0);
60
-
61
- // Rotate the entire 3D scene slowly for an extra cool effect
62
- rotateY(frameCount * 0.005);
63
- rotateX(frameCount * 0.003);
64
-
65
- // Update and draw our snake in 3D
66
- updateSnake();
67
- drawSnake();
68
-
69
- // Draw the Matrix rain overlay on top (in 2D)
70
- drawMatrixRain();
71
- }
72
 
73
- // Update the snake's position using Perlin noise for a fluid, organic motion
74
- function updateSnake() {
75
- t += 0.01;
76
- // Compute new head position using noise to vary x, y, and z independently
77
- let x = map(noise(t, 0), 0, 1, -width/3, width/3);
78
- let y = map(noise(t, 100), 0, 1, -height/3, height/3);
79
- let z = map(noise(t, 200), 0, 1, -300, 300);
80
- let newHead = createVector(x, y, z);
81
- snake.push(newHead);
82
- // Maintain a fixed length by removing the tail segment
83
- if (snake.length > maxSnakeLength) {
84
- snake.shift();
85
  }
 
 
 
 
 
 
 
 
86
  }
87
 
88
- // Draw the snake: each segment is rendered as a Matrix-style character,
89
- // and every 10th segment is rendered as a heart emoji for extra love ❤️.
90
- function drawSnake() {
91
- for (let i = 0; i < snake.length; i++) {
92
- let pos = snake[i];
93
- push();
94
- translate(pos.x, pos.y, pos.z);
95
- // Every 10th segment, show a heart emoji instead of a random character
96
- if (i % 10 === 0) {
97
- textSize(fontSize + 4);
98
- fill(255, 0, 100);
99
- text("❤️", 0, 0);
100
- } else {
101
- fill(0, 255, 70);
102
- textSize(fontSize);
103
- let matrixChars = "01ABCDEFGHIJKLMNOPQRSTUVWXYZ";
104
- let char = matrixChars.charAt(floor(random(matrixChars.length)));
105
- text(char, 0, 0);
106
- }
107
- pop();
108
  }
 
109
  }
110
 
111
- // Draw a classic Matrix rain effect as a 2D overlay on top of the 3D scene
112
- function drawMatrixRain() {
113
- push();
114
- // Reset to 2D drawing mode
115
- resetMatrix();
116
- // Shift the origin to the top-left corner
117
- translate(0, 0);
118
- textSize(fontSize);
119
- fill(0, 255, 70);
120
- for (let i = 0; i < numColumns; i++) {
121
- let x = i * fontSize;
122
- let y = matrixColumns[i];
123
- let matrixChars = "01ABCDEFGHIJKLMNOPQRSTUVWXYZ";
124
- let char = matrixChars.charAt(floor(random(matrixChars.length)));
125
- text(char, x, y);
126
- matrixColumns[i] += fontSize;
127
- // If the drop goes off the screen, reset it to the top with a random offset
128
- if (matrixColumns[i] > height) {
129
- matrixColumns[i] = random(-100, 0);
130
- }
 
 
 
 
131
  }
132
- pop();
133
  }
134
 
135
- // When the window is resized, adjust the canvas and recalc matrix columns
136
- function windowResized() {
137
- resizeCanvas(windowWidth, windowHeight);
138
- numColumns = floor(width / fontSize);
139
- matrixColumns = [];
140
- for (let i = 0; i < numColumns; i++) {
141
- matrixColumns[i] = random(-1000, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
 
143
  }
144
- </script>
145
- </body>
146
- </html>
147
- """
148
-
149
- # Embed the HTML (with the p5.js sketch) into the Streamlit app.
150
- st.components.v1.html(html_code, height=700, scrolling=True)
 
 
 
 
 
151
 
152
- if __name__ == '__main__':
153
- create_app()
 
1
  import streamlit as st
2
 
3
+ # Set wide layout
4
+ st.set_page_config(layout="wide", page_title="Galaxian Snake Game", initial_sidebar_state="expanded")
5
+
6
+ # Sidebar instructions
7
+ st.sidebar.markdown("## Galaxian Snake Game Controls")
8
+ st.sidebar.markdown(
9
+ """
10
+ - **Controls:**
11
+ - **Q:** Move Up–Left
12
+ - **W:** Move Up
13
+ - **E:** Move Up–Right
14
+ - **A:** Move Left
15
+ - **S:** (Center) Continue current direction
16
+ - **D:** Move Right
17
+ - **Z:** Move Down–Left
18
+ - **X:** Move Down
19
+ - **C:** Move Down–Right
20
+
21
+ - **Rules:**
22
+ - The snake moves on a grid and grows when it eats the alien food (👾).
23
+ - You must avoid colliding with the walls or yourself.
24
+ - Press **R** to restart after a game over.
25
 
26
+ Have fun and enjoy the retro Galaxian vibe!
27
+ """
28
+ )
29
+
30
+ # p5.js snake game embedded as an HTML string
31
+ html_code = r"""
32
+ <!DOCTYPE html>
33
+ <html>
34
+ <head>
35
+ <meta charset="UTF-8">
36
+ <title>Galaxian Snake Game</title>
37
+ <!-- Include p5.js (with WEBGL disabled because we use 2D grid) -->
38
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
39
+ <style>
40
+ body { margin: 0; padding: 0; overflow: hidden; background: black; }
41
+ canvas { display: block; margin: 0 auto; }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <script>
46
+ /*
47
+ Galaxian Snake Game
48
+ - The snake moves on a grid.
49
+ - Controls: Q, W, E, A, S, D, Z, X, C (with S meaning “no turn”).
50
+ - Food is represented as a retro alien (👾).
51
+ - A starfield background evokes classic Galaxian style.
52
+ */
53
+
54
+ // Game configuration
55
+ let tileSize = 20;
56
+ let cols, rows;
57
+ let snake;
58
+ let direction; // {x: dx, y: dy} in grid units
59
+ let food;
60
+ let score;
61
+ let gameOver;
62
+ let moveCounter = 0;
63
+ let moveDelay = 8; // lower => faster snake
64
+
65
+ // Starfield for background
66
+ let stars = [];
67
+ const numStars = 150;
68
+
69
+ function setup() {
70
+ createCanvas(windowWidth, windowHeight);
71
+ frameRate(30);
72
+ cols = floor(width / tileSize);
73
+ rows = floor(height / tileSize);
74
+ resetGame();
75
+ // Create starfield
76
+ for (let i = 0; i < numStars; i++) {
77
+ stars.push({
78
+ x: random(width),
79
+ y: random(height),
80
+ speed: random(0.5, 2),
81
+ size: random(1, 3)
82
+ });
83
+ }
84
+ }
85
+
86
+ function resetGame() {
87
+ snake = [];
88
+ // Start snake in the middle with length 3
89
+ let startX = floor(cols / 2);
90
+ let startY = floor(rows / 2);
91
+ snake.push({x: startX, y: startY});
92
+ snake.push({x: startX - 1, y: startY});
93
+ snake.push({x: startX - 2, y: startY});
94
+ // Initial direction: moving right
95
+ direction = {x: 1, y: 0};
96
+ placeFood();
97
+ score = 0;
98
+ gameOver = false;
99
+ moveCounter = 0;
100
+ }
101
+
102
+ // Place food (alien) at a random location not occupied by the snake
103
+ function placeFood() {
104
+ let valid = false;
105
+ let newFood;
106
+ while (!valid) {
107
+ newFood = {
108
+ x: floor(random(cols)),
109
+ y: floor(random(rows))
110
+ };
111
+ valid = true;
112
+ for (let seg of snake) {
113
+ if (seg.x === newFood.x && seg.y === newFood.y) {
114
+ valid = false;
115
+ break;
116
+ }
117
  }
118
+ }
119
+ food = newFood;
120
+ }
121
+
122
+ function draw() {
123
+ // Draw Galaxian–style starfield background
124
+ background(0);
125
+ noStroke();
126
+ fill(255);
127
+ for (let s of stars) {
128
+ ellipse(s.x, s.y, s.size);
129
+ s.y += s.speed;
130
+ if (s.y > height) {
131
+ s.y = 0;
132
+ s.x = random(width);
133
  }
134
  }
135
 
136
+ // Draw grid boundaries (optional: for visual style)
137
+ // stroke(40);
138
+ // for (let i = 0; i < cols; i++) {
139
+ // line(i * tileSize, 0, i * tileSize, height);
140
+ // }
141
+ // for (let j = 0; j < rows; j++) {
142
+ // line(0, j * tileSize, width, j * tileSize);
143
+ // }
 
 
 
 
 
 
 
 
 
 
144
 
145
+ // Game update if not over
146
+ if (!gameOver) {
147
+ moveCounter++;
148
+ if (moveCounter >= moveDelay) {
149
+ updateSnake();
150
+ moveCounter = 0;
 
 
 
 
 
 
151
  }
152
+ } else {
153
+ // Display Game Over message
154
+ textAlign(CENTER, CENTER);
155
+ textSize(64);
156
+ fill(255, 50, 50);
157
+ text("GAME OVER", width/2, height/2);
158
+ textSize(32);
159
+ text("Score: " + score + " (Press R to Restart)", width/2, height/2 + 50);
160
  }
161
 
162
+ // Draw food as a retro alien emoji
163
+ textSize(tileSize);
164
+ textAlign(CENTER, CENTER);
165
+ text("👾", food.x * tileSize + tileSize/2, food.y * tileSize + tileSize/2);
166
+
167
+ // Draw snake as neon blocks
168
+ for (let i = 0; i < snake.length; i++) {
169
+ let seg = snake[i];
170
+ if (i == 0) {
171
+ fill(0, 255, 0); // head bright green
172
+ } else {
173
+ fill(0, 180, 0);
 
 
 
 
 
 
 
 
174
  }
175
+ rect(seg.x * tileSize, seg.y * tileSize, tileSize, tileSize);
176
  }
177
 
178
+ // Draw score in top-left corner
179
+ fill(255);
180
+ textSize(16);
181
+ textAlign(LEFT, TOP);
182
+ text("Score: " + score, 10, 10);
183
+ }
184
+
185
+ // Update snake position and check collisions
186
+ function updateSnake() {
187
+ // Determine new head position
188
+ let head = snake[0];
189
+ let newHead = { x: head.x + direction.x, y: head.y + direction.y };
190
+
191
+ // Check wall collisions
192
+ if (newHead.x < 0 || newHead.x >= cols || newHead.y < 0 || newHead.y >= rows) {
193
+ gameOver = true;
194
+ return;
195
+ }
196
+
197
+ // Check self-collision
198
+ for (let seg of snake) {
199
+ if (seg.x === newHead.x && seg.y === newHead.y) {
200
+ gameOver = true;
201
+ return;
202
  }
 
203
  }
204
 
205
+ // Add new head
206
+ snake.unshift(newHead);
207
+
208
+ // Check food collision
209
+ if (newHead.x === food.x && newHead.y === food.y) {
210
+ score += 10;
211
+ placeFood();
212
+ // (Do not remove tail: snake grows)
213
+ } else {
214
+ // Remove tail (snake moves forward)
215
+ snake.pop();
216
+ }
217
+ }
218
+
219
+ // Map key presses to direction changes using the 3x3 layout:
220
+ // Q: (-1,-1), W: (0,-1), E: (1,-1)
221
+ // A: (-1, 0), S: (0,0) [no change], D: (1,0)
222
+ // Z: (-1, 1), X: (0,1), C: (1,1)
223
+ function keyPressed() {
224
+ if (gameOver && key.toLowerCase() === 'r') {
225
+ resetGame();
226
+ return;
227
+ }
228
+
229
+ // Only process if game is not over
230
+ if (gameOver) return;
231
+
232
+ let newDir = null;
233
+ let k = key.toLowerCase();
234
+ if (k === 'q') newDir = {x: -1, y: -1};
235
+ else if (k === 'w') newDir = {x: 0, y: -1};
236
+ else if (k === 'e') newDir = {x: 1, y: -1};
237
+ else if (k === 'a') newDir = {x: -1, y: 0};
238
+ else if (k === 's') newDir = {x: 0, y: 0}; // center: no change
239
+ else if (k === 'd') newDir = {x: 1, y: 0};
240
+ else if (k === 'z') newDir = {x: -1, y: 1};
241
+ else if (k === 'x') newDir = {x: 0, y: 1};
242
+ else if (k === 'c') newDir = {x: 1, y: 1};
243
+
244
+ // If a valid key was pressed and newDir is not the "no-turn" (0,0) command,
245
+ // update direction. Also disallow reversing (only if snake length > 1).
246
+ if (newDir) {
247
+ if (newDir.x === 0 && newDir.y === 0) {
248
+ // "S" was pressed: no turn, so do nothing.
249
+ return;
250
+ }
251
+ if (snake.length > 1) {
252
+ // Prevent reversing (i.e., newDir should not be exactly opposite of current)
253
+ if (newDir.x === -direction.x && newDir.y === -direction.y) {
254
+ return;
255
+ }
256
  }
257
+ direction = newDir;
258
  }
259
+ }
260
+
261
+ // Resize canvas on window resize
262
+ function windowResized() {
263
+ resizeCanvas(windowWidth, windowHeight);
264
+ cols = floor(width / tileSize);
265
+ rows = floor(height / tileSize);
266
+ }
267
+ </script>
268
+ </body>
269
+ </html>
270
+ """
271
 
272
+ st.components.v1.html(html_code, height=700, scrolling=False)