#include <graphics.h>
#include <conio.h>
#include <vector>
#include <ctime>
#include <string>
#include <sstream>
#include <windows.h>
#include <chrono> // 修复:正确的头文件名
#include <algorithm>
#include <atomic>
using namespace std;
using namespace std::chrono;
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 + 120;
const int COLORS[] = {RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, BROWN, LIGHTBLUE};
const int NUM_COLORS = sizeof(COLORS) / sizeof(COLORS[0]);
// Exit button position and state
const int EXIT_BTN_WIDTH = 120;
const int EXIT_BTN_HEIGHT = 40;
const int EXIT_BTN_X = WINDOW_WIDTH - EXIT_BTN_WIDTH - 20;
const int EXIT_BTN_Y = WINDOW_HEIGHT - EXIT_BTN_HEIGHT - 15;
bool exitBtnHovered = false;
bool exitBtnPressed = false;
vector<vector<int>> grid(ROW, vector<int>(COL));
atomic<int> score(0);
bool gameOver = false;
bool quitGame = false;
int selectedX = -1, selectedY = -1;
steady_clock::time_point lastClickTime = steady_clock::now();
const int MIN_CLICK_INTERVAL = 50;
bool isAnimating = false;
// High-resolution timer
class HighResTimer {
public:
void start() { startTime = steady_clock::now(); }
double elapsed() const {
return duration_cast<duration<double>>(steady_clock::now() - start极Time).count();
}
private:
steady_clock::time_point startTime;
};
// Integer to string conversion
string intToString(int value) {
stringstream ss;
ss << value;
return ss.str();
}
// Draw exit button
void drawExitButton() {
if (exitBtnHovered) {
setfillcolor(LIGHTGRAY);
} else {
setfillcolor(DARKGRAY);
}
// Draw button background
solidrectangle(EXIT_BTN_X, EXIT_BTN_Y,
EXIT_BTN_X + EXIT_BTN_WIDTH,
EXIT_BTN_Y + EXIT_BTN_HEIGHT);
// Draw button border
setlinecolor(WHITE);
rectangle(EXIT_BTN_X, EXIT_BTN_Y,
EXIT_BTN_X + EXIT_BTN_WIDTH,
EXIT_BTN_Y + EXIT_BTN_HEIGHT);
// Draw button text
settextcolor(exitBtnPressed ? RED : WHITE);
settextstyle(24, 0, _T("Arial"));
outtextxy(EXIT_BTN_X + 25, EXIT_BTN_Y + 8, "Exit Game");
}
// Initialize game grid
void initGrid() {
srand(static_cast<unsigned>(time(0)));
// Generate initial grid without matches
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;
}
}
// Check horizontal matches
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;
}
// Check vertical matches
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;
}
}
}
}
// Optimized grid drawing with exit button
void drawGridPartial(int highlightX = -1, int highlightY = -1) {
// Draw only highlighted block if specified
if (highlightX >= 0 && highlightY >= 0) {
int x = highlightX, y = highlightY;
// Clear original position
setfillcolor(BLACK);
solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE);
// Draw new block
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);
}
// Highlight selected block
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;
}
// Full screen redraw
cleardevice();
settextstyle(24, 0, _T("Arial"));
// Draw blocks
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);
// Highlight selected block
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 {
// Draw empty block (transparent)
setfillcolor(BLACK);
setlinecolor(DARKGRAY);
rectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,
(j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
}
}
}
// Draw score panel
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());
// Draw exit button
drawExitButton();
// Game over message
if (gameOver) {
settextcolor(RED);
settextstyle(36, 0, _T("Arial"));
outtextxy(WINDOW_WIDTH/2 - 120, WINDOW_HEIGHT/2 - 30, "Game Over!");
settextstyle(24, 0, _T("Arial"));
string finalScore = "Final Score: " + intToString(score);
outtextxy(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2 + 20, finalScore.c_str());
}
}
// Check for possible moves
bool hasPossibleMoves() {
// Horizontal swap check
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL - 1; ++j) {
// Try swapping
swap(grid[i][j], grid[i][j+1]);
// Check if swap creates a match
bool matchFound = false;
// Check row matches (optimized area)
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;
}
// Check column matches
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;
}
}
// Restore swap
swap(grid[i][j], grid[i][j+1]);
if (matchFound) return true;
}
}
// Vertical swap check
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW - 1; ++i) {
// Try swapping
swap(grid[i][j], grid[i+1][j]);
// Check if swap creates a match
bool matchFound = false;
// Check row matches
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;
}
// Check column matches
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;
}
}
// Restore swap
swap(grid[i][j], grid[i+1][j]);
if (matchFound) return true;
}
}
return false;
}
// Process matches and update score
int processMatches() {
vector<vector<bool>> toRemove(ROW, vector<bool>(COL, false));
int matchCount = 0;
// Horizontal match check
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; // Skip checked positions
} else {
j++;
}
}
}
// Vertical match check
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; // Skip checked positions
} else {
i++;
}
}
}
// Update score
if (matchCount > 0) {
static int comboCount = 0;
comboCount++;
int comboBonus = max(0, comboCount - 1) * 5;
score += matchCount * 10 + comboBonus;
} else {
// Reset combo count
static int comboCount = 0;
comboCount = 0;
}
// Remove matched blocks
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (toRemove[i][j]) {
grid[i][j] = -1;
}
}
}
return matchCount;
}
// Block falling animation
void dropBlocksAnimated() {
HighResTimer timer;
timer.start();
// Track new positions for each column
vector<vector<int>> newPositions(COL);
vector<vector<int>> temp(COL);
// Prepare falling data
for (int j = 0; j < COL; ++j) {
// Collect non-empty blocks (bottom to top)
for (int i = ROW - 1; i >= 0; --i) {
if (grid[i][j] != -1) {
temp[j].push_back(grid[i][j]);
}
}
// Generate new blocks
int newBlocks = ROW - temp[j].size();
for (int i = 0; i < newBlocks; ++i) {
temp[j].push_back(rand() % NUM_COLORS);
}
// Calculate new positions
for (int i = 0; i < ROW; ++i) {
newPositions[j].push_back(temp[j][ROW - 1 - i]);
}
}
// Animation duration (seconds)
const double animationDuration = 0.3;
vector<vector<int>> originalGrid = grid; // Save original state
while (true) {
double elapsed = timer.elapsed();
if (elapsed >= animationDuration) break;
double progress = min(1.0, elapsed / animationDuration);
// Update animation frame
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW; ++i) {
// If block needs to fall
if (originalGrid[i][j] != -1 && newPositions[j][i] != originalGrid[i][j]) {
// Calculate falling distance
int targetY = i;
while (targetY < ROW && grid[targetY][j] != newPositions[j][i]) {
targetY++;
}
if (targetY < ROW) {
// Calculate current position
double currentY = i + (targetY - i) * progress;
// Draw falling block
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);
}
// Apply final state
for (int j = 0; j < COL; ++j) {
for (int i = 0; i < ROW; ++i) {
grid[i][j] = newPositions[j][i];
}
}
}
// Handle mouse clicks (including exit button)
void handleMouseClick() {
// Ignore input during animation
if (isAnimating) return;
if (MouseHit()) {
MOUSEMSG msg = GetMouseMsg();
// Check click interval
auto now = steady_clock::now();
auto elapsed = duration_cast<milliseconds>(now - lastClickTime).count();
if (elapsed < MIN_CLICK_INTERVAL) {
return;
}
lastClickTime = now;
// Check if mouse is over exit button
bool prevHovered = exitBtnHovered;
exitBtnHovered = (msg.x >= EXIT_BTN_X && msg.x <= EXIT_BTN_X + EXIT_BTN_WIDTH &&
msg.y >= EXIT_BTN_Y && msg.y <= EXIT_BTN_Y + EXIT_BTN_HEIGHT);
// Redraw button if hover state changed
if (exitBtnHovered != prevHovered) {
drawExitButton();
FlushBatchDraw();
}
// Handle exit button click
if (msg.uMsg == WM_LBUTTONDOWN && exitBtnHovered) {
exitBtnPressed = true;
drawExitButton();
FlushBatchDraw();
}
else if (msg.uMsg == WM_LBUTTONUP && exitBtnPressed) {
exitBtnPressed = false;
if (exitBtnHovered) {
// Quit game
quitGame = true;
return;
}
drawExitButton();
FlushBatchDraw();
}
// Handle grid clicks
if (msg.uMsg == WM_LBUTTONDOWN) {
int x = msg.x / BLOCK_SIZE;
int y = msg.y / BLOCK_SIZE;
// Ensure click is within grid
if (x >= 0 && x < COL && y >= 0 && y < ROW && grid[y][x] != -1) {
// First click or change selection
if (selectedX == -1) {
selectedX = x;
selectedY = y;
drawGridPartial(x, y); // Partial redraw
FlushBatchDraw();
}
// Click same block - deselect
else if (selectedX == x && selectedY == y) {
selectedX = -1;
selectedY = -1;
drawGridPartial(x, y); // Partial redraw
FlushBatchDraw();
}
// Second click (swap)
else if ((abs(selectedX - x) == 1 && selectedY == y) ||
(abs(selectedY - y) == 1 && selectedX == x)) {
// Set animation state
isAnimating = true;
// Swap animation
int prevX = selectedX, prevY = selectedY;
// Draw swap animation
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);
// Calculate intermediate position
int offsetX = static_cast<int>((x - prevX) * progress * BLOCK_SIZE);
int offsetY = static_cast<int>((y - prevY) * progress * BLOCK_SIZE);
// Clear original position
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);
// Draw moving blocks
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);
}
// Actually swap blocks
swap(grid[prevY][prevX], grid[y][x]);
// Check if match is formed
bool matched = false;
if (processMatches() > 0) {
matched = true;
}
if (!matched) {
// Revert swap if no match
swap(grid[prevY][prevX], grid[y][x]);
drawGridPartial(prevX, prevY);
drawGridPartial(x, y);
FlushBatchDraw();
} else {
// Process chain reactions
while (true) {
// Drop blocks with animation
dropBlocksAnimated();
// Check for new matches
if (processMatches() == 0) break;
}
// Check if game is over
gameOver = !hasPossibleMoves();
}
// Reset selection
selectedX = selectedY = -1;
// End animation
isAnimating = false;
}
// Click different position, change selection
else {
// Clear old selection
int oldX = selectedX, oldY = selectedY;
selectedX = x;
selectedY = y;
// Partial redraw
if (oldX >= 0 && oldY >= 0) {
drawGridPartial(oldX, oldY);
}
drawGridPartial(x, y);
FlushBatchDraw();
}
}
// Click outside grid, reset selection
else if (selectedX >= 0 || selectedY >= 0) {
int oldX = selectedX, oldY = selectedY;
selectedX = selectedY = -1;
// Partial redraw
if (oldX >= 0 && oldY >= 0) {
drawGridPartial(oldX, oldY);
}
FlushBatchDraw();
}
}
}
}
int main() {
initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
initGrid();
BeginBatchDraw();
// High-precision timer
HighResTimer frameTimer;
const double targetFPS = 120.0;
const double frameTime = 1.0 / targetFPS;
while (!gameOver && !quitGame) {
frameTimer.start();
if (!isAnimating) {
drawGridPartial(); // Partial redraw mode
}
handleMouseClick();
FlushBatchDraw();
// Precise frame rate control
double elapsed = frameTimer.elapsed();
if (elapsed < frameTime) {
int sleepTime = static_cast<int>((frameTime - elapsed) * 1000);
if (sleepTime > 0) {
Sleep(sleepTime);
}
}
}
// Game over state
if (!quitGame) {
drawGridPartial();
FlushBatchDraw();
// Wait for key press to exit
while (!_kbhit() && !quitGame) {
Sleep(100);
}
}
EndBatchDraw();
closegraph();
return 0;
}
最新发布