Skip to content

Instantly share code, notes, and snippets.

@gerred
Created December 20, 2024 00:25
Show Gist options
  • Save gerred/ae5074a928f5ef29265dc468093b0a64 to your computer and use it in GitHub Desktop.
Save gerred/ae5074a928f5ef29265dc468093b0a64 to your computer and use it in GitHub Desktop.
gemini-2.0-flash-thinking-experimental doom ascii raytracer
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
// --- Game Constants ---
const MAP_WIDTH = 32;
const MAP_HEIGHT = 32;
const SCREEN_WIDTH = 80;
const SCREEN_HEIGHT = 30;
const FOV = Math.PI / 3; // Field of View
const MAX_DEPTH = 20;
// --- Game World ---
const map = [
"################################",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"#.......########.......########.#",
"#.......#..............#.......#",
"#.......#..............#.......#",
"#####...#.......##.......#...####",
"#.......#.......##.......#.......#",
"#.......#..............#.......#",
"#.......########.......########.#",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"################################",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"#.......########.......########.#",
"#.......#..............#.......#",
"#.......#..............#.......#",
"#####...#.......##.......#...####",
"#.......#.......##.......#.......#",
"#.......#..............#.......#",
"#.......########.......########.#",
"#..............#...............#",
"#..............#...............#",
"#..............#...............#",
"################################",
"################################",
"################################",
"################################",
];
// --- Game State ---
let playerX = 2;
let playerY = 2;
let playerAngle = 0;
// --- Helper Functions ---
function getMap(x: number, y: number): string {
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
return "#"; // Treat out of bounds as a wall
}
return map[Math.floor(y)][Math.floor(x)];
}
function distance(x1: number, y1: number, x2: number, y2: number): number {
return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
}
// --- Ray Casting ---
function castRay(angle: number): number {
let rayX = playerX;
let rayY = playerY;
const sinA = Math.sin(angle);
const cosA = Math.cos(angle);
for (let depth = 0; depth < MAX_DEPTH; depth++) {
rayX += cosA * 0.1;
rayY += sinA * 0.1;
if (getMap(rayX, rayY) === '#') {
return depth * 0.1;
}
}
return MAX_DEPTH;
}
// --- Rendering ---
function renderScreen(): string {
let screenBuffer = "";
for (let y = 0; y < SCREEN_HEIGHT; y++) {
for (let x = 0; x < SCREEN_WIDTH; x++) {
const rayAngle = playerAngle - FOV / 2 + (x / SCREEN_WIDTH) * FOV;
const dist = castRay(rayAngle);
// Ceiling and Floor
if (y < SCREEN_HEIGHT / 2) {
screenBuffer += ' '; // Ceiling
} else {
screenBuffer += '.'; // Floor
}
// Walls
const wallHeight = Math.floor(SCREEN_HEIGHT / dist);
const wallTop = Math.floor((SCREEN_HEIGHT - wallHeight) / 2);
if (y >= wallTop && y < wallTop + wallHeight) {
if (dist < 5) {
screenBuffer = screenBuffer.slice(0, -1) + '#'; // Closer wall
} else if (dist < 10) {
screenBuffer = screenBuffer.slice(0, -1) + '=';
} else {
screenBuffer = screenBuffer.slice(0, -1) + '-'; // Farther wall
}
}
}
screenBuffer += '\n';
}
return screenBuffer;
}
// --- Input Handling ---
async function handleInput(rl: readline.Interface): Promise<void> {
const answer = await rl.question('> ');
switch (answer.toLowerCase()) {
case 'w':
const moveX = Math.cos(playerAngle) * 0.5;
const moveY = Math.sin(playerAngle) * 0.5;
if (getMap(playerX + moveX, playerY) !== '#') {
playerX += moveX;
}
if (getMap(playerX, playerY + moveY) !== '#') {
playerY += moveY;
}
break;
case 's':
const moveBackX = Math.cos(playerAngle) * 0.5;
const moveBackY = Math.sin(playerAngle) * 0.5;
if (getMap(playerX - moveBackX, playerY) !== '#') {
playerX -= moveBackX;
}
if (getMap(playerX, playerY - moveBackY) !== '#') {
playerY -= moveBackY;
}
break;
case 'a':
playerAngle -= 0.1;
break;
case 'd':
playerAngle += 0.1;
break;
case 'q':
console.log('Exiting...');
process.exit(0);
default:
console.log('Invalid input.');
}
}
// --- Game Loop ---
async function gameLoop(rl: readline.Interface): Promise<void> {
console.clear();
console.log(renderScreen());
await handleInput(rl);
gameLoop(rl);
}
// --- Main ---
async function main(): Promise<void> {
const rl = readline.createInterface({ input, output });
console.log('Welcome to the Doom-like FPS!');
console.log('Controls: W (forward), S (backward), A (turn left), D (turn right), Q (quit)');
gameLoop(rl);
}
main();
Write a Doom style FPS in TS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment