猜数字 1a2b
2025-5-29 ~ 2025-6-8
(0)
程序简介
猜数字游戏是一种常见的数学游戏,规则很简单,极好上手,它的数学概念主要为排列组合,也因为排列组合的复杂多变性,使得这种游戏虽然简单却不单调,十分耐玩。
程序的美术画面风格是参考名为 Nums 的 app。app 只有标准规则(4 数位、10 数码、不含重复数字),本程序可以调整数位、数码、重复取值。
本程序设置的数位:3~8,数码:4~10。当数位大于数码,重复取值将自动变为是。程序中的 C 键是撤销,M 键是返回设置。点击数字键可以输入数字。不能再使用的数字键会变深色。显示区最多显示最近的 6 次结果,滚轮可以上下翻动查看之前之后的结果。A 前面的数字表示位置正确的数的个数,B 前的数字表示数字正确而位置不对的数的个数(程序的核心就是统计 B 的值)。
允许重复的数码,百度说这种规则的游戏被称为 Mastermind。百度列举的两种常见玩法是标准规则(4 数位、10 数码、不含重复数字)和 Mastermind 规则(4 数位、6 数码、可重复)。还可以试一试(5 数位、10 数码、不含重复数字)和(8 数位、8 数码、可重复)。
有一个拓展设定可供讨论,目前标准规则下密码是不重复,所以只准不重复猜测,即每个数字只准用一次,如果允许重复猜测,即猜的过程用 Mastermind 规则,同位同码时能不能在特定情况下降低标准规则下的难度。另外,Mastermind 规则是允许重复,不是必有重复哈。
程序运行展示
完整源代码
////////////////////////////////////////
// 程序:猜数字
// 作者:Gary
// 编译环境:Visual C++ 2010,EasyX_20211109
// 编写日期:2025.5.19
# include <graphics.h>
# include <string>
# include <time.h>
static HWND hOut; // 画布
// 定义一个类
class Gary
{
public:
void carry(); // 主进程
void initialization(); // 初始化
void move_set(); // 设置界面
void move(); // 游戏界面
void move_again(); // 游戏界面下半区更新
void draw(COLORREF textcolor, COLORREF linecolor, int num_posx, int num_posy, int num_solid, TCHAR s[10],
int num_width, int num_height, int num_line, int box_width, int box_height, int num_edge); // 绘制函数
void draw_set(); // 设置界面绘制函数
void draw_num(); // 游戏界面下半区绘制函数
void draw_small(int n); // 游戏界面上半区绘制函数
int num_size; // 密码长度
int num_type; // 取值范围
int num_repeat; // 取值重复
int num_out[8]; // 密码
int num_in[8]; // 猜测密码
ExMessage m; // 鼠标
int exit_carry; // 主进程参数
int exit_move; // 界面参数
int num_t; // 游戏轮次
};
// 结构体
struct Node1
{
int num_a;
int num_b;
TCHAR num_in[12][2];
int posx[12], posy;
};
struct Node2
{
TCHAR num_space[2];
TCHAR num_keyboard[2];
TCHAR num_button[10];
POINT pot[3];
int posx, posy;
int num_mode;
};
Node1 box_in[20]; // 游戏界面输入区格子
Node2 box_space[8]; // 游戏界面空格区格子
Node2 box_keyboard[12]; // 游戏界面键盘区格子
Node2 box_button[8]; // 设置界面按钮格子
Node2 box_set[7]; // 设置界面调整按钮
// 绘制函数
void Gary::draw(COLORREF textcolor, COLORREF linecolor, int num_posx, int num_posy, int num_solid, TCHAR s[10],
int num_width, int num_height, int num_line, int box_width, int box_height, int num_edge)
{
settextcolor(textcolor);
setlinecolor(linecolor);
setlinestyle(PS_SOLID, num_solid);
settextstyle(num_width, num_height, _T("微软雅黑"), 0, 0, num_line, 0, 0, 0);
outtextxy(num_posx + box_width / 2 - textwidth(s) / 2, num_posy + box_height / 2 - textheight(s) / 2, s);
roundrect(num_posx, num_posy, num_posx + box_width, num_posy + box_height, num_edge, num_edge);
}
// 游戏界面下半区绘制函数
void Gary::draw_num()
{
// 清屏
clearrectangle(15, 280, 180 + 35 * num_size, 595);
int i;
// 绘制
for(i = 0; i < num_size; i++)
{
if(i >= exit_move)
{
draw(RGB(252, 116, 66), WHITE, box_space[i].posx, box_space[i].posy, 1, box_space[i].num_space, 50, 25, 0, 44, 44, 7);
}
else
{
draw(RGB(252, 116, 66), WHITE, box_space[i].posx, box_space[i].posy, 1, box_space[i].num_space, 50, 0, 0, 44, 44, 7);
}
}
for(i = 0; i < 12; i++)
{
if(box_keyboard[i].num_mode == 1)
{
draw(RGB(255, 156, 156), RGB(248, 62, 249), box_keyboard[i].posx, box_keyboard[i].posy, 3, box_keyboard[i].num_keyboard, 50, 0, 0, 50, 50, 15);
}
else
{
draw(RGB(255, 156, 156), RGB(120, 20, 120), box_keyboard[i].posx, box_keyboard[i].posy, 3, box_keyboard[i].num_keyboard, 50, 0, 0, 50, 50, 15);
}
}
// 首次绘制前的上半区
if(num_t == 0)
{
draw(RGB(255, 156, 156), BLACK, box_button[7].posx, box_button[7].posy, 3, box_button[7].num_button, 50, 0, 0, 200, 50, 15);
}
FlushBatchDraw();
}
// 游戏界面上半区绘制函数
void Gary::draw_small(int n)
{
// 清屏
clearrectangle(15, 15, 180 + 35 * num_size, 275);
int i, j, k;
// 绘制
for(k = 0, i = ( n < 6 ? 0 : n - 6 ); i < n; k++, i++)
{
for(j = 0; j < num_size; j++)
{
draw(RGB(252, 116, 66), WHITE, box_in[k].posx[j], box_in[k].posy, 2, box_in[i].num_in[j], 33, 0, 900, 30, 30, 7);
}
for(j = num_size; j < num_size + 2; j++)
{
draw(RGB(20, 254, 0), WHITE, box_in[k].posx[j], box_in[k].posy, 2, box_in[i].num_in[j], 33, 0, 900, 30, 30, 7);
}
for(j = num_size + 2; j < num_size + 4; j++)
{
draw(RGB(252, 0, 0), WHITE, box_in[k].posx[j], box_in[k].posy, 2, box_in[i].num_in[j], 33, 0, 900, 30, 30, 7);
}
}
FlushBatchDraw();
}
// 设置界面绘制函数
void Gary::draw_set()
{
int i;
cleardevice();
// 更新参数
_stprintf_s(box_button[4].num_button, _T("%0d"), num_size);
_stprintf_s(box_button[5].num_button, _T("%0d"), num_type);
if(num_size > num_type) { num_repeat = 1; }
if(num_repeat == 0) { _stprintf_s(box_button[6].num_button, _T("否")); }
else if(num_repeat == 1) { _stprintf_s(box_button[6].num_button, _T("是")); }
// 绘制
for(i = 0; i < 4; i++)
{
draw(RGB(255, 156, 156), BLACK, box_button[i].posx, box_button[i].posy, 3, box_button[i].num_button, 50, 0, 0, 200, 50, 15);
}
for(i = 4; i < 7; i++)
{
draw(RGB(255, 156, 156), RGB(248, 62, 249), box_button[i].posx, box_button[i].posy, 3, box_button[i].num_button, 50, 0, 0, 70, 50, 15);
}
for(i = 0; i < 7; i++)
{
draw(BLACK, WHITE, box_set[i].posx, box_set[i].posy, 3, _T(" "), 50, 0, 0, 50, 50, 7);
fillpolygon(box_set[i].pot, 3);
}
FlushBatchDraw();
}
// 设置进程
void Gary::move_set()
{
int i;
// 设置界面初始化
// 字符
_stprintf_s(box_button[0].num_button, _T("密码长度"));
_stprintf_s(box_button[1].num_button, _T("取值范围"));
_stprintf_s(box_button[2].num_button, _T("重复取值"));
_stprintf_s(box_button[3].num_button, _T("开始游戏"));
_stprintf_s(box_button[4].num_button, _T("%0d"), num_size);
_stprintf_s(box_button[5].num_button, _T("%0d"), num_type);
if(num_repeat == 0) { _stprintf_s(box_button[6].num_button, _T("否")); }
else if(num_repeat == 1) { _stprintf_s(box_button[6].num_button, _T("是")); }
_stprintf_s(box_button[7].num_button, _T("输入数字"));
// 坐标
for(i = 0; i < 4; i++)
{
box_button[i].posx = 35 * num_size / 2;
box_button[i].posy = 35 + 140 * i;
}
for(i = 4; i < 7; i++)
{
box_button[i].posx = 65 + 35 * num_size / 2;
box_button[i].posy = 100 + 140 * ( i - 4 );
}
// 调节按钮
for(i = 0; i < 7; i++)
{
box_set[i].posx = ( i != 6 ? 35 * num_size / 2 + 150 * ( i % 2 ) : 35 * num_size / 2 + 75 );
box_set[i].posy = 100 + 140 * ( i / 2 );
if(i % 2 == 0 && i != 6)
{
box_set[i].pot[0].x = box_set[i].posx + 15; box_set[i].pot[0].y = box_set[i].posy + 25;
box_set[i].pot[1].x = box_set[i].posx + 35; box_set[i].pot[1].y = box_set[i].posy + 10;
box_set[i].pot[2].x = box_set[i].posx + 35; box_set[i].pot[2].y = box_set[i].posy + 40;
}
else
{
box_set[i].pot[0].x = box_set[i].posx + 35; box_set[i].pot[0].y = box_set[i].posy + 25;
box_set[i].pot[1].x = box_set[i].posx + 15; box_set[i].pot[1].y = box_set[i].posy + 10;
box_set[i].pot[2].x = box_set[i].posx + 15; box_set[i].pot[2].y = box_set[i].posy + 40;
}
}
// 绘制
draw_set();
// 进程
exit_move = 0;
while(exit_move == 0)
{
if(peekmessage(&m, EM_MOUSE | EM_KEY))
{
// 左键单击判断
if(m.message == WM_LBUTTONDOWN)
{
for(i = 0; i < 7; i++)
{
if(box_set[i].posx < m.x && m.x < box_set[i].posx + 50 && box_set[i].posy < m.y && m.y < box_set[i].posy + 50)
{
break;
}
}
// 调整参数
switch(i)
{
case 0: {num_size = ( num_size != 3 ? num_size - 1 : 8 ); draw_set(); break; }
case 1: {num_size = ( num_size != 8 ? num_size + 1 : 3 ); draw_set(); break; }
case 2: {num_type = ( num_type != 4 ? num_type - 1 : 10 ); draw_set(); break; }
case 3: {num_type = ( num_type != 10 ? num_type + 1 : 4 ); draw_set(); break; }
case 4: {num_repeat = ( num_repeat == 0 ? 1 : 0 ); draw_set(); break; }
case 5: {num_repeat = ( num_repeat == 0 ? 1 : 0 ); draw_set(); break; }
case 6: {exit_move = 1; break; }
default:break;
}
}
}
}
// 进入游戏界面
cleardevice();
hOut = initgraph(35 * num_size + 200, 610);
SetWindowText(hOut, _T("猜数字"));
}
// 游戏界面空格区进程更新
void Gary::move_again()
{
int j;
for(j = 0; j < num_size; j++)
{
_stprintf_s(box_space[j].num_space, _T("?"));
}
for(j = 0; j < 12; j++)
{
if(box_keyboard[j].num_mode == 2)
{
box_keyboard[j].num_mode = 1;
}
}
// 下半区绘制
draw_num();
}
void Gary::move()
{
// 初始化
int i, j;
int t1; // 上半区绘制参数
int a, b; // 统计参数
int flag_out, flag_in; // 统计参数
// 输入区
for(i = 0; i < 20; i++)
{
box_in[i].posy = 30 + 40 * i;
for(j = 0; j < num_size + 4; j++)
{
box_in[i].posx[j] = 30 + 35 * j + ( j < num_size ? 0 : 5 );
_stprintf_s(box_in[i].num_in[j], _T(" "));
if(j == num_size + 1)
{
_stprintf_s(box_in[i].num_in[j], _T("A"));
}
else if(j == num_size + 3)
{
_stprintf_s(box_in[i].num_in[j], _T("B"));
}
}
}
// 空格区
for(i = 0; i < num_size; i++)
{
box_space[i].posx = 127 - 19 * num_size / 2 + 54 * i - 22;
box_space[i].posy = 312 - 22;
_stprintf_s(box_space[i].num_space, _T("?"));
}
// 键盘区
for(i = 0; i < 12; i++)
{
box_keyboard[i].posx = 35 + ( 40 + 35 * num_size / 2 ) * ( i % 3 );
box_keyboard[i].posy = 350 + 60 * ( i / 3 );
if(i < num_type - 1 || i>8)
{
box_keyboard[i].num_mode = 1;
}
else
{
box_keyboard[i].num_mode = 0;
}
if(i < 9)
{
_stprintf_s(box_keyboard[i].num_keyboard, _T("%0d"), i + 1);
}
switch(i)
{
case 9: {_stprintf_s(box_keyboard[i].num_keyboard, _T("C")); break; }
case 10: {_stprintf_s(box_keyboard[i].num_keyboard, _T("%0d"), 0); break; }
case 11: {_stprintf_s(box_keyboard[i].num_keyboard, _T("M")); break; }
default:break;
}
}
// 首次输入前的输入区
box_button[7].posx = 35 * num_size / 2;
box_button[7].posy = 145 - textheight(box_button[7].num_button) / 2;
// 密码生成
srand((unsigned)time(NULL));
for(i = 0; i < num_size; i++)
{
num_out[i] = rand() % num_type;
j = 0;
while(j < i && num_repeat == 0)
{
if(num_out[j] == num_out[i])
{
num_out[i] = rand() % num_type;
j = -1;
}
j++;
}
}
// 绘制
num_t = 0;
a = 0;
exit_move = 0;
draw_small(num_t);
draw_num();
// 进程
while(a != num_size && num_t < 20)
{
num_t++;
t1 = num_t - 1;
exit_move = 0;
// 猜测
while(exit_move < num_size)
{
if(peekmessage(&m, EM_MOUSE | EM_KEY))
{
// 左键单击判断
if(m.message == WM_LBUTTONDOWN)
{
for(i = 0; i < 12; i++)
{
if(box_keyboard[i].posx < m.x && m.x < box_keyboard[i].posx + 50 && box_keyboard[i].posy < m.y && m.y < box_keyboard[i].posy + 50)
{
break;
}
}
// 数字按钮
if(( i < 9 || i == 10 ) && box_keyboard[i].num_mode == 1)
{
num_in[exit_move] = ( i < 9 ? i + 1 : 0 );
_stprintf_s(box_space[exit_move].num_space, _T("%0d"), num_in[exit_move]);
if(num_repeat == 0) { box_keyboard[i].num_mode = 2; }
exit_move++;
draw_num();
}
// 清除按钮
else if(i == 9)
{
exit_move = 0;
move_again();
}
// 返回按钮
else if(i == 11)
{
num_t = 21; exit_move = num_size; exit_carry = 1;
}
}
// 滚轮
if(m.message == WM_MOUSEWHEEL && num_t > 7)
{
if(30 < m.x && m.x < 35 * num_size + 170 && 30 < m.y && m.y < 260)
{
if(m.wheel < 0 && t1 < num_t - 1)
{
t1++;
}
else if(m.wheel > 0 && t1 > 6)
{
t1--;
}
// 绘制
draw_small(t1);
}
}
}
}
// 输入
if(num_t < 21)
{
Sleep(300);
exit_move = 0;
// 下半区更新
move_again();
// 统计
a = 0; b = 0;
for(i = 0; i < num_type; i++)
{
flag_out = 0;
flag_in = 0;
for(j = 0; j < num_size; j++)
{
// 位置数字都正确
if(num_out[j] == i && num_in[j] == i) { a++; }
// 数字正确位置不正确
else if(num_out[j] == i && num_in[j] != i) { flag_out++; }
else if(num_out[j] != i && num_in[j] == i) { flag_in++; }
}
b += ( flag_out <= flag_in ? flag_out : flag_in );
}
// 输入
for(i = 0; i < num_size; i++)
{
_stprintf_s(box_in[num_t - 1].num_in[i], _T("%0d"), num_in[i]);
}
_stprintf_s(box_in[num_t - 1].num_in[num_size], _T("%0d"), a);
_stprintf_s(box_in[num_t - 1].num_in[num_size + 2], _T("%0d"), b);
// 绘制
draw_small(num_t);
flushmessage();
}
}
// 结束
if(a == num_size)
{
MessageBox(hOut, _T("成功"), _T("来自小豆子的提醒"), MB_OK);
}
else if(num_t == 20)
{
MessageBox(hOut, _T("失败"), _T("来自小豆子的提醒"), MB_OK);
}
}
// 主进程
void Gary::carry()
{
// 初始化
num_size = 4;
num_type = 10;
num_repeat = 0;
// 窗口
hOut = initgraph(35 * num_size + 200, 610);
SetWindowText(hOut, _T("猜数字"));
BeginBatchDraw();
while(1)
{
// 设置界面
move_set();
exit_carry = 0;
while(exit_carry == 0)
{
// 游戏界面
move();
}
}
EndBatchDraw();
closegraph();
}
// 主函数
int main(void)
{
Gary G;
G.carry();
return 0;
}