1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f17b4db2cd1b232c0843571a8a4e9fcaf49edba5 384 lines 12 kB view raw
1#!/usr/bin/env python3 2""" 3Bouncy Game Visualizer — mirrors the VHDL physics exactly. 4 5Controls: WASD to move/jump, R to reset, T to toggle trail, Q to quit. 6 7Prerequisites: 8 pip install pygame 9 10Usage: 11 python3 visualizer.py 12""" 13 14import pygame 15import sys 16 17# --- Screen / VGA constants (match VHDL) --- 18SCREEN_W = 640 19SCREEN_H = 480 20FPS = 60 21 22# --- Physics constants (match VHDL exactly) --- 23GRAVITY = 1 24IMPULSE = 3 25AIR_CONTROL = 2 26JUMP_FORCE = 13 27MAX_VEL_X = 32 28SIZE = 7 29BOUNCE_SHIFT = 3 # energy loss = vel >> 3 (keep 87.5%) 30 31# --- Bounds (match VHDL) --- 32GROUND = 440 33CEILING = 16 34LEFT_WALL = 8 35RIGHT_WALL = 620 36GROUND_TOP = 448 37CEIL_BOT = 8 38 39# --- Obstacles: (left, top, right, bottom) matching VHDL --- 40OBSTACLES = [ 41 (60, 370, 180, 386), # Low platform left 42 (250, 300, 390, 316), # Middle floating platform 43 (440, 200, 580, 216), # High platform right 44 (140, 120, 200, 150), # Small block upper-left 45] 46 47# --- Colors (1-bit RGB) --- 48COLOR_SKY = (0, 0, 0) 49COLOR_GROUND = (0, 255, 0) 50COLOR_CEIL = (0, 255, 0) 51COLOR_WALL = (0, 255, 255) 52COLOR_CHAR = (255, 0, 0) 53COLOR_OBS = (255, 255, 0) 54 55# --- 10-bit signed helpers --- 56MASK = 0x3FF 57 58def to_signed(val): 59 val = val & MASK 60 return val - 1024 if val >= 512 else val 61 62def to_unsigned(val): 63 return val & MASK 64 65def negate(v): 66 return ((~v) + 1) & MASK 67 68 69def main(): 70 pygame.init() 71 screen = pygame.display.set_mode((SCREEN_W, SCREEN_H)) 72 pygame.display.set_caption("Bouncy Game — VHDL Physics Preview") 73 clock = pygame.time.Clock() 74 75 char_x = 100 76 char_y = 200 77 vel_x = to_unsigned(0) 78 vel_y = to_unsigned(0) 79 squish = 0 80 squish_h = False # False = vertical squish (floor/ceil), True = horizontal squish (walls) 81 on_ground = False 82 jump_pressed = False 83 84 keys_held = {'w': False, 'a': False, 's': False, 'd': False} 85 trail = [] 86 show_trail = True 87 font = pygame.font.SysFont("monospace", 14) 88 89 running = True 90 while running: 91 for event in pygame.event.get(): 92 if event.type == pygame.QUIT: 93 running = False 94 elif event.type == pygame.KEYDOWN: 95 if event.key == pygame.K_w: keys_held['w'] = True 96 elif event.key == pygame.K_a: keys_held['a'] = True 97 elif event.key == pygame.K_s: keys_held['s'] = True 98 elif event.key == pygame.K_d: keys_held['d'] = True 99 elif event.key == pygame.K_q: running = False 100 elif event.key == pygame.K_t: 101 show_trail = not show_trail; trail.clear() 102 elif event.key == pygame.K_r: 103 char_x, char_y = 100, 200 104 vel_x = vel_y = to_unsigned(0) 105 squish = 0; squish_h = False; on_ground = False; jump_pressed = False 106 trail.clear() 107 elif event.type == pygame.KEYUP: 108 if event.key == pygame.K_w: keys_held['w'] = False 109 elif event.key == pygame.K_a: keys_held['a'] = False 110 elif event.key == pygame.K_s: keys_held['s'] = False 111 elif event.key == pygame.K_d: keys_held['d'] = False 112 113 # ============================================================= 114 # Physics — matches VHDL exactly 115 # ============================================================= 116 vx = vel_x 117 vy = vel_y 118 bounced = False 119 bounce_wall = False 120 bounce_speed = 0 121 grounded = False 122 123 # Jump latch 124 if not keys_held['w']: 125 jump_pressed = False 126 127 # First press on ground: full jump 128 if keys_held['w'] and not jump_pressed and on_ground: 129 vy = to_unsigned(0) 130 vy = (vy - JUMP_FORCE) & MASK 131 jump_pressed = True 132 133 # Holding W while bouncing: boost each ground contact 134 if keys_held['w'] and jump_pressed and on_ground: 135 vy = (vy - 4) & MASK 136 137 # S: slam (air only) 138 if keys_held['s'] and not on_ground: 139 vy = (vy + IMPULSE) & MASK 140 141 # A/D 142 if keys_held['a']: 143 if on_ground: 144 vx = (vx - IMPULSE) & MASK 145 else: 146 vx = (vx - AIR_CONTROL) & MASK 147 148 if keys_held['d']: 149 if on_ground: 150 vx = (vx + IMPULSE) & MASK 151 else: 152 vx = (vx + AIR_CONTROL) & MASK 153 154 # Gravity 155 vy = (vy + GRAVITY) & MASK 156 157 # Friction: vel -= vel/4, min 1 158 svx = to_signed(vx) 159 if svx > 0: 160 drag = svx >> 2 161 if drag == 0: drag = 1 162 svx -= drag 163 elif svx < 0: 164 drag = (-svx) >> 2 165 if drag == 0: drag = 1 166 svx += drag 167 vx = to_unsigned(svx) 168 169 # Clamp X 170 svx = to_signed(vx) 171 if svx > MAX_VEL_X: svx = MAX_VEL_X 172 elif svx < -MAX_VEL_X: svx = -MAX_VEL_X 173 vx = to_unsigned(svx) 174 175 # Clamp Y 176 svy = to_signed(vy) 177 if svy > 63: svy = 63 178 elif svy < -64: svy = -64 179 vy = to_unsigned(svy) 180 181 # Update position 182 px = char_x + to_signed(vx) 183 py = char_y + to_signed(vy) 184 185 # --- Ground bounce --- 186 if py >= GROUND: 187 py = GROUND 188 bounced = True 189 grounded = True 190 bounce_speed = abs(to_signed(vy)) 191 vy = negate(vy) 192 svy = to_signed(vy) 193 # svy is now negative (upward), apply energy loss on magnitude 194 if svy < -1: 195 svy += (-svy) >> BOUNCE_SHIFT # reduce magnitude 196 # Kill tiny bounces 197 if abs(svy) < 2: 198 svy = 0 199 vy = to_unsigned(svy) 200 201 # --- Ceiling bounce --- 202 if py <= CEILING: 203 py = CEILING 204 bounced = True 205 bounce_speed = abs(to_signed(vy)) 206 vy = negate(vy) 207 svy = to_signed(vy) 208 if abs(svy) > 1: 209 svy_abs = abs(svy) 210 loss = svy_abs >> BOUNCE_SHIFT 211 if svy > 0: 212 svy -= loss 213 else: 214 svy += loss 215 vy = to_unsigned(svy) 216 217 # --- Left wall bounce --- 218 if px <= LEFT_WALL: 219 px = LEFT_WALL 220 bounced = True; bounce_wall = True 221 bounce_speed = abs(to_signed(vx)) 222 vx = negate(vx) 223 svx = to_signed(vx) 224 if svx > 1: 225 svx -= svx >> BOUNCE_SHIFT 226 vx = to_unsigned(svx) 227 228 # --- Right wall bounce --- 229 if px >= RIGHT_WALL: 230 px = RIGHT_WALL 231 bounced = True; bounce_wall = True 232 bounce_speed = abs(to_signed(vx)) 233 vx = negate(vx) 234 svx = to_signed(vx) 235 if svx < -1: 236 svx += (-svx) >> BOUNCE_SHIFT 237 vx = to_unsigned(svx) 238 239 # --- Obstacle collisions --- 240 for (ol, ot, orr, ob) in OBSTACLES: 241 c_left = px - SIZE 242 c_right = px + SIZE 243 c_top = py - SIZE 244 c_bot = py + SIZE 245 246 if c_right >= ol and c_left <= orr and c_bot >= ot and c_top <= ob: 247 # Compute overlap from velocity direction 248 svx_now = to_signed(vx) 249 svy_now = to_signed(vy) 250 251 if svx_now >= 0: 252 overlap_x = c_right - ol 253 else: 254 overlap_x = orr - c_left 255 256 if svy_now >= 0: 257 overlap_y = c_bot - ot 258 else: 259 overlap_y = ob - c_top 260 261 if overlap_y <= overlap_x: 262 # Vertical resolution 263 if svy_now >= 0: # moving down 264 py = ot - SIZE 265 grounded = True 266 else: # moving up 267 py = ob + SIZE 268 bounced = True 269 bounce_speed = abs(svy_now) 270 vy = negate(vy) 271 svy = to_signed(vy) 272 # Energy loss on magnitude 273 if abs(svy) > 1: 274 svy_abs = abs(svy) 275 loss = svy_abs >> BOUNCE_SHIFT 276 if svy > 0: 277 svy -= loss 278 else: 279 svy += loss 280 if abs(svy) < 2: 281 svy = 0 282 vy = to_unsigned(svy) 283 else: 284 # Horizontal resolution 285 if svx_now >= 0: 286 px = ol - SIZE 287 else: 288 px = orr + SIZE 289 bounced = True; bounce_wall = True 290 bounce_speed = abs(svx_now) 291 vx = negate(vx) 292 svx = to_signed(vx) 293 if svx > 1: 294 svx -= svx >> BOUNCE_SHIFT 295 elif svx < -1: 296 svx += (-svx) >> BOUNCE_SHIFT 297 vx = to_unsigned(svx) 298 299 # Commit 300 char_x = px 301 char_y = py 302 vel_x = vx 303 vel_y = vy 304 305 # On ground 306 on_ground = grounded or (py >= GROUND - 1) 307 308 # Squish — only on impacts with real velocity, not idle ground contact 309 if bounced and bounce_speed > 3: 310 squish = min(bounce_speed, 8) 311 squish_h = bounce_wall 312 elif squish > 0: 313 squish -= 1 314 315 # Trail 316 if show_trail: 317 trail.append((char_x, char_y)) 318 if len(trail) > 300: 319 trail.pop(0) 320 321 # ============================================================= 322 # Rendering 323 # ============================================================= 324 screen.fill(COLOR_SKY) 325 326 # Ceiling 327 pygame.draw.rect(screen, COLOR_CEIL, (0, 0, SCREEN_W, CEIL_BOT)) 328 329 # Ground 330 pygame.draw.rect(screen, COLOR_GROUND, 331 (0, GROUND_TOP, SCREEN_W, SCREEN_H - GROUND_TOP)) 332 333 # Walls 334 pygame.draw.rect(screen, COLOR_WALL, (0, 0, LEFT_WALL, SCREEN_H)) 335 pygame.draw.rect(screen, COLOR_WALL, 336 (RIGHT_WALL, 0, SCREEN_W - RIGHT_WALL, SCREEN_H)) 337 338 # Obstacles 339 for (ol, ot, orr, ob) in OBSTACLES: 340 pygame.draw.rect(screen, COLOR_OBS, 341 (ol, ot, orr - ol, ob - ot)) 342 343 # Trail 344 if show_trail and len(trail) > 1: 345 for i, (tx, ty) in enumerate(trail): 346 alpha = int(80 * i / len(trail)) 347 s = pygame.Surface((3, 3)) 348 s.set_alpha(alpha) 349 s.fill((255, 100, 100)) 350 screen.blit(s, (tx - 1, ty - 1)) 351 352 # Character with squish 353 # Vertical squish (floor/ceil): wider + shorter 354 # Horizontal squish (walls): taller + narrower 355 if not squish_h: 356 cw = SIZE + squish 357 ch = SIZE - squish // 2 358 else: 359 cw = SIZE - squish // 2 360 ch = SIZE + squish 361 pygame.draw.rect(screen, COLOR_CHAR, 362 (char_x - cw, char_y - ch, cw * 2, ch * 2)) 363 364 # HUD 365 svx = to_signed(vel_x) 366 svy = to_signed(vel_y) 367 info = [ 368 f"pos: ({char_x}, {char_y}) vel: ({svx}, {svy})", 369 f"squish: {squish} ground: {'yes' if on_ground else 'no'}", 370 "", 371 "WASD: move/jump R: reset T: trail Q: quit", 372 ] 373 for i, line in enumerate(info): 374 surf = font.render(line, True, (255, 255, 255)) 375 screen.blit(surf, (LEFT_WALL + 4, CEIL_BOT + 4 + i * 16)) 376 377 pygame.display.flip() 378 clock.tick(FPS) 379 380 pygame.quit() 381 382 383if __name__ == "__main__": 384 main()