C語言電梯模擬程序
一直以來我對電梯很感興趣,起初認爲用C語言不可能實現電梯模擬,需要多線程的支持,因此一直以來也沒有想着做。最近數據結構習題書的這道題引起了我的注意,通過幾天的努力終於實現了,先將程序的實現與大家分享出來。
在這個程序關鍵是處理好電梯運行狀態轉換與乘客進出的同步進行。好在題目要求每次輸入時要輸入下一個乘客到來的時間,使得程序變簡單了。通過一個模擬時鐘,根據模擬時鐘判斷該運行哪個函數。以下是模擬時鐘的代碼。
void DoTime(){
//此函數用於模擬時鐘
while(1){
if(Time>MaxTime)
return;
TestPeople();//兩個始終都會被調用的函數
Controler();
struct Activity* p=activity.next;
if(p==NULL){
Time=MaxTime;
}
if(p&&Time>=p->time){//取出活動隊頭的,檢測定時是否到了
activity.next=p->next;
p->fn();
free(p);
}
Time++;
}
}
在這個先不管TestPeople()、Controler()是什麼,其中activity是關鍵,它是一個鏈表在鏈表的頭部是計時器時間最小的函數,根據模擬時鐘判斷是否調用這個函數以及刪除這個節點。以下是Activity的具體定義。
typedef struct Activity{
int time;
void(*fn)(void);
struct Activity* next;
}Activity;
以及全局變量activity
Activity activity={0,NULL,NULL};
下面的代碼用於將一個函數加入activity鏈表,這是根據時鐘值從小到大插入activity的。
void AddAct(int time,void(*fn)(void)){//將一個活動加入定時器,時間到了會調用這個函數
time=Time+time; //這個函數參數必須是void,返回值也必須是void
struct Activity* act;
act=(struct Activity*)malloc(sizeof(struct Activity));
act->next=NULL;
act->fn=fn;
act->time=time;
struct Activity* p=&activity;
while(p->next!=NULL){
if(p->next->time>time)
break;
p=p->next;
}
act->next=p->next;
p->next=act;
}
一個簡單的活動加入計時器。
void Input(void){//輸入人員信息,這個需要手動調用一次,之後就根據定時器調用了
Person* p = (Person*)malloc(sizeof(Person));
int infloor,outfloor,giveuptime,intertime;
while(1){
printf("請輸入用戶的起始樓層:");
scanf("%d",&infloor);
printf("請輸入用戶的目標的樓層:");
scanf("%d",&outfloor);
printf("請輸入用戶的最長容忍時間:");
scanf("%d",&giveuptime);
printf("請輸入下一個用戶的到來時間:");
scanf("%d",&intertime);
if(!(infloor<0||infloor>MaxFloor-1||outfloor<0||outfloor>MaxFloor-1)&&(infloor!=outfloor))
break;
printf("錯誤的用戶信息錄入!\n");
}
p->Id=PersonId++;
p->GiveupTime=giveuptime+Time;
p->next=NULL;
p->OutFloor=outfloor;
if(outfloor>infloor)
CallUp[infloor]=1;
else
CallDown[infloor]=1;
AddQueue(infloor,p);
AddAct(intertime,Input);
}
這使得main函數可以寫的很簡單。
int main(){
Init();
Input();
DoTime();
return 0;
}
這樣整個電梯程序大體就搭建起來了。下面看Controler()這個函數,它就是判斷電梯的運行狀態,然後決定電梯的下一個運行狀態。AddAct()很重要,這很好的實現了一個需要時間的過程,如電梯的開門,上升,減速。
void Controler(void){
if(State==Idle||State==Stop){
if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
//當前層有請求,需要開門進出
if(CallCar[BaseFloor]==2){
CallCar[BaseFloor]=0;
State=Idle;
printf("現在在%d層,無人請求電梯!\n",BaseFloor);
return;
}
State=DoorOpening;
AddAct(DoorTime,doopendoor);
}
else{
//當前層無請求,判斷其他層請求
int whitch=GetWhere();
if(whitch==GoingUp){
State=SpeedUp;
AddAct(Accelerate,domove);
}else if(whitch==GoingDown){
State=SpeedDown;
AddAct(Accelerate,domove);
}else{
State=Idle;
if(Floor!=BaseFloor)
AddAct(OverTime,tofirst);
}
}
}
//否則電梯忙碌
return;
}
下面是一些用到宏定義以及全局變量。
#define GoingUp 1//勻速上升
#define GoingDown 2//勻速下降
#define SpeedUp 3//加速上升
#define SpeedDown 4//加速下降
#define SlowUp 5//減速上升準備停靠
#define SlowDown 6//減速下降準備停靠
#define Idle 7//空閒
#define Stop 8//停止且已關門
#define DoorOpen 9//停止且門已打開
#define DoorOpening 10
#define DoorCloseing 11
#define CloseTest 40 //電梯關門測試時間
#define OverTime 300 //電梯停候超時時間
#define Accelerate 15 //加速時間
#define UpTime 51 //上升時間
#define DownTime 61 //下降時間
#define UpDecelerate 14 //上升減速
#define DownDecelerate 23 //下降減速
#define DoorTime 20 //開門關門時間
#define InOutTime 25 //進出電梯時間
#define MaxTime 10000
#define MaxFloor 5
#define BaseFloor 1
//初始每層電梯等待隊列和棧
#define Init() ({int i;\
for(i=0;i<MaxFloor;i++){\
Stack[i].next=NULL;\
Queue[i].next=NULL;\
}\
activity.next=NULL;\
})
typedef struct Person{
int Id;
int OutFloor;
int GiveupTime;
struct Person* next;
}Person;
typedef struct Activity{
int time;
void(*fn)(void);
struct Activity* next;
}Activity;
typedef struct Person_Ele{
int Id;
struct Person_Ele* next;
}Person_Ele;
int Time=0;
int CallUp[MaxFloor]={0,};
int CallDown[MaxFloor]={0,};
int CallCar[MaxFloor]={0,};
int Floor=BaseFloor;
int State=Idle;
int PersonId=0;
Activity activity={0,NULL,NULL};
Person_Ele Stack[5]={0,};
Person Queue[5]={0,};
下面是Controler()具體調用細節。
void testinout(void){//檢測有無人進出
if(Queue[Floor].next||Stack[Floor].next)
AddAct(CloseTest,testinout);
else{
State=DoorCloseing;
CallUp[Floor]=0;
CallDown[Floor]=0;
CallCar[Floor]=0;
AddAct(DoorTime,doclosedoor);
}
}
void doclosedoor(void){//電梯門關了
printf("電梯門關了\n");
State=Stop;
}
void doopendoor(void){//打開電梯門
printf("電梯門開了\n");
State=DoorOpen;//門打開了
AddAct(CloseTest,testinout);
if(Stack[Floor].next)
AddAct(InOutTime,doout);
else{//沒人出,就看有沒有進的
if(Queue[Floor].next)
AddAct(InOutTime,doin);
}
}
void doout(void){
//根據棧出人,如果沒有看是否有人進
if(Stack[Floor].next){
Person_Ele* p=Stack[Floor].next;
Stack[Floor].next=p->next;
;//顯示信息
printf("用戶%d走出電梯\n",p->Id);
free(p);
}
if(Stack[Floor].next){
AddAct(InOutTime,doout);
}else{
if(Queue[Floor].next)
AddAct(InOutTime,doin);
}
}
void doin(void){//人進入電梯,這裏不用關電梯門它會定時關的
Person* p=Queue[Floor].next;
if(p){
Queue[Floor].next=p->next;
Person_Ele* pe=(Person_Ele*)malloc(sizeof(Person_Ele));
int in=p->OutFloor;
CallCar[in]=1;//置位請求
pe->next=Stack[in].next;
pe->Id=p->Id;
Stack[in].next=pe;
printf("用戶%d走入電梯\n",p->Id);
free(p);
}
if(Queue[Floor].next){
AddAct(InOutTime,doin);
}
}
int GetWhere(void){
static int old=0;//保存上一次電梯的方向,保證電梯儘可能在一個方向走
int isup=0,isdown=0;
int i;
for(i=Floor+1;i<MaxFloor;i++){
if(CallDown[i]||CallUp[i]||CallCar[i])
isup=1;
}
for(i=Floor-1;i>=0;i--){
if(CallDown[i]||CallUp[i]||CallCar[i])
isdown=1;
}
if(isup==0&&isdown==0){
return 0;
}
if(old==0){
if(isdown) old=GoingDown;
if(isup) old=GoingUp;
return old;
}
if(old==GoingUp&&isup)
return old;
else if(old==GoingDown&&isdown)
return old;
else if(isdown)
old=GoingDown;
else if(isup)
old=GoingUp;
else
printf("在選擇方向時發生錯誤!\n");
return old;
}
void tofirst(void){//去第一層
if(State!=Idle||Floor==BaseFloor)
return;
printf("長時間沒人請求電梯!將進入%d層\n",BaseFloor);
CallCar[BaseFloor]=2;//給電梯一個虛擬的去1層的請求,這並不會開門
}
void doslow(void){//電梯停了
printf("電梯停了,當前層是%d\n",Floor);
State=Stop;
}
void doup(void){
Floor++;
printf("電梯正在上升!現已到了%d層!\n",Floor);
if(CallDown[Floor]||CallUp[Floor]||CallCar[Floor]){
State=SlowUp;
AddAct(UpDecelerate,doslow);
}else{
if(Floor==MaxFloor-1){
State=SlowUp;
AddAct(UpDecelerate,doslow);
}else{
AddAct(UpTime,doup);
}
}
}
void dodown(void){
Floor--;
printf("電梯正在下降!現已到了%d層!\n",Floor);
if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
State=SlowDown;
AddAct(DownDecelerate,doslow);
}else{
if(Floor==0){
State=SlowDown;
AddAct(DownDecelerate,doslow);
}else{
AddAct(DownTime,dodown);
}
}
}
void domove(void){//加速完成,將進入正常速度
if(State==SpeedUp){
printf("電梯已加速上升!\n");
State=GoingUp;
AddAct(UpTime,doup);
}else{
printf("電梯已加速下降!\n");
State=GoingDown;
AddAct(DownTime,dodown);
}
}
他們之間儘管沒有直接調用關係,但都是通過AddAct()聯繫起來的。下面是TestPeople()的實現,我知道DoTime()每次都調用它嚴重影響效率,但是這需要修改AddAct(),增加複雜性。
void TestPeople(){//這是檢測每層隊列是否有人放棄,有人放棄就將他踢出隊列
int i;//這個函數每個時間都會被調用,效率相對較低
for(i=0;i<MaxFloor;i++){
Person* p=Queue[i].next;
Person* q=&Queue[i];
if(p==NULL)
continue;
while(p!=NULL){
if(p->GiveupTime<=Time){
if(Floor=i&&(State>=Idle))
break;
q->next=p->next;
printf("用戶%d放棄了等待!\n",p->Id);
free(p);
p=q->next;
continue;
}
q=p;
p=p->next;
}
}
}
下面是所有的源代碼:
Dianti.h
#ifndef _DIANTI_H_
#define _DIANTI_H_
#include <stdio.h>
#include <stdlib.h>
#define GoingUp 1//勻速上升
#define GoingDown 2//勻速下降
#define SpeedUp 3//加速上升
#define SpeedDown 4//加速下降
#define SlowUp 5//減速上升準備停靠
#define SlowDown 6//減速下降準備停靠
#define Idle 7//空閒
#define Stop 8//停止且已關門
#define DoorOpen 9//停止且門已打開
#define DoorOpening 10
#define DoorCloseing 11
#define CloseTest 40 //電梯關門測試時間
#define OverTime 300 //電梯停候超時時間
#define Accelerate 15 //加速時間
#define UpTime 51 //上升時間
#define DownTime 61 //下降時間
#define UpDecelerate 14 //上升減速
#define DownDecelerate 23 //下降減速
#define DoorTime 20 //開門關門時間
#define InOutTime 25 //進出電梯時間
#define MaxTime 10000
#define MaxFloor 5
#define BaseFloor 1
//初始每層電梯等待隊列和棧
#define Init() ({int i;\
for(i=0;i<MaxFloor;i++){\
Stack[i].next=NULL;\
Queue[i].next=NULL;\
}\
activity.next=NULL;\
})
typedef struct Person{
int Id;
int OutFloor;
int GiveupTime;
struct Person* next;
}Person;
typedef struct Activity{
int time;
void(*fn)(void);
struct Activity* next;
}Activity;
typedef struct Person_Ele{
int Id;
struct Person_Ele* next;
}Person_Ele;
int AddQueue(int floor,struct Person* p);
void AddAct(int time,void(*fn)(void));
void TestPeople();
void DoTime();
void Input(void);
//以下函數與電梯決策有關
void testinout(void);
void doclosedoor(void);
void doopendoor(void);
void doout(void);
void doin(void);
void doup(void);
void dodown(void);
void domove(void);
void doslow(void);
void tofirst();
int GetWhere(void);
#endif
Dianti.c
#include "Dianti.h"
int Time=0;
int CallUp[MaxFloor]={0,};
int CallDown[MaxFloor]={0,};
int CallCar[MaxFloor]={0,};
int Floor=BaseFloor;
int State=Idle;
int PersonId=0;
Activity activity={0,NULL,NULL};
Person_Ele Stack[5]={0,};
Person Queue[5]={0,};
int main(){
Init();
Input();
DoTime();
return 0;
}
int AddQueue(int floor,Person* p){//加入相應層的客戶等待隊列
Person* tmp=&Queue[floor];//這始終加在鏈表的最後一位,
while(tmp->next!=NULL){
tmp=tmp->next;
}
tmp->next=p;
return 0;
}
void AddAct(int time,void(*fn)(void)){//將一個活動加入定時器,時間到了會調用這個函數
time=Time+time; //這個函數參數必須是void,返回值也必須是void
struct Activity* act;
act=(struct Activity*)malloc(sizeof(struct Activity));
act->next=NULL;
act->fn=fn;
act->time=time;
struct Activity* p=&activity;
while(p->next!=NULL){
if(p->next->time>time)
break;
p=p->next;
}
act->next=p->next;
p->next=act;
}
void TestPeople(){//這是檢測每層隊列是否有人放棄,有人放棄就將他踢出隊列
int i;//這個函數每個時間都會被調用,效率相對較低
for(i=0;i<MaxFloor;i++){
Person* p=Queue[i].next;
Person* q=&Queue[i];
if(p==NULL)
continue;
while(p!=NULL){
if(p->GiveupTime<=Time){
if(Floor=i&&(State>=Idle))
break;
q->next=p->next;
printf("用戶%d放棄了等待!\n",p->Id);
free(p);
p=q->next;
continue;
}
q=p;
p=p->next;
}
}
}
void Input(void){//輸入人員信息,這個需要手動調用一次,之後就根據定時器調用了
Person* p = (Person*)malloc(sizeof(Person));
int infloor,outfloor,giveuptime,intertime;
while(1){
printf("請輸入用戶的起始樓層:");
scanf("%d",&infloor);
printf("請輸入用戶的目標的樓層:");
scanf("%d",&outfloor);
printf("請輸入用戶的最長容忍時間:");
scanf("%d",&giveuptime);
printf("請輸入下一個用戶的到來時間:");
scanf("%d",&intertime);
if(!(infloor<0||infloor>MaxFloor-1||outfloor<0||outfloor>MaxFloor-1)&&(infloor!=outfloor))
break;
printf("錯誤的用戶信息錄入!\n");
}
p->Id=PersonId++;
p->GiveupTime=giveuptime+Time;
p->next=NULL;
p->OutFloor=outfloor;
if(outfloor>infloor)
CallUp[infloor]=1;
else
CallDown[infloor]=1;
AddQueue(infloor,p);
AddAct(intertime,Input);
}
void testinout(void){//檢測有無人進出
if(Queue[Floor].next||Stack[Floor].next)
AddAct(CloseTest,testinout);
else{
State=DoorCloseing;
CallUp[Floor]=0;
CallDown[Floor]=0;
CallCar[Floor]=0;
AddAct(DoorTime,doclosedoor);
}
}
void doclosedoor(void){//電梯門關了
printf("電梯門關了\n");
State=Stop;
}
void doopendoor(void){//打開電梯門
printf("電梯門開了\n");
State=DoorOpen;//門打開了
AddAct(CloseTest,testinout);
if(Stack[Floor].next)
AddAct(InOutTime,doout);
else{//沒人出,就看有沒有進的
if(Queue[Floor].next)
AddAct(InOutTime,doin);
}
}
void doout(void){
//根據棧出人,如果沒有看是否有人進
if(Stack[Floor].next){
Person_Ele* p=Stack[Floor].next;
Stack[Floor].next=p->next;
;//顯示信息
printf("用戶%d走出電梯\n",p->Id);
free(p);
}
if(Stack[Floor].next){
AddAct(InOutTime,doout);
}else{
if(Queue[Floor].next)
AddAct(InOutTime,doin);
}
}
void doin(void){//人進入電梯,這裏不用關電梯門它會定時關的
Person* p=Queue[Floor].next;
if(p){
Queue[Floor].next=p->next;
Person_Ele* pe=(Person_Ele*)malloc(sizeof(Person_Ele));
int in=p->OutFloor;
CallCar[in]=1;//置位請求
pe->next=Stack[in].next;
pe->Id=p->Id;
Stack[in].next=pe;
printf("用戶%d走入電梯\n",p->Id);
free(p);
}
if(Queue[Floor].next){
AddAct(InOutTime,doin);
}
}
int GetWhere(void){
static int old=0;//保存上一次電梯的方向,保證電梯儘可能在一個方向走
int isup=0,isdown=0;
int i;
for(i=Floor+1;i<MaxFloor;i++){
if(CallDown[i]||CallUp[i]||CallCar[i])
isup=1;
}
for(i=Floor-1;i>=0;i--){
if(CallDown[i]||CallUp[i]||CallCar[i])
isdown=1;
}
if(isup==0&&isdown==0){
return 0;
}
if(old==0){
if(isdown) old=GoingDown;
if(isup) old=GoingUp;
return old;
}
if(old==GoingUp&&isup)
return old;
else if(old==GoingDown&&isdown)
return old;
else if(isdown)
old=GoingDown;
else if(isup)
old=GoingUp;
else
printf("在選擇方向時發生錯誤!\n");
return old;
}
void tofirst(void){//去第一層
if(State!=Idle||Floor==BaseFloor)
return;
printf("長時間沒人請求電梯!將進入%d層\n",BaseFloor);
CallCar[BaseFloor]=2;//給電梯一個虛擬的去1層的請求,這並不會開門
}
void doslow(void){//電梯停了
printf("電梯停了,當前層是%d\n",Floor);
State=Stop;
}
void doup(void){
Floor++;
printf("電梯正在上升!現已到了%d層!\n",Floor);
if(CallDown[Floor]||CallUp[Floor]||CallCar[Floor]){
State=SlowUp;
AddAct(UpDecelerate,doslow);
}else{
if(Floor==MaxFloor-1){
State=SlowUp;
AddAct(UpDecelerate,doslow);
}else{
AddAct(UpTime,doup);
}
}
}
void dodown(void){
Floor--;
printf("電梯正在下降!現已到了%d層!\n",Floor);
if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
State=SlowDown;
AddAct(DownDecelerate,doslow);
}else{
if(Floor==0){
State=SlowDown;
AddAct(DownDecelerate,doslow);
}else{
AddAct(DownTime,dodown);
}
}
}
void domove(void){//加速完成,將進入正常速度
if(State==SpeedUp){
printf("電梯已加速上升!\n");
State=GoingUp;
AddAct(UpTime,doup);
}else{
printf("電梯已加速下降!\n");
State=GoingDown;
AddAct(DownTime,dodown);
}
}
void Controler(void){
if(State==Idle||State==Stop){
if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
//當前層有請求,需要開門進出
if(CallCar[BaseFloor]==2){
CallCar[BaseFloor]=0;
State=Idle;
printf("現在在%d層,無人請求電梯!\n",BaseFloor);
return;
}
State=DoorOpening;
AddAct(DoorTime,doopendoor);
}
else{
//當前層無請求,判斷其他層請求
int whitch=GetWhere();
if(whitch==GoingUp){
State=SpeedUp;
AddAct(Accelerate,domove);
}else if(whitch==GoingDown){
State=SpeedDown;
AddAct(Accelerate,domove);
}else{
State=Idle;
if(Floor!=BaseFloor)
AddAct(OverTime,tofirst);
}
}
}
//否則電梯忙碌
return;
}
void DoTime(){
//此函數用於模擬時鐘
while(1){
if(Time>MaxTime)
return;
TestPeople();//兩個始終都會被調用的函數
Controler();
struct Activity* p=activity.next;
if(p==NULL){
Time=MaxTime;
}
if(p&&Time>=p->time){//取出活動隊頭的,檢測定時是否到了
activity.next=p->next;
p->fn();
free(p);
}
Time++;
}
}
附程序運行結果: