Skip to content

Commit 90035cb

Browse files
Optimizaclaude
andcommitted
feat(engine-host): A* pathfinding portable + 5 walkmaps + 54 tests (sub 2.3)
Cierro la sub-etapa 2.3 con el patron de motor host ya estandar (drift + comportamiento + goldens). El bloque A* del motor (`_walk_passable`, `_snap_walkable`, `_heuristic`, `engine_astar`, lineas 1097-1214 de agemki_engine.c) es 100% portable: puro algoritmo, sin VGA, sin malloc, sin globals del game loop. Por eso la copia byte-exact en tests/engine_host/lib/astar.c funciona sin parches. Por que estas tres garantias: - Drift: el regex captura el bloque entero del motor y lo compara byte-a-byte contra la copia. Si tu cambias engine_astar manana, el test rojo te avisa antes de que el SHA-256 de los goldens diverja por motivos no obvios. - Comportamiento (50 casos sobre 5 walkmaps): rangos de longitud, reachability esperada, primer/ultimo waypoint coherentes con start/target tras snap. Si el motor pierde la capacidad de encontrar rutas, el test falla con un mensaje legible (`expected 0 to be >= 14`), no solo con "SHA cambio". - Goldens deterministas (manifest.json): SHA-256 del raw del runner por caso. Cambiar el orden de expansion, los costes 10/14, o el tie-breaking altera el hash y rompe el test. Walkmaps elegidos para cubrir las situaciones reales del motor: - room_open: ruta directa, valida diagonales y clamping de coords. - room_with_obstacle: bloque 12x6 forzando detour por arriba/abajo. - corridor_l: forma de L, A* tiene que doblar. - maze_simple: pasillos en patron #, varias rutas posibles. - disconnected: barrera vertical, valida que `return 0` cuando no hay ruta funciona. Un guardarrail explicito: el caso `corr/snap-radius-cap` documenta que `_snap_walkable` esta acotado a radio 4 celdas. Si Javi amplia el radio en el motor (cambiando el `r <= 4` en agemki_engine.c:1107), este caso empieza a devolver una ruta y el SHA cambia. Es un anclaje sobre una decision de diseno que de otra manera pasaria desapercibida. Decision: NO escribi reimpl JS del A* (a diferencia de CRC32 y PCX). Para algoritmos con tie-breaking sensible al orden, replicar la logica en JS aporta poco: si la JS no es identica al C el bit-exact falla por motivos que no son bug. Drift + goldens dan suficiente cobertura. Documentado en la seccion "Como anadir un nuevo modulo" del README. Por que `astar_batch` ademas de `astar`: 50 spawns en Windows costarian ~2.5s solo en arrancar el binario. Una sola spawn que lee 50 lineas de stdin + cachea el ultimo walkmap_path entre casos del mismo mapa reduce el coste a ~150ms cross-platform. Tambien anoto F-07 en tests/FINDINGS.md (descubierto en este mismo trabajo): `runner.dSYM/` se colaba en `git status` porque la regla gitignore tenia un comentario inline. El fix va en commit aparte. Estado: 311 tests verde (257 + 54). Tiempo total npm test: ~10s, identico mac+windows x Node 22+24. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent af6cd2e commit 90035cb

14 files changed

Lines changed: 1667 additions & 29 deletions

File tree

goldens/engine/astar/manifest.json

Lines changed: 757 additions & 0 deletions
Large diffs are not rendered by default.

tests/FINDINGS.md

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ seguridad: cada vez que un test no encajaba con lo que esperaba, había
66
que entender por qué — y a veces el "por qué" era un bug latente que
77
nadie había disparado todavía.
88

9-
Cinco cosas en total: **cuatro bugs reales** (3 con fix incluido, 1 que
10-
requiere decisión tuya), y **un falso positivo (F-02)** que reporté como
11-
bug pero al verificarlo en Terminal nativa no era. Ninguna te bloquea
12-
hoy mismo; las verdaderas las descubrió la suite haciendo su trabajo, y
13-
la falsa la descubrí yo metiendo la pata — ambas cosas están aquí
14-
documentadas porque la honestidad importa más que la apariencia.
9+
Siete cosas en total: **seis bugs reales** (cuatro con fix incluido en
10+
este PR, dos abiertos que requieren decisión tuya), y **un falso
11+
positivo (F-02)** que reporté como bug pero al verificarlo en Terminal
12+
nativa no era. Ninguna te bloquea hoy mismo; las verdaderas las
13+
descubrió la suite haciendo su trabajo, y la falsa la descubrí yo
14+
metiendo la pata — ambas cosas están aquí documentadas porque la
15+
honestidad importa más que la apariencia.
1516

1617
Cada finding tiene:
1718
- **Resumen en cristiano** — qué pasa, sin tecnicismos.
@@ -36,6 +37,7 @@ Cada finding tiene:
3637
| F-04 | Alta | Codegen DAT | ✅ aplicado en commit `fix(dat,build)` | Sí, en este PR |
3738
| F-05 | Crítica | Codegen DAT | ✅ aplicado en commit `fix(dat,build)` | Sí, en este PR |
3839
| F-06 | Media | Motor (walkmap) | abierto | No — requiere tu decisión sobre walkmaps no-rectangulares |
40+
| F-07 | Baja | Tooling (git) | ✅ aplicado en commit `fix(.gitignore)` | Sí, en este PR |
3941

4042
Severidad:
4143
- **Crítica**: el motor falla silenciosamente en runtime.
@@ -585,6 +587,73 @@ helpers que sí existen (`_walk_passable`, `_heuristic`).
585587
586588
---
587589
590+
## F-07 — `.gitignore` con comentario inline en patrón `*.dSYM/`
591+
592+
**Severidad:** Baja · **Estado:** ✅ aplicado · **Fix incluido:** Sí (en este PR)
593+
594+
### Resumen en cristiano
595+
596+
Al añadir reglas para los artefactos de debug del runner host (clang en
597+
mac genera `runner.dSYM/`, MSVC en Windows genera `.pdb` y `.ilk`),
598+
puse el comentario explicativo **en la misma línea** que el patrón:
599+
600+
```gitignore
601+
tests/engine_host/*.dSYM/ # debug symbols dir generado por clang en mac
602+
```
603+
604+
Eso **no es válido en `.gitignore`**. A diferencia de muchos otros
605+
formatos, gitignore no soporta comentarios inline: el `#` solo
606+
funciona si está al inicio de la línea. La regla queda como un patrón
607+
literal `tests/engine_host/*.dSYM/ # debug symbols dir generado por clang en mac`,
608+
que jamás va a coincidir con nada. Resultado: `runner.dSYM/` se cuela
609+
en `git status` cada vez que clang genera símbolos, contaminando el
610+
working tree de cualquier contributor en mac.
611+
612+
### Reproducción
613+
614+
```bash
615+
node tests/engine_host/build.mjs --force
616+
git status --short # tests/engine_host/runner.dSYM/ aparece como untracked
617+
git check-ignore -v tests/engine_host/runner.dSYM/ # NO IGNORED
618+
```
619+
620+
### Impacto
621+
622+
Cosmético, pero molesto:
623+
624+
- `git status` siempre con ruido.
625+
- Riesgo de hacer `git add .` y commitear símbolos de debug por error
626+
(un `runner.dSYM/` típico ocupa varios MB).
627+
- Cualquier hook que dependa de "working tree limpio" se confunde.
628+
629+
### Fix
630+
631+
Cada comentario en su propia línea:
632+
633+
```gitignore
634+
# debug symbols dir generado por clang en mac
635+
tests/engine_host/*.dSYM/
636+
# debug symbols Windows
637+
tests/engine_host/*.pdb
638+
# incremental linker artifacts Windows
639+
tests/engine_host/*.ilk
640+
```
641+
642+
Aplicado en commit `fix(.gitignore): los comentarios inline no son válidos`.
643+
644+
### Lección para Claude (yo)
645+
646+
Lo introduje yo en una sesión anterior por escribir reglas + comentarios
647+
de un golpe sin probar. **Verificar siempre con
648+
`git check-ignore -v <ruta>`** después de añadir reglas — es la única
649+
forma de saber si la regla aplica de verdad.
650+
651+
### Para ti, ahora mismo
652+
653+
Nada. Ya está arreglado en este PR.
654+
655+
---
656+
588657
## Plan de commits sugerido
589658
590659
Estos hallazgos sugieren cuatro commits separables. El orden recomendado:
@@ -621,6 +690,9 @@ Cada vez que algo no encajaba, bisección + lectura del código.
621690
`generateDats()` desde un test.
622691
- F-05 detectado por un assertion `indexIsSorted` en el test de estructura
623692
semántica (chunks deben venir ordenados — del comentario en la spec).
693+
- F-07 detectado al ver `runner.dSYM/` en `git status` después de
694+
compilar el runner; `git check-ignore -v` confirmó que el patrón
695+
con comentario inline no se aplicaba.
624696
625697
Filosofía: **goldens no como ritual, sino como red real de seguridad**.
626698

tests/README.md

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,51 @@ lógica observable rompa al menos un test**.
1212

1313
## Índice
1414

15-
1. [Convención de idioma](#convención-de-idioma-mandatorio)
16-
2. [Quickstart](#quickstart)
17-
3. [Comandos del día a día](#comandos-del-día-a-día)
18-
4. [Layout completo](#layout-completo)
19-
5. [Qué cubre cada fichero de test](#qué-cubre-cada-fichero-de-test)
20-
6. [Cómo funciona el hook PostToolUse](#cómo-funciona-el-hook-posttooluse)
21-
7. [Cómo funciona el skill `/golden-update`](#cómo-funciona-el-skill-golden-update)
22-
8. [Cómo añadir un test nuevo](#cómo-añadir-un-test-nuevo)
23-
9. [Cómo añadir o modificar un fixture](#cómo-añadir-o-modificar-un-fixture)
24-
10. [Cómo regenerar los goldens (workflow seguro)](#cómo-regenerar-los-goldens-workflow-seguro)
25-
11. [Troubleshooting](#troubleshooting)
26-
12. [Por qué este diseño](#por-qué-este-diseño)
27-
13. [Pipeline DOSBox-X (futuro, Phase 3)](#pipeline-dosbox-x-futuro-phase-3)
28-
14. [Referencias](#referencias)
15+
1. [Estado actual del trabajo](#estado-actual-del-trabajo)
16+
2. [Convención de idioma](#convención-de-idioma-mandatorio)
17+
3. [Quickstart](#quickstart)
18+
4. [Comandos del día a día](#comandos-del-día-a-día)
19+
5. [Layout completo](#layout-completo)
20+
6. [Qué cubre cada fichero de test](#qué-cubre-cada-fichero-de-test)
21+
7. [Cómo funciona el hook PostToolUse](#cómo-funciona-el-hook-posttooluse)
22+
8. [Cómo funciona el skill `/golden-update`](#cómo-funciona-el-skill-golden-update)
23+
9. [Cómo añadir un test nuevo](#cómo-añadir-un-test-nuevo)
24+
10. [Cómo añadir o modificar un fixture](#cómo-añadir-o-modificar-un-fixture)
25+
11. [Cómo regenerar los goldens (workflow seguro)](#cómo-regenerar-los-goldens-workflow-seguro)
26+
12. [Troubleshooting](#troubleshooting)
27+
13. [Por qué este diseño](#por-qué-este-diseño)
28+
14. [Pipeline DOSBox-X (futuro, Phase 3)](#pipeline-dosbox-x-futuro-phase-3)
29+
15. [Referencias](#referencias)
30+
31+
---
32+
33+
## Estado actual del trabajo
34+
35+
Lo que está terminado, lo que viene y por qué — para que sepas dónde
36+
estamos sin tener que leer todo el `git log`.
37+
38+
| Etapa | Qué cubre | Estado |
39+
|-------|-----------|--------|
40+
| Phase 1 — Stores | 10 stores Zustand: state shape + acciones + IPC mocks | ✅ 178 tests |
41+
| Phase 2 — Codegen DAT | `game.json → .DAT` byte-equal contra goldens, validación de cabecera AGMK + orden de chunks | ✅ 26 tests |
42+
| Phase 3a sub 2.1 — CRC32 | Driver de motor host (clang) + `_sfx_crc32` byte-exact JS↔C | ✅ 22 tests |
43+
| Phase 3a sub 2.2 — PCX RLE | `_pcx_decode` (sección RLE, sin VGA) byte-exact JS↔C + 6 PCX golden | ✅ 16 tests |
44+
| Phase 3a sub 2.3 — A* | `engine_astar` + helpers: drift + 50 casos comportamiento + manifest deterministas | ✅ 54 tests |
45+
| Phase 3a sub 2.4 — Lightmap blur | El módulo de lighting del motor (último portable) | 🔜 siguiente |
46+
| Phase 3b — DOSBox-X build | Compilación Watcom autónoma desde Node, captura de logs | ⏳ pendiente |
47+
| Phase 3c — Runtime BMP | Captura de frames en puntos deterministas del game loop | ⏳ pendiente |
48+
49+
**Total ahora mismo**: **311 tests** verde en mac+windows × Node 22+24
50+
(matrix CI). Tiempo total `npm test`: ~10 segundos.
51+
52+
**Hallazgos**: 7 documentados en [`tests/FINDINGS.md`](FINDINGS.md). Cuatro
53+
con fix aplicado en este PR (F-01, F-04, F-05, F-07), dos abiertos
54+
esperando tu decisión (F-03 documentación, F-06 polígonos walkmap), uno
55+
descartado como falso positivo mío (F-02).
56+
57+
**Próximo paso (cuando retome)**: Sub 2.4 (lightmap blur). Una vez
58+
cerrado, pasamos a Phase 3b (DOSBox-X build pipeline) que ya no necesita
59+
clang — ejecuta el motor real con tu Watcom y captura logs.
2960

3061
---
3162

@@ -129,6 +160,9 @@ npm run goldens:update
129160

130161
# Regenerar los fixtures binarios (PCX, MIDI, WAV) desde el builder
131162
node tests/fixtures/builder.mjs
163+
164+
# Regenerar los walkmaps binarios para los tests de A* (sub 2.3)
165+
node tests/fixtures/walkmaps-builder.mjs
132166
```
133167

134168
Para entender qué dispara cada cambio automáticamente, ver
@@ -151,6 +185,8 @@ agemki/
151185
│ │
152186
│ ├── fixtures/ ← inputs deterministas para los tests
153187
│ │ ├── builder.mjs script que regenera los binarios PCX/MIDI/WAV + JSON
188+
│ │ ├── walkmaps-builder.mjs script que regenera los 5 walkmaps binarios para A* (sub 2.3)
189+
│ │ ├── walkmaps/ 5 .bin (room_open, room_with_obstacle, corridor_l, maze_simple, disconnected)
154190
│ │ ├── minimal/ 1 room, 1 char, 1 idioma — ejercita cada serializer
155191
│ │ │ ├── game.json
156192
│ │ │ ├── rooms/room_001/room.json
@@ -188,7 +224,8 @@ agemki/
188224
│ └── engine_host/ ← Phase 3a — runner C compilado con clang en host
189225
├── lib/
190226
│ ├── crc32.c copia byte-exact de _sfx_crc32 (sub 2.1)
191-
│ └── pcx_decode.c copia byte-exact de _pcx_decode RLE (sub 2.2)
227+
│ ├── pcx_decode.c copia byte-exact de _pcx_decode RLE (sub 2.2)
228+
│ └── astar.c copia byte-exact del bloque A* (sub 2.3)
192229
├── include/ag_test.h header compartido del runner
193230
├── runner.c entrypoint con dispatcher de tests
194231
├── build.mjs compila con clang (cross-platform mac/win)
@@ -203,7 +240,8 @@ agemki/
203240
│ │ ├── FONTS.DAT
204241
│ │ └── manifest.json sha256 + size + numBlocks por DAT
205242
│ ├── engine/ outputs binarios de lógica pura del motor host
206-
│ │ └── pcx/ SHA-256 de cada PCX decodificado (sub 2.2)
243+
│ │ ├── pcx/ SHA-256 de cada PCX decodificado (sub 2.2)
244+
│ │ └── astar/ manifest.json con { id, len, sha256 } por caso A* (sub 2.3)
207245
│ └── runtime/ (futuro Phase 3c) BMP frames del motor en DOSBox-X
208246
│ capturados en puntos deterministas del game loop
209247
@@ -295,6 +333,54 @@ LF→CRLF translation por defecto que corrompe bytes 0x0A. El runner
295333
C emite el buffer como hex (2 chars por byte) para evitar el issue
296334
sin parches específicos de OS.
297335

336+
### `tests/golden/engine-host-astar.test.js` (54 tests, Phase 3a sub 2.3)
337+
338+
Tests del pathfinding A* del motor: el bloque `_walk_passable` /
339+
`_snap_walkable` / `_heuristic` / `engine_astar` que vive en
340+
`resources/engine/agemki_engine.c:1097-1214`. Las cuatro funciones son
341+
puramente algorítmicas — sin tocar VGA, sin malloc, sin globals del
342+
loop principal — así que la copia portable en
343+
`tests/engine_host/lib/astar.c` es byte-exact con el motor.
344+
345+
Cubre:
346+
347+
- **Drift detection** (sin clang): el bloque A* en `lib/astar.c` sigue
348+
byte-exact con el del motor. Cualquier cambio (orden de expansión,
349+
costes 10/14, tie-breaking, radio del snap) se detecta con un solo
350+
test rojo y un diff legible.
351+
- **Comportamiento sobre 5 walkmaps × 10 casos** (50 cases): los
352+
walkmaps los genera `tests/fixtures/walkmaps-builder.mjs` y son
353+
representativos de las situaciones reales en el motor:
354+
- `room_open` (40×18, todo walkable): rutas directas, diagonales,
355+
edges, clamping de coords fuera de grid.
356+
- `room_with_obstacle`: bloque 12×6 en el centro forzando detour
357+
(skirt-left, skirt-right, start dentro del bloque snapping fuera).
358+
- `corridor_l`: forma de L (sólo fila inferior + columna derecha
359+
walkables) — A* debe doblar.
360+
- `maze_simple`: cuatro pasillos en patrón # con múltiples rutas.
361+
- `disconnected`: barrera vertical completa — verifica que A*
362+
devuelve 0 cuando no hay ruta.
363+
- **Goldens deterministas**: cada uno de los 50 casos guarda
364+
`{ id, walkmap, start, target, reachable, len, sha256 }` en
365+
`goldens/engine/astar/manifest.json`. El `sha256` es del raw del
366+
runner (formato `N|x1,y1|…|xN,yN`), así cualquier cambio en el orden
367+
de waypoints o en la ruta elegida rompe el hash.
368+
369+
Una propiedad importante que también queda anclada: el caso
370+
`corr/snap-radius-cap` documenta que `_snap_walkable` está acotado a
371+
radio 4 celdas. Si un start cae a 7+ celdas del walkable más cercano,
372+
A* devuelve 0. Si Javi amplía el radio en el motor, este caso se pone
373+
en verde de manera "inesperada" y el test rojo de `len/sha256` lo
374+
avisa — un guardarraíl explícito sobre una decisión de diseño que de
375+
otra forma pasaría desapercibida.
376+
377+
Por qué `astar_batch` y no spawn por caso: 50 invocaciones × ~50ms de
378+
spawn en Windows (vs Linux/macOS ~5ms) sumarían ~2.5s sólo en arrancar
379+
el binario. El batch hace una sola spawn, lee 50 líneas de stdin y
380+
emite 50 líneas de stdout, además cacheando el último walkmap_path
381+
para no releer el binario entre casos consecutivos del mismo mapa
382+
(reducción extra ~5×). El test corre en ~150ms en cualquier OS.
383+
298384
### `tests/golden/dat.test.js` (26 tests)
299385

300386
Para cada fixture (`minimal`):
@@ -519,7 +605,7 @@ Si tocas `resources/engine/agemki_audio.c` o `agemki_engine.c`:
519605

520606
### Cómo añadir un nuevo módulo (el patrón)
521607

522-
Cuando arranques una sub-etapa nueva (ej: A* en sub 2.3), el patrón:
608+
Sub 2.1 (CRC32), 2.2 (PCX) y 2.3 (A*) lo siguen. El patrón:
523609

524610
1. **Localizar la función pura** en el motor. Identifica qué deps HW
525611
tiene (`outp/inp/int86`, globals, etc.). Si el módulo está
@@ -537,13 +623,19 @@ Cuando arranques una sub-etapa nueva (ej: A* en sub 2.3), el patrón:
537623
- Función `runner<Modulo>(...)` que invoca el binario y parsea
538624
stdout.
539625
- Función `js<Modulo>(...)` que reimplementa el algoritmo en JS
540-
puro, sirve de referencia.
626+
puro, sirve de referencia. **Opcional**: tiene sentido cuando el
627+
algoritmo es lo bastante mecánico para reescribirlo sin bugs (CRC32,
628+
RLE de PCX). Para algoritmos con tie-breaking sensible al orden
629+
(A*), reimplementar JS aporta poco valor — basta drift + goldens.
541630
- Función `detect<Modulo>Drift()` con la regex que extrae el
542631
bloque del motor real y lo compara con la copia local.
543632
6. **Test vitest** en `tests/golden/engine-host-<modulo>.test.js`
544633
con tres bloques:
545634
- `describe('drift detection (sin clang)', ...)` — independiente.
546-
- `describe.skipIf(noClang)('bit-exact con JS', ...)` — el grueso.
635+
- `describe.skipIf(noClang)('bit-exact con JS' / 'comportamiento', ...)`
636+
— el grueso. "Bit-exact con JS" si hay reimpl JS; "comportamiento"
637+
si testeamos propiedades observables (ruta existe / no, longitudes
638+
en rangos, primer y último waypoint coherentes).
547639
- `describe.skipIf(noClang)('goldens', ...)` — SHA-256 vs
548640
`goldens/engine/<modulo>/`.
549641
7. **Hook**: el matcher actual ya cubre `resources/engine/*.c|*.h`,
@@ -781,14 +873,32 @@ se generan deterministicamente desde `tests/fixtures/builder.mjs`.
781873
```
782874
6. Commit con razón explícita.
783875

876+
### Modificar los walkmaps (sub 2.3)
877+
878+
Los 5 `.bin` de `tests/fixtures/walkmaps/` los genera
879+
`tests/fixtures/walkmaps-builder.mjs` (40×18 celdas, formato:
880+
`uint16 LE w`, `uint16 LE h`, `w*h bytes` con 1=walkable / 0=block).
881+
882+
1. Edita `walkmaps-builder.mjs` (cambiar geometría, añadir un mapa nuevo).
883+
2. Ejecuta `node tests/fixtures/walkmaps-builder.mjs`.
884+
3. `npm test` fallará los SHA-256 del manifest A* — esperado.
885+
4. Regenera el manifest:
886+
```bash
887+
UPDATE_GOLDENS=1 npm test -- tests/golden/engine-host-astar.test.js
888+
```
889+
5. Si añadiste un walkmap, edita `tests/golden/engine-host-astar.test.js`
890+
y suma sus 10 casos al array `CASES`.
891+
6. Stagea fixtures + manifest + test en el mismo commit.
892+
784893
### Por qué los binarios están commiteados
785894

786-
Los `.PCX`, `.MID`, `.WAV` van al repo por dos razones:
895+
Los `.PCX`, `.MID`, `.WAV` y los walkmap `.bin` van al repo por dos razones:
787896
- **Reproducibilidad**: cualquier contributor clona y testea sin pasos extra.
788897
- **Determinismo**: cualquier diff binario en estos ficheros es señal de
789898
algo raro (¿se modificó el builder sin querer?). Git lo detecta.
790899

791-
El `builder.mjs` documenta cómo regenerarlos y mantiene la lógica visible.
900+
Los dos `*-builder.mjs` documentan cómo regenerarlos y mantienen la
901+
lógica visible.
792902

793903
---
794904

tests/engine_host/include/ag_test.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,16 @@ unsigned long ag_test_crc32(const char* s);
2525
int ag_test_pcx_decode(const uint8_t* src, uint32_t src_size,
2626
uint8_t* dst, uint16_t* out_w, uint16_t* out_h);
2727

28+
/**
29+
* Pathfinding A* (subset portable del motor).
30+
* Coordenadas en píxeles mundo (no celdas). El subset convierte
31+
* internamente con WALKMAP_CELL_SIZE=8.
32+
* Devuelve la longitud de la ruta (nº de waypoints) o 0 si no hay.
33+
*/
34+
typedef struct { int16_t x, y; } AgTestPoint;
35+
36+
void ag_test_walkmap_load(int w, int h, const uint8_t* bm);
37+
int ag_test_astar(int16_t sx, int16_t sy, int16_t tx, int16_t ty,
38+
AgTestPoint* path, int max_path);
39+
2840
#endif

0 commit comments

Comments
 (0)