You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
11 KiB
321 lines
11 KiB
/**
|
|
* ConnectFour.java
|
|
* Copyright 2009 by Michael Peter Christen, Frankfurt a. M., Germany
|
|
* First published 03.12.2009 at http://yacy.net
|
|
*
|
|
* $LastChangedDate$
|
|
* $LastChangedRevision$
|
|
* $LastChangedBy$
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program in the file lgpl21.txt
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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;
|
|
}
|
|
@Override
|
|
public String toString() {return this.c;}
|
|
public final static Coin[] allCoins = {red, blue};
|
|
}
|
|
|
|
public static class Move extends AbstractFinding<Coin> implements Finding<Coin> {
|
|
protected 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;
|
|
}
|
|
@Override
|
|
public String toString() {
|
|
return super.getRole().toString() + ":" + Integer.toString(this.column);
|
|
}
|
|
}
|
|
|
|
public static class Board extends AbstractModel<Coin, Move> implements Model<Coin, Move>, Cloneable {
|
|
|
|
Coin[] b; // 2 dimensions folded: array starts in the bottom left and first position in second row is at index position <width>
|
|
|
|
/**
|
|
* 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) {
|
|
if (!(o instanceof Board)) return false;
|
|
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;
|
|
}
|
|
|
|
private int getPriority(Move finding) {
|
|
if (finding.column <= width / 2) return finding.column;
|
|
return width - 1 - finding.column;
|
|
}
|
|
|
|
public List<Move> explore() {
|
|
ArrayList<Move> moves = new ArrayList<Move>();
|
|
for (int i = 0; i < width; i++) {
|
|
Move move = new Move(this.currentRole(), i);
|
|
move.setPriority(getPriority(move));
|
|
if (!columnFull(i)) moves.add(move);
|
|
}
|
|
return moves;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuilder s = new StringBuilder((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<Coin, Move, Board> {
|
|
|
|
public Strategy() {
|
|
}
|
|
|
|
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<Coin, ContextFactory<Coin, Move, Board>> strategies = new HashMap<Coin, ContextFactory<Coin, Move, Board>>();
|
|
ContextFactory<Coin, Move, Board> redFactroy = new ContextFactory<Coin, Move, Board>(new Strategy(), 3000, false, false);
|
|
ContextFactory<Coin, Move, Board> blueFactroy = new ContextFactory<Coin, Move, Board>(new Strategy(), 50, false, false);
|
|
strategies.put(Coin.red, redFactroy);
|
|
strategies.put(Coin.blue, blueFactroy);
|
|
/*Battle<Coin, Move, Board> battle =*/ new Battle<Coin, Move, Board>(new Board(Coin.red), strategies, 2000);
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
|
|
battle();
|
|
/*
|
|
int cores = Runtime.getRuntime().availableProcessors();
|
|
Engine<Coin, Move, Board> engine = new Engine<Coin, Move, Board>(cores);
|
|
Agent<Coin, Move, Board> agent;
|
|
engine.start();
|
|
long comptime = 60;
|
|
long relaxtime = 20;
|
|
|
|
agent = new Agent<Coin, Move, Board>(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<Coin, Move, Board>(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();
|
|
}
|
|
|
|
}
|