#include <graphics.h>
#include <conio.h>
#include <vector>
#include <ctime>
#include <string>
#include <sstream>
#include <windows.h>
#include <algorithm>
#include <atomic>
#include <thread>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
using namespace std;
const int ROW = 8;
const int COL = 10;
const int BLOCK_SIZE = 80;
const int WINDOW_WIDTH = COL * BLOCK_SIZE;
const int WINDOW_HEIGHT = ROW * BLOCK_SIZE + 150;
const int COLORS[] = {RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, BROWN, LIGHTBLUE};
const int NUM_COLORS = sizeof(COLORS) / sizeof(COLORS[0]);
vector<vector<int>> grid(ROW, vector<int>(COL));
atomic<int> score(0);
atomic<bool> exitFlag(false);
bool gameOver = false;
int selectedX = -1, selectedY = -1;
long long lastClickTime = 0;
const int MIN_CLICK_INTERVAL = 50;
bool isAnimating = false;
enum GameState { READY, PLAYING, TIME_UP };
GameState gameState = READY;
const int GAME_DURATION = 60;
int remainingTime = GAME_DURATION;
long long gameStartTime = 0;
int finalScore = 0;
bool isMusicPlaying = false;
const char* MUSIC_FILE = "C:\\Users\\27287\\Desktop\\devc\\ma+7\\music.mp3"; // 使用普通字符串路径
class HighResTimer {
public:
void start() {
QueryPerformanceCounter(&startTime);
}
double elapsed() const {
LARGE_INTEGER endTime, frequency;
QueryPerformanceCounter(&endTime);
QueryPerformanceFrequency(&frequency);
return static_cast<double>(endTime.QuadPart - startTime.QuadPart) / frequency.QuadPart;
}
private:
LARGE_INTEGER startTime;
};
void playBackgroundMusic() {
string openCmd = "open \"" + string(MUSIC_FILE) + "\" type mpegvideo alias bgmusic";
mciSendString(openCmd.c_str(), NULL, 0, NULL);
mciSendString("setaudio bgmusic volume to 500", NULL, 0, NULL);
mciSendString("play bgmusic repeat", NULL, 0, NULL);
isMusicPlaying = true;
}
void stopBackgroundMusic() {
if (isMusicPlaying) {
mciSendString("stop bgmusic", NULL, 0, NULL);
mciSendString("close bgmusic", NULL, 0, NULL);
isMusicPlaying = false;
}
}
string intToString(int value) {
stringstream ss;
ss << value;
return ss.str();
}
void initGrid() {
srand(static_cast<unsigned>(time(0)));
bool hasMatches = true;
while (hasMatches) {
hasMatches = false;
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
grid[i][j] = rand() % NUM_COLORS;
}
}
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL - 2; ++j) {
if (grid[i][j] == grid[i][j+1] && grid[i][j] == grid[i][j+2]) {
hasMatches = true;
break;
}
}
if (hasMatches) break;
}
if (!hasMatches) {
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW - 2; ++i) {
if (grid[i][j] == grid[i+1][j] && grid[i][j] == grid[i+2][j]) {
hasMatches = true;
break;
}
}
if (hasMatches) break;
}
}
}
}
void resetGame() {
score = 0;
remainingTime = GAME_DURATION;
gameOver = false;
selectedX = -1;
selectedY = -1;
isAnimating = false;
gameState = PLAYING;
// 使用QueryPerformanceCounter获取高精度时间
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
gameStartTime = li.QuadPart;
initGrid();
if (!isMusicPlaying) {
playBackgroundMusic();
}
}
void drawGridPartial(int highlightX, int highlightY) {
if (highlightX >= 0 && highlightY >= 0) {
int x = highlightX, y = highlightY;
setfillcolor(BLACK);
solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
if (grid[y][x] != -1) {
setfillcolor(COLORS[grid[y][x]]);
solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
setlinecolor(BLACK);
rectangle(x * BLOCK_SIZE, y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
}
if (x == selectedX && y == selectedY) {
setlinecolor(WHITE);
setlinestyle(PS_SOLID, 3);
rectangle(x * BLOCK_SIZE + 2, y * BLOCK_SIZE + 2,
(x + 1) * BLOCK_SIZE - 2, (y + 1) * BLOCK_SIZE - 2);
setlinestyle(PS_SOLID, 1);
}
return;
}
cleardevice();
settextstyle(24, 0, "Arial");
if (gameState == READY) {
settextcolor(WHITE);
settextstyle(36, 0, "Arial");
outtextxy(WINDOW_WIDTH/2 - 150, WINDOW_HEIGHT/2 - 60, "Block Game");
settextstyle(24, 0, "Arial");
outtextxy(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2, "Click to Start");
outtextxy(20, WINDOW_HEIGHT - 80, "Rules:");
outtextxy(20, WINDOW_HEIGHT - 50, "1. Swap adjacent blocks");
outtextxy(20, WINDOW_HEIGHT - 20, "2. Match 3+ to eliminate");
setfillcolor(isMusicPlaying ? GREEN : RED);
solidrectangle(WINDOW_WIDTH - 120, 20, WINDOW_WIDTH - 20, 60);
setlinecolor(WHITE);
rectangle(WINDOW_WIDTH - 120, 20, WINDOW_WIDTH - 20, 60);
settextcolor(WHITE);
outtextxy(WINDOW_WIDTH - 110, 30, isMusicPlaying ? "Music:ON" : "Music:OFF");
return;
}
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if(grid[i][j] != -1) {
setfillcolor(COLORS[grid[i][j]]);
solidrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,
(j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
setlinecolor(BLACK);
rectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,
(j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
if (i == selectedY && j == selectedX) {
setlinecolor(WHITE);
setlinestyle(PS_SOLID, 3);
rectangle(j * BLOCK_SIZE + 2, i * BLOCK_SIZE + 2,
(j + 1) * BLOCK_SIZE - 2, (i + 1) * BLOCK_SIZE - 2);
setlinestyle(PS_SOLID, 1);
}
} else {
setfillcolor(BLACK);
setlinecolor(DARKGRAY);
rectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,
(j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}
}
setfillcolor(DARKGRAY);
solidrectangle(0, ROW * BLOCK_SIZE, WINDOW_WIDTH, WINDOW_HEIGHT);
settextcolor(WHITE);
string scoreStr = "Score: " + intToString(score);
outtextxy(20, ROW * BLOCK_SIZE + 20, scoreStr.c_str());
string timeStr = "Time: " + intToString(remainingTime) + "s";
outtextxy(WINDOW_WIDTH/2 - 80, ROW * BLOCK_SIZE + 20, timeStr.c_str());
setfillcolor(isMusicPlaying ? GREEN : RED);
solidrectangle(20, ROW * BLOCK_SIZE + 10, 120, ROW * BLOCK_SIZE + 40);
setlinecolor(WHITE);
rectangle(20, ROW * BLOCK_SIZE + 10, 120, ROW * BLOCK_SIZE + 40);
settextcolor(WHITE);
outtextxy(30, ROW * BLOCK_SIZE + 15, isMusicPlaying ? "Music:ON" : "Music:OFF");
setfillcolor(LIGHTGRAY);
solidrectangle(WINDOW_WIDTH - 120, ROW * BLOCK_SIZE + 10, WINDOW_WIDTH - 20, ROW * BLOCK_SIZE + 40);
setlinecolor(WHITE);
rectangle(WINDOW_WIDTH - 120, ROW * BLOCK_SIZE + 10, WINDOW_WIDTH - 20, ROW * BLOCK_SIZE + 40);
settextcolor(BLACK);
outtextxy(WINDOW_WIDTH - 110, ROW * BLOCK_SIZE + 15, "Main Menu");
if (gameOver) {
settextcolor(RED);
settextstyle(36, 0, "Arial");
outtextxy(WINDOW_WIDTH/2 - 120, WINDOW_HEIGHT/2 - 30, "Game Over!");
settextstyle(24, 0, "Arial");
string finalScoreStr = "Final: " + intToString(finalScore);
outtextxy(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2 + 20, finalScoreStr.c_str());
}
if (gameState == TIME_UP) {
setfillcolor(0x7F000000);
solidrectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
settextcolor(WHITE);
settextstyle(36, 0, "Arial");
outtextxy(WINDOW_WIDTH/2 - 120, WINDOW_HEIGHT/2 - 60, "Time Up!");
settextstyle(24, 0, "Arial");
string finalScoreStr = "Score: " + intToString(finalScore);
outtextxy(WINDOW_WIDTH/2 - 80, WINDOW_HEIGHT/2, finalScoreStr.c_str());
setfillcolor(GREEN);
solidrectangle(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2 + 50,
WINDOW_WIDTH/2 + 100, WINDOW_HEIGHT/2 + 100);
setlinecolor(WHITE);
rectangle(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2 + 50,
WINDOW_WIDTH/2 + 100, WINDOW_HEIGHT/2 + 100);
settextcolor(WHITE);
outtextxy(WINDOW_WIDTH/2 - 40, WINDOW_HEIGHT/2 + 65, "Restart");
}
}
bool hasPossibleMoves() {
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL - 1; ++j) {
swap(grid[i][j], grid[i][j+1]);
bool matchFound = false;
for (int r = max(0, i-1); r <= min(ROW-1, i+1); ++r) {
for (int c = max(0, j-2); c <= min(COL-3, j+2); ++c) {
if (c < 0 || c+2 >= COL) continue;
if (grid[r][c] != -1 &&
grid[r][c] == grid[r][c+1] &&
grid[r][c] == grid[r][c+2]) {
matchFound = true;
break;
}
}
if (matchFound) break;
}
if (!matchFound) {
for (int c = max(0, j-1); c <= min(COL-1, j+1); ++c) {
for (int r = max(0, i-2); r <= min(ROW-3, i+2); ++r) {
if (r < 0 || r+2 >= ROW) continue;
if (grid[r][c] != -1 &&
grid[r][c] == grid[r+1][c] &&
grid[r][c] == grid[r+2][c]) {
matchFound = true;
break;
}
}
if (matchFound) break;
}
}
swap(grid[i][j], grid[i][j+1]);
if (matchFound) return true;
}
}
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW - 1; ++i) {
swap(grid[i][j], grid[i+1][j]);
bool matchFound = false;
for (int r = max(0, i-1); r <= min(ROW-1, i+1); ++r) {
for (int c = max(0, j-2); c <= min(COL-3, j+2); ++c) {
if (c < 0 || c+2 >= COL) continue;
if (grid[r][c] != -1 &&
grid[r][c] == grid[r][c+1] &&
grid[r][c] == grid[r][c+2]) {
matchFound = true;
break;
}
}
if (matchFound) break;
}
if (!matchFound) {
for (int c = max(0, j-1); c <= min(COL-1, j+1); ++c) {
for (int r = max(0, i-2); r <= min(ROW-3, i+2); ++r) {
if (r < 0 || r+2 >= ROW) continue;
if (grid[r][c] != -1 &&
grid[r][c] == grid[r+1][c] &&
grid[r][c] == grid[r+2][c]) {
matchFound = true;
break;
}
}
if (matchFound) break;
}
}
swap(grid[i][j], grid[i+1][j]);
if (matchFound) return true;
}
}
return false;
}
int processMatches() {
vector<vector<bool>> toRemove(ROW, vector<bool>(COL, false));
int matchCount = 0;
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL - 2; ) {
if (grid[i][j] != -1 &&
grid[i][j] == grid[i][j + 1] &&
grid[i][j] == grid[i][j + 2]) {
int k = j + 2;
while (k < COL && grid[i][j] == grid[i][k]) {
k++;
}
for (int pos = j; pos < k; ++pos) {
toRemove[i][pos] = true;
matchCount++;
}
j = k;
} else {
j++;
}
}
}
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW - 2; ) {
if (grid[i][j] != -1 &&
grid[i][j] == grid[i + 1][j] &&
grid[i][j] == grid[i + 2][j]) {
int k = i + 2;
while (k < ROW && grid[i][j] == grid[k][j]) {
k++;
}
for (int pos = i; pos < k; ++pos) {
toRemove[pos][j] = true;
matchCount++;
}
i = k;
} else {
i++;
}
}
}
if (matchCount > 0) {
static int comboCount = 0;
comboCount++;
int comboBonus = max(0, comboCount - 1) * 5;
score += matchCount * 10 + comboBonus;
} else {
static int comboCount = 0;
comboCount = 0;
}
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (toRemove[i][j]) {
grid[i][j] = -1;
}
}
}
return matchCount;
}
void dropBlocksAnimated() {
HighResTimer timer;
timer.start();
vector<vector<int>> newPositions(COL);
vector<vector<int>> temp(COL);
for (int j = 0; j < COL; ++j) {
for (int i = ROW - 1; i >= 0; --i) {
if (grid[i][j] != -1) {
temp[j].push_back(grid[i][j]);
}
}
int newBlocks = ROW - temp[j].size();
for (int i = 0; i < newBlocks; ++i) {
temp[j].push_back(rand() % NUM_COLORS);
}
for (int i = 0; i < ROW; ++i) {
newPositions[j].push_back(temp[j][ROW - 1 - i]);
}
}
const double animationDuration = 0.3;
vector<vector<int>> originalGrid = grid;
while (true) {
double elapsed = timer.elapsed();
if (elapsed >= animationDuration) break;
double progress = min(1.0, elapsed / animationDuration);
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW; ++i) {
if (originalGrid[i][j] != -1 && newPositions[j][i] != originalGrid[i][j]) {
int targetY = i;
while (targetY < ROW && grid[targetY][j] != newPositions[j][i]) {
targetY++;
}
if (targetY < ROW) {
double currentY = i + (targetY - i) * progress;
setfillcolor(COLORS[originalGrid[i][j]]);
solidrectangle(j * BLOCK_SIZE, static_cast<int>(currentY * BLOCK_SIZE),
(j + 1) * BLOCK_SIZE, static_cast<int>((currentY + 1) * BLOCK_SIZE));
setlinecolor(BLACK);
rectangle(j * BLOCK_SIZE, static_cast<int>(currentY * BLOCK_SIZE),
(j + 1) * BLOCK_SIZE, static_cast<int>((currentY + 1) * BLOCK_SIZE));
}
}
}
}
FlushBatchDraw();
Sleep(10);
}
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW; ++i) {
grid[i][j] = newPositions[j][i];
}
}
}
void handleMouseClick() {
if (isAnimating) return;
if (MouseHit()) {
MOUSEMSG msg = GetMouseMsg();
// 使用GetTickCount()代替chrono
long long now = GetTickCount();
if (now - lastClickTime < MIN_CLICK_INTERVAL) {
return;
}
lastClickTime = now;
if (gameState == READY && msg.uMsg == WM_LBUTTONDOWN) {
resetGame();
return;
}
if (gameState == TIME_UP && msg.uMsg == WM_LBUTTONDOWN) {
int x = msg.x;
int y = msg.y;
if (x >= WINDOW_WIDTH/2 - 100 && x <= WINDOW_WIDTH/2 + 100 &&
y >= WINDOW_HEIGHT/2 + 50 && y <= WINDOW_HEIGHT/2 + 100) {
resetGame();
return;
}
}
if (msg.uMsg == WM_LBUTTONDOWN) {
int x = msg.x;
int y = msg.y;
if (x >= 20 && x <= 120 &&
y >= ROW * BLOCK_SIZE + 10 && y <= ROW * BLOCK_SIZE + 40) {
if (isMusicPlaying) {
stopBackgroundMusic();
} else {
playBackgroundMusic();
}
drawGridPartial(-1, -1);
FlushBatchDraw();
return;
}
if (x >= WINDOW_WIDTH - 120 && x <= WINDOW_WIDTH - 20 &&
y >= ROW * BLOCK_SIZE + 10 && y <= ROW * BLOCK_SIZE + 40) {
gameState = READY;
selectedX = -1;
selectedY = -1;
gameOver = false;
drawGridPartial(-1, -1);
FlushBatchDraw();
return;
}
}
if (msg.uMsg == WM_LBUTTONDOWN) {
int x = msg.x / BLOCK_SIZE;
int y = msg.y / BLOCK_SIZE;
if (x >= 0 && x < COL && y >= 0 && y < ROW && grid[y][x] != -1 && gameState == PLAYING) {
if (selectedX == -1) {
selectedX = x;
selectedY = y;
drawGridPartial(x, y);
FlushBatchDraw();
}
else if (selectedX == x && selectedY == y) {
selectedX = -1;
selectedY = -1;
drawGridPartial(x, y);
FlushBatchDraw();
}
else if ((abs(selectedX - x) == 1 && selectedY == y) ||
(abs(selectedY - y) == 1 && selectedX == x)) {
isAnimating = true;
int prevX = selectedX, prevY = selectedY;
HighResTimer swapTimer;
swapTimer.start();
const double swapDuration = 0.2;
while (true) {
double elapsed = swapTimer.elapsed();
if (elapsed >= swapDuration) break;
double progress = min(1.0, elapsed / swapDuration);
int offsetX = static_cast<int>((x - prevX) * progress * BLOCK_SIZE);
int offsetY = static_cast<int>((y - prevY) * progress * BLOCK_SIZE);
setfillcolor(BLACK);
solidrectangle(prevX * BLOCK_SIZE, prevY * BLOCK_SIZE,
(prevX + 1) * BLOCK_SIZE, (prevY + 1) * BLOCK_SIZE);
solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
setfillcolor(COLORS[grid[prevY][prevX]]);
solidrectangle(prevX * BLOCK_SIZE + offsetX, prevY * BLOCK_SIZE + offsetY,
(prevX + 1) * BLOCK_SIZE + offsetX, (prevY + 1) * BLOCK_SIZE + offsetY);
setfillcolor(COLORS[grid[y][x]]);
solidrectangle(x * BLOCK_SIZE - offsetX, y * BLOCK_SIZE - offsetY,
(x + 1) * BLOCK_SIZE - offsetX, (y + 1) * BLOCK_SIZE - offsetY);
FlushBatchDraw();
Sleep(10);
}
swap(grid[prevY][prevX], grid[y][x]);
bool matched = false;
if (processMatches() > 0) {
matched = true;
}
if (!matched) {
swap(grid[prevY][prevX], grid[y][x]);
drawGridPartial(prevX, prevY);
drawGridPartial(x, y);
FlushBatchDraw();
} else {
while (true) {
dropBlocksAnimated();
if (processMatches() == 0) break;
}
gameOver = !hasPossibleMoves();
}
selectedX = selectedY = -1;
isAnimating = false;
}
else {
int oldX = selectedX, oldY = selectedY;
selectedX = x;
selectedY = y;
if (oldX >= 0 && oldY >= 0) {
drawGridPartial(oldX, oldY);
}
drawGridPartial(x, y);
FlushBatchDraw();
}
}
else if (selectedX >= 0 || selectedY >= 0) {
int oldX = selectedX, oldY = selectedY;
selectedX = selectedY = -1;
if (oldX >= 0 && oldY >= 0) {
drawGridPartial(oldX, oldY);
}
FlushBatchDraw();
}
}
}
}
void keyListenerThread() {
while (!exitFlag) {
if (_kbhit()) {
char ch = _getch();
if (ch == 27) {
exitFlag = true;
break;
}
}
Sleep(50);
}
}
int main() {
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
initGrid();
playBackgroundMusic();
BeginBatchDraw();
thread keyThread(keyListenerThread);
HighResTimer frameTimer;
const double targetFPS = 120.0;
const double frameTime = 1.0 / targetFPS;
while (!exitFlag) {
frameTimer.start();
if (gameState == PLAYING) {
// 使用QueryPerformanceCounter计算剩余时间
LARGE_INTEGER now, frequency;
QueryPerformanceCounter(&now);
QueryPerformanceFrequency(&frequency);
double elapsedSeconds = static_cast<double>(now.QuadPart - gameStartTime) / frequency.QuadPart;
remainingTime = max(0, GAME_DURATION - static_cast<int>(elapsedSeconds));
if (remainingTime == 0) {
gameState = TIME_UP;
finalScore = score;
}
if (gameOver) {
finalScore = score;
}
}
drawGridPartial(-1, -1);
handleMouseClick();
FlushBatchDraw();
double elapsed = frameTimer.elapsed();
if (elapsed < frameTime) {
int sleepTime = static_cast<int>((frameTime - elapsed) * 1000);
if (sleepTime > 0) {
Sleep(sleepTime);
}
}
}
exitFlag = true;
if (keyThread.joinable()) {
keyThread.join();
}
stopBackgroundMusic();
EndBatchDraw();
closegraph();
return 0;
}把页面所展示的英文转换为中文,便于理解