/* ------------------------------------------------------------------------- */ /* よしのやぎゅどーん Java版 */ /* Copyright (C) 2003 まかまか般若波羅蜜 */ /* Queue,DoubleVectorQueue,EmptyQueueExceptionクラスは */ /* http://www.masatic.com/ masatic さんのものを利用 */ /* 突貫でつくったので処理がいい加減です。ご了承下さい… */ /* ------------------------------------------------------------------------- */ /* */ import java.awt.*; import java.awt.event.*; import java.applet.*; public class Yosinoya extends Applet implements Runnable { static boolean isAlive; // すでに起動している static boolean Run; // スレッドの動作開始・終了フラグ static Graphics G; static Image img; static TextArea textarea; static final int width = 500; static final int height = 400; static final int column = 60; static final int row = 20; static final boolean GO = true; static final boolean STOP = false; /* 外部パラメータのチェック */ final int MaxOfKeeper = 50; // 最大店員数 final int MaxOfVisitor = 200; // 最大客数 final int MaxCapacity = 100; // 最大店内容量 final double MaxOpenWait = 20; // 最大開店から注文受け付けタイムラグ(sec) final double MaxInterval = 30; // 最大客の来る間隔(sec) final double MaxCookTime = 10; // 最大調理時間(sec) /* 外部パラメータの格納 */ int NumOfKeeper; // 店員数 int NumOfVisitor; // 全客数 int Capacity; // 店内容量 double OpenWait; // 開店から注文受け付け開始までの時間(sec) double VInterval; // 客の来る間隔(sec) double CookTime; // 調理時間(sec) Thread main = null; ShopKeeper[] keeper = new ShopKeeper[50]; // 店員スレッドの格納 Visitor[] visitor = new Visitor[200]; // 客スレッドの格納 DoubleVectorQueue order = new DoubleVectorQueue(); // 注文品キュー public void init() { /* 初期化処理 */ if(Yosinoya.isAlive){ System.exit(0); } else{ chcekParam(); Yosinoya.isAlive = true; img = createImage(width,height); G = img.getGraphics(); add( textarea = new TextArea("",row,column) ); Yosigyu.setTotalBody( NumOfVisitor ); // 残り客数の設定 Yosigyu.setCapacity( Capacity ); // 店内容量の設定 } } public void chcekParam(){ NumOfKeeper = Integer.parseInt( getParameter("shopkeeper") ); NumOfVisitor = Integer.parseInt( getParameter("visitor") ); OpenWait = Integer.parseInt( getParameter("openwait") ); VInterval = Integer.parseInt( getParameter("interval") ); Capacity = Integer.parseInt( getParameter("capacity") ); CookTime = Integer.parseInt( getParameter("cooktime") ); if(NumOfKeeper > MaxOfKeeper) { NumOfKeeper = MaxOfKeeper; } if(NumOfKeeper <= 0) { NumOfKeeper = 1; } if(NumOfVisitor > MaxOfVisitor){ NumOfVisitor = MaxOfVisitor; } if(NumOfVisitor <= 0) { NumOfVisitor = 1; } if(OpenWait > MaxOpenWait) { OpenWait = MaxOpenWait; } if(VInterval > MaxInterval) { VInterval = MaxInterval; } if(Capacity > MaxCapacity) { Capacity = MaxCapacity; } if(Capacity <= 0) { Capacity = 1; } if(CookTime > MaxCookTime) { CookTime = MaxCookTime; } } public void run() { // 実行 long time; // 客の総待ち時間を格納 repaint(); keeperInit(keeper,NumOfKeeper); Run = GO; visitorInit(visitor,NumOfVisitor); synchronized (Yosigyu.isRemained) { try { Yosigyu.isRemained.wait(); // 全客終了シグナルを待つ } catch (InterruptedException e){ } } Run = STOP; time = joinData(visitor,NumOfVisitor); showResult(time,Visitor.maxWait,NumOfVisitor); Yosinoya.isAlive = false; } /* 店員の生成 */ private void keeperInit( ShopKeeper[] keeper, int max ) { ShopKeeper.setBodyCount(1); for(int i = 0; i < max; i++){ sleep(0.1); keeper[i] = new ShopKeeper(order,OpenWait,CookTime); } } /* 客の生成 */ private void visitorInit( Visitor[] visitor, int max ) { Visitor.setBodyCount(1); for(int i = 0; i < max; i++){ while( Yosigyu.isFilled() ){ sleep(1); } sleep( VInterval ); synchronized (Yosigyu.lock){ visitor[i] = new Visitor(order); Yosigyu.setInnerSpace( Yosigyu.getInnerSpace() + 1 ); } } } private long joinData( Visitor[] visitor, int max ){ int i; long time = 0; for(i = 0; i < max; i++){ time += visitor[i].time; visitor[i] = null; } return time; } private void showResult(double sumTime, double maxWait, int num){ this.print( "\n---- 閉店 ----\n" ); this.print( "総待ち時間\t" + sumTime /1000 ); this.print( "最大待ち時間\t" + maxWait / 1000 ); this.print( "客一人あたりの平均待ち時間 " + sumTime / (num * 1000) + " " ); } public void start() { if(main == null){ main = new Thread(this); main.start(); } } public void update(Graphics g) { paint(g); } public void paint(Graphics g){ g.drawImage(img, width, height, this); } synchronized public static void print( String str ){ textarea.append( str + "\n" ); } public void sleep(double num){ try { Thread.sleep( (int)(num * 1000) ); } catch (InterruptedException e){ } } public static void main(String[] args) { Frame frame = new Frame("YosinoyaGyudooon"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { System.exit(0); } }); Applet applet = new Yosinoya(); applet.init(); frame.add(applet); frame.setSize(width,height); frame.show(); applet.start(); } } /* ------------------ お店 ----------------------------------------------- */ /* クラスデータ : メニュー、実行中の客の数、店内キャパと店内スペース */ /* ------------------------------------------------------------------------ */ class Yosigyu{ static String menu[] = {"牛丼並","牛皿","牛鮭定食","牛丼大盛"}; static int innerSpace = 0; static int innerCapacity; static int Count; // 客残存数 static Object lock = new Object(); // ロック用オブジェクト static Object isRemained = new Object(); // 0になるとcond_signalする /* メンバ */ String name; int id; int check; boolean signal; /* コンストラクタ */ public Yosigyu( int id ) { // 客の注文を管理 this.id = id; // 客のid check = 1; // 0が注文の品が完成を表す } synchronized static int setTotalBody( int num ){ Count = num; if(Count == 0){ synchronized (isRemained) { isRemained.notify(); // 客が全員去ったら終了シグナル } } return Count; } synchronized static int getTotalBody(){ return Count; } synchronized static int setCapacity( int num ){ innerCapacity = num; return innerCapacity; } synchronized static int setInnerSpace( int num ){ innerSpace = num; return innerSpace; } synchronized static int getInnerSpace(){ return innerSpace; } synchronized static boolean isFilled () { return innerSpace >= innerCapacity ? true : false; } } /* ---------------- 店員クラス --------------------------------------------- */ /* ------------------------------------------------------------------------- */ class ShopKeeper implements Runnable { static Object lock = new Object(); // ロック用オブジェクト static int body; // 店員総数 /* member data */ Thread self = null; DoubleVectorQueue order; // 注文キュー double openwait; // 開店から注文受けまでの時間 double cooktime; // 調理にかかる時間(最大値) int id; // 連番 public ShopKeeper(DoubleVectorQueue order,double openwait,double cooktime){ this.order = order; this.openwait = openwait; this.cooktime = cooktime; synchronized (lock) { id = getBodyCount(); setBodyCount(id + 1); } self = new Thread(this); self.start(); } synchronized static public int setBodyCount( int num ){ body = num; return body; } synchronized static public int getBodyCount(){ return body; } public void run(){ // 実行 Yosigyu item; int vid; sayHello(); sleep(openwait); while( order.isEmpty() ){ if(!Yosinoya.Run){ break; } } while( Yosinoya.Run ){ // 一度に一人だけが注文をうけつけることができる synchronized (lock){ item = acceptOrder(); } if(item != null){ cookOrder(item); } else{ sleep(1); } } // System.out.println( "仕事終了" ); } public Yosigyu acceptOrder(){ Yosigyu item = null; if( !order.isEmpty() ){ item = (Yosigyu)order.dequeue(); Yosinoya.print("店 員" + id + ": 注文承りました。>お客その" + item.id ); } return item; } public void cookOrder( Yosigyu item ){ sleep( 1 + (double)( Math.random() * cooktime) ); Yosinoya.print( "店 員" + id + ": はいお待ち>お客その" + item.id ); item.check = 0; } public void sayHello(){ String str = "店 員" + id + ": 私が店員その" + id + "です。"; Yosinoya.print( str ); } public void sleep(double num){ try { Thread.sleep( (int)(num * 1000) ); } catch (InterruptedException e){ } } } /* ------- 客クラス --------------------------------------------------------*/ /* ------------------------------------------------------------------------ */ class Visitor implements Runnable { static Object lock = new Object(); // ロック用オブジェ static Object siglock = new Object(); // シグナル用ロック static long maxWait = 0; // 最大待ち時間 static int body; // 客人数 static int signal; // シグナルを発した客IDの保持 final int pThought = 40; // 注文思案中の確率(%) final int EatingTimeRand = 2; // 食事時間プラスアルファ final int EatingTimeBase = 2; // 基礎食事時間 /* member data */ Thread thr = null; DoubleVectorQueue order; // 注文キュー Yosigyu item; // メニューの保持 long time = 0; // 待ち時間 int id; // 連番 int hungry; // 腹のすき具合 boolean myorder; // 注文をした boolean mysignal; // 自分自身がシグナルを発した class Action{ // メッセージと「つゆだく」シグナルの有無 String mes; boolean sig; } private void setSignalData( Action signalData ){ signalData.mes = "つゆだく"; // これがつゆだくだ! signalData.sig = true; } private Action makeAction(){ // アクションの生成 int menuLength = Yosigyu.menu.length; int actLength = menuLength + 3; int i; Action[] act = new Action[actLength]; for(i=0; i < actLength; i++){ act[i] = new Action(); act[i].sig = false; } for(i=0; i < menuLength; i++){ act[i].mes = Yosigyu.menu[i]; } act[menuLength + 0].mes = "よーしパパ特盛頼んじゃうぞー"; act[menuLength + 1].mes = "大盛りねぎだくギョク"; setSignalData( act[menuLength + 2] ); // 「つゆだく」をセット return act[ (int)( Math.random() * actLength ) ]; } synchronized static public int setBodyCount( int num ){ body = num; return body; } synchronized static public int getBodyCount(){ return body; } public Visitor(DoubleVectorQueue order){ this.order = order; this.hungry = 0; synchronized(lock){ id = body++; } thr = new Thread(this); thr.start(); } public void run(){ // 実行 long time; sayHello(); while(!myorder){ // 注文するか、思案する sleep(1); if( Math.random() * 100 >= pThought ){ sendOrder(); } else { thinkOrder(); } } order.enqueue(item); // キューに入れて待ち時間の計測開始 timeWatch(); if(mysignal){ // 自分のつゆだくシグナルに応答させないため sleep(1); // つゆだくシグナルを発したら1秒後に消す synchronized(siglock){ signal = 0; } } while(item.check == 1){ // 料理が出来るまで、シグナルと空腹のチェック checkSignal(); checkHungry(); sleep(1); } time = timeWatch(); // 待ち時間計測終わり maxTime(time); // 最長記録かどうか確認 eatFood(); sayGoodbye(); // ごちそうさま } public void sayHello(){ String[] mes = { "ちは〜", "こんにちは", "腹減ったなぁ", "どうもぉ" }; String str; str = mes[ (int)( Math.random() * 4 ) ]; Yosinoya.print( "客その" + id + ": " + str + " ---- 客その" + id + "登場 ----" ); } public void sendOrder(){ Action act; act = makeAction(); Yosinoya.print( "客その" + id + ": " + act.mes ); myorder = true; if(act.sig){ setSignal(); } item = new Yosigyu( id ); } public void thinkOrder(){ Yosinoya.print( "客その" + id + ": 何にしようかなぁ…" ); } private void checkSignal(){ synchronized(siglock){ if(signal != 0){ Yosinoya.print( "客その" + id + ": お前は本当につゆだくが頼みたいのかと>客その" + signal ); } signal = 0; } } private void checkHungry(){ // 7秒ごとに腹減ったコール if( hungry > 0 && (hungry % 7) == 0 ){ Yosinoya.print( "客その" + id + ": ごはんまだ〜?" ); } hungry++; } private void eatFood(){ Yosinoya.print( "客その" + id + ": いただきます…" ); sleep( (double)( Math.random() * EatingTimeRand ) + EatingTimeBase ); } synchronized public void sayGoodbye(){ String str = "客その" + id + ": ごちそうさま ---- 客その" + id + "退場 ----"; Yosinoya.print( str ); synchronized (Yosigyu.lock){ Yosigyu.setTotalBody( Yosigyu.getTotalBody() - 1 ); Yosigyu.setInnerSpace( Yosigyu.getInnerSpace() - 1 ); } } synchronized private void maxTime(long time){ if(time > maxWait){ maxWait = time; } } private long timeWatch(){ if(time > 0){ time = System.currentTimeMillis() - time; } else{ time = System.currentTimeMillis(); } return time; } private void setSignal(){ // Visitor.signal にセット synchronized(siglock){ signal = id; mysignal = true; } } private void sleep(double num){ try { Thread.sleep( (int)(num * 1000) ); } catch (InterruptedException e){ } } }