You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
9.2 KiB
340 lines
9.2 KiB
#include <iostream>
|
|
#include <conio.h>
|
|
#include <windows.h>
|
|
#include <ctime>
|
|
|
|
using namespace std;
|
|
|
|
const int WIDTH = 30;
|
|
const int HEIGHT = 15;
|
|
const int OBSTACLE_COUNT = 10;
|
|
const int NORMAL_FRUIT_SCORE = 10;
|
|
const int HIGH_SCORE_FRUIT_SCORE = 30;
|
|
const int WIN_SCORE = 500;
|
|
|
|
bool gameover, paused, highScoreFruit;
|
|
int x, y, fruitX, fruitY, highScoreFruitX, highScoreFruitY, score;
|
|
int tailX[100], tailY[100], nTail;
|
|
int x2, y2, score2, tailX2[100], tailY2[100], nTail2;
|
|
int obstacles[OBSTACLE_COUNT][2];
|
|
enum Direction { STOP = 0, LEFT, RIGHT, UP, DOWN };
|
|
Direction dir, dir2;
|
|
bool twoSnakeMode = false; // Ôö¼ÓģʽѡÔñ±êÖ¾
|
|
|
|
void gotoxy(int x, int y) {
|
|
COORD coord;
|
|
coord.X = x;
|
|
coord.Y = y;
|
|
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
|
|
}
|
|
|
|
void SetConsoleCursor(bool visible) {
|
|
CONSOLE_CURSOR_INFO cci;
|
|
cci.dwSize = 1;
|
|
cci.bVisible = visible ? TRUE : FALSE;
|
|
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
|
|
}
|
|
|
|
void InitializeObstacles() {
|
|
for (int i = 0; i < OBSTACLE_COUNT; ++i) {
|
|
do {
|
|
obstacles[i][0] = rand() % WIDTH;
|
|
obstacles[i][1] = rand() % HEIGHT;
|
|
} while ((obstacles[i][0] == x && obstacles[i][1] == y) || (twoSnakeMode && obstacles[i][0] == x2 && obstacles[i][1] == y2));
|
|
}
|
|
}
|
|
|
|
void GenerateFruit(int& fruitX, int& fruitY) {
|
|
int tt;
|
|
do {
|
|
tt = 0;
|
|
fruitX = rand() % (WIDTH - 2) + 1;
|
|
fruitY = rand() % (HEIGHT - 2) + 1;
|
|
for (int i = 0; i < nTail; ++i)
|
|
if (fruitX == tailX[i] && fruitY == tailY[i]) tt = 1;
|
|
for (int i = 0; i < OBSTACLE_COUNT; ++i)
|
|
if (fruitX == obstacles[i][0] && fruitY == obstacles[i][1]) tt = 1;
|
|
if (fruitX == x && fruitY == y) tt = 1;
|
|
if (twoSnakeMode && fruitX == x2 && fruitY == y2) tt = 1;
|
|
} while (tt == 1);
|
|
}
|
|
|
|
void Setup() {
|
|
gameover = paused = highScoreFruit = false;
|
|
dir = dir2 = STOP;
|
|
x = WIDTH / 2;
|
|
y = HEIGHT / 2;
|
|
if (twoSnakeMode) {
|
|
x2 = WIDTH / 3;
|
|
y2 = HEIGHT / 3;
|
|
score2 = 0;
|
|
nTail2 = 0;
|
|
}
|
|
fruitX = rand() % WIDTH;
|
|
fruitY = rand() % HEIGHT;
|
|
score = 0;
|
|
nTail = 0;
|
|
InitializeObstacles();
|
|
}
|
|
|
|
void Draw() {
|
|
gotoxy(0, 0); // ½«¹â±êÒƶ¯µ½¿ØÖÆ̨µÄ×óÉϽÇ
|
|
|
|
for (int i = 0; i < WIDTH + 2; i++)
|
|
cout << "#";
|
|
cout << endl;
|
|
|
|
for (int i = 0; i < HEIGHT; i++) {
|
|
for (int j = 0; j < WIDTH; j++) {
|
|
if (j == 0)
|
|
cout << "#";
|
|
|
|
bool isObstacle = false;
|
|
for (int k = 0; k < OBSTACLE_COUNT; ++k) {
|
|
if (obstacles[k][0] == j && obstacles[k][1] == i) {
|
|
cout << "*";
|
|
isObstacle = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isObstacle) {
|
|
if (i == y && j == x)
|
|
cout << "O";
|
|
else if (twoSnakeMode && i == y2 && j == x2)
|
|
cout << "X";
|
|
else if (i == fruitY && j == fruitX)
|
|
cout << "F";
|
|
else if (i == highScoreFruitY && j == highScoreFruitX && highScoreFruit)
|
|
cout << "H";
|
|
else {
|
|
bool printTail = false;
|
|
for (int k = 0; k < nTail; k++) {
|
|
if (tailX[k] == j && tailY[k] == i) {
|
|
cout << "o";
|
|
printTail = true;
|
|
break;
|
|
}
|
|
}
|
|
if (twoSnakeMode) {
|
|
for (int k = 0; k < nTail2; k++) {
|
|
if (tailX2[k] == j && tailY2[k] == i) {
|
|
cout << "x";
|
|
printTail = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!printTail)
|
|
cout << " ";
|
|
}
|
|
}
|
|
|
|
if (j == WIDTH - 1)
|
|
cout << "#";
|
|
}
|
|
cout << endl;
|
|
}
|
|
|
|
for (int i = 0; i < WIDTH + 2; i++)
|
|
cout << "#";
|
|
cout << endl;
|
|
cout << "Score1: " << score;
|
|
if (twoSnakeMode) cout << " Score2: " << score2;
|
|
cout << endl;
|
|
|
|
if (paused)
|
|
cout << "Paused. Press 'R' to resume." << endl;
|
|
else
|
|
cout << " " << endl;
|
|
}
|
|
|
|
void Input() {
|
|
if (_kbhit()) {
|
|
switch (_getch()) {
|
|
case 'a':
|
|
dir = LEFT;
|
|
break;
|
|
case 'd':
|
|
dir = RIGHT;
|
|
break;
|
|
case 'w':
|
|
dir = UP;
|
|
break;
|
|
case 's':
|
|
dir = DOWN;
|
|
break;
|
|
case 'j':
|
|
if (twoSnakeMode) dir2 = LEFT;
|
|
break;
|
|
case 'l':
|
|
if (twoSnakeMode) dir2 = RIGHT;
|
|
break;
|
|
case 'i':
|
|
if (twoSnakeMode) dir2 = UP;
|
|
break;
|
|
case 'k':
|
|
if (twoSnakeMode) dir2 = DOWN;
|
|
break;
|
|
case 'p':
|
|
paused = !paused;
|
|
break;
|
|
case 'q':
|
|
gameover = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MoveSnake(int& x, int& y, int tailX[], int tailY[], int& nTail, Direction dir) {
|
|
int prevX = tailX[0];
|
|
int prevY = tailY[0];
|
|
int prev2X, prev2Y;
|
|
tailX[0] = x;
|
|
tailY[0] = y;
|
|
|
|
for (int i = 1; i < nTail; i++) {
|
|
prev2X = tailX[i];
|
|
prev2Y = tailY[i];
|
|
tailX[i] = prevX;
|
|
tailY[i] = prevY;
|
|
prevX = prev2X;
|
|
prevY = prev2Y;
|
|
}
|
|
|
|
switch (dir) {
|
|
case LEFT:
|
|
x--;
|
|
break;
|
|
case RIGHT:
|
|
x++;
|
|
break;
|
|
case UP:
|
|
y--;
|
|
break;
|
|
case DOWN:
|
|
y++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (x >= WIDTH) x = 0; else if (x < 0) x = WIDTH - 1;
|
|
if (y >= HEIGHT) y = 1; else if (y <= 0) y = HEIGHT - 1;
|
|
|
|
for (int i = 0; i < nTail; i++)
|
|
if (tailX[i] == x && tailY[i] == y)
|
|
gameover = true;
|
|
|
|
for (int i = 0; i < OBSTACLE_COUNT; ++i) {
|
|
if (x == obstacles[i][0] && y == obstacles[i][1]) {
|
|
gameover = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Algorithm() {
|
|
if (!paused) {
|
|
MoveSnake(x, y, tailX, tailY, nTail, dir);
|
|
if (twoSnakeMode) {
|
|
MoveSnake(x2, y2, tailX2, tailY2, nTail2, dir2);
|
|
}
|
|
|
|
static int timer = 0;
|
|
const int timerThreshold = 50;
|
|
|
|
timer++;
|
|
if (timer == timerThreshold) {
|
|
timer = 0;
|
|
highScoreFruit = true;
|
|
GenerateFruit(highScoreFruitX, highScoreFruitY);
|
|
}
|
|
|
|
if (x == highScoreFruitX && y == highScoreFruitY) {
|
|
score += HIGH_SCORE_FRUIT_SCORE;
|
|
highScoreFruit = false;
|
|
nTail += 2;
|
|
}
|
|
if (twoSnakeMode && x2 == highScoreFruitX && y2 == highScoreFruitY) {
|
|
score2 += HIGH_SCORE_FRUIT_SCORE;
|
|
highScoreFruit = false;
|
|
nTail2 += 2;
|
|
}
|
|
|
|
if (x == fruitX && y == fruitY) {
|
|
score += NORMAL_FRUIT_SCORE;
|
|
nTail++;
|
|
GenerateFruit(fruitX, fruitY);
|
|
}
|
|
if (twoSnakeMode && x2 == fruitX && y2 == fruitY) {
|
|
score2 += NORMAL_FRUIT_SCORE;
|
|
nTail2++;
|
|
GenerateFruit(fruitX, fruitY);
|
|
}
|
|
|
|
if (score >= WIN_SCORE) {
|
|
gameover = true;
|
|
gotoxy(0, HEIGHT + 3);
|
|
Draw();
|
|
cout << "Player 1 wins!" << endl;
|
|
return;
|
|
}
|
|
|
|
if (twoSnakeMode && score2 >= WIN_SCORE) {
|
|
gameover = true;
|
|
gotoxy(0, HEIGHT + 3);
|
|
Draw();
|
|
cout << "Player 2 wins!" << endl;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Index() {
|
|
cout << "Press 1 for single snake mode" << endl;
|
|
cout << "Press 9 for two snakes mode" << endl;
|
|
cout << "Press 2 to exit\nPress p to pause\nPress r to resume\nPress q to quit" << endl;
|
|
cout << "Rules:\nNormal fruit 'F' = 10 scores, increases length by 1;\nHigh-score fruit 'H' = 30 scores, increases length by 2;\nReach 500 scores to win!" << endl;
|
|
}
|
|
|
|
int main() {
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
srand(time(0));
|
|
Index();
|
|
while (true) {
|
|
int flag = 0;
|
|
switch (_getch()) {
|
|
case '1':
|
|
twoSnakeMode = false;
|
|
Setup();
|
|
while (!gameover) {
|
|
Draw();
|
|
Input();
|
|
Algorithm();
|
|
Sleep(100);
|
|
}
|
|
gotoxy(0, WIDTH + 1);
|
|
Index();
|
|
break;
|
|
case '9':
|
|
twoSnakeMode = true;
|
|
Setup();
|
|
while (!gameover) {
|
|
Draw();
|
|
Input();
|
|
Algorithm();
|
|
Sleep(100);
|
|
}
|
|
gotoxy(0, WIDTH + 1);
|
|
Index();
|
|
break;
|
|
case '2':
|
|
flag = 1;
|
|
break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
SetConsoleCursor(true);
|
|
return 0;
|
|
}
|