215 lines
5.2 KiB
C++
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;
|
|
}
|