summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannis Harder <harder@informatik.uni-luebeck.de>2011-06-17 22:12:41 +0200
committerJannis Harder <harder@informatik.uni-luebeck.de>2011-06-17 22:12:41 +0200
commit47bf19036e049787fa7742f5ff72a08a0c9e887c (patch)
tree0858aef8cc91af311a1c8505d69411d116da83ba
parenta07e723242da4fbdd00cee2d86a46f4db70bc87a (diff)
downloadJRummikub-47bf19036e049787fa7742f5ff72a08a0c9e887c.tar
JRummikub-47bf19036e049787fa7742f5ff72a08a0c9e887c.zip
Use new AI for computer players
git-svn-id: svn://sunsvr01.isp.uni-luebeck.de/swproj13/trunk@448 72836036-5685-4462-b002-a69064685172
-rw-r--r--src/jrummikub/ai/TurnLogic.java20
-rw-r--r--src/jrummikub/control/turn/AIControl.java197
-rw-r--r--src/jrummikub/control/turn/BaseAIControl.java174
-rw-r--r--src/jrummikub/control/turn/TurnControlFactory.java2
-rw-r--r--test/jrummikub/control/turn/AIControlTest.java (renamed from test/jrummikub/control/turn/BaseAIControlTest.java)19
5 files changed, 220 insertions, 192 deletions
diff --git a/src/jrummikub/ai/TurnLogic.java b/src/jrummikub/ai/TurnLogic.java
index 01a3d59..b9e2a2a 100644
--- a/src/jrummikub/ai/TurnLogic.java
+++ b/src/jrummikub/ai/TurnLogic.java
@@ -27,9 +27,10 @@ public class TurnLogic {
private State top;
private volatile boolean abort = false;
+ private volatile boolean autoAbort = false;
private int neededPoints = 0;
- private double neededScore = Double.NEGATIVE_INFINITY;
+ private double neededScore = 0;
@SuppressWarnings("serial")
private static class Contradiction extends Throwable {
@@ -548,6 +549,10 @@ public class TurnLogic {
public List<StoneSet> getResult() {
State result = bestState;
+ if (result == null) {
+ return null;
+ }
+
ArrayList<StoneSet> outputSets = new ArrayList<StoneSet>();
for (int i = 0; i < stoneCount; i++) {
@@ -560,9 +565,6 @@ public class TurnLogic {
&& isNullSet(stone.rightGroup)) {
break;
}
- /* System.out.println("--");
- System.out.println(stone.rightRun);
- System.out.println(stone.rightGroup); */
Integer rightRunID = stone.rightRun.iterator().next();
Integer rightGroupID = stone.rightGroup.iterator().next();
@@ -600,7 +602,7 @@ public class TurnLogic {
}
public double optimize() {
- while (solve()) {
+ while (!autoAbort && solve()) {
neededScore = top.getScore();
}
return neededScore;
@@ -688,4 +690,12 @@ public class TurnLogic {
// This should never happen
throw new Error("Internal AI error");
}
+
+ public void autoAbort() {
+ if (bestState != null) {
+ abort = true;
+ }
+ autoAbort = true;
+
+ }
}
diff --git a/src/jrummikub/control/turn/AIControl.java b/src/jrummikub/control/turn/AIControl.java
new file mode 100644
index 0000000..8ab0aa5
--- /dev/null
+++ b/src/jrummikub/control/turn/AIControl.java
@@ -0,0 +1,197 @@
+package jrummikub.control.turn;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+import jrummikub.ai.TurnLogic;
+import jrummikub.control.AIUtil;
+import jrummikub.model.Position;
+import jrummikub.model.Stone;
+import jrummikub.model.StoneColor;
+import jrummikub.model.StoneSet;
+import jrummikub.util.Pair;
+
+/**
+ * Base class for AI players
+ *
+ */
+public class AIControl extends AbstractTurnControl {
+ private TurnLogic logic;
+ private boolean turnDone = false;
+ boolean useBackgroundThread = true;
+ long startTime;
+
+ @Override
+ public void startTurn() {
+ timer.startTimer();
+ startTime = System.currentTimeMillis();
+ compute();
+ }
+
+ protected void timeOut() {
+ executeTurn();
+ }
+
+ @Override
+ protected void cleanUp() {
+ if (logic != null)
+ logic.abort();
+ turnDone = true;
+ super.cleanUp();
+ }
+
+ private void emitEndOfTurn() {
+ turnDone = true;
+ long turnLength = System.currentTimeMillis() - startTime;
+
+ if (useBackgroundThread) {
+ Timer timer = new Timer(Math.max(0, (int) (1000 + Math.random() * 2000 - turnLength)),
+ new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ cleanUp();
+ endOfTurnEvent.emit();
+ }
+ });
+ timer.setRepeats(false);
+ timer.start();
+ } else {
+ cleanUp();
+ endOfTurnEvent.emit();
+ }
+
+ }
+
+ private void compute() {
+ switch (turnInfo.getTurnMode()) {
+ case MAY_REDEAL:
+ emitEndOfTurn();
+ break;
+ case INSPECT_ONLY:
+ emitEndOfTurn();
+ break;
+ case NORMAL_TURN:
+ turn();
+ break;
+ }
+ }
+
+ private void turn() {
+ List<Stone> tableStones = new ArrayList<Stone>();
+ List<Stone> handStones = new ArrayList<Stone>();
+
+ for (Pair<Stone, Position> entry : turnInfo.getHand()) {
+ handStones.add(entry.getFirst());
+ }
+
+ if (turnInfo.getLaidOut()) {
+ for (Pair<StoneSet, Position> entry : turnInfo.getTable()) {
+ for (Stone stone : entry.getFirst()) {
+ tableStones.add(stone);
+ }
+ }
+ }
+
+ logic = new TurnLogic(settings, tableStones, handStones);
+
+ if (!turnInfo.getLaidOut()) {
+ logic.needIntialMeldThreshold();
+ }
+ if (useBackgroundThread) {
+
+ Timer timer = new Timer(10000, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ logic.autoAbort();
+ }
+ });
+ timer.setRepeats(false);
+ timer.start();
+
+ Thread computeThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ logic.optimize();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ executeTurn();
+ }
+ });
+ }
+ });
+ computeThread.start();
+ } else {
+ logic.optimize();
+ executeTurn();
+ }
+
+ }
+
+ private void executeTurn() {
+ if (turnDone) {
+ return;
+ }
+
+ List<StoneSet> result = logic.getResult();
+
+ if (result != null) {
+
+ if (turnInfo.getLaidOut()) {
+ outerLoop: for (Iterator<Pair<StoneSet, Position>> it = turnInfo
+ .getTable().iterator(); it.hasNext();) {
+ Pair<StoneSet, Position> pair = it.next();
+ setSearch: for (Iterator<StoneSet> it2 = result.iterator(); it2
+ .hasNext();) {
+ StoneSet set = it2.next();
+ if (set.getSize() != pair.getFirst().getSize()) {
+ continue;
+ }
+ for (int i = 0; i < set.getSize(); i++) {
+ if (set.get(i) != pair.getFirst().get(i)) {
+ continue setSearch;
+ }
+ }
+ it2.remove();
+ continue outerLoop;
+ }
+ it.remove();
+ }
+ }
+
+ for (StoneSet set : result) {
+ turnInfo.getTable().drop(
+ set,
+ new Position(10 * (Math.random() * 2 - 1), 5 * (Math
+ .random() * 2 - 1)));
+ for (Stone stone : set) {
+ turnInfo.getHand().pickUp(stone);
+ }
+ }
+ }
+
+ emitEndOfTurn();
+ }
+
+ /**
+ * Get the factory for the base AI control
+ *
+ * @return the factory
+ */
+ static public TurnControlFactory getFactory() {
+ return new TurnControlFactory() {
+ @Override
+ public ITurnControl create() {
+ return new AIControl();
+ }
+ };
+ }
+
+}
diff --git a/src/jrummikub/control/turn/BaseAIControl.java b/src/jrummikub/control/turn/BaseAIControl.java
deleted file mode 100644
index 11519c3..0000000
--- a/src/jrummikub/control/turn/BaseAIControl.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package jrummikub.control.turn;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.TreeMap;
-
-import javax.swing.SwingUtilities;
-
-import jrummikub.control.AIUtil;
-import jrummikub.model.Position;
-import jrummikub.model.Stone;
-import jrummikub.model.StoneColor;
-import jrummikub.model.StoneSet;
-import jrummikub.util.Pair;
-
-/**
- * Base class for AI players
- *
- */
-public class BaseAIControl extends AbstractTurnControl {
- private long startTime;
- private Thread computeThread;
-
- private volatile boolean stopRunning = false;
-
- @Override
- public void startTurn() {
- computeThread = new Thread(new Runnable() {
- @Override
- public void run() {
- compute();
- }
- });
- startTime = System.currentTimeMillis();
-
- timer.startTimer();
- computeThread.start();
- }
-
- protected void timeOut() {
- cleanUp();
- endOfTurnEvent.emit();
- }
-
- protected void cleanUp() {
- super.cleanUp();
- stopRunning = true;
- }
-
- private void compute() {
- switch (turnInfo.getTurnMode()) {
- case MAY_REDEAL:
- emitRedeal();
- break;
- case INSPECT_ONLY:
- emitEndOfTurn();
- break;
- case NORMAL_TURN:
- turn();
- break;
- }
- }
-
- private Stone findMatchingStone(Stone target) {
- for (Pair<Stone, Position> entry : turnInfo.getHand()) {
- Stone stone = entry.getFirst();
- if (stone.getValue() == target.getValue()
- && stone.getColor() == target.getColor()) {
- return stone;
- }
- }
- for (Pair<Stone, Position> entry : turnInfo.getHand()) {
- Stone stone = entry.getFirst();
- if (stone.isJoker()) {
- return stone;
- }
- }
- return null;
- }
-
- private Stone pickUpMatchingStone(Stone target) {
- Stone match = findMatchingStone(target);
- turnInfo.getHand().pickUp(match);
- return match;
- }
-
- private void turn() {
- List<Stone> stones = new ArrayList<Stone>();
-
- for (Pair<Stone, Position> entry : turnInfo.getHand()) {
- stones.add(entry.getFirst());
- }
-
- Pair<TreeMap<Pair<Integer, StoneColor>, Integer>, Integer> counts = AIUtil
- .countStones(stones);
-
- AIUtil aiUtil = new AIUtil(settings);
-
- Pair<List<StoneSet>, Integer> result = aiUtil.findSetsWithTotalPoints(
- Math.max(30, settings.getInitialMeldThreshold() * 2),
- counts.getFirst(), counts.getSecond());
-
- if (!turnInfo.getLaidOut()
- && result.getSecond() < settings.getInitialMeldThreshold()) {
- emitEndOfTurn();
- return;
- }
-
- for (StoneSet set : result.getFirst()) {
- List<Stone> handStones = new ArrayList<Stone>();
- for (Stone stone : set) {
- handStones.add(pickUpMatchingStone(stone));
- }
- turnInfo.getTable().drop(
- new StoneSet(handStones),
- new Position((float) Math.random() * 30 - 15,
- (float) Math.random() * 6 - 3));
- }
-
- emitEndOfTurn();
- }
-
- private void emitRedeal() {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (!stopRunning) {
- cleanUp();
- redealEvent.emit();
- }
- }
- });
- }
-
- private void emitEndOfTurn() {
- long timeElapsed = System.currentTimeMillis() - startTime;
- long timeNeeded = Math.min((long) (1000 + Math.random()
- * turnInfo.getHand().getSize() * 100), 50000);
- long waitTime = timeNeeded - timeElapsed;
-
- if (waitTime > 0) {
- try {
- Thread.sleep(waitTime);
- } catch (InterruptedException e) {
- // This shouldn't happen
- }
- }
-
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (!stopRunning) {
- cleanUp();
- endOfTurnEvent.emit();
- }
- }
- });
- }
-
- /**
- * Get the factory for the base AI control
- *
- * @return the factory
- */
- static public TurnControlFactory getFactory() {
- return new TurnControlFactory() {
- @Override
- public ITurnControl create() {
- return new BaseAIControl();
- }
- };
- }
-
-}
diff --git a/src/jrummikub/control/turn/TurnControlFactory.java b/src/jrummikub/control/turn/TurnControlFactory.java
index 4d143e7..7c5077d 100644
--- a/src/jrummikub/control/turn/TurnControlFactory.java
+++ b/src/jrummikub/control/turn/TurnControlFactory.java
@@ -27,7 +27,7 @@ public abstract class TurnControlFactory {
case HUMAN:
return HumanTurnControl.getFactory();
case COMPUTER:
- return BaseAIControl.getFactory();
+ return AIControl.getFactory();
}
return null;
}
diff --git a/test/jrummikub/control/turn/BaseAIControlTest.java b/test/jrummikub/control/turn/AIControlTest.java
index 51b1f1f..6f7bf6f 100644
--- a/test/jrummikub/control/turn/BaseAIControlTest.java
+++ b/test/jrummikub/control/turn/AIControlTest.java
@@ -22,7 +22,7 @@ import org.junit.Before;
import org.junit.Test;
/** */
-public class BaseAIControlTest {
+public class AIControlTest {
ITurnControl aiControl;
GameSettings gameSettings;
@@ -38,6 +38,7 @@ public class BaseAIControlTest {
@Before
public void setUp() {
aiControl = TurnControlFactory.getFactory(Type.COMPUTER).create();
+ ((AIControl)aiControl).useBackgroundThread = false;
gameSettings = new GameSettings();
playerSettings = new PlayerSettings("ROBOT_01", Color.GRAY);
player = new Player(playerSettings);
@@ -74,14 +75,12 @@ public class BaseAIControlTest {
* @throws InterruptedException
*/
@Test(timeout = 10000)
- public void testTurnZeroRedealing() throws InterruptedException {
+ public void testTurnZeroNoRedealing() throws InterruptedException {
aiControl.setup(new ITurnControl.TurnInfo(table, player.getHand(),
player.getLaidOut(), TurnMode.MAY_REDEAL), gameSettings, view);
aiControl.startTurn();
- while (!redealt) {
- Thread.sleep(100);
- }
- assertFalse(turnEnded);
+ assertTrue(turnEnded);
+ assertFalse(redealt);
assertEquals(table.getSize(), 0);
}
@@ -95,9 +94,7 @@ public class BaseAIControlTest {
player.getLaidOut(), TurnMode.INSPECT_ONLY),
gameSettings, view);
aiControl.startTurn();
- while (!turnEnded) {
- Thread.sleep(100);
- }
+ assertTrue(turnEnded);
assertFalse(redealt);
assertEquals(table.getSize(), 0);
}
@@ -110,9 +107,7 @@ public class BaseAIControlTest {
aiControl.setup(new ITurnControl.TurnInfo(table, player.getHand(),
player.getLaidOut(), TurnMode.NORMAL_TURN), gameSettings, view);
aiControl.startTurn();
- while (!turnEnded) {
- Thread.sleep(100);
- }
+ assertTrue(turnEnded);
assertFalse(redealt);
assertEquals(table.getSize(), 1);
assertEquals(player.getHand().getSize(), 0);