aaa
2026-08-02 15:24:29
发布于:广东
// ========== Force Console Subsystem ==========
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
// ========== Resolve Header Conflicts ==========
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
// ========== Standard Libraries ==========
#include <iostream>
#include <vector>
#include <deque>
#include <conio.h>
#include <ctime>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mmsystem.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "winmm.lib")
using namespace std;
// ========== Color Definitions ==========
enum ConsoleColor {
BLACK = 0,
BLUE = 1,
GREEN = 2,
CYAN = 3,
RED = 4,
MAGENTA = 5,
YELLOW = 6,
WHITE = 7,
GRAY = 8,
LIGHT_BLUE = 9,
LIGHT_GREEN = 10,
LIGHT_CYAN = 11,
LIGHT_RED = 12,
LIGHT_MAGENTA = 13,
LIGHT_YELLOW = 14,
BRIGHT_WHITE = 15
};
// ========== Game Definitions ==========
enum Dir { UP, DOWN, LEFT, RIGHT, NONE };
struct Point {
int x, y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
};
// Network Packet
struct SyncPacket {
int seqId;
int type;
bool gameOver;
int score;
int dir;
Point food;
int bodyLen;
Point body[512];
bool alive;
char playerName[32];
bool isRespawning;
int respawnTimer;
};
// ========== Console Buffer for Double Buffering ==========
class ConsoleBuffer {
private:
HANDLE hConsole;
int width, height;
vector<vector<char> > buffer;
vector<vector<int> > colorBuffer;
public:
ConsoleBuffer(int w = 80, int h = 30) : width(w), height(h) {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
buffer.resize(height, vector<char>(width, ' '));
colorBuffer.resize(height, vector<int>(width, WHITE));
clear();
}
void clear() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
buffer[y][x] = ' ';
colorBuffer[y][x] = WHITE;
}
}
}
void setChar(int x, int y, char c, int color = WHITE) {
if (x >= 0 && x < width && y >= 0 && y < height) {
buffer[y][x] = c;
colorBuffer[y][x] = color;
}
}
void writeString(int x, int y, const string& s, int color = WHITE) {
for (size_t i = 0; i < s.length(); i++) {
setChar(x + i, y, s[i], color);
}
}
void flush() {
CONSOLE_CURSOR_INFO ci;
GetConsoleCursorInfo(hConsole, &ci);
BOOL oldVisible = ci.bVisible;
ci.bVisible = FALSE;
SetConsoleCursorInfo(hConsole, &ci);
COORD pos = { 0, 0 };
SetConsoleCursorPosition(hConsole, pos);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (buffer[y][x] != ' ') {
SetConsoleTextAttribute(hConsole, colorBuffer[y][x]);
cout << buffer[y][x];
}
else {
cout << ' ';
}
}
if (y < height - 1) cout << '\n';
}
ci.bVisible = oldVisible;
SetConsoleCursorInfo(hConsole, &ci);
}
};
// ========== Utility Functions ==========
void setColor(int color) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
}
void gotoxy(int x, int y) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hConsole, pos);
}
void hideCursor() {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO ci;
GetConsoleCursorInfo(hConsole, &ci);
ci.bVisible = FALSE;
SetConsoleCursorInfo(hConsole, &ci);
}
void showCursor() {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO ci;
GetConsoleCursorInfo(hConsole, &ci);
ci.bVisible = TRUE;
SetConsoleCursorInfo(hConsole, &ci);
}
void playSound(int freq, int duration) {
Beep(freq, duration);
}
void welcome() {
system("cls");
setColor(LIGHT_CYAN);
cout << "Welcome to Snake Game!\n";
cout << "===================================\n";
cout << "Created by: RTX4399Ti Super \n";
cout << "===================================\n";
cout << endl;
setColor(WHITE);
cout << "Tips: Use WASD to control\n";
cout << "Space: Speed Boost\n";
cout << "P: Pause\n";
cout << "Q: Quit\n";
cout << "\nPress any key to start...";
_getch();
}
// ========== Game Class ==========
class SnakeGame {
private:
static const int WIDTH = 40;
static const int HEIGHT = 20;
static const int PORT = 8888;
static const int BUFFER_WIDTH = 80;
static const int BUFFER_HEIGHT = 30;
static const int RESPAWN_DELAY = 3;
static const int MAX_EXTRA_FOOD = 20;
static const int FOOD_REFRESH_INTERVAL = 5000;
// Network
SOCKET sock;
sockaddr_in targetAddr;
bool isOnline;
bool isConnected;
bool isHost;
int lastSeq;
int sendSeq;
DWORD connectStartTime;
string playerName;
string opponentName;
bool networkRunning;
// Game
deque<Point> mySnake;
deque<Point> enemySnake;
Dir myDir;
Dir enemyDir;
Point food;
int myScore;
int enemyScore;
bool gameOver;
bool paused;
int speed;
int level;
int foodEaten;
// Respawn
bool isRespawning;
DWORD deathTime;
DWORD respawnTime;
bool isInvincible;
DWORD invincibleEndTime;
// Statistics
int maxScore;
int gamesPlayed;
int wins;
// Power-ups
bool hasSpeedBoost;
DWORD boostEndTime;
bool foodIsSpecial;
int specialFoodTimer;
// Extra Foods
vector<Point> extraFoods;
DWORD lastFoodRefresh;
// Console buffer
ConsoleBuffer buffer;
public:
SnakeGame() : buffer(BUFFER_WIDTH, BUFFER_HEIGHT),
isOnline(false), isConnected(false), isHost(false),
lastSeq(-1), sendSeq(0), connectStartTime(0),
playerName("Player"), opponentName("Opponent"),
networkRunning(true),
myDir(RIGHT), enemyDir(LEFT),
myScore(0), enemyScore(0),
gameOver(false), paused(false),
speed(120), level(1), foodEaten(0),
isRespawning(false), deathTime(0), respawnTime(0),
isInvincible(false), invincibleEndTime(0),
maxScore(0), gamesPlayed(0), wins(0),
hasSpeedBoost(false), boostEndTime(0),
foodIsSpecial(false), specialFoodTimer(0),
lastFoodRefresh(0) {
srand((unsigned)time(NULL));
hideCursor();
loadStatistics();
}
~SnakeGame() {
if (isOnline && sock != INVALID_SOCKET) {
networkRunning = false;
closesocket(sock);
WSACleanup();
}
showCursor();
saveStatistics();
}
void run() {
while (true) {
showMainMenu();
if (isOnline) {
if (!initNetwork()) {
showMessage("Network initialization failed!", RED);
continue;
}
chooseRole();
if (!establishConnection()) {
showMessage("Connection failed!", RED);
waitForKey();
isOnline = false;
continue;
}
}
setupGame();
mainGameLoop();
showGameOver();
if (!askReplay()) break;
resetGame();
}
}
private:
// ========== Menu System ==========
void showMainMenu() {
system("cls");
setColor(LIGHT_CYAN);
cout << "+=====================================+\n";
cout << "| SNAKE GAME |\n";
cout << "+=====================================+\n\n";
setColor(WHITE);
cout << " [1] Single Player (AI Opponent)\n";
cout << " [2] Multiplayer (UDP)\n";
cout << " [3] Statistics\n";
cout << " [4] Settings\n";
cout << " [5] Exit\n\n";
cout << " Choose: ";
int choice;
cin >> choice;
cin.ignore();
switch (choice) {
case 1: isOnline = false; break;
case 2: isOnline = true; break;
case 3: showStatistics(); showMainMenu(); break;
case 4: showSettings(); showMainMenu(); break;
case 5: exit(0);
default: showMainMenu();
}
}
void showSettings() {
system("cls");
setColor(LIGHT_YELLOW);
cout << "+=====================================+\n";
cout << "| SETTINGS |\n";
cout << "+=====================================+\n\n";
setColor(WHITE);
cout << " Current Speed: " << speed << " ms\n";
cout << " [1] Faster (-10 ms)\n";
cout << " [2] Slower (+10 ms)\n";
cout << " [3] Reset to default (120)\n";
cout << " [4] Back\n\n";
cout << " Choose: ";
int choice;
cin >> choice;
switch (choice) {
case 1: if (speed > 30) speed -= 10; break;
case 2: if (speed < 300) speed += 10; break;
case 3: speed = 120; break;
case 4: return;
}
showSettings();
}
void showStatistics() {
system("cls");
setColor(LIGHT_GREEN);
cout << "+=====================================+\n";
cout << "| STATISTICS |\n";
cout << "+=====================================+\n\n";
setColor(WHITE);
cout << " Games Played: " << gamesPlayed << "\n";
cout << " High Score: " << maxScore << "\n";
cout << " Wins: " << wins << "\n";
if (gamesPlayed > 0) {
cout << " Win Rate: " << (wins * 100 / gamesPlayed) << "%\n";
}
cout << "\n Press any key to continue...";
_getch();
}
void chooseRole() {
system("cls");
setColor(LIGHT_CYAN);
cout << "+=====================================+\n";
cout << "| MULTIPLAYER SETUP |\n";
cout << "+=====================================+\n\n";
setColor(WHITE);
cout << " [1] Create Room (Host)\n";
cout << " [2] Join Room (Guest)\n";
cout << " [3] Back\n\n";
cout << " Choose: ";
int choice;
cin >> choice;
cin.ignore();
if (choice == 3) {
isOnline = false;
return;
}
isHost = (choice == 1);
cout << "\n Enter your name: ";
getline(cin, playerName);
if (playerName.empty()) playerName = isHost ? "Host" : "Guest";
if (isHost) {
setColor(LIGHT_GREEN);
cout << "\n [OK] Room created on port " << PORT << "\n";
cout << " [WAIT] Waiting for opponent...\n";
setColor(WHITE);
}
else {
string ip;
int port = PORT;
cout << "\n Enter opponent IP: ";
cin >> ip;
cout << " Enter port (default 8888): ";
string tmp;
cin >> tmp;
if (!tmp.empty()) port = atoi(tmp.c_str());
targetAddr.sin_family = AF_INET;
targetAddr.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &targetAddr.sin_addr);
setColor(LIGHT_YELLOW);
cout << "\n [WAIT] Connecting to " << ip << ":" << port << "...\n";
setColor(WHITE);
}
}
bool establishConnection() {
connectStartTime = GetTickCount();
const int CONNECT_TIMEOUT = 15000;
if (isHost) {
while (true) {
SyncPacket pkt;
if (recvPacket(pkt)) {
if (pkt.type == 1) {
opponentName = string(pkt.playerName);
setColor(LIGHT_GREEN);
cout << "\n [OK] " << opponentName << " connected!\n";
setColor(WHITE);
SyncPacket ack;
memset(&ack, 0, sizeof(ack));
ack.type = 2;
ack.seqId = ++sendSeq;
strcpy_s(ack.playerName, playerName.c_str());
sendPacket(ack);
isConnected = true;
Sleep(500);
return true;
}
}
DWORD elapsed = GetTickCount() - connectStartTime;
if (elapsed > CONNECT_TIMEOUT) {
setColor(RED);
cout << "\n [ERROR] Connection timeout!\n";
setColor(WHITE);
return false;
}
Sleep(50);
}
}
else {
for (int attempt = 0; attempt < 15; attempt++) {
SyncPacket req;
memset(&req, 0, sizeof(req));
req.type = 1;
req.seqId = ++sendSeq;
strcpy_s(req.playerName, playerName.c_str());
sendPacket(req);
for (int i = 0; i < 20; i++) {
SyncPacket pkt;
if (recvPacket(pkt)) {
if (pkt.type == 2) {
opponentName = string(pkt.playerName);
setColor(LIGHT_GREEN);
cout << "\n [OK] Connected to " << opponentName << "!\n";
setColor(WHITE);
isConnected = true;
return true;
}
}
Sleep(50);
}
setColor(LIGHT_YELLOW);
cout << "\r [RETRY] Attempt " << (attempt + 1) << "/15..." << string(20, ' ');
setColor(WHITE);
}
setColor(RED);
cout << "\n [ERROR] Connection failed!\n";
setColor(WHITE);
return false;
}
}
bool initNetwork() {
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
return false;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
WSACleanup();
return false;
}
u_long mode = 1;
ioctlsocket(sock, FIONBIO, &mode);
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(PORT);
if (bind(sock, (sockaddr*)&local, sizeof(local)) == SOCKET_ERROR) {
closesocket(sock);
WSACleanup();
return false;
}
return true;
}
void sendPacket(const SyncPacket& pkt) {
if (!networkRunning) return;
sendto(sock, (char*)&pkt, sizeof(SyncPacket), 0,
(sockaddr*)&targetAddr, sizeof(targetAddr));
}
bool recvPacket(SyncPacket& pkt) {
if (!networkRunning) return false;
int len = sizeof(targetAddr);
int ret = recvfrom(sock, (char*)&pkt, sizeof(SyncPacket), 0,
(sockaddr*)&targetAddr, &len);
return ret > 0;
}
// ========== Respawn Function ==========
void respawnPlayer() {
Point pos;
bool found = false;
for (int attempt = 0; attempt < 100; attempt++) {
pos.x = 2 + rand() % (WIDTH - 4);
pos.y = 2 + rand() % (HEIGHT - 4);
bool blocked = false;
for (size_t i = 0; i < enemySnake.size(); i++) {
if (enemySnake[i] == pos) { blocked = true; break; }
}
if (!blocked && !(pos == food)) {
found = true;
break;
}
}
if (!found) {
pos.x = 1 + rand() % (WIDTH - 2);
pos.y = 1 + rand() % (HEIGHT - 2);
}
mySnake.clear();
mySnake.push_back(pos);
mySnake.push_back({ pos.x - 1, pos.y });
mySnake.push_back({ pos.x - 2, pos.y });
myDir = RIGHT;
isRespawning = false;
isInvincible = true;
invincibleEndTime = GetTickCount() + 2000;
showMessage("You respawned! (2s invincible)", LIGHT_GREEN);
playSound(500, 100);
}
// ========== Game Setup ==========
void setupGame() {
system("cls");
mySnake.clear();
enemySnake.clear();
extraFoods.clear();
mySnake.push_back({ WIDTH / 2 + 5, HEIGHT / 2 });
mySnake.push_back({ WIDTH / 2 + 4, HEIGHT / 2 });
mySnake.push_back({ WIDTH / 2 + 3, HEIGHT / 2 });
enemySnake.push_back({ WIDTH / 2 - 5, HEIGHT / 2 });
enemySnake.push_back({ WIDTH / 2 - 4, HEIGHT / 2 });
enemySnake.push_back({ WIDTH / 2 - 3, HEIGHT / 2 });
myDir = RIGHT;
enemyDir = LEFT;
myScore = enemyScore = 0;
gameOver = false;
paused = false;
foodEaten = 0;
level = 1;
hasSpeedBoost = false;
foodIsSpecial = false;
isRespawning = false;
isInvincible = false;
lastFoodRefresh = GetTickCount();
spawnFood();
buffer.clear();
}
// ========== Main Game Loop ==========
void mainGameLoop() {
DWORD lastSend = GetTickCount();
DWORD lastAI = GetTickCount();
DWORD lastFrame = GetTickCount();
int frameCount = 0;
while (!gameOver) {
DWORD now = GetTickCount();
if (isRespawning) {
if (now >= respawnTime) {
respawnPlayer();
}
}
if (isInvincible && now >= invincibleEndTime) {
isInvincible = false;
}
//定时刷新食物
if (now - lastFoodRefresh > FOOD_REFRESH_INTERVAL) {
refreshFood();
lastFoodRefresh = now;
}
if (isOnline && isConnected) {
SyncPacket pkt;
while (recvPacket(pkt)) {
if (pkt.type == 3) {
gameOver = true;
showMessage("Opponent disconnected!", RED);
}
if (pkt.type == 0 && pkt.seqId > lastSeq) {
lastSeq = pkt.seqId;
applyPacket(pkt);
}
}
}
handleInput();
if (!paused && (isConnected || !isOnline) && !isRespawning) {
DWORD diff = now - lastFrame;
int currentSpeed = speed;
if (hasSpeedBoost) currentSpeed = speed / 2;
if (diff > (DWORD)currentSpeed) {
updateGame();
lastFrame = now;
frameCount++;
if (frameCount % 10 == 0) {
updatePowerups();
}
}
if (!isOnline) {
DWORD aiDiff = now - lastAI;
if (aiDiff > (DWORD)(speed * 1.5)) {
aiMove();
bool ate2 = false;
if (!moveSnake(enemySnake, enemyDir, mySnake, ate2)) {
enemySnake.clear();
enemySnake.push_back({ WIDTH / 2 - 5, HEIGHT / 2 });
enemySnake.push_back({ WIDTH / 2 - 4, HEIGHT / 2 });
enemySnake.push_back({ WIDTH / 2 - 3, HEIGHT / 2 });
enemyDir = LEFT;
}
if (ate2) {
enemyScore += 10;
spawnFood();
}
lastAI = now;
}
}
}
render();
if (isOnline && isConnected) {
DWORD diff = now - lastSend;
if (diff > (DWORD)(speed / 2)) {
sendPacket(buildPacket());
lastSend = now;
}
}
Sleep(10);
}
}
void updateGame() {
bool ate = false;
if (!moveSnake(mySnake, myDir, enemySnake, ate)) {
if (isOnline) {
isRespawning = true;
deathTime = GetTickCount();
respawnTime = GetTickCount() + RESPAWN_DELAY * 1000;
showMessage("You died! Respawning in 3s...", LIGHT_RED);
playSound(200, 100);
SyncPacket pkt = buildPacket();
pkt.alive = false;
pkt.isRespawning = true;
pkt.respawnTimer = RESPAWN_DELAY;
sendPacket(pkt);
}
else {
gameOver = true;
}
return;
}
if (ate) {
myScore += 10;
foodEaten++;
if (foodEaten % 5 == 0) {
level++;
if (speed > 50) speed -= 5;
}
if (foodEaten % 3 == 0) {
foodIsSpecial = true;
specialFoodTimer = 30;
}
spawnFood();
}
}
void updatePowerups() {
if (foodIsSpecial) {
specialFoodTimer--;
if (specialFoodTimer <= 0) {
foodIsSpecial = false;
spawnFood();
}
}
if (hasSpeedBoost) {
DWORD now = GetTickCount();
if (now > boostEndTime) {
hasSpeedBoost = false;
}
}
}
// ========== Game Logic ==========
bool moveSnake(deque<Point>& snake, Dir& dir, const deque<Point>& other, bool& ate) {
if (snake.empty()) return false;
Point head = snake.front();
Point newHead = head;
switch (dir) {
case UP: newHead.y--; break;
case DOWN: newHead.y++; break;
case LEFT: newHead.x--; break;
case RIGHT: newHead.x++; break;
default: break;
}
// 边界碰撞检测
if (newHead.x < 0 || newHead.x >= WIDTH ||
newHead.y < 0 || newHead.y >= HEIGHT) {
if (&snake == &mySnake && isInvincible) {
if (newHead.x < 0) newHead.x = WIDTH - 1;
else if (newHead.x >= WIDTH) newHead.x = 0;
if (newHead.y < 0) newHead.y = HEIGHT - 1;
else if (newHead.y >= HEIGHT) newHead.y = 0;
}
else {
// 蛇死亡,身体化为食物
if (&snake == &mySnake) {
spawnBodyAsFood(snake);
}
return false;
}
}
// 自身碰撞检测
for (size_t i = 0; i < snake.size() - 1; i++) {
if (snake[i] == newHead) {
if (&snake == &mySnake && isInvincible) {
break;
}
else {
if (&snake == &mySnake) {
spawnBodyAsFood(snake);
}
return false;
}
}
}
// 与其他蛇碰撞检测
for (size_t i = 0; i < other.size(); i++) {
if (other[i] == newHead) {
if (&snake == &mySnake && isInvincible) {
break;
}
else {
if (&snake == &mySnake) {
spawnBodyAsFood(snake);
}
return false;
}
}
}
snake.push_front(newHead);
// 检查是否吃到食物
ate = (newHead == food);
if (!ate) {
for (size_t i = 0; i < extraFoods.size(); i++) {
if (extraFoods[i] == newHead) {
ate = true;
extraFoods.erase(extraFoods.begin() + i);
break;
}
}
}
if (ate && foodIsSpecial) {
myScore += 20;
hasSpeedBoost = true;
boostEndTime = GetTickCount() + 5000;
foodIsSpecial = false;
}
else if (ate) {
if (&snake == &mySnake) {
myScore += 10;
}
else {
enemyScore += 10;
}
}
if (!ate) snake.pop_back();
return true;
}
// ========== 蛇身体化为食物 ==========
void spawnBodyAsFood(const deque<Point>& snake) {
if (snake.empty()) return;
int bodyCount = 0;
for (size_t i = 1; i < snake.size(); i++) {
if (rand() % 100 < 70 && extraFoods.size() < MAX_EXTRA_FOOD) {
Point foodPos = snake[i];
bool occupied = false;
if (foodPos == food) occupied = true;
for (size_t j = 0; j < extraFoods.size(); j++) {
if (extraFoods[j] == foodPos) {
occupied = true;
break;
}
}
for (size_t j = 0; j < mySnake.size(); j++) {
if (mySnake[j] == foodPos && &snake != &mySnake) {
occupied = true;
break;
}
}
for (size_t j = 0; j < enemySnake.size(); j++) {
if (enemySnake[j] == foodPos && &snake != &enemySnake) {
occupied = true;
break;
}
}
if (!occupied) {
extraFoods.push_back(foodPos);
bodyCount++;
}
}
}
if (bodyCount > 0) {
char msg[64];
sprintf(msg, "Snake body turned into %d foods!", bodyCount);
showMessage(msg, LIGHT_YELLOW);
playSound(400, 50);
}
}
// ========== 定时刷新食物 ==========
void refreshFood() {
if (extraFoods.size() >= MAX_EXTRA_FOOD) return;
int newFoods = 1 + rand() % 3;
for (int i = 0; i < newFoods && extraFoods.size() < MAX_EXTRA_FOOD; i++) {
Point newFood;
bool valid = false;
for (int attempt = 0; attempt < 50; attempt++) {
newFood.x = rand() % WIDTH;
newFood.y = rand() % HEIGHT;
bool occupied = false;
if (newFood == food) occupied = true;
for (size_t j = 0; j < extraFoods.size(); j++) {
if (extraFoods[j] == newFood) {
occupied = true;
break;
}
}
for (size_t j = 0; j < mySnake.size(); j++) {
if (mySnake[j] == newFood) {
occupied = true;
break;
}
}
for (size_t j = 0; j < enemySnake.size(); j++) {
if (enemySnake[j] == newFood) {
occupied = true;
break;
}
}
if (!occupied) {
valid = true;
break;
}
}
if (valid) {
extraFoods.push_back(newFood);
}
}
}
void spawnFood() {
int tries = 0;
while (tries < 1000) {
food.x = rand() % WIDTH;
food.y = rand() % HEIGHT;
bool bad = false;
for (size_t i = 0; i < mySnake.size(); i++) {
if (mySnake[i] == food) { bad = true; break; }
}
if (!bad) {
for (size_t i = 0; i < enemySnake.size(); i++) {
if (enemySnake[i] == food) { bad = true; break; }
}
}
if (!bad) {
for (size_t i = 0; i < extraFoods.size(); i++) {
if (extraFoods[i] == food) {
bad = true;
break;
}
}
}
if (!bad) {
if (foodEaten > 0 && foodEaten % 3 == 0 && rand() % 5 == 0) {
foodIsSpecial = true;
specialFoodTimer = 30;
}
return;
}
tries++;
}
}
void aiMove() {
if (enemySnake.empty()) return;
Point head = enemySnake.front();
int dx = food.x - head.x;
int dy = food.y - head.y;
Dir preferredDirs[4];
int dirCount = 0;
if (abs(dx) >= abs(dy)) {
if (dx > 0) preferredDirs[dirCount++] = RIGHT;
if (dx < 0) preferredDirs[dirCount++] = LEFT;
if (dy > 0) preferredDirs[dirCount++] = DOWN;
if (dy < 0) preferredDirs[dirCount++] = UP;
}
else {
if (dy > 0) preferredDirs[dirCount++] = DOWN;
if (dy < 0) preferredDirs[dirCount++] = UP;
if (dx > 0) preferredDirs[dirCount++] = RIGHT;
if (dx < 0) preferredDirs[dirCount++] = LEFT;
}
if (dirCount > 0) {
enemyDir = preferredDirs[rand() % dirCount];
}
}
// ========== Input Handling ==========
void handleInput() {
if (!_kbhit()) return;
int key = _getch();
if (key == 224) {
int arrowKey = _getch();
if (!isHost && isOnline) {
switch (arrowKey) {
case 72: if (myDir != DOWN) myDir = UP; break;
case 80: if (myDir != UP) myDir = DOWN; break;
case 75: if (myDir != RIGHT) myDir = LEFT; break;
case 77: if (myDir != LEFT) myDir = RIGHT; break;
}
}
return;
}
char c = (char)tolower(key);
if (isHost && isOnline) {
switch (c) {
case 'w': if (myDir != DOWN) myDir = UP; break;
case 's': if (myDir != UP) myDir = DOWN; break;
case 'a': if (myDir != RIGHT) myDir = LEFT; break;
case 'd': if (myDir != LEFT) myDir = RIGHT; break;
}
}
else if (!isOnline) {
switch (c) {
case 'w': if (myDir != DOWN) myDir = UP; break;
case 's': if (myDir != UP) myDir = DOWN; break;
case 'a': if (myDir != RIGHT) myDir = LEFT; break;
case 'd': if (myDir != LEFT) myDir = RIGHT; break;
}
}
switch (c) {
case 'p': paused = !paused; break;
case 'q': gameOver = true; break;
case 'r': if (gameOver) resetGame(); break;
case ' ':
if (!paused && !gameOver) {
hasSpeedBoost = true;
boostEndTime = GetTickCount() + 3000;
}
break;
}
}
// ========== Rendering ==========
void render() {
buffer.clear();
for (int x = 0; x < WIDTH + 2; x++) {
buffer.setChar(1 + x, 0, '-', LIGHT_CYAN);
buffer.setChar(1 + x, HEIGHT + 1, '-', LIGHT_CYAN);
}
for (int y = 0; y < HEIGHT + 2; y++) {
buffer.setChar(0, y, '|', LIGHT_CYAN);
buffer.setChar(WIDTH + 1, y, '|', LIGHT_CYAN);
}
buffer.setChar(0, 0, '+', LIGHT_CYAN);
buffer.setChar(WIDTH + 1, 0, '+', LIGHT_CYAN);
buffer.setChar(0, HEIGHT + 1, '+', LIGHT_CYAN);
buffer.setChar(WIDTH + 1, HEIGHT + 1, '+', LIGHT_CYAN);
for (size_t i = 0; i < mySnake.size(); i++) {
int x = 1 + mySnake[i].x;
int y = 1 + mySnake[i].y;
int color = LIGHT_GREEN;
if (i == 0) {
if (isInvincible) {
DWORD now = GetTickCount();
if ((now % 300) < 150) {
color = BRIGHT_WHITE;
}
}
buffer.setChar(x, y, 'O', color);
}
else {
buffer.setChar(x, y, 'o', GREEN);
}
}
for (size_t i = 0; i < enemySnake.size(); i++) {
int x = 1 + enemySnake[i].x;
int y = 1 + enemySnake[i].y;
if (i == 0) {
buffer.setChar(x, y, 'X', LIGHT_RED);
}
else {
buffer.setChar(x, y, 'x', RED);
}
}
int fx = 1 + food.x;
int fy = 1 + food.y;
if (foodIsSpecial) {
buffer.setChar(fx, fy, '$', LIGHT_YELLOW);
}
else {
buffer.setChar(fx, fy, '*', LIGHT_CYAN);
}
// 绘制额外食物
for (size_t i = 0; i < extraFoods.size(); i++) {
int x = 1 + extraFoods[i].x;
int y = 1 + extraFoods[i].y;
int colors[] = { LIGHT_MAGENTA, LIGHT_YELLOW, LIGHT_BLUE, LIGHT_CYAN };
buffer.setChar(x, y, '+', colors[i % 4]);
}
string info = " " + playerName + ": " + toString(myScore);
if (hasSpeedBoost) {
info += " [BOOST]";
}
if (isInvincible) {
info += " [INVINCIBLE]";
}
if (isRespawning) {
DWORD now = GetTickCount();
int remaining = (respawnTime - now) / 1000;
if (remaining < 0) remaining = 0;
info += " [RESPAWN in " + toString(remaining) + "s]";
}
info += " | " + opponentName + ": " + toString(enemyScore);
info += " | Level: " + toString(level);
info += " | Speed: " + toString(speed) + "ms";
info += " | Extra: " + toString(extraFoods.size());
if (isOnline) {
info += " [" + string(isConnected ? "Connected" : "Waiting") + "]";
}
buffer.writeString(0, HEIGHT + 3, info, WHITE);
string controls;
if (isOnline && isConnected) {
if (isHost) {
controls = " WASD: Move | Space: Boost | P: Pause | Q: Quit";
}
else {
controls = " Arrows: Move | Space: Boost | P: Pause | Q: Quit";
}
}
else if (isOnline && !isConnected) {
controls = " [WAIT] Waiting for connection...";
}
else {
controls = " WASD: Move | Space: Boost | P: Pause | Q: Quit";
}
buffer.writeString(0, HEIGHT + 4, controls, WHITE);
if (paused) {
buffer.writeString(WIDTH / 2 - 3, HEIGHT / 2, "PAUSED", LIGHT_YELLOW);
}
if (hasSpeedBoost) {
buffer.writeString(WIDTH / 2 - 5, HEIGHT / 2 + 2, "SPEED BOOST!", LIGHT_YELLOW);
}
if (foodIsSpecial) {
buffer.writeString(WIDTH / 2 - 6, HEIGHT / 2 + 4, "SPECIAL FOOD!", LIGHT_YELLOW);
}
if (isRespawning) {
DWORD now = GetTickCount();
int remaining = (respawnTime - now) / 1000;
if (remaining < 0) remaining = 0;
string respawnText = "RESPAWN IN " + toString(remaining) + "s";
buffer.writeString(WIDTH / 2 - respawnText.length() / 2, HEIGHT / 2 - 2, respawnText, LIGHT_RED);
}
buffer.flush();
}
string toString(int num) {
char buf[32];
sprintf(buf, "%d", num);
return string(buf);
}
// ========== Network Packets ==========
SyncPacket buildPacket() {
SyncPacket pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.type = 0;
pkt.seqId = ++sendSeq;
pkt.gameOver = gameOver;
pkt.score = myScore;
pkt.dir = (int)myDir;
pkt.food = food;
pkt.bodyLen = (int)mySnake.size();
for (size_t i = 0; i < mySnake.size(); i++) {
pkt.body[i] = mySnake[i];
}
pkt.alive = !isRespawning;
pkt.isRespawning = isRespawning;
if (isRespawning) {
DWORD now = GetTickCount();
pkt.respawnTimer = (respawnTime - now) / 1000;
if (pkt.respawnTimer < 0) pkt.respawnTimer = 0;
}
else {
pkt.respawnTimer = 0;
}
strcpy_s(pkt.playerName, playerName.c_str());
return pkt;
}
void applyPacket(const SyncPacket& pkt) {
if (pkt.isRespawning) {
enemyScore = pkt.score;
if (pkt.respawnTimer > 0) {
showMessage("Opponent is respawning in " + toString(pkt.respawnTimer) + "s...", LIGHT_YELLOW);
}
}
else if (pkt.alive) {
enemySnake.clear();
enemyDir = (Dir)pkt.dir;
enemyScore = pkt.score;
for (int i = 0; i < pkt.bodyLen; i++) {
enemySnake.push_back(pkt.body[i]);
}
}
else {
enemySnake.clear();
enemyScore = pkt.score;
}
bool myHasFood = false;
for (size_t i = 0; i < mySnake.size(); i++) {
if (mySnake[i] == food) { myHasFood = true; break; }
}
bool enHasFood = false;
for (size_t i = 0; i < enemySnake.size(); i++) {
if (enemySnake[i] == pkt.food) { enHasFood = true; break; }
}
if (!myHasFood && !enHasFood) {
food = pkt.food;
}
if (pkt.gameOver) {
gameOver = true;
}
if (string(pkt.playerName) != opponentName) {
opponentName = string(pkt.playerName);
}
}
// ========== Game Over ==========
void showGameOver() {
gamesPlayed++;
if (myScore > maxScore) maxScore = myScore;
if (myScore > enemyScore) wins++;
buffer.clear();
int cx = WIDTH / 2 - 4;
int cy = HEIGHT / 2 - 2;
buffer.writeString(cx, cy, "+----------+", LIGHT_RED);
buffer.writeString(cx, cy + 1, "|GAME OVER!|", LIGHT_RED);
buffer.writeString(cx, cy + 2, "+----------+", LIGHT_RED);
string scoreText = "Your Score: " + toString(myScore) + " Opponent: " + toString(enemyScore);
buffer.writeString(WIDTH / 2 - scoreText.length() / 2, cy + 4, scoreText, WHITE);
string result;
int resultColor;
if (myScore > enemyScore) {
result = ">>> YOU WIN! <<<";
resultColor = LIGHT_GREEN;
}
else if (myScore < enemyScore) {
result = ">>> YOU LOSE! <<<";
resultColor = LIGHT_RED;
}
else {
result = ">>> DRAW! <<<";
resultColor = LIGHT_YELLOW;
}
buffer.writeString(WIDTH / 2 - result.length() / 2, cy + 5, result, resultColor);
buffer.writeString(WIDTH / 2 - 6, cy + 7, "Press R to replay", WHITE);
buffer.writeString(WIDTH / 2 - 6, cy + 8, "Press Q to quit", WHITE);
buffer.flush();
}
bool askReplay() {
while (true) {
if (_kbhit()) {
char c = tolower(_getch());
if (c == 'r') return true;
if (c == 'q') return false;
}
Sleep(50);
}
}
void resetGame() {
mySnake.clear();
enemySnake.clear();
extraFoods.clear();
myDir = RIGHT;
enemyDir = LEFT;
myScore = 0;
enemyScore = 0;
gameOver = false;
paused = false;
foodEaten = 0;
level = 1;
speed = 120;
hasSpeedBoost = false;
foodIsSpecial = false;
isRespawning = false;
isInvincible = false;
lastFoodRefresh = GetTickCount();
setupGame();
}
// ========== Utility ==========
void showMessage(const string& msg, int color = WHITE) {
setColor(color);
gotoxy(2, HEIGHT + 5);
cout << msg << string(20, ' ');
setColor(WHITE);
}
void waitForKey() {
gotoxy(0, HEIGHT + 7);
cout << "Press any key to continue...";
_getch();
}
void loadStatistics() {
maxScore = 0;
gamesPlayed = 0;
wins = 0;
}
void saveStatistics() {
// Save to file would go here
}
};
// ========== Main Function ==========
int main() {
welcome();
SetConsoleTitleA("Snake Game");
try {
SnakeGame game;
game.run();
}
catch (const exception& e) {
setColor(RED);
cout << "Error: " << e.what() << endl;
setColor(WHITE);
system("pause");
}
return 0;
}
这里空空如也




















有帮助,赞一个