GameEngine is now a state machine so I can push its design further and keep it solid.
This commit is contained in:
parent
1c89afaee2
commit
9e6c05eccd
5 changed files with 56 additions and 56 deletions
11
builder.cpp
11
builder.cpp
|
@ -76,20 +76,22 @@ void Builder::building(BuildEvent ev) {
|
|||
int rc = pclose(build_out);
|
||||
|
||||
if(rc == 0) {
|
||||
game.event(GameEvent::BUILD_SUCCESS);
|
||||
gui.build_works();
|
||||
} else {
|
||||
// BUG: make it not play two sounds
|
||||
game.event(GameEvent::BUILD_FAILED);
|
||||
gui.build_failed(true, build_cmd);
|
||||
}
|
||||
|
||||
build_out = NULL;
|
||||
game.event(GameEvent::BUILD_DONE);
|
||||
state(DONE);
|
||||
} else {
|
||||
auto m = parse_line(line);
|
||||
|
||||
if(m.match) {
|
||||
gui.output(format("HIT WITH {} @ {}:{}:{} {}", m.type, m.file_name, m.lnumber, m.col, m.message));
|
||||
game.hit(m.type);
|
||||
game.event(GameEvent::HIT);
|
||||
}
|
||||
state(READING);
|
||||
}
|
||||
|
@ -117,7 +119,7 @@ void Builder::start(BuildEvent ev) {
|
|||
|
||||
void Builder::waiting(BuildEvent ev) {
|
||||
if(listener->changes) {
|
||||
game.start_round();
|
||||
game.event(GameEvent::BUILD_START);
|
||||
gui.building();
|
||||
gui.output(format("CHANGES! Running build {}", build_cmd));
|
||||
state(FORKING);
|
||||
|
@ -162,11 +164,8 @@ void Builder::reading(BuildEvent ev) {
|
|||
}
|
||||
|
||||
void Builder::done(BuildEvent ev) {
|
||||
game.end_round();
|
||||
|
||||
if(game.is_dead()) {
|
||||
gui.you_died();
|
||||
game.reset();
|
||||
}
|
||||
|
||||
listener->reset_state();
|
||||
|
|
4
fsm.hpp
4
fsm.hpp
|
@ -5,15 +5,19 @@
|
|||
#define FSM_EV(S, F) case S: F(); break
|
||||
#define FSM_STATE(S, F, E) case S: F(E); break
|
||||
|
||||
#define FSM_STATE_LOG(C, S, F, E) case C::S: fmt::println(">> " #C " " #S ":" #F " event={}, state={}", int(E), int(_state)); F(E); fmt::println("<< " #C " state={}", int(_state)); break
|
||||
|
||||
template<typename S, typename E>
|
||||
class DeadSimpleFSM {
|
||||
protected:
|
||||
// BUG: don't put this in your class because state() won't work
|
||||
S _state = S::START;
|
||||
|
||||
public:
|
||||
virtual void event(E event) = 0;
|
||||
|
||||
void state(S next_state) {
|
||||
// fmt::println("STATE {}->{}", int(_state), int(next_state));
|
||||
_state = next_state;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <fmt/core.h>
|
||||
#include <fmt/color.h>
|
||||
#include "game_engine.hpp"
|
||||
#include <cassert>
|
||||
|
||||
const auto ERROR = fmt::emphasis::bold | fg(fmt::color::red);
|
||||
|
||||
|
@ -23,18 +24,6 @@ int GameEngine::determine_damage(string &type) {
|
|||
}
|
||||
}
|
||||
|
||||
void GameEngine::start_round() {
|
||||
hits_taken = 0;
|
||||
}
|
||||
|
||||
void GameEngine::end_round() {
|
||||
++rounds;
|
||||
if(hits_taken == 0) {
|
||||
++streak;
|
||||
heal();
|
||||
}
|
||||
}
|
||||
|
||||
void GameEngine::reset() {
|
||||
rounds = 0;
|
||||
streak = 0;
|
||||
|
@ -46,8 +35,6 @@ bool GameEngine::hit(string &type) {
|
|||
int damage = determine_damage(type);
|
||||
hit_points -= damage;
|
||||
++hits_taken;
|
||||
streak = 0;
|
||||
|
||||
return is_dead();
|
||||
}
|
||||
|
||||
|
@ -62,20 +49,52 @@ bool GameEngine::is_dead() {
|
|||
|
||||
void GameEngine::start(GameEvent ev) {
|
||||
state(GameState::IDLE);
|
||||
idle(ev);
|
||||
}
|
||||
|
||||
void GameEngine::idle(GameEvent ev) {
|
||||
if(ev == GameEvent::BUILD_START) {
|
||||
hits_taken = 0;
|
||||
state(GameState::IN_ROUND);
|
||||
} else {
|
||||
state(GameState::IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
void GameEngine::in_round(GameEvent ev) {
|
||||
if(ev == GameEvent::HIT) {
|
||||
string er_type = "error";
|
||||
hit(er_type); // FIXME: bring back error type
|
||||
if(is_dead()) {
|
||||
state(GameState::DEAD);
|
||||
} else {
|
||||
state(GameState::IN_ROUND);
|
||||
}
|
||||
} else if(ev == GameEvent::BUILD_SUCCESS) {
|
||||
state(GameState::SUCCESS);
|
||||
} else if(ev == GameEvent::BUILD_FAILED) {
|
||||
state(GameState::FAILURE);
|
||||
} else {
|
||||
state(GameState::IN_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
void GameEngine::dead(GameEvent ev) {
|
||||
state(GameState::DEAD);
|
||||
reset();
|
||||
state(GameState::IDLE);
|
||||
}
|
||||
|
||||
void GameEngine::alive(GameEvent ev) {
|
||||
state(GameState::ALIVE);
|
||||
void GameEngine::success(GameEvent ev) {
|
||||
assert(ev == GameEvent::BUILD_DONE && "success state expected BUILD_DONE");
|
||||
++rounds;
|
||||
++streak;
|
||||
heal();
|
||||
state(GameState::IDLE);
|
||||
}
|
||||
|
||||
void GameEngine::failure(GameEvent ev) {
|
||||
assert(ev == GameEvent::BUILD_DONE && "failure state expected BUILD_DONE");
|
||||
++rounds;
|
||||
streak = 0;
|
||||
state(GameState::IDLE);
|
||||
}
|
||||
|
|
|
@ -9,16 +9,17 @@
|
|||
using std::string;
|
||||
|
||||
enum class GameState {
|
||||
START, IDLE, IN_ROUND, DEAD, ALIVE
|
||||
START, IDLE, IN_ROUND, DEAD, SUCCESS, FAILURE
|
||||
};
|
||||
|
||||
|
||||
enum class GameEvent {
|
||||
BUILD_START, BUILD_END,
|
||||
HIT
|
||||
BUILD_START, BUILD_SUCCESS,
|
||||
BUILD_DONE, BUILD_FAILED, HIT
|
||||
};
|
||||
|
||||
class GameEngine : DeadSimpleFSM<GameState, GameEvent> {
|
||||
|
||||
std::map<string, int> damage_types{
|
||||
{"error", 4},
|
||||
{"warning", 1},
|
||||
|
@ -31,7 +32,6 @@ class GameEngine : DeadSimpleFSM<GameState, GameEvent> {
|
|||
int hits_taken = 0;
|
||||
int rounds = 0;
|
||||
int streak = 0;
|
||||
GameState _state = GameState::START;
|
||||
|
||||
GameEngine(int hp);
|
||||
|
||||
|
@ -47,7 +47,8 @@ class GameEngine : DeadSimpleFSM<GameState, GameEvent> {
|
|||
FSM_STATE(GameState::IDLE, idle, ev);
|
||||
FSM_STATE(GameState::IN_ROUND, in_round, ev);
|
||||
FSM_STATE(GameState::DEAD, dead, ev);
|
||||
FSM_STATE(GameState::ALIVE, alive, ev);
|
||||
FSM_STATE(GameState::SUCCESS, success, ev);
|
||||
FSM_STATE(GameState::FAILURE, failure, ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,12 +57,10 @@ class GameEngine : DeadSimpleFSM<GameState, GameEvent> {
|
|||
void idle(GameEvent ev);
|
||||
void in_round(GameEvent ev);
|
||||
void dead(GameEvent ev);
|
||||
void alive(GameEvent ev);
|
||||
|
||||
void success(GameEvent ev);
|
||||
void failure(GameEvent ev);
|
||||
|
||||
// current API that will die
|
||||
void start_round();
|
||||
void end_round();
|
||||
void heal();
|
||||
bool hit(string &type);
|
||||
void reset();
|
||||
|
|
|
@ -7,32 +7,11 @@ using namespace fmt;
|
|||
TEST_CASE("game engine can start and take hit", "[game_engine]") {
|
||||
// test fails on purpose right now
|
||||
GameEngine game{4};
|
||||
std::string err{"error"};
|
||||
game.start_round();
|
||||
game.hit(err);
|
||||
game.end_round();
|
||||
REQUIRE(game.is_dead() == true);
|
||||
REQUIRE(!game.is_dead() == true);
|
||||
}
|
||||
|
||||
TEST_CASE("", "[game_engine]") {
|
||||
// test fails on purpose right now
|
||||
GameEngine game{100};
|
||||
std::string err{"error"};
|
||||
|
||||
game.start_round();
|
||||
game.hit(err);
|
||||
game.end_round();
|
||||
|
||||
REQUIRE(game.hit_points < 100);
|
||||
REQUIRE(game.rounds == 1);
|
||||
REQUIRE(game.streak == 0);
|
||||
REQUIRE(game.is_dead() == false);
|
||||
|
||||
game.start_round();
|
||||
game.end_round();
|
||||
|
||||
REQUIRE(game.hit_points == 100);
|
||||
REQUIRE(game.rounds == 2);
|
||||
REQUIRE(game.streak == 1);
|
||||
REQUIRE(game.is_dead() == false);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue