package net.sf.colossus.ai; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.colossus.ai.helper.LegionMove; import net.sf.colossus.client.Client; import net.sf.colossus.common.Constants; /** * DON'T USE THAT ONE YET. * This one implements a parallel findBestLegionMove. Unfortunately, SimpleAI * implementation of evaluateLegionBattleMove is anything but thread-safe, and * it goes very deep: * Evaluations functions mostly fall-back to asking the Client object property * of the current situation. So they have first to move all BattleCritter * (which are only a view of full BattleUnit) to the evaluated position, then * evaluate. * This version only tests the parallelism, so evaluateLegionBattleMove is * random. * We would need to * (1) implements Strike as BattleClientSide, a proper, per-battle extension of * Battle (the way it is done on with BattleServerSide) * (2) make sure all evaluate functions only use property of BattleClientSide, * even if that means changing the state of BattleClientSide. * (3) have a proper deep copy constructor in BattleClientSide, so we can work * on several variant at once. * @author Romain Dolbeau */ public class ParallelEvaluatorAI extends ExperimentalAI // NO_UCD { private static final Logger LOGGER = Logger .getLogger(ParallelEvaluatorAI.class.getName()); public ParallelEvaluatorAI(Client client) { super(client); } @Override protected int evaluateLegionBattleMove(LegionMove lm) { return random.nextInt(1000); } private class findBestLegionMoveThread extends Thread { private boolean timeIsUp = false; LegionMove best = null; Timer threadedSetupTimer() { // java.util.Timer, not Swing Timer Timer timer = new Timer(); this.timeIsUp = false; final int MS_PER_S = 1000; if (timeLimit < Constants.MIN_AI_TIME_LIMIT || timeLimit > Constants.MAX_AI_TIME_LIMIT) { timeLimit = Constants.DEFAULT_AI_TIME_LIMIT; } timer .schedule(new ThreadedTriggerTimeIsUp(), MS_PER_S * timeLimit); return timer; } protected class ThreadedTriggerTimeIsUp extends TimerTask { @Override public void run() { timeIsUp = true; } } private final Iterator<LegionMove> iterator; findBestLegionMoveThread(Iterator<LegionMove> it) { iterator = it; } @Override public void run() { LOGGER.finest("Running Thread number XXX"); int bestScore = Integer.MIN_VALUE; int count = 0; Timer findBestLegionMoveTimer = threadedSetupTimer(); boolean done = false; while (!done) { LegionMove lm = null; synchronized (iterator) { if (iterator.hasNext()) { lm = iterator.next(); } else { done = true; } } if (lm != null) { // this can't possibly work, as evaluateLegionBattleMove is // about as non-thread-safe as it is possible to be. int score = evaluateLegionBattleMove(lm); if (score > bestScore) { bestScore = score; best = lm; LOGGER.finest("INTERMEDIATE Best legion move: " + lm.getStringWithEvaluation() + " (" + score + ")"); } else { LOGGER.finest("INTERMEDIATE legion move: " + lm.getStringWithEvaluation() + " (" + score + ")"); } count++; if (timeIsUp) { if (count >= MIN_ITERATIONS) { LOGGER .finest("findBestLegionMove() time up after " + count + " iterations"); break; } else { LOGGER .finest("findBestLegionMove() time up after " + count + " iterations, but we keep searching until " + MIN_ITERATIONS); } } } } findBestLegionMoveTimer.cancel(); LOGGER.finer("Best legion move of " + count + " checked (turn " + client.getBattleTurnNumber() + "): " + ((best == null) ? "none " : best.getStringWithEvaluation()) + " (" + bestScore + ")"); } } private final static int NTHREADS = 2; @Override protected LegionMove findBestLegionMove(Collection<LegionMove> legionMoves) { LegionMove best = null; if (legionMoves instanceof List) Collections.shuffle((List<LegionMove>)legionMoves, random); LegionMove[] bests = new LegionMove[NTHREADS]; findBestLegionMoveThread[] threads = new findBestLegionMoveThread[NTHREADS]; Iterator<LegionMove> iterator = legionMoves.iterator(); for (int i = 0; i < NTHREADS; i++) { threads[i] = new findBestLegionMoveThread(iterator); LOGGER.finest("Starting Thread number " + i); threads[i].start(); } for (int i = 0; i < NTHREADS; i++) { try { threads[i].join(); bests[i] = threads[i].best; } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, null, ex); } } best = bests[0]; for (int i = 1; i < NTHREADS; i++) { if (best == null) { best = bests[i]; } else { if (bests[i] != null) { if (best.getValue() < bests[i].getValue()) { best = bests[i]; } } } } LOGGER.finer("// Best legion move (turn " + client.getBattleTurnNumber() + "): " + (best == null ? "none" : best.getStringWithEvaluation()) + " (" + (best == null ? "-" : ("" + best.getValue())) + ")"); return best; } }