请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
每块拼图分割成上下左右四个部分,要求相邻两块拼图对应的上下或左右部分的数字
颜色一致,边缘不考虑,最后完整拼接。
开发环境 vs2022 easyx
项目->属性->高级->字符集 该成多字节字符集
制作日期 2025年6月8日
技巧 黑色的数字位置是在地图的边缘的。
// 四邻游戏,玩法是数字拼图,
// 每块拼图分割成上下左右四个部分,要求相邻两块拼图对应的上下或左右部分的数字
// 颜色一致,边缘不考虑,最后完整拼接。
// 开发环境 vs2022 easyx
// 项目->属性->高级->字符集 该成多字节字符集
// 制作日期 2025年6月8日
// 技巧 黑色的数字位置是在地图的边缘的。
#include <graphics.h>
#include <conio.h>
#include <time.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int mapsize = 4; //地图尺寸
IMAGE memImage;
// 定义颜色常量
const int COLORS[5] = { BLACK, RED, GREEN, BLUE, LIGHTMAGENTA };
const int COLOR_COUNT = 5;
const int mtop=50,mbuttom=50;//地图四边留白
//自定义按钮类
class Button
{
public:
Button(int a, const char *t) : x(a)
{
// 计算字符串长度
int len = _tcslen(t) 1;
// 分配内存并复制字符串
text = new TCHAR[len];
_tcscpy_s(text, len, t);
}
~Button()
{
// 释放内存
delete[] text;
}
bool isClicked(int mx, int my) {
int y = mtop mapsize * 100 mbuttom/2;
return (mx >= x - 30 && mx <= x 30 && my >= y - 20 && my <= y 20);
}
void mousemove(int mx, int my) {
int y = mtop mapsize * 100 mbuttom/2;
selected= (mx >= x - 30 && mx <= x 30 && my >= y - 20 && my <= y 20);
}
void draw()
{
int y =mtop mapsize * 100 mbuttom/2;
setlinecolor(BLACK);
if (selected) {
setfillcolor(LIGHTGRAY);
settextcolor(RED);
}
else {
setfillcolor(WHITE);
settextcolor(BLUE);
}
fillrectangle(x -30, y - 20, x 30, y 20);
outtextxy(x-20, y-15 , text);
}
public:
int x; // 按钮的X坐标 Y坐标通过计算得出
LPTSTR text; // 按钮文本
bool selected=false; // 是否被选中
};
// 拼图块类
class PuzzlePiece {
public:
int top, right, bottom, left; // 上右下左四个方向的数字颜色索引
int x, y; // 拼图块在屏幕上的位置
int gridX, gridY; // 拼图块在网格中的位置
bool isPlaced; // 是否已经放置在正确位置
bool mousemove = false; // 是否有鼠标划过
public:
PuzzlePiece(int t, int r, int b, int l, int gx, int gy)
: top(t), right(r), bottom(b), left(l), gridX(gx), gridY(gy) {
x = gx * 100 50;
y = gy * 100 50 mtop;
isPlaced = false;
}
// 绘制拼图块
void draw(bool selected = false) {
// 绘制边框
if (selected) {
setfillcolor(LIGHTGREEN);
}
else if (mousemove) {
setfillcolor(LIGHTGRAY);
}
else if (isPlaced) {
setfillcolor(LIGHTCYAN);
}
else {
setfillcolor(WHITE);
}
// 绘制四个方向的数字
setbkmode(TRANSPARENT); // 设置字体背景为透明
settextstyle(24, 0, _T("黑体")); // 设置字体
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 3);
fillrectangle(x - 45, y - 45, x 45, y 45);
// 绘制四个方向的数字
settextcolor(COLORS[top]);
outtextxy(x, y - 30, '0' top 1);
settextcolor(COLORS[right]);
outtextxy(x 30, y, '0' right 1);
settextcolor(COLORS[bottom]);
outtextxy(x, y 20, '0' bottom 1);
settextcolor(COLORS[left]);
outtextxy(x - 30, y, '0' left 1);
}
};
// 游戏类
class PuzzleGame {
private:
PuzzlePiece ** pieces; // 拼图块集合
int rows, cols; // 网格行数和列数
int selectedIndex; // 当前选中的拼图块索引
bool isWin; // 游戏是否结束
int moves; // 移动次数
public:
PuzzleGame(int r, int c) : rows(r), cols(c), selectedIndex(-1), isWin(false), moves(0)
{
initPieces();
shufflePieces();
}
~PuzzleGame(){
for (int i = 0; i < rows * cols; i) {
delete pieces[i]; // 释放每行
}
delete[] pieces;
}
// 初始化拼图块
void initPieces() {
// 创建正确的拼图
pieces = new PuzzlePiece*[rows*cols];
for (int y = 0; y < rows; y ) {
for (int x = 0; x < cols; x ) {
// 计算四个方向的颜色索引
int top = (y == 0) ? 0 : (1 rand() % (COLOR_COUNT - 1)); // -1表示边缘
int right = (x == cols - 1) ? 0 : (1 rand() % (COLOR_COUNT - 1));
int bottom = (y == rows - 1) ? 0 : (1 rand() % (COLOR_COUNT - 1));
int left = (x == 0) ? 0 : (1 rand() % (COLOR_COUNT - 1));
pieces[y * cols x] = new PuzzlePiece(top, right, bottom, left, x, y);
// 确保相邻块颜色匹配
if (x > 0) {
pieces[y * cols x ]->left = pieces[y * cols x - 1]->right;
}
if (y > 0) {
pieces[y* cols x]->top = pieces[(y - 1) * cols x]->bottom;
}
}
}
}
// 打乱拼图块位置
void shufflePieces() {
for (int y = 0; y < rows; y ) {
for (int x = 0; x < cols; x ) {
int r = rand() % (rows*cols);
swapPieces(y*cols x,r);
}
}
}
// 绘制游戏界面
void draw()
{
// 绘制标题
settextcolor(BLACK);
settextstyle(24, 0, _T("宋体"));
outtextxy(10, 10, _T("数字拼图游戏"));
char info[50];
sprintf_s(info, "移动次数: %d", moves);
outtextxy(200, 10,(LPCTSTR)info);
// 绘制所有拼图块
for (size_t i = 0; i < rows*cols; i )
{
pieces[i]->draw(i == selectedIndex);
}
// 游戏结束提示
if (isWin)
{
setfillcolor(RGB(0, 128, 0));
fillrectangle(100, 100, 400, 200);
settextcolor(WHITE);
settextstyle(36, 0, _T("宋体"));
outtextxy(150, 130, _T("恭喜完成!"));
settextstyle(24, 0, _T("黑体"));
}
}
// 鼠标移动到该方块颜色变成灰色
void mousemove(int mx, int my)
{
int gx = mx / 100;
int gy = (my - mtop) / 100;
int gx1 = mx % 100;
int gy1 = (my - mtop) % 100;
// 检查是否点击了拼图块
for (int i = 0; i < rows * cols; i ) pieces[i]->mousemove = false;
if (gx >= 0 && gx < rows && gy >= 0 && gy < cols && gx1 > 5 && gx1 < 95 && gy1>5 && gy1 < 95)
{
int r = gy * cols gx;
pieces[r]->mousemove = true;
}
}
// 处理鼠标点击方块 选中后颜色变成绿色
void handleClick(int mx, int my) {
if (isWin) return;
int gx = mx/ 100;
int gy = (my-mtop)/ 100;
int gx1 = mx %100;
int gy1 = (my - mtop) % 100;
// 检查是否点击了拼图块
if (gx >= 0 && gx < rows && gy >= 0 && gy < cols && gx1>5&&gx1<95 && gy1>5 && gy1 < 95)
{
int r = gy * cols gx;
if (selectedIndex == -1) {
// 如果没有选中的拼图块,选中当前点击的
selectedIndex = r;
}
else if (selectedIndex != (int)r) {
// 如果已经有选中的拼图块,尝试交换位置
swapPieces(selectedIndex, r);
selectedIndex = -1;
moves ;
checkWin();
}
return;
}
else
// 如果点击了空白处,取消选择
selectedIndex = -1;
return;
}
// 交换两个拼图块的位置
void swapPieces(int i, int j)
{
int top = pieces[i]->top ; // -1表示边缘
int right = pieces[i]->right;
int bottom = pieces[i]->bottom;
int left = pieces[i]->left;
pieces[i]->top = pieces[j]->top;
pieces[i]->bottom = pieces[j]->bottom;
pieces[i]->left = pieces[j]->left;
pieces[i]->right = pieces[j]->right;
pieces[j]->top = top;
pieces[j]->bottom = bottom;
pieces[j]->left =left;
pieces[j]->right =right;
}
// 检查拼图块是否放置正确, 正确放置的方块变成蓝色
void checkPiecePlacement(int index) {
PuzzlePiece * piece = pieces[index];
int gx = piece->gridX;
int gy = piece->gridY;
// 检查周围四个方向的拼图块
bool match = true;
// 上方
if (gy > 0) {
if (!(pieces[index-cols]->bottom== pieces[index]->top))
{
match = false;
goto enn;
}
}
// 下方
if (gy < rows - 1)
{
if (!(pieces[index cols]->top == pieces[index]->bottom))
{
match = false;
goto enn;
}
}
// 左方
if (gx > 0) {
if (!(pieces[index -1]->right == pieces[index]->left))
{
match = false;
goto enn;
}
}
// 右方
if (gx < cols - 1) {
if (!(pieces[index 1]->left == pieces[index]->right))
{
match = false;
goto enn;
}
}
enn:
piece->isPlaced = match;
}
// 检查游戏是否获胜
bool checkWin() {
isWin = true;
for (size_t i = 0; i < cols*rows; i ) {
checkPiecePlacement(i);
if (!pieces[i]->isPlaced)
{
isWin = false;
}
}
return isWin;
}
// 重新开始游戏
void restart(int size)
{
for (int i = 0; i < rows*cols; i) {
delete pieces[i]; // 释放每行
}
delete[] pieces;
rows=cols=mapsize = size;
closegraph();
initgraph(mapsize * 100, mapsize * 100 mtop mbuttom);
memImage.Resize(mapsize* 100, mapsize * 100 mtop mbuttom);
selectedIndex = -1;
isWin = false;
moves = 0;
initPieces();
shufflePieces();
checkWin();
}
};
WNDPROC g_OriginWndProc = NULL;
bool gameover=false;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CLOSE) {
gameover = true;
}
else {
LRESULT result = CallWindowProc((WNDPROC)g_OriginWndProc, hwnd, uMsg, wParam, lParam);
return result;
}
}
int main() {
// 初始化随机数种子
srand((unsigned)time(NULL));
// 创建游戏窗口
HWND hwnd=initgraph(mapsize*100, mapsize* 100 mtop mbuttom);
// 创建内存画布,用于双缓冲绘图
memImage.Resize(mapsize * 100, mapsize * 100 mtop mbuttom);
//为了能通过点窗口的X号关闭窗口
g_OriginWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);
// 创建游戏对象 (4x4 拼图)
PuzzleGame game(mapsize, mapsize);
game.checkWin();
Button s4(100, "4X4");
Button s5(200, "5X5");
Button s6(300, "退出");
auto print = [&]()
{
SetWorkingImage(&memImage); // 设置当前绘图设备为内存画布
setbkcolor(RGB(240, 240, 240));
cleardevice(); // 清内存画布
setbkcolor(RGB(240, 240, 240)); // 在内存画布上绘制游戏界面
game.draw(); // 设置当前绘图设备回屏幕
s4.draw();s5.draw(); s6.draw();
SetWorkingImage(); // 将内存画布上的内容一次性复制到屏幕上
putimage(0, 0, &memImage);
Sleep(1); // 延时
};
print();
// 游戏循环
ExMessage msg;
while (!gameover)
{
int i = -1;
// 处理消息
if (peekmessage(&msg, EM_MOUSE | EM_KEY)) {
switch (msg.message) {
case WM_LBUTTONDOWN:
game.handleClick(msg.x, msg.y);
if(s4.isClicked(msg.x, msg.y))game.restart(4);
if(s5.isClicked(msg.x, msg.y))game.restart(5);
if (s6.isClicked(msg.x, msg.y))
{
closegraph();
return 0;
}
print();
break;
case WM_MOUSEMOVE:
game.mousemove(msg.x, msg.y);
s4.mousemove(msg.x, msg.y);
s5.mousemove(msg.x, msg.y);
s6.mousemove(msg.x, msg.y);
print();
break;
case WM_KEYDOWN:
if (msg.vkcode == VK_ESCAPE) {
return 0; // 退出游戏
}
break;
case WM_CLOSE: // 检测到关闭窗口消息
closegraph(); // 关闭图形窗口
return 0; // 退出程序
}
}
}
// 关闭图形窗口
closegraph();
return 0;
}