From bbd22f830e512023c93bfa6ad047c83eb793b4c8 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Wed, 18 Oct 2017 15:16:05 +0200 Subject: [PATCH 01/16] Working on Patterns, doing great --- src/game/Game.java | 4 +-- src/player/malte/MalteAI.java | 47 +++++++++++++++++++++----------- src/player/malte/Pattern.java | 50 ++++++++++++++++++++++++++++++++++ src/player/malte/Position.java | 27 ++++++++++++++++++ 4 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 src/player/malte/Position.java diff --git a/src/game/Game.java b/src/game/Game.java index 974eda6..89f4b7d 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -13,12 +13,12 @@ public class Game { /** * Number of columns on the board. */ - private final static int GAME_COLUMNS = 7; + public final static int GAME_COLUMNS = 7; /** * Number of rows on the board. */ - private final static int GAME_ROWS = 6; + public final static int GAME_ROWS = 6; /** * Player One diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 1211ed8..340bb4f 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -11,7 +11,7 @@ public class MalteAI implements Player{ private String name; private Random ran; - private int id = 2; + private int id = 1; public MalteAI(String name){ this.name = name; @@ -25,26 +25,41 @@ public class MalteAI implements Player{ options.remove(i); } } - Set instantWins = getWinningOptions(options, board); - // Set preventions = getPreventionOptions(options, board); + Set winningOptions = getWinningOptions(options, board); + for (Integer i: winningOptions) { + return i.intValue(); + } return takeRandom(options).intValue(); } private Set getWinningOptions(Set options, int[][] board) { - Pattern topOfColumn = new Pattern(new Item(0, 0, 0), - new Item(0, 1, id), - new Item(0, 2, id), - new Item(0, 3, id)); - Pattern leftInRow = new Pattern(new Item(0, 0, 0), - new Item(1, 0, id), - new Item(2, 0, id), - new Item(3, 0, id)); - Pattern rightInRow = new Pattern(new Item(0, 0, 0), - new Item(-1, 0, id), - new Item(-2, 0, id), - new Item(-3, 0, id)); - return null; + Pattern[] pats = new Pattern[]{ + // Three in a column, empty above + new Pattern(new Item(0, 0, 0), + new Item(0, 1, id), + new Item(0, 2, id), + new Item(0, 3, id)), + // Three in a row, empty left + new Pattern(new Item(0, 0, 0), + new Item(1, 0, id), + new Item(2, 0, id), + new Item(3, 0, id)), + // Three in a row, empty right + new Pattern(new Item(0, 0, 0), + new Item(-1, 0, id), + new Item(-2, 0, id), + new Item(-3, 0, id))} + Set matches = pats.matches(board); + Set ret = new HashSet<>(); + for (Position x: matches) { + System.out.println(x); + if (options.contains(x.getPosX())) { + ret.add(new Integer(x.getPosX())); + } + } + + return ret; } private Set copySet(Set s) { diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index 156f03f..979b3ca 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -1,5 +1,10 @@ package player.malte; +import java.util.Set; +import java.util.HashSet; + +import game.Game; + public class Pattern { private Item[] parts; @@ -7,4 +12,49 @@ public class Pattern { public Pattern(Item... parts) { this.parts = parts; } + + public Set matches(int[][] board) { + // Preparing iteration + int maxLeft = 0, + maxRight = 0, + maxUp = 0, + maxDown = 0; + for (Item i: parts) { + 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 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 (!k.hasID(board[posX][posY])) { + continue inner; + } + } + set.add(new Position(i, j)); + } + } + return set; + } + + public static Set matches(Pattern[] pats, int[][] board) { + Set ret = new HashSet<>(); + for (Pattern p: pats) { + ret.addAll(p.matches(board)); + } + return ret; + } } diff --git a/src/player/malte/Position.java b/src/player/malte/Position.java new file mode 100644 index 0000000..3f28b12 --- /dev/null +++ b/src/player/malte/Position.java @@ -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); + } +} From 3085c9255b1cabacce7bcaebebfd020b6df21225 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Wed, 18 Oct 2017 22:41:44 +0200 Subject: [PATCH 02/16] Working on pattern implementation --- src/player/malte/MalteAI.java | 24 +++++++++++++++++++++--- src/player/malte/Pattern.java | 29 +++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 340bb4f..407c600 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -44,13 +44,31 @@ public class MalteAI implements Player{ new Pattern(new Item(0, 0, 0), new Item(1, 0, id), new Item(2, 0, id), - new Item(3, 0, id)), + new Item(3, 0, id), + new Item(0, 1, new int[]{1, 2})), // Three in a row, empty right new Pattern(new Item(0, 0, 0), new Item(-1, 0, id), new Item(-2, 0, id), - new Item(-3, 0, id))} - Set matches = pats.matches(board); + new Item(-3, 0, id), + new Item(0, 1, new int[]{1, 2})), + // Three in a diagonal line from lower left to upper right, empty right above + new Pattern(new Item(0, 0, 0), + new Item(-1, 1, id), + new Item(-2, 2, id), + new Item(-3, 3, id), + // TODO: Improve IDs here + new Item(0, 1, new int[]{1, 2})), + // Three in a diagonal line from upper left to lower right, empty left above + new Pattern(new Item(0, 0, 0), + new Item(1, 1, id), + new Item(2, 2, id), + new Item(3, 3, id), + // TODO: Improve IDs here + new Item(0, 1, new int[]{1, 2})), + }; + // Set patterns = + Set matches = Pattern.matches(pats, board); Set ret = new HashSet<>(); for (Position x: matches) { System.out.println(x); diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index 979b3ca..c47a1c4 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -2,15 +2,16 @@ package player.malte; import java.util.Set; import java.util.HashSet; +import java.util.Arrays; import game.Game; public class Pattern { - private Item[] parts; + private Set parts; public Pattern(Item... parts) { - this.parts = parts; + this.parts = new HashSet(Arrays.asList(parts)); } public Set matches(int[][] board) { @@ -50,6 +51,17 @@ public class Pattern { return set; } + public void replaceItem(Item oldItem, Item newItem) { + if (this.parts.contains(oldItem)) { + this.parts.remove(oldItem); + this.parts.add(newItem); + } + } + + public void addItem(Item item) { + this.parts.add(item); + } + public static Set matches(Pattern[] pats, int[][] board) { Set ret = new HashSet<>(); for (Pattern p: pats) { @@ -57,4 +69,17 @@ public class Pattern { } return ret; } + + public static Set emptySpaceGenerator(Item... model) { + Set ret = new HashSet<>(); + for (int i = 0; i < model.length; i++) { + Pattern newP = new Pattern(model); + Item x = model[i]; + Item newI = new Item(x.getPosX(), x.getPosY(), 0); + newP.replaceItem(x, newI); + newP.addItem(new Item(x.getPosX(), x.getPosY() - 1, new int[]{1, 2})); + ret.add(newP); + } + return ret; + } } From e126b18cde8040cb5c5bca0246f6c12afd26300c Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Thu, 19 Oct 2017 13:07:10 +0200 Subject: [PATCH 03/16] Changing Patterns, creating and testing them --- src/player/malte/MalteAI.java | 60 ++++++++++++----------------------- src/player/malte/Pattern.java | 12 ++++++- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index c83fa24..ef285fb 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -38,47 +38,29 @@ public class MalteAI implements Player{ } private Set getWinningOptions(Set options, int[][] board) { - Pattern[] pats = new Pattern[]{ - // Three in a column, empty above - new Pattern(new Item(0, 0, 0), - new Item(0, 1, id), - new Item(0, 2, id), - new Item(0, 3, id)), - // Three in a row, empty left - new Pattern(new Item(0, 0, 0), - new Item(1, 0, id), - new Item(2, 0, id), - new Item(3, 0, id), - new Item(0, 1, new int[]{1, 2})), - // Three in a row, empty right - new Pattern(new Item(0, 0, 0), - new Item(-1, 0, id), - new Item(-2, 0, id), - new Item(-3, 0, id), - new Item(0, 1, new int[]{1, 2})), - // Three in a diagonal line from lower left to upper right, empty right above - new Pattern(new Item(0, 0, 0), - new Item(-1, 1, id), - new Item(-2, 2, id), - new Item(-3, 3, id), - // TODO: Improve IDs here - new Item(0, 1, new int[]{1, 2})), - // Three in a diagonal line from upper left to lower right, empty left above - new Pattern(new Item(0, 0, 0), - new Item(1, 1, id), - new Item(2, 2, id), - new Item(3, 3, id), - // TODO: Improve IDs here - new Item(0, 1, new int[]{1, 2})), - }; - // Set patterns = - Set matches = Pattern.matches(pats, board); + // Four in a row with one hole + Set patterns = Pattern.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 + patterns.addAll(Pattern.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 + patterns.addAll(Pattern.emptySpaceGenerator(new Item(0, 0, id), + new Item(1, 1, id), + new Item(2, 2, id), + new Item(3, 3, id))); + patterns.add(new Pattern(new Item(0, 0, 0), + new Item(0, 1, id), + new Item(0, 2, id), + new Item(0, 3, id))); + Set matches = Pattern.matches(patterns, board); Set ret = new HashSet<>(); for (Position x: matches) { - System.out.println(x); - if (options.contains(x.getPosX())) { - ret.add(new Integer(x.getPosX())); - } + } return ret; diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index c47a1c4..afc1621 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -62,7 +62,17 @@ public class Pattern { this.parts.add(item); } - public static Set matches(Pattern[] pats, int[][] board) { + public Set getZeros() { + Set ret = new HashSet<>(); + for (Item i: parts) { + if (i.hasID(0)) { + ret.add(i); + } + } + return ret; + } + + public static Set matches(Set pats, int[][] board) { Set ret = new HashSet<>(); for (Pattern p: pats) { ret.addAll(p.matches(board)); From 5495b3e157a5e6ad056c5905d78e1658a23591c3 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Thu, 19 Oct 2017 13:44:03 +0200 Subject: [PATCH 04/16] Having problems with the Pattern, losing --- src/game/Game.java | 5 +++- src/player/malte/Item.java | 27 +++++++++++++++++++++ src/player/malte/MalteAI.java | 12 +++++++--- src/player/malte/Pattern.java | 45 ++++++++++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/game/Game.java b/src/game/Game.java index 6b84c57..dce9669 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -86,6 +86,7 @@ public class Game { for (int i = 0; i < runs; i++) { int result = new Game(p1, p2).start(false, true); // TODO: Improve IDs + System.out.println(result); statistic[result]++; } double[] percents = new double[3]; @@ -140,6 +141,7 @@ public class 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 ;) } @@ -166,7 +168,8 @@ public class Game { // Check his choice against the current board. if (choice < 0 || choice > GAME_COLUMNS || this.board[choice][0] != 0) { // 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!? if(output){ log(currentP.getP().getName() + " made an illegal move and lost!"); diff --git a/src/player/malte/Item.java b/src/player/malte/Item.java index a27847e..b203025 100644 --- a/src/player/malte/Item.java +++ b/src/player/malte/Item.java @@ -36,4 +36,31 @@ public class Item { } return false; } + + public void changeID(int oldID, int newID) { + for (int id: ids) { + if (id == newID) { + return; + } + } + 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 + "])"; + } + + public Item copy() { + return new Item(posX, posY, ids); + } } diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index ef285fb..77476ad 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -31,6 +31,7 @@ public class MalteAI implements Player{ } Set winningOptions = getWinningOptions(options, board); for (Integer i: winningOptions) { + System.out.println(i); return i.intValue(); } @@ -57,10 +58,15 @@ public class MalteAI implements Player{ new Item(0, 1, id), new Item(0, 2, id), new Item(0, 3, id))); - Set matches = Pattern.matches(patterns, board); + // for (Pattern p: patterns) { + // System.out.println(p); + // } + Set matches = Pattern.matchingPatterns(patterns, board); Set ret = new HashSet<>(); - for (Position x: matches) { - + for (Pattern p: matches) { + for (Item i: p.getZeros()) { + ret.add(new Integer(i.getPosX())); + } } return ret; diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index afc1621..ffab4b2 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -14,6 +14,10 @@ public class Pattern { this.parts = new HashSet(Arrays.asList(parts)); } + public Pattern(Set parts) { + this.parts = parts; + } + public Set matches(int[][] board) { // Preparing iteration int maxLeft = 0, @@ -72,6 +76,14 @@ public class Pattern { return ret; } + public Pattern copy() { + Set itemCopy = new HashSet<>(); + for (Item i: parts) { + itemCopy.add(i.copy()); + } + return new Pattern(itemCopy); + } + public static Set matches(Set pats, int[][] board) { Set ret = new HashSet<>(); for (Pattern p: pats) { @@ -80,6 +92,16 @@ public class Pattern { return ret; } + public static Set matchingPatterns(Set pats, int[][] board) { + Set ret = new HashSet<>(); + for (Pattern p: pats) { + if (p.matches(board).size() > 0) { + ret.add(p); + } + } + return ret; + } + public static Set emptySpaceGenerator(Item... model) { Set ret = new HashSet<>(); for (int i = 0; i < model.length; i++) { @@ -87,9 +109,30 @@ public class Pattern { Item x = model[i]; Item newI = new Item(x.getPosX(), x.getPosY(), 0); newP.replaceItem(x, newI); - newP.addItem(new Item(x.getPosX(), x.getPosY() - 1, new int[]{1, 2})); + newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{1, 2})); ret.add(newP); } return ret; } + + public static Set changeID(Set pats, int oldID, int newID) { + Set ret = new HashSet<>(); + for (Pattern p: pats) { + Pattern cpy = p.copy(); + for (Item i: p.parts) { + i.changeID(oldID, newID); + } + ret.add(cpy); + } + return ret; + } + + @Override + public String toString() { + String s = ""; + for (Item i: parts) { + s += i + ", "; + } + return s + "\n"; + } } From 6232484d63d464bca332d41d969a59d0efef3bc6 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Thu, 19 Oct 2017 13:52:08 +0200 Subject: [PATCH 05/16] Strip output, fixing all AI problemes --- src/game/Game.java | 1 - src/player/malte/MalteAI.java | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/game/Game.java b/src/game/Game.java index dce9669..f9c9e32 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -86,7 +86,6 @@ public class Game { for (int i = 0; i < runs; i++) { int result = new Game(p1, p2).start(false, true); // TODO: Improve IDs - System.out.println(result); statistic[result]++; } double[] percents = new double[3]; diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 77476ad..95207f8 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -31,7 +31,6 @@ public class MalteAI implements Player{ } Set winningOptions = getWinningOptions(options, board); for (Integer i: winningOptions) { - System.out.println(i); return i.intValue(); } @@ -58,14 +57,14 @@ public class MalteAI implements Player{ new Item(0, 1, id), new Item(0, 2, id), new Item(0, 3, id))); - // for (Pattern p: patterns) { - // System.out.println(p); - // } Set matches = Pattern.matchingPatterns(patterns, board); Set ret = new HashSet<>(); for (Pattern p: matches) { + Set positions = p.matches(board); for (Item i: p.getZeros()) { - ret.add(new Integer(i.getPosX())); + for(Position pos: positions) { + ret.add(new Integer(i.getPosX() + pos.getPosX())); + } } } From 33c16320a9b18feb72c7958e55fed06c07ff8aab Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Thu, 19 Oct 2017 14:26:12 +0200 Subject: [PATCH 06/16] Worked on Patterns for the ai --- Makefile | 1 + src/player/malte/MalteAI.java | 62 +++++++++++++++---------- src/player/malte/Pattern.java | 13 ------ src/player/malte/PatternGenerator.java | 63 ++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 src/player/malte/PatternGenerator.java diff --git a/Makefile b/Makefile index e134f54..48429a8 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ src/player/Player.java \ src/player/malte/MalteAI.java \ src/player/malte/Pattern.java \ src/player/malte/Item.java \ +src/player/malte/PatternGenerator.java \ src/player/maurizio/MaurizioAI.java OBJECTS=$($(subst $(CLASSPATH),$(BUILDS),$(CLASSES)):.java=.class) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 95207f8..b719ae1 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -12,6 +12,7 @@ public class MalteAI implements Player{ private String name; private Random ran; private int id; + private int enemyID; public MalteAI(String name){ this.name = name; @@ -20,6 +21,7 @@ public class MalteAI implements Player{ public void setPlayerID(int id) { this.id = id; + this.enemyID = id == 1 ? 2: 1; } public int move(int[][] board){ @@ -29,35 +31,50 @@ public class MalteAI implements Player{ options.remove(i); } } - Set winningOptions = getWinningOptions(options, board); + Set winningOptions = getRowCompletionOptions(options, board, id); for (Integer i: winningOptions) { + System.out.println("WINNING"); return i.intValue(); } + Set preventionsOptions = getRowCompletionOptions(options, board, enemyID); + for (Integer i: preventionsOptions) { + int[][] fakeBoard = makeMove(copyBoard(board), i, id); + if (getRowCompletionOptions(options, fakeBoard, enemyID).size() == 0) { + System.out.println("PREVENTING"); + return i.intValue(); + } + } + + System.out.println("RANDOM"); return takeRandom(options).intValue(); } - private Set getWinningOptions(Set options, int[][] board) { - // Four in a row with one hole - Set patterns = Pattern.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 - patterns.addAll(Pattern.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 - patterns.addAll(Pattern.emptySpaceGenerator(new Item(0, 0, id), - new Item(1, 1, id), - new Item(2, 2, id), - new Item(3, 3, id))); - patterns.add(new Pattern(new Item(0, 0, 0), - new Item(0, 1, id), - new Item(0, 2, id), - new Item(0, 3, id))); - Set matches = Pattern.matchingPatterns(patterns, 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; + } + + private int[][] makeMove(int[][] board, int choice, int id) { + int column = 0; + if (board[choice][column] != 0) { + return board; + } + while (board[choice][column + 1] == 0) { + column++; + } + board[choice][column] = id; + return board; + } + + private Set getRowCompletionOptions(Set options, int[][] board, int id) { + Set pats = PatternGenerator.winningPatterns(id); + Set matches = Pattern.matchingPatterns(pats, board); Set ret = new HashSet<>(); for (Pattern p: matches) { Set positions = p.matches(board); @@ -67,7 +84,6 @@ public class MalteAI implements Player{ } } } - return ret; } diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index ffab4b2..a44108c 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -102,19 +102,6 @@ public class Pattern { return ret; } - public static Set emptySpaceGenerator(Item... model) { - Set ret = new HashSet<>(); - for (int i = 0; i < model.length; i++) { - Pattern newP = new Pattern(model); - Item x = model[i]; - Item newI = new Item(x.getPosX(), x.getPosY(), 0); - newP.replaceItem(x, newI); - newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{1, 2})); - ret.add(newP); - } - return ret; - } - public static Set changeID(Set pats, int oldID, int newID) { Set ret = new HashSet<>(); for (Pattern p: pats) { diff --git a/src/player/malte/PatternGenerator.java b/src/player/malte/PatternGenerator.java new file mode 100644 index 0000000..6f7b5b8 --- /dev/null +++ b/src/player/malte/PatternGenerator.java @@ -0,0 +1,63 @@ +package player.malte; + +import java.util.Set; +import java.util.HashSet; + +public class PatternGenerator { + + private PatternGenerator() {} + + public static Set winningPatterns(int id) { + // Four in a row with one hole + Set 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))); + 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 emptySpaceGenerator(Item... model) { + Set ret = new HashSet<>(); + for (int i = 0; i < model.length; i++) { + Pattern newP = new Pattern(model); + Item x = model[i]; + Item newI = new Item(x.getPosX(), x.getPosY(), 0); + newP.replaceItem(x, newI); + newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{1, 2})); + ret.add(newP); + } + return ret; + } + + public static Set empty2SpaceGenerator(Item... model) { + Set ret = new HashSet<>(); + for (int i = 0; i < model.length; i++) { + Pattern newP = new Pattern(model); + Item x = model[i]; + Item newI = new Item(x.getPosX(), x.getPosY(), 0); + newP.replaceItem(x, newI); + newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{1, 2})); + ret.add(newP); + } + return ret; + } + + public static Set twoOfFourRowPatterns(int id) { + // TODO: THIS + return null; + } +} From bee8baec1d4431c63052d0bb755e17f52a42f681 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 09:12:16 +0200 Subject: [PATCH 07/16] AI improvements --- src/player/malte/MalteAI.java | 3 --- src/player/malte/Pattern.java | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index b719ae1..6a18f74 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -33,7 +33,6 @@ public class MalteAI implements Player{ } Set winningOptions = getRowCompletionOptions(options, board, id); for (Integer i: winningOptions) { - System.out.println("WINNING"); return i.intValue(); } @@ -41,12 +40,10 @@ public class MalteAI implements Player{ for (Integer i: preventionsOptions) { int[][] fakeBoard = makeMove(copyBoard(board), i, id); if (getRowCompletionOptions(options, fakeBoard, enemyID).size() == 0) { - System.out.println("PREVENTING"); return i.intValue(); } } - System.out.println("RANDOM"); return takeRandom(options).intValue(); } diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index a44108c..ca61f66 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -6,6 +6,9 @@ import java.util.Arrays; import game.Game; +/** + * A Connect Four Pattern. + */ public class Pattern { private Set parts; From 00f813ba0f0e5e05814fff8d6c95aae7daf94377 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 13:24:11 +0200 Subject: [PATCH 08/16] Did commenting --- src/player/malte/MalteAI.java | 82 ++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 6a18f74..e902236 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -7,46 +7,81 @@ import java.util.Arrays; import player.Player; +/** + * Maltes artificial intelligence for playing a game of Connect Four + */ public class MalteAI implements Player{ + /** + * Name of the player. + */ private String name; + + /** + * Random Object. + */ private Random ran; + + /** + * The players ID. + */ private int id; + + /** + * The enemy's ID. + */ private int enemyID; + /** + * Constructor. + * + * @param name The name for the player. + */ public MalteAI(String name){ this.name = name; this.ran = new Random(); } + @Override public void setPlayerID(int id) { this.id = id; this.enemyID = id == 1 ? 2: 1; } + @Override public int move(int[][] board){ + // Create a set of all possible options. Set 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); } } + // Get options which would lead to instant win. Set winningOptions = getRowCompletionOptions(options, board, id); for (Integer i: winningOptions) { return i.intValue(); } - + // Get options which would prevent an instant win of the enemy. Set preventionsOptions = getRowCompletionOptions(options, board, enemyID); for (Integer i: preventionsOptions) { + // Only accept options, that would not cause a winning opportunity for the enemy. 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(); } + /** + * 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++) { @@ -57,25 +92,56 @@ public class MalteAI implements Player{ 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 column = 0; + // If the column is full, do nothing. if (board[choice][column] != 0) { return board; } + // Find the last empty row. while (board[choice][column + 1] == 0) { column++; } + // Add the players piece. board[choice][column] = 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 getRowCompletionOptions(Set options, int[][] board, int id) { + // Get winning patterns from Generator. Set pats = PatternGenerator.winningPatterns(id); + // Get patterns, that match anywhere on the board. Set matches = Pattern.matchingPatterns(pats, board); + // Create set to be returned. Set ret = new HashSet<>(); + // Iterate over all matches. for (Pattern p: matches) { + // Get all positions, this pattern matches. Set 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())); } @@ -84,10 +150,19 @@ public class MalteAI implements Player{ return ret; } + /** + * Copies a set of Integer. + */ private Set copySet(Set s) { return new HashSet(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 s) { int item = ran.nextInt(s.size()); int i = 0; @@ -101,6 +176,9 @@ public class MalteAI implements Player{ return 0; } + /** + * Get the player's name. + */ public String getName(){ return this.name; } From ee60b2898674e58ea9cf4cae380332a728f178ac Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 13:38:42 +0200 Subject: [PATCH 09/16] Did commenting --- src/player/malte/Pattern.java | 75 ++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index ca61f66..cb28298 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -8,19 +8,43 @@ 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 parts; + /** + * Constructor. + * + * @param parts The parts of the pattern. + */ public Pattern(Item... parts) { this.parts = new HashSet(Arrays.asList(parts)); } + /** + * Constructor. + * + * @param parts The parts of the pattern. + */ public Pattern(Set 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 matches(int[][] board) { // Preparing iteration int maxLeft = 0, @@ -58,6 +82,12 @@ public class Pattern { return set; } + /** + * 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); @@ -65,10 +95,21 @@ public class Pattern { } } - public void addItem(Item item) { - this.parts.add(item); + /** + * 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 getZeros() { Set ret = new HashSet<>(); for (Item i: parts) { @@ -79,6 +120,11 @@ public class Pattern { return ret; } + /** + * Returns a deep copy of this pattern. + * + * @return This pattern... only copied. + */ public Pattern copy() { Set itemCopy = new HashSet<>(); for (Item i: parts) { @@ -87,6 +133,13 @@ public class Pattern { 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 matches(Set pats, int[][] board) { Set ret = new HashSet<>(); for (Pattern p: pats) { @@ -95,6 +148,12 @@ public class Pattern { 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 matchingPatterns(Set pats, int[][] board) { Set ret = new HashSet<>(); for (Pattern p: pats) { @@ -105,18 +164,6 @@ public class Pattern { return ret; } - public static Set changeID(Set pats, int oldID, int newID) { - Set ret = new HashSet<>(); - for (Pattern p: pats) { - Pattern cpy = p.copy(); - for (Item i: p.parts) { - i.changeID(oldID, newID); - } - ret.add(cpy); - } - return ret; - } - @Override public String toString() { String s = ""; From b0a1999b940689cc9a64d286fe975773c1ec3197 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 13:51:52 +0200 Subject: [PATCH 10/16] Did commenting --- src/player/malte/PatternGenerator.java | 39 +++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/player/malte/PatternGenerator.java b/src/player/malte/PatternGenerator.java index 6f7b5b8..db1d467 100644 --- a/src/player/malte/PatternGenerator.java +++ b/src/player/malte/PatternGenerator.java @@ -3,10 +3,27 @@ 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 winningPatterns(int id) { // Four in a row with one hole Set pats = PatternGenerator.emptySpaceGenerator(new Item(0, 0, id), @@ -23,6 +40,7 @@ public class PatternGenerator { 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), @@ -30,19 +48,36 @@ public class PatternGenerator { 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 emptySpaceGenerator(Item... model) { Set 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, 2})); + // Add the pattern to the set. ret.add(newP); } return ret; } + /** + * TODO: THIS + */ public static Set empty2SpaceGenerator(Item... model) { Set ret = new HashSet<>(); for (int i = 0; i < model.length; i++) { @@ -56,8 +91,10 @@ public class PatternGenerator { return ret; } + /** + * TODO: THIS + */ public static Set twoOfFourRowPatterns(int id) { - // TODO: THIS return null; } } From 54b015ec77652933bcd8adb24536829237dd2bfa Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 13:57:50 +0200 Subject: [PATCH 11/16] Did commenting --- src/player/malte/Item.java | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/player/malte/Item.java b/src/player/malte/Item.java index b203025..e28e1cb 100644 --- a/src/player/malte/Item.java +++ b/src/player/malte/Item.java @@ -1,15 +1,43 @@ 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; + + /** + * 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}); } + /** + * 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; @@ -37,12 +65,22 @@ public class Item { 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; @@ -60,6 +98,11 @@ public class Item { 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); } From 21ec72f4991c3fb842255c94336a34a0e77e6f77 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 17:11:08 +0200 Subject: [PATCH 12/16] Did bug work-around --- src/player/malte/MalteAI.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 6bba1e7..492fb8a 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -68,6 +68,7 @@ public class MalteAI implements Player{ return i.intValue(); } // Get options which would prevent an instant win of the enemy. + enemyID = enemyID == 0 ? 1: enemyID; Set preventionsOptions = getRowCompletionOptions(options, board, enemyID); for (Integer i: preventionsOptions) { // Only accept options, that would not cause a winning opportunity for the enemy. From 7f83c6f01f6b389ee0f168da0dc78db25e190f1b Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 20:56:05 +0200 Subject: [PATCH 13/16] Commiting wrongly on this branch to cherry-pick --- src/player/malte/Pattern.java | 24 +++++++++++++++++++++++- src/player/malte/PatternGenerator.java | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/player/malte/Pattern.java b/src/player/malte/Pattern.java index cb28298..af3a129 100644 --- a/src/player/malte/Pattern.java +++ b/src/player/malte/Pattern.java @@ -52,6 +52,9 @@ public class Pattern { maxUp = 0, maxDown = 0; for (Item i: parts) { + if (i.hasID(-1)) { + continue; + } if (maxLeft < -i.getPosX()) { maxLeft = -i.getPosX(); } @@ -72,7 +75,10 @@ public class Pattern { for (Item k: parts) { int posX = i + k.getPosX(); int posY = j + k.getPosY(); - if (!k.hasID(board[posX][posY])) { + if ((!isOnBoard(new Position(posX, posY)) && + !k.hasID(-1)) || + (isOnBoard(new Position(posX, posY)) && + !k.hasID(board[posX][posY]))) { continue inner; } } @@ -82,6 +88,22 @@ public class Pattern { 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. * diff --git a/src/player/malte/PatternGenerator.java b/src/player/malte/PatternGenerator.java index db1d467..33364e1 100644 --- a/src/player/malte/PatternGenerator.java +++ b/src/player/malte/PatternGenerator.java @@ -68,7 +68,7 @@ public class PatternGenerator { // 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, 2})); + newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{-1, 1, 2})); // Add the pattern to the set. ret.add(newP); } From dbc2d5b2d8af1d8fa807d476cbae92ad69deb270 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 17:38:32 +0200 Subject: [PATCH 14/16] AI improvements --- src/player/malte/MalteAI.java | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 492fb8a..9f4e2f0 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.Arrays; import player.Player; +import game.Game; /** * Maltes artificial intelligence for playing a game of Connect Four @@ -64,14 +65,21 @@ public class MalteAI implements Player{ } // Get options which would lead to instant win. Set winningOptions = getRowCompletionOptions(options, board, id); + System.out.println("WINNING: " + winningOptions); for (Integer i: winningOptions) { return i.intValue(); } // Get options which would prevent an instant win of the enemy. enemyID = enemyID == 0 ? 1: enemyID; Set preventionsOptions = getRowCompletionOptions(options, board, enemyID); + System.out.println("PREVENT: " + preventionsOptions); for (Integer i: preventionsOptions) { - // Only accept options, that would not cause a winning opportunity for the enemy. + return i.intValue(); + } + Set 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(); @@ -109,17 +117,17 @@ public class MalteAI implements Player{ * @return The modified board. */ private int[][] makeMove(int[][] board, int choice, int id) { - int column = 0; + int row = 0; // If the column is full, do nothing. - if (board[choice][column] != 0) { + if (board[choice][row] != 0) { return board; } // Find the last empty row. - while (board[choice][column + 1] == 0) { - column++; + while (row < Game.GAME_ROWS - 1 && board[choice][row + 1] == 0) { + row++; } // Add the players piece. - board[choice][column] = id; + board[choice][row] = id; return board; } From bc9b43fcc06abd32490e8d56731b35e38b507513 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 21:08:14 +0200 Subject: [PATCH 15/16] Removed unneccesary output --- src/player/malte/MalteAI.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 9f4e2f0..53ac38e 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -65,14 +65,12 @@ public class MalteAI implements Player{ } // Get options which would lead to instant win. Set winningOptions = getRowCompletionOptions(options, board, id); - System.out.println("WINNING: " + winningOptions); for (Integer i: winningOptions) { return i.intValue(); } // Get options which would prevent an instant win of the enemy. enemyID = enemyID == 0 ? 1: enemyID; Set preventionsOptions = getRowCompletionOptions(options, board, enemyID); - System.out.println("PREVENT: " + preventionsOptions); for (Integer i: preventionsOptions) { return i.intValue(); } From e77cd680e320ab709fd86a3771cb079d899adf22 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Fri, 20 Oct 2017 22:05:35 +0200 Subject: [PATCH 16/16] Implemented twoOfFourPatterns, ai development --- src/player/malte/MalteAI.java | 27 +++++++++++ src/player/malte/PatternGenerator.java | 64 ++++++++++++++++++++------ 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/player/malte/MalteAI.java b/src/player/malte/MalteAI.java index 53ac38e..6b7b540 100644 --- a/src/player/malte/MalteAI.java +++ b/src/player/malte/MalteAI.java @@ -74,6 +74,12 @@ public class MalteAI implements Player{ for (Integer i: preventionsOptions) { return i.intValue(); } + // Choose a move that will continue a sequence that already exists. + Set 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 choosewisely = new HashSet<>(options); while (choosewisely.size() > 0){ Integer i = takeRandom(choosewisely); @@ -87,6 +93,27 @@ public class MalteAI implements Player{ return takeRandom(options).intValue(); } + private Set getTwoOfFourOptions(Set options, int[][] board, int id) { + Set twoOfFourPattern = PatternGenerator.winInTwoPatterns(id); + // Get patterns, that match anywhere on the board. + Set matches = Pattern.matchingPatterns(twoOfFourPattern, board); + // Create set to be returned. + Set ret = new HashSet<>(); + // Iterate over all matches. + for (Pattern p: matches) { + // Get all positions, this pattern matches. + Set 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 the given board. * diff --git a/src/player/malte/PatternGenerator.java b/src/player/malte/PatternGenerator.java index 33364e1..5b34f2b 100644 --- a/src/player/malte/PatternGenerator.java +++ b/src/player/malte/PatternGenerator.java @@ -48,6 +48,29 @@ public class PatternGenerator { return pats; } + public static Set winInTwoPatterns(int id) { + // Four in a row with two holes + Set 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. @@ -76,25 +99,40 @@ public class PatternGenerator { } /** - * TODO: THIS + * 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 empty2SpaceGenerator(Item... model) { Set ret = new HashSet<>(); + // Iterate over all models. for (int i = 0; i < model.length; i++) { - Pattern newP = new Pattern(model); - Item x = model[i]; - Item newI = new Item(x.getPosX(), x.getPosY(), 0); - newP.replaceItem(x, newI); - newP.addItem(new Item(x.getPosX(), x.getPosY() + 1, new int[]{1, 2})); - ret.add(newP); + 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; } - /** - * TODO: THIS - */ - public static Set twoOfFourRowPatterns(int id) { - return null; - } }