More cleanups of the tests and fixes to the room and door positioning.

This commit is contained in:
Zed A. Shaw 2026-03-15 22:53:00 -04:00
parent 8e2a691337
commit 02d23bb77d
11 changed files with 75 additions and 78 deletions

View file

@ -1,6 +1,6 @@
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
all: build test all: build
reset: reset:
ifeq '$(OS)' 'Windows_NT' ifeq '$(OS)' 'Windows_NT'
@ -37,7 +37,7 @@ tracy_build:
meson compile -j 10 -C builddir meson compile -j 10 -C builddir
test: build test: build
./builddir/runtests -d yes "[map-fail]" ./builddir/runtests -d yes
run: build test run: build test
ifeq '$(OS)' 'Windows_NT' ifeq '$(OS)' 'Windows_NT'
@ -60,7 +60,7 @@ clean:
meson compile --clean -C builddir meson compile --clean -C builddir
debug_test: build debug_test: build
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests "[map-fail]" gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests
win_installer: win_installer:
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'

View file

@ -9,16 +9,6 @@ using std::string;
using matrix::Matrix; using matrix::Matrix;
namespace maze { namespace maze {
inline size_t rand(size_t i, size_t j) {
if(i < j) {
return Random::uniform(i, j);
} else if(j < i) {
return Random::uniform(j, i);
} else {
return i;
}
}
inline bool complete(Matrix& maze, size_t width, size_t height) { inline bool complete(Matrix& maze, size_t width, size_t height) {
for(size_t row = 1; row < height; row += 2) { for(size_t row = 1; row < height; row += 2) {
for(size_t col = 1; col < width; col += 2) { for(size_t col = 1; col < width; col += 2) {
@ -89,8 +79,9 @@ namespace maze {
} }
bool Builder::room_should_exist(Room& room, bool allow_dupes) { bool Builder::room_should_exist(Room& room, bool allow_dupes) {
if(!matrix::inbounds($walls, room.x, room.y) || // padding by 1 for the perimeter wall
!matrix::inbounds($walls, room.x + room.width, room.y + room.height)) if(!matrix::inbounds($walls, room.x - 1, room.y - 1) ||
!matrix::inbounds($walls, room.x + room.width + 2, room.y + room.height + 2))
{ {
return false; return false;
} }
@ -187,7 +178,7 @@ namespace maze {
$walls[row][col] = 0; $walls[row][col] = 0;
} else { } else {
// found neighbors, pick random one // found neighbors, pick random one
auto nb = n[rand(size_t(0), n.size() - 1)]; auto nb = n[Random::abs(size_t(0), n.size() - 1)];
$walls[nb.y][nb.x] = 0; $walls[nb.y][nb.x] = 0;
size_t row = (nb.y + on.y) / 2; size_t row = (nb.y + on.y) / 2;
@ -196,12 +187,14 @@ namespace maze {
on = nb; on = nb;
} }
} }
if($rooms.size() > 0) place_rooms();
} }
void Builder::place_rooms() { void Builder::place_rooms() {
for(auto& room : $rooms) { for(auto& room : $rooms) {
for(matrix::rectangle it{$walls, room.x, room.y, room.width, room.height}; it.next();) { for(matrix::rectangle it{$walls, room.x, room.y, room.width, room.height}; it.next();) {
$walls[it.y][it.x] = 0; $walls[it.y][it.x] = SPACE_VALUE;
} }
} }
} }
@ -239,7 +232,7 @@ namespace maze {
} }
void Builder::dump(const std::string& msg, bool path_too) { void Builder::dump(const std::string& msg, bool path_too) {
auto wall_copy = $walls; Matrix wall_copy = $walls;
// mark the rooms too, but not if pathing // mark the rooms too, but not if pathing
if(!path_too) { if(!path_too) {
@ -307,6 +300,11 @@ namespace maze {
} }
} }
bool Builder::valid_door(size_t x, size_t y) {
return (space_available(x, y - 1) && space_available(x, y + 1)) // north south
|| (space_available(x - 1, y) && space_available(x + 1, y));
}
void Builder::place_doors() { void Builder::place_doors() {
for(auto room : $rooms) { for(auto room : $rooms) {
int best_longest = 0; int best_longest = 0;
@ -319,17 +317,14 @@ namespace maze {
// can't path out of the room, so now punch a hole until it can // can't path out of the room, so now punch a hole until it can
matrix::perimeter it{room.x - 1, room.y - 1, room.width + 2, room.height + 2}; matrix::perimeter it{room.x - 1, room.y - 1, room.width + 2, room.height + 2};
while(it.next()) {
// don't use corners
if((it.x == room.x - 1 && it.y == room.y - 1) ||
(it.x == room.x - 1 && it.y == room.y + 2) ||
(it.x == room.width + 2 && it.y == room.y + 2) ||
(it.x == room.width + 2 && it.y == room.y - 1)) {
continue;
}
while(it.next()) {
if($walls[it.y][it.x] == WALL_VALUE) { if($walls[it.y][it.x] == WALL_VALUE) {
// valid doors are free north/south or east/west
if(!valid_door(it.x, it.y)) continue;
$walls[it.y][it.x] = SPACE_VALUE; $walls[it.y][it.x] = SPACE_VALUE;
longest = compute_paths(room.x, room.y); longest = compute_paths(room.x, room.y);
// keep track of the best door so far, which is the one with the longest path // keep track of the best door so far, which is the one with the longest path
@ -348,8 +343,6 @@ namespace maze {
$walls[best_door.y][best_door.x] = SPACE_VALUE; $walls[best_door.y][best_door.x] = SPACE_VALUE;
$doors.insert_or_assign(best_door, true); $doors.insert_or_assign(best_door, true);
} }
fmt::println("map valid after doors? {}", validate());
} }
void Builder::inner_box(size_t outer_size, size_t inner_size) { void Builder::inner_box(size_t outer_size, size_t inner_size) {
@ -408,7 +401,7 @@ namespace maze {
// initial path test can just use one room then look for // initial path test can just use one room then look for
// any cells that are empty in the walls map but unpathed in the paths // any cells that are empty in the walls map but unpathed in the paths
Room test_room = $rooms.at(rand(0, $rooms.size() - 1)); Room test_room = $rooms.at(Random::abs(size_t(0), $rooms.size() - 1));
compute_paths(test_room.x, test_room.y); compute_paths(test_room.x, test_room.y);
for(matrix::each_cell it{$walls}; it.next();) { for(matrix::each_cell it{$walls}; it.next();) {
@ -422,10 +415,10 @@ namespace maze {
} }
bool Builder::space_available(size_t x, size_t y) { bool Builder::space_available(size_t x, size_t y) {
bool in_bounds = matrix::inbounds($walls, x, y); return (matrix::inbounds($walls, x, y) && // in bounds
bool is_space = $walls[y][x] == SPACE_VALUE; $walls[y][x] == SPACE_VALUE && // is a space
bool not_perimeter = x > 0 && y > 0 && x < $width - 2 && y < $height - 2; !$doors.contains({x, y}) && // no door there
return in_bounds && is_space && not_perimeter; (x > 0 && y > 0 && x < $width - 2 && y < $height - 2)); // not perimeter;
} }
void Builder::punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y) { void Builder::punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y) {

View file

@ -47,6 +47,7 @@ namespace maze {
void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y); void punch_dead_end(size_t at_x, size_t at_y, size_t x, size_t y);
bool space_available(size_t x, size_t y); bool space_available(size_t x, size_t y);
int compute_paths(size_t x, size_t y); int compute_paths(size_t x, size_t y);
bool valid_door(size_t x, size_t y);
}; };
std::pair<Builder, bool> script(Map& map, nlohmann::json& config); std::pair<Builder, bool> script(Map& map, nlohmann::json& config);

View file

@ -9,5 +9,4 @@ namespace Random {
int tick = Random::uniform_real(float(from), float(to)); int tick = Random::uniform_real(float(from), float(to));
return std::chrono::milliseconds{tick}; return std::chrono::milliseconds{tick};
} }
} }

View file

@ -27,5 +27,15 @@ namespace Random {
return rand(GENERATOR); return rand(GENERATOR);
} }
auto abs(auto i, auto j) {
if(i < j) {
return Random::uniform(i, j);
} else if(j < i) {
return Random::uniform(j, i);
} else {
return i;
}
}
std::chrono::milliseconds milliseconds(int from, int to); std::chrono::milliseconds milliseconds(int from, int to);
} }

View file

@ -30,11 +30,6 @@ namespace combat {
for(auto& [entity, enemy] : $combatants) { for(auto& [entity, enemy] : $combatants) {
if(enemy.combat->ap < enemy.combat->max_ap) { if(enemy.combat->ap < enemy.combat->max_ap) {
int new_ap = std::min(enemy.combat->max_ap, enemy.combat->ap_delta + enemy.combat->ap); int new_ap = std::min(enemy.combat->max_ap, enemy.combat->ap_delta + enemy.combat->ap);
// only add up to the max
fmt::println("enemy {} get more ap {}->{}",
entity, enemy.combat->ap, new_ap);
enemy.combat->ap = new_ap; enemy.combat->ap = new_ap;
} }
} }

View file

@ -3,39 +3,39 @@
#include <string> #include <string>
#include <array> #include <array>
constexpr const int INV_SLOTS=16; constexpr int INV_SLOTS=16;
constexpr const int TEXTURE_WIDTH=256; constexpr int TEXTURE_WIDTH=256;
constexpr const int TEXTURE_HEIGHT=256; constexpr int TEXTURE_HEIGHT=256;
constexpr const int RAY_VIEW_WIDTH=900; constexpr int RAY_VIEW_WIDTH=900;
constexpr const int RAY_VIEW_HEIGHT=600; constexpr int RAY_VIEW_HEIGHT=600;
constexpr const int SCREEN_WIDTH=1280; constexpr int SCREEN_WIDTH=1280;
constexpr const int SCREEN_HEIGHT=720; constexpr int SCREEN_HEIGHT=720;
constexpr const int RAY_VIEW_X=(SCREEN_WIDTH - RAY_VIEW_WIDTH); constexpr int RAY_VIEW_X=(SCREEN_WIDTH - RAY_VIEW_WIDTH);
constexpr const int RAY_VIEW_Y=0; constexpr int RAY_VIEW_Y=0;
constexpr const int GLOW_LIMIT=220; constexpr int GLOW_LIMIT=220;
constexpr const int LIGHT_MULTIPLIER=2.5; constexpr int LIGHT_MULTIPLIER=2.5;
constexpr const float AIMED_AT_BRIGHTNESS=0.2f; constexpr float AIMED_AT_BRIGHTNESS=0.2f;
constexpr const int MAP_TILE_DIM=64; constexpr int MAP_TILE_DIM=64;
constexpr const int ICONGEN_MAP_TILE_DIM=64; constexpr int ICONGEN_MAP_TILE_DIM=64;
constexpr const int PLAYER_SPRITE_DIR_CORRECTION=270; constexpr int PLAYER_SPRITE_DIR_CORRECTION=270;
constexpr const int RENDER_DISTANCE=500; constexpr int RENDER_DISTANCE=500;
constexpr const int ROOM_SIZE=3; constexpr int ROOM_SIZE=3;
constexpr const int BOSS_VIEW_WIDTH=1080; constexpr int BOSS_VIEW_WIDTH=1080;
constexpr const int BOSS_VIEW_HEIGHT=SCREEN_HEIGHT; constexpr int BOSS_VIEW_HEIGHT=SCREEN_HEIGHT;
constexpr const int BOSS_VIEW_X=SCREEN_WIDTH - BOSS_VIEW_WIDTH; constexpr int BOSS_VIEW_X=SCREEN_WIDTH - BOSS_VIEW_WIDTH;
constexpr const int BOSS_VIEW_Y=0; constexpr int BOSS_VIEW_Y=0;
constexpr const bool VSYNC=false; constexpr bool VSYNC=true;
constexpr const int FRAME_LIMIT=60; constexpr int FRAME_LIMIT=60;
constexpr const int NUM_SPRITES=1; constexpr int NUM_SPRITES=1;
constexpr const int MAX_LOG_MESSAGES=17; constexpr int MAX_LOG_MESSAGES=17;
#ifdef NDEBUG #ifdef NDEBUG
constexpr const bool DEBUG_BUILD=false; constexpr bool DEBUG_BUILD=false;
#else #else
constexpr const bool DEBUG_BUILD=true; constexpr bool DEBUG_BUILD=true;
#endif #endif

View file

@ -152,11 +152,11 @@ namespace gui {
try_move(1, true); try_move(1, true);
break; break;
case ROTATE_LEFT: case ROTATE_LEFT:
$main_ui.plan_rotate(-1, 0.25f); $main_ui.plan_rotate(-1, DEFAULT_ROTATE);
state(State::ROTATING); state(State::ROTATING);
break; break;
case ROTATE_RIGHT: case ROTATE_RIGHT:
$main_ui.plan_rotate(1, 0.25f); $main_ui.plan_rotate(1, DEFAULT_ROTATE);
state(State::ROTATING); state(State::ROTATING);
break; break;
case MAP_OPEN: case MAP_OPEN:

View file

@ -65,18 +65,17 @@ TEST_CASE("battle operations fantasy", "[combat-battle]") {
while(auto act = battle.next()) { while(auto act = battle.next()) {
auto& [enemy, wants_to, cost, enemy_state] = *act; auto& [enemy, wants_to, cost, enemy_state] = *act;
fmt::println(">>>>> entity: {} wants to {} cost={}; has {} HP; {} ap", // fmt::println(">>>>> entity: {} wants to {} cost={}; has {} HP; {} ap",
enemy.entity, wants_to, // enemy.entity, wants_to,
cost, enemy.combat->hp, // cost, enemy.combat->hp,
enemy.combat->ap); // enemy.combat->ap);
switch(enemy_state) { switch(enemy_state) {
case BattleHostState::agree: case BattleHostState::agree:
fmt::println("HOST and PLAYER requests match {}, doing it.", // fmt::println("HOST and PLAYER requests match {}, doing it.", wants_to);
wants_to);
break; break;
case BattleHostState::disagree: case BattleHostState::disagree:
fmt::println("REBELIOUS ACT: {}", wants_to); // fmt::println("REBELIOUS ACT: {}", wants_to);
battle.clear_requests(); battle.clear_requests();
REQUIRE(battle.$player_requests.size() == 0); REQUIRE(battle.$player_requests.size() == 0);
break; break;
@ -86,7 +85,7 @@ TEST_CASE("battle operations fantasy", "[combat-battle]") {
} }
break; break;
case BattleHostState::out_of_ap: case BattleHostState::out_of_ap:
fmt::println("ENEMY OUT OF AP"); // fmt::println("ENEMY OUT OF AP");
break; break;
} }
} }

View file

@ -25,7 +25,7 @@ TEST_CASE("camera control", "[map]") {
Point center = map.center_camera({10,10}, 5, 5); Point center = map.center_camera({10,10}, 5, 5);
map.dump(center.x, center.y); // map.dump(center.x, center.y);
REQUIRE(center.x == 8); REQUIRE(center.x == 8);
REQUIRE(center.y == 8); REQUIRE(center.y == 8);

View file

@ -7,7 +7,7 @@
#include "algos/maze.hpp" #include "algos/maze.hpp"
#include "algos/stats.hpp" #include "algos/stats.hpp"
#define DUMP 1 #define DUMP 0
using std::string; using std::string;
using matrix::Matrix; using matrix::Matrix;