diff --git a/docs/processor.md b/docs/processor.md index 7cf2dc1..0660e85 100644 --- a/docs/processor.md +++ b/docs/processor.md @@ -97,7 +97,7 @@ 当一个玩家掉线后一段时间,适配器认为该玩家无重连的可能性后,会向处理器发送掉线通知。处理器收到掉线通知后, -- 若该玩家是其队伍中唯一一个还未输掉游戏的玩家,则其全部单位变为中立单位。 +- 若该玩家是其队伍中唯一一个还未输掉游戏的玩家,则其全部单位变为中立单位,并把他的首都变为一个要塞。 - 否则,寻找首都距离该玩家首都最近的同一队伍的玩家,并将该玩家所全部占领的格子所变为该队友所占领,并把他的首都变为一个要塞。这里的距离是指在不经过任何山区和要塞的前提下将单位从一个格子移动到另一个格子的最小步数。 ### 每半回合处理次序 diff --git a/processor/logic/GameBoard.cpp b/processor/logic/GameBoard.cpp index d962c6c..f25d419 100644 --- a/processor/logic/GameBoard.cpp +++ b/processor/logic/GameBoard.cpp @@ -1,13 +1,23 @@ #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), board(w * h) { + : 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) { @@ -18,6 +28,35 @@ 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; @@ -30,6 +69,9 @@ bool GameBoard::attack(const PlayerMove& o) { 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); @@ -43,7 +85,7 @@ bool GameBoard::attack(const PlayerMove& o) { if (delta > 0 && (!isfriend || tt_tile.type != TileType::Capital)) { if (!isfriend && tt_tile.type == TileType::Capital) capitalCaptured(tt_tile.owner, o.player); - tt_tile.owner = o.player; + setOwner(o.tx(), o.ty(), o.player); } return true; } @@ -52,12 +94,17 @@ 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 || tile.type == TileType::Capital) + if (tile.owner != tt) continue; - - tile.owner = sc; - tile.unit = std::max(tile.unit / 2, 1); + 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); @@ -80,10 +127,10 @@ void GameBoard::turnUpdate() { tile.unit += 1; if (tile.type == TileType::Swamp) { - if (tile.unit > 0) + if (tile.unit > 0) tile.unit -= 1; if (tile.unit == 0) - tile.owner = neutral_player; + setOwner(x, y, neutral_player); } } } @@ -120,6 +167,72 @@ 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); @@ -164,6 +277,68 @@ std::vector GameBoard::leaderboard() const { 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; } diff --git a/processor/logic/GameBoard.h b/processor/logic/GameBoard.h index 1e16ce8..dd89411 100644 --- a/processor/logic/GameBoard.h +++ b/processor/logic/GameBoard.h @@ -1,14 +1,14 @@ #ifndef OGLG_GAMEBOARD_H_ #define OGLG_GAMEBOARD_H_ -#include +#include "PlayerMove.h" +#include "pc/astuple.h" +#include "pc/commcode.h" +#include "pc/pctypes.h" #include #include #include -#include "PlayerMove.h" -#include "pc/pctypes.h" -#include "pc/commcode.h" -#include "pc/astuple.h" +#include struct PlayerState { Player id; @@ -19,19 +19,13 @@ struct PlayerState { PlayerState(); }; +class ImportedTerrain; + class GameBoard { public: - GameBoard(pos_t w, pos_t h, const InitInfo &info); + GameBoard(pos_t w, pos_t h, const InitInfo& info); - const Tile& at(pos_t x, pos_t y) const; - Tile& at(pos_t x, pos_t y); - - bool attack(const PlayerMove &o); - - void turnUpdate(); - void roundUpdate(); - - std::vector leaderboard() const; + friend ImportedTerrain; bool isTeammate(Player x, Player y) const; Team teamOf(Player x) const; @@ -40,26 +34,46 @@ public: std::uint8_t numPlayers() const; std::uint8_t numTeams() const; - void appendOrderQueue(const PlayerMove &p); + void appendOrderQueue(const PlayerMove& p); void clearOrderQueue(Player p); void popOrderQueue(Player p); - void setOffline(Player x); + void setOffline(Player p); std::vector getViewOf(Team x) const; GameState tick(); private: + const Tile& at(pos_t x, pos_t y) const; + Tile& at(pos_t x, pos_t y); + void setOwner(pos_t x, pos_t y, Player p); + + bool attack(const PlayerMove& o); void capitalCaptured(Player target, Player source); + + void turnUpdate(); + void roundUpdate(); void updatedPosition(pos_t x, pos_t y); + void offline(Player p); + + std::vector leaderboard() const; + + TeamViewDiff teamViewDiff(Team team); + std::uint8_t n, t; std::vector players; - pos_t w, h; + pos_t w, h, view_radius; std::vector board; + + std::uint32_t halfturn_id; + + std::vector>> cover; + std::vector> diff_tiles; + + std::vector new_offline_players; std::set updated_tiles; std::vector new_defeated_players; }; #endif - diff --git a/processor/logic/terrain.cpp b/processor/logic/terrain.cpp index 6c3ca59..dfcbb66 100644 --- a/processor/logic/terrain.cpp +++ b/processor/logic/terrain.cpp @@ -123,16 +123,10 @@ GameBoard ImportedTerrain::makeGameBoard(const InitInfo& init_info) { }; 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) { + auto arrange = [&](const vector& players, + const vector>& capitals, + vector& rest) { vector caps; for (const auto& v : capitals) { for (const auto& p : v) @@ -145,10 +139,17 @@ GameBoard ImportedTerrain::makeGameBoard(const InitInfo& init_info) { 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]]; + g.setOwner(caps[D[j]].x, caps[D[j]].y, players[C[j]]); } }; + 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; + } + } + vector> players(g.numTeams()); for (int i = 1, n = g.numPlayers(); i <= n; i++) players[g.teamOf(i) - 1].push_back(i); @@ -201,7 +202,7 @@ GameBoard ImportedTerrain::makeGameBoard(const InitInfo& init_info) { 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]; + g.setOwner(pr.second[i].x, pr.second[i].y, rest2[i]); } return g; } diff --git a/processor/pc/pctypes.cpp b/processor/pc/pctypes.cpp index 946f484..edc4e1f 100644 --- a/processor/pc/pctypes.cpp +++ b/processor/pc/pctypes.cpp @@ -5,5 +5,5 @@ PlayerRanking::PlayerRanking() : player(neutral_player) TeamRanking::TeamRanking() : team(neutral_team), land(0), unit(0) {} -Tile::Tile() : owner(neutral_player), unit(0) {} +Tile::Tile() : owner(neutral_player), type(TileType::Misty), unit(0) {}