■ ルール定義
ブログにしては、ちょっと多めの行数だけど一行当たりの文字数が少ないので、記述量は全然大した事無い(以下、prettyprint が固まり気味なので小分けする)。
package tictactoe
import tictactoe.Cell;
import tictactoe.Row;
import tictactoe.Game;
import tictactoe.Game.State;
rule "初期化"
when
game: Game(state == State.INITIALIZED)
then
for (Row row: game.getRows()) insert(row);
for (Cell[] row: game.getBoard()) {
for (Cell cell: row) insert(cell);
}
modify(game) { setState(State.START); }
end
rule "開始"
when
game: Game(state == State.START)
then
modify(game) { start(); }
endrule "マシン側戦略1" @Strategy(Win)
salience 70
when
game: Game(state == State.MACHINE_TURN)
row: Row($cells: cells)
c1: Cell(state == Cell.MACHINE) from $cells
c2: Cell(state == Cell.MACHINE) from $cells
c3: Cell(state == Cell.FREE) from $cells
eval (c1 != c2)
then
modify(c3) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
end
rule "マシン側戦略2" @Strategy(Block)
salience 60
when
game: Game(state == State.MACHINE_TURN)
row: Row($cells: cells)
c1: Cell(state == Cell.USER) from $cells
c2: Cell(state == Cell.USER) from $cells
c3: Cell(state == Cell.FREE) from $cells
eval (c1 != c2)
then
modify(c3) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
endrule "マシン側戦略3" @Strategy(Fork)
salience 50
when
game: Game(state == State.MACHINE_TURN)
c: Cell(state == Cell.FREE)
mc1: Cell(state == Cell.MACHINE)
mc2: Cell(state == Cell.MACHINE)
fc1: Cell(state == Cell.FREE)
fc2: Cell(state == Cell.FREE)
r1: Row(cells contains c, cells contains mc1, cells contains fc1)
r2: Row(cells contains c, cells contains mc2, cells contains fc2)
eval (r1 != r2 && c != fc1 && c != fc2)
then
modify(c) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
end
rule "マシン側戦略4" @Strategy(Block Opponent^s Fork)
salience 40
when
game: Game(state == State.MACHINE_TURN)
c: Cell(state == Cell.FREE)
mc1: Cell(state == Cell.USER)
mc2: Cell(state == Cell.USER)
fc1: Cell(state == Cell.FREE)
fc2: Cell(state == Cell.FREE)
r1: Row(cells contains c, cells contains mc1, cells contains fc1)
r2: Row(cells contains c, cells contains mc2, cells contains fc2)
eval (r1 != r2 && c != fc1 && c != fc2)
then
modify(c) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
endrule "マシン側戦略5" @Strategy(Center)
salience 30
when
game: Game(state == State.MACHINE_TURN)
c: Cell(state == Cell.FREE, x == 1, y == 1)
then
modify(c) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
end
rule "マシン側戦略6" @Strategy(Opposite Corner)
salience 20
when
game: Game(state == State.MACHINE_TURN)
uc: Cell(state == Cell.USER, $ux: x, $uy: y)
mc: Cell(state == Cell.FREE, $mx: x, $my: y)
eval (($ux==0 && $mx==2 || $ux==2 && $mx==0)
&& ($uy==0 && $my==2 || $uy==2 && $my==0))
then
modify(mc) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
endrule "マシン側戦略7" @Strategy(Empty Corner)
salience 10
when
game: Game(state == State.MACHINE_TURN)
c: Cell(state == Cell.FREE, x != 1, y != 1)
then
modify(c) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
end
rule "マシン側戦略8" @Strategy(Empty Side)
when
game: Game(state == State.MACHINE_TURN)
c: Cell(state == Cell.FREE)
then
modify(c) { setState(Cell.MACHINE); }
modify(game) { setState(State.JUDGING); }
endrule "ユーザの番"
when
game: Game(state == State.USER_TURN)
then
update(game.play());
modify(game) { setState(State.JUDGING); }
end
rule "決着"
salience 20
when
game: Game(state == State.JUDGING)
row: Row($cells: cells)
c1: Cell($s: state, state != Cell.FREE) from $cells
c2: Cell(state == $s) from $cells
c3: Cell(state == $s) from $cells
eval (c1 != c2 && c2 != c3 && c3 != c1)
then
System.out.printf("== %s won!%n",
(Cell.MACHINE == $s) ? "machine": "user");
modify(game) { setState(State.FINISHED); }
end
rule "引き分け"
salience 10
when
game: Game(state == State.JUDGING)
not Cell(state == Cell.FREE)
then
System.out.println("== draw!");
modify(game) { setState(State.FINISHED); }
end
rule "続行"
when
game: Game(state == State.JUDGING)
then
modify(game) { change(); }
end(割とすっきり書けたと思うが、いかにも Java なコードがまだまだ少なくないので、その辺りもう少し工夫したい。)
■ Java コード
ルール定義を見れば察しが付くようなコードばかりなので面白く無いので、以下のエベントリスナの定義を含む main() だけ抜粋。
public static final void main(String[] args) throws Exception {
KnowledgeBase kbase = readKnowledgeBase();
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
final Game game = new Game();
ksession.insert(game);
ksession.addEventListener(new DefaultAgendaEventListener() {
public void afterActivationFired(AfterActivationFiredEvent event) {
Rule rule = event.getActivation().getRule();
String strategy = rule.getMetaAttribute("Strategy");
if (null != strategy) {
System.out.printf(" strategy = %s%n", strategy);
} else if (Arrays.asList(new String[]{
"続行", "引き分け", "決着"}).contains(rule.getName())) {
game.printBoard();
}
}
});
ksession.fireAllRules();
}
この辺りをもう一工夫すれば、ルール定義 と Java コードをもっときれいに分離できそうな気がする。
0 件のコメント:
コメントを投稿