More Related Content Similar to 人狼知能プログラミング演習資料2015 (20) 人狼知能プログラミング演習資料20155. 人狼ゲーム (Are You a Werewolf?)
目的:村人の中に紛れ込んだ人狼を見つけだす
昼: 参加者は議論によって、人狼と疑わしい村人を処刑する
夜:人狼は村人を一人襲う
村人には役職がある
占い: 夜に任意のプレイヤがどの陣営か知れる
狩人: 夜に人狼が襲いそうなプレイヤを守る
狂人: 陣営は人間側だが、勝利条件は人狼側と同じ、人狼側からは役割は
わからない。
7. 完全ゲーム vs. 不完全ゲーム
• 完全情報ゲーム
• お互いの情報が完全に与えられているゲーム
• 将棋,囲碁,チェスなど
• すでにコンピュータが人間を上回るものが多い
• 不完全情報ゲーム
• ゲーム情報が完全には与えられていないゲーム
• 推論の対象が多岐にわたる
• 定型的な研究の場は少ない
9. 実際に人狼をプレイしてみよう
• 人狼のゲームの進めかた
• ゲームマスタ: ゲームを進行する係
• プレイヤ:
• 村人サイド: 村人、占い師、狩人 など
• 人狼サイド: 人狼、狂人 など
※ http://jinrodou.com/whats-jinro/ より引用
占
狼
村 村
村
GM
一緒にプレイできる友達がいない人は
Youtube か ニコ動で動画をみてください
12. プログラム環境の準備(1)
• Eclipse 4 をインストール
• https://www.eclipse.org/downloads/
• 人狼知能ライブラリ
• DLサイト: http://aiwolf.org/server/
• aiwolf-client-0.X.X.jar
• プレイヤークライアント用ライブラリ
• aiwolf-common-0.X.X.jar
• Serverとclient両方で用いる共有ライブラリ
• aiwolf-server-0.X.X.jar
• ゲームサーバ用ライブラリ
• 本資料は 0.2.0 の時点で作成したものを 0.3.4 に合うよう
に修正したものである.未修正の部分があるかもしれな
いので,それには注意されたい
29. 犬 class
データ: 名前
データ: 年齢
データ:所有物
手続き:つかむ
手続き:移動する(徒歩)
人間 class
データ: 名前
データ: 年齢
データ:所有物
手続き:つかむ
手続き:移動する(徒歩)
Javaにおけるオブジェクトとは
• データと手続きをclassとしてまとめられる
32. クライアントライブラリの構成
• Playerインタフェース(AbstractRole)を継承
Player インタフェース
Villager Seer Bodygaurd Medium Werewolf Possessed
• getName()
• update(GameInfo
gameinfo)
• initialize(GameInfo
gameinfo)
• dayStart()
• talk()
• vote()
• finish()
各役職ごとのメソッドが定義されている
talk(), whisper()
全体,もしくは囁きで発話
するメソッド
戻り値:String
• guard()• divine() • attack()
• whisper()
vote(), divine(), guard(), attack()
対象プレイヤーを選択する
メソッド
戻り値:Agent
37. 人狼知能サーバ: クラス構成
• エージェント情報
• 状態
• ロール
• 行動情報
• 結果
• ゲームターン管理
• データ管理
• gemeData
• 集計
• ゲームログ出力
ServerStarter
Port: int
PlayerNum: int
+main()
GameSetting
+getDefault(int): void
AIWolfGame
gameDataMap: Map<Integer, GameData>
+init():void
+start(): void
+setRandom(Random): void
GameServer
- agentList List<Agent>
+ getServerLogger(): void
+ waitForConnection(): void
Handler
AiWolfLogFormat
ゲーム開始クラス
ゲーム制御クラス
GaneData
-
+ getGaneinfoToSend(Agent)
ゲームデータ/1日単位通信用コネクタ
Talk Vote Judge Guard
Agent Status
39. AIWolfGameServer(1/4)
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.aiwolf.common.AIWolfRuntimeException;
import org.aiwolf.common.data.Agent;
import org.aiwolf.common.data.Guard;
import org.aiwolf.common.data.Judge;
import org.aiwolf.common.data.Role;
import org.aiwolf.common.data.Species;
import org.aiwolf.common.data.Status;
import org.aiwolf.common.data.Talk;
import org.aiwolf.common.data.Team;
import org.aiwolf.common.data.Vote;
import org.aiwolf.common.net.GameInfo;
import org.aiwolf.common.net.GameSetting;
import org.aiwolf.common.util.AiWolfLoggerFactory;
import org.aiwolf.common.util.Counter;
import org.aiwolf.server.net.GameServer;
import org.aiwolf.server.util.FileGameLogger;
import org.aiwolf.server.util.GameLogger;
※ 0.2.0 時点でのソースコード
40. AIWolfGameServer(2/4)
Random rand;
GameSetting gameSetting;
GameServer gameServer;
Map<Integer, GameData> gameDataMap;
GameData gameData;
boolean isShowConsoleLog = true;
File logFile;
GameLogger gameLogger;
Map<Agent, String> agentNameMap;
public AIWolfGame(GameSetting gameSeting, GameServer gameServer) {
rand = new Random();
this.gameSetting = gameSeting;
this.gameServer = gameServer;
gameLogger = AiWolfLoggerFactory.getSimpleLogger(this.getClass().getSimpleName());
}
public void setLogFile(File logFile) throws IOException {
gameLogger = new FileGameLogger(logFile);
}
public void setGameLogger(GameLogger gameLogger){
this.gameLogger = gameLogger;
}
<中略>
メソッド変数の宣言
• Random: 乱数生成器クラス: ただしJava の乱数は脆弱
• 乱数のインスタンスは可能な限り1つにする
• Map<K, V>: あるkeyでvalueを保持するクラス
• File: ファイルを扱うためのクラス
public クラス名(引数): コンストラクタ
オブジェクトが new されたときに呼び出される
部分
返値 メソッド名(引数) throw hoge
例外処理を行う: IOEcveption は入出力に関する例
外処理
41. AIWolfGameServer(3/4)
protected void init(){
gameDataMap = new TreeMap<Integer, GameData>();
gameData = new GameData(gameSetting);
agentNameMap = new HashMap<Agent, String>();
gameServer.setGameData(gameData);
List<Agent> agentList = gameServer.getConnectedAgentList();
if(agentList.size() != gameSetting.getPlayerNum()){
throw new IllegalPlayerNumException("Player num is “+gameSetting.getPlayerNum() +" but connected agent is "+agentList.size());
}
Collections.shuffle(agentList, rand);
Map<Role, List<Agent>> requestRoleMap = new HashMap<Role, List<Agent>>();
for(Role role:Role.values()){
requestRoleMap.put(role, new ArrayList<Agent>());
}
List<Agent> noRequestAgentList = new ArrayList<Agent>();
for(Agent agent:agentList){
Role requestedRole = gameServer.requestRequestRole(agent);
if(requestedRole != null){
if(requestRoleMap.get(requestedRole).size() < gameSetting.getRoleNum(requestedRole)){
requestRoleMap.get(requestedRole).add(agent);
} else {
noRequestAgentList.add(agent)
}
メンバ変数の初期化
エージェントリストのシャッフル
プライベート変数の宣言
for(型 変数名: イテレータ)
拡張for文(for-each文)
42. AIWolfGameServer(4/4)
} else {
noRequestAgentList.add(agent)
}
}
for(Role role:Role.values()){
List<Agent> requestedAgentList = requestRoleMap.get(role);
for(int i = 0; i < gameSetting.getRoleNum(role); i++){
if(requestedAgentList.isEmpty()){
gameData.addAgent(noRequestAgentList.remove(0), Status.ALIVE, role);
} else {
gameData.addAgent(requestedAgentList.remove(0), Status.ALIVE, role);
}
}
}
gameDataMap.put(gameData.getDay(), gameData);
gameServer.setGameData(gameData);
gameServer.setGameSetting(gameSetting);
for(Agent agent:agentList){
gameServer.init(agent);
agentNameMap.put(agent, gameServer.requestName(agent));
}
}
※ 補足資料: Java言語に関する資料も参照
43. ソースコードリーディングの勧め
• コードリーディングとは
1. プログラムのソースコードを読む
2. 何をしているプログラムか理解する
3. プログラムの全体構造を把握する
4. そこにあるアルゴリズムを知る
• プログラマのすべてが書かれている
• そこにはアイディアがある
• アルゴリズムが書かれている
• 研究のコア、実現方法も隠れている
• ソースコードは公開されている
• オープンソースソフトウェア
• (ライセンスにもよるけど)自由に改変できる
• (ライセンスにもよるけど)自由に再配布できる
• 絵を模写するように、文章を写本するように、プログラムも真似
て読めるようすることが重要!!
• 人狼知能サーバのプログラムは,サーバークライアント型のプロ
グラムを実装する上でのノウハウが詰め込まれていて最適!
47. サンプル: 占師(Seer)の実装
• 例として MySeer クラスとして実装
• MySeerが継承したAbstractSeer
• 占い師に必要ないメソッド(attack, guard等)を事
前に消してある
• 毎朝,サーバから送られた占い結果をListに格納す
る 等の簡単な実装がしてある
• 実装する必要のあるメソッド
• talk(), divine(), vote()の3つ
50. @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);
}
}
↑getLatestDayGameInfo()はAbstractPlayer内のメソッドで,
最新のGameInfoを返す
GameInfoはサーバから送られてくるゲーム内情報.ロ
グや占い結果等の全ての情報はここから取得する
52. @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();
}
}
54. boolean isComingOut = false; //既に役職のカミングアウトをしているどうかのフラグ
List<Agent> myToldJudgeList = new List<Agent>();
@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; //話すことが無ければ会話終了
}
56. UtteranceクラスのAPIDoc
戻り値 メソッド名と説明
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を返す
57. GameInfo から Talk を読む!
@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は読まないように変更してみる
58. //その日のログの何番目まで読み込んだか
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++;
}
}
59. //偽占い師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++;
}
}
オレンジ部分:追
加
赤部分:修正
66. 村人エージェントの基本行動
• 村人の「一番の」役割とは
• 会話を通じて情報を引き出す
• 会話を行うための「情報」をどうするか
• 他のプレイヤの「投票」から考える
• 誰に投票したのか、その理由はなぜなのかを推論
• 他のプレイヤの「会話」から考える
• 誰がCOしたのか、誰を疑っているのか(疑っていないのか)
• 発言に矛盾がないか
• 自ら発言することで、場の情報を増やす
• 誰が怪しいかをしっかり発言する
• 自分が村人であることをCOする
• 投票する相手をきめる
http://www.beginners-jinro.jp/
68. 占いエージェントの基本行動
• 占う相手の順序をつける
• 人狼にありがちな行動をしたプレイヤ順
• 会話が矛盾しているプレイヤ
• やたらと誰かを怪しんでいるプレイヤ
• 特定のプレイヤをかばうような発言・投票をするプレイ
ヤ
• 自分が占い師であることを告白(CO)する
• 狼を発見したとき
• 自分が追放されそうになったとき
• 自分が「占い師」であることへの信頼を得る
• 「人狼」を見つけたのに、理由なくほかのプレイヤ
に投票するなどは、信頼を得られない
http://www.beginners-jinro.jp/
71. K村人の場合
• 会話に参加
• 他人に投票させるよう誘導
• 人狼を推理し誘導
• 役職を聞き,推理
• 投票
• 発話が極端なプレイヤーに投票
• 1,2を繰り返す
必要なデータ:
プレイヤリスト, 投票候補リスト,評価
用データ,協調プレイヤリスト?
アルゴリズム:
対象プレイヤを決定する評価式
他のプレイヤの協調度合評価式
必要なデータ:
役職回答リスト
アルゴリズム:
役職を問うメソッド
必要なデータ:
プレイヤの発言データリスト
アルゴリズム:
「発話が極端な」プレイヤを決定メソッド
80. デバッグとは
• プログラムがきちんと動作しているのかを確かめ
るための機能
• 安直な実装は プリントデバック
• System.out.println(hoge); をプログラム埋め込んで変数や
ルート分岐が意図どおり動いているのかを確認する方法
• Eclipseで利用可能なデバック機能
• ブレークポイントの設定
• 変数の値の参照
• ステップイン
• ステップリターン
• ステップオーバー など
• プログラムの文法的な間違いは Eclipse は指摘でき
るが,アルゴリズム実装のミスは確認するしかな
い.
81. なぜ、デバッカーを使うのか
(1/2)
1. public class SummationDbg {
2. public static void main(String[] args){
3. int[] a = {1,2,3};
4. int total = new Summation().addArray(a);
5. System.out.println("1+2+3="+total);
6. }
7. }
SummationDbg.java の作成
1. public class Summation {
2. int addArray(int[] boxes){
3. for(int i = 1; i < boxes.length; i++)
4. boxes[0] += boxes[i];
5. return boxes[0];
6. }
7. }
Summation.javaの作成
82. なぜ、デバッカーを使うのか
(2/2)
この回答は
(1) 7 (2) 12 (3) 13
1. public class SummationDbg {
2. public static void main(String[] args){
3. int[] a = {1,2,3};
4. int total = new Summation().addArray(a);
5. System.out.println("1+2+3="+total);
6. a[2] = 4;
7. total = new Summation().addArray(a);
8. System.out.println("1+2+4="+total);
9. }
10. } SummationDbg.java の追加
83. Breakpoint とは
• プログラムの実行を途中で止めてみる個所
• Java の break; とほぼ同じ意味
• デバックモードで実行すると、その時点での変
数や配列などのデータの状態を確認できる
• printf デバックで System.out.println(変数) と打って途
中経過を見ようとするのに似ている...
• でも、大きなプログラムだと、どこで出力したもの
か確認するのが大変
93. N狩人の場合(1/3)
package org1551018aiwolf;
import java.util.List;
public class MyBodyguard {
AdvanceGameInfo agi = new AdvanceGameInfo();
Agent planningVoteAgent; //今日投票しようと思っているプレイヤー
Agent declaredPlanningVoteAgent; //自分が最後に宣言した「投票しようと思っているプレイヤー」
int readTalkListNum; //会話をどこまで読んだか
@Override
public void dayStart() {
declaredPlanningVoteAgent = null;
planningVoteAgent = null;
setPlanningVoteAgent();
readTalkListNum =0;
}
@Override
public String talk() {
if(declaredPlanningVoteAgent != planningVoteAgent){
String string = TemplateTalkFactory.vote(planningVoteAgent);
declaredPlanningVoteAgent = planningVoteAgent;
return string;
}else{
return TemplateTalkFactory.over();
}
}
@Override
public Agent vote() {
return planningVoteAgent;
}
狩人の行動
占い師を優先的に守るようにする。霊媒師が名乗り出ても
優先的に守ることはないように変更した。3%の確率で村人
からランダムで守るようにした。
94. N狩人の場合(2/3)
@Override
public Agent guard() {
//占い師,もしくは霊能者COしているプレイヤーからランダムに選択(3%の確率で生存プレイヤーの中からランダムに
変更)
List<Agent> guardAgentCandidate = new ArrayList<Agent>();
List<Agent> aliveAgentList = getLatestDayGameInfo().getAliveAgentList();
aliveAgentList.remove(getMe());
for(Agent agent: aliveAgentList){
if(agi.getComingoutMap().containsKey(agent)){
List<Role> guardRoleList = Arrays.asList(Role.MEDIUM);
if(guardRoleList.contains(agi.getComingoutMap().get(agent))){
guardAgentCandidate.add(agent);
}
}
}
Agent guardAgent;
if(guardAgentCandidate.size() > 0 && Math.random() < 0.97){
Random rand = new Random();
guardAgent = guardAgentCandidate.get(rand.nextInt(guardAgentCandidate.size()));
}else{
Random rand = new Random();
guardAgent = aliveAgentList.get(rand.nextInt(aliveAgentList.size()));
}
return guardAgent;
}
public void setPlanningVoteAgent(){
<中略>
}
}
95. N狩人の場合(3/3)
@Override
public void update(GameInfo gameInfo) {
super.update(gameInfo);
List<Talk> talkList = gameInfo.getTalkList();
boolean existInspectResult = false;
/* talkListからCO,占い結果の抽出 */
for(int i = readTalkListNum; i < talkList.size(); i++){
Talk talk = talkList.get(i);
Utterance utterance = new Utterance(talk.getContent());
switch (utterance.getTopic()) {
case COMINGOUT: //カミングアウトの発話の場合
agi.getComingoutMap().put(talk.getAgent(), utterance.getRole());
break;
case DIVINED: //占い結果の発話の場合
//AGIのJudgeListに結果を加える
Agent seerAgent = talk.getAgent();
Agent inspectedAgent = utterance.getTarget();
Species inspectResult = utterance.getResult();
Judge judge = new Judge(getDay(), seerAgent, inspectedAgent, inspectResult);
agi.addInspectJudgeList(judge);
existInspectResult =true;
break;
}
}
readTalkListNum =talkList.size();
/* 新しい占い結果があれば投票先を変える.(新たに黒判定が出た,
または投票先のプレイヤーに白判定が出た場合)*/
if(existInspectResult){
setPlanningVoteAgent();
}
}
96. O村人の場合(1/3)
package org.aiwolf.roAgent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import org.aiwolf.client.base.player.AbstractVillager;
import org.aiwolf.client.base.smpl.AdvanceGameInfo;
import org.aiwolf.client.lib.*;
import org.aiwolf.common.*;
import org.aiwolf.common.data.*;
import org.aiwolf.common.net.*;
public class MyVillager extends AbstractVillager{
AdvanceGameInfo agi = new AdvanceGameInfo();
Agent planningVoteAgent; //今日投票しようと思っているプレイヤー
Agent declaredPlanningVoteAgent; //自分が最後に宣言した「投票しようと思っているプレイヤー」
int readTalkListNum; //会話をどこまで読んだか
@Override
public void dayStart() {
declaredPlanningVoteAgent = null;
planningVoteAgent = null;
setPlanningVoteAgent();
readTalkListNum =0;
}
投票アルゴリズム:
人狼だと占われたエージェント,いなければ,ランダム
発話:
その日に投票しようとしているエージェントを報告.
変化すれば報告.
97. O村人の場合(2/3)
@Override
public String talk() {
int comingoutDay = 0;
boolean isCameout = false;
//役職をカミングアウトする
if(!isCameout && getDay() >= comingoutDay){
String string = TemplateTalkFactory.comingout(getMe(), getMyRole());
isCameout = true;
return string;
}
if(declaredPlanningVoteAgent != planningVoteAgent){
String string = TemplateTalkFactory.vote(planningVoteAgent);
declaredPlanningVoteAgent = planningVoteAgent;
return string;
}else{
return TemplateTalkFactory.over();
}
}
@Override
public Agent vote() {
return planningVoteAgent;
}
@Override
public void finish() {
// TODO 自動生成されたメソッド・スタブ
}
98. O村人の場合(2/3)
public void setPlanningVoteAgent(){
/* 人狼だと占われたプレイヤーを指定している場合はそのまま */
if(planningVoteAgent != null){
for(Judge judge: agi.getInspectJudgeList()){
if(judge.getTarget().equals(planningVoteAgent)){
return;
}
}
}
/*
* 投票先を未設定,または人狼だと占われたプレイヤー以外を投票先にしている場合
* 人狼だと占われたプレイヤーがいれば,投票先をそのプレイヤーに設定
* いなければ生存プレイヤーからランダムに選択
*/
List<Agent> voteAgentCandidate = new ArrayList<Agent>();
List<Agent> aliveAgentList = getLatestDayGameInfo().getAliveAgentList();
aliveAgentList.remove(getMe());
for(Judge judge: agi.getInspectJudgeList()){
if(aliveAgentList.contains(judge.getTarget()) && judge.getResult() == Species.WEREWOLF){
voteAgentCandidate.add(judge.getTarget());
}
}
if(voteAgentCandidate.size() > 0){
Random rand = new Random();
planningVoteAgent = voteAgentCandidate.get(rand.nextInt(voteAgentCandidate.size()));
}else{
Random rand = new Random();
planningVoteAgent = aliveAgentList.get(rand.nextInt(aliveAgentList.size()));
}
return;
}
}
99. package com.gmail.tnamura;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.aiwolf.client.base.player.AbstractSeer;
import org.aiwolf.client.lib.TemplateTalkFactory;
import org.aiwolf.client.lib.Utterance;
import org.aiwolf.common.data.Agent;
import org.aiwolf.common.data.Judge;
import org.aiwolf.common.data.Role;
import org.aiwolf.common.data.Species;
import org.aiwolf.common.data.Talk;
import org.aiwolf.common.net.GameInfo;
public class NamuraSeer extends AbstractSeer {
boolean isComingOut = false;
int readTalkNum=0;
Agent Target;
Agent declaredTarget;
List<Agent> voteTarget = new ArrayList<Agent>(); //投票候補者リスト
List<Agent> nonvoteTarget = new ArrayList<Agent>(); //非投票候補者リスト
List<Judge> myToldJudgeList = new ArrayList<Judge>(); //自分が公表したうらない結果を入れる
List<Agent> fakeSeerCOAgent = new ArrayList<Agent>(); //自分以外で占い師COしているプレイヤー
@Override
public void dayStart(){
super.dayStart();
Target = setTarget();
declaredTarget = null;
readTalkNum=0;
}
N占いの場合(1/3)
100. N占いの場合(2/4)
@Override
public String talk() { //人狼を見つけるまでカミングアウトしない
if(isComingOut==false){ //COしてなければ
for(Judge judge:getMyJudgeList()){ //占い結果をすべて調べる
if(judge.getResult()==Species.WEREWOLF){
//自分の役職をCO
String comingoutTalk = TemplateTalkFactory.comingout(getMe(), getMyRole());
isComingOut = true;
return comingoutTalk;
}
}
} else { //COしたら
for(Judge judge:getMyJudgeList()){
if(!myToldJudgeList.contains(judge)){
String resulttalk = TemplateTalkFactory.divined(judge.getTarget(),judge.getResult());
myToldJudgeList.add(judge);
return resulttalk;
}
}
}
if(declaredTarget!=Target){
String string = TemplateTalkFactory.vote(Target);
declaredTarget = Target;
return string;
}
return Talk.OVER;
}
@Override
public Agent vote() {
return Target;
}
101. N占いの場合(3/3)
public Agent setTarget(){
if(fakeSeerCOAgent.size()>0){ //自分以外で占い師COしているプレイヤーがいれば,優先して投票
return randomSelect(fakeSeerCOAgent);
}
//占いで人狼を見つけていれば,その中からランダムで投票
//占いで人狼を見つけていなければ,自分と白以外のプレイヤーからランダムで投票
List<Agent> white = new ArrayList<Agent>(); //ブラックリスト
List<Agent> black = new ArrayList<Agent>(); //ホワイトリスト
for(Judge judge:getMyJudgeList()){ //占った結果黒だったプレイヤーをblackリストに入れる
if(judge.getResult()==Species.WEREWOLF){
black.add(judge.getTarget());
}else{ //占った結果白だったプレイヤーをwhiteリストに入れる
white.add(judge.getTarget());
}
}
if(black.size()>0){ //リストからランダムに投票
return randomSelect(black);
}else{
List<Agent> voteCandidates = new ArrayList<Agent>(); //投票対象候補者リスト
//生き残っているプレイヤーをリストに加える
voteCandidates.addAll(getLatestDayGameInfo().getAliveAgentList());
//自分と白確定プレイヤーを候補から外す
voteCandidates.remove(getMe());
voteCandidates.removeAll(white);
return randomSelect(voteCandidates); //リストからランダムに投票
}
}
106. Smpl エージェントの実装….
• AdvancedGameInfo
• SampleRoleAssignPlayer
• SampleBodyguard
• SampleMedium
• SamplePossessed
• SampleSeer
• SampleVillager
• SampleWerewolf
SampleRole
AssignPlayer
Sample
Bodygau
rad
Sample
Medium
Sample
Possess
ed
Sample
Seer
Sample
Villager
Sample
Werewo
lf
Advanced
GameInfo
Advanced
GameInfo
Advanced
GameInfo
Advanced
GameInfo
Advanced
GameInfo
Advanced
GameInfo
108. Talkの実装
public String talk() {
if(投票するプレイヤを宣言していない場合)
投票相手を宣言する発話内容を生成
宣言フラグを立てる
else
発話を終了
end
}
public String talk() {
if(declaredPlanningVoteAgent != planningVoteAgent){
String string = TemplateTalkFactory.vote(planningVoteAgent);
declaredPlanningVoteAgent = planningVoteAgent;
return string;
}else{
return TemplateTalkFactory.over();
}
}
109. AdvancedGameInfo.java (1/2)
public class AdvanceGameInfo {
/* 発話で伝えられた占い結果のリスト.今回のプロトコルでは何日目に占ったのか分からないので,発話日に設定.*/
private List<Judge> inspectJudgeList = new ArrayList<Judge>();
/* 発話で伝えられた霊能結果のリスト.今回のプロトコルでは何日目に霊能したのか分からないので,発話日に設定.*/
private List<Judge> mediumJudgeList = new ArrayList<Judge>();
private Map<Agent, Role> comingoutMap = new HashMap<Agent, Role>();
public Map<Agent, Role> getComingoutMap() {
return comingoutMap;
}
/* COしたプレイヤーをcomingoutMapに加える.
* @param agent
* @param role */
public void putComingoutMap(Agent agent, Role role){
comingoutMap.put(agent, role);
}
public void setComingoutMap(Map<Agent, Role> comingoutMap) {
this.comingoutMap = comingoutMap;
}
ComingOut した
プレイヤと役職を保
存
エージェントをkeyと
したMapに格納
110. AdvancedGameInfo.java (2/2)
public List<Judge> getInspectJudgeList() {
return inspectJudgeList;
}
public void setInspectJudgeList(List<Judge> inspectJudgeList) {
this.inspectJudgeList = inspectJudgeList;
}
public void addInspectJudgeList(Judge judge) {
this.inspectJudgeList.add(judge);
}
public List<Judge> getMediumJudgeList() {
return mediumJudgeList;
}
public void setMediumJudgeList(List<Judge> mediumJudgeList) {
this.mediumJudgeList = mediumJudgeList;
}
public void addMediumJudgeList(Judge judge) {
this.mediumJudgeList.add(judge);
}
}
発話から占いCOしたユーザの Judge
情報を拾ってしまうだけ.
なので…
こちらも、占いと同じ
なので…
112. LearningPlayerの構成
• BasePlayer
• GiftedPlayer
• WolfSidePlayer
KajiRole
AssignPlayer
Kaji
Bodygau
rad
Kaji
Medium
Kaji
Possess
ed
Kaji
Seer
Kaji
Villager
Kaji
Werewo
lf
Abstract
KajiBase
Player
AbstractGi
ftedPlayer
Abstract
KajiWolfS
ideAgent
AbstractGi
ftedPlayer
Abstract
KajiWolfS
ideAgent
Abstract
KajiBase
Player
Abstract
KajiBase
Player
Abstract
KajiBase
Player
Abstract
KajiBase
Player
Abstract
KajiBase
Player
113. KajiPlayer のライブラリ
• AdvancedGameInfo
• Strategies
• CauseOfDeath
• EnemyCase
• Pattern
• PatternMaker
• AbstractFaceRoleChanger
• FakeRoleChanger
• PossessedFakeRoleChanger
• WolfRoleChanger
GameInfo をプレイヤの行動決定用に拡張
行動戦略アルゴリズム?
場合分け用 enum
ゲーム状況分類?
115. 他のプレイヤの役割の可能性を探る(1/3)
player 村人 占い 人狼 狂人
A
B
C
D 村人
E
• 5人人狼(村2, 占1, 狼1, 狂1)の場合
• Player A が占いをCO
• Player B が占いをCO
• Player C が村人だと宣言
Player D (村人) 目線での情報
村人目線からは情報が少ない
でも、ランダムに選んでは負ける
….
116. 他のプレイヤの役割の可能性を探る(2/3)
player 村人 占い 人狼 狂人
A
B
C
D
E 狂人
• 5人人狼(村2, 占1, 狼1, 狂1)の場合
• Player A が占いをCO
• Player B が占いをCO
• Player C が村人だと宣言
Player E (狂人) 目線での情報
狼はAかBだが、
決めてなし
投票は、C or D
117. 他のプレイヤの役割の可能性を探る(3/3)
player 村人 占い 人狼 狂人
A
B 人狼
C
D
E
• 5人人狼(村2, 占1, 狼1, 狂1)の場合
• Player A が占いをCO
• Player B が占いをCO
• Player C が村人だと宣言
Player B (人狼) 目線での情報
狂人が誰?
A が真占だとすると
…
121. 質問①-1
能力持ちにカミングアウトに重複があった場合のみ,交互に守
る.防御が成功したとき,防御の優先度を上げる優先度を上げる.
という風に考えた.サンプルを参考にして,public Agent guard(){}
の部分を書き換えて,
のように書いた.
その後,if分で条件分岐させ,占い師がいた場合,霊媒師が居た場
合,村人が居た場合と分けようと思ったが,if()の()の中に何を記
述していけばよいかわからなかった.
はじめ,リストの番号で管理しようと思ったが,役職の人数が不
確定で変動するのでうまく行かなかった.
役職名で分岐させようと思ったが型があわなくて失敗した.
122. アドバイス①-1
• gaurdAgentCandidate : エージェントリスト
• If文で分岐とは
for (Agent agent: gaurdAgentCandidate){
if(agent が占い師COなら)
;
else if (agent が霊媒師COなら)
;
end
}
• 役職に関する情報はどこにあるのか?
• Agent クラスに役職を取ってくるメソッドがある??
• AdvancedGameInfo クラスの ComingOutMap では….
• 「型があわなくて失敗した」
• 何と何を比較したのか? Agent と Role?
125. アドバイス②
• 投票を構成する情報
• 何日目、誰が、誰に、投票した
• ラインを構成する情報
• 誰と誰
• 誰(Agent)の情報
• 名前: getName() 同じ名前の場合がある
• ID: getAgentIdx() おそらくユニーク
• AgentIndex をkeyとするMapを作成して保存
• String: “(ID,ID)” という文字列を使って1次元配列
• int: ID を使用して2次元配列とする
129. エージェントのQ学習(2/2)
• ある時刻tでの環境と行動の組み合わせ (s, a)
に対して、次の時刻 t + 1 の環境 st+1 をもとに報
酬 r を決定して、 (s, a) に対して報酬rを与える。
• Q(s,a) ← Q(s,a) + α[r + γ max Q(st+1,p)- Q(s,a)] α 学習係数
• π(s,a): ランダム, ε-グリーディ, ルーレット、soft
max, 等
• そのため、環境をすべて観測できるか、できな
いかによって、学習した尤度への信頼性がこと
なる
• MDP: マルコフ行動決定過程
• POMDP: 部分観測マルコフ行動決定過程
131. 人狼知能を強化学習するには
• 環境 S を定義する
• 投票結果
• CO状況
• 会話内容
• (役職)
• 行動 A を定義する
• 投票
• 占い・護衛・襲撃 対象の決定
• 発話
• 報酬 R を定義する
• ゲームの勝利
• 人狼・占師の追放
• 騙せたとき
これらを定義したうえで,人狼BBSの
データなどを用いて学習させる!
139. パッケージの宣言、インポート
• パッケージとは
• 複数あるクラスファイルをまとめるための仕組み
• ファイル名(名前)の衝突回避
• ライブラリとしての公開
• パッケージの定義
package org.aiwolf.common.util;
同じファイル構造(org/aiwolf/common/util/)に設置が必
要
• パッケージの読み込み
import org.aiwolf.common.util.*;
import org.aiwolf.common.util.Sorter.java;
* とすることで、そのフォルダにあるものをすべてイン
ポート可能であるが、実行ファイル・速度に影響があ
る
140. Javaの変数のスコープ
• スコープ修飾子
• public: インスタンスを通して直接アクセス可能
• protected: クラス内、継承したサブクラスからアク
セス可能
• 無し: パッケージ内から
• private: クラス内から
• メソッド内、 {} 内
• static
• 異なるインスタンスどうしで値を共有
141. ジェネリクス型
• AiWolfCommon/src/org/aiwolf/common/util/ 以下などで
使用
• Counter.java
• Sorter.java
• BidiMap.java
• Java の配列データは、基本的に任意の型を格納できる。
• でも、実際に一つの配列にいろんな方を入れることはない
• データを取り出すときに、キャストするのが煩わしい
• 特定のデータ型(クラス)を格納する宣言をしてしまいたい
• ジェネリクスを用いた実装
public class ArrayList<E> extends AbstractLink<E> … {
…
public <T> T[] toArray(T[] a) { … }
….
}
143. イテレータ・拡張for文
List<Objet> list;
• 通常: データに要素番号でアクセスできないと難しい
for(int i = 0; i < MAX; i++) {
Object obj = list[i]; // list.get(i);
System.out.println(obj);
}
• イテレータ: データ構造から直接取得するので高速
for(Iterator<Object> it = list.iterator(); it.hasNext(); ){
Object obj = it.next();
System.out.println();
}
• 拡張for文(for-each文): イテレータを活用して実装してある
For(Object obj: list) {
System.out.println(obj);
}
148. Javaコーディング規則(1)
• パッケージ名は小文字
java.io.*;
• クラス名
• 先頭は大文字、複数の単語からなる場合には単語の先頭は大文字
class Sample {}
• 意味ある名前にして略さない
• メソッド名
• コンストラクタと同じ名前のメソッドは作らない
• メソッド名は文字の区切りのみ大文字
int getParameter() {}
void setParameter(int value) {}
• メソッドの役割を表す名前は統一する
• 返り値が Boolean の値の時にはTrue/Falseがわかるようにする
boolean isSampleClass() {} Boolean hasStock() {}
• 引数に入れる変数とメンバ変数を同じ名前にしない
Int num = 0;
void setParameter(int num){
this.num = num;
}
149. Java コーディング規則(2)
• 変数
• boolen 変数は、true/false がわかるような名前に
boolean hasTree;
boolean isMan;
• 定数は、なるべく static final で宣言して大文字に
static final int NUM_TRIAL;
• 変数名は役割がわかるように命名
• スコープの狭い変数はわかる範囲で略してもよい