Constant Story "Bank of Zork"; Constant Headline "^An Interactive Queueing Model^\ Copyright (c) 1997 by David Wildstrom^"; Constant DEBUG; Constant MANUAL_PRONOUNS; Global CID=1; Global SID=1; Constant MaxTellers=31; Constant MaxCustomers=150; Global WaitTimes=0; Global CustsServed=0; Array ServerUse table 100; Array ServerLive table 100; Global rhetoricflag=0; #IFDEF DEBUG; Replace XPurloinSub; Replace XAbstractSub; #ENDIF; Replace PronounNotice; Include "Parser"; Include "VerbLib"; Class Customer(MaxCustomers) with parse_name [i j k isnum; isnum=0; i=0; if (self has male) j='man'; else j='woman'; for(::) { k=NextWord(); wn--; if (k=='customer' or 'client' or 'person' or j) i++; else if (TryNumber(wn)==self.number) { i++; isnum=1;} else {wn++; return (i*isnum);} wn++; } ], before [; Make:<>; Attack: remove self; Customer.destroy(self); "The customer falls dead by your hand. Several GUE \ custodial staff wander in and remove the body.";], short_name [; print "Customer ", self.number; rtrue;], desc_head, description [; if (self in Queue) print_ret (string) self.desc_head, "is standing in line."; else print_ret (string) self.desc_head,"is at ",(the) parent(self),"."; ], time 0, create [; self.number=CID; CID++; self.time=turns; if (random(2)==1) { give self ~female male ~neuter; self.desc_head="He "; } else { give self female ~male ~neuter; self.desc_head="She "; } move self to Queue; print_ret "Customer ",self.number," enters the Bank."; ], destroy [; PronounFlush(self); ], life [; Ask, Tell, Answer: "The customer pretends not to hear you."; ], orders [; "The customer pretends not to hear you."; ], number, has proper animate scenery concealed; Class Teller(MaxTellers) with parse_name [i j isnum; isnum=0; i=0; for(::) { j=NextWord(); wn--; if (j=='teller' or 'server' or 'clerk' or 'man' or 'woman' or 'epicene' or 'gnome' or 'zurich' or 'window' or 'of') i++; else if (TryNumber(wn)==self.number) {i++; isnum=1;} else {wn++; return (i*isnum);} wn++; } ], short_name [; print "Teller ", self.number; rtrue;], before [i; Make:<>; Attack: remove self; print "The teller falls backwards and lands ungracefully \ behind the window. Seconds later, a screen is \ pulled across the window by an unseen hand."; if (child(self)~=nothing) { i=child(self); print " ",(The) i," leaves the bank disgusted.^"; remove i; Customer.destroy(i);} else print "^"; StopDaemon(self); Teller.destroy(self); return true; ], create [; StartDaemon(self); self.number=SID; SID++; move self to Bank; ], destroy [; PronounFlush(self); ], description [; print "The teller is a completely average epicene gnome of \ Zurich (FrobozzCo supply catalog #423126709)"; if (child(self)~=nothing) print ", currently serving ",(the) child(self); "."; ], daemon [i j; (ServerLive-->(self.number))++; if (child(self)~=nothing) (ServerUse-->(self.number))++; if ((child(self)~=nothing) && (random(Job_Timer.number)==1)) { print "Customer ",child(self).number," finishes his business and leaves the \ Bank. Teller ",self.number," is now available.^"; i=child(self); remove i; Customer.destroy(i); } if ((child(self)==nothing) && (child(Queue)~=nothing)) { i=child(Queue); while (sibling(i)~=nothing) i=sibling(i); j=turns-i.time; CustsServed++; WaitTimes=WaitTimes+j; print "Customer ",i.number," walks up to Teller ",self.number,", after waiting ",j," turns.^"; move i to self; } ], life [; Ask, Tell, Answer: "The teller pretends not to hear you."; ], number, orders [; "The teller pretends not to hear you."; ], has proper male neuter female animate container open scenery concealed; Object Bank "Bank of Zork" with name "door" "doors""carpet" "rug" "carpeting", description "Huge glass doors flank the southern exit of the awe-inspiring \ Bank of Zork. Plush red carpeting runs the full length of this tremendous hall \ up to the narrow, glass windows lining the northern end of the hall. From all \ around, although not emanating from a clear source, you hear the muted ringing \ of telephones and taps of typing.", before [; Go: if (noun==s_obj) {rhetoricflag=2; "Leaving so soon?";} if (noun==n_obj) "The windows are locked and reinforced against burglars like \ you."; Exit: rhetoricflag=2; "Leaving so soon?"; Yes: if (rhetoricflag>0) "No, you're not. I'm the game and you're just the player. \ So if I don't want you to leave, you don't leave. If you \ don't like it, then just quit. But you're not leaving."; No: if (rhetoricflag>0) "Good."; ], daemon [i; rhetoricflag--; if (random(Arrival_Timer.number)==1) { if (Customer.remaining()==0) "A customer almost enters the bank, but thinks better of \ it after seeing the length of the line."; i=Customer.create();} ], has light; Object -> Queue "queue" with name "queue" "line" "posts" "post" "rope", initial [; if (children(self)==0) "An empty grid of posts and rope \ indicates a queue."; if (children(self)<5) "A queue contains a few people here."; if (children(self)<10) "A queue contains several people here."; "A queue contains many people here."; ], before [; Enter: "You aren't a customer."; Search: <>; ], description [j x; if (children(self)==0) "There are no customers in the queue."; if (children(self)==1) { print "The customer currently in the queue is numbered ", child(self).number; "."; } print "The customers currently in the queue are numbered "; if (children(self)==2) { j=0; objectloop (x in self) { j++; if (j==1) print x.number," and "; else print x.number; } "."; } j=0; objectloop (x in self) { j++; if (j Gen_Customer "customers" with name "customer" "client" "person" "man" "woman" "customers" "clients" "people" "men" "women", description [; <>;], before [i j x; Make: print "With a crackle of eldritch energy...^"; if (Customer.remaining()==0) "A customer almost enters the bank, but thinks better of \ it after seeing the length of the line."; i=Customer.create(); rtrue; default: j=0; objectloop (x ofclass Customer) if (x.number~=0) j++; if (j==0) "There are no customers in the bank."; print "Please refer to customers by number. "; if (j==1) { print "The customer currently in the Bank is numbered "; objectloop (x ofclass Customer) { if (x.number~=0) print x.number; } "."; } if (j==2) { print "The customers currently in the Bank are numbered "; i=0; objectloop (x ofclass Customer) { if (x.number~=0) { i++; if (i==1) print x.number," and "; else print x.number; } } "."; } print "The customers currently in the Bank are numbered "; i=0; objectloop (x ofclass Customer) { if (x.number~=0) { i++; if (i Non_Customer "nonexistent customer" with parse_name [i k isnum; isnum=0; i=0; for(::) { k=NextWordStopped(); wn--; if (k=='customer' or 'client' or 'person' or 'man' or 'woman') i++; else if ((k~=-1) && (TryNumber(wn)~=-1000)) { i++; isnum=1;} else {wn++; return (i*isnum);} print k," ",i,"^"; wn++; } ], before [; Make:<>; default: "That customer is not in the Bank."; ], has static scenery concealed; Object -> Gen_Teller "tellers" with name "teller" "server" "clerk" "person" "man" "woman" "tellers" "servers" "window" "windows" "people" "men" "women" "gnomes" "gnome" "epicene" "of" "zurich", description [; <>;], before [i j x; Make: print "With a crackle of eldritch energy...^"; if (Teller.remaining()==0) "A teller window tries to form out of a \ vortex in time and space, but doesn't quite materialize."; x=Teller.create(); "A new teller window slides open."; default: j=0; objectloop (x ofclass Teller) if (x.number~=0) j++; if (j==0) "There are no tellers in the bank."; print "Please refer to tellers by number. "; if (j==1) { print "The teller currently in the Bank is numbered "; objectloop (x ofclass Teller) { if (x.number~=0) print x.number; } "."; } if (j==2) { print "The tellers currently in the Bank are numbered "; i=0; objectloop (x ofclass Teller) { if (x.number~=0) { i++; if (i==1) print x.number," and "; else print x.number; } } "."; } print "The tellers currently in the Bank are numbered "; i=0; objectloop (x ofclass Teller) { if (x.number~=0) { i++; if (i Non_Teller "nonexistent teller" with parse_name [i j isnum; isnum=0; i=0; for(::) { j=NextWordStopped(); wn--; if (j=='teller' or 'server' or 'clerk' or 'man' or 'woman' or 'epicene' or 'gnome' or 'zurich' or 'window' or 'of') i++; else if ((j~=-1) && (TryNumber(wn)~=-1000)) {i++; isnum=1;} else {wn++; return (i*isnum);} wn++; } ], before [; Make:<>; default: "That teller is not in the Bank."; ], has static scenery concealed; Object -> Arrival_Timer "Arrival Timer" with name "arrival" "timer" "frequency" "control", number 2, before [; AdjustSetting: self.number=second; if (second~=0) print_ret "Arrivals are now every ",second," time steps."; "Arrivals are disabled."; default: "The arrival timer can only be ~set~."; ], has static scenery concealed; Object -> Job_Timer "Job Timer" with name "job" "timer" "frequency" "control" "length" "end", number 5, before [; AdjustSetting: self.number=second; if (second~=0) print_ret "Jobs now take, on average, ",second," time steps."; "Jobs now take infinitely long."; default: "The job timer can only be ~set~."; ], has static scenery concealed; Object notepad "notepad" with name "notepad" "pad" "note" "notes" "paper" "papers" "usage", description [i; print "The daily statistics are as follows:^"; font off; print "Customers served: ",CustsServed,"^"; if (CustsServed~=0) print "Average customer wait time: ",WaitTimes/CustsServed,".", ((WaitTimes*10)/CustsServed)%10,"^^"; for(i=1:i<=SID:i++) if ((ServerLive-->i)~=0) { print "Teller "; if ((SID>=10) && (i<10)) print " "; print i, " utilization rate: "; if (SID<10) print " "; print ((ServerUse-->i)*100)/(ServerLive-->i),"%^"; } font on; "^This is another fine product of the Frobozz Magic Paper Company."; ]; [ChooseObjects obj code; if (code<2) { if (obj~=Queue) return 0; rfalse;} if (obj ofclass Customer or Teller) return 1; if ((obj==Gen_Teller) && (Teller.remaining()==MaxTellers)) return 1; if ((obj==Gen_Customer) && (Customer.remaining()==MaxCustomers)) return 1; if (obj==Non_Teller or Non_Customer) return 0; return 5; ]; [Initialise i; location=Bank; i=Teller.create(); i=Teller.create(); i=Teller.create(); move notepad to player; StartDaemon(Bank); ]; ! Some special hermaphrodite control. [ PronounNotice obj x bm; if (obj == player) return; #ifdef EnglishNaturalLanguage; PronounOldEnglish(); #endif; if (obj ofclass Teller) bm = $$111000000000; else if (obj==Non_Teller or Non_Customer) bm = 0; else bm = PowersOfTwo_TB-->(GetGNAOfObject(obj)); #ifdef DEBUG; if (parser_trace>=1) print "[Pronoun Notice Bitmask ",bm,"]^"; #endif; for (x = 1 : x <= LanguagePronouns-->0: x = x+3) if (bm & (LanguagePronouns-->(x+1)) ~= 0) LanguagePronouns-->(x+2) = obj; #ifdef EnglishNaturalLanguage; itobj = PronounValue('it'); old_itobj = itobj; himobj = PronounValue('him'); old_himobj = himobj; herobj = PronounValue('her'); old_herobj = herobj; #endif; ]; ! And dynamic object control.... [PronounFlush obj x; for (x = 1 : x <= LanguagePronouns-->0: x = x+3) if ((LanguagePronouns-->(x+2))==obj) LanguagePronouns-->(x+2)=NULL; #ifdef EnglishNaturalLanguage; itobj = PronounValue('it'); old_itobj = itobj; himobj = PronounValue('him'); old_himobj = himobj; herobj = PronounValue('her'); old_herobj = herobj; #endif; ]; #IFDEF DEBUG; [XPurloinSub; "[The purloin command has been specifically disabled in this program.]"; ]; [XAbstractSub; "[The abstract command has been specifically disabled in this program.]"; ]; #ENDIF; Include "Grammar"; [AboutSub; "The Bank of Zork was born as a project for a high school modeling and simulation class. \ This particular project was eventually completed in Pascal, due mostly to limitations on \ floating-point representations in Inform. Since then, I have turned the queueing model core \ (which was by far one of the simplest parts of the project) into a full-fledged parser-hacking \ exercise. Some particularly interesting hacks are hermaphroditic tellers, numbered people \ (a code snippet stolen shamelessly from Graham Nelson's ~Balances~), pronoun management for \ disappearing objects, and a few other interesting exercises. This is my first full program in \ Inform, so I'm glad it was such a learning experience. Below is a little more information on \ specifics of this project.^^\ VERBS OF NOTE:^\ If you ~make~, ~summon~, or a few other synonyms, you can create tellers or customers. The same \ is true, in reverse, of ~kill~, ~destroy~, and its ilk. You can also ~set~ the simulation \ parameters, the job timer and arrival timer.^^\ FEATURES (PATCHED BUGS):^\ No more than 31 tellers or 150 customers can be active at once. If there is an arrival and/or \ summoning to exceed this number, the program will reply in form.^^\ BUGS:^\ After about 367 active turns, the teller statistics on the notepad will become unreliable as \ integers overflow . The same is true of the tenths digit of the customer wait time after \ 3678 turns. I don't really see any way to fix this bug without recreating integer arithmetic.^^\ FEEDBACK:^\ If you've found a particularly interesting bug or typo or strangeness of this program, or just \ feel like telling me something, you can write me at dwildstr@@64mbhs.edu or dwildstr@@64mit.edu. I'm \ not sure how long either of these accounts will be active: wait for release 2 for deatails."; ]; [MakeSub; "That isn't something you can create.";]; [MakeHelpSub; "You cannot make a numbered object. Please refer to objects in general \ (i.e. ~make teller~ instead of ~make teller 4~).";]; [AdjustSettingSub; "That isn't an adjustable simulation parameter.";]; [AdjustHelpSub; "You can only set simulation parameters to numbers.";]; Verb meta "about" "help" "author" "info" "information" * special -> About; Verb "create" "make" "summon" "construct" "build" * noun -> Make * noun number -> MakeHelp; Extend "set" first * noun "to" number -> AdjustSetting * noun "to" special -> AdjustHelp;