diff options
Diffstat (limited to 'src/jrummikub')
-rw-r--r-- | src/jrummikub/ai/TurnLogic.java | 20 | ||||
-rw-r--r-- | src/jrummikub/control/turn/AIControl.java | 197 | ||||
-rw-r--r-- | src/jrummikub/control/turn/BaseAIControl.java | 174 | ||||
-rw-r--r-- | src/jrummikub/control/turn/TurnControlFactory.java | 2 |
4 files changed, 213 insertions, 180 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; } |