// ConnectFour.java // (C) 2009 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany // first published 03.12.2009 on http://yacy.net; // // This is a part of YaCy, a peer-to-peer based web search engine // // $LastChangedDate: 2009-05-28 01:51:34 +0200 (Do, 28 Mai 2009) $ // $LastChangedRevision: 5988 $ // $LastChangedBy: orbiter $ // // LICENSE // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package net.yacy.ai.example; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.yacy.ai.greedy.AbstractModel; import net.yacy.ai.greedy.AbstractFinding; import net.yacy.ai.greedy.Battle; import net.yacy.ai.greedy.ContextFactory; import net.yacy.ai.greedy.Finding; import net.yacy.ai.greedy.Model; import net.yacy.ai.greedy.Role; import net.yacy.ai.greedy.Goal; public class ConnectFour { static final int width = 7; static final int height = 6; public static enum Coin implements Role { red('*'), blue('#'); private String c; Coin(char c) {this.c = String.valueOf(c);} public Coin nextRole() { return (this == red) ? blue : red; } public String toString() {return this.c;} public final static Coin[] allCoins = {red, blue}; } public static class Move extends AbstractFinding implements Finding { private final int column; public Move(Coin role, int column) { super(role, (column > (width / 2)) ? (width - column - 1) : column); this.column = column; } @Override public Object clone() { return new Move(this.getRole(), this.column); } public int getColumn() { return this.column; } @Override public boolean equals(Object other) { if (!(other instanceof Move)) return false; Move m = (Move) other; return this.column == m.column; } @Override public int hashCode() { return this.column; } public String toString() { return super.getRole().toString() + ":" + Integer.toString(this.column); } } public static class Board extends AbstractModel implements Model, Cloneable { Coin[] b; // 2 dimensions folded: array starts in the bottom left and first position in second row is at index position /** * create a board with start configuration: empty board * @param nextRole */ public Board(Coin startPlayer) { super(startPlayer); b = new Coin[height * width]; for (int i = 0; i < b.length; i++) b[i] = null; } /** * clone a board and return a new board * @param board */ public Board(Board board) { super(board.currentRole()); this.b = new Coin[board.b.length]; System.arraycopy(board.b, 0, b, 0, b.length); } public Object clone() { return new Board(this); } public boolean columnFull(int column) { return b[(height - 1) * width + column] != null; } public Coin getCell(int column, int row) { return b[row * width + column]; } public void applyFinding(Move nextStep) { int column = nextStep.getColumn(); int row = 0; while (row < height && b[row * width + column] != null) row++; if (row == height) throw new RuntimeException("column " + column + " is full"); b[row * width + column] = nextStep.getRole(); } public int hashCode() { int c = 0; Coin x; for (int i = 0; i < b.length; i++) { x = b[i]; if (x != null) c += (i + 1) * (x.ordinal() + 1); } return c; } public boolean equals(Object o) { Board om = (Board) o; Coin c0, c1; for (int i = 0; i < b.length; i++) { c0 = b[i]; c1 = om.b[i]; if (!(c0 == null ? c1 == null : c0.equals(c1))) return false; } return true; } private boolean count4(int x, int y, int xi, int yi, Coin c) { int steps = 4; Coin cell; while (steps-- > 0) { cell = b[y * width + x]; if (cell == null || !cell.equals(c)) return false; x += xi; y += yi; } return true; } private int countSame(int x, int y, int xi, int yi, Coin c) { int rb = 0; int steps = 4; Coin cell; while (steps-- > 0) { cell = b[y * width + x]; if (cell != null) { if (cell.equals(c)) rb++; else return rb; } x += xi; y += yi; } return rb; } public Coin isTermination() { for (Coin coin: Coin.allCoins) if (isTermination(coin)) return coin; return null; } public boolean isTermination(Coin coin) { // waagerecht for (int x = 0; x < width - 3; x++) for (int y = 0; y < height; y++) if (count4(x, y, 1, 0, coin)) return true; // senkrecht for (int x = 0; x < width; x++) for (int y = 0; y < height - 3; y++) if (count4(x, y, 0, 1, coin)) return true; // slash-schraeg for (int x = 0; x < width - 3; x++) for (int y = 0; y < height - 3; y++) if (count4(x, y, 1, 1, coin)) return true; // backslash-schraeg for (int x = 0; x < width - 3; x++) for (int y = 3; y < height; y++) if (count4(x, y, 1, -1, coin)) return true; return false; } public int getRanking(int findings, Coin coin) { return 2 * getRankingSingle(findings, coin) - getRankingSingle(findings, coin.nextRole()) - 3 * findings; } private int getRankingSingle(int findings, Coin coin) { int r = 0; // waagerecht for (int x = 0; x < width - 3; x++) for (int y = 0; y < height; y++) r += countSame(x, y, 1, 0, coin); // senkrecht for (int x = 0; x < width; x++) for (int y = 0; y < height - 3; y++) r += countSame(x, y, 0, 1, coin); // slash-schraeg for (int x = 0; x < width - 3; x++) for (int y = 0; y < height - 3; y++) r += countSame(x, y, 1, 1, coin); // backslash-schraeg for (int x = 0; x < width - 3; x++) for (int y = 3; y < height; y++) r += countSame(x, y, 1, -1, coin); return r; } public List explore() { ArrayList moves = new ArrayList(); for (int i = 0; i < width; i++) { if (!columnFull(i)) moves.add(new Move(this.currentRole(), i)); } return moves; } public String toString() { StringBuffer s = new StringBuffer((width + 1) * height); Coin coin; for (int row = height - 1; row >= 0; row--) { s.append("\""); for (int column = 0; column < width; column++) { coin = b[row * width + column]; s.append((coin == null) ? " " : coin.toString()); } if (row == 0) s.append("\""); else s.append("\"+\n"); } return s.toString(); } } public static class Strategy implements Goal { public Strategy() { } public int getPriority(Board model, Move finding) { if (finding.column <= width / 2) return finding.column; return width - 1 - finding.column; } public boolean isFulfilled(Board model) { return false; } public boolean isSnapshot(Board model) { return false; } public boolean pruning(Board model) { return false; } } public static void battle() { Map> strategies = new HashMap>(); ContextFactory redFactroy = new ContextFactory(new Strategy(), 3000, false, false); ContextFactory blueFactroy = new ContextFactory(new Strategy(), 50, false, false); strategies.put(Coin.red, redFactroy); strategies.put(Coin.blue, blueFactroy); Battle battle = new Battle(new Board(Coin.red), strategies, 2000); } public static void main(String[] args) { battle(); /* int cores = Runtime.getRuntime().availableProcessors(); Engine engine = new Engine(cores); Agent agent; engine.start(); long comptime = 60; long relaxtime = 20; agent = new Agent(new Board(Coin.red), new Strategy(comptime, false, false)); // red begins engine.inject(agent); agent.getTeorem().getGoal().awaitTermination(relaxtime); System.out.println("=========== terminated =========="); agent.getTeorem().printReport(100); */ /* agent = new Agent(new Board(), Coin.red, new Strategy(comptime, false, true)); // red begins engine.inject(agent); agent.getGoal().awaitTermination(relaxtime); System.out.println("=========== terminated =========="); agent.printReport(10); */ //engine.stop(); } }