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