// 圈地为王游戏样例程序
// 最后更新 2013-06-25 21:56
// 作者:zhouhy
#include <iostream>
#include <string>
#include <ctime>
#include <set>
#include <vector>
#include <algorithm>
#define INITIAL_OWNER -1 // 无主
#define TEMPORARY_FLAG -2 // 临时标志
#define max(a, b) ((b < a) ? (a) : (b))
#define min(a, b) ((a < b) ? (a) : (b))
#define PLAYER_COUNT 4
#define FIELD_WIDTH 10
#define FIELD_HEIGHT 10
using namespace std;
struct Point
{
int x, y;
Point(int _x, int _y) : x(_x), y(_y) { }
Point()
{
x = y = 0;
}
bool operator<(const Point& b) const
{
if (y == b.y)
return x < b.x;
return y < b.y;
}
bool operator==(const Point& b) const
{
return x == b.x && y == b.y;
}
bool operator!=(const Point& b) const
{
return !operator==(b);
}
void operator+=(const Point& b)
{
x += b.x;
y += b.y;
}
};
int playerLeft, myID, turnCount, vborders[FIELD_WIDTH + 1][FIELD_HEIGHT], hborders[FIELD_WIDTH][FIELD_HEIGHT + 1], //记录轨迹用
vborderOwner[FIELD_WIDTH + 1][FIELD_HEIGHT], hborderOwner[FIELD_WIDTH][FIELD_HEIGHT + 1], // 记录可否行走用
grids[FIELD_WIDTH][FIELD_HEIGHT], areaSquareSum[PLAYER_COUNT], areaSum[PLAYER_COUNT], // 记录格子占领情况
dx[5] = {0, 0, -1, 1, 0}, dy[5] = {-1, 1, 0, 0, 0}, playerState[PLAYER_COUNT], // 0为抬笔,1为落笔,-1为死亡
stuckStatusLeft[PLAYER_COUNT], // 玩家被束缚剩余回合数
scoreDecline[PLAYER_COUNT]; // 玩家因施放技能损失的分数
char actions[5] = {'u', 'd', 'l', 'r', 's'},
lastDir[PLAYER_COUNT]; // 上次走的方向,仅用于落笔后状态
Point prevPos[PLAYER_COUNT], currPos[PLAYER_COUNT];
vector<Point> trail[PLAYER_COUNT];
set<Point> traps;
struct EnclosingArgu
{
int enclosedArea, // 被圈住的内部面积,包括不可占据区域
charID;
set<Point> actuallyOccupiedArea; // 实际属于该角色的网格坐标
bool willBeDead[PLAYER_COUNT];
bool operator<(const EnclosingArgu& b) const
{
return enclosedArea < b.enclosedArea;
}
};
inline void DrawLine(int charID, Point& prevPos, Point& currPos)
{
if (currPos == prevPos)
return;
if (currPos.x == prevPos.x)
vborders[currPos.x][min(prevPos.y, currPos.y)] = charID;
else if (currPos.y == prevPos.y)
hborders[min(prevPos.x, currPos.x)][currPos.y] = charID;
}
inline bool MoveStep(int &x, int &y, int dir, int charID) // 移动并检查是否撞到charID边界
{
if (dir == 0)
{
if (hborders[x][y] == charID)
return false;
}
else if (dir == 1)
{
if (hborders[x][y + 1] == charID)
return false;
}
else if (dir == 2)
{
if (vborders[x][y] == charID)
return false;
}
else if (dir == 3)
{
if (vborders[x + 1][y] == charID)
return false;
}
x += dx[dir];
y += dy[dir];
if (x < 0 || x >= FIELD_WIDTH || y < 0 || y >= FIELD_HEIGHT)
return false;
return true;
}
inline bool MoveStep(int &x, int &y, int dir) // 移动
{
x += dx[dir];
y += dy[dir];
if (x < 0 || x >= FIELD_WIDTH || y < 0 || y >= FIELD_HEIGHT)
return false;
return true;
}
int TryExpand(int charID, int tempBoard[FIELD_WIDTH][FIELD_HEIGHT], int gridX, int gridY,
set<Point>& actuallyOccupiedArea, bool willBeDead[PLAYER_COUNT]) // 试图用漫水法找出charID所围全部区域
{
if (tempBoard[gridX][gridY] == TEMPORARY_FLAG) // tempBoard复制grids,但增加了TEMPORARY_FLAG这种状态,表示已经走过
return 0;
int x, y, area = 0, initialOwner = tempBoard[gridX][gridY]; // area为所围区域全部面积
tempBoard[gridX][gridY] = TEMPORARY_FLAG;
for (int dir = 0; dir < 4; dir++)
{
x = gridX;
y = gridY;
if (MoveStep(x, y, dir, charID))
area += TryExpand(charID, tempBoard, x, y, actuallyOccupiedArea, willBeDead);
}
if (gridX > 0 && gridX < FIELD_WIDTH && gridY > 0 && gridY < FIELD_HEIGHT &&
hborders[gridX - 1][gridY] != charID && hborders[gridX][gridY] != charID &&
vborders[gridX][gridY - 1] != charID && vborders[gridX][gridY] != charID)
{
for (int i = 0; i < PLAYER_COUNT; i++)
if (prevPos[i].x == gridX && prevPos[i].y == gridY)
willBeDead[i] = true;
}
if (initialOwner == INITIAL_OWNER)
actuallyOccupiedArea.insert(Point(gridX, gridY));
return area + 1;
}
int TryExpand(int tempBoard[FIELD_WIDTH][FIELD_HEIGHT], int gridX, int gridY, set<Point>& allArea) // 试图用漫水法从gridX/Y开始找出内容为TEMPFLAG的连续格子面积
{
if (tempBoard[gridX][gridY] != TEMPORARY_FLAG)
return 0;
int x, y, area = 0; // area为所围区域全部面积
tempBoard[gridX][gridY] = INITIAL_OWNER;
allArea.insert(Point(gridX, gridY));
for (int dir = 0; dir < 4; dir++)
{
x = gridX;
y = gridY;
if (MoveStep(x, y, dir))
area += TryExpand(tempBoard, x, y, allArea);
}
return area + 1;
}
// 产生圈地的参数,因为根据规则,在正式圈地之前还要从小到大排序
EnclosingArgu CalcEnclose(int charID, vector<Point>::iterator p, vector<Point>::iterator e) // p~e是真正在边界上的点
{
EnclosingArgu tempArgu;
for (int i = 0; i < PLAYER_COUNT; i++)
tempArgu.willBeDead[i] = false;
// 从最高的横线向下漫水搜索
sort(p, e);
vector<Point>::iterator lp = p;
for (++p; p != e; ++p, ++lp)
if (p->x - lp->x == 1)
break;
int tempGrids[FIELD_WIDTH][FIELD_HEIGHT];
for (int x = 0; x < FIELD_WIDTH; x++)
for (int y = 0; y < FIELD_HEIGHT; y++)
tempGrids[x][y] = grids[x][y];
tempArgu.enclosedArea = TryExpand(charID, tempGrids, lp->x, lp->y, tempArgu.actuallyOccupiedArea, tempArgu.willBeDead);
tempArgu.charID = charID;
return tempArgu;
}
// 按照圈地参数进行正式圈地
void DoEnclose(const EnclosingArgu& argu)
{
int currArea;
int tempGrids[FIELD_WIDTH][FIELD_HEIGHT]; // 用于找出连续区域
for (int x = 0; x < FIELD_WIDTH; x++)
for (int y = 0; y < FIELD_HEIGHT; y++)
tempGrids[x][y] = INITIAL_OWNER;
for (int i = 0; i < PLAYER_COUNT; i++)
if (argu.willBeDead[i])
playerState[i] = -1;
set<Point>::const_iterator i, e;
for (i = argu.actuallyOccupiedArea.begin(), e = argu.actuallyOccupiedArea.end(); i != e; i++)
{
grids[i->x][i->y] = argu.charID;
tempGrids[i->x][i->y] = TEMPORARY_FLAG;
}
// 标记所有内部边
Point x1, x2;
for (x1.x = 0; x1.x < FIELD_WIDTH; x1.x++)
for (x1.y = 0; x1.y < FIELD_HEIGHT; x1.y++)
{
if (x1.x < FIELD_WIDTH - 1)
{
x2.x = x1.x + 1;
x2.y = x1.y;
if (tempGrids[x1.x][x1.y] == TEMPORARY_FLAG && tempGrids[x2.x][x2.y] == TEMPORARY_FLAG &&
argu.actuallyOccupiedArea.count(x1) && argu.actuallyOccupiedArea.count(x2))
vborderOwner[x2.x][x2.y] = argu.charID;
}
if (x1.y < FIELD_HEIGHT - 1)
{
x2.x = x1.x;
x2.y = x1.y + 1;
if (tempGrids[x1.x][x1.y] == TEMPORARY_FLAG && tempGrids[x2.x][x2.y] == TEMPORARY_FLAG &&
argu.actuallyOccupiedArea.count(x1) && argu.actuallyOccupiedArea.count(x2))
hborderOwner[x2.x][x2.y] = argu.charID;
}
};
areaSum[argu.charID] += argu.actuallyOccupiedArea.size();
set<Point> allArea;
// 找出所有连续的区域
for (i = argu.actuallyOccupiedArea.begin(); i != e; i++)
if (tempGrids[i->x][i->y] == TEMPORARY_FLAG)
{
allArea.clear();
currArea = TryExpand(tempGrids, i->x, i->y, allArea);
areaSquareSum[argu.charID] += currArea * currArea;
};
// 清除轨迹
int x, y;
for (x = 0; x <= FIELD_WIDTH; x++)
for (y = 0; y <= FIELD_HEIGHT; y++)
{
if (x < FIELD_WIDTH && hborders[x][y] == argu.charID)
hborders[x][y] = INITIAL_OWNER;
if (y < FIELD_HEIGHT && vborders[x][y] == argu.charID)
vborders[x][y] = INITIAL_OWNER;
};
trail[argu.charID].clear();
}
bool CheckRoute(int charID, int state, Point& prevPos, Point& currPos) // 检查路线是否可以行走
{
if (currPos == prevPos)
return true;
if (currPos.y == prevPos.y)
if (currPos.y == 0 || currPos.y == FIELD_HEIGHT)
return true;
else
{
int gridX = currPos.x;
if (prevPos.x < gridX)
gridX = prevPos.x;
if ((state == 1 && hborderOwner[gridX][currPos.y] != INITIAL_OWNER) ||
(state == 0 && hborderOwner[gridX][currPos.y] != INITIAL_OWNER && hborderOwner[gridX][currPos.y] != charID))
return false;
return true;
}
else
if (currPos.x == 0 || currPos.x == FIELD_WIDTH)
return true;
else
{
int gridY = currPos.y;
if (prevPos.y < gridY)
gridY = prevPos.y;
if ((state == 1 && vborderOwner[currPos.x][gridY] != INITIAL_OWNER) ||
(state == 0 && vborderOwner[currPos.x][gridY] != INITIAL_OWNER && vborderOwner[currPos.x][gridY] != charID))
return false;
return true;
}
}
inline bool CheckPosValidity(Point& pos)
{
if (pos.x >= 0 && pos.x <= FIELD_WIDTH &&
pos.y >= 0 && pos.y <= FIELD_HEIGHT)
return true;
return false;
}
inline bool IsReverse(int dir1, int dir2) // 判断方向是否相反
{
if (dir1 == 0 && dir2 == 1)
return true;
if (dir1 == 1 && dir2 == 0)
return true;
if (dir1 == 2 && dir2 == 3)
return true;
if (dir1 == 3 && dir2 == 2)
return true;
return false;
}
inline int GetDistance(int ida, int idb) // 获得两名角色之间的坐标差
{
int deltax = currPos[ida].x - currPos[idb].x, deltay = currPos[ida].y - currPos[idb].y;
if (deltax < 0)
deltax *= -1;
if (deltay < 0)
deltay *= -1;
return deltax + deltay;
}
int main()
{
int i, changePenState, nTraps, turnLeft;
string cmd;
Point temp;
multiset<EnclosingArgu> allEnclosure;
time_t startTick;
for (int x = 0; x <= FIELD_WIDTH; x++)
for (int y = 0; y <= FIELD_HEIGHT; y++)
{
if (x != FIELD_WIDTH)
{
hborderOwner[x][y] = INITIAL_OWNER;
hborders[x][y] = INITIAL_OWNER;
}
if (y != FIELD_HEIGHT)
{
vborderOwner[x][y] = INITIAL_OWNER;
vborders[x][y] = INITIAL_OWNER;
}
if (x != FIELD_WIDTH && y != FIELD_HEIGHT)
grids[x][y] = INITIAL_OWNER;
};
for (i = 0; i < PLAYER_COUNT; i++)
{
areaSquareSum[i] = areaSum[i] = 0;
lastDir[i] = -1;
}
turnCount = 0;
while (cin >> cmd)
{
if (cmd == "[START]")
{
startTick = clock();
while (clock() - startTick < 100); // 等待0.1秒……
srand(clock());
cin >> myID;
cout << "[POS] " << rand() % (FIELD_WIDTH + 1) << ' ' << rand() % (FIELD_HEIGHT + 1) << endl;
}
if (cmd == "[STATUS]")
{
startTick = clock();
while (clock() - startTick < 100); // 等待0.1秒……
srand(clock());
for (i = 0; i < PLAYER_COUNT; i++)
cin >> currPos[i].x >> currPos[i].y >> playerState[i] >> stuckStatusLeft[i] >> scoreDecline[i];
cin >> nTraps;
traps.clear();
for (i = 0; i < nTraps; i++)
{
cin >> temp.x >> temp.y >> turnLeft;
traps.insert(temp);
}
if (++turnCount == 1) // 第一回合
for (i = 0; i < PLAYER_COUNT; i++)
prevPos[i] = currPos[i];
else
{
for (i = 0; i < PLAYER_COUNT; i++)
{
vector<Point>::iterator it;
if (playerState[i] != 1 && !trail[i].empty() && (it = find(trail[i].begin(), trail[i].end(), currPos[i])) != --trail[i].end() && it != trail[i].end())
{
lastDir[i] = -1;
DrawLine(i, prevPos[i], currPos[i]);
allEnclosure.insert(CalcEnclose(i, it, trail[i].end()));
}
else if (playerState[i] == 1)
{
if (trail[i].empty())
trail[i].push_back(prevPos[i]);
if (trail[i][trail[i].size() - 1] != currPos[i])
{
trail[i].push_back(currPos[i]);
DrawLine(i, prevPos[i], currPos[i]);
}
}
}
for_each(allEnclosure.begin(), allEnclosure.end(), DoEnclose);
allEnclosure.clear();
}
if (stuckStatusLeft[myID] > 0)
{
cout << "[ACTION] s 0" << endl;
for (i = 0; i < PLAYER_COUNT; i++)
prevPos[i] = currPos[i];
continue;
}
if (playerState[myID] == 0)
{
for (i = 0; i < PLAYER_COUNT; i++)
if (i != myID && playerState[i] != -1 && GetDistance(i, myID) < 4) // 如果落笔后4回合内可能被打断
break;
if (i != PLAYER_COUNT)
{
if (rand() % 2)
changePenState = -1;
else
changePenState = 0;
}
else if (rand() % 2)
{
playerState[myID] = 1;
changePenState = 1;
}
else
changePenState = 0;
}
else
changePenState = 0;
if (playerState[myID] == 1) // 落笔态
{
while (true)
{
i = rand() % 5;
temp.x = currPos[myID].x + dx[i];
temp.y = currPos[myID].y + dy[i];
if (!IsReverse(i, lastDir[myID]) && CheckPosValidity(temp) && CheckRoute(myID, 1, currPos[myID], temp))
break;
}
if (i != 4)
lastDir[myID] = i;
}
else // 抬笔态
while (true)
{
i = rand() % (changePenState == -1 ? 4 : 5);
temp.x = currPos[myID].x + dx[i];
temp.y = currPos[myID].y + dy[i];
if (CheckPosValidity(temp) && CheckRoute(myID, 0, currPos[myID], temp))
break;
};
cout << "[ACTION] " << actions[i] << ' ' << changePenState << endl;
for (i = 0; i < PLAYER_COUNT; i++)
prevPos[i] = currPos[i];
}
}
}
圈地为王示例
最新推荐文章于 2019-08-20 16:39:45 发布