C语言—打单词游戏

ps:页面最下面附上程序打包,也可在线浏览

在这里插入图片描述

打单词游戏:

本项目,主要目的是练习模块划分能力,我将整个程序划分为多个模块

项目分析:

显示要求
打单词
下落方式
显示多个单词
通过正确输入消除单词
对输入正确性统计
能够随着熟练度改变速度
有软件版权信息
好看点,更有吸引力

隐式要求
单词来源(外部文件或是字符串)
原始单词存储问题(多个同类型元素,采用数组的方式,每个数组元素为字符串单词的地址)char *word[300]
显示单词的选择,显示个数的确定
单词显示区域
单词输入时的回显问题,输入的单词需要被看见
输入正确性验证(验证查表的位置选择,是数据源还是其他存储区域)

模块划分
dictionary数据源模块,负责数据源的获取的,和单词分割
emitter发射器模块,负责从数据源中获取需要显示的单词,并保存
view显示模块,负责显示单词和其余操作

针对接口编程而非针对实现编程
深拷贝和浅拷贝

浅拷贝
struct foo
{
int *p;
}

struct foo f1;
f1.p=malloc(20);

struct foo f2=f1;
free(f1.p);
int a=*(f2.p)
上面的程序会出现错误,原因是浅拷贝中f1.p被释放后,f2.p变为失效指针
所以需要做到深拷贝的话,需要在拷贝时为f2.p重新开辟空间后赋值

特殊的:
struct foo
{
int ar[3];
}

struct foo f1={1,2,3};
struct foo f2=f1;
这种情况也为深拷贝,f2的ar改变不会影响f1的ar的值,说明两的结构体变量在拷贝后的空间是不同的。
两种拷贝都具有各自的意义和作用。

int a[3]={1,2,3}
int c=(a+=1)//这种情况无法编译通过,因为数组名是只读的无法被赋值
但是int c=
(a+1)//由于数组名a没有被赋值,只是移动了地址位置

.c和.cpp被称为编译单元

头文件保护符
.h文件中使用确保每个头文件只被包含一次
#pragma once
或者
#define 指令把一个名字设定为预处理变量
#ifndef 当且仅当变量未定义时为真
#ifdef 当且仅当变量定义时为真
#endif 一旦检查结果为怎则执行后续操作,直到遇到endif位置

由于后期改进的情况,我把 dictionary数据源模块划分为更加清除的两个模块

所以先展示模块的划分情况
在这里插入图片描述
一共四个模块
下面一一放出各个模块的完成详细过程
1.file_division.h

#pragma once 
#include<string.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
#include <windows.h>
#include"data_source.h"
//函数指针
typedef void(*fun)(char *word);
//解析函数
void parse(char *file_name,fun fun_name);

file_division.c

#include"file_division.h"

#define start 0
#define in_word 1
#define out_word 2

void parse(char *file_name, fun fun_name)
{
	FILE *fp = fopen(file_name, "r");
	int i = 0;
	if (fp != NULL)
	{
		char buffer[32] = { 0 };
		char *p = buffer;
		int state = start;//正常单词
		while (!feof(fp))//判断文件 是否读到末尾,如果未读到末尾,则返回假,然后求反
		{
			char ch = fgetc(fp);
			switch (state)
			{
			case start:
				if (isalpha(ch))
				{
					*p = ch;
					state = in_word;
				}
				else
				{
					state = out_word;
					p--;
				}
				break;
			case in_word:
				if (!isalpha(ch))//else keeping in_word
				{
					if (ch == ' ')
					{
						state = start;
						*p = '\0';
						
						if (fun_name)
							fun_name(buffer);
						i++;
						p = buffer;
						p--;
					}
					else
					{
						state = out_word;
						p = buffer;
						p--;
					}
				}
				else *p = ch;
				break;
			case out_word:
				if (isalpha(ch) && *(p - 1) == ' ')
				{
					*p = ch;
					state = in_word;
				}//else keeping out_word
				else p--;
				break;
			}
			p++;
		}
	}
	fclose(fp);
}

2 data_source.h

#pragma once 
#include"file_division.h"

struct dictionary;//结构体声明,而不是定义,是为了数据隐藏不希望用户看见内部数据表示

struct dictionary *download(const char *file_name);//下载数据源
void release();//释放数据源

const char *provide(int index);//提供某个下标的单词
const int words_num();//提供单词总数

data_source.c

#include"data_source.h"

#define MAX_WORDS 256
struct dictionary
{
	char *words[MAX_WORDS];
	char *file_name;
	int num;
}g_dictionary;//结构体变量为全局变量,为了私有不让其他模块看见
//由于结构体变量为全局变量,所以所有元素为0值
int is_same_word(const char *word)
{
	int j = 0;
	for (; j <g_dictionary.num; j++)
	{
		if (!stricmp(g_dictionary.words[j], word))//出现相同,则放弃这个单词
		{
			return -1;
		}
	}
	return j;
}

void add(const char *word)
{
	int n = is_same_word(word);
	if (n!=-1)
	{
		g_dictionary.words[n] = _strdup(word);
		g_dictionary.num++;
	}
}

struct dictionary *download(const char *file_name)//下载数据源,并分割单词
{	release();
	parse(file_name,add);
	g_dictionary.file_name = _strdup(file_name);//深拷贝的原因是不知道文件名是否由局部变量得来
	return &g_dictionary;
}

void release()//释放下载的数据源
{
	if (g_dictionary.num == 0)
	{
		return;
	}
	for (int i = 0; i < g_dictionary.num; i++)
	{
		if (g_dictionary.words[i] != NULL)
		{
			free(g_dictionary.words[i]);
			g_dictionary.words[i] = NULL;//赋值为空,方便二次下载数据源正常
		}
	}
	g_dictionary.num = 0;

	free(g_dictionary.file_name);//可能有人会认为在file_name未初始化时free不安全,但是结构体为全局变量未初始化时默认为0值
	g_dictionary.file_name = NULL;
}

const char *provide(int index)//提供下标对应单词
{
	return g_dictionary.words[index];
}

const int words_num()//提供单词总数
{
	return g_dictionary.num;
}

3 Launcher.h

#pragma once 
#include<stdbool.h>

struct Word
{
	const char *word;
	int color;
	int killAnim;
	int x, y;//列是x,行是y
};

void emit_init(int winWidth, int winHeight);
void emit_release();

void emit_words();
bool emit_hittest(const char *word);
int  emit_update();

int emit_word_count();
struct Word* emit_get_word(int index);

Launcher.c

#include"data_source.h"
#include"Launcher.h"

#define MAX_NUM 5
static int times = 0;
struct windows//窗口结构
{
	struct Word word[MAX_NUM];
	int wide;
	int high;

}g_windows;

struct windows sd;
void emit_init(int winWidth, int winHeight)//初始化窗口范围
{
	g_windows.wide = winWidth;
	g_windows.high = winHeight;
}

void emit_release()//重置当前窗口
{
	return;
}

void emit_words()//提取设定数目的随机单词
{
	for (int i = 0; i < MAX_NUM; i++)
	{
		if (g_windows.word[i].word == NULL)
		{
			g_windows.word[i].color = rand() % 9 + 1;
			g_windows.word[i].killAnim = 0;
			g_windows.word[i].word = provide(rand() % words_num());
			g_windows.word[i].x = rand() % g_windows.wide;
			int row = 0;
			row = g_windows.word[i].x + strlen(g_windows.word[i].word);//判断单词所在位置加上单词长度的和是否越界显示区域
			if (row > g_windows.wide)
			{
				g_windows.word[i].x -= (row - g_windows.wide);
			}
			if (times == 0)
			{
				g_windows.word[i].y = i;
			}
			else g_windows.word[i].y = 0;
		}
	}
	times += 1;//static变量,使后面的单词都初始化在第一行

}

bool emit_hittest(const char *word)//判断输入的单词是否匹配
{
	for (int i = 0; i < MAX_NUM; i++)
	{
		struct Word *wd = &g_windows.word[i].word;
		if (wd->word != NULL&&strcmp(wd->word, word) == 0)
		{
			wd->killAnim =13;
			return true;
		}
	}
	return false;
}


int emit_update()//单词下落跟新
{
	int missed = 0;
	for (int i = MAX_NUM-1; i >= 0; i--)
	{
		struct Word *wd = &g_windows.word[i];
		if (wd->word != NULL&&wd->killAnim==0)
		{
			wd->y += 1;
			if (wd->y>g_windows.high)
			{
				wd->word = NULL;
				missed += 1;
			}
		}
	}
	return missed;
}

int emit_word_count()//返回单词总数
{
	return MAX_NUM;
}

struct Word* emit_get_word(int index)//返回某下标的单词
{
	return &g_windows.word[index];
}

4.display.h

#pragma once 
void view_init();
void view_release();

void view_show_appinfo();

void view_show_score(int killed, int missed);
void view_show_level(int level, int speed);

void view_show_popwords();

void view_show_input_prompt();
void view_show_input_word(const char *word);

int  view_emitter_winid();
void view_clear_emitwnd();

void view_do_killanim();

display.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "glConsole.h"
#include "display.h"
#include"Launcher.h"
int about_window = -1;
int score_window = -1;
int emitter_window = -1;
int input_window = -1;

void view_init()
{
	int main_window;
	int bottom_window1, bottom_window2;

	main_window = glmxConsole_CreateWindow(45, 30, "word-pop");
	glmxConsole_SplitWindow(main_window, 4, WND_SPLIT_HORIZONTAL, '-', 7, &about_window, &bottom_window1);
	glmxConsole_SplitWindow(bottom_window1, 2, WND_SPLIT_HORIZONTAL, '-', 6, &score_window, &bottom_window2);
	glmxConsole_SplitWindow(bottom_window2, 19, WND_SPLIT_HORIZONTAL, '-', 5, &emitter_window, &input_window);
}

void view_release()
{
	return;
}

void view_show_appinfo()
{
	glmxConsole_WndDrawFormatText(about_window, 0, 1, 12, "WORD-POP", TEXT_ALIGN_CENTER);
	glmxConsole_WndDrawFormatText(about_window, 0, 2, 12, "> GLIMIX STUDIO <", TEXT_ALIGN_CENTER);
	glmxConsole_WndDrawFormatText(about_window, 0, 2, 12, "ZYF", TEXT_ALIGN_RIGHT);
}

void view_show_score(int killed, int missed)
{
	char buffer[64];

	sprintf_s(buffer, 64, "KILLED: %d", killed);
	glmxConsole_WndDrawTextEx(score_window, 0, 0, 13, buffer);

	sprintf_s(buffer, 64, "MISSED: %d", missed);
	glmxConsole_WndDrawTextEx(score_window, 0, 1, 13, buffer);
}

void view_show_level(int level, int speed)
{
	char buffer[64];

	sprintf_s(buffer, 64, "LEVEL: %d", level);
	glmxConsole_WndDrawFormatText(score_window, 0, 0, 13, buffer, TEXT_ALIGN_RIGHT);

	sprintf_s(buffer, 64, "SPEED: %d", speed);
	glmxConsole_WndDrawFormatText(score_window, 0, 1, 13, buffer, TEXT_ALIGN_RIGHT);
}

void view_show_input_prompt()
{
	glmxConsole_WndDrawTextEx(input_window, 0, 0, 9, ">>");
}

void view_show_input_word(const char *word)
{
	glmxConsole_WndDrawTextEx(input_window, 2, 0, 9, word);
}

int view_emitter_winid()
{
	return emitter_window;
}

void view_show_popwords()//显示发射器提供单词单词
{
	for (int i = 0; i < emit_word_count(); i++)
	{
		struct Word*p = emit_get_word(i);
		if (emit_get_word(i) != NULL&&p->word != NULL)
		{
			glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, p->color,p->word);
		}
	}
}

void view_clear_emitwnd()
{
	for (int i = 0; i < emit_word_count(); i++)
	{
		struct Word*p = emit_get_word(i);
		if (p != NULL&&p->word != NULL)
		{
			int len = strlen(p->word);
			char space[32];//栈空间做法
			memset(space, ' ', sizeof(char)*32);
			space[len] = '\0';
			glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, p->color, space);
		/*	int len = strlen(p->word);               堆空间做法,但是频繁的开辟小空间会产生内存碎片
			char *space = (char*)malloc(len + 1);
			for (int j = 0; j < len; j++)
			{
				space[j] = ' ';
			}
			space[len] = '\0';
			glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, p->color, space);
			free(space);*/
		}
	}
}

void view_do_killanim()
{
	for (int i = 0; i < emit_word_count(); i++)
	{
		struct Word*p = emit_get_word(i);
		if (p->killAnim != 0)
		{
			if (p->killAnim % 2 == 1)
			{
				glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, 7, p->word);
			}
			else
			{
				glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, p->color, p->word);
			}
			p->killAnim--;

			if (p->killAnim == 0)
			{
				int len = strlen(p->word);
				char space[32];
				memset(space, ' ', sizeof(char) * 32);
				space[len] = '\0';
				glmxConsole_WndDrawTextEx(emitter_window, p->x, p->y, p->color, space);
				p->word = NULL;
			}
		}
	}
	

}

5 main.c

#include"data_source.h"
#include"display.h"
#include"Launcher.h"
#include "glConsole.h"

int main()
{
		glmxConsole_Init();
		view_init();

		srand((unsigned)time(NULL));
		struct dictionary *dict;
		dict = download("D:\\text\\text.txt");
		int w, h;
		glmxConsole_GetWindowRect(view_emitter_winid(), NULL, NULL, &w, &h);
		view_show_appinfo();
		//输入缓冲
		char word[50] = { 0 };
		char *p = word;
		char val;
		//下落计时器
		int time_count = 30;
		//成功,失败,等级,速度
		int killed=0, missed=0, level=1, speed=40;
		while (1)
		{
			//发射单词
			emit_init(w, h - 1);
			emit_words(dict);
			if (time_count-- == 0)
			{
				//获取成绩记录
				view_show_score(killed, missed);
				view_show_level(level, speed);
				//下落显示
				view_show_popwords();
				view_show_input_prompt();
				view_clear_emitwnd();
				missed=missed+emit_update();
				view_show_popwords();

				time_count = 25;
			}
			while (kbhit())//kbhit按下返回非0,这里使用while循环调用getch,这样在多次输入错误答案情况下,可以在循环中快速排除
			{
				val = getch();
				if (val == '\r')
				{
					if (emit_hittest(word))
					{
						killed += 1;
					}
					memset(word, ' ', 50);
					view_show_input_word(word);
					memset(word, 0, 50);
					p = word;
				}
				else if (val == '\b')
				{
					p--;
					*p = ' ';
					view_show_input_word(word);
					*p = '\0';
				}
				else
				{
					*p = val;
					p++;
					*p = '\0';
					view_show_input_word(word);
				}						
			}
			if (killed >= level * 10)
			{
				level += 1;
				speed -= 5;
			}
			Sleep(speed);
			view_do_killanim();
		}
	return 0;
}

下载资源:
资源

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,让我们开始吧!这是一个基于C语言的背单词游戏游戏规则如下: 1. 系统会随机生成一个英文单词,用户需要输入该单词的中文翻译。 2. 如果用户输入的翻译与系统给出的答案一致,则得到一分;如果不一致,则不得分。 3. 游戏共进行10轮,最后会给出用户的得分。 下面是代码实现: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define WORD_NUM 10 // 单词总数 // 单词结构体 typedef struct { char* word; // 英文单词 char* trans; // 中文翻译 } Word; // 初始化单词 void initWords(Word* words) { words[0].word = "apple"; words[0].trans = "苹果"; words[1].word = "banana"; words[1].trans = "香蕉"; words[2].word = "cat"; words[2].trans = "猫"; words[3].word = "dog"; words[3].trans = "狗"; words[4].word = "elephant"; words[4].trans = "象"; words[5].word = "fish"; words[5].trans = "鱼"; words[6].word = "grape"; words[6].trans = "葡萄"; words[7].word = "house"; words[7].trans = "房子"; words[8].word = "ice"; words[8].trans = "冰"; words[9].word = "jacket"; words[9].trans = "夹克"; } // 随机获取一个单词 Word getRandomWord(Word* words) { int index = rand() % WORD_NUM; return words[index]; } int main() { int score = 0; // 得分 srand(time(NULL)); // 初始化随机数种子 Word words[WORD_NUM]; // 单词数组 initWords(words); // 初始化单词 printf("Let's start the game!\n"); for (int i = 1; i <= WORD_NUM; i++) { Word word = getRandomWord(words); printf("Round %d: What's the Chinese meaning of %s ?\n", i, word.word); char input[100]; scanf("%s", input); if (strcmp(input, word.trans) == 0) { printf("Right!\n"); score++; } else { printf("Wrong! The answer is %s.\n", word.trans); } } printf("Game over! Your score is %d.\n", score); return 0; } ``` 以上就是C语言单词游戏的代码实现,希望对你有帮助!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值