commit
9df17ac2e8
21
Makefile
21
Makefile
|
@ -11,16 +11,17 @@ DOC=doc
|
||||||
CLASSPATH=src
|
CLASSPATH=src
|
||||||
LIBARIES=
|
LIBARIES=
|
||||||
CLASSES=\
|
CLASSES=\
|
||||||
src\game\Main.java \
|
src/game/Main.java \
|
||||||
src\game\Game.java \
|
src/game/Game.java \
|
||||||
src\game\GameHistory.java \
|
src/game/GameHistory.java \
|
||||||
src\game\GameEntry.java \
|
src/game/GameEntry.java \
|
||||||
src\game\PlayerObject.java \
|
src/game/PlayerObject.java \
|
||||||
src\player\Player.java \
|
src/player/Player.java \
|
||||||
src\player\malte\MalteAI.java \
|
src/player/malte/MalteAI.java \
|
||||||
src\player\malte\Pattern.java \
|
src/player/malte/Pattern.java \
|
||||||
src\player\malte\Item.java \
|
src/player/malte/Item.java \
|
||||||
src\player\maurizio\MaurizioAI.java
|
src/player/malte/PatternGenerator.java \
|
||||||
|
src/player/maurizio/MaurizioAI.java
|
||||||
|
|
||||||
OBJECTS=$($(subst $(CLASSPATH),$(BUILDS),$(CLASSES)):.java=.class)
|
OBJECTS=$($(subst $(CLASSPATH),$(BUILDS),$(CLASSES)):.java=.class)
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ public class Game {
|
||||||
return state; // the result of the game
|
return state; // the result of the game
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println("BUG!");
|
||||||
return checkState(false); // it is impossible to reach this, but it makes the compiler happy ;)
|
return checkState(false); // it is impossible to reach this, but it makes the compiler happy ;)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +170,8 @@ public class Game {
|
||||||
// Check his choice against the current board.
|
// Check his choice against the current board.
|
||||||
if (choice < 0 || choice > GAME_COLUMNS || this.board[choice][0] != 0) {
|
if (choice < 0 || choice > GAME_COLUMNS || this.board[choice][0] != 0) {
|
||||||
// If a player makes a false move, the game punishes him.
|
// If a player makes a false move, the game punishes him.
|
||||||
this.gameOn = false;
|
// TODO: Fix this, causes -1 return of checkstate in Simulation mode, breaks the game.
|
||||||
|
// this.gameOn = false;
|
||||||
//TODO the game gets stopped, but checkState doesn't know what that means!?
|
//TODO the game gets stopped, but checkState doesn't know what that means!?
|
||||||
if(output){
|
if(output){
|
||||||
log(currentP.getP().getName() + " made an illegal move and lost!");
|
log(currentP.getP().getName() + " made an illegal move and lost!");
|
||||||
|
|
|
@ -1,15 +1,43 @@
|
||||||
package player.malte;
|
package player.malte;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item of a pattern for the game of Connect Four.
|
||||||
|
*/
|
||||||
public class Item {
|
public class Item {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The relative horizontal position of the item.
|
||||||
|
*/
|
||||||
private int posX;
|
private int posX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The relative vertical position of the item.
|
||||||
|
*/
|
||||||
private int posY;
|
private int posY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IDs this item recognizes valid.
|
||||||
|
*/
|
||||||
private int[] ids;
|
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) {
|
public Item(int posX, int posY, int id) {
|
||||||
this(posX, posY, new int[]{id});
|
this(posX, posY, new int[]{id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
public Item(int posX, int posY, int[] ids) {
|
||||||
this.posX = posX;
|
this.posX = posX;
|
||||||
this.posY = posY;
|
this.posY = posY;
|
||||||
|
@ -36,4 +64,46 @@ public class Item {
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,60 +6,201 @@ import java.util.HashSet;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import player.Player;
|
import player.Player;
|
||||||
|
import game.Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maltes artificial intelligence for playing a game of Connect Four
|
||||||
|
*/
|
||||||
public class MalteAI implements Player{
|
public class MalteAI implements Player{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the player.
|
||||||
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random Object.
|
||||||
|
*/
|
||||||
private Random ran;
|
private Random ran;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The players ID.
|
||||||
|
*/
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The enemy's ID.
|
||||||
|
*/
|
||||||
private int enemyID;
|
private int enemyID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param name The name for the player.
|
||||||
|
*/
|
||||||
public MalteAI(String name){
|
public MalteAI(String name){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.ran = new Random();
|
this.ran = new Random();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setPlayerID(int id) {
|
public void setPlayerID(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setEnemyID(int id) {
|
public void setEnemyID(int id) {
|
||||||
this.enemyID = enemyID;
|
this.enemyID = enemyID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int move(int[][] board){
|
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));
|
Set<Integer> options = new HashSet<>(Arrays.asList(0,1,2,3,4,5,6));
|
||||||
|
// Remove impossible options.
|
||||||
for (Integer i: copySet(options)) {
|
for (Integer i: copySet(options)) {
|
||||||
if (board[i][0] != 0) {
|
if (board[i][0] != 0) {
|
||||||
options.remove(i);
|
options.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<Integer> instantWins = getWinningOptions(options, board);
|
// Get options which would lead to instant win.
|
||||||
// Set<Integer> preventions = getPreventionOptions(options, board);
|
Set<Integer> winningOptions = getRowCompletionOptions(options, board, id);
|
||||||
|
for (Integer i: winningOptions) {
|
||||||
|
return i.intValue();
|
||||||
|
}
|
||||||
|
// Get options which would prevent an instant win of the enemy.
|
||||||
|
enemyID = enemyID == 0 ? 1: enemyID;
|
||||||
|
Set<Integer> preventionsOptions = getRowCompletionOptions(options, board, enemyID);
|
||||||
|
for (Integer i: preventionsOptions) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
return i.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If nothing applies, take a random valied one.
|
||||||
return takeRandom(options).intValue();
|
return takeRandom(options).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Integer> getWinningOptions(Set<Integer> options, int[][] board) {
|
private Set<Integer> getTwoOfFourOptions(Set<Integer> options, int[][] board, int id) {
|
||||||
Pattern topOfColumn = new Pattern(new Item(0, 0, 0),
|
Set<Pattern> twoOfFourPattern = PatternGenerator.winInTwoPatterns(id);
|
||||||
new Item(0, 1, id),
|
// Get patterns, that match anywhere on the board.
|
||||||
new Item(0, 2, id),
|
Set<Pattern> matches = Pattern.matchingPatterns(twoOfFourPattern, board);
|
||||||
new Item(0, 3, id));
|
// Create set to be returned.
|
||||||
Pattern leftInRow = new Pattern(new Item(0, 0, 0),
|
Set<Integer> ret = new HashSet<>();
|
||||||
new Item(1, 0, id),
|
// Iterate over all matches.
|
||||||
new Item(2, 0, id),
|
for (Pattern p: matches) {
|
||||||
new Item(3, 0, id));
|
// Get all positions, this pattern matches.
|
||||||
Pattern rightInRow = new Pattern(new Item(0, 0, 0),
|
Set<Position> positions = p.matches(board);
|
||||||
new Item(-1, 0, id),
|
// Get empty spaces in the pattern.
|
||||||
new Item(-2, 0, id),
|
for (Item i: p.getZeros()) {
|
||||||
new Item(-3, 0, id));
|
// Add all options to the set.
|
||||||
return null;
|
for(Position pos: positions) {
|
||||||
|
ret.add(new Integer(i.getPosX() + pos.getPosX()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the given board.
|
||||||
|
*
|
||||||
|
* @param board The board to be copied.
|
||||||
|
* @return A deep copy of the board.
|
||||||
|
*/
|
||||||
|
private int[][] copyBoard(int[][] board) {
|
||||||
|
int[][] copy = new int[board.length][board[0].length];
|
||||||
|
for (int i = 0; i < board.length; i++) {
|
||||||
|
for (int j = 0; j < board[i].length; j++) {
|
||||||
|
copy[i][j] = board[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a move on the given board.
|
||||||
|
* Makes a move on the given board, no winning check or anything,
|
||||||
|
* just a fake move on the given board. Necessary for confirming that
|
||||||
|
* a chosen move will not give the enemy an instant win.
|
||||||
|
*
|
||||||
|
* @param board The board on which the move is made.
|
||||||
|
* @param choice The column to play in.
|
||||||
|
* @param id The player's id.
|
||||||
|
* @return The modified board.
|
||||||
|
*/
|
||||||
|
private int[][] makeMove(int[][] board, int choice, int id) {
|
||||||
|
int row = 0;
|
||||||
|
// If the column is full, do nothing.
|
||||||
|
if (board[choice][row] != 0) {
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
// Find the last empty row.
|
||||||
|
while (row < Game.GAME_ROWS - 1 && board[choice][row + 1] == 0) {
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
// Add the players piece.
|
||||||
|
board[choice][row] = id;
|
||||||
|
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) {
|
private Set<Integer> copySet(Set<Integer> s) {
|
||||||
return new HashSet<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) {
|
private Integer takeRandom(Set<Integer> s) {
|
||||||
int item = ran.nextInt(s.size());
|
int item = ran.nextInt(s.size());
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -73,6 +214,9 @@ public class MalteAI implements Player{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the player's name.
|
||||||
|
*/
|
||||||
public String getName(){
|
public String getName(){
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,197 @@
|
||||||
package player.malte;
|
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 {
|
public class Pattern {
|
||||||
|
|
||||||
private Item[] parts;
|
/**
|
||||||
|
* The parts of the pattern.
|
||||||
|
*/
|
||||||
|
private Set<Item> parts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param parts The parts of the pattern.
|
||||||
|
*/
|
||||||
public Pattern(Item... parts) {
|
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;
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
138
src/player/malte/PatternGenerator.java
Normal file
138
src/player/malte/PatternGenerator.java
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
src/player/malte/Position.java
Normal file
27
src/player/malte/Position.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package player.malte;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
|
||||||
|
public class Position {
|
||||||
|
|
||||||
|
private final int posX;
|
||||||
|
private final int posY;
|
||||||
|
|
||||||
|
public Position(int posX, int posY) {
|
||||||
|
this.posX = posX;
|
||||||
|
this.posY = posY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosX() {
|
||||||
|
return this.posX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosY() {
|
||||||
|
return this.posY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("(%d, %d)", posX, posY);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue