#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <conio.h>
#include <utility>
#include <cstring>
#ifdef _WIN32
#include <windows.h>
#define CLEAR_SCREEN "cls"
#define SLEEP_MS(x) Sleep(x)
#else
#define CLEAR_SCREEN "clear"
#define SLEEP_MS(x) usleep(x * 1000)
#endif
using namespace std;
enum Direction { UP, DOWN, LEFT, RIGHT };
struct Bullet {
int x, y;
Direction dir;
bool alive;
Bullet(int x_, int y_, Direction dir_) : x(x_), y(y_), dir(dir_), alive(true) {}
};
class Tank {
public:
int x, y;
Direction dir;
bool alive;
char symbol;
Tank(int x_, int y_, char sym) : x(x_), y(y_), dir(UP), alive(true), symbol(sym) {}
bool move(Direction newDir, int mapWidth, int mapHeight, const vector<pair<int, int> >& obstacles);
};
const int MAP_WIDTH = 120;
const int MAP_HEIGHT = 26;
const int REFRESH_RATE = 100;
const int DIR[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
const int ENEMY_COUNT = 5;
const int ENEMY_DETECT_RANGE = 8;
Tank player(1, 1, 'P');
vector<Tank> enemies;
vector<Bullet> bullets;
vector<pair<int, int> > obstacles;
bool gameOver = false;
string winner = "";
bool visited[MAP_HEIGHT][MAP_WIDTH];
bool isObstacle(int x, int y) {
if (x <= 0 || x >= MAP_HEIGHT-1 || y <= 0 || y >= MAP_WIDTH-1)
return true;
for (vector<pair<int, int> >::const_iterator it = obstacles.begin(); it != obstacles.end(); ++it) {
if (it->first == x && it->second == y) {
return true;
}
}
return false;
}
bool hasTankAt(int x, int y) {
if (player.alive && player.x == x && player.y == y)
return true;
for (size_t i = 0; i < enemies.size(); ++i) {
if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y)
return true;
}
return false;
}
bool Tank::move(Direction newDir, int mapWidth, int mapHeight, const vector<pair<int, int> >& obstacles) {
dir = newDir;
int newX = x, newY = y;
switch (newDir) {
case UP: newX--; break;
case DOWN: newX++; break;
case LEFT: newY--; break;
case RIGHT: newY++; break;
}
if (newX >= 1 && newX <= mapHeight-2
&& newY >= 1 && newY <= mapWidth-2
&& !isObstacle(newX, newY)
&& !hasTankAt(newX, newY)) {
x = newX;
y = newY;
return true;
}
return false;
}
void dfs(int x, int y, int endX, int endY, bool& reachable) {
if (x == endX && y == endY) {
reachable = true;
return;
}
if (x < 1 || x >= MAP_HEIGHT-1 || y < 1 || y >= MAP_WIDTH-1 || visited[x][y] || isObstacle(x, y)) {
return;
}
visited[x][y] = true;
for (int i = 0; i < 4 && !reachable; i++) {
dfs(x + DIR[i][0], y + DIR[i][1], endX, endY, reachable);
}
}
bool isMapConnected(int playerX, int playerY, const vector<Tank>& enemies) {
for (size_t i = 0; i < enemies.size(); ++i) {
if (!enemies[i].alive) continue;
memset(visited, false, sizeof(visited));
bool reachable = false;
dfs(playerX, playerY, enemies[i].x, enemies[i].y, reachable);
if (!reachable) return false;
}
return true;
}
pair<int, int> generateSuperSafePos() {
int x, y;
bool safe = false;
while (!safe) {
x = rand() % (MAP_HEIGHT - 2) + 1;
y = rand() % (MAP_WIDTH - 2) + 1;
if (isObstacle(x, y)) continue;
int freeDir = 0;
for (int i = 0; i < 4; i++) {
int nx = x + DIR[i][0];
int ny = y + DIR[i][1];
if (nx >= 1 && nx <= MAP_HEIGHT-2 && ny >= 1 && ny <= MAP_WIDTH-2 && !isObstacle(nx, ny)) {
freeDir++;
}
}
if (freeDir >= 3) {
safe = true;
}
}
return make_pair(x, y);
}
void generateOpenMap() {
obstacles.clear();
bool connected = false;
while (!connected) {
obstacles.clear();
const int WALL_SEGMENTS = 25;
const int MIN_WALL_LEN = 2;
const int MAX_WALL_LEN = 4;
for (int i = 0; i < WALL_SEGMENTS; i++) {
Direction dir = (Direction)(rand() % 2);
int len = MIN_WALL_LEN + rand() % (MAX_WALL_LEN - MIN_WALL_LEN + 1);
int x, y;
do {
x = rand() % (MAP_HEIGHT - 2) + 1;
y = rand() % (MAP_WIDTH - 2) + 1;
} while (isObstacle(x, y));
for (int j = 0; j < len; j++) {
int nx = x, ny = y;
if (dir == 0) ny += j;
else nx += j;
if (nx < 1 || nx >= MAP_HEIGHT-1 || ny < 1 || ny >= MAP_WIDTH-1 || isObstacle(nx, ny)) {
break;
}
obstacles.push_back(make_pair(nx, ny));
}
}
pair<int, int> playerPos = generateSuperSafePos();
player.x = playerPos.first;
player.y = playerPos.second;
player.alive = true;
enemies.clear();
for (int i = 0; i < ENEMY_COUNT; ++i) {
pair<int, int> enemyPos = generateSuperSafePos();
enemies.push_back(Tank(enemyPos.first, enemyPos.second, 'E'));
}
connected = isMapConnected(player.x, player.y, enemies);
}
}
bool hasLineOfSight(int enemyX, int enemyY, int playerX, int playerY) {
int dx = abs(playerX - enemyX);
int dy = abs(playerY - enemyY);
if (dx > 0 && dy > 0) {
return false;
}
if (dx == 0) {
int startY = min(enemyY, playerY) + 1;
int endY = max(enemyY, playerY);
for (int y = startY; y < endY; y++) {
if (isObstacle(enemyX, y)) {
return false;
}
}
}
else if (dy == 0) {
int startX = min(enemyX, playerX) + 1;
int endX = max(enemyX, playerX);
for (int x = startX; x < endX; x++) {
if (isObstacle(x, enemyY)) {
return false;
}
}
}
return true;
}
bool canAttackPlayer(int enemyX, int enemyY) {
if (!player.alive) return false;
int dx = abs(player.x - enemyX);
int dy = abs(player.y - enemyY);
int distance = dx + dy;
if (distance > ENEMY_DETECT_RANGE) {
return false;
}
return hasLineOfSight(enemyX, enemyY, player.x, player.y);
}
void clearScreen() {
system(CLEAR_SCREEN);
}
void setColor(int color) {
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
#else
(void)color;
#endif
}
void drawMap() {
clearScreen();
for (int i = 0; i < MAP_HEIGHT; i++) {
for (int j = 0; j < MAP_WIDTH; j++) {
if (i == 0 || i == MAP_HEIGHT-1 || j == 0 || j == MAP_WIDTH-1) {
setColor(7);
cout << "#";
} else if (isObstacle(i, j)) {
setColor(7);
cout << "X";
} else if (i == player.x && j == player.y && player.alive) {
setColor(9);
cout << player.symbol;
} else {
bool isEnemy = false;
for (size_t k = 0; k < enemies.size(); ++k) {
if (enemies[k].alive && i == enemies[k].x && j == enemies[k].y) {
setColor(12);
cout << enemies[k].symbol;
isEnemy = true;
break;
}
}
if (isEnemy) continue;
bool isBullet = false;
for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ++it) {
if (it->alive && it->x == i && it->y == j) {
setColor(14);
cout << "*";
isBullet = true;
break;
}
}
if (!isBullet) {
setColor(7);
cout << " ";
}
}
}
cout << endl;
}
setColor(7);
cout << "===== 超大开阔版坦克对战 =====" << endl;
cout << "操作:W(上) A(左) S(下) D(右) 空格(发射) Q(退出) R(手动重开)" << endl;
cout << "玩家:";
setColor(9); cout << "P(蓝色)";
setColor(7); cout << " 敌人:";
setColor(12); cout << "E(红色)";
setColor(7); cout << " 子弹:";
setColor(14); cout << "*";
setColor(7); cout << " 边界:# 障碍物:X" << endl;
if (gameOver) {
cout << "游戏结束!" << winner << "!3秒后自动重新开始..." << endl;
}
}
void checkCollision() {
for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ) {
if (!it->alive) {
it = bullets.erase(it);
continue;
}
if (it->x == player.x && it->y == player.y && player.alive) {
player.alive = false;
gameOver = true;
winner = "玩家被击败,电脑获胜";
it->alive = false;
}
bool hitEnemy = false;
for (size_t k = 0; k < enemies.size(); ++k) {
if (enemies[k].alive && it->x == enemies[k].x && it->y == enemies[k].y) {
enemies[k].alive = false;
it->alive = false;
hitEnemy = true;
break;
}
}
if (hitEnemy) {
bool allDead = true;
for (size_t k = 0; k < enemies.size(); ++k) {
if (enemies[k].alive) { allDead = false; break; }
}
if (allDead) {
gameOver = true;
winner = "所有敌人被消灭,玩家获胜";
}
continue;
}
if (isObstacle(it->x, it->y) || it->x <= 0 || it->x >= MAP_HEIGHT-1 || it->y <= 0 || it->y >= MAP_WIDTH-1) {
it->alive = false;
} else {
++it;
}
}
}
void moveBullets() {
for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ++it) {
if (!it->alive) continue;
switch (it->dir) {
case UP: it->x--; break;
case DOWN: it->x++; break;
case LEFT: it->y--; break;
case RIGHT: it->y++; break;
}
}
}
void enemyAI() {
if (gameOver || !player.alive) return;
for (size_t k = 0; k < enemies.size(); ++k) {
Tank& enemy = enemies[k];
if (!enemy.alive) continue;
bool playerInRange = canAttackPlayer(enemy.x, enemy.y);
if (playerInRange) {
if (player.x < enemy.x) {
enemy.dir = UP;
} else if (player.x > enemy.x) {
enemy.dir = DOWN;
} else if (player.y < enemy.y) {
enemy.dir = LEFT;
} else if (player.y > enemy.y) {
enemy.dir = RIGHT;
}
int fireRand = rand() % 5;
if (fireRand == 0 || fireRand == 1 || fireRand == 2 || fireRand == 3) {
int bulletX = enemy.x, bulletY = enemy.y;
switch (enemy.dir) {
case UP: bulletX--; break;
case DOWN: bulletX++; break;
case LEFT: bulletY--; break;
case RIGHT: bulletY++; break;
}
bullets.push_back(Bullet(bulletX, bulletY, enemy.dir));
}
int moveRand = rand() % 4;
if (moveRand == 0) {
enemy.move(enemy.dir, MAP_WIDTH, MAP_HEIGHT, obstacles);
}
}
else {
int moveRand = rand() % 4;
if (moveRand == 0) {
Direction newDir = (Direction)(rand() % 4);
enemy.move(newDir, MAP_WIDTH, MAP_HEIGHT, obstacles);
}
int fireRand = rand() % 30;
if (fireRand == 0) {
int bulletX = enemy.x, bulletY = enemy.y;
switch (enemy.dir) {
case UP: bulletX--; break;
case DOWN: bulletX++; break;
case LEFT: bulletY--; break;
case RIGHT: bulletY++; break;
}
bullets.push_back(Bullet(bulletX, bulletY, enemy.dir));
}
}
}
}
char getInput() {
#ifdef _WIN32
if (kbhit()) {
return getch();
}
#else
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv) > 0) {
return getchar();
}
#endif
return 0;
}
void handleInput() {
if (!player.alive || gameOver) return;
char key = getInput();
switch (key) {
case 'w': case 'W': player.move(UP, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
case 's': case 'S': player.move(DOWN, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
case 'a': case 'A': player.move(LEFT, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
case 'd': case 'D': player.move(RIGHT, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
case ' ': {
int bulletX = player.x, bulletY = player.y;
switch (player.dir) {
case UP: bulletX--; break;
case DOWN: bulletX++; break;
case LEFT: bulletY--; break;
case RIGHT: bulletY++; break;
}
bullets.push_back(Bullet(bulletX, bulletY, player.dir));
break;
}
case 'r': case 'R':
gameOver = true;
winner = "手动重新开始";
break;
case 'q': case 'Q':
cout << "\n游戏退出!" << endl;
exit(0);
break;
}
}
void resetGame() {
player.alive = true;
bullets.clear();
gameOver = false;
winner = "";
generateOpenMap();
}
int main() {
srand(static_cast<unsigned int>(time(NULL)));
generateOpenMap();
while (true) {
while (!gameOver) {
handleInput();
enemyAI();
moveBullets();
checkCollision();
drawMap();
SLEEP_MS(REFRESH_RATE);
}
drawMap();
SLEEP_MS(3000);
resetGame();
}
return 0;
}