3.6 交渉

エージェントの内部状態により振る舞いを変えるエージェントについて説明します.
教科書p.80 "3.6 状況を認識して判断する" の内容に相当します.

概要

%0 Secretary_A Secretary_A Secretary_B Secretary_B Secretary_A->Secretary_B adjust schedule Secretary_C Secretary_C Secretary_A->Secretary_C adjust schedule

人名 属性 秘書エージェント名
Alice 社員 Secretary_A
Bob Aliceの上司 Secretary_B
Carn Aliceの同僚 Secretary_C

メッセージの流れ

Aliceの秘書AgentSecretary_AAliceの秘書AgentSecretary_ABob(Aliceの上司)の秘書AgentSecretary_BBob(Aliceの上司)の秘書AgentSecretary_BCarn(Aliceの同僚)の秘書AgentSecretary_CCarn(Aliceの同僚)の秘書AgentSecretary_Cuseruserdo-appointmentSecretary_Cに指示を出すappointmentacceptCarnからの申込みを受理するdo-appointmentSecretary_Bに指示を出すappointmentacceptBobからの申込みを優先して受理するcancelCarnとの予定はキャンセル

プログラム(配布版からの変更があります)

Secretary0.groovy

class Secretary extends TAG {

    // boss(秘書エージェントに対応する人)の予定集.
    // 要素は[hour:13, item:'会議', with:'Bob']など (13時からBobと会議)
    def schedules = []

    // bossの名前と秘書エージェント名の対応情報
    def secretaries =[
        [name:'Alice', agent:'Secretary_A'],
        [name:'Bob',   agent:'Secretary_B'],
        [name:'Carn',  agent:'Secretary_C'] ]

    // 重要度に関する人間関係(Aliceにとって,BobはCarnより重要度が高い)
    def priority = [Alice: ['Bob', 'Carn'], Bob: ['Alice', 'Carn'], Carn: ['Bob', 'Alice']]

    // 末尾のTAG.start()内でエージェント名を指定して起動する場合に必要(→2.3(2)節)
    Secretary(String c) { super("Secretary_$c") } // エージェント名は"Secretary_A"などとなる

    def loop(msg) {
        // bossの名前(Alice, Bob, またはCarn)
        def boss = secretaries.find{it.agent == this.agentname}.name

        if (msg._p=='do-appointment' && msg.name!=null && msg.hour!=null && msg.item!=null) {
            def agent = secretaries.find{it.name==msg.name}.agent // nameの担当秘書エージェントを調べる
            def m = request(agent, [_p:'request-appointment', hour:msg.hour, item:msg.item])
            if (m._p == 'accept') {
                println("予定が受理されました: "+m)
            }
        } else if (msg._p=='cancel') {

        } else if (msg._p=='request-appointment' && msg.hour!=null && msg.item!=null) {
            def name = secretaries.find{it.agent==msg._f}.name // 秘書エージェントに対応する名前を調べる
            def schedule = schedules.find{it.hour == msg.hour} // 同じ時間にある先約を調べる
            if (schedule == null) { // 先約がない
                schedule = [hour:msg.hour, item:msg.item, with:name] // 新しい予定を作り,
                schedules.add(schedule)                              // 予定集に追加する
                println("予定を受理しました: "+schedule)
                reply(msg, [_p:'accept', schedule:schedule])
            } else { // 先約がある
                def myPriority = priority.get(boss)
                if (myPriority.indexOf(schedule.with) < myPriority.indexOf(name)) { // 優先度をチェック
                    println("優先度の高い先約があるので予定を受理しません: "+msg)
                    reply(msg, [_p:'refusal', msg:msg])
                } else {
                    def newSchedule = [hour:msg.hour, item:msg.item, with:name] // 優先度の高い予定
                    println("優先度の高い予定を受理しました:"+newSchedule)
                    schedules.add(newSchedule)
                    reply(msg, [_p:'accept', schedule:newSchedule])
                    println("優先度の低い先約をキャンセルしました")
                    schedules.remove(schedule)
                    def agent = secretaries.find{it.name==schedule.with}.agent
                    send(agent, [_p:'cancel', schedule:schedule])
                }
            }
        } else {
            reply(msg, [_p:'sorry', msg:msg])
        }
    }
}
TAG.start(new Secretary('A'), new Secretary('B'), new Secretary('C'))

動作方法

課題

  1. cancelを受信したときに「予約がキャンセルされました」と表示させてみまよう.
  2. 43行のreply(msg, [_p:'refusal', msg:msg])をSecretary_Aに実行させる方法を考えてみましょう.Secretary_BとSecretary_Cに,どのようなメッセージをどのような順番で送信すればよいでしょうか?
  3. refusalを受信したときに「予約が拒否されました」と表示させてみましょう.

Groovy文法情報

def list = ['apple', 'banana', 'melon']
def p = list.indexOf('apple')  // p=0になる
def q = list.indexOf('banana') // q=1になる