Write the GALAGA tutorial chapters and the repeated-host-calls guide
docs/20 builds the engine and the boundary: the startup order, the starfield, actors and collision, booting a DEF-only script, the issue #8 mode workaround, the custom update hook, first light, screens, and the headless harness. docs/21 builds the three shared structures and the AI: the host type tables, the actor binding, the randomness route around issue #16, the measured case against structure arguments (issue #36), the three language rules that shape the script, the maneuvers, the argued formation decision, the script-death policy, and the interop proof. Every fenced block runs under tests/docs_examples.sh in both build configurations; five new preludes carry the C fragments. docs/10 gains the 'Calling a function every frame' section the chapters lean on: the per-call akbasic_environment_zero() rule, the set_mode(RUN) workaround, the clear_error() revival, and the case for rebinding over structure arguments. Index rows and chapter counts updated. Co-authored-by: andrew <andrew@aklabs.net> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
This commit is contained in:
8
tests/docs_preludes/galagacalls.post
Normal file
8
tests/docs_preludes/galagacalls.post
Normal file
@@ -0,0 +1,8 @@
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
(void)SCRIPT; (void)SINK; (void)SINKSTATE; (void)SOURCE;
|
||||
(void)args; (void)argp; (void)dtval; (void)result;
|
||||
(void)enemy; (void)actor; (void)dt;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
53
tests/docs_preludes/galagacalls.pre
Normal file
53
tests/docs_preludes/galagacalls.pre
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Prelude for the interpreter-facing fragments in docs/20: the boot sequence,
|
||||
* the ADDEM proof and the rebind-call-reset protocol, shown as runs of CATCH
|
||||
* calls. The statics are the ones examples/galaga/script.c keeps; the locals
|
||||
* are the superset every fragment draws from, void-cast in the postlude so an
|
||||
* unused one is not a warning.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <akerror.h>
|
||||
#include <akstdlib.h>
|
||||
|
||||
#include <akgl/actor.h>
|
||||
|
||||
#include <akbasic/environment.h>
|
||||
#include <akbasic/error.h>
|
||||
#include <akbasic/host.h>
|
||||
#include <akbasic/runtime.h>
|
||||
#include <akbasic/sink.h>
|
||||
|
||||
typedef struct galaga_docs_Enemy
|
||||
{
|
||||
int32_t kind;
|
||||
int32_t state;
|
||||
float homex;
|
||||
float homey;
|
||||
float t;
|
||||
int32_t hp;
|
||||
int32_t fire;
|
||||
float rnd;
|
||||
} galaga_docs_Enemy;
|
||||
|
||||
static akbasic_Runtime SCRIPT;
|
||||
static akbasic_TextSink SINK;
|
||||
static akbasic_StdioSink SINKSTATE;
|
||||
static char SOURCE[16384];
|
||||
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_docs_fragment(galaga_docs_Enemy *enemy, akgl_Actor *actor, float dt);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_docs_fragment(galaga_docs_Enemy *enemy, akgl_Actor *actor, float dt)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_Value args[2];
|
||||
akbasic_Value *argp[2];
|
||||
akbasic_Value dtval;
|
||||
akbasic_Value *result = NULL;
|
||||
|
||||
ATTEMPT {
|
||||
121
tests/docs_preludes/galagagame.pre
Normal file
121
tests/docs_preludes/galagagame.pre
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Prelude for file-scope fragments in docs/20 and docs/21 that assume the
|
||||
* galaga example's own declarations already exist -- the shared structures
|
||||
* from examples/galaga/galaga.h and the helpers a fragment calls but does not
|
||||
* define. The types are copied rather than included so a fragment compiles
|
||||
* against exactly what the chapter has shown so far; the helper declarations
|
||||
* are invented prototypes, per the prelude policy in MAINTENANCE.md.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <akerror.h>
|
||||
#include <akstdlib.h>
|
||||
|
||||
#include <akgl/actor.h>
|
||||
#include <akgl/character.h>
|
||||
#include <akgl/controller.h>
|
||||
#include <akgl/draw.h>
|
||||
#include <akgl/error.h>
|
||||
#include <akgl/game.h>
|
||||
#include <akgl/heap.h>
|
||||
#include <akgl/physics.h>
|
||||
#include <akgl/registry.h>
|
||||
#include <akgl/renderer.h>
|
||||
#include <akgl/sprite.h>
|
||||
#include <akgl/text.h>
|
||||
#include <akgl/ui.h>
|
||||
#include <akgl/util.h>
|
||||
|
||||
#include <akbasic/environment.h>
|
||||
#include <akbasic/error.h>
|
||||
#include <akbasic/host.h>
|
||||
#include <akbasic/runtime.h>
|
||||
#include <akbasic/sink.h>
|
||||
|
||||
#define GALAGA_ENEMY_BEE 0
|
||||
#define GALAGA_ENEMY_BUTTERFLY 1
|
||||
#define GALAGA_ENEMY_BOSS 2
|
||||
#define GALAGA_ENEMY_KINDS 3
|
||||
#define GALAGA_MAX_ENEMIES 40
|
||||
#define GALAGA_MAX_PLAYER_SHOTS 2
|
||||
#define GALAGA_MAX_ENEMY_SHOTS 8
|
||||
#define GALAGA_ES_ENTERING (1 << 0)
|
||||
#define GALAGA_ES_FORMATION (1 << 1)
|
||||
#define GALAGA_ES_DIVING (1 << 2)
|
||||
|
||||
typedef struct galaga_Enemy
|
||||
{
|
||||
int32_t kind;
|
||||
int32_t state;
|
||||
float homex;
|
||||
float homey;
|
||||
float t;
|
||||
int32_t hp;
|
||||
int32_t fire;
|
||||
float rnd;
|
||||
} galaga_Enemy;
|
||||
|
||||
typedef struct galaga_Shared
|
||||
{
|
||||
float playerx;
|
||||
float playery;
|
||||
int32_t wave;
|
||||
float rnd;
|
||||
} galaga_Shared;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GALAGA_SCREEN_TITLE = 0,
|
||||
GALAGA_SCREEN_PLAY,
|
||||
GALAGA_SCREEN_GAMEOVER,
|
||||
GALAGA_SCREEN_VICTORY
|
||||
} galaga_Screen;
|
||||
|
||||
typedef struct galaga_Game
|
||||
{
|
||||
galaga_Screen screen;
|
||||
int frame;
|
||||
float dt;
|
||||
bool autoplay;
|
||||
int score;
|
||||
int lives;
|
||||
int kills[GALAGA_ENEMY_KINDS];
|
||||
int shots[GALAGA_ENEMY_KINDS];
|
||||
int script_errors;
|
||||
akgl_Actor *player;
|
||||
float fire_cooldown;
|
||||
float respawn_timer;
|
||||
bool firing;
|
||||
bool moveleft;
|
||||
bool moveright;
|
||||
int player_shots_live;
|
||||
int enemy_shots_live;
|
||||
} galaga_Game;
|
||||
|
||||
extern galaga_Game galaga_game;
|
||||
extern galaga_Shared galaga_shared;
|
||||
extern galaga_Enemy galaga_enemies[GALAGA_MAX_ENEMIES];
|
||||
extern akgl_Actor *galaga_enemy_actors[GALAGA_MAX_ENEMIES];
|
||||
|
||||
float galaga_random(void);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_script_update_enemy(galaga_Enemy *enemy, akgl_Actor *actor, float dt);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_boom_spawn(float x, float y);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *enemy_fire(galaga_Enemy *enemy, akgl_Actor *from);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *kill_enemy(int index);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *player_update(akgl_Actor *obj);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *left_on(akgl_Actor *obj, SDL_Event *event);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *left_off(akgl_Actor *obj, SDL_Event *event);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *right_on(akgl_Actor *obj, SDL_Event *event);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *right_off(akgl_Actor *obj, SDL_Event *event);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *fire_on(akgl_Actor *obj, SDL_Event *event);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *fire_off(akgl_Actor *obj, SDL_Event *event);
|
||||
void shot_box(akgl_Actor *actor, SDL_FRect *dest);
|
||||
void enemy_box(akgl_Actor *actor, SDL_FRect *dest);
|
||||
void player_box(akgl_Actor *actor, SDL_FRect *dest);
|
||||
6
tests/docs_preludes/galagahost.post
Normal file
6
tests/docs_preludes/galagahost.post
Normal file
@@ -0,0 +1,6 @@
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
(void)event;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
55
tests/docs_preludes/galagahost.pre
Normal file
55
tests/docs_preludes/galagahost.pre
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Prelude for statement-context fragments in docs/20: runs of CATCH calls
|
||||
* from the galaga frame loop, shown without their scaffolding because the
|
||||
* ATTEMPT protocol is the scaffolding. Same policy as hostcalls.pre.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <akerror.h>
|
||||
#include <akstdlib.h>
|
||||
|
||||
#include <akgl/actor.h>
|
||||
#include <akgl/controller.h>
|
||||
#include <akgl/draw.h>
|
||||
#include <akgl/game.h>
|
||||
#include <akgl/heap.h>
|
||||
#include <akgl/renderer.h>
|
||||
#include <akgl/ui.h>
|
||||
|
||||
#include <akbasic/error.h>
|
||||
#include <akbasic/runtime.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GALAGA_SCREEN_TITLE = 0,
|
||||
GALAGA_SCREEN_PLAY,
|
||||
GALAGA_SCREEN_GAMEOVER,
|
||||
GALAGA_SCREEN_VICTORY
|
||||
} galaga_Screen;
|
||||
|
||||
struct galaga_docs_Game
|
||||
{
|
||||
galaga_Screen screen;
|
||||
float dt;
|
||||
akgl_Actor *player;
|
||||
};
|
||||
extern struct galaga_docs_Game galaga_game;
|
||||
extern akgl_Actor *galaga_enemy_actors[40];
|
||||
|
||||
akerr_ErrorContext AKERR_NOIGNORE *declare_title(void);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *declare_play(void);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *declare_end(void);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_docs_fragment(void);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *galaga_docs_fragment(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
SDL_Event event;
|
||||
|
||||
ATTEMPT {
|
||||
37
tests/docs_preludes/galagatypes.pre
Normal file
37
tests/docs_preludes/galagatypes.pre
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Prelude for the self-contained file-scope fragments in docs/20 and docs/21:
|
||||
* blocks that define a struct, a table or a whole function from scratch need
|
||||
* only the includes. Only compiled in the AKBASIC_WITH_AKGL build.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <akerror.h>
|
||||
#include <akstdlib.h>
|
||||
|
||||
#include <akgl/actor.h>
|
||||
#include <akgl/character.h>
|
||||
#include <akgl/controller.h>
|
||||
#include <akgl/draw.h>
|
||||
#include <akgl/error.h>
|
||||
#include <akgl/game.h>
|
||||
#include <akgl/heap.h>
|
||||
#include <akgl/physics.h>
|
||||
#include <akgl/registry.h>
|
||||
#include <akgl/renderer.h>
|
||||
#include <akgl/sprite.h>
|
||||
#include <akgl/text.h>
|
||||
#include <akgl/ui.h>
|
||||
#include <akgl/util.h>
|
||||
|
||||
#include <akbasic/environment.h>
|
||||
#include <akbasic/error.h>
|
||||
#include <akbasic/host.h>
|
||||
#include <akbasic/runtime.h>
|
||||
#include <akbasic/sink.h>
|
||||
@@ -2,5 +2,7 @@
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
(void)score;
|
||||
(void)argp;
|
||||
(void)result;
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* surrounding prose says exists but does not print.
|
||||
*/
|
||||
#include <akerror.h>
|
||||
#include <akbasic/environment.h>
|
||||
#include <akbasic/error.h>
|
||||
#include <akbasic/runtime.h>
|
||||
#include <akbasic/variable.h>
|
||||
@@ -23,5 +24,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_docs_fragment(void)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
int64_t score = 0;
|
||||
akbasic_Value *argp[4];
|
||||
akbasic_Value *result = NULL;
|
||||
|
||||
ATTEMPT {
|
||||
|
||||
Reference in New Issue
Block a user