猜拳游戲需求分析說明書
1.引言
1.編寫目的
小時候,我們喜歡以猜拳來贏得獎勵。
現在,我們希望能開發一個猜拳的游戲。
2.定義
需求:用戶解決問題或達到目標所需的條件或功能;系統或系統部件要滿足合同、標準,規范或其它正式規定文檔所需具有的條件或權能。而且其很強的交互性及簡單易行性,可以讓人在很短時間內熟悉它的操作方法,不論用戶文化水平如何,都會很輕松的學會使用它。
2.任務概述
1.目標
2.用戶的特點
由于本程序簡單易操作,交互性好,對用戶沒什么特別要求。一般用戶經過幾分鐘練系都可以熟悉本系統的規則。
3.系統功能示例
3.需求細則
1. 對功能的規定
1.1 界面友好
屬性:必選
描述:用戶的一些操作前后做出一些必要的提示或確認。所有完成的功能都需要友好,例如:
● 增加失敗提示;
● 刪除失敗和成功提示、刪除前的確認;
● 修改失敗和成功提示、修改前/后的確認;
● 查找失敗提示、查找成功的確認;
● 退出系統時,要提示是否退出;
● 其它方面的界面問題;
1.2 用戶注冊
屬性:必選
描述:登錄界面可以用戶注冊,注冊信息包含用戶名和密碼,例如:
● 不能使用已經存在的用戶名
● 密碼不能少于6個字符
1.3 用戶登錄
屬性:必選
描述:用戶通過登錄界面進行登錄,必須輸入正確的用戶名和密碼方可進入系統,例如:
● 提示輸入用戶名
● 提示輸入密碼
● 用戶名和密碼驗證,都符合則進入系統,不然回到第一步,超過三次登錄,則提示登錄次數超過三次,退出系統
1.4 開始游戲
屬性:必選
描述:金額大于1的用戶可以開始游戲
● 下注:提示用戶下注的金額數
● 猜拳:(必須先下注,如果用戶下注金額小于等于0,不能進行游戲)與電腦進行猜拳游戲,如果猜輸了,扣除你的金額(由下注決定),勝利將隨機獲得1~5倍的下注金額獎勵
n提示:電腦隨機出拳,勝利獎勵也是隨機的,例如:
n隨機獲得1~5數字代碼:
nsrand((unsigned)time(NULL));
nint radio = rand() % 5 + 1;//radio 為隨機 1~5的整數
● 如果金額少于下注數,將不能進行猜拳,給出充值提示
1.5 充值
屬性:必選
描述:用戶可以進行充值
● 充值金額
● 充值的時候,必須輸入充值密碼(由5個字符組成,隨機包括數字0~9和所有小寫字母)
● 如果充值密碼正確,充值成功,不然充值失敗
1.6 修改用戶信息
屬性:必選
描述:用戶可以注銷(刪除)自己的賬戶、或者修改個人信息
● 修改個人信息
n 修改用戶密碼
● 注銷用戶(本人)
n 刪除用戶所有信息(刪除完畢將返回登錄界面,用戶賬號已經不存在)
1.7 查看用戶信息
屬性:必選
描述:用戶登錄成功后,可以選擇查看用戶自己信息或者所有用戶信息
● 查看用戶自己信息:(本人)用戶名 密碼 金額 勝率
● 查看所有用戶信息:所有用戶名 金額 勝率
1.8 排序
屬性:必選
描述:用戶可以對所有用戶信息進行排序操作,便于查看
● 金額排序
● 勝率排序
對性能的規定
本系統在設計方面本著方便、實用的宗旨,性能方面應遵循如下原則:
● 執行效率(時間): 軟件運行應該盡量高效;避免沒有必要的循環處理、重復處理;
● 資源損耗(空間):設計盡量節約資源(內存、數組、鏈表等);不用的資源要及時釋放;
● 初始化: 變量、數組、內存塊;
● 健壯性:不能出現野指針、內存泄露、數組越界訪問等等:
◎ 聲明指針后,立即將指針設置為NULL,防止產生“野指針”;
◎ 函數的入參必須進行有效性判斷;用戶輸入、函數返回值(如果用到的話)都要判斷;
◎ switch-case一定要有default;if-else if等后要有else,除非if滿足后返回或退出;
◎ 不允許出現goto語句;
◎ 數組的下標不要溢出。
3. 對排版的規定
● 縮進要對齊;
● 長行拆分;
● 二元操作符的前后應當加空格,包括如下操作符:
賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符,如"="、"+="">="、"<="、"+"、"*"、"%"、"&&"、"||"、"<<","^" 等;
● 空行:
◎ 聲明之后、每個函數定義結束之后都要加1行空行;
◎ 邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔;
◎ 一行代碼只做一件事情;
◎ "if"、"for"、"while"、"do"等語句自占一行,執行語句不得緊跟其后。不論執行語句有多少都要加 "{ }";
4. 對可維護性的規定
對可維護性的最終要求:別人能夠輕松上手你的代碼。
● 結構清晰:
◎ 模塊化:對界面(顯示)、菜單管理、邏輯管理、文件操作等等代碼要獨立,必須多個.c文件,用makefile;
◎ 封裝:一個函數只做一件事,函數功能要單一;一個函數不能超過50行;
避免重復、冗余代碼;
◎ 代碼塊清晰。
● 命名規范,變量名應該具有自明性:
◎ 函數的命名:
使用"動詞"或者"動詞+名詞"(動賓詞組)的形式,由一個或多個單詞組成且以小寫字母開頭,以后每個單詞之間用下劃線隔開
◎ 變量的命名與定義
應當使用"名詞"或者"形容詞+名詞",由一個或多個單詞組成且以小寫字母開頭,以后每個單詞之間用下劃線隔開。
● 注釋充分:變量、函數(包括參數、返回值)、代碼功能塊、一些復雜算法……等都需要
清晰明了地說明;
5. 對個性的規定
把項目做出個性出來。下列各項中有和比人不同之處、或很有創意,即可認為有個性。獨立設計的軟件,一般都會出現一些個性。參考、抄襲不會出現個性。
● 設計思路:包括軟件的整體架構、功能塊的設計思路、函數封裝等等;
● 功能實現:從用戶的角度,使用上發現與眾不同的地方;
● 其它方面;
c語言程序代碼:
/*
func:猜拳游戲
auth:肖喬
date:2014.02.22
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define TRUE 1
typedef struct {
charname[30]; //用戶名長度最長28
charpassword[30]; //密碼長度最長28
doublemoney;
inttimes;
intwin;
}game_t;
//清空緩存
void fflush_in();
//登錄菜單函數
void log_menu();
//功能菜單函數
void ctrl_menu();
//登錄函數
int log_in(game_t *p,int *count,int *my);
//注冊函數
int add_user(game_t *p,int *count);
//登錄控制函數
void log_ctrl(game_t *p,int *count,int*my);
//功能控制函數
void ctrl(game_t *p,int *count,int *my);
//查看函數
void look(game_t *p,int *count,int *my);
//查看菜單
void look_menu();
//查看所有
void look_all(game_t *p,int *count);
//查看個人
void look_my(game_t *p,int *my);
//初始化函數
int log_start(game_t *p,int *count);
//保存函數
int exit_save(game_t *p,int *count);
//充值函數
int pay(game_t *p,int *my);
//修改函數
void alter(game_t *p,int *count,int *my);
//修改菜單
void alter_menu();
//修改密碼
int alter_pass(game_t *p,int *my);
//注銷用戶
int del(game_t *p,int *count,int *my);
//游戲控制函數
void game_ctrl(game_t *p,int *my);
//游戲菜單
void game_menu();
//下注
int game_pay(game_t *p,int *my,double*mon);
//游戲函數
void game_play(game_t *p,int *my,double*mon);
//游戲
int game();
//排序控制函數
void cmp(game_t *p,int *count);
//排序菜單
void cmp_menu();
//按金額排序函數
int cmp_money(const void *px,const void*py);
//勝率函數
void win_times(game_t *p,int *count);
//按勝率排序函數
int cmp_win(const void *px,const void *py);
int main(){
game_tstu[100]={"\0"}; //最多100個用戶
intcount=0; //count表示用戶數
intmy=0; //my表示本人用戶下標
printf("歡迎使用猜拳游戲\n");
if(log_start(stu,&count)==0) //系統初始化
printf("系統初始化成功\n");
else
printf("系統初始化失敗\n");
log_ctrl(stu,&count,&my); //功能控制函數
return0;
}
//初始化函數 0-成功1-失敗
int log_start(game_t *p,int *count){
FILE*fp=NULL;
fp=fopen("data","a+");
if(NULL==fp)
return1;
while(fread(&p[*count],sizeof(game_t),1,fp))
*count+=1;
fclose(fp);
return0;
}
//保存函數 0-成功 1-失敗
int exit_save(game_t *p,int *count){
FILE*fp=NULL;
fp=fopen("data","w");
if(NULL==fp)
return1;;
fwrite(p,sizeof(game_t),*count,fp);
fclose(fp);
return0;
}
//功能控制函數
void ctrl(game_t *p,int *count,int *my){
intchose=0;
while(TRUE){
chose=0;
ctrl_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //查看
look(p,count,my);
break;
case2: //游戲
game_ctrl(p,my);
break;
case3: //充值
if(pay(p,my)==0)
printf("充值成功\n");
else
printf("充值失敗\n");
break;
case4: //修改
alter(p,count,my);
break;
case5: //排序
cmp(p,count);
break;
case6: //返回
return;
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//排序控制函數
void cmp(game_t *p,int *count){
intchose=0;
while(TRUE){
chose=0;
cmp_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //金額
qsort(p,*count,sizeof(game_t),cmp_money);
printf("排序成功\n");
break;
case2: //勝率
win_times(p,count);
printf("排序成功\n");
break;
case3: //返回
return;
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//勝率函數
void win_times(game_t *p,int *count){
doublewins[100]={0.0};
doublei=0,j=0;
intk=0;
for(k=0;k<*count;k++){
i=p[k].times;
j=p[k].win;
wins[k]=(i=0?0:(double)j/i*100);
}
qsort(wins,*count,sizeof(game_t),cmp_win);
}
//按勝率排序函數
int cmp_win(const void *px,const void *py){
constgame_t *p1=px;
constgame_t *p2=py;
if(p1->money>p2->money)
return-1;
if(p1->money<p2->money)
return1;
else
return0;
}
//按金額排序函數
int cmp_money(const void *px,const void*py){
constgame_t *p1=px;
constgame_t *p2=py;
if(p1->money>p2->money)
return-1;
if(p1->money<p2->money)
return1;
else
return0;
}
//排序菜單
void cmp_menu(){
printf("1.按金額排序 2.按勝率排序 3.返回\n");
}
//游戲控制函數
void game_ctrl(game_t *p,int *my){
intchose=0;
doublemon=0.0;
while(TRUE){
chose=0;
game_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //下注
if(game_pay(p,my,&mon)==0)
printf("下注成功,請開始游戲\n");
else
printf("下注失敗\n");
break;
case2: //猜拳
game_play(p,my,&mon);
break;
case3: //返回
return;
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//游戲函數
void game_play(game_t *p,int *my,double*mon){
intresult=0;
intradio=0;
if(*mon==0){
printf("請先下注\n");
return;
}
p[*my].money-=*mon;
result=game();
srand((unsigned)time(NULL));
radio=rand()%5+1;
if(result==0){
p[*my].money+=0;
printf("平局,余額%.2f\n",p[*my].money);
p[*my].times+=1;
}
elseif(result==1){
p[*my].money+=(*mon)*radio;
printf("您勝利了,獎金:%.2f余額:%.2f\n",(*mon)*radio,p[*my].money);
p[*my].times+=1;
p[*my].win+=1;
}
else{
printf("您輸了%.2lf,余額%.2lf\n",*mon,p[*my].money);
p[*my].times+=1;
}
*mon=0.0;
}
//游戲 0-平局,1-勝,-1-輸
int game(){
intmy=0;
intradio=0;
while(my!=1&&my!=2&&my!=3){
printf("1.剪刀 2.石頭 3.布\n");
printf("請選擇:");
scanf("%d",&my);
fflush_in();
switch(my){
case1:
printf("您出的是剪刀\n");
break;
case2:
printf("您出的是石頭\n");
break;
case3:
printf("您出的是布\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
srand((unsigned)time(NULL));
radio=rand()%3+1;
if(radio==1)
printf("電腦出的是剪刀\n");
elseif(radio==2)
printf("電腦出的是石頭\n");
elseif(radio==3)
printf("電腦出的是布\n");
switch(radio-my)
{
case1:
case-2:
return-1;
case2:
case-1:
return1;
default:
return0;
}
}
//下注 0-成功 1-失敗
int game_pay(game_t *p,int *my,double*mon){
doublemoney=0.0;
if(p[*my].money==0){
printf("您的余額為0.00,請充值\n");
return1;
}
printf("您的余額為:%.2lf元\n",p[*my].money);
printf("請下注:");
scanf("%lf",&money);
fflush_in();
if(money<=0){
printf("金額輸入有誤\n");
return1;
}
if(money>p[*my].money){
printf("余額不足,請充值\n");
return1;
}
*mon=money;
return0;
}
//游戲菜單
void game_menu(){
printf("1.下注 2.猜拳 3.返回\n");
}
//修改函數 0-成功 1-失敗
void alter(game_t *p,int *count,int *my){
intchose=0;
while(TRUE){
chose=0;
alter_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //改密
if(alter_pass(p,my)==0)
printf("修改密碼成功\n");
else
printf("修改密碼失敗\n");
break;
case2: //注銷
if(del(p,count,my)==0){
printf("注銷成功\n");
log_ctrl(p,count,my); //功能控制函數
}
else
printf("注銷失敗\n");
break;
case3: //返回
return;
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//注銷用戶 0-成功 1-失敗
int del(game_t *p,int *count,int *my){
charpass[30]="\0";
inti=0,chose=0;
printf("密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
if(!(strcmp(p[*my].password,pass))==0){
printf("密碼輸入錯誤\n");
return1;
}
printf("是否注銷?\n1.是 2.否\n");
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
if(chose==1){
for(i=*my;i<*count-1;i++){
strcpy(p[i].name,p[i+1].name);
strcpy(p[i].password,p[i+1].password);
p[i].money=p[i+1].money;
p[i].times=p[i+1].times;
p[i].win=p[i+1].win;
}
}
else
return1;
*count-=1;
return0;
}
//修改密碼 0-成功 1-失敗
int alter_pass(game_t *p,int *my){
charpass[30]="\0",pass2[30]="\0";
intchose=0;
printf("密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
if(!(strcmp(p[*my].password,pass))==0){
printf("密碼輸入錯誤\n");
return1;
}
printf("提示:密碼長度為6-28位\n");
printf("輸入新密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
printf("確認新密碼:");
fgets(pass2,30,stdin);
pass2[strlen(pass2)-1]='\0';
if(strcmp(pass,pass2)!=0){
printf("兩次輸入密碼不一樣\n");
return1;
}
if(strcmp(p[*my].password,pass)==0){
printf("新密碼與原密碼相同\n");
return1;
}
printf("是否改密?\n1.是 2.否\n");
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
if(chose==1)
strcpy(p[*my].password,pass);
else
return1;
return0;
}
//修改函數菜單
void alter_menu(){
printf("1.修改密碼 2.注銷 3.返回\n");
}
//充值函數
int pay(game_t *p,int *my){
inti=0;
doublemoney=0;
charpass[30]="\0";
charstr[10]="\0",str2[10]="\0";
printf("請輸入金額:");
scanf("%lf",&money);
fflush_in();
if(money<=0.0)
return1;
//驗證碼
for(i=0;i<5;i++){
srand((unsigned)time(NULL)+i);
str[i]="0123456789abcdefghihklmnopqrstuvwxyz"[rand()%36];
}
printf("充值密碼:%s",str);
printf("\n");
printf("請輸入充值密碼:");
fgets(str2,10,stdin);
str2[strlen(str2)-1]='\0';
if(strcmp(str,str2)!=0){
printf("充值密碼輸入錯誤\n");
return1;
}
printf("密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
if(!(strcmp(p[*my].password,pass))==0)
return1;
p[*my].money+=money;
return0;
}
//查看函數
void look(game_t *p,int *count,int *my){
intchose=0;
while(TRUE){
chose=0;
look_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //個人
look_my(p,my);
break;
case2: //所有
look_all(p,count);
break;
case3: //返回
return;
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//查看所有
void look_all(game_t *p,int *count){
inti=0; //i表示游戲次數
intj=0; //j表示勝利次數
intk=0;
printf("用戶\t金額\t勝率\t\n");
for(k=0;k<*count;k++){
i=p[k].times;
j=p[k].win;
printf("%s\t%.2f\t%.2lf%%\t\n",p[k].name,
p[k].money,i==0?0:(double)j/i*100);
}
}
//查看個人
void look_my(game_t *p,int *my){
inti=0; //i表示游戲次數
intj=0; //j表示勝利次數
i=p[*my].times;
j=p[*my].win;
printf("用戶名:%s\n",p[*my].name);
printf("密 碼:%s\n",p[*my].password);
printf("金 額:%.2lf\n",p[*my].money);
printf("勝 率:%.2lf%%\n",i==0?0:(double)j/i*100);
}
//查看菜單
void look_menu(){
printf("1.個人 2.所有 3.返回\n");
}
//功能菜單函數
void ctrl_menu(){
printf("1.查看 2.游戲 3.充值 4.修改 5.排序 6.返回\n");
}
//登錄控制函數
void log_ctrl(game_t *p,int *count,int*my){
intchose=0,chose2=0;
while(TRUE){
chose=0;
chose2=0;
log_menu();
printf("請選擇:");
scanf("%d",&chose);
fflush_in();
switch(chose){
case1: //登錄
if(log_in(p,count,my)==1)
printf("登錄失敗\n");
else{
printf("登錄成功\n");
ctrl(p,count,my);
}
break;
case2: //注冊
if(add_user(p,count)==1)
printf("注冊失敗\n");
else
printf("恭喜您,注冊成功\n");
break;
case3: //退出
printf("是否退出?\n1.是 2.否\n");
printf("請選擇:");
scanf("%d",&chose2);
fflush_in();
if(chose2==1){
if(exit_save(p,count)==0)
printf("信息保存成功\n");
else
printf("信息保存失敗\n");
exit(0);
}
else
log_ctrl(p,count,my);
break;
default:
printf("輸入錯誤,請重新輸入\n");
}
}
}
//登錄函數 0-成功 1-失敗
int log_in(game_t *p,int *count,int *my){
inti=0,flag=0;
charname[30]="\0",pass[30]="\0";
printf("用戶名:");
fgets(name,30,stdin);
name[strlen(name)-1]='\0';
flag=0;
for(i=0;i<*count;i++)
if(strcmp(p[i].name,name)==0){
*my=i;
flag=1;
break;
}
if(flag==0){
printf("用戶不存在\n");
return1;
}
i=0;
while(i<3){
printf("密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
if(strcmp(p[*my].password,pass)==0){
return0;
}
printf("密碼輸入錯誤,請重新輸入\n");
i++;
}
if(i==3){
printf("登錄失敗\n");
printf("登錄次數超過三次,您將退出系統\n");
if(exit_save(p,count)==0) //保存信息
printf("信息保存成功\n");
else
printf("信息保存失敗\n");
exit(1);
}
return0;
}
//注冊函數 0-成功 1-失敗
int add_user(game_t *p,int *count){
inti=0;
charname[30]="\0";
charpass[30]="\0",pass2[30]="\0";
if(*count>100){
printf("注冊用戶已滿\n");
return1;
}
printf("提示:用戶名長度為3-28\n");
printf("用戶名:");
fgets(name,30,stdin);
name[strlen(name)-1]='\0';
if(strlen(name)<3||strlen(name)>28){ //用戶名長度3-28
printf("用戶名長度不合法\n");
return1;
}
for(i=0;i<*count;i++){
if(strcmp(p[i].name,name)==0){
printf("已有同名用戶名\n");
return1;
}
}
strcpy(p[*count].name,name);
printf("提示:密碼長度6-28\n");
printf("密碼:");
fgets(pass,30,stdin);
pass[strlen(pass)-1]='\0';
if(strlen(pass)<6||
strlen(pass)>28){ //密碼長度6-28
printf("密碼長度不合法\n");
return1;
}
printf("確認密碼:");
fgets(pass2,30,stdin);
pass2[strlen(pass2)-1]='\0';
if(!(strcmp(pass,pass2))==0){
printf("兩次輸入密碼不一樣\n");
return1;
}
strcpy(p[*count].password,pass);
*count+=1;
return0;
}
//登錄菜單函數
void log_menu(){
printf("1.登錄 2.注冊 3.退出\n");
}
//清空緩存
void fflush_in(){
charc='\0';
c=getchar();
while(c!='\n'){
c=getchar();
}
}