【创作灵感】
看到原神BWiki里有抽卡模拟器,便自己根据游戏内的描述用C语言编写了一个简单的模拟器,供参考使用。
作者注:
普通C语言编程是没有图形化界面的,只看见原神两个字就点进来的请不要随便喷。因为三星、四星物品实在过多,对于这两种星级,程序只分4星角色、4星物品、3星这三类,不具体到名称,不然会导致程序代码大大增长且无实际意义。免费下载链接:C语言:原神角色UP池祈愿模拟器
目录
一、背景与描述
二、模块及介绍
1.祈愿基础概率的模拟
2.祈愿保底的模拟
3.历史记录的书写
4.历史记录页面实现翻页
5.修改已经抽取的数量
三、运行与展示
1.运行主界面
2.单抽
3.十连抽
4.查看历史纪录
5.修改垫抽数量
6.1 完整运行(单抽)
6.2 完整运行(十连抽)
四、代码与注释
一、背景与描述
原神BWiki里的抽卡模拟器:原神BWiki模拟器传送门
以4.2版本下半期UP祈愿-1为例:
![](https://img-blog.csdnimg.cn/direct/d4964be99f5d469eaba44bc72ca17461.jpeg)
对于这期UP池,我们有以下五星列表:
![](https://img-blog.csdnimg.cn/direct/cd450e398e40442a8f7e3def7ca895bf.png)
同时我们还要注意到以下这句话:
![](https://img-blog.csdnimg.cn/direct/6dcf7e38212c4c27bc8de12362213377.png)
二、模块及介绍
1.祈愿基础概率的模拟
从上面图中我们可以得到小数点位数最多的就是概率为0.0255的4星武器和4星角色,也就是255/10000,为便于理解与阅读,这里我们不进行约分。
我们可以利用1-10000的一万个数字作为随机数生成的范围,让1-60为5星角色,61-315为4星角色(或物品),316-570为4星物品(或角色),剩下的就是3星物品与角色。这样处理就行了。
简单点描述就是:
srand(unsigned int)time(NULL));
int NumberCode = rand()%10000 + 1;
if (NumberCode >=1 && NumberCode =61 && NumberCode =316 && NumberCode 0; i--)
strcpy(History[i], History[i - 1]);
strcpy(History[0], "这里是新的抽卡记录(一条)");
4.历史记录页面实现翻页
为了模仿游戏中的一页五条历史记录,翻页的写法是不可避免的。
首先,根据上面的写法,我们知道History[0]是最近的历史记录,History[N - 1]则为最久的历史记录。所以我们第一页无疑需要从上到下输出History[0]到History[4],以此类推每一页的输出就是:
int page;//由用户决定大小
for (int i = 0;i < 5; i++)
printf("%s\n",History[(page - 1)*5 + i]);
5.修改已经抽取的数量
这个功能是方便大家根据自己的已垫次数进行模拟。其实只用把SmokedNumber更改就行了。
三、运行与展示
1.运行主界面
![](https://img-blog.csdnimg.cn/direct/5613b89706f348c4bb85f081b00f3b4a.png)
2.单抽
![](https://img-blog.csdnimg.cn/direct/8f0c0506e28e43538c1f66195fb26c79.png)
3.十连抽
![](https://img-blog.csdnimg.cn/direct/869422447a1c4db3892b486b37f4ce0e.png)
4.查看历史纪录
![](https://img-blog.csdnimg.cn/direct/f0610bc70d4247e497adab49d3ec6901.png)
5.修改垫抽数量
![](https://img-blog.csdnimg.cn/direct/1b7b895bbafb486b9d86244e664be516.png)
6.1 完整运行(单抽)
受限于gif图时间限制,下图为2.5倍速
![](https://img-blog.csdnimg.cn/direct/2d002c61fede4d3fadef598f2a32d17c.gif)
6.2 完整运行(十连抽)
受限于gif图时间限制,下图为2.5倍速
![](https://img-blog.csdnimg.cn/direct/cfd485186b40458a8e167b4d29c2a9d9.gif)
四、代码与注释
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include "unistd.h"
///注意,该项目规定输出区域为第三行以后(不包括第三行),前两行用于常驻简介与目前大保底的抽数
#define TimeInterval 25 //预定的动画时间间隔
#define ChangeColor_Purple printf("\033[35m")//将颜色换成紫色
#define ChangeColor_Green_ printf("\033[32m")//将颜色换成绿色
#define ChangeColor_Red___ printf("\033[31m")//将颜色换成红色
#define ChangeColor_Yellow printf("\033[33m")//将颜色换成黄色
#define ChangeColor_Whilt_ printf("\033[0m") //将颜色换成白色
#define ChangeColor_Blue__ printf("\033[36m")//将颜色换成蓝色
#define MoveTheLocation printf("\033[%d;%dH",4,0)//移动光标保证输出位置一样
#define MoveToChangeTheNumber printf("\033[%d;%dH",2,NumberOutput_X)//移动光标保证输出位置一样
#define HideCursor printf("\033[?25l")//隐藏光标
#define ShowCursor printf("\033[?25h")//隐藏光标
int Number_Big = 0;//分别记录目前大保底抽了多少抽,90就是小保底,180是大保底
int N_History = 0;//记录总共可查询的历史记录数量(不一定等于Number_Big)
char UPer_5_Stars[25] = "Furina"; //五星Up角色的名称,此处为芙宁娜
char FiveStars[7][25] = {"Tighnari", "Dehya", "Keqing", "Mona", "Qiqi", "Diluc", "Jean"};
//分别对应提纳里、迪希雅、刻晴、莫娜、七七、迪卢克、琴
int NumberOutput_X;
char History[500][100];//最多可查询500条历史记录
struct InformationForTenPrayer {//用于储存十连抽的数据,便于排序显示
int StarNumber;
char Name[25];
};
void CleanTheScreen(void);
void InputTheOperate();
void Welcome();
void Prayer(char Oper);
void Output(int n, int Cod[10]);
void EditTheSmokedNumber();
void EditShowedSmokedNumber();
void SearchTheHistory();
void History_CleanThePanel();
int main(void)
{
NumberOutput_X = 1 + strlen(UPer_5_Stars) + 16 + 16 + 14;
Welcome();//输出欢迎页面与顶部栏
char CharToContinue;
do {
InputTheOperate();
ChangeColor_Purple; printf("\n>");
ChangeColor_Green_; printf("Operation completed.Press [Enter] to continue...");//提示用户操作完毕
ChangeColor_Whilt_;
CharToContinue = getchar();
do { CharToContinue = getchar(); } while (CharToContinue != '\n');//持续读取直到输入回车
} while (1);
return 0;
}
void InputTheOperate()
{//用于简单介绍如何操作并要求用户输入操作
///总共A、B、C、D三种操作,分别对应单抽、十连抽、查看历史记录、修改已垫抽数量
CleanTheScreen();
ChangeColor_Whilt_; Sleep(25); printf("\n");
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃ Please input the Operate you want: ┃\n");
printf("┃ A.Prayer B.Prayer * 10 ┃\n");
printf("┃ C.History D.Edit Smoked Number ┃\n");
printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
printf("┃ Personal blog homepage:\033[36msherrychou.blog.csdn.net \033[0m┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
ChangeColor_Purple; printf(">");
ChangeColor_Green_; printf("Please input A,B,C or D:");
ChangeColor_Whilt_;
char Operate = ' ';//储存用户需要进行的操作
do {
scanf("%c", &Operate);
if (!(Operate == 'A' || Operate == 'B' || Operate == 'C' || Operate == 'D'))
{
HideCursor; printf("\033[%d;%dH", 12, 26);
ChangeColor_Whilt_; printf(" ");
ShowCursor; printf("\033[%d;%dH", 12, 26);
}
} while (!(Operate == 'A' || Operate == 'B' || Operate == 'C' || Operate == 'D'));
//持续读取直到输入A或B或C或D
switch (Operate)
{
case 'A'://单抽
case 'B'://十连抽
Prayer(Operate);//并列
break;
case 'C'://查看历史记录
SearchTheHistory();
break;
case 'D'://修改垫抽次数
EditTheSmokedNumber();
break;
}
return;
}
void Prayer(char Oper)
{//进行祈愿并输出的函数
srand((unsigned int)time(NULL));
switch (Oper)
{
case 'A'://进行单抽
{
Number_Big++;
N_History++;
int CodeNumber = rand() % 10000 + 1;
/*对获得物品进行分段
*1-60为5星角色(0.6%)
*61-315为4星角色(2.55%)
*316-570为4星物品(2.55%)
*剩下的全是3星
*/
int Temp_1[1];
if (Number_Big % 10 != 0)//没有保底的情况
{
if (CodeNumber >= 1 && CodeNumber = 61 && CodeNumber = 316 && CodeNumber = 1 && CodeNumber = 61 && CodeNumber = 316 && CodeNumber 0; i--)
strcpy(History[i], History[i - 1]);//把历史记录往后推
sprintf(History[0], "\033[33m*****\t%s", UPer_5_Stars);
strcpy(pIFTP->Name, UPer_5_Stars);
pIFTP->StarNumber = 5;
break;
case 1:
srand((unsigned int)time(NULL));
int FiveStarsNotUPer_Code = rand() % 7;
for (int i = n_History - 1; i > 0; i--)
strcpy(History[i], History[i - 1]);//把历史记录往后推
sprintf(History[0], "\033[33m*****\t%s", FiveStars[FiveStarsNotUPer_Code]);
strcpy(pIFTP->Name, FiveStars[FiveStarsNotUPer_Code]);
pIFTP->StarNumber = 5;
break;
case 2:
strcpy(pIFTP->Name, "4 stars role");
for (int i = n_History - 1; i > 0; i--)
strcpy(History[i], History[i - 1]);//把历史记录往后推
sprintf(History[0], "\033[35m****\t%s", pIFTP->Name);
pIFTP -> StarNumber= 4;
break;
case 3:
strcpy(pIFTP->Name, "4 stars object");
for (int i = n_History - 1; i > 0; i--)
strcpy(History[i], History[i - 1]);//把历史记录往后推
sprintf(History[0], "\033[35m****\t%s", pIFTP->Name);
pIFTP->StarNumber = 4;
break;
case 4:
strcpy(pIFTP->Name, "3 stars object/role");
for (int i = n_History - 1; i > 0; i--)
strcpy(History[i], History[i - 1]);//把历史记录往后推
sprintf(History[0], "\033[36m***\t%s", pIFTP->Name);
pIFTP->StarNumber = 3;
break;
}
}//到这里按顺序写完了历史记录
//接下来根据抽到的星数进行降序排序,符合原神实际十连抽的最终页面
struct InformationForTenPrayer Temp_Swap;//交换的中间值
for (int i=0;iName);
Sleep(100);
ChangeColor_Whilt_;
}
break;
}
}
ShowCursor;
return;
}
void EditTheSmokedNumber()
{//修改已垫抽次数的函数
SmokedNumberInputWrong://如果输入的不是用户需要修改的,或者不符合要求,则跳回到这里
CleanTheScreen();
ChangeColor_Purple; printf(">");
ChangeColor_Green_; printf("Please input the Smoked Number:");
ChangeColor_Whilt_;
while (scanf("%d", &Number_Big) == EOF) { }//如果读取失败(比如输入的不是数字)就一直循环
if (Number_Big >= 0 && Number_Big < 180)
{
ChangeColor_Purple; printf(">");
ChangeColor_Green_; printf("Do you want to change the Smoked Number to %d ?(Y/N)", Number_Big);
ChangeColor_Whilt_;
char CharToNext = getchar();
do { CharToNext = getchar(); } while (!(CharToNext == 'Y' || CharToNext == 'N'));//持续读取直到读取到Y或N
if (CharToNext == 'N') goto SmokedNumberInputWrong;
else {
EditShowedSmokedNumber();
CleanTheScreen();
}
}
else {//数据范围不符合要求
ChangeColor_Purple; printf(">");
ChangeColor_Red___; printf("The number is not allowed.\n");
ChangeColor_Purple; printf(">");
ChangeColor_Red___; printf("You should input between 0 to 179.Please try again.\n");
ChangeColor_Purple; printf("\n>");
ChangeColor_Green_; printf("Press [Enter] to continue...");
char charToContinue = getchar();
do { charToContinue = getchar(); } while (charToContinue != '\n');//持续读取直到输入回车
goto SmokedNumberInputWrong;
}
return;
}
void EditShowedSmokedNumber()
{//修改显示的已抽取数量的函数
MoveToChangeTheNumber;printf(" ");
MoveToChangeTheNumber;
ChangeColor_Yellow; printf("%d", Number_Big);
ChangeColor_Whilt_;
MoveTheLocation;
return;
}
void CleanTheScreen(void)
{//清空屏幕输出区域的函数
HideCursor;
MoveTheLocation;
for (int i = 1; i 1)
{
page--;
StartLine = 6;
History_CleanThePanel();
for (; StartLine = 0)
{
printf("\t%s", History[5 * (page - 1) + (StartLine - 6)]);
}
}
printf("\033[%d;%dH", 11, 22);//定位
ChangeColor_Whilt_; printf("%d", page);
}
printf("\033[%d;%dH", 15, 17);
printf(" ");
printf("\033[%d;%dH", 15, 17);
ShowCursor;//移动并显示光标
break;
case 'B'://翻到下一页
HideCursor;
if (page |