diff --git a/config.hpp b/config.hpp index fcc3f7b..8539d3c 100644 --- a/config.hpp +++ b/config.hpp @@ -44,7 +44,10 @@ class Config { root["spriteHeight"] = h; } - int getMoveDelay() const {return root.get("moveDelay", 1000).asInt();} + int getMoveDelay() const {return root.get("moveDelay", 250).asInt();} + void setMoveDelay(int delay) {root["moveDelay"] = delay;} + + int getMaxMoves() const {return root.get("maxMoves", 1000000).asInt();} int getUnitsPerLeague() const noexcept {return root.get("unitsPerLeague", 10).asInt();} const std::unordered_map &getLeagueInfo() const noexcept {return leagueInfo;} diff --git a/deathgame/bees/start.dasm b/deathgame/bees/start.dasm index 8cebbc8..fd692e3 100644 --- a/deathgame/bees/start.dasm +++ b/deathgame/bees/start.dasm @@ -1,3 +1,3 @@ turn r -go r +go j 0 diff --git a/deathgame/chickens/chick.dasm b/deathgame/chickens/chick.dasm index a738a67..0867c07 100644 --- a/deathgame/chickens/chick.dasm +++ b/deathgame/chickens/chick.dasm @@ -1 +1,4 @@ +eat +go +back j 0 diff --git a/deathgame/config.json b/deathgame/config.json index 6f1e515..a6ae9dd 100644 --- a/deathgame/config.json +++ b/deathgame/config.json @@ -4,7 +4,7 @@ "columnNumber": 20, "rowNumber": 20, - "unitsPerLeague": 10, + "unitsPerLeague": 2, "leagues": { "bees": { "unitKinds": { diff --git a/executable.cpp b/executable.cpp index a41e365..bbc44c3 100644 --- a/executable.cpp +++ b/executable.cpp @@ -8,12 +8,11 @@ Executable::Executable(const std::string &path) { auto in = FileOpenIn(path); std::vector jmp; - Word offs = 0; std::string line; int n = 1; while (std::getline(in, line)) { - jmp.push_back(offs); + jmp.push_back((Word)bytecode.size()); std::vector insn; @@ -41,10 +40,8 @@ Executable::Executable(const std::string &path) { case 2: try { bytecode.push_back((Word)std::stoul(insn[1])); - } catch (const std::invalid_argument &) { - throw ExecutableError(path, n); - } - break; + break; + } catch (const std::invalid_argument &) {} default: throw ExecutableError(path, n); } @@ -147,8 +144,6 @@ Executable::Executable(const std::string &path) { throw ExecutableError(path, n); } - offs += bytecode[jmp.back()] == InsnTurn ? 1 : insn.size(); - n++; } } diff --git a/game.cpp b/game.cpp index eaa9c41..8503ed8 100644 --- a/game.cpp +++ b/game.cpp @@ -3,10 +3,11 @@ #include #include #include -#include #include #include "util.hpp" +//#include + Game::Game(const Config &config) : config(config), display( @@ -25,12 +26,14 @@ Game::Game(const Config &config) leagues[kv.first] = League(*this, kv.second); } -Game::~Game() noexcept { +Game::~Game() { thread.join(); } bool Game::isValidPosition(int x, int y) const { - return x < config.getColumnNumber() && y < config.getRowNumber(); + return + x > 0 && x < config.getColumnNumber() && + y > 0 && y < config.getRowNumber(); } bool Game::isFreePosition(int x, int y) const { @@ -52,12 +55,19 @@ void Game::start() { auto ileague = std::next(leagues.begin(), GetRandom((std::uint32_t)leagues.size())); + int move = 0; + int maxMoves = config.getMaxMoves(); + while (threadCont) { auto &league = ileague->second; - auto unit = league.getNextUnit(); - if (!unit) { - auto next = ileague++; + if (auto unit = league.getNextUnit()) { + unit->execInsn(*this, league); + + if (++move == maxMoves) + break; + } else { + auto next = std::next(ileague); leagues.erase(ileague); if (leagues.size() < 2) { @@ -70,8 +80,6 @@ void Game::start() { continue; } - unit->execInsn(*this, league); - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); if (++ileague == leagues.end()) @@ -84,6 +92,10 @@ void Game::start() { threadCont = false; } +/*std::ostream &operator<<(std::ostream &os, const Game &game) { + +}*/ + static Sprite spriteFromInfo(const std::string &directory, const SpriteInfo *info) { switch (info->getType()) { case SpriteInfo::Type::SquareRGB: { @@ -123,17 +135,53 @@ League::League(Game &game, const LeagueInfo &info) { } } +std::uint64_t League::getTotalBiomass() const { + std::uint64_t total = 0; + + for (auto &unit : units) + total += unit.getWeight(); + + return total; +} + +void Unit::Position::move(const Game &game, Direction dir) { + int x1 = x; + int y1 = y; + + switch (dir) { + case Direction::North: + y1--; + break; + case Direction::East: + x1++; + break; + case Direction::South: + y1++; + break; + case Direction::West: + x1--; + break; + } + + if (game.isFreePosition(x1, y1)) { + x = x1; + y = y1; + } +} + void Unit::execInsn(Game &game, League &league) { + //std::cout << "pc == " << pc << " "; + auto eat = [this] { weight++; }; auto go = [this, &game] { + //std::cout << "go\n"; + if (!loseWeight(game, 1)) return; - std::cout << "go\n"; - game.board[position.getY() * game.getConfig().getColumnNumber() + position.getX()] = nullptr; game.display.blitSprite(position.getX(), position.getY(), 0); @@ -144,6 +192,9 @@ void Unit::execInsn(Game &game, League &league) { }; auto clon = [this, &game, &league] { + if (!loseWeight(game, 10)) + return; + auto pos = position; pos.move(game, direction); @@ -152,15 +203,15 @@ void Unit::execInsn(Game &game, League &league) { if (unit) unit->weight += 2; else { - league.units.push_back(Unit(sprite, *exec, pos.getX(), pos.getY())); + league.units.push_back(Unit(sprite, *exec, pos.getX(), pos.getY(), true)); unit = &league.units.back(); } }; auto str = [this, &game] { if (loseWeight(game, 1)) - if (auto enemy = findEnemy(game)) - enemy->damage(game, weight); + if (auto enemy = findEnemy(game)) + enemy->damage(game, weight); }; auto left = [this] { @@ -176,6 +227,7 @@ void Unit::execInsn(Game &game, League &league) { }; auto turn = [this] { + //std::cout << "turn\n"; direction = getRandomDirection(); }; @@ -190,12 +242,6 @@ void Unit::execInsn(Game &game, League &league) { case InsnRep::Str: str(); break; - case InsnRep::Left: - left(); - break; - case InsnRep::Right: - right(); - break; } insnRepCnt--; @@ -203,10 +249,9 @@ void Unit::execInsn(Game &game, League &league) { struct Handler { std::function fn; std::size_t size; + bool pseudo; }; - std::cout << pc << '\n'; - std::array handlers = { #define REP_HANDLER(c, f) \ Handler {\ @@ -220,27 +265,27 @@ void Unit::execInsn(Game &game, League &league) { f();\ \ insnRepCnt--;\ - }, .size = 2\ + }, .size = 2, .pseudo = false\ } REP_HANDLER(Eat, eat), REP_HANDLER(Go, go), - Handler {.fn = clon, .size = 1}, + Handler {.fn = clon, .size = 1, .pseudo = false}, - REP_HANDLER(Str, str), - REP_HANDLER(Left, left), - REP_HANDLER(Right, right), + REP_HANDLER(Str, str), - Handler {.fn = back, .size = 1}, - Handler {.fn = turn, .size = 1}, + Handler {.fn = left, .size = 1, .pseudo = true}, + Handler {.fn = right, .size = 1, .pseudo = true}, + Handler {.fn = back, .size = 1, .pseudo = true}, + Handler {.fn = turn, .size = 1, .pseudo = true}, Handler { .fn = [this] { if (weight > (*exec)[pc + 1]) pc = (*exec)[pc + 2] - 3; }, - .size = 3 + .size = 3, .pseudo = true }, Handler { @@ -248,14 +293,15 @@ void Unit::execInsn(Game &game, League &league) { if (weight < (*exec)[pc + 1]) pc = (*exec)[pc + 2] - 3; }, - .size = 3 + .size = 3, .pseudo = true }, Handler { .fn = [this] { + //std::cout << "j " << (*exec)[pc + 1] << '\n'; pc = (*exec)[pc + 1] - 2; }, - .size = 2 + .size = 2, .pseudo = true }, Handler { @@ -263,23 +309,32 @@ void Unit::execInsn(Game &game, League &league) { if (findEnemy(game)) pc = (*exec)[pc + 1] - 2; }, - .size = 2 + .size = 2, .pseudo = true } #undef REP_HANDLER }; - auto opcode = (*exec)[pc]; - assert(opcode < handlers.size()); - - auto &h = handlers[opcode]; - h.fn(); - pc += h.size; - - std::cout << "pc == " << pc << '\n'; + int mad = 0; + while (true) { + auto opcode = (*exec)[pc]; + assert(opcode < handlers.size()); + + auto &h = handlers[opcode]; + h.fn(); + pc += h.size; + + if (!h.pseudo) + break; + + if (++mad >= 31) { + loseWeight(game, 5); + break; + } + } - //if (pc >= exec->size()) - // pc = 0; + if (pc >= exec->size()) + pc = 0; } } @@ -317,29 +372,32 @@ bool Unit::loseWeight(Game &game, Weight loss) { return true; game.board[position.getY() * game.getConfig().getColumnNumber() + position.getX()] = nullptr; + game.display.blitSprite(position.getX(), position.getY(), 0); return false; } Unit *League::getNextUnit() { + if (units.empty()) + return nullptr; + auto *unit = &units[nextUnitIndex]; while (unit->isDead()) { - auto a = std::next(units.begin(), nextUnitIndex + 1); - auto b = a; - while (b->isDead()) + auto a = units.begin(); + std::advance(a, nextUnitIndex); + + auto b = a + 1; + while (b != units.end() && b->isDead()) b++; units.erase(a, b); - if (units.empty()) { - - } + if (units.empty()) + return nullptr; - if (nextUnitIndex == units.size()) + if (nextUnitIndex >= units.size()) nextUnitIndex = 0; - - unit = &units[nextUnitIndex]; } if (++nextUnitIndex >= units.size()) diff --git a/game.hpp b/game.hpp index d07bb47..b827efc 100644 --- a/game.hpp +++ b/game.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "executable.hpp" #include "ui.hpp" @@ -27,6 +28,8 @@ class Executable; */ class Game { + friend std::ostream &operator<<(std::ostream &os, const Game &game); + friend League; friend Unit; @@ -45,12 +48,14 @@ class Game { public: Game(const Config &config); - ~Game() noexcept; + ~Game(); SpriteID registerSprite(const Sprite &sprite) {return display.registerSprite(sprite);} const Config &getConfig() const noexcept {return config;}; + const LeagueMap &getLeagues() const noexcept {return leagues;} + bool isValidPosition(int x, int y) const; bool isFreePosition(int x, int y) const; @@ -67,14 +72,23 @@ class League { private: std::unordered_map unitKinds; std::size_t nextUnitIndex = 0; + + std::uint64_t totalBiomass; + public: League() {} League(Game &game, const LeagueInfo &info); std::vector units; Unit *getNextUnit(); + + std::uint64_t getTotalBiomass() const; }; +static inline bool operator<(const League &a, const League &b) { + return a.getTotalBiomass() < b.getTotalBiomass(); +} + /* * Unit. */ @@ -98,30 +112,7 @@ class Unit { int getX() const noexcept {return x;} int getY() const noexcept {return y;} - void move(const Game &game, Direction dir) { - int x1 = x; - int y1 = y; - - switch (dir) { - case Direction::North: - y1++; - break; - case Direction::East: - x1++; - break; - case Direction::South: - y1++; - break; - case Direction::West: - x1--; - break; - } - - if (game.isFreePosition(x1, y1)) { - x = x1; - y = y1; - } - } + void move(const Game &game, Direction dir); }; static Direction getRandomDirection(); @@ -129,15 +120,13 @@ class Unit { enum class InsnRep { Eat, Go, - Str, - Left, - Right + Str }; typedef long Weight; - Unit(/*Game &game, const League &league,*/ SpriteID sprite, Executable &exec, int x, int y) - : /*game(game), league(league),*/ sprite(sprite), exec(&exec), position(x, y) {} + Unit(/*Game &game, const League &league,*/ SpriteID sprite, Executable &exec, int x, int y, bool newborn = false) + : /*game(game), league(league),*/ sprite(sprite), exec(&exec), position(x, y), weight(newborn? 2 : 5) {} /*Unit(const Unit &src) : position(src.position), direction(src.direction), @@ -168,7 +157,7 @@ class Unit { InsnRep insnRep; int insnRepCnt = 0; - Weight weight = 5; + Weight weight; bool loseWeight(Game &game, Weight loss = 1); diff --git a/main.cpp b/main.cpp index 918dd28..a3f8d46 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "game.hpp" #include "util.hpp" @@ -16,8 +17,9 @@ static void help_exit(const char *exec, int code) { "Game setup files must be in the current working directory.\n" "\n" "Options:\n" - " -help show this help text\n" - " -sprite-size WxH set sprite size overriding configuration\n"; + " -help show this help text\n" + " -sprite-size WxH set sprite size overriding configuration\n" + " -move-delay DELAY set the delay between moves\n"; std::exit(code); } @@ -25,6 +27,8 @@ static void help_exit(const char *exec, int code) { int main(int argc, char *argv[]) { int spriteWidth = -1; int spriteHeight = -1; + + int moveDelay = -1; for (int i = 1; i < argc; i++) { std::unordered_map> options = { @@ -45,8 +49,22 @@ int main(int argc, char *argv[]) { try { spriteWidth = std::stoi(wh.substr(0, sep)); spriteHeight = std::stoi(wh.substr(sep + 1)); - } catch (...) { - std::cerr << "Flag '-sprite-size' argument is invalid, it must be two integers separated by 'x'.\n"; + } catch (const std::invalid_argument &) { + std::cerr << "Flag '-sprite-size' value is invalid, it must be two integers separated by 'x'.\n"; + help_exit(argv[0], 1); + } + }}, + + {"-move-delay", [argv, argc, &i, &moveDelay] { + if (++i >= argc) { + std::cerr << "Argument expected after flag '-move-delay'.\n"; + help_exit(argv[0], 1); + } + + try { + moveDelay = std::atoi(argv[i]); + } catch (const std::invalid_argument &) { + std::cerr << "Flag '-move-delay' value is invalid, it must be an integer.\n"; help_exit(argv[0], 1); } }} @@ -69,6 +87,9 @@ int main(int argc, char *argv[]) { if (spriteWidth > 0) config.setSpriteSize(spriteWidth, spriteHeight); + if (moveDelay >= 0) + config.setMoveDelay(moveDelay); + std::cout << "Dumping league information...\n"; for (auto &kv : config.getLeagueInfo()) { @@ -92,6 +113,11 @@ int main(int argc, char *argv[]) { Game game(config); game.start(); + + std::cout << "Finish!\n"; + + for (auto &kv : game.getLeagues()) + std::cout << "- " << kv.first << ": " << kv.second.getTotalBiomass() << '\n'; } catch (const std::exception &exc) { std::cout << exc.what() << std::endl; return 1; diff --git a/util.hpp b/util.hpp index 79e60b7..c4db57f 100644 --- a/util.hpp +++ b/util.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -67,5 +67,34 @@ class FileError : public std::exception { virtual const char *what() const noexcept {return reason.c_str();} }; +template +class Lazy { +private: + typedef std::function Getter; + + Getter get; + Value value; + + bool ready = false; + +public: + Lazy(const Getter &get) : get(get) {}; + + operator Value() { + if (ready) + return value; + + return value = get(); + } + + Value *operator->() { + return &value; + } + + void reset() { + ready = false; + } +}; + #endif