Skip to content

Instantly share code, notes, and snippets.

@gerred
Created December 20, 2024 00:35
Show Gist options
  • Save gerred/abddc667d11cf368c1c8cbb256ee599a to your computer and use it in GitHub Desktop.
Save gerred/abddc667d11cf368c1c8cbb256ee599a to your computer and use it in GitHub Desktop.
❯ curl 'https://gist.githubusercontent.com/gerred/ae5074a928f5ef29265dc468093b0a64/raw/e1621a1575d189c18112c586ede3e8176648f813/doom.ts' | uvx llm -m gemini-2.0-flash-thinking-exp-1219 -s 'convert to python'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5364 100 5364 0 0 121k 0 --:--:-- --:--:-- --:--:-- 121k
The user wants to convert the provided JavaScript code to Python. This looks like a simple 2D raycasting engine.
Here's a plan to convert the code:
1. **Imports:** The `readline` import from Node.js needs to be replaced with Python's `asyncio` and `readline` (or potentially just input for simplicity).
2. **Constants:** Translate the JavaScript `const` declarations to Python constants (uppercase variable names).
3. **Game World (Map):** The map array can be directly translated to a Python list of strings.
4. **Game State:** `let` variables become regular Python variables.
5. **Helper Functions:** Convert the `getMap` and `distance` functions to Python. Note the slight differences in how string indexing and exponentiation work.
6. **Ray Casting:** Translate the `castRay` function, paying attention to the loop structure and how math functions are called.
7. **Rendering:** The `renderScreen` function is more involved due to string concatenation. Python's string formatting or joining methods will be useful.
8. **Input Handling:** The asynchronous input handling using `readline` in Node.js needs to be adapted using Python's `asyncio` and `input`.
9. **Game Loop:** The recursive `gameLoop` function needs to be adapted to Python's asynchronous structure.
10. **Main:** The `main` function needs to create the `readline` interface in Python and start the game loop.MAX_DEPTH = 20
# --- Game World ---
MAP_DATA = [
"################################",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"#.......########.......########.#",
"#اريات.#..............#.......#",
"#.......#..............#.......#",
"#####...#.......##.......#...####",
"#.......#.......##.......#.......#",
"#.......#..............#.......#",
"#.......########.......########.#",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"################################",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"#.......########.......########.#",
"#.......#..............#.......#",
"#.......#..............#.......#",
"#####...#.......##اريات.#...####",
"#.......#.......##.......#.......#",
"#.......#..............#.......#",
"#.......########.......########.#",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"################################",
"################################",
"################################",
"################################",
]
# --- Game State ---
player_x = 2.0
player_y = 2.0
player_angle = 0.0
# --- Helper Functions ---
def get_map(x: float, y: float) -> str:
if x < 0 or x >= MAP_WIDTH or y < 0 or y >= MAP_HEIGHT:
return "#" # Treat out of bounds as a wall
return MAP_DATA[int(y)][int(x)]
def distance(x1: float, y1: float, x2: float, y2: float) -> float:
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
# --- Ray Casting ---
def cast_ray(angle: float) -> float:
ray_x = player_x
ray_y = player_y
sin_a = math.sin(angle)
cos_a = math.cos(angle)
for depth in range(MAX_DEPTH):
ray_x += cos_a * 0.1
ray_y += sin_a * 0.1
if get_map(ray_x, ray_y) == '#':
return depth * 0.1
return MAX_DEPTH
# --- Rendering ---
def render_screen() -> str:
screen_buffer = ""
for y in range(SCREEN_HEIGHT):
for x in range(SCREEN_WIDTH):
ray_angle = player_angle - FOV / 2 + (x / SCREEN_WIDTH) * FOV
dist = cast_ray(ray_angle)
# Ceiling and Floor
if y < SCREEN_HEIGHT / 2:
screen_buffer += ' ' # Ceiling
else:
screen_buffer += '.' # Floor
# Walls
wall_height = int(SCREEN_HEIGHT / dist)
wall_top = int((SCREEN_HEIGHT - wall_height) / 2)
if y >= wall_top and y < wall_top + wall_height:
if dist < 5:
screen_buffer = screen_buffer[:-1] + '#' # Closer wall
elif dist < 10:
screen_buffer = screen_buffer[:-1] + '='
else:
screen_buffer = screen_buffer[:-1] + '-' # Farther wall
screen_buffer += '\n'
return screen_buffer
# --- Input Handling ---
async def handle_input():
answer = await asyncio.get_event_loop().run_in_executor(None, input, '> ')
answer = answer.lower()
global player_x, player_y, player_angle
if answer == 'w':
move_x = math.cos(player_angle) * 0.5
move_y = math.sin(player_angle) * 0.5
if get_map(player_x + move_x, player_y) != '#':
player_x += move_x
if get_map(player_x, player_y + move_y) != '#':
player_y += move_y
elif answer == 's':
move_back_x = math.cos(player_angle) * 0.5
move_back_y = math.sin(player_angle) * 0.5
if get_map(player_x - move_back_x, player_y) != '#':
player_x -= move_back_x
if get_map(player_x, player_y - move_back_y) != '#':
player_y -= move_back_y
elif answer == 'a':
player_angle -= 0.1
elif answer == 'd':
player_angle += 0.1
elif answer == 'q':
print('Exiting...')
exit(0)
else:
print('Invalid input.')
# --- Game Loop ---
async def game_loop():
while True:
print("\033c", end="") # Clear screen in terminal
print(render_screen())
await handle_input()
await asyncio.sleep(0.05) # Add a small delay to avoid busy-waiting
# --- Main ---
async def main():
print('Welcome to the Doom-like FPS!')
print('Controls: W (forward), S (backward), A (turn left), D (turn right), Q (quit)')
await game_loop()
if __name__ == "__main__":
asyncio.run(main())
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment