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.control.ITurnTimer; import jrummikub.control.TurnTimer; import jrummikub.model.Position; import jrummikub.model.Stone; import jrummikub.model.StoneColor; import jrummikub.model.StoneSet; import jrummikub.util.Connection; import jrummikub.util.IListener; import jrummikub.util.Pair; /** * Base class for AI players * */ public class BaseAIControl extends AbstractTurnControl { private long startTime; private ITurnTimer turnTimer; private List connections = new ArrayList(); private Thread computeThread; private volatile boolean stopRunning = false; @Override public void startTurn() { turnTimer = new TurnTimer(view); connections.add(turnTimer.getTimeRunOutEvent().add(new IListener() { @Override public void handle() { cleanUp(); endOfTurnEvent.emit(); } })); computeThread = new Thread(new Runnable() { @Override public void run() { compute(); } }); startTime = System.currentTimeMillis(); turnTimer.startTimer(); computeThread.start(); } private void cleanUp() { turnTimer.stopTimer(); for (Connection c : connections) { c.remove(); } stopRunning = true; } private void compute() { switch (turnMode) { case MAY_REDEAL: emitRedeal(); break; case INSPECT_ONLY: emitEndOfTurn(); break; case NORMAL_TURN: turn(); break; } } private Stone findMatchingStone(Stone target) { for (Pair entry : player.getHand()) { Stone stone = entry.getFirst(); if (stone.getValue() == target.getValue() && stone.getColor() == target.getColor()) { return stone; } } for (Pair entry : player.getHand()) { Stone stone = entry.getFirst(); if (stone.isJoker()) { return stone; } } return null; } private Stone pickUpMatchingStone(Stone target) { Stone match = findMatchingStone(target); player.getHand().pickUp(match); return match; } private void turn() { List stones = new ArrayList(); for (Pair entry : player.getHand()) { stones.add(entry.getFirst()); } Pair, Integer>, Integer> counts = AIUtil .countStones(stones); AIUtil aiUtil = new AIUtil(settings); Pair, Integer> result = aiUtil.findSetsWithTotalPoints( Math.max(30, settings.getInitialMeldThreshold() * 2), counts.getFirst(), counts.getSecond()); if (!player.getLaidOut() && result.getSecond() < settings.getInitialMeldThreshold()) { emitEndOfTurn(); return; } for (StoneSet set : result.getFirst()) { List handStones = new ArrayList(); for (Stone stone : set) { handStones.add(pickUpMatchingStone(stone)); } table.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() * player.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(); } }; } }