#!/usr/bin/ruby #============================================================================== # YosinoyaGyudooooon! for Ruby #============================================================================== =begin プログラムを開始すると店員が登場。続いて一定時間おきに客が来店。 注文をする。注文を受けると調理を開始。客に手渡す。食べ終わると 客は去る。各客ごとに注文してから料理が出されるまでの待ち時間を 計っているので、最後に全客の総計時間と、一人あたりの平均値、及び 一番待たされた客の時間が表示される。 それだけですよ? そ れ だ け。 コマンドラインオプションは perl版 を参照してください (オプションのうち、`shopkeeper'だけ`keeper'に変更しています) Copyright (C) 2003 まかまか般若波羅蜜 =end #============================================================================== require "thread"; #============================================================================== $run = false # 店員スレッドの停止フラグ #============================================================================== # デフォルト値 #============================================================================== SHOPKEEPER = 2 # 店員数 MAX_VISITOR = 10 # 客数 WAIT_OPEN = 3 # 開店してから注文を受け始めるまでのラグ INTERVAL = 1 # 客が来る間隔 CAPACITY = 10 # 店内の許容量 COOKTIME = 3 # 調理時間 #============================================================================== # 店クラス #============================================================================== class Yosigyu @@cv = nil # 全客終了条件変数(set_cv で受け取る) @@count = 0 # 全来客人数 set_count で設定 @@capacity = 0 # 同時来客可能人数 @@space = 0 # 店内スペース @@item = {} # 注文客id格納 @@signal = nil # つゆだくシグナル @@menu = [ ['牛丼並', 0], ['牛皿', 0], ['牛鮭定食',0], ['牛丼大盛',0] ] def Yosigyu.menu return @@menu end def Yosigyu.is_filled? return @@space >= Yosigyu.capacity() ? true : false end def Yosigyu.capacity( num = nil ) if(num) @@capacity = num end return @@capacity end def Yosigyu.space( num = nil ) if(num) @@space = num end return @@space end def Yosigyu.set_cv(cv) @@cv = cv end def Yosigyu.is_remained?() return @@count end def Yosigyu.set_count( num ) @@count = num end def Yosigyu.count_down @@count -= 1 if @@count == 0 @@cv.signal end end def Yosigyu.item( id, num = nil ) if(num) @@item[id] = num end return @@item[id] end def Yosigyu.signal( vid = nil ) if (vid) @@signal = vid end return @@signal end def Yosigyu.clear_signal @@signal = nil end end #============================================================================== # 店員クラス #============================================================================== class Keeper @@body = 0 # 店員idカウンタ @@accept = Mutex.new # 注文受け付け用ロック @@lock = Mutex.new # カウンタ用ロック(不要) def thread return @thread end def initialize( order, openwait, cooktime ) @@lock.synchronize{ @@body += 1 @id = @@body } @order = order @openwait = openwait @cooktime = cooktime self.say_hellow() self.run end def run @thread = Thread.start{ sleep(@openwait) while @order.size == 0 break if(!$run) end while $run vid = nil @@accept.synchronize{ vid = self.accept( @order ) } (vid != nil) ? self.cook(vid) : sleep(1) end } end def say_hellow print "私が店員その#@idです\n" end def accept( order ) vid = nil if( order.size != 0 ) vid = order.deq print "店 員#@id: 注文を承りました。>お客その#{vid}\n" end return vid end def cook( vid ) sleep( rand( @cooktime ) + 1 ) print "店 員#{@id}: はいお待ち>お客その#{vid}\n" Yosigyu.item(vid,0) end end #============================================================================== # 客クラス #============================================================================== class Visitor @@body = 0 # 人数 @@wait = 0.0 # 最大待ち時間 @@lock = Mutex.new def Visitor.wait return @@wait end def initialize( order ) @@body += 1 @id = @@body # 客id @order = order # 注文キュー @ordered = nil # オーダーしたかどうかフラグ @mysignal = nil # つゆだくシグナル発したときのフラグ @time = nil # 待ち時間用Timeオブジェクト @hungry = 0 # はらぺこ度 @wait = 0.0 # 最大待ち時間 self.set_action self.run end def signal_sender return ['つゆだく',1] end def set_action @action = Yosigyu.menu() @action.push ['よーしパパ特盛頼んじゃうぞー',0] @action.push ['大盛りねぎだくギョク' ,0] @action.push self.signal_sender() end def thread return @thread end def run @thread = Thread.start{ time = 0 # 注文待ち時間 self.say_hellow while ! @ordered sleep(1) if rand(100) > 60 # 注文するか、思案する self.send_order() # 注文をキューに入れて self.time_watch() # 待ち時間計測開始 else self.thinking() end end if( @mysignal == 1 ) # 自分のつゆだくシグナルに応答させないため sleep(1) # つゆだくシグナルを発したら1秒後に消す @@lock.synchronize{ Yosigyu.clear_signal() } end while Yosigyu.item(@id) != 0 self.check_signal # つゆだくシグナルのチェック self.check_hungry # ごはんまだ〜チェック sleep(1) end # 待ち時間計測終わり 最長記録かどうか確認 @wait = Time.now.to_i - self.time_watch @@wait = @wait > @@wait ? @wait : @@wait self.eat() self.say_goodbye() } end def say_hellow mes = ['こんにちは','ちは〜','腹減ったなぁ','どうもぉ'] print "客その#{@id}: #{mes[rand(mes.size)]} ---- 客その#{@id} 登場 ----\n" end def thinking print "客その#{@id}: 何にしようかなぁ…\n" end def send_order item = @action[ rand( @action.length ) ] print "客その#{@id}: #{item[0]}\n" self.set_signal() if(item[1] == 1) Yosigyu.item(@id,1) @ordered = true @order.enq(@id) end def set_signal @@lock.synchronize{ Yosigyu.signal( @id ) @mysignal = 1 } end def check_hungry @hungry += 1 if( @hungry > 0 and @hungry % 7 == 0 ) print "客その#{@id}: ごはんまだ〜?\n" end end def check_signal @@lock.synchronize{ vid = Yosigyu.signal() if( vid != nil ) item = signal_sender print "客その#{@id}: お前は本当に", "#{item[0]}が頼みたいのかと>客その#{vid}\n" end Yosigyu.clear_signal() } end def eat print "客その#{@id}: いただきます…\n" sleep( rand(2) + 2 ) end def say_goodbye print "客その#{@id}: ごちそうさま ---- 客その#{@id}退場 ----\n" @@lock.synchronize{ Yosigyu.count_down() Yosigyu.space( Yosigyu.space() - 1 ) } end def time_watch @time == nil ? @time = Time.now.to_i : @time end def get_wait_time return @wait end end #============================================================================== # subroutine #============================================================================== def keeper(order,num,openwait,cooktime) array = [] while num > 0 array.push Keeper.new( order,openwait,cooktime ) num -= 1 end return array end def visitor(order,num,interval) array = [] lock = Mutex.new while num > 0 if(Yosigyu.is_filled?) sleep(1) next end array.push Visitor.new( order ) lock.synchronize{ Yosigyu.space( Yosigyu.space() + 1 ) } sleep(interval) num -= 1 end return array end def finish( keeper, visitor ) time = 0 for i in keeper Thread.kill( i.thread() ) end for i in visitor time += i.get_wait_time() Thread.kill( i.thread() ) end return time end def show_result( time, visitor ) wait = Visitor.wait num = visitor.size num = Float.induced_from(num) print "\n---- 閉店 ----\n" print "\n総待ち時間\t#{time}\t" print "最大待ち時間\t#{wait}\n"; print "客一人あたりの平均待ち時間\t", sprintf("%.2f",time / num), "\n" end def get_option opt = {} ARGV.each { |arg| arg =~ /--(\w+)=(\d+)/ opt[$1] = $2.to_i } opt['keeper'] = SHOPKEEPER if(!opt['keeper']) opt['visitor'] = MAX_VISITOR if(!opt['visitor']) opt['openwait'] = WAIT_OPEN if(!opt['openwait']) opt['interval'] = INTERVAL if(!opt['interval']) opt['capacity'] = CAPACITY if(!opt['capacity']) opt['cooktime'] = COOKTIME if(!opt['cooktime']) return opt end #============================================================================== # Main #============================================================================== keeper = [] # 店員オブジェクトの格納 visitor = [] # 客オブジェクトの格納 opt = get_option() # オプション order = Queue.new # 注文用キュー(各スレッドに渡す) cv = ConditionVariable.new # 全客終了判定用条件変数 lock = Mutex.new # cv用ロック sum_time = 0 # 総計時間 Yosigyu.set_count(opt['visitor']) # 来客人数セット Yosigyu.capacity(opt['capacity']) # 店内スペースをセット Yosigyu.set_cv(cv) # 全客終了条件変数をセット keeper = keeper(order,opt['keeper'],opt['openwait'],opt['cooktime']) $run = true visitor = visitor(order,opt['visitor'],opt['interval']) lock.synchronize{ cv.wait(lock) } # 全客終了シグナルを待つ $run = false sum_time = finish(keeper,visitor) # スレッドの回収と総計時間取得 show_result(sum_time,visitor) # 結果を表示して終わり #==============================================================================