awacke1 commited on
Commit
25446a9
ยท
verified ยท
1 Parent(s): 9d12910

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -248
app.py CHANGED
@@ -4,75 +4,45 @@ import random
4
  import json
5
  import math
6
 
7
- # Shared state for multiplayer
8
  if 'game_state' not in st.session_state:
9
  st.session_state.game_state = {
10
- 'hex_grid': [[{'type': 'empty', 'emoji': ''} for _ in range(15)] for _ in range(20)], # 20x15 hex grid
11
  'buildings': [],
12
  'players': {},
13
- 'train': {'x': 0, 'y': 5 * 40, 'dir': 1},
14
  'monster_mode': False,
15
  'current_monster': 'Godzilla',
16
- 'monster_x': 400,
17
- 'monster_y': 300
 
18
  }
19
 
20
- # Random player names
21
- random_names = [
22
- "SkyWalker", "ForestRanger", "CityBuilder", "MonsterTamer", "RailMaster",
23
- "PlantWhisperer", "UrbanWizard", "NatureSage", "GridLord", "EcoWarrior"
24
- ]
 
25
 
26
- # Story plot generator beginnings
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
- let hexSize = 40;
 
63
  let hexGrid = [];
64
  let buildings = [];
65
- let train = { x: 0, y: 0, dir: 1 };
66
  let monsterMode = false;
67
- let currentMonster = 'Godzilla';
68
  let monsterX, monsterY;
69
- let angle = PI / 6;
70
- let debugText = "Loading...";
71
- let playerId = null;
72
- const sqrt3 = Math.sqrt(3);
73
 
74
  function setup() {
75
- createCanvas(800, 600);
 
 
 
 
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
- fill(0);
98
- textSize(12);
99
- text(debugText, 10, 20);
 
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 * hexSize * 1.5;
106
- let y = j * hexSize * sqrt3 + (i % 2 === 1 ? hexSize * sqrt3 / 2 : 0);
107
- let z = j * hexSize * sin(angle);
108
- fill(hexGrid[i][j].type === 'track' ? 100 : 150, 200, 150);
109
  stroke(0);
110
- beginShape();
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 - z);
119
  }
120
  }
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
124
  function drawBuildings() {
125
  buildings.forEach(b => {
126
- let x = b.x * hexSize * 1.5;
127
- let y = b.y * hexSize * sqrt3 + (b.x % 2 === 1 ? hexSize * sqrt3 / 2 : 0);
128
- let z = b.y * hexSize * sin(angle);
129
- fill(b.color);
130
  noStroke();
131
- beginShape();
132
- if (b.type === 'Residential') {
133
- vertex(x + 10, y + 30 - z); vertex(x + 20, y + 10 - z); vertex(x + 30, y + 30 - z);
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 tx = train.x;
159
- let ty = train.y;
160
- let tz = train.y * sin(angle);
161
  fill(150, 50, 50);
162
- noStroke();
163
- beginShape();
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 mz = monsterY * sin(angle);
 
175
  fill(255, 0, 0);
176
- noStroke();
177
- beginShape();
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 / (hexSize * 1.5));
200
- let j = floor((mouseY - (i % 2 === 1 ? hexSize * sqrt3 / 2 : 0)) / (hexSize * sqrt3));
201
- if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length) {
202
- if (hexGrid[i][j].type === 'empty' && !monsterMode) {
203
- let selectedEmoji = document.getElementById('selected_emoji').innerHTML;
204
- if (selectedEmoji.startsWith('๐Ÿ ') || selectedEmoji.startsWith('๐Ÿก') || selectedEmoji.startsWith('๐Ÿข') ||
205
- selectedEmoji.startsWith('๐Ÿฃ') || selectedEmoji.startsWith('๐Ÿค') || selectedEmoji.startsWith('๐Ÿฅ') ||
206
- selectedEmoji.startsWith('๐Ÿฆ') || selectedEmoji.startsWith('๐Ÿจ') || selectedEmoji.startsWith('๐Ÿฉ') ||
207
- selectedEmoji.startsWith('๐Ÿช')) {
208
- let types = ['Residential', 'Commercial', 'Industrial', 'School', 'PowerPlant'];
209
- let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0], [200, 0, 200], [100, 100, 100]];
210
- let idx = floor(random(5));
211
- buildings.push({ x: i, y: j, type: types[idx], color: colors[idx], player: playerId });
 
212
  hexGrid[i][j].type = 'building';
213
  } else {
214
  hexGrid[i][j].type = 'placed';
215
- hexGrid[i][j].emoji = selectedEmoji;
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
- monsterMode = !monsterMode;
226
- debugText = `Monster Mode: ${monsterMode}`;
227
- updateState();
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
- # Player selection
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
- # Story plot generator panel
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
- col = cols[i % 5]
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
- for b in st.session_state.game_state['buildings']:
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
- score_html += f"<li>{p}: {s}</li>"
302
- score_html += "</ul></div>"
303
- st.sidebar.markdown(score_html, unsafe_allow_html=True)
304
 
305
- # Main game panel
306
  game_html = f"""
307
- <!DOCTYPE html>
308
- <html>
309
- <head>
310
- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
311
- <style>
312
- body {{ font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f0f0f0; }}
313
- #controls {{ padding: 10px; background: #e0e0e0; }}
314
- button {{ padding: 8px 16px; margin: 5px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; }}
315
- button:hover {{ background: #45a049; }}
316
- select {{ padding: 8px; margin: 5px; border-radius: 5px; }}
317
- </style>
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
- # Streamlit app layout
351
- st.title("HexCity 2000 Multiplayer Adventure")
352
- st.write(f"Player: {player_id}. Click to place {selected_emoji}. Use M/G/R/T keys.")
353
- html(game_html, height=650)
354
 
355
- # Update state function (mocked for Streamlit)
356
  def update_state(data):
357
- st.session_state.game_state['hex_grid'] = data['hex_grid']
358
- st.session_state.game_state['buildings'] = data['buildings']
359
- st.session_state.game_state['train'] = data['train']
360
- st.session_state.game_state['monster_mode'] = data['monster_mode']
361
- st.session_state.game_state['current_monster'] = data['current_monster']
362
- st.session_state.game_state['monster_x'] = data['monster_x']
363
- st.session_state.game_state['monster_y'] = data['monster_y']
 
 
 
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
- # Mock POST handler (for debugging)
370
- if st.session_state.get('last_update'):
371
- update_state(st.session_state['last_update'])
 
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'