Merge remote-tracking branch 'origin/master' into mauriAI
This commit is contained in:
commit
5605076dbf
5
Makefile
5
Makefile
|
@ -20,9 +20,8 @@ src/game/PlayerObject.java \
|
|||
src/player/Player.java \
|
||||
src/player/Human.java \
|
||||
src/player/malte/MalteAI.java \
|
||||
src/player/malte/Pattern.java \
|
||||
src/player/malte/Item.java \
|
||||
src/player/malte/PatternGenerator.java \
|
||||
src/player/malte/Position.java \
|
||||
src/player/malte/Sequence.java \
|
||||
src/player/maurizio/MaurizioAI.java
|
||||
|
||||
OBJECTS=$($(subst $(CLASSPATH),$(BUILDS),$(CLASSES)):.java=.class)
|
||||
|
|
|
@ -307,7 +307,11 @@ public class Game {
|
|||
}
|
||||
System.out.println("|");
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("+-+-+-+-+-+-+-+");
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
System.out.print("|" + i);
|
||||
}
|
||||
System.out.println("|");
|
||||
}
|
||||
|
||||
public static boolean checkWin(int[][] board, int player){
|
||||
|
|
|
@ -27,16 +27,15 @@ public class Human implements Player {
|
|||
}
|
||||
}
|
||||
int choice = -1;
|
||||
// make sure that the user chooses a legal move
|
||||
// make sure that the user chooses a legal move
|
||||
while (!options.contains(new Integer(choice))) {
|
||||
System.out.print("Choose a move from " + options + ": ");
|
||||
System.out.flush();
|
||||
System.out.flush();
|
||||
choice = sc.nextInt();
|
||||
if(!options.contains(new Integer(choice))){
|
||||
System.out.println("This is not a legal move!");
|
||||
}
|
||||
if(!options.contains(new Integer(choice))){
|
||||
System.out.println("This is not a legal move!");
|
||||
}
|
||||
}
|
||||
System.out.println(choice);
|
||||
return choice;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +1,35 @@
|
|||
package player.malte;
|
||||
|
||||
/**
|
||||
* An item of a pattern for the game of Connect Four.
|
||||
*/
|
||||
public class Item {
|
||||
|
||||
/**
|
||||
* The relative horizontal position of the item.
|
||||
*/
|
||||
private int posX;
|
||||
public final Position pos;
|
||||
public final int id;
|
||||
public final boolean hasScaffold;
|
||||
public final boolean onBoard;
|
||||
|
||||
/**
|
||||
* The relative vertical position of the item.
|
||||
*/
|
||||
private int posY;
|
||||
|
||||
/**
|
||||
* The IDs this item recognizes valid.
|
||||
*/
|
||||
private int[] ids;
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param posX Relative horizontal position.
|
||||
* @param posY Relative vertical position.
|
||||
* @param The accepted id.
|
||||
*/
|
||||
public Item(int posX, int posY, int id) {
|
||||
this(posX, posY, new int[]{id});
|
||||
public Item(Position pos,
|
||||
int id,
|
||||
boolean hasScaffold,
|
||||
boolean onBoard) {
|
||||
this.pos = pos;
|
||||
this.id = id;
|
||||
this.hasScaffold = hasScaffold;
|
||||
this.onBoard = onBoard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for multiple IDs.
|
||||
*
|
||||
* @param posX Relative horizontal position.
|
||||
* @param posY Relative vertical position.
|
||||
* @param The accepted IDs.
|
||||
*/
|
||||
public Item(int posX, int posY, int[] ids) {
|
||||
this.posX = posX;
|
||||
this.posY = posY;
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public int getPosX() {
|
||||
return this.posX;
|
||||
}
|
||||
|
||||
public int getPosY() {
|
||||
return this.posY;
|
||||
}
|
||||
|
||||
public int[] getIDs() {
|
||||
return this.ids;
|
||||
}
|
||||
|
||||
public boolean hasID(int id) {
|
||||
for (int i: ids) {
|
||||
if (i == id) {
|
||||
return true;
|
||||
}
|
||||
public String idString(int playerID, int enemyID) {
|
||||
String s = "";
|
||||
if (!onBoard) {
|
||||
s = "!";
|
||||
} else if (this.id == playerID) {
|
||||
s = "M";
|
||||
} else if (this.id == enemyID) {
|
||||
s = "E";
|
||||
} else if (hasScaffold) {
|
||||
s = "O";
|
||||
} else {
|
||||
s = " ";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the given oldID to the newID.
|
||||
* If newID already exists, do nothing.
|
||||
* IF oldID does not exist, do nothing.
|
||||
*
|
||||
* @param oldID The ID to be replaced.
|
||||
* @param newID The new ID.
|
||||
*/
|
||||
public void changeID(int oldID, int newID) {
|
||||
// If the ID already exists, do nothing.
|
||||
for (int id: ids) {
|
||||
if (id == newID) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise replace it, if oldID exists.
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (ids[i] == oldID) {
|
||||
ids[i] = newID;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = String.format("(%d, %d, [", posX, posY);
|
||||
for (int i: ids) {
|
||||
s += i + " ";
|
||||
}
|
||||
return s + "])";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of the item.
|
||||
*
|
||||
* @return A deep copy of this element.
|
||||
*/
|
||||
public Item copy() {
|
||||
return new Item(posX, posY, ids);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Random;
|
|||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
import java.lang.Math;
|
||||
|
||||
import player.Player;
|
||||
import game.Game;
|
||||
|
@ -56,67 +57,178 @@ public class MalteAI implements Player{
|
|||
|
||||
@Override
|
||||
public int move(int[][] board){
|
||||
// Create a set of all possible options.
|
||||
Set<Integer> options = new HashSet<>(Arrays.asList(0,1,2,3,4,5,6));
|
||||
// Remove impossible options.
|
||||
for (Integer i: copySet(options)) {
|
||||
if (board[i][0] != 0) {
|
||||
options.remove(i);
|
||||
double[] weights = new double[Game.GAME_COLUMNS];
|
||||
for (int i = 0; i < weights.length; i++) {
|
||||
weights[i] = calculateWeight(board, i);
|
||||
}
|
||||
double max = -1;
|
||||
for (double d: weights) {
|
||||
max = max > d ? max: d;
|
||||
}
|
||||
for (int i = 0; i < weights.length; i++) {
|
||||
if (Math.abs(max - weights[i]) < 0.000001) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Get options which would lead to instant win.
|
||||
Set<Integer> winningOptions = getRowCompletionOptions(options, board, id);
|
||||
for (Integer i: winningOptions) {
|
||||
// System.out.println("Choose to win");
|
||||
return i.intValue();
|
||||
}
|
||||
// Get options which would prevent an instant win of the enemy.
|
||||
Set<Integer> preventionsOptions = getRowCompletionOptions(options, board, enemyID);
|
||||
for (Integer i: preventionsOptions) {
|
||||
// System.out.println("Choose to prevent");
|
||||
return i.intValue();
|
||||
}
|
||||
// Choose a move that will continue a sequence that already exists.
|
||||
Set<Integer> twoOfFour = getTwoOfFourOptions(options, board, id);
|
||||
for (Integer i: twoOfFour) {
|
||||
// System.out.println("Choose to progress");
|
||||
return i.intValue();
|
||||
}
|
||||
// Choose a move that will not lead to an instant win of the enemy
|
||||
Set<Integer> choosewisely = new HashSet<>(options);
|
||||
while (choosewisely.size() > 0){
|
||||
Integer i = takeRandom(choosewisely);
|
||||
choosewisely.remove(i);
|
||||
int[][] fakeBoard = makeMove(copyBoard(board), i, id);
|
||||
if (getRowCompletionOptions(options, fakeBoard, enemyID).size() == 0) {
|
||||
// System.out.println("Choose random but smart");
|
||||
return i.intValue();
|
||||
}
|
||||
}
|
||||
// If nothing applies, take a random valied one.
|
||||
// System.out.println("Choose random and stupid");
|
||||
return takeRandom(options).intValue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Set<Integer> getTwoOfFourOptions(Set<Integer> options, int[][] board, int id) {
|
||||
Set<Pattern> twoOfFourPattern = PatternGenerator.winInTwoPatterns(id);
|
||||
// Get patterns, that match anywhere on the board.
|
||||
Set<Pattern> matches = Pattern.matchingPatterns(twoOfFourPattern, board);
|
||||
// Create set to be returned.
|
||||
Set<Integer> ret = new HashSet<>();
|
||||
// Iterate over all matches.
|
||||
for (Pattern p: matches) {
|
||||
// Get all positions, this pattern matches.
|
||||
Set<Position> positions = p.matches(board);
|
||||
// Get empty spaces in the pattern.
|
||||
for (Item i: p.getZeros()) {
|
||||
// Add all options to the set.
|
||||
for(Position pos: positions) {
|
||||
ret.add(new Integer(i.getPosX() + pos.getPosX()));
|
||||
}
|
||||
public double calculateWeight(int[][] board, int column) {
|
||||
double W_ILLEGAL = Double.MIN_VALUE;
|
||||
double W_DONT = 0.01;
|
||||
double weight = 1.0;
|
||||
// Prever a place in the center;
|
||||
weight *= 1.0 - Math.abs(3.0 - column) / (3.0 * 100.0);
|
||||
// Relative positions like (-1, -1), (0, -1), etc.
|
||||
Position[] relAround = Position.getRelCirclePositions();
|
||||
// The position we're looking at
|
||||
Position thisPos = getLastEmpty(board, column);
|
||||
// There is no space left in this row.
|
||||
if (thisPos == null) {
|
||||
weight *= W_ILLEGAL;
|
||||
return weight;
|
||||
}
|
||||
// Setup Sequences
|
||||
Sequence[] sequences = new Sequence[8];
|
||||
Sequence[] sequencesAbove = new Sequence[8];
|
||||
int[][] futureBoard = makeMove(copyBoard(board), column, this.id);
|
||||
for (int i = 0; i < sequences.length; i++) {
|
||||
sequences[i] = Sequence.readSequenceFromBoard(board,
|
||||
thisPos.add(relAround[i]),
|
||||
relAround[i],
|
||||
this.id,
|
||||
this.enemyID);
|
||||
sequences[i].setPlayerID(this.id);
|
||||
sequences[i].setEnemyID(this.enemyID);
|
||||
sequencesAbove[i] = Sequence.readSequenceFromBoard(futureBoard,
|
||||
thisPos.add(relAround[0]).add(relAround[i]),
|
||||
relAround[i],
|
||||
this.id,
|
||||
this.enemyID);
|
||||
sequencesAbove[i].setPlayerID(this.id);
|
||||
sequencesAbove[i].setEnemyID(this.enemyID);
|
||||
}
|
||||
for (int i = 0; i < sequences.length / 2; i++) {
|
||||
Sequence x = sequences[i];
|
||||
Sequence y = sequences[i + 4];
|
||||
weight *= getBaseWeightForSequences(x, y);
|
||||
Sequence xA = sequencesAbove[i];
|
||||
Sequence yA = sequencesAbove[i + 4];
|
||||
// weight *= getBaseWeightForSequences(xA, yA);
|
||||
if ((xA.matches("EEE") ||
|
||||
yA.matches("EEE")) ||
|
||||
((xA.matches("E") &&
|
||||
yA.matches("EE")) ||
|
||||
(xA.matches("EE") &&
|
||||
yA.matches("E")))) {
|
||||
// TODO: Do this right!
|
||||
weight /= 2<<6;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return weight;
|
||||
}
|
||||
|
||||
private double getBaseWeightForSequences(Sequence x, Sequence y) {
|
||||
double W_PRIORITY_1 = 2 << 8;
|
||||
double W_PRIORITY_2 = 2 << 6;
|
||||
double W_PRIORITY_3 = 2 << 4;
|
||||
double W_PRIORITY_4 = 2 << 2;
|
||||
double W_PRIORITY_5 = 2 << 0;
|
||||
double W_PRIORITY_6 = 1.0;
|
||||
double W_START = 1.0;
|
||||
double weight = W_START;
|
||||
if (x.matches("MMM") ||
|
||||
y.matches("MMM")) {
|
||||
weight *= W_PRIORITY_1;
|
||||
} else if ((x.matches("M") &&
|
||||
y.matches("MM")) ||
|
||||
(x.matches("MM") &&
|
||||
y.matches("M"))) {
|
||||
weight *= W_PRIORITY_1;
|
||||
} else if (x.matches("EEE") ||
|
||||
y.matches("EEE")) {
|
||||
weight *= W_PRIORITY_2;
|
||||
} else if ((x.matches("E") &&
|
||||
y.matches("EE")) ||
|
||||
(x.matches("EE") &&
|
||||
y.matches("E"))) {
|
||||
weight *= W_PRIORITY_2;
|
||||
} else if ((x.matches("O") &&
|
||||
y.matches("MMO")) ||
|
||||
(x.matches("MMO") &&
|
||||
y.matches("O"))) {
|
||||
weight *= W_PRIORITY_3;
|
||||
} else if ((x.matches("O") &&
|
||||
y.matches("EEO")) ||
|
||||
(x.matches("EEO") &&
|
||||
y.matches("O"))) {
|
||||
weight *= W_PRIORITY_4;
|
||||
} else if (x.matches("EO") &&
|
||||
y.matches("EO")) {
|
||||
weight *= W_PRIORITY_4;
|
||||
} else if (x.matches("MMO") ||
|
||||
y.matches("MMO")) {
|
||||
weight *= W_PRIORITY_5;
|
||||
} else if ((x.matches("O") &&
|
||||
y.matches("MM")) ||
|
||||
(x.matches("MM") &&
|
||||
y.matches("O"))) {
|
||||
weight *= W_PRIORITY_5;
|
||||
} else if ((x.matches("M") &&
|
||||
y.matches("MO")) ||
|
||||
(x.matches("MO") &&
|
||||
y.matches("M"))) {
|
||||
weight *= W_PRIORITY_5;
|
||||
} else if (x.matches("EEO") ||
|
||||
y.matches("EEO")) {
|
||||
weight *= W_PRIORITY_6;
|
||||
} else if ((x.matches("O") &&
|
||||
y.matches("EE")) ||
|
||||
(x.matches("EE") &&
|
||||
y.matches("O"))) {
|
||||
weight *= W_PRIORITY_6;
|
||||
} else if ((x.matches("E") &&
|
||||
y.matches("EO")) ||
|
||||
(x.matches("EO") &&
|
||||
y.matches("E"))) {
|
||||
weight *= W_PRIORITY_6;
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Position getLastEmpty(int[][] board, int column) {
|
||||
if (board[column][0] != 0) {
|
||||
return null;
|
||||
}
|
||||
int row = 0;
|
||||
while (row < Game.GAME_ROWS - 1 &&
|
||||
board[column][row + 1] == 0) {
|
||||
row++;
|
||||
}
|
||||
return new Position(column, row);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private boolean isPosOnBoard(Position pos) {
|
||||
if (pos.getPosX() <= Game.GAME_COLUMNS - 1 &&
|
||||
pos.getPosX() >= 0 &&
|
||||
pos.getPosY() <= Game.GAME_ROWS - 1 &&
|
||||
pos.getPosY() >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private int getIDFromBoard(int[][] board, Position pos) {
|
||||
return board[pos.getPosX()][pos.getPosY()];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,64 +273,6 @@ public class MalteAI implements Player{
|
|||
return board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instant winning options.
|
||||
* Returns a set of options for the player to choose from. If any one is choosen and
|
||||
* played by the player with the given id, a win is certain.
|
||||
*
|
||||
* @param options The options, that are valid, a subset is returned.
|
||||
* @param board The current game's board.
|
||||
* @param id The players id.
|
||||
* @return A subset of options which would lead to a win.
|
||||
*/
|
||||
private Set<Integer> getRowCompletionOptions(Set<Integer> options, int[][] board, int id) {
|
||||
// Get winning patterns from Generator.
|
||||
Set<Pattern> pats = PatternGenerator.winningPatterns(id);
|
||||
// Get patterns, that match anywhere on the board.
|
||||
Set<Pattern> matches = Pattern.matchingPatterns(pats, board);
|
||||
// Create set to be returned.
|
||||
Set<Integer> ret = new HashSet<>();
|
||||
// Iterate over all matches.
|
||||
for (Pattern p: matches) {
|
||||
// Get all positions, this pattern matches.
|
||||
Set<Position> positions = p.matches(board);
|
||||
// Get empty spaces in the pattern.
|
||||
for (Item i: p.getZeros()) {
|
||||
// Add all options to the set.
|
||||
for(Position pos: positions) {
|
||||
ret.add(new Integer(i.getPosX() + pos.getPosX()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a set of Integer.
|
||||
*/
|
||||
private Set<Integer> copySet(Set<Integer> s) {
|
||||
return new HashSet<Integer>(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a random Integer from a set of Integer.
|
||||
*
|
||||
* @param s The set to take from.
|
||||
* @return A random element of s.
|
||||
*/
|
||||
private Integer takeRandom(Set<Integer> s) {
|
||||
int item = ran.nextInt(s.size());
|
||||
int i = 0;
|
||||
for (Object obj: s) {
|
||||
if (i == item) {
|
||||
return (Integer) obj;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// TODO: Change this
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player's name.
|
||||
*/
|
||||
|
|
|
@ -1,197 +0,0 @@
|
|||
package player.malte;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
|
||||
import game.Game;
|
||||
|
||||
/**
|
||||
* A Connect Four Pattern.
|
||||
* Every part has a relative position and a number of IDs that it matches.
|
||||
* When Pattern.matches(int[][]) is called, all possible positions on the board
|
||||
* are checked against every part of the pattern. If all parts match, the whole
|
||||
* pattern matches.
|
||||
*/
|
||||
public class Pattern {
|
||||
|
||||
/**
|
||||
* The parts of the pattern.
|
||||
*/
|
||||
private Set<Item> parts;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parts The parts of the pattern.
|
||||
*/
|
||||
public Pattern(Item... parts) {
|
||||
this.parts = new HashSet<Item>(Arrays.asList(parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parts The parts of the pattern.
|
||||
*/
|
||||
public Pattern(Set<Item> parts) {
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching positions.
|
||||
* Checks the given board for matches. The positions of the matches are returned.
|
||||
*
|
||||
* @param board The game's board.
|
||||
* @return The positions of the matches.
|
||||
*/
|
||||
public Set<Position> matches(int[][] board) {
|
||||
// Preparing iteration
|
||||
int maxLeft = 0,
|
||||
maxRight = 0,
|
||||
maxUp = 0,
|
||||
maxDown = 0;
|
||||
for (Item i: parts) {
|
||||
if (i.hasID(-1)) {
|
||||
continue;
|
||||
}
|
||||
if (maxLeft < -i.getPosX()) {
|
||||
maxLeft = -i.getPosX();
|
||||
}
|
||||
if (maxRight > -i.getPosX()) {
|
||||
maxRight = -i.getPosX();
|
||||
}
|
||||
if (maxUp < -i.getPosY()) {
|
||||
maxUp = -i.getPosY();
|
||||
}
|
||||
if (maxDown > -i.getPosY()) {
|
||||
maxDown = -i.getPosY();
|
||||
}
|
||||
}
|
||||
// Iteration + Position accumulation
|
||||
Set<Position> set = new HashSet<>();
|
||||
for (int i = maxLeft; i < Game.GAME_COLUMNS + maxRight; i++) {
|
||||
inner:for (int j = maxUp; j < Game.GAME_ROWS + maxDown; j++) {
|
||||
for (Item k: parts) {
|
||||
int posX = i + k.getPosX();
|
||||
int posY = j + k.getPosY();
|
||||
if ((!isOnBoard(new Position(posX, posY)) &&
|
||||
!k.hasID(-1)) ||
|
||||
(isOnBoard(new Position(posX, posY)) &&
|
||||
!k.hasID(board[posX][posY]))) {
|
||||
continue inner;
|
||||
}
|
||||
}
|
||||
set.add(new Position(i, j));
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given position on the Board.
|
||||
*
|
||||
* @param pos The position to check.
|
||||
* @return Whether the position is on the board.
|
||||
*/
|
||||
private boolean isOnBoard(Position pos) {
|
||||
if (pos.getPosX() < 0 ||
|
||||
pos.getPosX() > Game.GAME_COLUMNS - 1 ||
|
||||
pos.getPosY() < 0 ||
|
||||
pos.getPosY() > Game.GAME_ROWS - 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a part of the Pattern.
|
||||
*
|
||||
* @param oldItem The part to be replaced.
|
||||
* @param newItem The part to added.
|
||||
*/
|
||||
public void replaceItem(Item oldItem, Item newItem) {
|
||||
if (this.parts.contains(oldItem)) {
|
||||
this.parts.remove(oldItem);
|
||||
this.parts.add(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a part to the pattern.
|
||||
*
|
||||
* @param part The new part.
|
||||
*/
|
||||
public void addItem(Item part) {
|
||||
this.parts.add(part);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the zeros in this pattern.
|
||||
* Returns a set of all parts which contain a zero in their valid IDs.
|
||||
*
|
||||
* @return The set of parts
|
||||
*/
|
||||
public Set<Item> getZeros() {
|
||||
Set<Item> ret = new HashSet<>();
|
||||
for (Item i: parts) {
|
||||
if (i.hasID(0)) {
|
||||
ret.add(i);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of this pattern.
|
||||
*
|
||||
* @return This pattern... only copied.
|
||||
*/
|
||||
public Pattern copy() {
|
||||
Set<Item> itemCopy = new HashSet<>();
|
||||
for (Item i: parts) {
|
||||
itemCopy.add(i.copy());
|
||||
}
|
||||
return new Pattern(itemCopy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static matching method for a set of pattern.
|
||||
* Returns a set of all positions any one pattern matches against.
|
||||
*
|
||||
* @param pats The patterns to match.
|
||||
* @param board The board to match against.
|
||||
*/
|
||||
public static Set<Position> matches(Set<Pattern> pats, int[][] board) {
|
||||
Set<Position> ret = new HashSet<>();
|
||||
for (Pattern p: pats) {
|
||||
ret.addAll(p.matches(board));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of all patterns, that do have a match on the board.
|
||||
*
|
||||
* @param pats The patterns to check.
|
||||
* @param board The board to match against.
|
||||
*/
|
||||
public static Set<Pattern> matchingPatterns(Set<Pattern> pats, int[][] board) {
|
||||
Set<Pattern> ret = new HashSet<>();
|
||||
for (Pattern p: pats) {
|
||||
if (p.matches(board).size() > 0) {
|
||||
ret.add(p);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "";
|
||||
for (Item i: parts) {
|
||||
s += i + ", ";
|
||||
}
|
||||
return s + "\n";
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package player.malte;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* A generator for patterns for the game of Connect Four.
|
||||
*/
|
||||
public class PatternGenerator {
|
||||
|
||||
/**
|
||||
* Do not instanciate.
|
||||
*/
|
||||
private PatternGenerator() {}
|
||||
|
||||
/**
|
||||
* Returns the winning patterns.
|
||||
* X | X | XXXX | X
|
||||
* X | X | | X
|
||||
* X | X | | X
|
||||
* X | X | | X
|
||||
* Any of the above with an empty space is considered a winning pattern.
|
||||
* Except the first one, in which only the topmost space can be empty.
|
||||
*
|
||||
* @param id The player's id.
|
||||
*/
|
||||
public static Set<Pattern> winningPatterns(int id) {
|
||||
// Four in a row with one hole
|
||||
Set<Pattern> pats = PatternGenerator.emptySpaceGenerator(new Item(0, 0, id),
|
||||
new Item(1, 0, id),
|
||||
new Item(2, 0, id),
|
||||
new Item(3, 0, id));
|
||||
// Four in a diagonal line from lower left to upper right with one hole
|
||||
pats.addAll(PatternGenerator.emptySpaceGenerator(new Item(0, 0, id),
|
||||
new Item(-1, 1, id),
|
||||
new Item(-2, 2, id),
|
||||
new Item(-3, 3, id)));
|
||||
// Four in a diagonal line from upper left to lower right with one hole
|
||||
pats.addAll(PatternGenerator.emptySpaceGenerator(new Item(0, 0, id),
|
||||
new Item(1, 1, id),
|
||||
new Item(2, 2, id),
|
||||
new Item(3, 3, id)));
|
||||
// Three on top of each other.
|
||||
pats.add(new Pattern(new Item(0, 0, 0),
|
||||
new Item(0, 1, id),
|
||||
new Item(0, 2, id),
|
||||
new Item(0, 3, id)));
|
||||
return pats;
|
||||
}
|
||||
|
||||
public static Set<Pattern> winInTwoPatterns(int id) {
|
||||
// Four in a row with two holes
|
||||
Set<Pattern> pats = PatternGenerator.empty2SpaceGenerator(new Item(0, 0, id),
|
||||
new Item(1, 0, id),
|
||||
new Item(2, 0, id),
|
||||
new Item(3, 0, id));
|
||||
// Four in a diagonal line from lower left to upper right with two holes
|
||||
pats.addAll(PatternGenerator.empty2SpaceGenerator(new Item(0, 0, id),
|
||||
new Item(-1, 1, id),
|
||||
new Item(-2, 2, id),
|
||||
new Item(-3, 3, id)));
|
||||
// Four in a diagonal line from upper left to lower right with two holes
|
||||
pats.addAll(PatternGenerator.empty2SpaceGenerator(new Item(0, 0, id),
|
||||
new Item(1, 1, id),
|
||||
new Item(2, 2, id),
|
||||
new Item(3, 3, id)));
|
||||
// Three on top of each other.
|
||||
pats.add(new Pattern(new Item(0, 0, 0),
|
||||
new Item(0, 1, id),
|
||||
new Item(0, 2, id)));
|
||||
return pats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates patterns with one empty space.
|
||||
* Returns a set of patterns, each one containing an empty space.
|
||||
* Each element of model is replaced once with an empty space Item.
|
||||
* Always adds one support Item beneath the hole.
|
||||
*
|
||||
* @param model The basis of the pattern.
|
||||
* @return A set of patterns created by the above rule.
|
||||
*/
|
||||
public static Set<Pattern> emptySpaceGenerator(Item... model) {
|
||||
Set<Pattern> ret = new HashSet<>();
|
||||
// Iterate over all models.
|
||||
for (int i = 0; i < model.length; i++) {
|
||||
// Create a new pattern.
|
||||
Pattern newP = new Pattern(model);
|
||||
Item x = model[i];
|
||||
Item newI = new Item(x.getPosX(), x.getPosY(), 0);
|
||||
// Replace one item with the new 0-one.
|
||||
newP.replaceItem(x, newI);
|
||||
// Add the support.
|
||||
newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{-1, 1, 2}));
|
||||
// Add the pattern to the set.
|
||||
ret.add(newP);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Patterns with two empty holes.
|
||||
* Generates all possible patterns given the parts, which contain
|
||||
* two empty spaces supported by two parts.
|
||||
*
|
||||
* @param model The pattern's basis.
|
||||
* @return All possible patterns with two holes.
|
||||
*/
|
||||
public static Set<Pattern> empty2SpaceGenerator(Item... model) {
|
||||
Set<Pattern> ret = new HashSet<>();
|
||||
// Iterate over all models.
|
||||
for (int i = 0; i < model.length; i++) {
|
||||
for (int j = 0; j < model.length; j++) {
|
||||
// If we're looking at the same model, skip.
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
// Create a new Pattern with the models.
|
||||
Pattern newP = new Pattern(model);
|
||||
Item x = model[i];
|
||||
Item y = model[j];
|
||||
// Create new Items to replace two items.
|
||||
Item newXI = new Item(x.getPosX(), x.getPosY(), 0);
|
||||
Item newYI = new Item(y.getPosX(), y.getPosY(), 0);
|
||||
// Actually replace them.
|
||||
newP.replaceItem(x, newXI);
|
||||
newP.replaceItem(y, newYI);
|
||||
// Add support items.
|
||||
newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{-1, 1, 2}));
|
||||
newP.addItem(new Item(y.getPosX(), y.getPosY() + 1, new int[]{-1, 1, 2}));
|
||||
// Add the pattern to the set.
|
||||
ret.add(newP);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,8 +20,26 @@ public class Position {
|
|||
return this.posY;
|
||||
}
|
||||
|
||||
public Position add(Position pos) {
|
||||
return new Position(this.getPosX() + pos.getPosX(),
|
||||
this.getPosY() + pos.getPosY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%d, %d)", posX, posY);
|
||||
return String.format("(%+d, %+d)", posX, posY);
|
||||
}
|
||||
|
||||
public static Position[] getRelCirclePositions() {
|
||||
return new Position[]{
|
||||
new Position(0, -1),
|
||||
new Position(1, -1),
|
||||
new Position(1, 0),
|
||||
new Position(1, 1),
|
||||
new Position(0, 1),
|
||||
new Position(-1, 1),
|
||||
new Position(-1, 0),
|
||||
new Position(-1, -1)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
98
src/player/malte/Sequence.java
Normal file
98
src/player/malte/Sequence.java
Normal file
|
@ -0,0 +1,98 @@
|
|||
package player.malte;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import game.Game;
|
||||
|
||||
public class Sequence {
|
||||
|
||||
private Position startingPos;
|
||||
private Position direction;
|
||||
private List<Item> items;
|
||||
private int playerID;
|
||||
private int enemyID;
|
||||
|
||||
public Sequence(Position startingPos, Position direction, List<Item> items) {
|
||||
this.items = new ArrayList<Item>(items);
|
||||
this.startingPos = startingPos;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public void setPlayerID(int playerID) {
|
||||
this.playerID = playerID;
|
||||
}
|
||||
|
||||
public void setEnemyID(int enemyID) {
|
||||
this.enemyID = enemyID;
|
||||
}
|
||||
|
||||
public boolean matches(String s) {
|
||||
String idStr = idString().toLowerCase();
|
||||
s = s.toLowerCase();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (i >= idStr.length()) {
|
||||
return false;
|
||||
}
|
||||
if (s.charAt(i) == 'p' &&
|
||||
(idStr.charAt(i) != 'm' &&
|
||||
idStr.charAt(i) != 'e')) {
|
||||
return false;
|
||||
} else if (s.charAt(i) != idStr.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Sequence readSequenceFromBoard(int[][] board,
|
||||
Position pos,
|
||||
Position dir,
|
||||
int playerID,
|
||||
int enemyID) {
|
||||
Position cur = pos;
|
||||
List<Item> items = new ArrayList<Item>();
|
||||
Position down = new Position(0, 1);
|
||||
while (isPosOnBoard(cur)) {
|
||||
boolean hasScaffold = (isPosOnBoard(cur.add(down)) &&
|
||||
getIDFromBoard(board, cur.add(down)) != 0) || cur.getPosY() == 5;
|
||||
items.add(new Item(cur,
|
||||
getIDFromBoard(board, cur),
|
||||
hasScaffold,
|
||||
true));
|
||||
cur = cur.add(dir);
|
||||
}
|
||||
items.add(new Item(cur,
|
||||
-1,
|
||||
false,
|
||||
false));
|
||||
return new Sequence(pos, dir, items);
|
||||
}
|
||||
|
||||
private static boolean isPosOnBoard(Position pos) {
|
||||
if (pos.getPosX() <= Game.GAME_COLUMNS - 1 &&
|
||||
pos.getPosX() >= 0 &&
|
||||
pos.getPosY() <= Game.GAME_ROWS - 1 &&
|
||||
pos.getPosY() >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Integer getIDFromBoard(int[][] board, Position pos) {
|
||||
return new Integer(board[pos.getPosX()][pos.getPosY()]);
|
||||
}
|
||||
|
||||
public String idString() {
|
||||
String s = "";
|
||||
for (Item x: items) {
|
||||
s += x.idString(playerID, enemyID);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("START: %s, DIR: %s, \"%s\"", startingPos, direction, idString());
|
||||
}
|
||||
}
|
14
src/player/malte/SequencesOrdered
Normal file
14
src/player/malte/SequencesOrdered
Normal file
|
@ -0,0 +1,14 @@
|
|||
1 XMMM // Vierter in der Reihe
|
||||
1 MXMM
|
||||
2 XEEE // Vierter beim Gegner
|
||||
2 EXEE
|
||||
3 _XMM_ // Sicherer Sieg nächste Runde
|
||||
3 _MXM_
|
||||
4 _XEE_ // Sicherer Sieg des Gegners nächste Runde
|
||||
4 _EXE_
|
||||
5 XMM_ // Möglicher Sieg nächste Runde
|
||||
5 _XMM
|
||||
5 MXM_
|
||||
6 XEE_ // Möglicher Sieg des Gegners in der nächsten Runde
|
||||
6 _XEE
|
||||
6 EXE_
|
Loading…
Reference in a new issue