Files
Verbatim/tests/descent.rs
T
Emil 357db17c2f feat: GPU optimization — lighting, viewport CA, benchmark mode, 531 FPS
- Resolution: 8x8 world cells, 2x2 UI cells (UI_SCALE=4)
- GPU lighting: vertex-shader computed, light source list buffer (max 64)
  instead of O(N×R²) grid scan, O(N×S) per cell
- Viewport-aware CA: iterate only active chunks, not all 250×250
- Flat array entity/item/shadow maps instead of HashMaps
- Flat 128-entry ASCII atlas array instead of HashMap lookup
- Partial grid upload: viewport + 30-cell margin only
- Pre-allocated viewport arrays in renderer structs (zero alloc/frame)
- Skip CPU lighting for GPU modes (pass None)
- Benchmark mode: --mode benchmark with per-subsystem timing
- GpuLightSource struct, light_count in push constants
- gather_sources_in_range() for viewport-scoped source gathering

Benchmark (600 ticks, release):
  Graphics: 531 FPS (was 386, +38%), render 1013us (was 1699us, -40%)
  ASCII:    402 FPS (was 313, +28%), render 1502us (was 2346us, -36%)

All 171 tests + 14 scenarios pass.
2026-06-21 16:09:45 +03:00

153 lines
3.9 KiB
Rust

use verbatim::ai::AiAction;
use verbatim::ai::GameSession;
fn setup() -> GameSession {
let mut s = GameSession::new_seeded(42);
s.init();
s
}
#[test]
fn descend_requires_stairs() {
let mut s = setup();
let start_depth = s.game.depth;
s.perform_action(&AiAction::Descend);
assert_eq!(
s.game.depth, start_depth,
"descend should not work without stairs"
);
}
#[test]
fn descend_increases_depth_on_stairs() {
let mut s = GameSession::new_seeded(42);
s.init_empty();
s.clear_area(90, 90, 50, 50);
let player = s.game.player.center(&s.game.entities);
let foot_x = player.0 as i32;
let foot_y = (player.1 + 3.0) as i32;
s.perform_action(&AiAction::SetCell {
x: foot_x,
y: foot_y,
material: "stairs".into(),
});
s.perform_action(&AiAction::SetCell {
x: foot_x,
y: foot_y + 1,
material: "stone".into(),
});
s.step(5);
let before = s.game.depth;
s.perform_action(&AiAction::Descend);
assert_eq!(
s.game.depth,
before + 1,
"descend should increase depth when on stairs"
);
}
#[test]
fn depth_shown_in_hud() {
let s = setup();
let player = s.game.entities.all()[0].clone();
let mut ui = verbatim::ui::UiLayer::new();
ui.draw_hud(
80,
25,
Some(&player),
s.game.tick,
verbatim::world::cell::MaterialId::Sand,
0,
0,
s.game.depth,
&s.game.player,
60.0,
);
let stats = format!(
"LV:{} XP:{} K:{} S:{} D:{} T:{}",
player.level, player.xp, 0, 0, s.game.depth, s.game.tick
);
let stats_w = verbatim::ui::UiLayer::text_width(&stats);
let stats_x = (80 - stats_w).max(0);
let line: String = (stats_x..80)
.step_by(3)
.map(|x| ui.get(x, 24).map(|c| c.ch).unwrap_or(' '))
.collect();
assert!(line.contains("D:1"), "HUD should show depth: {}", line);
}
#[test]
fn stairs_material_exists() {
let mut s = setup();
let cell = s.get_cell(0, 0);
assert_ne!(cell.material, "stairs", "empty corner should not be stairs");
s.perform_action(&AiAction::SetCell {
x: 50,
y: 50,
material: "stairs".into(),
});
let cell = s.get_cell(50, 50);
assert_eq!(
cell.material, "stairs",
"stairs material should be placeable"
);
}
#[test]
fn cell_stairs_bytes_roundtrip() {
let cell = verbatim::world::cell::Cell::new(verbatim::world::cell::MaterialId::Stairs);
let bytes = cell.to_bytes();
let cell2 = verbatim::world::cell::Cell::from_bytes(&bytes);
assert_eq!(cell2.material, verbatim::world::cell::MaterialId::Stairs);
}
#[test]
fn material_name_stairs() {
let mut s = setup();
let cell = s.get_cell(50, 50);
assert_eq!(cell.material, "empty");
s.perform_action(&AiAction::SetCell {
x: 50,
y: 50,
material: "stairs".into(),
});
let cell = s.get_cell(50, 50);
assert_eq!(cell.material, "stairs");
}
#[test]
fn descend_resets_world() {
let mut s = GameSession::new_seeded(42);
s.init_empty();
s.clear_area(90, 90, 50, 50);
let player = s.game.player.center(&s.game.entities);
let foot_x = player.0 as i32;
let foot_y = (player.1 + 3.0) as i32;
s.perform_action(&AiAction::SetCell {
x: foot_x,
y: foot_y,
material: "stairs".into(),
});
s.perform_action(&AiAction::SetCell {
x: foot_x,
y: foot_y + 1,
material: "stone".into(),
});
s.step(5);
let before = s.game.depth;
s.perform_action(&AiAction::Descend);
assert_eq!(s.game.depth, before + 1);
assert!(
s.game.player.entity(&s.game.entities).is_some(),
"player should respawn"
);
assert!(
s.game
.entities
.all()
.iter()
.all(|e| e.alive || e.kind == verbatim::entity::EntityKind::Corpse),
"old corpses should be gone"
);
}