Upload
kengo009
View
1.524
Download
2
Embed Size (px)
Citation preview
ビルド・パスの構成• プレイヤー作成に必要な jar ファイルをビ
ルド・パスに追加– “ http://www.aiwolf.org/ ” から「資料>人狼知
能サーバ> aiwolf-ver0.2.1 」をダウンロード– 中身は 3 つの jar ファイル
Jar ファイルの概要• aiwolf-client– プレイヤーを作成する際に用いる
• aiwolf-common– server と client 両方で用いる
• aiwolf-server– ゲームを実行する際に用いる
自作プレイヤーでゲーム実行1. AbstractRoleAssignPlayer を継承したクラス
を作成2. 実行の構成を変更3. 各役職の Player を作成4. RoleAssignPlayer に各役職の Player をセッ
ト
自作プレイヤーでゲーム実行1. AbstractRoleAssignPlayer を継承したクラス
を作成2. 実行の構成を変更3. 各役職の Player を作成4. RoleAssignPlayer で各役職の Player をセッ
ト
まず実行できる環境を整える
新規パッケージ作成(2)• パッケージ名を入力して完了– パッケージ名は自分のアドレスを逆から入力• 例) [email protected] → org.aiwolf.myAgent
実行の構成(2)引数>プログラムの引数に下記を入力 「 -n 11 -c org.aiwolf.KajiwaraAgent.KajiwaraRoleAssignPlayer SEER 」 プレイヤー数 パッケージ名 クラス名 希望役職
自作プレイヤーでゲーム実行1. AbstractRoleAssignPlayer を継承したクラス
を作成2. 実行の構成を変更3. 各役職の Player を作成4. RoleAssignPlayer で各役職の Player をセッ
ト
例として占い師を作ってみましょう
MyRoleAssignPlayer を変更• MyRoleAssignPlayer に次のコンストラクタを
追加
– 役職として占い師を振り分けられた時にKajiwaraSeerPlayer を呼び出す
– 他の役職の場合はデフォルトでサンプルプレイヤーが呼び出される
public MyRoleAssignPlayer(){ setSeerPlayer(new KajiwaraSeer());}
Player インターフェースPlayer インターフェース内の Abstract Method
ゲームの各タイミングでサーバがプレイヤーのこれらのメソッドを呼び出す
• getName()• update(GameInfo gameinfo)• initialize(GameInfo gameinfo)• dayStart()• talk()• whisper()
• vote()• attack()• divine()• guard()• finish()
ゲームの流れinitialize(GameInfo)
vote()divine() (占い師のみ)attack() (人狼のみ)guard() (狩人のみ)
update(GameInfo)whisper() (人狼のみ)
talk()
update(GameInfo)dayStart()
update(GameInfo)finish()
会話終了
ゲーム終了
1 日
各メソッドの戻り値
• getName()• update(GameInfo
gameinfo)• initialize(GameInfo
gameinfo)• dayStart()• talk()• whisper()
• vote()• attack()• divine()• guard()• finish()
全体,もしくは囁きで発話するメソッド戻り値: String
対象プレイヤーを選択するメソッド戻り値: Agent
MySeer の実装• MySeer が継承した AbstractSeer– 占い師に必要ないメソッド( attack, guard
等)を事前に消してある– 毎朝,サーバから送られた占い結果を List に
格納する 等の簡単な実装がしてある
• 実装する必要のあるメソッド– talk(), divine(), vote() の3つ
実装の流れ1. vote の実装 (1)– ランダムに投票
2. divine の実装– ランダムに占う
3. vote の実装 (2)– 占い結果を考慮して投票
4. talk の実装– カミングアウト,占い結果の報告
実装の流れ1. vote の実装 (1)– ランダムに投票
2. divine の実装– ランダムに占う
3. vote の実装 (2)– 占い結果を考慮して投票
4. talk の実装– カミングアウト,占い結果の報告
@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 はサーバから送られてくるゲーム内情報.ログや占い結果等の全ての情報はここから取得する
続く
vote() の実装/** * 引数の Agent のリストからランダムに Agent を選択する * @param agentList * @return */private Agent randomSelect(List<Agent> agentList){ int num = new Random().nextInt(agentList.size()); return agentList.get(num);}
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() を実装してみよう
実装の流れ1. vote の実装 (1)– ランダムに投票
2. divine の実装– ランダムに占う
3. vote の実装 (2)– 占い結果を考慮して投票
4. talk の実装– カミングアウト,占い結果の報告
divine() の実装@Overridepublic 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()); } }
続く
divine() の実装 if(divineCandidates.size() > 0){ // 候補者リストからランダムに選択 return randomSelect(divineCandidates); }else{ // 候補者がいない場合は自分を占い return getMe(); }}
divine() の実装(全体像)@Overridepublic 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(); }}
次は占い結果を投票に反映させてみましょう
実装の流れ1. vote の実装 (1)– ランダムに投票
2. divine の実装– ランダムに占う
3. vote の実装 (2)– 占い結果を考慮して投票
4. talk の実装– カミングアウト,占い結果の報告
vote() の実装2実装例– 占いで人狼を見つけていたらその中からランダム– 人狼を見つけていなければ,自分と白判定が出たプ
レイヤー以外からランダム– まだ占っていない場合は,自分以外からランダム
※ 既に死んでいるプレイヤーから選択しないように注意
vote() の実装2@Overridepublic 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()); } } }
続く
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); }}
vote() の実装2(全体像)@Overridepublic 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); }}
次は占い結果を発話してみましょう
実装の流れ1. vote の実装 (1)– ランダムに投票
2. divine の実装– ランダムに占う
3. vote の実装 (2)– 占い結果を考慮して投票
4. talk の実装– カミングアウト,占い結果の報告
talk, whisper での発話主な発話は org.aiwolf.client.lib.TemplateTalkFactory( 全体用 ) org.aiwolf.client.lib.TemplateWhisperFactory( 囁き用 )で簡単に作成可能
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()– もう今日は話すことない
TemplateTalkFactory の使用例
// 占いの情報の取得Judge judge = getLatestDayGameInfo().getDivineResult();
// 発話の作成String talk = TemplateTalkFactory.divined(judge.getTarget(), judge.getResult());
今日の占い結果を報告する発話を生成
talk() の実装実装例– 占いで人狼を見つけたらカミングアウト– カミングアウトした後は占い結果を報告する– 話すことが無ければ” Over”
(プレイヤーが全員 Over を返せば会話のターンが終了)
talk() の実装boolean isComingOut = false; // 既に役職のカミングアウトをしているか
// 報告済みの Judge を格納List<Judge> myToldJudgeList = new ArrayList<Judge>();
@Overridepublic String talk() {
// 占いで人狼を見つけたらカミングアウトする if(!isComingOut){ for(Judge judge: getMyJudgeList()){ if(judge.getResult() == Species.WEREWOLF){ // 占い結果が人狼の場合 String comingoutTalk = TemplateTalkFactory.comingout(getMe(), getMyRole()); isComingOut = true; return comingoutTalk; } } }
続く
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;}
talk() の実装(全体像)//既に役職のカミングアウトをしているかboolean isComingOut = false;
@Overridepublic 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;}
次は他の人の発話を読み込んでみましょう
ログの読み込みログは GameInfo.getTalkList() で List<Talk>型として取得Talk クラス内のメソッドは 4 つ
– getAgent() :発話した Agent を取得– getContent() :発話内容( String )を取得– getDay() :発話日( int )を取得– getIdx() :その日の何番目の発話か( int )を取得
ログの読み込みTalk.getContent() で得られる String の中身は
“DIVINED Agent[04] HUMAN”のように人狼言語で記載 → Utterance クラスでパース//Utterance クラスのコンストラクタの引数に発話内容のString を入れると自動的にパースされるUtterance utterance = new Utterance(talk.getContent());
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 を返す
@Overridepublic 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 は読まないように変更してみる
ログの読み込み2//その日のログの何番目まで読み込んだかint readTalkNum = 0;
@Overridepublic void dayStart(){ super.dayStart(); readTalkNum = 0;}
オレンジ部分:追加赤部分:修正
@Overridepublic 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
ログの読み込み2(全体像)//その日のログの何番目まで読み込んだかint readTalkNum = 0;
@Overridepublic void dayStart(){ super.dayStart(); readTalkNum = 0;}
@Overridepublic 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 しているプレイヤーを敵リストに追加してみましょう
//偽占い師 CO しているプレイヤーのリスト List<Agent> fakeSeerCOAgent = new ArrayList<Agent>();
@Overridepublic 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 ( 全体像 )
Player の各メソッドの説明• initialize(GameInfo)– ゲーム開始時に一度だ
け呼ばれる– サーバから送られてく
る GameInfo を取得• update(GameInfo)– 各行動の前に呼ばれる– サーバから送られてく
る GameInfo を取得• dayStart()
– 日の初めに呼ばれる
• finish()– ゲームが終了した時に
呼ばれる
Player の各メソッドの説明
• vote()– 投票する相手を選択す
る• attack()– 襲撃する相手を選択す
る– 人狼のみ呼ばれる
• divine()– 占いする相手を選択す
る– 占い師のみ呼ばれる
• guard()– 護衛する相手を選択す
る– 狩人のみ呼ばれる
1 日の終わりに呼ばれるメソッド
Agent を返す必要あり
Player の各メソッドの説明
• talk()– 全体に対して発話する– 全員呼ばれるメソッド
• whisper()– 人狼だけに対して発話
する– 人狼のプレイヤーだけ
が使用するメソッド
発話のメソッド
String を返す必要あり
GameInfo の説明戻り値 メソッド名と説明int
getDay() 日にちを返す
RolegetRole() 自分の役職を返す
AgentgetAgent() 自分 (Agent型 ) を返す
List<Agent>getAgentList() 全プレイヤーのリストを返す
SpeciesgetMediumResult() 霊能結果を返す (霊能者のみ )
SpeciesgetDivineResult() 占い結果を返す ( 占い師のみ )
AgentgetExecutedAgent() 処刑されたプレイヤーを返す
AgentgetAttackedAgent() 襲撃されたプレイヤーを返す
List<Vote>getVoteList() 処刑の際の投票リストを返す
List<Vote>getAttackVoteList() 襲撃の際の人狼による投票のリストを返す ( 人狼のみ )
List<Talk>getTalkList() 会話のログを返す
List<Talk>getWhisperList() 囁きのログを返す ( 人狼のみ )
List<Agent>getAliveAgentList() 生きているプレイヤーのリストを返す
Map<Agent, Status>getStatusMap() 各プレイヤーの生死の状態を返す
Map<Agent, Role>getRoleMap() 各プレイヤーの役職を返す.ゲーム中は自分の分かる役
職のみ(村人なら自分だけ,人狼なら仲間の人狼も) ゲーム終了時は全員の役職が分かる.