chainreaction/src/core/board2d.cpp

152 lines
3.8 KiB
C++

#include <cstdio>
#include <iostream>
#include "board2d.hpp"
using namespace core;
Board2D::Board2D(const size_type w, const size_type h)
{
init(w, h);
}
void Board2D::add_unit(const Coord c)
{
std::cerr << "add_unit " << c.x << "," << c.y << std::endl;
auto& space = at(c);
if (!(space.capacity >= space.value)) throw InvalidMove {"Space is overloaded"};
space.value++;
}
void Board2D::capture(const Coord c, const Player p) noexcept
{
std::cerr << static_cast<int>(p) << " captures " << c.x << "," << c.y << std::endl;
at(c).owner = p;
}
void Board2D::init(const size_type w, const size_type h)
{
_width = w;
_height = h;
_spaces.reserve(w * h); // NOLINT
for (size_type y = 0; y < h; y++) {
for (size_type x = 0; x < w; x++) {
_spaces.push_back(Coord(x, y));
if (x == 0 || x == w - 1) {
if (y == 0 || y == h - 1) {
_spaces.back().capacity = 1;
}
else {
_spaces.back().capacity = 2;
}
}
else {
if (y == 0 || y == h - 1) {
_spaces.back().capacity = 2;
}
else {
_spaces.back().capacity = 3;
}
}
}
}
}
auto Board2D::move(const Coord c, const Player p) -> Generator<Board2D>
{
const auto& space = at(c);
if (space.owner == Player::NONE) capture(c, p);
else if (space.owner != p) throw InvalidMove {"Player is not the Space owner"};
add_unit(c);
co_yield *this;
std::vector<Coord> overloaded;
if (is_overloaded(c)) {
overloaded.push_back(c);
do {
const auto src = overloaded.back();
const auto targets = neighbours(src);
overloaded.pop_back();
auto spreader = spread(src, targets, p);
while (spreader) co_yield spreader();
for (const auto t : targets) {
if (is_overloaded(t)) {
std::cerr << "overloaded: " << t.x << "," << t.y << std::endl;
overloaded.push_back(t);
};
}
} while (!overloaded.empty());
}
}
auto Board2D::neighbours(const Coord c) const noexcept -> std::vector<Coord>
{
std::vector<Coord> nghbrs;
if (c.x > 0) nghbrs.push_back({c.x - 1, c.y});
if (c.x < _width - 1) nghbrs.push_back({c.x + 1, c.y});
if (c.y > 0) nghbrs.push_back({c.x, c.y - 1});
if (c.y < _height - 1) nghbrs.push_back({c.x, c.y + 1});
return nghbrs;
}
auto Board2D::is_overloaded(Coord c) const noexcept -> bool
{
const auto& s = at(c);
return s.value > s.capacity;
}
auto Board2D::size() const noexcept -> std::array<size_type, 2>
{
return {_width, _height};
}
auto Board2D::spread(const Coord src, const std::vector<Coord>& targets, Player p)
-> Generator<Board2D>
{
auto& s = at(src);
std::cerr << "spread " << src.x << "," << src.y << " " << s.value << "/"
<< s.capacity << std::endl;
{
const auto count = targets.size();
if (s.value != static_cast<int>(count)) {
throw std::logic_error("cannot spread: unit count and target "
"spaces count differ");
}
}
s.reset();
co_yield *this;
for (const auto& c : targets) {
capture(c, p);
add_unit(c);
co_yield *this;
}
}
auto Board2D::operator()(const Coord c) const noexcept -> const Board2D::Space&
{
return _spaces.at(coord2idx(c));
}
auto Board2D::at(const Coord c) noexcept -> Space&
{
return _spaces.at(coord2idx(c));
}
auto Board2D::at(const Coord c) const noexcept -> const Space&
{
return _spaces.at(coord2idx(c));
}
auto Board2D::coord2idx(const Coord c) const noexcept -> size_type
{
return c.y * _width + c.x;
}