2014年11月7日金曜日

人狼知能作成tips 第3回 ゲームの実行

前回でエージェントが完成したので対戦させてみよう.
(2015.03.31修正しました.aiwolf-ver0.1.16対応版)

ゲームを実行するためのソースコードは以下の通り.

/**
 * 指定した回数ゲームを実行し,各陣営の勝利数を表示するMainクラス
 * @author inaba
 *
 */

 public class MyStarter {

/**
 * 参加エージェントの数
 */
static protected int PLAYER_NUM = 12;

/**
* 1回の実行で行うゲーム数
*/
static protected int GAME_NUM = 100;

public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
//村人側勝利数
int villagerWinNum = 0;
//人狼側勝利数
int werewolfWinNum = 0;


for(int i = 0;i<GAME_NUM;i++){
List<Player> playerList = new ArrayList<Player>();

for(int j=0;j<PLAYER_NUM;j++){
playerList.add(new RoleAssignPlayer()); //ここで作成したエージェントを指定
}

    GameServer gameServer = new DirectConnectServer(playerList); GameSetting gameSetting = GameSetting.getDefaultGame(PLAYER_NUM); AIWolfGame game = new AIWolfGame(gameSetting, gameServer); game.setRand(new Random(gameSetting.getRandomSeed())); game.start(); if(game.getWinner() == Team.VILLAGER){ villagerWinNum++; }else{ werewolfWinNum++; }

System.out.println("村人側勝利:" + villagerWinNum + " 人狼側勝利" + werewolfWinNum);

}

}

ソースコードの解説は人狼知能エージェントの作成・その3とかなり重複するので割愛.

プログラムを実行すると,100回ゲームを実行し,村人側と人狼側の勝利数をそれぞれ表示する.
ポイントは,ソースコード中央辺りで前回作成したRoleAssignPlayerをplayerListに12回(PLAYER_NUM回)追加している点である.
これにより,RoleAssignPlayer12体による対戦が行える.

実行するとコンソールにこんな感じで表示される.

Agent[11] request no role
Agent[09] request no role
Agent[01] request no role
Agent[12] request no role
Agent[06] request no role
Agent[04] request no role
Agent[08] request no role
Agent[10] request no role
Agent[02] request no role
Agent[05] request no role
Agent[03] request no role
Agent[07] request no role
=============================================
======
Agent[01] null ALIVE POSSESSED
Agent[02] null ALIVE VILLAGER
Agent[03] null ALIVE WEREWOLF
Agent[04] null ALIVE VILLAGER
Agent[05] null ALIVE VILLAGER
Agent[06] null ALIVE VILLAGER
Agent[07] null ALIVE WEREWOLF
Agent[08] null ALIVE VILLAGER
Agent[09] null ALIVE MEDIUM
Agent[10] null ALIVE VILLAGER
Agent[11] null ALIVE BODYGUARD
Agent[12] null ALIVE SEER
Human:10
Werewolf:2



Send finish to Agent[09]
Send finish to Agent[10]
Send finish to Agent[11]
Send finish to Agent[12]
Winner:VILLAGER



Send finish to Agent[09]
Send finish to Agent[10]
Send finish to Agent[11]
Send finish to Agent[12]
Winner:WEREWOLF
Villager:42 Werewolf:58

100回中,村人側が42回,人狼側が58回勝ったようだ.
現段階では人狼側のほうが強いらしい.それもそのはずで,前回作成したMySeerPlayerは占い結果を全く発表せず,投票もランダムで行っている.重要な役職の占い師がこの体たらくではこれも仕方ない.

というわけで,前回作成したRoleAssignPlayerのソースコードからthis.setSeerPlayer(new MySeerPlayer())をコメントアウトしてみよう.これですべてサンプルがゲームを行うことになる.
実行結果は以下のとおり.




Send finish to Agent[09]
Send finish to Agent[10]
Send finish to Agent[11]
Send finish to Agent[12]
Winner:VILLAGER
Villager:56 Werewolf:44

村人側が56回,人狼側が44回勝利,ということでやはり前回作成したSeerPlayerが諸悪の根源だったようだ(泣)
これでは残念すぎるので,次回はもっと強いエージェントを作っていこう.

2014年11月5日水曜日

人狼知能作成tips 第2回 エージェントの作成

今回は,実際にエージェントを実装する.
(2015.03.31修正しました.aiwolf-ver0.1.16対応版)


役職に応じてエージェントを切り替えるプレイヤーの作成

 人狼サーバには役職別のエージェント作成に便利な抽象クラスが用意されている.
今回はそのAbstractRoleAssignPlayerを継承してエージェントを作っていく.

AbstractRoleAssignPlayerにはsetSeerPlayerやsetMediumPlayerなどのメソッドが用意されており,コンストラクタで各役職に対応したエージェントを指定してやれば,ゲーム開始時に決まった役職に応じて自動でエージェントを切り替えてくれる.

以下の例では,占い師のエージェントにMySeerPlayerを指定している.

public class RoleAssignPlayer extends AbstractRoleAssignPlayer {


public RoleAssignPlayer() {
this.setSeerPlayer(new MySeerPlayer());
}

@Override
public String getName() {
return null;
}

}
なお指定していない役職のエージェントに関しては,サンプルエージェントが自動的に指定される.


占い師エージェントの実装

では次に,実際にMySeerPlayerを実装して,占い師のエージェントを作ってみよう.
各役職に関しても便利な抽象クラスが用意されている.
ということで,占い師の抽象クラスであるAbstractSeerPlayerを継承して占い師を作ってみる. 

まず最初に,EclipseでJavaクラスの新規作成ウインドウの「スーパークラス」の欄に「org.aiwolf.client.base.player.AbstractSeerPlayer」を入力し, 適当な名前を付けて完了を押せば,以下の様なクラスが自動生成される.

public class SeerPlayer extends AbstractSeer {

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

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

}

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

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

}

あとは,各メソッドの中身を埋めればとりあえず動くものができる.
占い先を指定するdivineメソッドと投票先を指定するvoteメソッドは,
ランダムで自分以外を指定することにしてみる.
ソースコードは以下のとおり.

//現時点で生存しているエージェントのリストを取得
List<Agent> agentList = this.getLatestDayGameInfo().getAliveAgentList();

//リストから自分を除外する
agentList.remove(this.getMe());

//エージェントのリストをシャッフルする
Collections.shuffle(agentList);

//リストの先頭のエージェントを指定
Agent target = agentList.get(0);

発言内容を指定するtalkメソッドは第1回のものをそのまま使う.
finishメソッドはゲーム終了時に呼ばれるメソッドだが,今回は必要ないのでそのまま.
というわけで完成したものが以下.

package player;

import java.util.Collections;
import java.util.List;

import org.aiwolf.client.base.player.AbstractSeer;
import org.aiwolf.client.lib.TemplateTalkFactory;
import org.aiwolf.client.lib.Topic;
import org.aiwolf.client.lib.Utterance;
import org.aiwolf.common.data.Agent;
import org.aiwolf.common.data.Role;
import org.aiwolf.common.data.Talk;
import org.aiwolf.common.net.GameInfo;

public class MySeerPlayer extends AbstractSeer {

@Override
public Agent divine() {
//現時点で生存しているエージェントのリストを取得
List<Agent> agentList = this.getLatestDayGameInfo().getAliveAgentList();

//リストから自分を除外する
agentList.remove(this.getMe());

//エージェントのリストをシャッフルする
Collections.shuffle(agentList);

//リストの先頭のエージェントを指定
Agent target = agentList.get(0);

return target;
}

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

}

@Override
public String talk() {
GameInfo info = this.getLatestDayGameInfo();
List<Talk> talkList = info.getTalkList();

for(Talk t : talkList){
Utterance u = new Utterance(t.getContent());
if(u.getTopic() == Topic.COMINGOUT && u.getRole() == Role.SEER){
//対抗占い師CO
return TemplateTalkFactory.comingout(info.getAgent(), Role.SEER);
}
}

//占い師COがない場合はSKIP
return TemplateTalkFactory.skip();
}

@Override
public Agent vote() {
//現時点で生存しているエージェントのリストを取得
List<Agent> agentList = this.getLatestDayGameInfo().getAliveAgentList();

//リストから自分を除外する
agentList.remove(this.getMe());

//エージェントのリストをシャッフルする
Collections.shuffle(agentList);

//リストの先頭のエージェントを指定
Agent target = agentList.get(0);

return target;
}

}

次回は実際にエージェント同士で対戦させてみる.

2014年9月21日日曜日

人狼知能作成tips 第1回 発話の処理と発言のやり方

人狼知能エージェントを作る際に役立つことを書いていく.
今回は,他のエージェントの発話の処理の仕方と,発言の仕方について.
なお,人狼サーバのバージョンは0.1.9を使用している.



発話の処理のやり方

プレイヤーの発話のリストは,GameInfoクラスのgetTalkListメソッドで取得できる.
なので,何番のエージェントがどんな発言をしたかということは,以下のコードで取得できる.


GameInfo info = this.getLatestDayGameInfo();
List<Talk> talkList = info.getTalkList();

for(Talk t : talkList){
  System.out.println(t.getAgent().getAgentIdx() + " 番が " + t.getContent() + " と発言");
}

上のコードのように,getContent()を使用すれば発話の内容が取得できるが,ただの文字列なので,このままでは使いづらい.
人狼知能作成キットには,人狼言語パーサとしてUtteranceクラスが用意されているので,これを使う.

ここで,Utteranceクラスを使って占い師COをしたエージェントを取得してみる.

GameInfo info = this.getLatestDayGameInfo();
List<Talk> talkList = info.getTalkList();

for(Talk t : talkList){
Utterance u = new Utterance(t.getContent());
if(u.getTopic() == Topic.COMINGOUT && u.getRole() == Role.SEER){
System.out.println(t.getAgent().getAgentIdx() + "が占い師カミングアウト");
}
}


発言のやり方

次に自分のエージェントに発言をさせてみる.
こちらでは人狼言語を簡単に生成できるTemplateTalkFactoryクラスが用意されているのでこれを使う.

自分が占い師COをする人狼言語を作成するためには,以下のようにすればいい.

GameInfo info = this.getLatestDayGameInfo();
TemplateTalkFactory.comingout(info.getAgent(), Role.SEER);
また,発言はせず,様子見をする場合に使うSKIPや,話すことが無くなったことを明示するOVERなども同様に生成できる.

TemplateTalkFactory.skip();
TemplateTalkFactory.over();


まとめ

最後に,他のプレイヤーが占い師COしてきた際に,対抗COするtalkメソッドを書いてみる.

@Override
public String talk() {
GameInfo info = this.getLatestDayGameInfo();
List<Talk> talkList = info.getTalkList();

for(Talk t : talkList){
Utterance u = new Utterance(t.getContent());
if(u.getTopic() == Topic.COMINGOUT && u.getRole() == Role.SEER){
//対抗占い師CO
return TemplateTalkFactory.comingout(info.getAgent(), Role.SEER);
}
}

//占い師COがない場合はSKIP
return TemplateTalkFactory.skip();
}