猜数字 1a2b 银牌收录

程序简介

猜数字游戏是一种常见的数学游戏,规则很简单,极好上手,它的数学概念主要为排列组合,也因为排列组合的复杂多变性,使得这种游戏虽然简单却不单调,十分耐玩。
程序的美术画面风格是参考名为 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;
}