chainreaction/src/core/board2d.cpp

215 lines
5.2 KiB
C++

#include <iostream>
#include <queue>
#include "board2d.hpp"
#include "player.hpp"
using namespace core;
using namespace std;
Board2D::Board2D(const size_type w, const size_type h)
{
init(w, h);
}
void Board2D::add_unit(const Coord c)
{
auto& space = at(c);
cout << "+ add_unit " << space.coord.x << "," << space.coord.y << " ";
if (!(space.capacity >= space.value)) throw InvalidMove {"Space is overloaded"};
space.value++;
units[space.owner]++;
cout << "total: " << units[space.owner] << endl;
}
auto Board2D::begin() const noexcept -> const_iterator
{
return spaces.cbegin();
}
void Board2D::capture(const Coord c, const Player p) noexcept
{
auto& s = at(c);
if (s.owner != p) {
cout << "= P" << static_cast<int>(p) << " captures " << s.coord.x << ","
<< s.coord.y << " : +" << s.value << endl;
units[s.owner] -= s.value;
units[p] += s.value;
s.owner = p;
}
}
auto Board2D::end() const noexcept -> const_iterator
{
return spaces.cend();
}
void Board2D::init(const size_type w_, const size_type h_)
{
w = w_;
h = 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;
}
}
}
}
units.emplace(Player::P1, 0);
units.emplace(Player::P2, 0);
}
auto Board2D::is_overloaded(Coord c) const noexcept -> bool
{
const auto& s = at(c);
return s.value > s.capacity;
}
auto Board2D::move(const Coord c, const Player p) -> Generator<pair<Space, Player>>
{
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 make_pair(space, Player::NONE);
queue<Coord> overloaded;
if (is_overloaded(c)) {
overloaded.push(c);
do {
const auto src = overloaded.back();
const auto targets = neighbours(src);
overloaded.pop();
auto spreader = spread(src, targets, p);
while (spreader) {
co_yield make_pair(spreader(), winner());
}
for (const auto t : targets) {
if (is_overloaded(t)) {
cout << "* overloaded: " << t.x << "," << t.y << endl;
overloaded.push(t);
};
}
} while (!overloaded.empty());
}
}
auto Board2D::neighbours(const Coord c) const noexcept -> vector<Coord>
{
vector<Coord> nghbrs;
if (c.x > 0) nghbrs.emplace_back(Coord {c.x - 1, c.y});
if (c.x < w - 1) nghbrs.emplace_back(Coord {c.x + 1, c.y});
if (c.y > 0) nghbrs.emplace_back(Coord {c.x, c.y - 1});
if (c.y < h - 1) nghbrs.emplace_back(Coord {c.x, c.y + 1});
return nghbrs;
}
auto Board2D::operator()(const Coord c) const noexcept -> const Board2D::Space&
{
return spaces.at(coord2idx(c));
}
void Board2D::remove_unit(const Coord c)
{
auto& s = at(c);
const auto owner = s.owner;
if (s.value > 1) {
s.value--;
units[owner]--;
}
else if (s.value == 1) {
s.reset();
units[owner]--;
}
else throw logic_error("Space is empty");
cout << "- remove " << c.x << "," << c.y << " "
<< "total: " << units[owner] << endl;
}
auto Board2D::size() const noexcept -> pair<size_type, size_type>
{
return {w, h};
}
auto Board2D::spread(const Coord src, const vector<Coord>& targets, const Player p)
-> Generator<Space>
{
auto& s = at(src);
cout << "= spread " << src.x << "," << src.y << " " << s.value << "/" << s.capacity
<< endl;
{
const auto count = targets.size();
if (s.value != static_cast<int>(count)) {
throw logic_error("cannot spread: unit count and target "
"spaces count differ");
}
}
for (const auto& t : targets) {
remove_unit(src);
co_yield at(src);
capture(t, p);
add_unit(t);
co_yield at(t);
}
}
auto Board2D::winner() const noexcept -> Player
{
return winner(*this);
}
auto Board2D::winner(const Board2D& b) noexcept -> Player
{
Player winner {Player::NONE};
for (auto [key, value] : b.units) {
if (value > 0) {
if (winner == Player::NONE) winner = key;
else return Player::NONE;
}
}
return winner;
}
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 * w + c.x;
}