Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

人狼知能セミナー資料20160427

875 Aufrufe

Veröffentlicht am

人狼知能プラットフォームver0.3.x対応
人狼知能エージェント作成方法

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

人狼知能セミナー資料20160427

  1. 1. 人狼知能エージェント作成方法
  2. 2. 目次 • 自作Playerを用いた人狼の実行方法 – プロジェクト作成 – ビルド・パスの構成 – 実行の構成 • 実際にPlayerを実装 – 占い師を実装
  3. 3. 新規プロジェクトの作成(1) ファイル>新規>Javaプロジェクト
  4. 4. 新規プロジェクトの作成(2) • 適当なプロジェクト名を入力して完了(ここで はKajiwaraAgent)
  5. 5. ビルド・パスの構成 • プレイヤー作成に必要なjarファイルをビルド・パ スに追加 – “ http://www.aiwolf.org/ ”から「開発者向け情報>人 狼知能プラットフォーム>aiwolf-ver0.3.2」をダウン ロード – Zipの中身 • 5つの.jarファイル • 2つの.shファイル • 3つの.batファイル – 使用するのはaiwolf-client.jar , aiwolf-common.jar, aiwolf-server.jarの3つ
  6. 6. 開発に使うJarファイルの概要 • aiwolf-client – プレイヤーを作成する際に用いる • aiwolf-common – serverとclient両方で用いる • aiwolf-server – ゲームを実行する際に用いる
  7. 7. ちなみに他のファイルは • 主にJava以外の言語で人狼知能を開発 し,ゲームを実行するために使用 –StartServer • ゲームサーバーをGUIで起動させるツール –StartClient • サーバーに接続するためのクライアント (人狼知能)を起動 –StartGUIClient • サーバーに接続するためのクライアントをGUI で起動させるツール
  8. 8. 新規フォルダーの作成(1) プロジェクトを右クリック>新規>フォルダー
  9. 9. 新規フォルダーの作成(2) • 適当なフォルダー名を入力して完了(ここでは lib)
  10. 10. 新規フォルダーの作成(3) • フォルダー内にclient, common, serverの3つ のjarファイルをドラッグ&ドロップ →これらのjarファイルをビルド・パスに追加
  11. 11. ビルド・パスの構成(1) プロジェクトを右クリック>ビルド・パス>ビル ド・パスの構成
  12. 12. ビルド・パスの構成(2) ライブラリー>Jar追加
  13. 13. ビルド・パスの構成(3) フォルダー内に入れた3つのjarファイルを選択 して「OK」
  14. 14. 自作プレイヤーでゲーム実行 1. AbstractRoleAssignPlayerを継承したクラスを 作成 2. 実行の構成を変更 3. 各役職のPlayerを作成 4. RoleAssignPlayerに各役職のPlayerをセット
  15. 15. 自作プレイヤーでゲーム実行 1. AbstractRoleAssignPlayerを継承したクラスを 作成 2. 実行の構成を変更 3. 各役職のPlayerを作成 4. RoleAssignPlayerで各役職のPlayerをセット まず実行できる環境を整える
  16. 16. 新規パッケージ作成(1) KajiwaraAgent>srcを右クリック>新規>パッ ケージ
  17. 17. 新規パッケージ作成(2) • パッケージ名を入力して完了 – パッケージ名は自分のアドレスを逆から入力 • 例) myAgent@aiwolf.org → org.aiwolf.myAgent
  18. 18. 新規クラス作成(1) 作成したパッケージを右クリック>新規>クラス
  19. 19. 新規クラス作成(2) スーパークラス>参照から ”AbstractRoleAssignPlayer”を選択
  20. 20. 新規クラス作成(3) • 適当なクラス名を入力して完了(ここでは, KajiwaraRoleAssignPlayer)
  21. 21. 実行の構成 プロジェクトを右クリック>実行>実行の構成
  22. 22. 実行の構成(1) • メイン・クラス>検索から “RoleRequestStarter”を選択
  23. 23. 実行の構成(2) 引数>プログラムの引数に下記を入力 「-n 11 -c org.aiwolf.KajiwaraAgent.KajiwaraRoleAssignPlayer SEER」 プレイヤー数 パッケージ名 クラス名 希望役職
  24. 24. 実行の構成(3) 10人のサンプルプレイヤーと自分のプレイヤー でゲームを行う状態(自分は占い師だと設定) サンプ ル サンプ ル サンプ ル 自分 ・・・ サーバ
  25. 25. 実行結果 • 適用→実行 を押すと実行結果が表示されて ゲームが実行出来る状態に
  26. 26. Playerの実装
  27. 27. 自作プレイヤーでゲーム実行 1. AbstractRoleAssignPlayerを継承したクラスを 作成 2. 実行の構成を変更 3. 各役職のPlayerを作成 4. RoleAssignPlayerで各役職のPlayerをセット 例として占い師を作ってみましょう
  28. 28. 新規クラスの作成 • MyRoleAssignPlayerの作成と同様に新規クラ スを作成 – スーパークラスはAbstractSeerを選択
  29. 29. MyRoleAssignPlayerを変更 • MyRoleAssignPlayerに次のコンストラクタを追加 – 役職として占い師を振り分けられた時に KajiwaraSeerPlayerを呼び出す – 他の役職の場合はデフォルトでサンプルプレイ ヤーが呼び出される public MyRoleAssignPlayer(){ setSeerPlayer(new KajiwaraSeer()); }
  30. 30. ゲーム実行 • もう一度ゲームを実行して動けばOK • KajiwaraSeerはまだ実装すべきメソッドを実装 していないから適当な行動をとる 実装すべきメソッドとは?
  31. 31. Playerインターフェース Playerインターフェース内のAbstract Method ゲームの各タイミングでサーバがプレイヤーの これらのメソッドを呼び出す • getName() • update(GameInfo gameinfo) • initialize(GameInfo gameinfo) • dayStart() • talk() • whisper() • vote() • attack() • divine() • guard() • finish()
  32. 32. ゲームの流れ initialize(GameInfo) vote() divine()(占い師のみ) attack()(人狼のみ) guard()(狩人のみ) update(GameInfo) whisper()(人狼のみ) talk() update(GameInfo) dayStart() update(GameInfo) finish() 会話終了 ゲーム終了 1日
  33. 33. 各メソッドの戻り値 • getName() • update(GameInfo gameinfo) • initialize(GameInfo gameinfo) • dayStart() • talk() • whisper() • vote() • attack() • divine() • guard() • finish() 全体,もしくは囁き で発話するメソッド 戻り値:String 対象プレイヤーを選択する メソッド 戻り値:Agent
  34. 34. MySeerの実装 • MySeerが継承したAbstractSeer – 占い師に必要ないメソッド(attack, guard等)を事 前に消してある – 毎朝,サーバから送られた占い結果をListに格納 する 等の簡単な実装がしてある • 実装する必要のあるメソッド – talk(), divine(), vote()の3つ
  35. 35. 実際に実装してみましょう
  36. 36. 実装の流れ 1. voteの実装(1) – ランダムに投票 2. divineの実装 – ランダムに占う 3. voteの実装(2) – 占い結果を考慮して投票 4. talkの実装 – カミングアウト,占い結果の報告
  37. 37. 実装の流れ 1. voteの実装(1) – ランダムに投票 2. divineの実装 – ランダムに占う 3. voteの実装(2) – 占い結果を考慮して投票 4. talkの実装 – カミングアウト,占い結果の報告
  38. 38. vote()の実装 実装例 – 生きているプレイヤーから自分を除いてランダム に投票してみる
  39. 39. @Override public Agent vote(){ //投票対象の候補者リスト List<Agent> voteCandidates = new ArrayList<Agent>(); //生きているプレイヤーを候補者リストに加える voteCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //自分自身と白判定のプレイヤーは候補から外す voteCandidates.remove(getMe()); return randomSelect(voteCandidates); } vote()の実装 ↑getLatestDayGameInfo()はAbstractPlayer内のメソッド で,最新のGameInfoを返す GameInfoはサーバから送られてくるゲーム内情報.ロ グや占い結果等の全ての情報はここから取得する 続く
  40. 40. vote()の実装 /** * 引数のAgentのリストからランダムにAgentを選択する * @param agentList * @return */ private Agent randomSelect(List<Agent> agentList){ int num = new Random().nextInt(agentList.size()); return agentList.get(num); }
  41. 41. vote()の実装(全体像) @Override public Agent vote(){ //投票対象の候補者リスト List<Agent> voteCandidates = new ArrayList<Agent>(); //生きているプレイヤーを候補者リストに加える voteCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //自分自身と白判定のプレイヤーは候補から外す voteCandidates.remove(getMe()); return randomSelect(voteCandidates); } /** * 引数のAgentのリストからランダムにAgentを選択する * @param agentList * @return */ private Agent randomSelect(List<Agent> agentList){ int num = new Random().nextInt(agentList.size()); return agentList.get(num); } } 次はdivine()を実装してみよう
  42. 42. 実装の流れ 1. voteの実装(1) – ランダムに投票 2. divineの実装 – ランダムに占う 3. voteの実装(2) – 占い結果を考慮して投票 4. talkの実装 – カミングアウト,占い結果の報告
  43. 43. divine()の実装 実装例 – 自分を除いて,まだ占っていないプレイヤーからラ ンダムに選択 ※既に死んでいるプレイヤーから選択しないように注意
  44. 44. divine()の実装 @Override public Agent divine() { //占い対象の候補者リスト List<Agent> divineCandidates = new ArrayList<Agent>(); //生きているプレイヤーを候補者リストに加える divineCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //自分自身と既に占ったことのあるプレイヤーは候補から外す divineCandidates.remove(getMe()); for(Judge judge: getMyJudgeList()){ if(divineCandidates.contains(judge.getTarget())){ divineCandidates.remove(judge.getTarget()); } } 続く
  45. 45. divine()の実装 if(divineCandidates.size() > 0){ //候補者リストからランダムに選択 return randomSelect(divineCandidates); }else{ //候補者がいない場合は自分を占い return getMe(); } }
  46. 46. divine()の実装(全体像) @Override public Agent divine() { //占い対象の候補者リスト List<Agent> divineCandidates = new ArrayList<Agent>(); //生きているプレイヤーを候補者リストに加える divineCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //自分自身と既に占ったことのあるプレイヤーは候補から外す divineCandidates.remove(getMe()); for(Judge judge: getMyJudgeList()){ if(divineCandidates.contains(judge.getTarget())){ divineCandidates.remove(judge.getTarget()); } } if(divineCandidates.size() > 0){ //候補者リストからランダムに選択 return randomSelect(divineCandidates); }else{ //候補者がいない場合は自分を占い return getMe(); } } 次は占い結果を投票に反映させてみましょう
  47. 47. 実装の流れ 1. voteの実装(1) – ランダムに投票 2. divineの実装 – ランダムに占う 3. voteの実装(2) – 占い結果を考慮して投票 4. talkの実装 – カミングアウト,占い結果の報告
  48. 48. vote()の実装2 実装例 – 占いで人狼を見つけていたらその中からランダム – 人狼を見つけていなければ,自分と白判定が出たプレイ ヤー以外からランダム – まだ占っていない場合は,自分以外からランダム ※既に死んでいるプレイヤーから選択しないように注意
  49. 49. vote()の実装2 @Override public Agent vote() { List<Agent> whiteAgent = new ArrayList<Agent>(), //白判定だったプレイヤー blackAgent = new ArrayList<Agent>(); //黒判定だったプレイヤー //今まで占ったプレイヤーをwhiteAgentとblackAgentに分ける for(Judge judge: getMyJudgeList()){ if(getLatestDayGameInfo().getAliveAgentList().contains(judge.getTarget())){ switch (judge.getResult()) { case HUMAN: whiteAgent.add(judge.getTarget());] break; case WEREWOLF: blackAgent.add(judge.getTarget()); } } } 続く
  50. 50. vote()の実装2 if(blackAgent.size() > 0){ //blackAgentがいればその中から選択 return randomSelect(blackAgent); }else{ //投票対象の候補者リスト List<Agent> voteCandidates = new ArrayList<Agent>(); voteCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //生きているプレイヤーを候補者リストに加える //自分自身と白判定のプレイヤーは候補から外す voteCandidates.remove(getMe()); voteCandidates.removeAll(whiteAgent); return randomSelect(voteCandidates); } }
  51. 51. vote()の実装2(全体像) @Override public Agent vote() { List<Agent> whiteAgent = new ArrayList<Agent>(), //白判定だったプレイヤー blackAgent = new ArrayList<Agent>(); //黒判定だったプレイヤー for(Judge judge: getMyJudgeList()){ if(getLatestDayGameInfo().getAliveAgentList().contains(judge.getTarget())){ switch (judge.getResult()) { case HUMAN: whiteAgent.add(judge.getTarget());] break; case WEREWOLF: blackAgent.add(judge.getTarget()); } } } if(blackAgent.size() > 0){ return randomSelect(blackAgent); }else{ //投票対象の候補者リスト List<Agent> voteCandidates = new ArrayList<Agent>(); voteCandidates.addAll(getLatestDayGameInfo().getAliveAgentList()); //生きているプレイヤーを候補者リストに加える //自分自身と白判定のプレイヤーは候補から外す voteCandidates.remove(getMe()); voteCandidates.removeAll(whiteAgent); return randomSelect(voteCandidates); } } 次は占い結果を発話してみましょう
  52. 52. 実装の流れ 1. voteの実装(1) – ランダムに投票 2. divineの実装 – ランダムに占う 3. voteの実装(2) – 占い結果を考慮して投票 4. talkの実装 – カミングアウト,占い結果の報告
  53. 53. talk, whisperでの発話 主な発話は org.aiwolf.client.lib.TemplateTalkFactory(全体用) org.aiwolf.client.lib.TemplateWhisperFactory(囁き用) で簡単に作成可能
  54. 54. TemplateTalkFactoryクラス • estimate(Agent, Role) – Agentの役職はRoleだと思う • comingout(Agent, Role) – AgentがRoleをカミングアウトする • divined(Agent, Species) – Agentを占った結果Speciesだった • inquested(Agent, Species) – Agentの霊能結果がSpeciesだった • guarded(Agent) – Agentを守った • vote(Agent) – Agentに投票する • (dis)agree(TalkType, day, id) – 対象の発話に同意(反対)する • skip() – まだ今日話したいことがある • over() – もう今日は話すことない
  55. 55. TemplateTalkFactoryの使用例 //占いの情報の取得 Judge judge = getLatestDayGameInfo().getDivineResult(); //発話の作成 String talk = TemplateTalkFactory.divined(judge.getTarget(), judge.getResult()); 今日の占い結果を報告する発話を生成
  56. 56. talk()の実装 実装例 – 占いで人狼を見つけたらカミングアウト – カミングアウトした後は占い結果を報告する – 話すことが無ければ”Over” (プレイヤーが全員Overを返せば会話のターンが終了)
  57. 57. talk()の実装 boolean isComingOut = false; //既に役職のカミングアウトをしているか //報告済みのJudgeを格納 List<Judge> myToldJudgeList = new ArrayList<Judge>(); @Override public String talk() { //占いで人狼を見つけたらカミングアウトする if(!isComingOut){ for(Judge judge: getMyJudgeList()){ if(judge.getResult() == Species.WEREWOLF){ //占い結果が人狼の場合 String comingoutTalk = TemplateTalkFactory.comingout(getMe(), getMyRole()); isComingOut = true; return comingoutTalk; } } } 続く
  58. 58. talk()の実装 //カミングアウトした後は,まだ言っていない占い結果を順次報告 else{ for(Judge judge: getMyJudgeList()){ if(!myToldJudgeList.contains(judge)){ //まだ報告していないJudgeの場合 String resultTalk = TemplateTalkFactory.divined(judge.getTarget(), judge.getResult()); myToldJudgeList.add(judge); return resultTalk; } } } //話すことが無ければ会話終了 return Talk.OVER; }
  59. 59. talk()の実装(全体像) //既に役職のカミングアウトをしているか boolean isComingOut = false; @Override public String talk() { //占いで人狼を見つけたらカミングアウトする if(!isComingOut){ for(Judge judge: getMyJudgeList()){ if(judge.getResult() == Species.WEREWOLF){ //占い結果が人狼の場合 String comingoutTalk = TemplateTalkFactory.comingout(getMe(), getMyRole()); isComingOut = true; return comingoutTalk; } } } //カミングアウトした後は,まだ言っていない占い結果を順次報告 else{ for(Judge judge: getMyJudgeList()){ if(!myToldJudgeList.contains(judge)){ //まだ報告していないJudgeの場合 String resultTalk = TemplateTalkFactory.divined(judge.getTarget(), judge.getResult()); myToldJudgeList.add(judge); return resultTalk; } } } //話すことが無ければ会話終了 return Talk.OVER; } 次は他の人の発話を読み込んでみましょう
  60. 60. ログの読み込み ログは GameInfo.getTalkList()でList<Talk>型として取得 Talkクラス内のメソッドは4つ – getAgent():発話したAgentを取得 – getContent():発話内容(String)を取得 – getDay():発話日(int)を取得 – getIdx():その日の何番目の発話か(int)を取得
  61. 61. ログの読み込み Talk.getContent()で得られるStringの中身は “DIVINED Agent[04] HUMAN” のように人狼言語で記載 →Utteranceクラスでパース //Utteranceクラスのコンストラクタの引数に発話内容のStringを入れ ると自動的にパースされる Utterance utterance = new Utterance(talk.getContent());
  62. 62. Utteranceクラスの使い方 戻り値 メソッド名と説明 String getText() 発話内容をそのまま返す Topic getTopic() 発話のTopicを返す(COMINGOUTやDIVINED等) Agent getTarget() 発話内の目的語となるプレイヤーを返す(例えば”DIVINED Agent[01] HUMAN” → Agent[01]) Role getRole() 発話の目的語となる役職を返す(例えば”COMINGOUT Agent[02] SEER” → SEER) Species getResult() 占い(霊能)の結果を返す(例えば”INQUESTED Agent[03] WEREWOLF” → WEREWOLF) TalkType getTalkType() TopicがAGREE,DISAGREEの時,対象発話のTalkType(全体ログor囁き)を返す int getTalkDay() TopicがAGREE,DISAGREEの時,対象発話の発話日を返す int getTalkID() TopicがAGREE,DISAGREEの時,対象発話の発話IDを返す
  63. 63. @Override public void update(GameInfo gameInfo) { super.update(gameInfo); //今日のログを取得 List<Talk> talkList = gameInfo.getTalkList(); for(int i = 0; i < talkList.size(); i++){ Talk talk = talkList.get(i); //発話をパース Utterance utterance = new Utterance(talk.getContent()); //発話のトピックごとに処理 switch (utterance.getTopic()) { case COMINGOUT: //カミングアウトの発話の処理 break; case DIVINED: // 占い結果の発話の処理 break; } } } ログの読み込み このままだと,update()が呼ばれる度に,その日のログを全部読み込む → 一度読み込んだTalkは読まないように変更してみる
  64. 64. ログの読み込み2 //その日のログの何番目まで読み込んだか int readTalkNum = 0; @Override public void dayStart(){ super.dayStart(); readTalkNum = 0; } オレンジ部分:追加 赤部分:修正
  65. 65. @Override public void update(GameInfo gameInfo) { super.update(gameInfo); //今日のログを取得 List<Talk> talkList = gameInfo.getTalkList(); for(int i = readTalkNum; i < talkList.size(); i++){ Talk talk = talkList.get(i); //発話をパース Utterance utterance = new Utterance(talk.getContent()); //発話のトピックごとに処理 switch (utterance.getTopic()) { case COMINGOUT: //カミングアウトの発話の処理 break; case DIVINED: // 占い結果の発話の処理 break; } readTalkNum++; } } オレンジ部分:追加 赤部分:修正 ログの読み込み2
  66. 66. ログの読み込み2(全体像) //その日のログの何番目まで読み込んだか int readTalkNum = 0; @Override public void dayStart(){ super.dayStart(); readTalkNum = 0; } @Override public void update(GameInfo gameInfo) { super.update(gameInfo); //今日のログを取得 List<Talk> talkList = gameInfo.getTalkList(); for(int i = readTalkNum; i < talkList.size(); i++){ Talk talk = talkList.get(i); //発話をパース Utterance utterance = new Utterance(talk.getContent()); //発話のトピックごとに処理 switch (utterance.getTopic()) { case COMINGOUT: //カミングアウトの発話の処理 break; case DIVINED: // 占い結果の発話の処理 break; } readTalkNum++; } } オレンジ部分:追加 赤部分:修正 ←自分以外に占い師COしているプレイヤーを 敵リストに追加してみましょう
  67. 67. //偽占い師COしているプレイヤーのリスト List<Agent> fakeSeerCOAgent = new ArrayList<Agent>(); @Override public void update(GameInfo gameInfo) { super.update(gameInfo); //今日のログを取得 List<Talk> talkList = gameInfo.getTalkList(); for(int i = readTalkNum; i < talkList.size(); i++){ Talk talk = talkList.get(i); //発話をパース Utterance utterance = new Utterance(talk.getContent()); //発話のトピックごとに処理 switch (utterance.getTopic()) { case COMINGOUT: //自分以外で占い師COしているプレイヤーの場合 if(utterance.getRole() == Role.SEER && !talk.getAgent().equals(getMe())){ fakeSeerCOAgent.add(utterance.getTarget()); } break; case DIVINED: // 占い結果の発話の処理 break; } readTalkNum++; } } オレンジ部分:追加 赤部分:修正 ログの読み込み3(全体像)
  68. 68. エージェントを作り終えたら・・・
  69. 69. Jarファイル作成方法(1) プロジェクトを右クリック>エクスポート
  70. 70. Jarファイル作成方法(2) JAVA>JARファイル を選択して次へ
  71. 71. Jarファイル作成方法(3) • プロジェクト全てを選択 • エクスポート先を設定 完了
  72. 72. 補足
  73. 73. Playerの各メソッドの説明 • initialize(GameInfo) – ゲーム開始時に一度だけ 呼ばれる – サーバから送られてくる GameInfoを取得 • update(GameInfo) – 各行動の前に呼ばれる – サーバから送られてくる GameInfoを取得 • dayStart() – 日の初めに呼ばれる • finish() – ゲームが終了した時に呼 ばれる
  74. 74. Playerの各メソッドの説明 • vote() – 投票する相手を選択する • attack() – 襲撃する相手を選択する – 人狼のみ呼ばれる • divine() – 占いする相手を選択する – 占い師のみ呼ばれる • guard() – 護衛する相手を選択する – 狩人のみ呼ばれる 1日の終わりに呼ばれるメソッド Agentを返す必要あり
  75. 75. Playerの各メソッドの説明 • talk() – 全体に対して発話する – 全員呼ばれるメソッド • whisper() – 人狼だけに対して発話す る – 人狼のプレイヤーだけが 使用するメソッド 発話のメソッド Stringを返す必要あり
  76. 76. GameInfoの説明 戻り値 メソッド名と説明 int getDay() 日にちを返す Role getRole() 自分の役職を返す Agent getAgent() 自分(Agent型)を返す List<Agent> getAgentList() 全プレイヤーのリストを返す Species getMediumResult() 霊能結果を返す(霊能者のみ) Species getDivineResult() 占い結果を返す(占い師のみ) Agent getExecutedAgent() 処刑されたプレイヤーを返す Agent getAttackedAgent() 襲撃されたプレイヤーを返す List<Vote> getVoteList() 処刑の際の投票リストを返す List<Vote> getAttackVoteList() 襲撃の際の人狼による投票のリストを返す(人狼のみ) List<Talk> getTalkList() 会話のログを返す List<Talk> getWhisperList() 囁きのログを返す(人狼のみ) List<Agent> getAliveAgentList() 生きているプレイヤーのリストを返す Map<Agent, Status> getStatusMap() 各プレイヤーの生死の状態を返す Map<Agent, Role> getRoleMap() 各プレイヤーの役職を返す.ゲーム中は自分の分かる役職の み(村人なら自分だけ,人狼なら仲間の人狼も) ゲーム終了時は全員の役職が分かる.

×