2014年6月27日金曜日

人狼知能エージェントの作成・その3 スタート編

2015/02/09変更
サーバのバージョンの変更に従って,一部ソースコードが変更されています.
古いバージョンをお使いの方はver0.1.15以上にアップデートしてください.

さて,今日は人狼知能Talkする編を書こうと思ったけど,
その前に「テスト用に,自分で繰り返し実行する方法が知りたい」という意見があったので,
先にそちらを書こうと思う.

ゲーム実行のためのメインクラス

AIWolfのゲームを実行するには,

  • AIWolfGame:ゲーム実行サーバ本体クラス
  • GameSetting:ゲームの設定クラス
  • GameServer:エージェントとゲームサーバとの通信用クラス
の三つのクラスと,エージェントクラスを利用する.
以下は実行サンプルのソースコードである.
ただし,このソースコードは,人狼知能エージェントの作成・その1-ver0.1.15対応にある BasePlayerをつかっている.ので,作成していない場合は先にこちらを参照して作成して欲しい.

package org.aiwolf.player.bin;

import java.io.File;
import java.io.IOException;

import java.util.HashMap;
import java.util.Map;

import org.aiwolf.client.base.smpl.SampleRoleAssignPlayer;
import org.aiwolf.common.data.Player;
import org.aiwolf.common.data.Role;
import org.aiwolf.common.data.Team;
import org.aiwolf.common.net.GameSetting;
import org.aiwolf.player.BasePlayer;
import org.aiwolf.server.AIWolfGame;
import org.aiwolf.server.GameData;
import org.aiwolf.server.net.DirectConnectServer;



/**
 * テスト用に人狼をスタートするためのMainクラス
 * @author tori
 *
 */
public class TestStarter {

 /**
  * 他のエージェントの数
  */
 static protected int PLAYER_NUM = 12;
 
 /**
  * @param args
  * @throws IOException 
  */
 public static void main(String[] args) throws IOException {
  

  Map playerMap = new HashMap();//・・・(1)
  //BasePlayerはVillagerに固定
  playerMap.put(new BasePlayer(), Role.VILLAGER);
  for(int i = 0; i < PLAYER_NUM-1; i++){
   //そのほかのエージェントは,SamplePlayerで役職の希望は無し.
   playerMap.put(new SampleRoleAssignPlayer(), null);
  }
  
  
  DirectConnectServer gameServer = new DirectConnectServer(playerMap);//・・・(2)
  GameSetting gameSetting = GameSetting.getDefaultGame(playerMap.size());//・・・(3)
  AIWolfGame game = new AIWolfGame(gameSetting, gameServer);//・・・(4)
  game.setLogFile(new File("./log/TestStarter.log"));//・・・(5)
  game.start();//・・・(6)
  Team winner = game.getWinner();//・・・(7)
  if(winner == Team.VILLAGER){
   System.out.println("Win!");
  }
  GameData gameData = game.getGameData();//・・・(8)

 }

}

ソースについて説明する.

メインクラスの説明

(1)役職の登録
まずエージェントクラスと当該エージェントを付けたい役職をMap型クラスに登録する.
新しいインスタンスを作成し,当該エージェントを付けたRoleをMapに登録していくだけである.
この時,各役職の人数制限を超えたエージェントが同じ役職に希望していた場合,その中のいずれかのエージェントが希望する役職になり, それ以外のエージェントは余った役職に割り当てられる.
また,役職を特に指定したくないエージェントはnullをsetすればよい.
この例では,自作のBasePlayerだけVillagerを指定し,それ以外はSampleRoleAssignPlayerとして,いずれも特に役職を指定していない.
(2)Connectorの作成
人狼サーバは,TCPIPを使ってサーバとエージェントを接続するが,テスト用環境ではTCPIPを使う必要性がない. そのような場合は,DirectConnectServerを使う.
ここでは,先ほどの希望役職を指定したMapを引数として,DirectConnectServerを作成している.
なお,すべてのエージェントにランダムに役職を与えて実行する場合は,
List<Agent> playerList = new ArrayList<Agent>();
//中略
DirectConnectServer gameServer = new DirectConnectServer(playerList);
のように,AgentのListを引数としてDirectConnectServerを作成すればよい.
(3)GameSettingの作成
GameSettingクラスは,さまざまなゲームの条件を決めるクラスである.
各役職に付けるエージェント数や,各自の投票結果を他のエージェントに知らせるか,などを決定するものである.
現状では,
GameSetting.getDefaultGameエージェント数);
で取得できるデフォルトのGameSettingを利用することが推奨されている.
これ以外のGameSettingを利用した場合の動作の保証はない.
(4)AIWolfGameの作成
ここでは,ゲーム実行サーバ本体の作成を行っている.
GameSettingとGameServerを指定して作成する.
(5)LogFileの設定
ゲームログを出力する場合,ここでログファイルを指定する.
ログを設定しない場合,ゲームログはコンソールにしか表示されない.
(6)ゲームの開始
game.start();
によって,ゲームが開始される.以降,ゲームが終了するまで自動で処理される.
(7)勝利チームの獲得
ゲーム終了後,どちらが勝ったのかを取得する.
勝利したかどうかに応じてエージェントを進化させたい場合などに利用することが可能.
単純に何回も実行して勝率を確認するためにも利用できる.
(8)全ゲームデータの取得
GameData gameData = game.getGameData();
で獲得できるGameDataは,このゲームで行われたすべての行動が記録されているデータクラスである.
このデータクラスを使って,ゲームの解析を行うことが可能.

以上で,起動メインクラスの説明は終了.
いくつかエージェントを作って,Roleを割り当て方を変えながらエージェントの実力をテストしてみればよいと思う.

2014年6月26日木曜日

人狼知能エージェントの作成・その2 Talk編

Talkの仕様変更に伴い,本記事の内容は利用できなくなりました.人狼知能エージェントの作成・その5 Talk編をご覧ください.






さて,人狼知能作成その2である. 今回は会話にチャレンジする.
とりあえず,人狼知能のサーバ,クライアントなどをGitHubにUpしたので, 中身を見たい人は https://github.com/aiwolf/ を参照して欲しい.
この記事は,書いている時点で最新のライブラリを使っているので,古いバージョンだと一部動かないこともあるかもしれないし, 新しいバージョンになっても変更があるかもしれないので,その点はご了承ください.
まだ,開発中のライブラリということで,ご勘弁を・・・
一応動かない機能が発生する場合はマイナーバージョンをアップするつもりではいます.
ちなみに,現在これを書いている段階で,バージョンは0.1.6.

発話の取得

ところで,前回までは村人エージェントについて,適当に自分以外の人に投票するようにした.
今回は,ほかの人が何を言っているのか聞いてみたいと思う.
他の人が話したことは,GameInfo#gameInfo.getTalkList()で獲得可能である.
まずは,自分の話す番が来るたびにそれまでの会話を確認するようにしてみよう.
自分が話す番が来たら,talkメソッドが呼ばれるので,そこに以下のように記述する.
 @Override
 public String talk() {
  GameInfo gameInfo = getLatestDayGameInfo();
  List<Talk> talkList = gameInfo.getTalkList();
  
  System.out.println("Today's talk");
  for(Talk talk:talkList){
   System.out.println(talk);
  }
  return null;
 }
これを実行すると,
Today's talk
Day00[000] Agent[02] 5 werewolf
中略
Today's talk
Day01[000] Agent[02] 6 werewolf
Day01[001] Agent[04] ( 11 werewolf ) and ( 4 comingout seer ) and ( 3 inspected HUMAN ) 
Day01[002] Agent[09] 4 werewolf
Day01[003] Agent[10] 2 werewolf
Day01[004] Agent[01] 12 werewolf
Day01[005] Agent[03] 12 werewolf
Day01[006] Agent[12] 2 werewolf
中略
Today's talk
Day01[000] Agent[02] 6 werewolf
Day01[001] Agent[04] ( 11 werewolf ) and ( 4 comingout seer ) and ( 3 inspected HUMAN ) 
Day01[002] Agent[09] 4 werewolf
Day01[003] Agent[10] 2 werewolf
Day01[004] Agent[01] 12 werewolf
Day01[005] Agent[03] 12 werewolf
Day01[006] Agent[12] 2 werewolf
Day01[007] Agent[06] 1 werewolf
Day01[008] Agent[11] 10 werewolf
Day01[009] Agent[11] Over
Day01[010] Agent[10] Over
Day01[011] Agent[03] Over
Day01[012] Agent[04] Over
中略
Today's talk
Day01[000] Agent[02] 6 werewolf
Day01[001] Agent[04] ( 11 werewolf ) and ( 4 comingout seer ) and ( 3 inspected HUMAN ) 
Day01[002] Agent[09] 4 werewolf
Day01[003] Agent[10] 2 werewolf
Day01[004] Agent[01] 12 werewolf
Day01[005] Agent[03] 12 werewolf
Day01[006] Agent[12] 2 werewolf
Day01[007] Agent[06] 1 werewolf
Day01[008] Agent[11] 10 werewolf
Day01[009] Agent[11] Over
Day01[010] Agent[10] Over
Day01[011] Agent[03] Over
Day01[012] Agent[04] Over
Day01[013] Agent[06] Over
Day01[014] Agent[02] 9 werewolf
Day01[015] Agent[09] Over
Day01[016] Agent[12] Over
Day01[017] Agent[01] Over
Day01[018] Agent[09] Over
Day01[019] Agent[11] Over
Day01[020] Agent[01] Over
Day01[021] Agent[06] Over
Day01[022] Agent[12] Over
Day01[023] Agent[04] Over
というように表示されるはず.
これを見れば分かる通り,getTalkListは,gameInfoが指す日のすべてのTalkを返してくる. したがって,前回からの差分は取ることができない.
差分がとりたい場合は,前回どこまでのTalkを取得したか記憶しておく必要がある.
そこで,アップデートされたTalkだけを取得するメソッドgetUpdatedTalkを作成してみる.
 @Override
 public String talk() {
  List<Talk> talkList = getUpdatedTalk();
  
  System.out.println("Updated talk");
  for(Talk talk:talkList){
   System.out.println(talk);
  }
  return null;
 }

 /**
  * 前回最後に獲得したTalkのインデックス
  */
 int lastTalkIdx = -1;
 
 /**
  * 前回最後に獲得したTalkの日付
  */
 int lastTalkDay = -1;
 
 /**
  * 前回Talkを取得した後投稿されたTalkのみを獲得する
  * @return 前回獲得したTalkとの差分が入ったList
  */
 protected List<Talk> getUpdatedTalk(){
  GameInfo gameInfo = getLatestDayGameInfo();
  List<Talk> talkList = gameInfo.getTalkList();
  
  if(lastTalkDay != gameInfo.getDay()){
   lastTalkDay = gameInfo.getDay();
   if(!talkList.isEmpty()){
    lastTalkIdx = talkList.get(talkList.size()-1).getIdx();
   }
   return talkList;
  }
  for(int i = 0; i < talkList.size(); i++){
   if(talkList.get(i).getIdx() > lastTalkIdx){
    lastTalkIdx = talkList.get(talkList.size()-1).getIdx();
    return talkList.subList(i, talkList.size());
   }
  }
  return new ArrayList<>();
 }
Updated talk
Day00[000] Agent[04] 7 werewolf
Day00[001] Agent[08] 4 werewolf
Day00[002] Agent[05] 11 werewolf
Day00[003] Agent[10] 4 werewolf
Day00[004] Agent[11] 4 werewolf
Day00[005] Agent[01] 5 werewolf
Day00[006] Agent[12] 8 werewolf
Day00[007] Agent[07] 10 werewolf
Day00[008] Agent[02] 10 werewolf
Day00[009] Agent[06] 10 werewolf
中略
Updated talk
Day00[010] Agent[09] 6 werewolf
Day00[011] Agent[08] Over
中略
Updated talk
Day02[000] Agent[01] 10 werewolf
Day02[001] Agent[10] ( 2 werewolf ) and ( 10 comingout seer ) and ( 9 inspected HUMAN ) and ( 6 inspected werewolf ) 
Day02[002] Agent[06] ( 1 werewolf ) and ( 6 comingout medium ) and ( 3 medium_telled HUMAN ) and ( 4 medium_telled HUMAN ) 
Day02[003] Agent[02] 6 werewolf
Day02[004] Agent[09] 6 werewolf
Day02[005] Agent[11] 6 werewolf
というわけで,無事前回との差分を獲得できた.

発話の内容

さて,次に他のエージェントが何を言っているのかを理解しよう.
ログに表示される
Day00[000] Agent[04] 7 werewolf
は,0日目のTalkIndex000は,Agent04が,「7 werewolf」と発言していることを示している.
Day00[011] Agent[08] Over
は,エージェント08はもうこれ以上話すことはない,という発言をしている.
さらに,
Day02[001] Agent[10] ( 2 werewolf ) and ( 10 comingout seer ) and ( 9 inspected HUMAN ) and ( 6 inspected werewolf ) 
Day02[002] Agent[06] ( 1 werewolf ) and ( 6 comingout medium ) and ( 3 medium_telled HUMAN ) and ( 4 medium_telled HUMAN ) 
では,エージェント10が,「( 2 werewolf ) and ( 10 comingout seer ) and ( 9 inspected HUMAN ) and ( 6 inspected werewolf ) 」と発言し, 同様にAgent06も長い発言をしている.
このうち,Overは良いとして,それ以外の発話はどういう意味だろうか.
まず,
Day00[000] Agent[04] 7 werewolf
は,「Agent7が人狼ではないかと疑っている」宣言である.事実上,今日投票するのはAgent7であると同等のもの・・・らしい.
(これは,SamplePlayerの実装依存だが,ソースコードを見ると,そうなっている)
次に,長い発話についてみてみよう.
Day02[001] Agent[10] ( 2 werewolf ) and ( 10 comingout seer ) and ( 9 inspected HUMAN ) and ( 6 inspected werewolf ) 
については,
  • Agent2が人狼だと思う
  • Agent10は占い師だとカミングアウトする
  • Agent9は占いの結果人間だった
  • Agent6は占いの結果人狼だった
という4つの内容が含まれた発話である.
一方,
Day02[001] Agent[10] ( 2 werewolf ) and ( 10 comingout seer ) and ( 9 inspected HUMAN ) and ( 6 inspected werewolf ) 
Day02[002] Agent[06] ( 1 werewolf ) and ( 6 comingout medium ) and ( 3 medium_telled HUMAN ) and ( 4 medium_telled HUMAN ) 
は,
  • Agent1が人狼だと思う
  • Agent6は霊媒師だとカミングアウトする
  • Agent3は霊媒の結果人間だった
  • Agent4は霊媒の結果人間だった
という内容を含んだ発話である.

発話の理解

発話は上記のようにテキストとして送られてくるため,それをエージェントが理解する必要がある. このとき,直接発話のテキストを見て,何を言っているのか理解をしてもよいが, そのためのParser(構文解析機)を作るのは手間がかかるため,aiwolf-clientライブラリでは,発話をマシンリーダブルに変換するためのParserライブラリが用意されている.
そこで,実際にTalkの内容をパースしてみよう.
Talkのパースには,Protocolクラスを利用する.
Protocolクラスは,人狼プロトコルを理解するためのクラスで,複数のUtteranceから構成され, UtteranceはSentenceTypeとPassageからなり・・・という多層構造を持っている.
大まかなProtocolクラスの概略は以下の通り.

プロトコルクラスのインスタンスをパースした発話から作成すると,この構造に従って発話をパースしてくれる.
実際に以下のコードで実行してみる.
なお,ここではその日の会話を総合して投票先を決める,ということを考え,talkメソッドではなく,voteメソッドでtalkのパースを行う.
 @Override
 public Agent vote() {
  GameInfo gameInfo = getLatestDayGameInfo();
  Agent myself = gameInfo.getAgent();

  
  List<Talk> talkList = gameInfo.getTalkList();
  for(Talk talk:talkList){
   System.out.println(talk);
   Protocol protocol = new Protocol(talk.getContent());
   List<Utterance> utteranceList = protocol.getUtterances();
   System.out.println("Num of Utterance="+utteranceList.size());
   for(Utterance utterance:utteranceList){
    SentenceType sentenceType = utterance.getSentenceType();
    Passage passage = utterance.getPassage();
    
    if(sentenceType != null){
     System.out.println("sentenceType");
     System.out.println(sentenceType.getUv());
     System.out.println(sentenceType.getRate());
    }
    
    if(passage != null){
     System.out.println("passage");
     System.out.println("Action="+passage.getAction());
     System.out.println("Attribution="+passage.getAttribution());
     System.out.println("Category="+passage.getCategory());
     System.out.println("Object="+passage.getObject());
     System.out.println("State="+passage.getState());
     System.out.println("Subject="+passage.getSubject());
     System.out.println("Verb="+passage.getVerb());
    }
    
   }
  }
  
  List<Agent> agentList = gameInfo.getAliveAgentList();

  for(Agent agent:agentList){
   if(agent != myself){
    System.out.println(myself+" vote to "+agent);
    return agent;
   }
  }
  throw new AIWolfRuntimeException("Something wrong");
 }

すると,「7 werewolf」に関してだと,以下のような出力が得られる.
Day00[003] Agent[07] 7 werewolf
Num of Utterance=1
passage
Action=null
Attribution=null
Category=ESTIMATE
Object=null
State=werewolf
Subject=Agent[07]
Verb=is
特に内容がないところにはnullが入ってくるので注意が必要.
さて,この中身について具体的にみる. まず「7 werewolf」という発話は,「Agent7は人狼だと思う」という意味を持つ.
文としては単文のため,Utteranceは1つ.
そのUtteranceを分解すると,
  • カテゴリは推定(ESTIMATE)
  • 対象(Subject)はAgent07
  • 動詞はis
  • 状態は人狼(werewolf)
となっていることが分かる.

一方,長い発話の場合はどうなるか, 「( 2 werewolf ) and ( 4 comingout seer ) and ( 12 inspected HUMAN ) and ( 10 inspected 」を例に見てみよう.
Day03[005] Agent[04] ( 2 werewolf ) and ( 4 comingout seer ) and ( 12 inspected HUMAN ) and ( 10 inspected werewolf ) 
Num of Utterance=4
passage2 werewolf部分のUtterance
Action=null
Attribution=null
Category=ESTIMATE
Object=null
State=werewolf
Subject=Agent[02]
Verb=is
passage//4 comingout seer部分のUtterance
Action=null
Attribution=null
Category=COMINGOUT
Object=seer
State=null
Subject=Agent[04]
Verb=comingout
passage//12 inspected HUMAN部分のUtterance
Action=inspected
Attribution=Human
Category=RESULT
Object=null
State=null
Subject=Agent[12]
Verb=inspected
passage//10 inspected部分のUtterance
Action=inspected
Attribution=Werewolf
Category=RESULT
Object=null
State=null
Subject=Agent[10]
Verb=inspected
となる.二番目のPassageにおいて,このエージェントがseerであるとCOMINGOUTしていることが分かる.

他エージェントの発話を考慮した投票先決定

では,最後に他のエージェントの発話に基づいて投票先を決めるようにしてみよう.
ここでは,簡単のために
「seerだとカミングアウトしたエージェントの投票に合わせる」.
という戦略を実装する.

 /**
  * 人狼候補
  */
 Set werewolfCandidateSet = new HashSet<>();
 
 @Override
 public Agent vote() {
  GameInfo gameInfo = getLatestDayGameInfo();
  Agent myself = gameInfo.getAgent();

  List<Talk> talkList = gameInfo.getTalkList();
  for(Talk talk:talkList){
   Protocol protocol = new Protocol(talk.getContent());
   List<Utterance> utteranceList = protocol.getUtterances();
   for(Utterance utterance:utteranceList){
    SentenceType sentenceType = utterance.getSentenceType();
    Passage passage = utterance.getPassage();
    
    
    if(passage != null){
     if(passage.getCategory() == Category.COMINGOUT){
      if(passage.getObject() == Role.seer){
       Agent candidate = talk.getAgent();
       seerCandidateSet.add(candidate);
      }
     }
    }
   }
  }
  
  System.out.println("Find Candidate");
  //占い師の意見だけ採用
  for(Talk talk:talkList){
   if(!seerCandidateSet.contains(talk.getAgent())){
    continue;
   }
   Protocol protocol = new Protocol(talk.getContent());
   List<Utterance> utteranceList = protocol.getUtterances();
   for(Utterance utterance:utteranceList){
    Passage passage = utterance.getPassage();
    
    if(passage.getCategory() == Category.RESULT && passage.getVerb() == Verb.inspected){
     if(passage.getAttribution() == Species.Werewolf){
      System.out.println(talk);
      Agent candidate = passage.getSubject();
      if(candidate != myself){
       werewolfCandidateSet.add(candidate);
      }
      else{
       //自分を人狼だと言ってきたら,その占い師こそが人狼だ!
       seerCandidateSet.remove(talk.getAgent());
       werewolfCandidateSet.add(talk.getAgent());
      }
     }
    }
   }
  }
  
  for(Agent candidate:seerCandidateSet){
   System.out.println("Seer candidate:"+candidate+" "+gameInfo.getStatusMap().get(candidate));
  }
  for(Agent candidate:werewolfCandidateSet){
   System.out.println("Werewolf candidate:"+candidate+" "+gameInfo.getStatusMap().get(candidate));
  }
  
  //見つかった人狼候補に生きているものがいれば,そこに投票
  for(Agent candidate:werewolfCandidateSet){
   if(gameInfo.getStatusMap().get(candidate) == Status.alive){
    System.out.println(myself+" vote to "+candidate+" by seer's result");
    return candidate;
   }
  }
  
  //生きている人狼候補がいなければ,適当に投票
  List<Agent> agentList = gameInfo.getAliveAgentList();

  for(Agent agent:agentList){
   if(agent != myself || seerCandidateSet.contains(agent)){
    System.out.println(myself+" vote to "+agent);
    return agent;
   }
  }
  throw new AIWolfRuntimeException("Something wrong");
 }

この結果以下のようになった.
Find Candidate
Day02[008] Agent[04] ( 12 werewolf ) and ( 3 inspected werewolf ) 
Seer candidate:Agent[04] alive
Seer candidate:Agent[12] alive
Werewolf candidate:Agent[03] alive
Agent[10] vote to Agent[03] by seer's result

中略

===========
Day 03
Agent[03] executed
Agent[12] divine Agent[04]. Result is Human
Agent[07] guarded Agent[09]@2 guarded
null attacked
======
Agent[01] SamplePlayer dead medium
Agent[02] SamplePlayer alive villager
Agent[03] SamplePlayer dead werewolf executed
Agent[04] SamplePlayer alive possessed divined
Agent[05] SamplePlayer alive villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer alive villager guarded
Agent[10] BasePlayer alive villager
Agent[11] SamplePlayer alive villager
Agent[12] SamplePlayer alive seer
というわけで,seer(Agent12)の占い結果に基づいてAgent03に投票し, 結果として人狼を処刑することに成功した.
今回は,他のエージェントのTalkを理解して,それを投票につなげるコードを書いてみた.
次は,自分の意見をTalkで表明することを目指してみよう.

2014年6月20日金曜日

人狼知能エージェントの作成・その1

ver0.1.16に対応するよう改訂しました(2015/03/07).
ver0.1.15までとの最大の違いは,RoleAssignPlayerに挿入するPlayerがPlayerクラスを継承しなくなったことです.
AbstractVillagerPlayer→AbstractVillagerのようになりました.
ご了承ください.
今日は人狼知能エージェントを作ろうと思うので,その作り方をメモしていく.

まずは,http://www.aiwolf.org/の資料→人狼知能サーバから人狼知能0.1.15をダウンロード.

中身は簡易マニュアルとjarファイルが4つ.
aiwolf-client-0.1.16.jar
aiwolf-common-0.1.16.jar
aiwolf-server-0.1.16.jar
jsonic-1.3.2.jar

Eclipseで新しいプロジェクトを作って,これらのjarファイルをライブラリに登録しておく.

さて,人狼知能エージェントを作るには,まずRoleBasePlayerを継承したクラスを作るところから始まる.
とりあえず,面倒なのでBasePlayerという名前で作成.


BasePlayerは何をやるかというと,職業が割り当てられたときに,職業ごとに異なるPlayerを起動してくれるのである.
人狼においては,占い師になったときと人狼になったときでは,プレイ方法が全然異なる.
そこで,職業が割り当てられた時点で,職業ごとに対応するPlayerクラスを立ち上げるようにしておけば,一つのPlayerクラスに付き一つの職業についてのみ考えれば良くなるわけである.

ここでは,まず村人から作ってみよう.
というわけで,新しくMyVillagerPlayerを作成する.
自作村人エージェントを作るには,org.aiwolf.client.base.player.AbstractViillagerPlayerを継承したクラスを作れば良い.

package org.aiwolf.player;

import org.aiwolf.client.base.player.AbstractVillager;
import org.aiwolf.common.AIWolfRuntimeException;
import org.aiwolf.common.data.Agent;
import org.aiwolf.common.net.GameInfo;

public class MyVillagerPlayer extends AbstractVillager {

 @Override
 public void dayStart() {
  // TODO 自動生成されたメソッド・スタブ
  
 }

 @Override
 public void finish() {
  // TODO 自動生成されたメソッド・スタブ
  
 }

 @Override
 public String talk() {
  // TODO 自動生成されたメソッド・スタブ
  return null;
 }

 @Override
 public Agent vote() {
  // TODO 自動生成されたメソッド・スタブ
  return null;
 }

}


今のところ何もしないが,とりあえずこのMyVillagerPlayerをBasePlayerに登録する.
package org.aiwolf.player;

import org.aiwolf.client.base.player.AbstractRoleAssignPlayer;

public class BasePlayer extends AbstractRoleAssignPlayer {

 public BasePlayer() {
  setVillagerPlayer(new MyVillager());
 }

 @Override
 public String getName() {
  return BasePlayer.class.getSimpleName();
 }
}
まずは,ここで起動してみよう.
起動する場合は,Eclipseのメニューで実行→実行構成
名前はRoleRequestStarterとでもしておいて,メインクラスにRoleRequestStarterを選ぶ.
さらに,引数タグを選んで,プログラムの引数に
-n 12 -c org.aiwolf.player.BasePlayer VILLAGER
と書いておこう.






これで,実行.
Agent[04] request no role
Agent[08] request no role
Agent[01] request no role
Agent[05] request no role
Agent[03] request no role
Agent[10] request no role
Agent[02] request no role
Agent[11] request no role
Agent[09] request villager
Agent[12] request no role
Agent[06] request no role
Agent[07] request no role
===========
Day 00
======
Agent[01] SamplePlayer alive possessed
Agent[02] SamplePlayer alive villager
Agent[03] SamplePlayer alive villager
Agent[04] SamplePlayer alive bodyguard
Agent[05] SamplePlayer alive seer
Agent[06] SamplePlayer alive werewolf
Agent[07] SamplePlayer alive werewolf
Agent[08] SamplePlayer alive medium
Agent[09] BasePlayer alive villager
Agent[10] SamplePlayer alive villager
Agent[11] SamplePlayer alive villager
Agent[12] SamplePlayer alive villager
10-2
===========
こんな感じでBasePlayerがvilagerになっていれば起動は完了.
その後は自動でゲームが進行する. ところで,ここではvoteでnullを返している.つまり投票先を決めていないことになる.
この場合,ver0.1.16ではランダムに投票したことになっている.
別に問題は無いが,せっかくなのでnull以外を返すように変更してみよう.

変更するのは,MyVillagerのvoteメソッド.
 @Override
 public Agent vote() {
  // TODO 自動生成されたメソッド・スタブ
  return null;
 }
まずは,簡単に
・生きているエージェントの中で
・自分を除いて
・一番最初に見つけたエージェント
に投票することにしよう.

進行中のゲームに関する全ての情報は,
getGameInfoで取得可能.
特に,最新の情報は

GameInfo gameInfo = getLatestDayGameInfo();
で取得可能.
GameInfoからは様々な情報が取得可能だが,まずは自分自身のエージェントと状態,職業,そして日付を取得してみよう.
  GameInfo gameInfo = getLatestDayGameInfo();
  Agent myself = gameInfo.getAgent();
  System.out.println("I am "+gameInfo.getStatusMap().get(myself));
  System.out.println("I am "+gameInfo.getRole());
  System.out.println("Today is "+gameInfo.getDay()+" day");
と書いておくと,
I am alive
I am villager
Today is 0 day
と表示され,とりあえずちゃんと動いていることが分かる.
さて,次はいよいよ投票する対象を決めよう.
生きているエージェント一覧は,gameInfo.getAliveAgentList();で取得可能.
このとき,返ってきたエージェントの中で自分じゃない最初のエージェントに投票することにする.
 @Override
 public Agent vote() {
  GameInfo gameInfo = getLatestDayGameInfo();
  Agent myself = gameInfo.getAgent();
  List<agent> agentList = gameInfo.getAliveAgentList();

  for(Agent agent:agentList){
   if(agent != myself){
    System.out.println(myself+" vote to "+agent);
    return agent;
   }
  }
  throw new AIWolfRuntimeException("Something wrong");
 }
自分しかいなかった場合は誰も選ばれずにメソッドが終わってしまうが,ゲームの仕様上そんなことはないので, もしそんなことがあったらAIWolfRuntimeExceptionを投げて終了することにしておこう.
さて,コレで実行するとどうなるか.
6 20, 2014 4:24:48 午後 org.aiwolf.common.util.AiWolfLoggerFactory getSimpleLogger
構成: ログ設定: LogManagerを設定しました。
Agent[07] request no role
Agent[04] request no role
Agent[03] request no role
Agent[01] request no role
Agent[09] request no role
Agent[10] request no role
Agent[08] request no role
Agent[11] request no role
Agent[05] request villager
Agent[12] request no role
Agent[02] request no role
Agent[06] request no role
===========
Day 00
======
Agent[01] SamplePlayer alive seer
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium
Agent[05] BasePlayer alive villager
Agent[06] SamplePlayer alive werewolf
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer alive villager
Agent[10] SamplePlayer alive villager
Agent[11] SamplePlayer alive villager
Agent[12] SamplePlayer alive villager
10-2
===========

中略

Agent[05] vote to Agent[03]
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,5,3
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,6,6
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,11,6
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,12,1
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,9,2
6 20, 2014 4:24:48 午後 org.aiwolf.server.AIWolfGame vote
情報: 0,vote,10,8

中略

===========
Day 01
Agent[06] executed
Agent[01] divine Agent[09]. Result is Human
Agent[07] guarded Agent[08]@0 guarded
Agent[11] attacked
======
Agent[01] SamplePlayer alive seer
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium
Agent[05] BasePlayer alive villager
Agent[06] SamplePlayer dead werewolf executed
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager guarded
Agent[09] SamplePlayer alive villager divined
Agent[10] SamplePlayer alive villager
Agent[11] SamplePlayer dead villager attacked
Agent[12] SamplePlayer alive villager
9-1

中略

===========
Day 02
Agent[10] executed
Agent[01] divine Agent[12]. Result is Human
Agent[07] guarded Agent[01]@1 guarded
null attacked
======
Agent[01] SamplePlayer alive seer guarded
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium
Agent[05] BasePlayer alive villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer alive villager
Agent[10] SamplePlayer dead villager executed
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer alive villager divined

中略
===========
Day 03
Agent[05] executed
Agent[01] divine Agent[04]. Result is Human
Agent[07] guarded Agent[02]@2 guarded
Agent[09] attacked
======
Agent[01] SamplePlayer alive seer
Agent[02] SamplePlayer alive werewolf guarded
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium divined
Agent[05] BasePlayer dead villager executed
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer dead villager attacked
Agent[10] SamplePlayer dead villager
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer alive villager
中略
===========
Day 04
Agent[01] executed
Agent[01] divine Agent[08]. Result is Human
Agent[07] guarded Agent[08]@3 guarded
Agent[01] attacked
======
Agent[01] SamplePlayer dead seer executed attacked
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium
Agent[05] BasePlayer dead villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer alive bodyguard
Agent[08] SamplePlayer alive villager divined guarded
Agent[09] SamplePlayer dead villager
Agent[10] SamplePlayer dead villager
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer alive villager

中略

===========
Day 05
Agent[07] executed
Agent[07] guarded Agent[04]@4 guarded
null attacked
======
Agent[01] SamplePlayer dead seer
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer alive medium guarded
Agent[05] BasePlayer dead villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer dead bodyguard executed
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer dead villager
Agent[10] SamplePlayer dead villager
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer alive villager

中略

===========
Day 06
Agent[12] executed
Agent[04] attacked
======
Agent[01] SamplePlayer dead seer
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer alive possessed
Agent[04] SamplePlayer dead medium attacked
Agent[05] BasePlayer dead villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer dead bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer dead villager
Agent[10] SamplePlayer dead villager
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer dead villager executed

===========
Day 07
Agent[03] executed
Agent[03] attacked
======
Agent[01] SamplePlayer dead seer
Agent[02] SamplePlayer alive werewolf
Agent[03] SamplePlayer dead possessed executed attacked
Agent[04] SamplePlayer dead medium
Agent[05] BasePlayer dead villager
Agent[06] SamplePlayer dead werewolf
Agent[07] SamplePlayer dead bodyguard
Agent[08] SamplePlayer alive villager
Agent[09] SamplePlayer dead villager
Agent[10] SamplePlayer dead villager
Agent[11] SamplePlayer dead villager
Agent[12] SamplePlayer dead villager
===========
Send finish to Agent[03]
Send finish to Agent[04]
Send finish to Agent[01]
Send finish to Agent[02]
Send finish to Agent[07]
Send finish to Agent[08]
Send finish to Agent[05]
Send finish to Agent[06]
Send finish to Agent[11]
Send finish to Agent[12]
Send finish to Agent[09]
Send finish to Agent[10]
Winner:werewolf
というわけで,ちゃんと別のエージェントを指定して投票行動を行っていたことが分かります.
ただし,三日目に本人が処刑されちゃったみたいですね.
しかも,最後は人狼の勝利.
投票行動を適当に行うだけではやはり勝てそうもありません.
次回は,他の人が何を言っているのか理解して,占い師が人狼だと言っているエージェントに投票するように 改良してみたいと思います.
できるのかなあ?



ちなみに,今日作ったMyVillagerのソースコードは以下の通り.
package org.aiwolf.player;

import java.util.List;

import org.aiwolf.client.base.player.AbstractVillager;
import org.aiwolf.common.AIWolfRuntimeException;
import org.aiwolf.common.data.Agent;
import org.aiwolf.common.net.GameInfo;

public class MyVillager extends AbstractVillager {

 @Override
 public void dayStart() {
  // TODO 自動生成されたメソッド・スタブ
  
 }

 @Override
 public void finish() {
  // TODO 自動生成されたメソッド・スタブ
  
 }

 @Override
 public String talk() {
  // TODO 自動生成されたメソッド・スタブ
  return null;
 }

 @Override
 public Agent vote() {
  GameInfo gameInfo = getLatestDayGameInfo();
  Agent myself = gameInfo.getAgent();

  List agentList = gameInfo.getAliveAgentList();

  for(Agent agent:agentList){
   if(agent != myself){
    System.out.println(myself+" vote to "+agent);
    return agent;
   }
  }
  throw new AIWolfRuntimeException("Something wrong");
 }

}
BasePlayerのソースコードは以下の通り.
package org.aiwolf.player;

import org.aiwolf.client.base.player.AbstractRoleAssignPlayer;

public class BasePlayer extends AbstractRoleAssignPlayer {

 public BasePlayer() {
  setVillagerPlayer(new MyVillager());
 }

 @Override
 public String getName() {
  return BasePlayer.class.getSimpleName();
 }
}