Update app.py
Browse files
app.py
CHANGED
@@ -4,75 +4,45 @@ import random
|
|
4 |
import json
|
5 |
import math
|
6 |
|
7 |
-
#
|
8 |
if 'game_state' not in st.session_state:
|
9 |
st.session_state.game_state = {
|
10 |
-
'hex_grid': [[{'type': 'empty', 'emoji': ''} for _ in range(
|
11 |
'buildings': [],
|
12 |
'players': {},
|
13 |
-
'train': {'x': 0, 'y': 5
|
14 |
'monster_mode': False,
|
15 |
'current_monster': 'Godzilla',
|
16 |
-
'monster_x':
|
17 |
-
'monster_y':
|
|
|
18 |
}
|
19 |
|
20 |
-
#
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
]
|
|
|
25 |
|
26 |
-
#
|
27 |
-
story_beginnings = [
|
28 |
-
("๐ ", "In a bustling city nestled between towering mountains,"),
|
29 |
-
("๐", "A lone figure darted through the streets as the sun began to set,"),
|
30 |
-
("๐ง ", "Thoughts raced through their mind, planning the next move,"),
|
31 |
-
("๐ฒ", "Shock rippled through the crowd as the ground trembled,"),
|
32 |
-
("๐ฌ", "'We need to act now!' someone shouted over the chaos,"),
|
33 |
-
("๐", "A closer look revealed hidden secrets in the cityโs core,"),
|
34 |
-
("๐ฌ", "A train screeched into the station, unloading its mysteries,"),
|
35 |
-
("๐๏ธ", "Lush greenery framed the skyline, a stark contrast to the urban sprawl,"),
|
36 |
-
("โฉ", "Events unfolded rapidly, leaving no time to breathe,"),
|
37 |
-
("๐๏ธ", "The horizon glowed with an eerie, captivating light,"),
|
38 |
-
("๐", "Whispers of wind carried tales of forgotten lands,"),
|
39 |
-
("๐ฃ๏ธ", "A juicy rumor spread like wildfire among the citizens,"),
|
40 |
-
("๐คธ", "With a simple leap, the adventure began anew,"),
|
41 |
-
("๐งฉ", "The essentials of survival lay scattered across the grid,"),
|
42 |
-
("๐ถ", "A slow walk revealed the cityโs heartbeat,"),
|
43 |
-
("๐", "A journey unfolded, waves of change crashing in,"),
|
44 |
-
("๐ฏ", "Focus sharpened as the goal came into view,"),
|
45 |
-
("๐ค", "Neurotic thoughts plagued the plannerโs restless mind,")
|
46 |
-
]
|
47 |
-
|
48 |
-
# Witty nature labels
|
49 |
-
nature_labels = [
|
50 |
-
"Whispering Winds", "Sassy Sunlight", "Grumpy Gravel", "Chatty Clouds",
|
51 |
-
"Pensive Pines", "Witty Wetlands", "Sneaky Streams", "Bouncy Breezes",
|
52 |
-
"Daring Dewdrops", "Cheeky Crags"
|
53 |
-
]
|
54 |
-
|
55 |
-
# Emoji palette
|
56 |
-
plants = ["๐ฑ", "๐ฒ", "๐ณ", "๐ด", "๐ต", "๐พ", "๐ฟ", "๐", "๐", "๐"]
|
57 |
-
buildings = ["๐ ", "๐ก", "๐ข", "๐ฃ", "๐ค", "๐ฅ", "๐ฆ", "๐จ", "๐ฉ", "๐ช"]
|
58 |
-
creatures = ["๐พ", "๐ฑ", "๐ถ", "๐ญ", "๐ฐ", "๐ฆ", "๐ป", "๐ท", "๐ฎ", "๐ธ"]
|
59 |
-
|
60 |
-
# p5.js code for hex grid and gameplay
|
61 |
p5js_code = """
|
62 |
-
|
|
|
63 |
let hexGrid = [];
|
64 |
let buildings = [];
|
65 |
-
let train = {
|
66 |
let monsterMode = false;
|
67 |
-
let currentMonster = '
|
68 |
let monsterX, monsterY;
|
69 |
-
let
|
70 |
-
let debugText = "Loading...";
|
71 |
-
let playerId = null;
|
72 |
-
const sqrt3 = Math.sqrt(3);
|
73 |
|
74 |
function setup() {
|
75 |
-
createCanvas(
|
|
|
|
|
|
|
|
|
76 |
hexGrid = JSON.parse(document.getElementById('game_state').innerHTML);
|
77 |
buildings = JSON.parse(document.getElementById('buildings').innerHTML);
|
78 |
train = JSON.parse(document.getElementById('train').innerHTML);
|
@@ -81,11 +51,6 @@ function setup() {
|
|
81 |
monsterX = parseInt(document.getElementById('monster_x').innerHTML);
|
82 |
monsterY = parseInt(document.getElementById('monster_y').innerHTML);
|
83 |
playerId = document.getElementById('player_id').innerHTML;
|
84 |
-
debugText = `Player ${playerId} joined. Click to build!`;
|
85 |
-
// Set train track on row 5
|
86 |
-
for (let i = 0; i < hexGrid.length; i++) {
|
87 |
-
hexGrid[i][5].type = 'track';
|
88 |
-
}
|
89 |
}
|
90 |
|
91 |
function draw() {
|
@@ -94,153 +59,96 @@ function draw() {
|
|
94 |
drawBuildings();
|
95 |
drawTrain();
|
96 |
if (monsterMode) drawMonster();
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
100 |
}
|
101 |
|
102 |
function drawHexGrid() {
|
103 |
for (let i = 0; i < hexGrid.length; i++) {
|
104 |
for (let j = 0; j < hexGrid[i].length; j++) {
|
105 |
-
let x = i *
|
106 |
-
let y = j *
|
107 |
-
|
108 |
-
fill(hexGrid[i][j].type === 'track' ? 100 : 150, 200, 150);
|
109 |
stroke(0);
|
110 |
-
|
111 |
-
for (let k = 0; k < 6; k++) {
|
112 |
-
let angleRad = PI / 3 * k;
|
113 |
-
vertex(x + hexSize * cos(angleRad), y + hexSize * sin(angleRad) - z);
|
114 |
-
}
|
115 |
-
endShape(CLOSE);
|
116 |
if (hexGrid[i][j].emoji) {
|
117 |
textSize(20);
|
118 |
-
text(hexGrid[i][j].emoji, x - 10, y + 5
|
119 |
}
|
120 |
}
|
121 |
}
|
122 |
}
|
123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
function drawBuildings() {
|
125 |
buildings.forEach(b => {
|
126 |
-
let x = b.x *
|
127 |
-
let y = b.y *
|
128 |
-
|
129 |
-
fill(b.color);
|
130 |
noStroke();
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
vertex(x + 25, y + 30 - z); vertex(x + 25, y + 35 - z); vertex(x + 15, y + 35 - z);
|
135 |
-
vertex(x + 15, y + 30 - z); vertex(x + 17, y + 25 - z); vertex(x + 23, y + 25 - z);
|
136 |
-
} else if (b.type === 'Commercial') {
|
137 |
-
vertex(x + 10, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
|
138 |
-
vertex(x + 30, y + 35 - z); vertex(x + 27, y + 35 - z); vertex(x + 27, y + 20 - z);
|
139 |
-
vertex(x + 13, y + 20 - z); vertex(x + 13, y + 35 - z);
|
140 |
-
} else if (b.type === 'Industrial') {
|
141 |
-
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 20 - z); vertex(x + 25, y + 20 - z);
|
142 |
-
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 15 - z);
|
143 |
-
vertex(x + 33, y + 15 - z); vertex(x + 33, y + 35 - z);
|
144 |
-
} else if (b.type === 'School') {
|
145 |
-
vertex(x + 5, y + 35 - z); vertex(x + 10, y + 20 - z); vertex(x + 30, y + 20 - z);
|
146 |
-
vertex(x + 35, y + 35 - z); vertex(x + 25, y + 35 - z); vertex(x + 25, y + 10 - z);
|
147 |
-
vertex(x + 27, y + 10 - z); vertex(x + 27, y + 35 - z);
|
148 |
-
} else if (b.type === 'PowerPlant') {
|
149 |
-
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
|
150 |
-
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 25 - z);
|
151 |
-
vertex(x + 20, y + 25 - z); vertex(x + 20, y + 35 - z);
|
152 |
-
}
|
153 |
-
endShape(CLOSE);
|
154 |
});
|
155 |
}
|
156 |
|
157 |
function drawTrain() {
|
158 |
-
let
|
159 |
-
let
|
160 |
-
let tz = train.y * sin(angle);
|
161 |
fill(150, 50, 50);
|
162 |
-
|
163 |
-
|
164 |
-
vertex(tx + 10, ty + 10 - tz); vertex(tx + 30, ty + 10 - tz); vertex(tx + 35, ty + 20 - tz);
|
165 |
-
vertex(tx + 30, ty + 30 - tz); vertex(tx + 10, ty + 30 - tz); vertex(tx + 5, ty + 20 - tz);
|
166 |
-
vertex(tx + 15, ty + 20 - tz); vertex(tx + 15, ty + 15 - tz); vertex(tx + 25, ty + 15 - tz);
|
167 |
-
vertex(tx + 25, ty + 20 - tz);
|
168 |
-
endShape(CLOSE);
|
169 |
-
train.x += train.dir * 2;
|
170 |
-
if (train.x > width || train.x < 0) train.dir *= -1;
|
171 |
}
|
172 |
|
173 |
function drawMonster() {
|
174 |
-
let
|
|
|
175 |
fill(255, 0, 0);
|
176 |
-
|
177 |
-
|
178 |
-
if (currentMonster === 'Godzilla') {
|
179 |
-
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
|
180 |
-
vertex(monsterX + 20, monsterY - mz); vertex(monsterX + 30, monsterY + 20 - mz);
|
181 |
-
vertex(monsterX + 40, monsterY + 40 - mz); vertex(monsterX + 35, monsterY + 50 - mz);
|
182 |
-
vertex(monsterX + 25, monsterY + 60 - mz); vertex(monsterX + 15, monsterY + 50 - mz);
|
183 |
-
vertex(monsterX + 20, monsterY + 40 - mz); vertex(monsterX + 25, monsterY + 30 - mz);
|
184 |
-
} else if (currentMonster === 'GiantRobot') {
|
185 |
-
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
|
186 |
-
vertex(monsterX + 15, monsterY - mz); vertex(monsterX + 25, monsterY - mz);
|
187 |
-
vertex(monsterX + 30, monsterY + 20 - mz); vertex(monsterX + 40, monsterY + 40 - mz);
|
188 |
-
vertex(monsterX + 35, monsterY + 50 - mz); vertex(monsterX + 20, monsterY + 60 - mz);
|
189 |
-
vertex(monsterX + 5, monsterY + 50 - mz); vertex(monsterX + 15, monsterY + 30 - mz);
|
190 |
-
}
|
191 |
-
endShape(CLOSE);
|
192 |
-
monsterX += random(-5, 5);
|
193 |
-
monsterY += random(-5, 5);
|
194 |
-
monsterX = constrain(monsterX, 0, width);
|
195 |
-
monsterY = constrain(monsterY, 0, height);
|
196 |
}
|
197 |
|
198 |
function mousePressed() {
|
199 |
-
let i = floor(mouseX / (
|
200 |
-
let j = floor((mouseY - (i % 2
|
201 |
-
if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length) {
|
202 |
-
|
203 |
-
|
204 |
-
if (
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
|
|
212 |
hexGrid[i][j].type = 'building';
|
213 |
} else {
|
214 |
hexGrid[i][j].type = 'placed';
|
215 |
-
hexGrid[i][j].emoji =
|
216 |
}
|
217 |
-
debugText = `Player ${playerId} placed ${selectedEmoji} at (${i}, ${j})`;
|
218 |
updateState();
|
219 |
}
|
220 |
}
|
221 |
}
|
222 |
|
223 |
function keyPressed() {
|
224 |
-
if (key === 'm' || key === 'M')
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
if (key === 'g' || key === 'G') {
|
230 |
-
currentMonster = 'Godzilla';
|
231 |
-
debugText = "Monster set to Godzilla";
|
232 |
-
updateState();
|
233 |
-
}
|
234 |
-
if (key === 'r' || key === 'R') {
|
235 |
-
currentMonster = 'GiantRobot';
|
236 |
-
debugText = "Monster set to Giant Robot";
|
237 |
-
updateState();
|
238 |
-
}
|
239 |
-
if (key === 't' || key === 'T') {
|
240 |
-
train.dir *= -1;
|
241 |
-
debugText = "Train direction reversed";
|
242 |
-
updateState();
|
243 |
-
}
|
244 |
}
|
245 |
|
246 |
function updateState() {
|
@@ -261,111 +169,66 @@ function updateState() {
|
|
261 |
}
|
262 |
"""
|
263 |
|
264 |
-
#
|
265 |
if 'player_id' not in st.session_state:
|
266 |
st.session_state.player_id = random.choice(random_names)
|
|
|
267 |
player_id = st.sidebar.selectbox("Choose Player", random_names, index=random_names.index(st.session_state.player_id))
|
268 |
st.session_state.player_id = player_id
|
269 |
|
270 |
-
#
|
271 |
-
story_html = f"""
|
272 |
-
<div style='background: #f9f9f9; padding: 10px; border-radius: 5px; margin-bottom: 10px;'>
|
273 |
-
<h3>๐ Story Plot</h3>
|
274 |
-
<p>{random.choice(story_beginnings)[1]} {random.choice(nature_labels)} stirred the scene.</p>
|
275 |
-
</div>
|
276 |
-
"""
|
277 |
-
st.sidebar.markdown(story_html, unsafe_allow_html=True)
|
278 |
-
|
279 |
-
# Palette grid
|
280 |
st.sidebar.subheader("๐จ Palette")
|
281 |
cols = st.sidebar.columns(5)
|
282 |
selected_emoji = st.session_state.get('selected_emoji', plants[0])
|
283 |
for i, emoji in enumerate(plants + buildings + creatures):
|
284 |
-
|
285 |
-
if col.button(emoji, key=f"emoji_{i}"):
|
286 |
st.session_state.selected_emoji = emoji
|
287 |
selected_emoji = emoji
|
288 |
|
289 |
# Scoreboard
|
290 |
if player_id not in st.session_state.game_state['players']:
|
291 |
st.session_state.game_state['players'][player_id] = 0
|
292 |
-
|
293 |
-
if b.get('player') == player_id:
|
294 |
-
st.session_state.game_state['players'][player_id] += 1
|
295 |
-
for i in range(len(st.session_state.game_state['hex_grid'])):
|
296 |
-
for j in range(len(st.session_state.game_state['hex_grid'][i])):
|
297 |
-
if st.session_state.game_state['hex_grid'][i][j]['type'] == 'placed':
|
298 |
-
st.session_state.game_state['players'][player_id] += 1
|
299 |
-
score_html = "<div style='background: #f9f9f9; padding: 10px; border-radius: 5px;'><h3>๐ Scores</h3><ul>"
|
300 |
for p, s in st.session_state.game_state['players'].items():
|
301 |
-
|
302 |
-
score_html += "</ul></div>"
|
303 |
-
st.sidebar.markdown(score_html, unsafe_allow_html=True)
|
304 |
|
305 |
-
#
|
306 |
game_html = f"""
|
307 |
-
|
308 |
-
<
|
309 |
-
<
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
</head>
|
319 |
-
<body>
|
320 |
-
<div id="controls">
|
321 |
-
<button onclick="toggleMonster()">Toggle Monster Mode (M)</button>
|
322 |
-
<select onchange="setMonster(this.value)">
|
323 |
-
<option value="Godzilla">Godzilla (G)</option>
|
324 |
-
<option value="GiantRobot">Giant Robot (R)</option>
|
325 |
-
</select>
|
326 |
-
<button onclick="reverseTrain()">Reverse Train (T)</button>
|
327 |
-
</div>
|
328 |
-
<div id="sketch-holder"></div>
|
329 |
-
<div id="game_state" style="display:none">{json.dumps(st.session_state.game_state['hex_grid'])}</div>
|
330 |
-
<div id="buildings" style="display:none">{json.dumps(st.session_state.game_state['buildings'])}</div>
|
331 |
-
<div id="train" style="display:none">{json.dumps(st.session_state.game_state['train'])}</div>
|
332 |
-
<div id="monster_mode" style="display:none">{json.dumps(st.session_state.game_state['monster_mode'])}</div>
|
333 |
-
<div id="current_monster" style="display:none">{st.session_state.game_state['current_monster']}</div>
|
334 |
-
<div id="monster_x" style="display:none">{st.session_state.game_state['monster_x']}</div>
|
335 |
-
<div id="monster_y" style="display:none">{st.session_state.game_state['monster_y']}</div>
|
336 |
-
<div id="player_id" style="display:none">{player_id}</div>
|
337 |
-
<div id="selected_emoji" style="display:none">{selected_emoji}</div>
|
338 |
-
|
339 |
-
<script>
|
340 |
-
{p5js_code}
|
341 |
-
|
342 |
-
function toggleMonster() {{ window.toggleMonsterMode(); }}
|
343 |
-
function setMonster(monster) {{ window.setMonster(monster); }}
|
344 |
-
function reverseTrain() {{ window.reverseTrain(); }}
|
345 |
-
</script>
|
346 |
-
</body>
|
347 |
-
</html>
|
348 |
"""
|
349 |
|
350 |
-
#
|
351 |
-
st.title("HexCity
|
352 |
-
st.write(f"Player: {player_id}. Click to place {selected_emoji}.
|
353 |
-
html(game_html, height=
|
354 |
|
355 |
-
#
|
356 |
def update_state(data):
|
357 |
-
st.session_state.game_state
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
|
|
|
|
|
|
364 |
if data['player_id'] not in st.session_state.game_state['players']:
|
365 |
st.session_state.game_state['players'][data['player_id']] = 0
|
366 |
st.session_state.game_state['players'][data['player_id']] += 1
|
367 |
st.rerun()
|
368 |
|
369 |
-
#
|
370 |
-
|
371 |
-
|
|
|
4 |
import json
|
5 |
import math
|
6 |
|
7 |
+
# Initialize game state
|
8 |
if 'game_state' not in st.session_state:
|
9 |
st.session_state.game_state = {
|
10 |
+
'hex_grid': [[{'type': 'empty', 'emoji': ''} for _ in range(12)] for _ in range(16)], # Smaller 16x12 grid for better visibility
|
11 |
'buildings': [],
|
12 |
'players': {},
|
13 |
+
'train': {'x': 0, 'y': 5, 'dir': 1}, # Using grid coordinates
|
14 |
'monster_mode': False,
|
15 |
'current_monster': 'Godzilla',
|
16 |
+
'monster_x': 8,
|
17 |
+
'monster_y': 6,
|
18 |
+
'last_update': None
|
19 |
}
|
20 |
|
21 |
+
# Constants and assets
|
22 |
+
HEX_SIZE = 40
|
23 |
+
random_names = ["SkyWalker", "ForestRanger", "CityBuilder", "MonsterTamer", "RailMaster"]
|
24 |
+
plants = ["๐ฑ", "๐ฒ", "๐ณ", "๐ด", "๐ต"]
|
25 |
+
buildings = ["๐ ", "๐ก", "๐ข", "๐ฅ", "๐ฆ"]
|
26 |
+
creatures = ["๐พ", "๐ฑ", "๐ถ", "๐ญ", "๐ฐ"]
|
27 |
|
28 |
+
# Updated p5.js code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
p5js_code = """
|
30 |
+
const HEX_SIZE = 40;
|
31 |
+
const SQRT_3 = Math.sqrt(3);
|
32 |
let hexGrid = [];
|
33 |
let buildings = [];
|
34 |
+
let train = {};
|
35 |
let monsterMode = false;
|
36 |
+
let currentMonster = '';
|
37 |
let monsterX, monsterY;
|
38 |
+
let playerId;
|
|
|
|
|
|
|
39 |
|
40 |
function setup() {
|
41 |
+
createCanvas(640, 480); // Adjusted canvas size for 16x12 grid
|
42 |
+
updateFromState();
|
43 |
+
}
|
44 |
+
|
45 |
+
function updateFromState() {
|
46 |
hexGrid = JSON.parse(document.getElementById('game_state').innerHTML);
|
47 |
buildings = JSON.parse(document.getElementById('buildings').innerHTML);
|
48 |
train = JSON.parse(document.getElementById('train').innerHTML);
|
|
|
51 |
monsterX = parseInt(document.getElementById('monster_x').innerHTML);
|
52 |
monsterY = parseInt(document.getElementById('monster_y').innerHTML);
|
53 |
playerId = document.getElementById('player_id').innerHTML;
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
function draw() {
|
|
|
59 |
drawBuildings();
|
60 |
drawTrain();
|
61 |
if (monsterMode) drawMonster();
|
62 |
+
|
63 |
+
// Train movement
|
64 |
+
train.x += train.dir * 0.05;
|
65 |
+
if (train.x >= hexGrid.length || train.x < 0) train.dir *= -1;
|
66 |
}
|
67 |
|
68 |
function drawHexGrid() {
|
69 |
for (let i = 0; i < hexGrid.length; i++) {
|
70 |
for (let j = 0; j < hexGrid[i].length; j++) {
|
71 |
+
let x = i * HEX_SIZE * 1.5;
|
72 |
+
let y = j * HEX_SIZE * SQRT_3 + (i % 2 ? HEX_SIZE * SQRT_3 / 2 : 0);
|
73 |
+
fill(hexGrid[i][j].type === 'track' ? '#808080' : '#90EE90');
|
|
|
74 |
stroke(0);
|
75 |
+
drawHex(x, y);
|
|
|
|
|
|
|
|
|
|
|
76 |
if (hexGrid[i][j].emoji) {
|
77 |
textSize(20);
|
78 |
+
text(hexGrid[i][j].emoji, x - 10, y + 5);
|
79 |
}
|
80 |
}
|
81 |
}
|
82 |
}
|
83 |
|
84 |
+
function drawHex(x, y) {
|
85 |
+
beginShape();
|
86 |
+
for (let a = 0; a < 6; a++) {
|
87 |
+
let angle = TWO_PI / 6 * a;
|
88 |
+
vertex(x + HEX_SIZE * cos(angle), y + HEX_SIZE * sin(angle));
|
89 |
+
}
|
90 |
+
endShape(CLOSE);
|
91 |
+
}
|
92 |
+
|
93 |
function drawBuildings() {
|
94 |
buildings.forEach(b => {
|
95 |
+
let x = b.x * HEX_SIZE * 1.5;
|
96 |
+
let y = b.y * HEX_SIZE * SQRT_3 + (b.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0);
|
97 |
+
fill(b.color[0], b.color[1], b.color[2]);
|
|
|
98 |
noStroke();
|
99 |
+
ellipse(x, y, HEX_SIZE * 0.8);
|
100 |
+
textSize(20);
|
101 |
+
text(b.emoji, x - 10, y + 5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
});
|
103 |
}
|
104 |
|
105 |
function drawTrain() {
|
106 |
+
let x = train.x * HEX_SIZE * 1.5;
|
107 |
+
let y = train.y * HEX_SIZE * SQRT_3;
|
|
|
108 |
fill(150, 50, 50);
|
109 |
+
rect(x - 15, y - 10, 30, 20);
|
110 |
+
text('๐', x - 10, y + 5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
}
|
112 |
|
113 |
function drawMonster() {
|
114 |
+
let x = monsterX * HEX_SIZE * 1.5;
|
115 |
+
let y = monsterY * HEX_SIZE * SQRT_3 + (monsterX % 2 ? HEX_SIZE * SQRT_3 / 2 : 0);
|
116 |
fill(255, 0, 0);
|
117 |
+
ellipse(x, y, HEX_SIZE * 1.2);
|
118 |
+
text(currentMonster === 'Godzilla' ? '๐ฆ' : '๐ค', x - 10, y + 5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
120 |
|
121 |
function mousePressed() {
|
122 |
+
let i = Math.floor(mouseX / (HEX_SIZE * 1.5));
|
123 |
+
let j = Math.floor((mouseY - (i % 2 ? HEX_SIZE * SQRT_3 / 2 : 0)) / (HEX_SIZE * SQRT_3));
|
124 |
+
if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length && hexGrid[i][j].type === 'empty') {
|
125 |
+
let emoji = document.getElementById('selected_emoji').innerHTML;
|
126 |
+
if (!monsterMode) {
|
127 |
+
if (emoji.startsWith('๐ ') || emoji.startsWith('๐ก') || emoji.startsWith('๐ข') ||
|
128 |
+
emoji.startsWith('๐ฅ') || emoji.startsWith('๐ฆ')) {
|
129 |
+
buildings.push({
|
130 |
+
x: i,
|
131 |
+
y: j,
|
132 |
+
emoji: emoji,
|
133 |
+
color: [random(100, 255), random(100, 255), random(100, 255)],
|
134 |
+
player: playerId
|
135 |
+
});
|
136 |
hexGrid[i][j].type = 'building';
|
137 |
} else {
|
138 |
hexGrid[i][j].type = 'placed';
|
139 |
+
hexGrid[i][j].emoji = emoji;
|
140 |
}
|
|
|
141 |
updateState();
|
142 |
}
|
143 |
}
|
144 |
}
|
145 |
|
146 |
function keyPressed() {
|
147 |
+
if (key === 'm' || key === 'M') monsterMode = !monsterMode;
|
148 |
+
if (key === 'g' || key === 'G') currentMonster = 'Godzilla';
|
149 |
+
if (key === 'r' || key === 'R') currentMonster = 'GiantRobot';
|
150 |
+
if (key === 't' || key === 'T') train.dir *= -1;
|
151 |
+
updateState();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
}
|
153 |
|
154 |
function updateState() {
|
|
|
169 |
}
|
170 |
"""
|
171 |
|
172 |
+
# UI Components
|
173 |
if 'player_id' not in st.session_state:
|
174 |
st.session_state.player_id = random.choice(random_names)
|
175 |
+
|
176 |
player_id = st.sidebar.selectbox("Choose Player", random_names, index=random_names.index(st.session_state.player_id))
|
177 |
st.session_state.player_id = player_id
|
178 |
|
179 |
+
# Emoji palette
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
st.sidebar.subheader("๐จ Palette")
|
181 |
cols = st.sidebar.columns(5)
|
182 |
selected_emoji = st.session_state.get('selected_emoji', plants[0])
|
183 |
for i, emoji in enumerate(plants + buildings + creatures):
|
184 |
+
if cols[i % 5].button(emoji, key=f"emoji_{i}"):
|
|
|
185 |
st.session_state.selected_emoji = emoji
|
186 |
selected_emoji = emoji
|
187 |
|
188 |
# Scoreboard
|
189 |
if player_id not in st.session_state.game_state['players']:
|
190 |
st.session_state.game_state['players'][player_id] = 0
|
191 |
+
st.sidebar.subheader("๐ Scores")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
for p, s in st.session_state.game_state['players'].items():
|
193 |
+
st.sidebar.write(f"{p}: {s}")
|
|
|
|
|
194 |
|
195 |
+
# Game HTML
|
196 |
game_html = f"""
|
197 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
|
198 |
+
<div id="game_state" style="display:none">{json.dumps(st.session_state.game_state['hex_grid'])}</div>
|
199 |
+
<div id="buildings" style="display:none">{json.dumps(st.session_state.game_state['buildings'])}</div>
|
200 |
+
<div id="train" style="display:none">{json.dumps(st.session_state.game_state['train'])}</div>
|
201 |
+
<div id="monster_mode" style="display:none">{json.dumps(st.session_state.game_state['monster_mode'])}</div>
|
202 |
+
<div id="current_monster" style="display:none">{st.session_state.game_state['current_monster']}</div>
|
203 |
+
<div id="monster_x" style="display:none">{st.session_state.game_state['monster_x']}</div>
|
204 |
+
<div id="monster_y" style="display:none">{st.session_state.game_state['monster_y']}</div>
|
205 |
+
<div id="player_id" style="display:none">{player_id}</div>
|
206 |
+
<div id="selected_emoji" style="display:none">{selected_emoji}</div>
|
207 |
+
<script>{p5js_code}</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
"""
|
209 |
|
210 |
+
# Main layout
|
211 |
+
st.title("HexCity Adventure")
|
212 |
+
st.write(f"Player: {player_id}. Click to place {selected_emoji}. Keys: M (monster), G/R (monster type), T (train)")
|
213 |
+
html(game_html, height=500)
|
214 |
|
215 |
+
# State update handler
|
216 |
def update_state(data):
|
217 |
+
st.session_state.game_state.update({
|
218 |
+
'hex_grid': data['hex_grid'],
|
219 |
+
'buildings': data['buildings'],
|
220 |
+
'train': data['train'],
|
221 |
+
'monster_mode': data['monster_mode'],
|
222 |
+
'current_monster': data['current_monster'],
|
223 |
+
'monster_x': data['monster_x'],
|
224 |
+
'monster_y': data['monster_y'],
|
225 |
+
'last_update': data
|
226 |
+
})
|
227 |
if data['player_id'] not in st.session_state.game_state['players']:
|
228 |
st.session_state.game_state['players'][data['player_id']] = 0
|
229 |
st.session_state.game_state['players'][data['player_id']] += 1
|
230 |
st.rerun()
|
231 |
|
232 |
+
# Initialize tracks
|
233 |
+
for i in range(len(st.session_state.game_state['hex_grid'])):
|
234 |
+
st.session_state.game_state['hex_grid'][i][5]['type'] = 'track'
|