#include "GameBoard.h" #include #include GameBoard::GameBoard(pos_t w, pos_t h, const InitInfo& info) : n(info.n) , t(0) , players(info.n + 1) , w(w) , h(h) , view_radius(1) , board(w * h) , halfturn_id(0) { for (std::uint8_t i = 1; i <= n; ++i) { players[i].id = i; players[i].team = info.player_team[i]; t = std::max(t, players[i].team); } diff_tiles.resize(t + 1); cover.resize(t + 1, std::vector(h, std::vector(w))); } Tile& GameBoard::at(pos_t x, pos_t y) { return board[x * w + y]; } const Tile& GameBoard::at(pos_t x, pos_t y) const { return board[x * w + y]; } void GameBoard::setOwner(pos_t x, pos_t y, Player p) { auto flip = [&](int team, Point pos) { if (!team) return; auto pr = diff_tiles[team].insert(pos); if (!pr.second) diff_tiles[team].erase(pr.first); }; auto t1 = players[at(x, y).owner].team, t2 = players[p].team; if (t1 != t2) { pos_t sx = x - std::min(x, view_radius), tx = x + std::min(h - x, view_radius), sy = y - std::min(y, view_radius), ty = y + std::min(w - y, view_radius); for (pos_t xx = sx; xx <= tx; xx++) { for (pos_t yy = sy; yy <= ty; yy++) { cover[t1][xx][yy]--; if (!cover[t1][xx][yy]) flip(t1, Point(xx, yy)); if (!cover[t2][xx][yy]) flip(t2, Point(xx, yy)); cover[t2][xx][yy]++; } } } at(x, y).owner = p; updatedPosition(x, y); } bool GameBoard::attack(const PlayerMove& o) { if (!o.isValid(w, h)) return false; auto& sc_tile = at(o.x, o.y); auto& tt_tile = at(o.tx(), o.ty()); if (sc_tile.owner != o.player) return false; if (sc_tile.unit <= 1) return false; if (tt_tile.type == TileType::Mountain) return false; auto moving_unit = o.half ? sc_tile.unit - 1 : sc_tile.unit / 2; sc_tile.unit -= moving_unit; auto isfriend = isTeammate(o.player, tt_tile.owner); auto delta = isfriend ? moving_unit + tt_tile.unit : moving_unit - tt_tile.unit; updatedPosition(o.x, o.y); updatedPosition(o.ty(), o.ty()); tt_tile.unit = std::abs(delta); if (delta > 0 && (!isfriend || tt_tile.type != TileType::Capital)) { if (!isfriend && tt_tile.type == TileType::Capital) capitalCaptured(tt_tile.owner, o.player); setOwner(o.tx(), o.ty(), o.player); } return true; } void GameBoard::capitalCaptured(Player tt, Player sc) { for (pos_t x = 0; x < h; ++x) { for (pos_t y = 0; y < w; ++y) { auto& tile = at(x, y); if (tile.owner != tt) continue; updatedPosition(x, y); if (tile.type == TileType::Capital) { tile.type = TileType::Stronghold; continue; } setOwner(x, y, sc); tile.unit = std::max(tile.unit / 2, 1); } } new_defeated_players.push_back(tt); players[tt].is_defeated = 1; } bool GameBoard::isTeammate(Player x, Player y) const { return teamOf(x) == teamOf(y); } void GameBoard::turnUpdate() { for (pos_t x = 0; x < h; ++x) { for (pos_t y = 0; y < w; ++y) { auto& tile = at(x, y); if (tile.owner == neutral_player) continue; if (tile.type == TileType::Stronghold || tile.type == TileType::Capital) tile.unit += 1; if (tile.type == TileType::Swamp) { if (tile.unit > 0) tile.unit -= 1; if (tile.unit == 0) setOwner(x, y, neutral_player); } } } } void GameBoard::roundUpdate() { for (pos_t x = 0; x < h; ++x) { for (pos_t y = 0; y < w; ++y) { auto& tile = at(x, y); if (tile.owner == neutral_player) continue; tile.unit += 1; } } } void GameBoard::updatedPosition(pos_t x, pos_t y) { updated_tiles.emplace(x, y); } Team GameBoard::teamOf(Player x) const { return players[x].team; } bool GameBoard::isDefeated(Player x) const { return players[x].is_defeated; } std::uint8_t GameBoard::numPlayers() const { return n; } std::uint8_t GameBoard::numTeams() const { return t; } void GameBoard::appendOrderQueue(const PlayerMove& p) { if (!players[p.player].is_defeated) players[p.player].orders.emplace_back(p); } void GameBoard::clearOrderQueue(Player p) { players[p].orders.clear(); } void GameBoard::popOrderQueue(Player p) { players[p].orders.pop_front(); } void GameBoard::setOffline(Player p) { new_offline_players.push_back(p); } void GameBoard::offline(Player p) { if (players[p].is_defeated) return; std::vector> vis(h, std::vector(w)); std::queue q; for (pos_t i = 0; i < h; i++) { for (pos_t j = 0; j < w; j++) { if (at(i, j).type == TileType::Capital && at(i, j).owner == p) { vis[i][j] = 1; q.emplace(i, j); } } } Player v = neutral_player; while (!q.empty()) { pos_t x = q.front().x, y = q.front().y; q.pop(); for (int i = 0; i < 4; i++) { pos_t xx = x + direction_dx[i], yy = y + direction_dy[i]; Tile& tile = at(xx, yy); if (xx >= h || yy >= w || tile.type == TileType::Mountain || tile.type != TileType::Stronghold || vis[xx][yy]) continue; if (tile.type == TileType::Capital) { if (players[tile.owner].team != players[p].team) continue; v = tile.owner; break; } vis[xx][yy] = 1; q.emplace(xx, yy); } if (v != neutral_player) break; } for (pos_t i = 0; i < h; i++) { for (pos_t j = 0; j < w; j++) { Tile& tile = at(i, j); if (tile.owner == p) { setOwner(i, j, v); if (tile.type == TileType::Capital) tile.type = TileType::Stronghold; } } } new_defeated_players.push_back(p); players[p].is_defeated = 1; } std::vector GameBoard::leaderboard() const { std::vector res(t); std::vector prank(n); for (Player i = 1; i <= n; ++i) { prank[i - 1].player = i; prank[i - 1].is_defeated = players[i].is_defeated; } for (Team i = 1; i <= t; ++i) res[i - 1].team = i; for (pos_t x = 0; x < h; ++x) { for (pos_t y = 0; y < w; ++y) { auto& tile = at(x, y); if (tile.owner == neutral_player) continue; prank[tile.owner - 1].land += 1; prank[tile.owner - 1].unit += tile.unit; } } for (const auto& p : prank) res[teamOf(p.player) - 1].players.push_back(p); for (auto& team : res) { for (const auto& p : team.players) { team.land += p.land; team.unit += p.unit; } sort(team.players.begin(), team.players.end(), [](const PlayerRanking& a, const PlayerRanking& b) { return a.unit != b.unit ? a.unit > b.unit : a.player < b.player; }); } sort(res.begin(), res.end(), [](const TeamRanking& a, const TeamRanking& b) { return a.unit != b.unit ? a.unit > b.unit : a.team < b.team; }); return res; } TeamViewDiff GameBoard::teamViewDiff(Team team) { TeamViewDiff res; res.team = team; for (const auto& pos : updated_tiles) { if (cover[team][pos.x][pos.y]) diff_tiles[team].insert(pos); } for (const auto& pos : diff_tiles[team]) { auto x = pos.x, y = pos.y; res.diffs.emplace_back(x, y, cover[team][x][y] ? at(x, y) : Tile()); } diff_tiles[team].clear(); return res; } std::vector GameBoard::getViewOf(Team team) const { std::vector res(h * w); for (pos_t x = 0; x < h; x++) { for (pos_t y = 0; y < w; y++) { if (cover[team][x][y]) res[x * w + y] = at(x, y); } } return res; } GameState GameBoard::tick() { halfturn_id++; for (int _i = 1; _i <= n; _i++) { int i = (halfturn_id & 1) ? _i : n - _i + 1; if (!players[i].orders.empty()) { if (!attack(players[i].orders.front())) players[i].orders.clear(); } } if (!(halfturn_id % 50)) roundUpdate(); if (!(halfturn_id & 1)) turnUpdate(); for (auto p : new_offline_players) offline(p); new_offline_players.clear(); GameState res; res.halfturn_id = halfturn_id; res.defeated_players = new_defeated_players; new_defeated_players.clear(); for (Player i = 1; i <= n; i++) { if (!players[i].is_defeated) res.alive_players.push_back(i); } res.leaderboard = leaderboard(); res.diffs.resize(t); for (Team i = 1; i <= t; i++) res.diffs[i] = teamViewDiff(i); updated_tiles.clear(); return res; } PlayerState::PlayerState() { is_defeated = false; }