#include "terrain.h" #include #include #include 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 void upmin(T& x, const T& y) { if (y < x) x = y; } bool findCut(pos_t w, pos_t h, vector>& vertex_type) { int idx = 0; vector> dfn(h, vector(w)), low = dfn; function 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> generateCapital( pos_t w, pos_t h, vector>& vertex_type, int r) { vector res(r); for (int i = 0; i <= r; i++) { if (!findCut(w, h, vertex_type)) return {false, vector()}; if (i == r) break; vector 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 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& players, const vector>& capitals, vector& rest) { vector 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> 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 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 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> vertex_type( h, vector(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(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>> vec(27, vector>(101)); vector> vertex_type( t.h, vector(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; }