基于学生管理系统修改得来的投资小游戏(哈夫曼加密存档)

代码隔太久了,重复存档有bug,太久了我也忘了当时怎么写的不改了。我也不知道为啥用的都是.h文件,太不成熟了,当时刚了解到.h可以自己编写之后,练手的时候就这样写了可能。

直接上代码

main.cpp

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
#define MAX_NAME_LENTH 40

#include "coding.h"
#include "decode.h"
#include "getnum.h"
#include "display.h"
#include "deal.h"
#include "read_write.h"

int main() {
R:
	int c = N;
	HuTree n[2 * N - 1];
	int x;
	char choose = 0;
	for (x = 0; x < c; x++)
		n[x].weight = 0;
	int m = 0;
	int* _m_ = &m;
	int daysnum = 1;//从第一天开始,后期随天数调整难度
	double deposit = 500000;//现金持有额
	double amount_inv = 0;//资金持有额
	double level = 0;//等级
	double objection = 100;//初始目标
	double gap = 0;	//难度选择,选择差距多大视作失败
	printf("\n\n\t\t\t\t\t投资小游戏\n\n");
	//提供一个选择界面
	printf("\n\t\t\t1.新建游戏\n\n\t\t\t2.读取存档\n\n\t\t\t3.成就系统(待做)\n\n\t\t\t");
	printf("注意:\n\n\t\t\t* 请等待至鼠标指针不带加载圆圈动画");
	printf("\n\n\t\t\t* 移动、修改存档会产生读档失败问题;退出游戏时会自动存档;进入读档之后立刻存档也会出错");
	printf("\n\n\t\t\t* 存档时处在第x天,进入存档时进入x+1天,同时刷新当前价");
	char ch[] = "archive.lj";//地址用的
	printf("\n\n\t\t\t* 存档存放位置:同目录下的名为%s的文件(您如果不需要这次存档,可以选择删除)", ch);
	printf("\n\n\t\t\t* 项目地址:https://github.com/DreamSpoonBird/InvestGame\n\n");
	do {
		printf("\t\t\t您选择:");
			scanf_s("%d", &choose);
			//新建游戏
			if (choose == 1) {
				//清空界面
				system("cls");
				printf("\n\n\n\t\t\t选择你的游戏难度:\n\t\t\t1.简单(与目标差距为20w视为失败)\n\t\t\t2.困难(与目标差距为10w视为失败)\n\t\t\t3.地狱(和目标差距为5k视为失败)\n");
				printf("\n\t\t\t您选择:");
				scanf_s("%lf", &gap);//难度选择
				Init(inv, m);//结构体数组初始化
			}
			else if (choose == 2)//读存档
			{
				//解密
				int flag = UnCompress(n, c);//解压缩文件操作
				//读取存档(包括现金额投资额)
				if (flag != 0) {
					//清空界面
					system("cls");
					goto R;
				}
				read_lijie(inv, _m_, deposit, amount_inv, daysnum, level, objection, gap);
				//删除明文文件
				remove("archive.txt");
			}
			else if (choose == 3) {
				//成就系统

			}
			else {
				printf("\t\t\t您输入有误!\n\n");
			}
	} while (choose!=1&&choose!=2&&choose!=3);
	if (gap == 1) {
		gap = 200000;
	}
	else if (gap == 2) {
		gap = 100000;
	}
	else {
		gap = 5000;
	}
	
	do {
		//清空界面
		system("cls");
		//功能提示
		printf("\n\t\t\t\t\t投资小游戏(第%d天)\n", daysnum);
		getNum(inv, m, daysnum);
		OutputInv(inv, m);
		//TableFormat(inv,m);
		//newOutputInv(inv, m);
		amount_inv = 0;
		for (int i = 0; i < *_m_; i++) {
			amount_inv += (inv[i].ownnum * inv[i].ownprice);
		}
		if (deposit + amount_inv > objection) 
			level++;
		objection *= (1 + level * 0.25);
		printf("\n\t您当前的现金数:%.2lf, 持有总额: %.2lf, 当前等级:%.0lf, 当前目标: %.2lf(现金+投资额达到当前目标会晋级)\n", deposit, amount_inv, level, objection);

		//与目标金额差距过大视作游戏失败
		if ((deposit + amount_inv + gap) < objection) {
			printf("\n\t\t\t您当前的现金数:%.2lf, 投资额: %.2lf, 你当前的等级:%.0lf, 游玩天数:%d\n\t\t\t由于与目标金额差距过大,你失败了!期待你的下一次挑战!", deposit, amount_inv, level, daysnum);
			exit(0);
		}
		printf("\n\t\t\t1.交易\t\t\t\t2.随机事件(未做)\n\n\t\t\t3.返回主菜单(不会存档!)\t4.添加项目\n\n\t\t\t0.退出游戏(自动存档)\t\t其他数字:下一轮\n\n\t\t\t请键入对应数字进行功能选择:");
		scanf_s("%d", &choose);
		printf("\n");
		
		switch (choose)
		{
			case 1:
				
				Deal(inv,m,deposit,amount_inv);
				break;
			case 2:
				printf("\n选项2\n");
				break;
			case 3:
				//清空界面
				system("cls");
				goto R;
				break;
			case 4:
				if (m == 20) {		// n的其他情况前面已考虑,此处考虑数组上限问题 
					printf("项目数不能超过20个,请重新进行系统功能选项选择!\n");
				}
				else {
					//printf("%d", *_m_);
					AppendInv(inv, m);
				}
				break;
			case 0:
				printf("\n\t\t\t你确定退出游戏吗?(确定请按 ‘1’ , 取消请按其他数字):");
				scanf_s("%d", &choose);
				if (choose == 1) {
					printf("\n\t\t\t您当前的现金数:%.2lf, 投资额: %.2lf\n", deposit, amount_inv);
					printf("\t\t\t已为您自动存档(请勿删除或修改同目录下生成的存档文件)\n");
					//写入文件
					write_lijie(inv,_m_,deposit,amount_inv,daysnum, level, objection, gap);
					//加密
					Compress(n, c);
					//删除明文文件
					remove("archive.txt");
					printf("\t\t\t期待您的下一次游玩!\n");
					exit(0);
				}
				else {
					break;
				}
			default:
				break;				
		}
		daysnum++;
	} while (true);
    return 0;
}

read_write.h

void read_lijie(struct INVEST inv[], int* _m_, double &deposit, double &amount_inv, int &daysnum, double &level, double &objection, double &gap) {
    // 从文件中读取数据
    FILE* r_file;
    int i = 0;
    if (fopen_s(&r_file, "archive.txt", "r") == 0) {
        int temp;
        fscanf_s(r_file, "%lf %lf %d %d %.2lf %.2lf %.2lf", &deposit, &amount_inv, &daysnum, &temp, &level, &objection, &gap);
        *_m_ = temp;
        for (i = 0; i < *_m_; i++) {
            fscanf_s(r_file, "%d %s %lf %lf %lf %lf %lf", &inv[i].num, inv[i].name, sizeof(inv[i].name),
                &inv[i].ownprice, &inv[i].ownnum, &inv[i].lastprice, &inv[i].nowprice, &inv[i].yield);
        }
       
        fclose(r_file);
    }
    else {
        printf("\n\t\t\t没有找到存档!\n");
    }
    return;
}

void write_lijie(struct INVEST inv[], int* _m_, double deposit, double amount_inv, int daysnum, double level, double objection, double gap) {
    FILE* w_file;
    /*if (fopen_s(&file, "C:\myarchive\archive.txt", "w") != 0) {
        string dir = "C:\\myarchive";
        CreateMultiLevel(dir);
    }*/
    fopen_s(&w_file, "archive.txt", "w");
    // 写入数据到文件
    /*if (fopen_s(&file, "C:\\archive.txt", "w") == 0) {*/
        fprintf_s(w_file, "%.2lf %.2lf %d %d %.2lf %.2lf %.2lf\n", deposit, amount_inv, daysnum+1, *_m_, level, objection, gap);
        for (int i = 0; i < *_m_; i++) {
            fprintf_s(w_file, "%d %s %.2lf %.2lf %.2lf %.2lf %.2lf", inv[i].num, inv[i].name,
                inv[i].ownprice, inv[i].ownnum, inv[i].lastprice, inv[i].nowprice, inv[i].yield);
            if (i != *_m_ - 1)
                fprintf_s(w_file, "\n");
        }
        fclose(w_file);
    //}
    /*else {
        printf("\n存档文件创建失败!请联系开发者解决\n");
    }*/
    return;
}

deal.h

void Deal(struct INVEST inv[], int _m_, double &deposit, double &amount_inv) {
	int tnum=0,flag=0,index=0,model=0;
	double town=0, result=0;
	do {
		printf("\t -------------------------------------------------------------------------------------------- \n");
		printf("\n\t\t\t\t\t项目交易系统\n");
		printf("\n\t\t\t注意:可以在同一交易日多次交易,此时交易价固定,退出交易系统后刷新交易价\n");
		printf("\n\t\t\t\t1.购买\t\t2.卖出\t\t0.退出\n");
		do {
			printf("\n\t\t\t您选择:"); 
			scanf_s("%d", &model);
			if (model == 0) {
				return; 
			}//输入0退出购买
		}while (model != 1 && model != 2 && model != 0);
		do {
			printf("\n\t\t\t请输入你要交易的项目对应的发行号:");
			scanf_s("%d", &tnum);
			//printf("%d", _m_);
			for (int i = 0; i < _m_; i++) {
				if (inv[i].num == tnum)//在现有项目中匹配
				{
					printf("\n\t\t\t\t名字\t\t持有价格\t持有量\t上一次价格\t当前交易价\t收益率\n");
					printf("\n\t\t匹配到项目:");
					flag = 1; index = i;//记录下标
					printf("\t%s\t%.2lf\t\t%.2lf\t%.2lf\t\t%.2lf\t\t%.2lf\n", inv[i].name, inv[i].ownprice, inv[i].ownnum, inv[i].lastprice, inv[i].nowprice, inv[i].yield);
					break;
				}
			}
				
			if (flag == 0)
				printf("\n\t\t\t非法项目号,请重新输入(#^.^#)\n");
		} while (flag == 0);
		do {
			printf("\n\t\t请输入交易量(1~999闭区间,不想交易请输入0):");
			scanf_s("%lf", &town);
			if (town == 0) break;
			result = town * inv[index].nowprice;
			if (model == 1 && (result <= deposit) && town >= 1 && town <= 999) {//购买
				deposit -= result;	//现金减少
				//修改该项其他数据
				//持有价赋值,如果是0就直接赋值;如果不是就(持有量*持有价+购买量和当前价)/(持有量+购买量)。
				if (inv[index].ownnum == 0) {
					inv[index].ownprice = inv[index].nowprice;
				}
				else {
					inv[index].ownprice = (inv[index].ownnum * inv[index].ownprice + town * inv[index].nowprice)/(inv[index].ownnum+town);
				}
				//持有量赋值,直接相加
				inv[index].ownnum += town;//持有量赋值
				printf("\n\t\t\t购买成功!\n");
			}
			else if (model == 2 && (town <= inv[index].ownnum) && town >= 1 && town <= 999) {//卖出
				deposit += result;//现金减少
				//修改该项其他数据
				inv[index].ownnum -= town;
				if (inv[index].ownnum == 0) {//持有量为零时
					inv[index].ownprice = 0;//持有价清零
					inv[index].yield = 0;	//收益率率清零
				}
				printf("\n\t\t\t成功卖出!\n");
			}
			else {
				printf("\n\t\t\t你的输入超过能够买卖的金额或交易量范围!请重新输入!\n");
				flag = 0;
			}
		} while (flag == 0);
		printf("\n\t\t\t交易成功!祝您财源广进!\n");
	} while (model != 0);
	return;
}


display.h

#include<math.h>
void OutputInv(struct INVEST inv[], int _m_) {
	int i;
	printf("\n\t发行号\t名字\t\t持有价格\t持有量\t\t上一次价格\t当前交易价\t收益率\t\n");
	printf("\t ------------------------------------------------------------------------------------------------- \n");
	for (i = 0; i < _m_; i++) {
		if (inv[i].ownnum != 0) {
			inv[i].yield = (inv[i].nowprice - inv[i].ownprice)/ inv[i].ownprice;
		}
		printf("\t %d  \t%s\t%.2lf\t\t%.2lf\t\t%.2lf\t\t%.2lf\t\t%.2lf\t\n", inv[i].num, inv[i].name, inv[i].ownprice, inv[i].ownnum, inv[i].lastprice, inv[i].nowprice, inv[i].yield);
		printf("\t ------------------------------------------------------------------------------------------------- \n");
	}
}

void AppendInv(struct INVEST inv[], int &_m_)// 在投资表末尾添加一个记录
{
	printf("\t\t\t输入新项目名字(其他数据将自动生成):");//录入名字
	char tname[MAX_NAME_LENTH] = {};
	scanf_s("%s", tname, MAX_NAME_LENTH-1);
	inv[_m_].num = (_m_ + 450) * (_m_ + 1) - 52; strcpy_s(inv[_m_].name,tname);inv[_m_].ownprice = 0;
	inv[_m_].lastprice = 1; inv[_m_].nowprice = 300; inv[_m_].ownnum = 0; inv[_m_].yield = 0;
	_m_ += 1;
	return;
}

getnum.h

struct INVEST {
	int num;  //发行号
	char name[MAX_NAME_LENTH];  //名字
	double ownprice; //持有价
	double ownnum;	//持有量
	double lastprice; //上一次价格
	double nowprice;	//当前交易价
	double yield; // 收益率,小数点后两位!!!
	//int rspace[7];//右侧空格数
}inv[20];//介个是一个类型为 INVEST 的一维数组 inv,最大为20 

//获取随机数,实现波动,可以外套循环实现多次波动
void Init(struct INVEST inv[], int &_m_) {
	_m_ = 6;
	char temp[6][MAX_NAME_LENTH] = { "喵喵科技", "熊猫集团", "华佗药业", "袋鼠健身", "春蚕纺织", "猿猴娱乐" };
	for (int i = 0; i < _m_; i++) {
		inv[i].num = (i + 500) * (i + 1) - 52; strcpy_s(inv[i].name, temp[i]);inv[i].ownprice = 0;
		inv[i].lastprice = 1; inv[i].nowprice = 300; inv[i].ownnum = 0; inv[i].yield = 0; 
		/*for (int j = 0; j < 7; j++) {
			inv[i].rspace[j] = 2;
		}*/
	}
	return;
}

//生成随机数
void getNum(struct INVEST inv[], int _m_, int daysnum) {
	srand((unsigned)time(NULL));//保证每次生成的随机数不一样
	int x=0;//概率
	if (daysnum < 20) {
		x = 780;
	}
	else if (daysnum < 50) {
		x = 623;
	}
	else if (daysnum < 90) {
		x = 515;
	}
	else if(daysnum < 150){
		x = 300;
	}
	else if(daysnum < 200){
		x = 645;
	}
	else if (daysnum < 250) {
		x = 150;
	}
	else if(daysnum < 315){
		x = 650;
	}
	else {
		x = 450;
	}
	for (int i = 0; i < _m_; i++)
	{
		inv[i].lastprice = inv[i].nowprice;
		int myran = rand() % (1000 - 99) + 99;
		if (myran > x) //有(1000-x)/1000的可能上涨
		{	
			inv[i].nowprice += rand() % (100 - 9) + 9;//上涨
		}
		else 
		{
			double temp = rand() % (100 - 9) + 9;//下跌
			if (inv[i].nowprice - temp < 200) {
				inv[i].nowprice += rand() % (300 - 99) + 99;
			}
			else {
				inv[i].nowprice -= rand() % (100 - 9) + 9;//下跌
			}
		}
	}
	return;
}



decode.h

#ifndef MYCLASS_H2
#define MYCLASS_H2

/*********************************
函数描述:读取压缩文件,一个个地匹对Huffman编码信息
函数名:Matching
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
          c int 型 , 记录结点总数,控制循环次数
          *ifp,*ofp FILE型, 控制读取文件
          ch unsigned char型, 存储临时读取到的字符||作为数组下标
          flength long型, 记录文件长度
          ofname char型, 传入解压后的文件名
注意事项:文件要注意关闭
*********************************/
void Matching(HuTree n[], int c, FILE*& ifp, FILE* ofp, unsigned char& ch, long& flength, char ofname[]) {
    long i, j, p, m, f, l;
    char buf[255]{}; char bx[255]{}; HuTree tmp;
    for (i = 0; i < c; i++)//构造Huffman数的c个叶子结点
    {
        fread(&n[i].node, 1, 1, ifp);//读取一个字节,得到Huffman树的一个结点
        fread(&ch, 1, 1, ifp);         //读取字符对应的哈夫曼编码长度
        p = (long)ch;
        n[i].weight = p;//weight由保存结点字符出现次数改为保存结点的编码长度
        n[i].code[0] = 0;  //初始编码为'\0'
        if (p % 8 > 0)   m = p / 8 + 1;//字节数
        else m = p / 8;
        for (j = 0; j < m; j++)
        {
            fread(&ch, 1, 1, ifp);//每次取出一个字节
            f = ch;                 //十六进制转十进制
            _itoa_s(f, buf, 2);    //将f转换为二进制表示的字符串
            f = strlen(buf);    //long变成二进制时,不足8位则补0
            for (l = 8; l > f; l--)
                strcat_s(n[i].code, "0");//先在哈夫曼树结点编码补0
            strcat_s(n[i].code, buf);//补足0后连接已转好的01字符串
        }
        n[i].code[p] = 0;   //设置结束符
    }
    for (i = 0; i < c; i++)//根据哈夫曼编码的长短,对结点进行排序,编码短的在前
        for (j = i + 1; j < c; j++)
            if (strlen(n[i].code) > strlen(n[j].code))
            {
                tmp = n[i];
                n[i] = n[j];
                n[j] = tmp;
            }
    p = strlen(n[c - 1].code);//编码的最大长度
    fseek(ifp, 8, SEEK_SET);//定位文件指针至存放原文件哈夫曼编码的位置
    m = 0;
    bx[0] = 0;//每次处理的解码的字符串
    while (true)//通过哈夫曼编码的长短,依次解码,从原来的位存储还原到字节存储
    {
        //保存最长编码的01串,字符数>=1
        while (strlen(bx) < (unsigned int)p)
        {
            fread(&ch, 1, 1, ifp);//取一个字节,转换成二进制01
            f = ch;
            _itoa_s(f, buf, 2);    //将一个整数转成字符串,2表示二进制
            f = strlen(buf);
            for (l = 8; l > f; l--)//在单字节内对相应位置补0
                strcat_s(bx, "0");
            strcat_s(bx, buf);
        }
        for (i = 0; i < c; i++)//找相同的编码
            if (memcmp(n[i].code, bx, n[i].weight) == 0) break;
        //比较成功后,需继续往后判断bx对应的其他字符
        strcpy_s(bx, bx + n[i].weight);
        ch = n[i].node;         //得到匹配后的哈夫曼编码对应的字符
        fwrite(&ch, 1, 1, ofp); //将得到得到字符写入目标文件
        m++;                    //继续比较,统计解压缩后文件的长度
        if (m == flength) break;//flength是原文件长度
    }
    fclose(ifp);    //关闭
    fclose(ofp);
    //if (m == flength)//对解压缩后文件和原文件相同性比较进行判断
    //    printf("\t解压缩文件与原文件相同!");
    //else
    //    printf("\t解压缩文件与原文件不同!");
    return;
}
/*********************************
函数描述:读取.huf文件,扫描文件信息生成Huffman树和Huffman编码表
函数名:ReadFile
返回值:-1 -2 0
参数描述:*ifp,*ofp FILE型,控制读取文件
          c int 型,记录结点总数,控制循环次数
          flength long型, 记录文件长度
          ifname[] char型,记录要解压的文件名
          ofname[] char型, 记录解压后的文件名
注意事项:在win32环境下sizeof(long)是 4。其他环境会出现值为8的情况
*********************************/
int ReadFile(FILE*& ifp, FILE*& ofp, int& c, long& flength, char ifname[], char ofname[]) {
    long f;
    //以二进制只读方式打开.huf文件,ifp指向该文件
    //scanf_s("%s", ifname, 25);
    errno_t ierr = fopen_s(&ifp,"archive.lj", "rb");
    if (ifp == NULL)
    {
        //printf("\n\t解码文件打开失败\n");
        return -1;
    }
    //以二进制只写方式打开outputfile文件,ofp指向该文件
    errno_t oerr = fopen_s(&ofp, "archive.txt", "wb");
    if (ofp == NULL) {
        //printf("\n\t解压缩文件失败!\n");
        return -2;
    }
    fread(&flength, sizeof(long), 1, ifp);
    //printf("\n\tflength:%ld\n", flength);
    fread(&f, sizeof(long), 1, ifp);
    fseek(ifp, f, SEEK_SET);
    fread(&c, sizeof(long), 1, ifp);
    return 0;
}
/*********************************
函数描述:调用读取文件函数和解压函数
函数名:UnCompress
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
          c int 型  记录结点总数,控制循环次数
注意事项:参数的传入传出要考虑是否实际被修改
*********************************/
int UnCompress(HuTree n[], int& c) {
    char ifname[25]{}, ofname[25]{};
    unsigned char ch = 0;
    long flength = 0L;
    FILE* ifp, * ofp;
    int flag = ReadFile(ifp, ofp, c, flength, ifname, ofname);
    if (flag != 0) {
        return -1;
    }
    Matching(n, c, ifp, ofp, ch, flength, ofname);
    return 0;
}
#endif

coding.h

#ifndef MYCLASS_H1
#define MYCLASS_H1
#define N 256        //叶子结点数
//压缩源文件
typedef struct Tree
{
	unsigned char node;//文字
	char code[256];//编码
	int weight;       //结点权重
	int parent, lchild, rchild;//双亲孩子的仿真指针
}HuTree;//哈夫曼树

/*********************************
函数描述:反转哈夫曼编码
函数名:rec
返回值:无返回值
参数描述:arr[] char型 传入结点的编码数组
注意事项:使用strlen应注意数组的初始化等问题
*********************************/
void rec(char arr[]) {
	int len = strlen(arr);
	for (int i = 0; i < len / 2; i++)
	{
		char temp;
		temp = arr[i];
		arr[i] = arr[len - i - 1];
		arr[len - i - 1] = temp;
	}
}
/*********************************
函数描述:实现冒泡排序
函数名:BubbleSort
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
		  c int 型  记录结点总数,控制循环次数
注意事项:权值相同时不能交换
*********************************/
void BubbleSort(HuTree arr[], int c)
{
	int end = c; //  初始化数组末尾元素下标
	while (end)  //  循环至数组末尾
	{
		int flag = 0;//  设置交换标志
		for (int i = 1; i < end; ++i)
		{
			if (arr[i - 1].weight < arr[i].weight)//  如果前一个元素权重小于当前元素权重
			{
				HuTree tem = arr[i];
				arr[i] = arr[i - 1];
				arr[i - 1] = tem;
				flag = 1;
			}
		}
		if (flag == 0)//  如果没有进行交换
		{
			break;
		}
		--end;
	}
}
/*********************************
函数描述:建立哈夫曼树
函数名:CreateTree
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
		  c int 型  记录结点总数,控制循环次数
注意事项:此函数修改了 c 的值
*********************************/
void CreateTree(HuTree n[], int& c)
{
	int node1, node2, i, k;
	int lnode = -1, rnode = -1;//node1, node2记录当前最小值。lnode, rnode记录下标。
	for (i = 0; i < 2 * c - 1; i++)
	{
		n[i].parent = n[i].lchild = n[i].rchild = -1;//全部置空,初始化
		if (n[i].weight != 0) {
			n[i].node = (unsigned char)i;//如果有权值,设置叶子结点文本
		}
		else {
			n[i].node = 0;//如果没有权值,置零
		}
	}
	BubbleSort(n, c);//使用冒泡排序对结点权重从大到小排序
	for (i = 0; i < c; i++)
		if (n[i].weight == 0) break;//前面排过序,这里作为出口节省时间
	c = i;//实际叶子结点总数
	for (i = c; i < 2 * c - 1; i++)
	{
		node1 = 10000;  //存放最小值
		node2 = 10000;// 找出weight值最小的两个结点node1和node2   存放次小
		lnode = rnode = -1;
		for (k = 0; k < i; k++)
		{
			if (n[k].parent == -1)// 仅在无双亲的结点中挑选
			{
				if (n[k].weight < node1)
				{
					node2 = node1;
					node1 = n[k].weight;
					rnode = lnode;
					lnode = k;
				}
				else if (n[k].weight < node2)
				{
					node2 = n[k].weight;
					rnode = k;
				}
			}
		}
		n[lnode].parent = n[rnode].parent = i;
		n[i].weight = n[lnode].weight + n[rnode].weight;
		// 用数组的第i个结点作为根结点来合并node1和node2
		n[i].lchild = lnode;   n[i].rchild = rnode;
	}
}
/*********************************
函数描述:实现哈夫曼编码,左子树为0,右子树为1。
函数名:EditCode
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
		  c int 型  记录结点总数,控制循环次数
注意事项:编码从下往上走,是反的,应反转数组再存储编码。
*********************************/
void EditCode(HuTree n[], int c)
{
	int s;//记录编码位置site
	int t, z, i;//其他一些临时变量、中间变量
	for (i = 0; i < c; i++)
	{
		n[i].code[0] = 0;//给编码结束符
		s = -1;
		z = i;
		t = n[i].parent;
		do
		{
			//父亲左孩子的权重与结点(沿着i结点往上走)权重比较
			if (z == n[t].lchild)
				n[i].code[++s] = '0';//左孩子记为0
			else n[i].code[++s] = '1';//右孩子记为1
			z = t;
			t = n[z].parent;
		} while (t != -1);
		n[i].code[s + 1] = '\0';
		//这时候一个叶子的edit数组已经记录完毕了,但是是反序的
		//此处测试用
		//printf("\t%c:", n[i].node);//输出结点文字
		rec(n[i].code);
		//printf("%s\n", n[i].code);//换行,为下次输出做准备
	}
}
/*********************************
函数描述:读取文件,调用建树和编码的函数,关闭文件
函数名:Compress
返回值:无返回值
参数描述:n[] HuTree型,哈夫曼树结构体数组
		  c int 型  记录结点总数,控制循环次数
注意事项:在末尾应关闭文件
*********************************/
void Compress(HuTree n[], int& c)//传入数组,存储读到的文件内容
{
	FILE* ifp, * ofp;
	unsigned char ch = 0;
	char ifname[12]{}, ofname[12]{};
	int flength = 0;
	int i = 0, j = 0;
	errno_t ierr = fopen_s(&ifp, "archive.txt", "rb");//二进制读的方式打开需要压缩的文件
	errno_t oerr = fopen_s(&ofp, "archive.lj", "wb");//二进制写的方式,指向压缩后的位置
	if (ierr != 0)
	{
		//printf("加密存档过程中文件打开失败...\n");
		exit(0);
	}
	while (!feof(ifp))
	{
		fread(&ch, 1, 1, ifp);//一个个字符地读取
		n[ch].weight++;//该字符的权重++
		flength++;//文件长度++
	}
	flength--;
	long length1 = flength;//原文件总长度
	n[ch].weight--;
	CreateTree(n, c);
	EditCode(n, c);
	//压缩之前写入关键信息
	fseek(ifp, 0, SEEK_SET);//SEEK_SET指向文件头,将文件指针指向待压缩文件的开始位置
	fwrite(&length1, sizeof(int), 1, ofp);//在压缩文件头写入文件总长度,占四个字节
	//printf("\n\t写入之前的length1:%ld\n", length1);
	/*重定位压缩文件指针,从头偏移8个字节,留出空间写入其他信息,并为写入哈夫曼编码准备*/
	fseek(ofp, 8, SEEK_SET);
	char buf[512]{};	//定义缓冲区,保存字节的Huffman编码
	buf[0] = 0;			//初始为'\0'
	long f = 0;			//统计字符个数,可判断原文件是否读完
	int pt1 = 8;		//统计文件长度,哈夫曼编码从第八个字节开始写入
	while (!feof(ifp)) //扫描原文件
	{
		//从文件中读取一个字符,读取一个之后后移一位
		ch = fgetc(ifp);
		f++;
		for (i = 0; i < c; i++)
			if (ch == n[i].node)
				break;
		strcat_s(buf, n[i].code);
		int j = strlen(buf);
		ch = 0;
		while (j >= 8)//若当前编码长度大于等于8,则进行拆分,分成两个字节存
		{
			for (i = 0; i < 8; i++) {
				if (buf[i] == '1')	ch = (ch << 1) | 1;
				else ch = ch << 1;
			}
			fwrite(&ch, 1, 1, ofp);//把凑好的一个字节编码写入文件
			pt1++;				//统计压缩后文件的长度,字节数加1
			strcpy_s(buf, buf + 8);//把buf后一个字节起的所有内容复制到buf中,一个个地取
			j = strlen(buf);
		}
		if (f == length1)	break;//若源文件所有的字符取完,结束
	}
	if (j > 0)
	{
		strcat_s(buf, "00000000");
		for (i = 0; i < 8; i++)
		{
			if (buf[i] == '1')	ch = (ch << 1) | 1;
			else ch = ch << 1;
		}
		fwrite(&ch, 1, 1, ofp);//把最后一个字节写入文件中
		pt1++;
	}
	fseek(ofp, 4, SEEK_SET);	//移动文件指针位置到第4个字节
	fwrite(&pt1, sizeof(long), 1, ofp);//写入统计压缩后文件的长度,4个字节
	fseek(ofp, pt1, SEEK_SET);//移动文件指针到压缩后文件尾
	fwrite(&c, sizeof(long), 1, ofp);//写入节点数目,即总的不同字节的个数

	for (i = 0; i < c; i++) {
		fwrite(&(n[i].node), 1, 1, ofp);//写入每个节点代表的字符
		ch = strlen(n[i].code);
		fwrite(&ch, 1, 1, ofp);
		j = strlen(n[i].code);//统计哈夫曼编码长度
		if (j % 8 != 0)//若存储的位数不是8的倍数,则补0
		{
			for (f = j % 8; f < 8; f++)
				strcat_s(n[i].code, "0");
		}
		/*将哈夫曼编码字符串变成二进制数*/
		while (n[i].code[0] != 0)//不到编码结尾不结束喵
		{
			ch = 0;
			for (j = 0; j < 8; j++)//字符有效存储不超过8位,则对有效位数左移实现补0
			{
				if (n[i].code[j] == '1')
					ch = (ch << 1) | 1;
				else
					ch = ch << 1;
			}
			strcpy_s(n[i].code, n[i].code + 8);//继续转换后面的字符串
			fwrite(&ch, 1, 1, ofp);
		}
	}
	long length2 = pt1--;//压缩后文件大小
	double div = ((double)length1 - (double)length2) / (double)length1;
	fclose(ifp);//关闭文件
	fclose(ofp);
	//printf("\t压缩率为%f%%\n", div * 100);
}
#endif

achieve.h

成就系统,只是构思,没做

//固定增长量:每个阶段的目标数字增加一个固定的值。例如,每个阶段的目标数字增加100。
//
//百分比增长:每个阶段的目标数字增加一个固定百分比。例如,每个阶段的目标数字增加当前目标数字的10% 。
//
//级别倍增:每次升级时,目标数字会按照一个固定的倍数增长。例如,初始阶段的目标数字是100,下一个阶段的目标数字是初始值的2倍,再下一个阶段是前一个阶段的2倍,以此类推。
//
//动态增长:目标数字的增长根据玩家的游戏进度和表现进行动态调整。例如,根据玩家的黄金收集速度或完成任务的数量来调整目标数字的增长速度。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值