在文档中更新了导入地图的首都分配机制,并相应更新了代码实现

This commit is contained in:
lcw 2024-02-08 22:15:34 +08:00
parent f6be04300f
commit ffe2271b6c
2 changed files with 87 additions and 58 deletions

View File

@ -474,12 +474,19 @@ m, ,gA99,-12,s,n-10,g 1,L_10,L_n5
若队伍标识数量少于队伍数量,则随机选择队伍数量减去队伍标识数量个队伍,并将选出的队伍中的全部玩家标记为“剩余玩家”,选出的队伍不参与队伍标识分配。对于剩余玩家,创建一个候选列表,从大到小枚举优先级,并将其全部为该优先级的剩余首都都加入候选列表中,直到候选列表中包含至少等于剩余玩家数量个候选首都为止。然后在列表中随机选取作为剩余伍玩家的首都。若剩余玩家数多于剩余首都数,则在空地中随机选取一些地方作为剩余玩家的首都。在随机选择空地为首都时,至少要满足以下条件: 若队伍标识数量少于队伍数量,则随机选择队伍数量减去队伍标识数量个队伍,并将选出的队伍中的全部玩家标记为“剩余玩家”,选出的队伍不参与队伍标识分配。对于剩余玩家,创建一个候选列表,从大到小枚举优先级,并将其全部为该优先级的剩余首都都加入候选列表中,直到候选列表中包含至少等于剩余玩家数量个候选首都为止。然后在列表中随机选取作为剩余伍玩家的首都。若剩余玩家数多于剩余首都数,则在空地中随机选取一些地方作为剩余玩家的首都。在随机选择空地为首都时,至少要满足以下条件:
1. 将山区和首都视为不可走的点时,生成的地图中的空地、要塞和沼泽联通; 1. 将山区视为不可走的点时,生成的地图中的首都、空地、要塞和沼泽连通;
2. 没有两名玩家的首都相邻。 2. 将山区和首都视为不可走的点时,生成的地图中的空地、要塞和沼泽连通;
3. 没有两名玩家的首都相邻。
若无力进行合适的首都分配,则应生成随机地图。但当以下条件都满足时,应确保能进行合适的首都分配:
1. 地图中任意候选首都都不相邻;
2. 队伍数量不超过队伍标识数量;
3. 每个队伍的玩家数量都不超过每个队伍标识所包含的全部的候选首都数量。
#### 错误处理 #### 错误处理
不能保证给出的地图总是正确,请判断格式不正确的地图。 不能保证给出的地图总是正确,请判断格式不正确的地图(若将山区视为不可走的点时,导入地图中的候选首都、空地、要塞和沼泽不连通,则视为该导入地图格式不正确)
### 随机地图的生成参数 ### 随机地图的生成参数
@ -491,7 +498,7 @@ m, ,gA99,-12,s,n-10,g 1,L_10,L_n5
值得注意的是,不论使用何种生成算法,应当至少满足以下条件: 值得注意的是,不论使用何种生成算法,应当至少满足以下条件:
1. 将山区和要塞视为不可走的点时,生成的地图中的空地和沼泽通; 1. 将山区和要塞视为不可走的点时,生成的地图中的空地和沼泽通;
2. 地图包括至少 $16$ 个空地。 2. 地图包括至少 $16$ 个空地。
下面假设地图的大小为 $h$ 行 $w$ 列。 下面假设地图的大小为 $h$ 行 $w$ 列。
@ -511,7 +518,7 @@ $$
第一步,在地图上随机选择恰好 $p_m$ 个格子,将类型设置为山区,剩下的格子的类型设置为空地。 第一步,在地图上随机选择恰好 $p_m$ 个格子,将类型设置为山区,剩下的格子的类型设置为空地。
第二步,寻找一个将尽可能少的山区变为空地的方案,使得空地通。 第二步,寻找一个将尽可能少的山区变为空地的方案,使得空地通。
第三步,在山区中随机选择 $p_c$ 个格子,将这些山区的格子变为要塞。并在空地中选择 $p_s$ 个格子,将它们变为沼泽。 第三步,在山区中随机选择 $p_c$ 个格子,将这些山区的格子变为要塞。并在空地中选择 $p_s$ 个格子,将它们变为沼泽。
@ -525,7 +532,7 @@ $$
值得注意的是,不论使用何种分配算法,应当至少满足以下条件: 值得注意的是,不论使用何种分配算法,应当至少满足以下条件:
1. 将山区和首都视为不可走的点时,生成的地图中的空地、要塞和沼泽通; 1. 将山区和首都视为不可走的点时,生成的地图中的空地、要塞和沼泽通;
2. 玩家的首都应当总是分配在空地上; 2. 玩家的首都应当总是分配在空地上;
3. 没有两名玩家的首都相邻。 3. 没有两名玩家的首都相邻。

View File

@ -34,61 +34,63 @@ void upmin(T& x, const T& y) {
x = y; x = y;
} }
const int dx[] = {0, 0, -1, 1}, dy[] = {-1, 1, 0, 0};
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(int, int, int, int, bool)> tarjan =
[&](int x, int y, int fx, int fy, bool rt) {
dfn[x][y] = low[x][y] = ++idx;
int ch = 0;
for (int i = 0; i < 4; i++) {
const int xx = x + dx[i], yy = y + dy[i];
if (xx < 0 || xx >= h || yy < 0 || 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( pair<bool, vector<Point>> generateCapital(
pos_t w, pos_t w,
pos_t h, pos_t h,
vector<vector<VertexType>>& vertex_type, vector<vector<VertexType>>& vertex_type,
int r) { int r) {
auto findCut = [&]() {
int idx = 0;
vector<vector<int>> dfn(h, vector<int>(w)), low = dfn;
function<void(int, int, int, int, bool)> tarjan =
[&](int x, int y, int fx, int fy, bool rt) {
dfn[x][y] = low[x][y] = ++idx;
int ch = 0;
for (int i = 0; i < 4; i++) {
static const int dx[] = {0, 0, -1, 1}, dy[] = {-1, 1, 0, 0};
const int xx = x + dx[i], yy = y + dy[i];
if (xx < 0 || xx >= h || yy < 0 || 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;
};
vector<Point> res(r); vector<Point> res(r);
for (int i = 0; i <= r; i++) { for (int i = 0; i <= r; i++) {
if (!findCut()) if (!findCut(w, h, vertex_type))
return {false, vector<Point>()}; return {false, vector<Point>()};
if (i == r) if (i == r)
@ -164,6 +166,21 @@ GameBoard ImportedTerrain::makeGameBoard(const InitInfo& init_info) {
vector<Player> rest2; vector<Player> rest2;
arrange(rest1, capitals.back(), 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 int xx = x + dx[i], yy = y + dy[i];
if (xx < 0 || xx >= h || yy < 0 || yy >= w)
continue;
if (g.at(xx, yy).type == TileType::Capital)
throw BadCapitalAssign();
}
}
}
}
int T = 5; int T = 5;
while (T--) { while (T--) {
@ -229,19 +246,22 @@ ImportedTerrain importTerrain(const ImportTerrainConfig& in) {
}; };
vector<vector<vector<Point>>> vec(27, vector<vector<Point>>(101)); 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 i = 0; i < t.h; i++) {
for (pos_t j = 0; j < t.w; j++) { for (pos_t j = 0; j < t.w; j++) {
auto& p = t.tiles[i][j]; auto& p = t.tiles[i][j];
if (view(' ')) if (view(' ')) {
p.type = TileType::Blank; p.type = TileType::Blank;
else if (view('m')) } else if (view('m')) {
p.type = TileType::Mountain; p.type = TileType::Mountain;
else if (view('n')) { vertex_type[i][j] = VertexType::Mountain;
} else if (view('n')) {
p.type = TileType::Blank; p.type = TileType::Blank;
p.unit = read(); p.unit = read();
} else if (view('s')) } else if (view('s')) {
p.type = TileType::Swamp; p.type = TileType::Swamp;
else if (view('g')) { } else if (view('g')) {
p.type = TileType::Blank; p.type = TileType::Blank;
bool tag = 1; bool tag = 1;
int team = (pt < in.value.size() && isupper(in.value[pt]) int team = (pt < in.value.size() && isupper(in.value[pt])
@ -264,6 +284,8 @@ ImportedTerrain importTerrain(const ImportTerrainConfig& in) {
if (pt != in.value.size()) if (pt != in.value.size())
error(); error();
if (!findCut(t.w, t.h, vertex_type))
error();
t.capitals.emplace_back(); t.capitals.emplace_back();
for (int i = 0; i <= 26; i++) { for (int i = 0; i <= 26; i++) {