4 This is a front end that serves as a basic automatic test of the game. At this
5 point it's very simple and basically just checks if anything really
6 substantial wasn't broken.
8 This fronted tries to play the game and see if it behaves how expected. If you
9 change anything substantial in the game, this test may start to fail and you
10 may need to adjust it.
12 by Miloslav Ciz (drummyfish), 2019
14 Released under CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/)
15 plus a waiver of all other intellectual property. The goal of this work is
16 be and remain completely in the public domain forever, available for any use
23 #define SFG_SCREEN_RESOLUTION_X 67
24 #define SFG_SCREEN_RESOLUTION_Y 31
25 #define SFG_BACKGROUND_BLUR 1
26 #define SFG_DITHERED_SHADOW 1
32 uint8_t screen
[SFG_SCREEN_RESOLUTION_X
* SFG_SCREEN_RESOLUTION_Y
];
33 uint8_t keys
[SFG_KEY_COUNT
];
35 uint32_t gameTime
= 0;
37 int8_t SFG_keyPressed(uint8_t key
)
42 void SFG_getMouseOffset(int16_t *x
, int16_t *y
)
46 uint32_t SFG_getTimeMs()
51 void SFG_sleepMs(uint16_t gameTimeMs
)
57 static inline void SFG_setPixel(uint16_t x
, uint16_t y
, uint8_t colorIndex
)
59 screen
[y
* SFG_SCREEN_RESOLUTION_X
+ x
] = colorIndex
;
62 void SFG_playSound(uint8_t soundIndex
, uint8_t volume
)
66 void SFG_setMusic(uint8_t value
)
70 void SFG_processEvent(uint8_t event
, uint8_t data
)
74 void SFG_save(uint8_t data
[SFG_SAVE_SIZE
])
78 uint8_t SFG_load(uint8_t data
[SFG_SAVE_SIZE
])
83 void printTestHeading(const char *text
)
85 printf("\n~~~~~ testing: %s ~~~~~\n\n",text
);
88 const char colors
[8] = " .,-;imX";
92 const char *c
= screen
;
94 for (uint8_t y
= 0; y
< SFG_SCREEN_RESOLUTION_Y
; ++y
)
96 for (uint8_t x
= 0; x
< SFG_SCREEN_RESOLUTION_X
; ++x
)
98 putchar(*c
!= 7 ? colors
[(*c
) % 8] : '@');
108 puts("===== TESTING ANARCH =====\n");
110 puts("initializing");
112 #define ASSERT(text,cond) { printf("checking \"%s\": ",text); if (cond) puts("OK"); else { puts("ERROR"); return 1; }}
116 double msPerFrame
= 0;
118 ASSERT("frame == 0",SFG_game
.frame
== 0);
121 printTestHeading("music and sounds");
123 static const uint8_t expectedSamples
[] = { 1, 0, 0, 0, 0, 0, 255, 251, 80, 240, 240, 10, 0, 6, 4, 0 };
127 for (uint32_t i
= 0; i
< (SFG_TRACK_COUNT
* SFG_TRACK_SAMPLES
); ++i
)
129 uint8_t sample
= SFG_getNextMusicSample();
133 ASSERT("music sample", sample
== expectedSamples
[pos
]);
138 ASSERT("sfx sample",SFG_GET_SFX_SAMPLE(0,0) == 128);
139 ASSERT("sfx sample",SFG_GET_SFX_SAMPLE(1,200) == 112);
140 ASSERT("sfx sample",SFG_GET_SFX_SAMPLE(3,512) == 112);
141 ASSERT("sfx sample",SFG_GET_SFX_SAMPLE(4,1000) == 128);
145 printTestHeading("levels");
147 SFG_TileDefinition t
;
150 t
= SFG_getMapTile(&SFG_level1
,10,8,&p
);
151 ASSERT("level1 tile",SFG_TILE_FLOOR_HEIGHT(t
) == 14 && SFG_TILE_CEILING_HEIGHT(t
) == 0 && SFG_TILE_FLOOR_TEXTURE(t
) == 4 && p
== 0);
153 t
= SFG_getMapTile(&SFG_level3
,9,20,&p
);
154 ASSERT("level3 tile",SFG_TILE_FLOOR_HEIGHT(t
) == 17 && SFG_TILE_CEILING_HEIGHT(t
) == 13 && SFG_TILE_FLOOR_TEXTURE(t
) == 0 && p
== 128);
156 t
= SFG_getMapTile(&SFG_level5
,-9,0,&p
);
157 ASSERT("outside tile",SFG_TILE_FLOOR_HEIGHT(t
) == 31 && SFG_TILE_CEILING_HEIGHT(t
) == 0 && SFG_TILE_FLOOR_TEXTURE(t
) == 7 && p
== 0);
161 printTestHeading("gameplay");
163 for (uint8_t i
= 0; i
< SFG_KEY_COUNT
; ++i
)
166 #define STEP(ms) { printf("(fr %d, step %d ms) ",SFG_game.frame,ms); gameTime += ms; SFG_mainLoopBody(); }
167 #define PRESS(k) { printf("(press %d) ",k); keys[k] = 1; }
168 #define RELEASE(k) { printf("(release %d) ",k); keys[k] = 0; }
169 #define TEST_PIXEL(x,y,v) { printf("(testing pixel %d %d)",x,y); uint8_t val = screen[y * SFG_SCREEN_RESOLUTION_X + y]; if (val != v) { printf("\nERROR: expcted %d, got %d\n",v,val); return 1; }}
173 PRESS(SFG_KEY_DOWN
) // select "exit"
175 RELEASE(SFG_KEY_DOWN
)
179 ASSERT("menu item == exit",SFG_getMenuItem(SFG_game
.selectedMenuItem
) == SFG_MENU_ITEM_EXIT
)
181 PRESS(SFG_KEY_UP
) // select "play"
184 PRESS(SFG_KEY_A
) // confirm "play"
189 PRESS(SFG_KEY_A
) // skip intro
193 ASSERT("state == playing",SFG_game
.state
== SFG_GAME_STATE_PLAYING
)
196 PRESS(SFG_KEY_RIGHT
) // turn
198 RELEASE(SFG_KEY_RIGHT
)
199 PRESS(SFG_KEY_UP
) // take ammo
203 ASSERT("weapon == shotgun",SFG_player
.weapon
== SFG_WEAPON_SHOTGUN
)
206 PRESS(SFG_KEY_LEFT
) // turn back
208 RELEASE(SFG_KEY_LEFT
)
209 PRESS(SFG_KEY_UP
) // go to barrels
214 RELEASE(SFG_KEY_RIGHT
)
215 PRESS(SFG_KEY_A
) // shoot barrels
220 ASSERT("health < 100",SFG_player
.health
< 100)
227 RELEASE(SFG_KEY_LEFT
)
233 RELEASE(SFG_KEY_RIGHT
)
238 PRESS(SFG_KEY_A
) // shoot monster
240 RELEASE(SFG_KEY_A
) // shoot monster
243 PRESS(SFG_KEY_NEXT_WEAPON
) // switch to machine gun
245 RELEASE(SFG_KEY_LEFT
)
246 RELEASE(SFG_KEY_NEXT_WEAPON
)
249 ASSERT("weapon == machine gun",SFG_player
.weapon
== SFG_WEAPON_MACHINE_GUN
)
252 PRESS(SFG_KEY_A
) // shoot
256 ASSERT("health == 74",SFG_player
.health
== 74)
263 #define FRAMES 1000000
265 printf("\nbenchmarking frame time on %d frames.\n",FRAMES
);
272 msPerFrame
= (((double) (t2
- t1
)) * 1000.0) / (CLOCKS_PER_SEC
* FRAMES
);
273 RELEASE(SFG_KEY_LEFT
)
276 PRESS(SFG_KEY_C
) // open menu
282 ASSERT("state == menu",SFG_game
.state
== SFG_GAME_STATE_MENU
)
285 PRESS(SFG_KEY_A
) // exit game
289 ASSERT("game exitted",SFG_mainLoopBody() == 0)
299 puts("======================================\n\nDone.\nEverything seems OK.");
301 printf("benchmarked ms per frame: %lf\n",msPerFrame
);