307 lines
7.3 KiB
C++
307 lines
7.3 KiB
C++
#include "terrain.h"
|
|
#include <assert.h>
|
|
#include <functional>
|
|
#include <random>
|
|
using namespace std;
|
|
|
|
BadImportTerrainConfig::BadImportTerrainConfig(size_t pt): pt(pt) {}
|
|
|
|
const char* BadImportTerrainConfig::what() const noexcept {
|
|
return ("ImportTerrainConfig " + to_string(pt)).c_str();
|
|
}
|
|
|
|
const char* BadCapitalAssign::what() const noexcept {
|
|
return "Bad Capital Assign";
|
|
}
|
|
|
|
inline namespace {
|
|
|
|
mt19937 rnd(19260817);
|
|
enum class VertexType : std::uint8_t {
|
|
Ordinary,
|
|
Mountain,
|
|
Capital,
|
|
Cut
|
|
};
|
|
|
|
bool ban(VertexType t) {
|
|
return t == VertexType::Mountain || t == VertexType::Capital;
|
|
}
|
|
|
|
template<typename T>
|
|
void upmin(T& x, const T& y) {
|
|
if (y < x)
|
|
x = y;
|
|
}
|
|
|
|
bool findCut(pos_t w, pos_t h, vector<vector<VertexType>>& vertex_type) {
|
|
int idx = 0;
|
|
vector<vector<int>> dfn(h, vector<int>(w)), low = dfn;
|
|
function<void(pos_t, pos_t, pos_t, pos_t, bool)> tarjan =
|
|
[&](pos_t x, pos_t y, pos_t fx, pos_t fy, bool rt) {
|
|
dfn[x][y] = low[x][y] = ++idx;
|
|
int ch = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
const pos_t xx = x + direction_dx[i], yy = y + direction_dy[i];
|
|
if (xx >= h || yy >= w)
|
|
continue;
|
|
|
|
if (vertex_type[xx][yy] == VertexType::Capital)
|
|
vertex_type[x][y] = VertexType::Cut;
|
|
|
|
if (ban(vertex_type[xx][yy]) || (xx == fx && yy == fy))
|
|
continue;
|
|
|
|
if (!dfn[xx][yy]) {
|
|
ch++;
|
|
tarjan(xx, yy, x, y, 0);
|
|
upmin(low[x][y], low[xx][yy]);
|
|
if (!rt && low[xx][yy] >= dfn[x][y])
|
|
vertex_type[x][y] = VertexType::Cut;
|
|
} else {
|
|
upmin(low[x][y], dfn[xx][yy]);
|
|
}
|
|
}
|
|
|
|
if (rt && ch >= 2)
|
|
vertex_type[x][y] = VertexType::Cut;
|
|
};
|
|
|
|
bool flag = false;
|
|
for (pos_t i = 0; i < h; i++) {
|
|
for (pos_t j = 0; j < w; j++) {
|
|
if (!ban(vertex_type[i][j]) && !dfn[i][j]) {
|
|
if (flag)
|
|
return false;
|
|
flag = 1;
|
|
tarjan(i, j, -1, -1, 1);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
pair<bool, vector<Point>> generateCapital(
|
|
pos_t w,
|
|
pos_t h,
|
|
vector<vector<VertexType>>& vertex_type,
|
|
int r) {
|
|
vector<Point> res(r);
|
|
for (int i = 0; i <= r; i++) {
|
|
if (!findCut(w, h, vertex_type))
|
|
return {false, vector<Point>()};
|
|
|
|
if (i == r)
|
|
break;
|
|
vector<Point> options;
|
|
for (pos_t x = 0; x < h; x++) {
|
|
for (pos_t y = 0; y < w; y++) {
|
|
if (vertex_type[x][y] == VertexType::Ordinary)
|
|
options.emplace_back(x, y);
|
|
if (vertex_type[x][y] == VertexType::Cut)
|
|
vertex_type[x][y] = VertexType::Ordinary;
|
|
}
|
|
}
|
|
|
|
if (options.empty())
|
|
return {false, {}};
|
|
|
|
res[i] = options[rnd() % options.size()];
|
|
vertex_type[res[i].x][res[i].y] = VertexType::Capital;
|
|
}
|
|
return {true, res};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
GameBoard ImportedTerrain::makeGameBoard(const InitInfo& init_info) {
|
|
auto perm = [](std::uint8_t n) {
|
|
vector<std::uint8_t> vec(n);
|
|
iota(vec.begin(), vec.end(), 0);
|
|
shuffle(vec.begin(), vec.end(), rnd);
|
|
return vec;
|
|
};
|
|
|
|
GameBoard g(w, h, init_info);
|
|
for (pos_t i = 0; i < h; i++) {
|
|
for (pos_t j = 0; j < w; j++) {
|
|
g.at(i, j).type = tiles[i][j].type;
|
|
g.at(i, j).unit = tiles[i][j].unit;
|
|
}
|
|
}
|
|
|
|
auto arrange = [&perm, &g](const vector<Player>& players,
|
|
const vector<vector<Point>>& capitals,
|
|
vector<Player>& rest) {
|
|
vector<Point> caps;
|
|
for (const auto& v : capitals) {
|
|
for (const auto& p : v)
|
|
caps.push_back(p);
|
|
if (caps.size() >= players.size())
|
|
break;
|
|
}
|
|
auto C = perm(players.size()), D = perm(caps.size());
|
|
for (size_t j = D.size(); j < C.size(); j++)
|
|
rest.push_back(players[C[j]]);
|
|
for (size_t j = 0; j < min(C.size(), D.size()); j++) {
|
|
g.at(caps[D[j]].x, caps[D[j]].y).type = TileType::Capital;
|
|
g.at(caps[D[j]].x, caps[D[j]].y).owner = players[C[j]];
|
|
}
|
|
};
|
|
|
|
vector<vector<Player>> players(g.numTeams());
|
|
for (int i = 1, n = g.numPlayers(); i <= n; i++)
|
|
players[g.teamOf(i) - 1].push_back(i);
|
|
auto A = perm(players.size()), B = perm(capitals.size() - 1);
|
|
vector<Player> rest1;
|
|
for (size_t i = B.size(); i < A.size(); i++) {
|
|
for (auto id : players[A[i]])
|
|
rest1.push_back(id);
|
|
}
|
|
|
|
for (size_t i = 0; i < min(A.size(), B.size()); i++)
|
|
arrange(players[A[i]], capitals[B[i]], rest1);
|
|
|
|
vector<Player> rest2;
|
|
arrange(rest1, capitals.back(), rest2);
|
|
|
|
for (pos_t x = 0; x < h; x++) {
|
|
for (pos_t y = 0; y < w; y++) {
|
|
if (g.at(x, y).type == TileType::Capital) {
|
|
for (int i = 0; i < 4; i++) {
|
|
const pos_t xx = x + direction_dx[i],
|
|
yy = y + direction_dy[i];
|
|
if (xx >= h || yy >= w)
|
|
continue;
|
|
if (g.at(xx, yy).type == TileType::Capital)
|
|
throw BadCapitalAssign();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int T = 5;
|
|
|
|
while (T--) {
|
|
vector<vector<VertexType>> vertex_type(
|
|
h,
|
|
vector<VertexType>(w, VertexType::Ordinary));
|
|
for (pos_t i = 0; i < h; i++) {
|
|
for (pos_t j = 0; j < w; j++) {
|
|
if (g.at(i, j).type == TileType::Mountain)
|
|
vertex_type[i][j] = VertexType::Mountain;
|
|
if (g.at(i, j).type == TileType::Capital)
|
|
vertex_type[i][j] = VertexType::Capital;
|
|
}
|
|
}
|
|
|
|
auto pr = generateCapital(w, h, vertex_type, rest2.size());
|
|
if (!pr.first)
|
|
continue;
|
|
|
|
for (size_t i = 0; i < rest2.size(); i++) {
|
|
g.at(pr.second[i].x, pr.second[i].y).type = TileType::Capital;
|
|
g.at(pr.second[i].x, pr.second[i].y).owner = rest2[i];
|
|
}
|
|
return g;
|
|
}
|
|
|
|
throw BadCapitalAssign();
|
|
}
|
|
|
|
ImportedTerrain importTerrain(const ImportTerrainConfig& in) {
|
|
ImportedTerrain t;
|
|
t.w = in.w;
|
|
t.h = in.h;
|
|
t.tiles.resize(t.h, vector<ImportedTerrain::TerrainTile>(t.w));
|
|
|
|
size_t pt = 0;
|
|
auto error = [&]() { throw BadImportTerrainConfig(pt); };
|
|
auto view = [&](char c) {
|
|
return pt < in.value.size() && in.value[pt] == c ? (pt++, true) : false;
|
|
};
|
|
|
|
auto jump = [&](char c) {
|
|
if (!view(c))
|
|
error();
|
|
};
|
|
|
|
auto read = [&](int l = -99999, int r = 99999) {
|
|
bool neg = view('-');
|
|
if (!(pt < in.value.size() && isdigit(in.value[pt])))
|
|
error();
|
|
|
|
std::int32_t x = 0;
|
|
while (pt < in.value.size() && isdigit(in.value[pt])) {
|
|
x = x * 10 + (in.value[pt] ^ '0');
|
|
if (x > 99999)
|
|
error();
|
|
pt++;
|
|
}
|
|
if (neg)
|
|
x = -x;
|
|
if (x < l || x > r)
|
|
error();
|
|
return x;
|
|
};
|
|
|
|
if (t.w < 5 || t.h < 5)
|
|
error();
|
|
|
|
vector<vector<vector<Point>>> vec(27, vector<vector<Point>>(101));
|
|
vector<vector<VertexType>> vertex_type(
|
|
t.h,
|
|
vector<VertexType>(t.w, VertexType::Ordinary));
|
|
for (pos_t i = 0; i < t.h; i++) {
|
|
for (pos_t j = 0; j < t.w; j++) {
|
|
auto& p = t.tiles[i][j];
|
|
if (view(' ')) {
|
|
p.type = TileType::Blank;
|
|
} else if (view('m')) {
|
|
p.type = TileType::Mountain;
|
|
vertex_type[i][j] = VertexType::Mountain;
|
|
} else if (view('n')) {
|
|
p.type = TileType::Blank;
|
|
p.unit = read();
|
|
} else if (view('s')) {
|
|
p.type = TileType::Swamp;
|
|
} else if (view('g')) {
|
|
p.type = TileType::Blank;
|
|
bool tag = 1;
|
|
int team = (pt < in.value.size() && isupper(in.value[pt])
|
|
? in.value[pt++] - 'A'
|
|
: (tag = view(' '), 26));
|
|
|
|
int priority
|
|
= (tag && pt < in.value.size() && isdigit(in.value[pt])
|
|
? read(1, 99)
|
|
: 100);
|
|
vec[team][priority].emplace_back(i, j);
|
|
} else {
|
|
p.type = TileType::Stronghold;
|
|
p.unit = read();
|
|
}
|
|
if (i + 1 < t.h || j + 1 < t.w)
|
|
jump(',');
|
|
}
|
|
}
|
|
|
|
if (pt != in.value.size())
|
|
error();
|
|
if (!findCut(t.w, t.h, vertex_type))
|
|
error();
|
|
t.capitals.emplace_back();
|
|
|
|
for (int i = 0; i <= 26; i++) {
|
|
for (int j = 100; j >= 1; j--) {
|
|
if (!vec[i][j].empty()) {
|
|
t.capitals.back().emplace_back().swap(vec[i][j]);
|
|
}
|
|
}
|
|
if (!t.capitals.back().empty() && i != 26) {
|
|
t.capitals.emplace_back();
|
|
}
|
|
}
|
|
return t;
|
|
}
|