rename references

This commit is contained in:
2023-09-28 20:24:18 +08:00
parent 50b5f8c2c1
commit 0be2781d70
274 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,430 @@
package minesweeper.solver.bulk;
import java.math.BigDecimal;
import java.util.Random;
import minesweeper.gamestate.GameStateModel;
import minesweeper.solver.bulk.BulkRequest.BulkAction;
import minesweeper.solver.settings.PlayStyle;
import minesweeper.solver.settings.SolverSettings;
abstract public class BulkController implements Runnable {
/*
public enum PlayStyle {
FLAGGED(false, false),
NO_FLAG(true, false),
EFFICIENCY(true, true);
public final boolean flagless;
public final boolean useChords;
private PlayStyle(boolean flagless, boolean useChords) {
this.flagless = flagless;
this.useChords = useChords;
}
}
*/
private final int gamesToPlay;
private final int workers;
private final SolverSettings solverSettings;
private final int bufferSize;
private final BulkRequest[] buffer;
private final BulkWorker[] bulkWorkers;
private final static int REPORT_INTERVAL = 200;
private final static int DEFAULT_BUFFER_PER_WORKER = 1000;
private volatile int waitingSlot = -1; // this is the next slot we are waiting to be returned
private volatile int nextSlot = 0; // this is the next slot to be dispatched
private volatile int nextSequence = 1;
private volatile int waitingSequence = 0;
private volatile int reportInterval = REPORT_INTERVAL;
private final Random seeder;
private volatile boolean finished = false;
private volatile BulkEvent event;
private volatile BulkEvent finalEvent;
private Thread mainThread;
private long startTime;
private long endTime;
private BulkListener eventListener;
private GamePostListener postGameListener;
private GamePreListener preGameListener;
private volatile int failedToStart = 0;
private volatile int wins = 0;
private volatile int guesses = 0;
private volatile int noGuessWins = 0;
private volatile BigDecimal totalGamesValue = BigDecimal.ZERO;
private volatile int totalActions = 0;
private volatile long total3BV = 0;
private volatile long total3BVSolved = 0;
private volatile BigDecimal fairness = BigDecimal.ZERO;
private volatile int currentWinStreak = 0;
private volatile int bestWinStreak = 0;
private volatile int currentMastery = 0;
private volatile int bestMastery = 0;
private volatile boolean[] mastery = new boolean[100];
private PlayStyle playStyle = PlayStyle.NO_FLAG;
public BulkController(Random seeder, int gamesToPlay, SolverSettings solverSettings, int workers) {
this(seeder, gamesToPlay, solverSettings, workers, DEFAULT_BUFFER_PER_WORKER);
}
public BulkController(Random seeder, int gamesToPlay, SolverSettings solverSettings, int workers, int bufferPerWorker) {
this.seeder = seeder;
this.gamesToPlay = gamesToPlay;
this.workers = workers;
this.bulkWorkers = new BulkWorker[this.workers];
this.solverSettings = solverSettings;
this.bufferSize = bufferPerWorker * this.workers;
this.buffer = new BulkRequest[bufferSize];
}
public void registerEventListener(BulkListener listener) {
this.eventListener = listener;
}
public void registerPostGameListener(GamePostListener listener) {
this.postGameListener = listener;
}
public void registerPreGameListener(GamePreListener listener) {
this.preGameListener = listener;
}
/**
* Set the play style
*/
public void setPlayStyle(PlayStyle playStyle) {
this.playStyle = playStyle;
}
public PlayStyle getPlayStyle() {
return this.playStyle;
}
public void setReportInterval(int reportInterval) {
this.reportInterval = reportInterval;
}
/**
* Start the number of workers and wait for them to complete. If you don't want your main thread paused then run this on a separate thread.
*/
@Override
public void run() {
this.startTime = System.currentTimeMillis();
// remember the current thread so we can wake it when completed
mainThread = Thread.currentThread();
for (int i=0; i < workers; i++) {
bulkWorkers[i] = new BulkWorker(this, solverSettings);
new Thread(bulkWorkers[i], "worker-" + (i+1)).start();
}
while (!finished) {
try {
Thread.sleep(10000);
//System.out.println("Main thread waiting for bulk run to complete...");
} catch (InterruptedException e) {
//System.out.println("Main thread wait has been interrupted");
// process the event and then set it to null
if (event != null) {
if (eventListener != null) {
eventListener.intervalAction(event);
}
event = null;
}
}
}
this.endTime = System.currentTimeMillis();
//System.out.println("Finished after " + getDuration() + " milliseconds");
}
/**
* Request each of the workers to stop and then stop the run
*/
public void stop() {
for (BulkWorker worker: bulkWorkers) {
worker.stop();
}
// create a final event
finished = true;
this.event = createEvent();
this.finalEvent = this.event;
// set the process to finished and wake the main thread
mainThread.interrupt();
}
/**
* When the process is finished you can get the final results from here
*/
public BulkEvent getResults() {
return this.finalEvent;
}
/**
* Returns true when the bulk run is completed or been stopped
*/
public boolean isFinished() {
return finished;
}
/**
* returns how log the bulk run took in milliseconds, or how long it has been running depending if it has finished ot not
* @return
*/
public long getDuration() {
if (startTime == 0) { // not started
return 0;
} else if (finished && endTime != 0) { // finished
return endTime - startTime;
} else {
return System.currentTimeMillis() - startTime; // in flight
}
}
private void processSlots() {
boolean doEvent = false;
BulkEvent bulkEvent = null;
// process all the games which have been processed and are waiting in the buffer
while (buffer[waitingSlot] != null) {
BulkRequest request = buffer[waitingSlot];
int masteryIndex = request.sequence % 100;
if (request.gs.getGameState() == GameStateModel.WON) {
wins++;
totalGamesValue = totalGamesValue.add(request.gameValue);
//System.out.println(request.gs.getSeed() + " has 3BV " + request.gs.getTotal3BV() + " and actions " + request.gs.getActionCount());
if (request.guesses == 0) {
noGuessWins++;
}
currentWinStreak++;
if (currentWinStreak > bestWinStreak) {
bestWinStreak = currentWinStreak;
}
// if we lost 100 games ago then mastery is 1 more
if (!mastery[masteryIndex]) {
mastery[masteryIndex] = true;
currentMastery++;
if (currentMastery > bestMastery) {
bestMastery = currentMastery;
}
}
double efficiency = 100 * ((double) request.gs.getTotal3BV() / (double) request.gs.getActionCount());
} else {
if (!request.startedOkay) {
failedToStart++;
}
currentWinStreak = 0;
// if we won 100 games ago, then mastery is now 1 less
if (mastery[masteryIndex]) {
mastery[masteryIndex] = false;
currentMastery--;
}
}
// accumulate the total actions taken
totalActions = totalActions + request.gs.getActionCount();
// accumulate 3BV in the game and how much was solved
total3BV = total3BV + request.gs.getTotal3BV();
total3BVSolved = total3BVSolved + request.gs.getCleared3BV();
// accumulate total guesses made
guesses = guesses + request.guesses;
fairness = fairness.add(request.fairness);
// clear the buffer and move on to the next slot
buffer[waitingSlot] = null;
waitingSlot++;
waitingSequence++;
// recycle the buffer when we get beyond the top
if (this.waitingSlot >= bufferSize) {
this.waitingSlot = this.waitingSlot - bufferSize;
}
// if we have run and processed all the games then wake the main thread
if (waitingSequence == gamesToPlay) {
//System.out.println("All games played, waking the main thread");
finished = true;
this.finalEvent = createEvent();
bulkEvent = this.finalEvent;
doEvent = true;
//mainThread.interrupt();
// provide an update every now and again, do that on the main thread
} else if (this.reportInterval != 0 && waitingSequence % this.reportInterval == 0) {
bulkEvent = createEvent();
doEvent = true;
}
if (postGameListener != null) {
postGameListener.postAction(request);
}
}
// if we have an event to do then interrupt the main thread which will post it
if (doEvent) {
if (this.event == null) {
this.event = bulkEvent;
mainThread.interrupt();
} else {
System.out.println("Event suppressed because earlier event is still in progress");
}
}
}
private BulkEvent createEvent() {
BulkEvent event = new BulkEvent();
event.setGamesToPlay(gamesToPlay);
event.setGamesPlayed(waitingSequence);
event.setGamesWon(wins);
event.setTotalGamesValue(totalGamesValue);
event.setTotalGuesses(guesses);
event.setNoGuessWins(noGuessWins);
if (guesses != 0) {
event.setFairness(fairness.doubleValue() / guesses);
} else {
event.setFairness(0);
}
event.setMastery(bestMastery);
event.setWinStreak(bestWinStreak);
event.setTotalActions(totalActions);
event.setFailedToStart(failedToStart);
event.setTotal3BV(total3BV);
event.setTotal3BVSolved(total3BVSolved);
long duration = getDuration();
long timeLeft;
if (waitingSequence != 0) {
timeLeft = ((duration * gamesToPlay) / waitingSequence) - duration;
} else {
timeLeft = 0;
}
event.setTimeSoFar(duration);
event.setEstimatedTimeLeft(timeLeft);
event.setFinished(finished);
return event;
}
/**
* Returns the last request and gets the next
*/
protected synchronized BulkRequest getNextRequest(BulkRequest request) {
if (request != null) {
buffer[request.slot] = request;
// if this is the slot we are waiting on then process the games which are in the buffer - this is all synchronised so nothing else arrives will it happens
if (request.slot == waitingSlot) {
processSlots();
}
}
// if we have played all the games or we have been stopped then tell the workers to stop
if (nextSequence > gamesToPlay || finished) {
return BulkRequest.STOP;
}
// if the next sequence is a long way ahead of the waiting sequence then wait until we catch up. Tell the worker to wait.
if (nextSequence > waitingSequence + bufferSize - 2) {
System.out.println("Buffer is full after " + nextSequence + " games dispatched");
return BulkRequest.WAIT;
}
// otherwise dispatch the next game to be played
//GameSettings gameSettings = GameSettings.EXPERT;
//GameType gameType = GameType.STANDARD;
//SolverSettings settings = SettingsFactory.GetSettings(Setting.SMALL_ANALYSIS).setExperimentalScoring(true);
BulkRequest next = new BulkRequest();
next.action = BulkAction.RUN;
next.sequence = this.nextSequence;
next.slot = this.nextSlot;
next.gs = getGameState(Math.abs(seeder.nextLong() & 0xFFFFFFFFFFFFFl));
if (this.preGameListener != null) {
preGameListener.preAction(next.gs);
}
if (next.gs.getGameState() == GameStateModel.LOST) {
next.startedOkay = false;
}
// roll onto the next sequence
this.nextSequence++;
this.nextSlot++;
// if this is the first request then initialise the waiting slot
if (waitingSlot == -1) {
waitingSlot = 0;
}
// recycle the buffer when we get beyond the top
if (this.nextSlot >= bufferSize) {
this.nextSlot = this.nextSlot - bufferSize;
}
return next;
}
abstract protected GameStateModel getGameState(long seed);
}

View File

@ -0,0 +1,131 @@
package minesweeper.solver.bulk;
import java.math.BigDecimal;
public class BulkEvent {
private int gamesToPlay;
private int gamesPlayed;
private int gamesWon;
private int noGuessWins;
private int totalActions;
private BigDecimal totalGamesValue;
private long total3BV;
private long total3BVSolved;
private int totalGuesses;
private double fairness;
private int winStreak;
private int mastery;
private int failedToStart; // this is when the game didn't survive the start sequence
private long timeSoFar;
private long estimatedTimeLeft;
private boolean isFinished = false;
public int getFailedToStart() {
return failedToStart;
}
public int getGamesToPlay() {
return gamesToPlay;
}
public int getGamesPlayed() {
return gamesPlayed;
}
public int getGamesWon() {
return gamesWon;
}
public int getNoGuessWins() {
return noGuessWins;
}
public int getTotalGuesses() {
return totalGuesses;
}
public double getFairness() {
return fairness;
}
public int getWinStreak() {
return winStreak;
}
public int getMastery() {
return mastery;
}
public int getTotalActions() {
return totalActions;
}
public long getTotal3BV() {
return total3BV;
}
public long getTotal3BVSolved() {
return total3BVSolved;
}
protected void setFailedToStart(int failedToStart) {
this.failedToStart = failedToStart;
}
protected void setGamesToPlay(int gamesToPlay) {
this.gamesToPlay = gamesToPlay;
}
protected void setGamesPlayed(int gamesPlayed) {
this.gamesPlayed = gamesPlayed;
}
protected void setGamesWon(int gamesWon) {
this.gamesWon = gamesWon;
}
protected void setNoGuessWins(int noGuessWins) {
this.noGuessWins = noGuessWins;
}
protected void setTotalGuesses(int totalGuesses) {
this.totalGuesses = totalGuesses;
}
protected void setFairness(double fairness) {
this.fairness = fairness;
}
protected void setWinStreak(int winStreak) {
this.winStreak = winStreak;
}
protected void setMastery(int mastery) {
this.mastery = mastery;
}
protected void setTotalActions(int actions) {
this.totalActions = actions;
}
public long getTimeSoFar() {
return timeSoFar;
}
public long getEstimatedTimeLeft() {
return estimatedTimeLeft;
}
protected void setTimeSoFar(long timeSoFar) {
this.timeSoFar = timeSoFar;
}
protected void setEstimatedTimeLeft(long estimatedTimeLeft) {
this.estimatedTimeLeft = estimatedTimeLeft;
}
public boolean isFinished() {
return isFinished;
}
protected void setFinished(boolean isFinished) {
this.isFinished = isFinished;
}
protected void setTotal3BV(long total3bv) {
total3BV = total3bv;
}
protected void setTotal3BVSolved(long total3bvSolved) {
total3BVSolved = total3bvSolved;
}
public BigDecimal getTotalGamesValue() {
return totalGamesValue;
}
public void setTotalGamesValue(BigDecimal totalGamesValue) {
this.totalGamesValue = totalGamesValue;
}
}

View File

@ -0,0 +1,10 @@
package minesweeper.solver.bulk;
public abstract class BulkListener {
/**
* This is run at regular intervals and should be used to provide any out put that is needed
*/
abstract public void intervalAction(BulkEvent event);
}

View File

@ -0,0 +1,42 @@
package minesweeper.solver.bulk;
import java.util.Random;
import minesweeper.gamestate.GameFactory;
import minesweeper.gamestate.GameStateModel;
import minesweeper.settings.GameSettings;
import minesweeper.settings.GameType;
import minesweeper.solver.settings.SolverSettings;
public class BulkPlayer extends BulkController {
protected final GameType gameType;
protected final GameSettings gameSettings;
//private List<Action> preActions;
/**
* Use the bulk controller to play games from the beginning
*/
public BulkPlayer(Random seeder, int gamesToPlay, GameType gameType, GameSettings gameSettings, SolverSettings solverSettings, int workers) {
super(seeder, gamesToPlay, solverSettings, workers);
this.gameType = gameType;
this.gameSettings = gameSettings;
}
public BulkPlayer(Random seeder, int gamesToPlay, GameType gameType, GameSettings gameSettings, SolverSettings solverSettings, int workers, int bufferPerWorker) {
super(seeder, gamesToPlay, solverSettings, workers, bufferPerWorker);
this.gameType = gameType;
this.gameSettings = gameSettings;
}
protected GameStateModel getGameState(long seed) {;
GameStateModel gs = GameFactory.create(gameType, gameSettings, seed);
return gs;
}
}

View File

@ -0,0 +1,44 @@
package minesweeper.solver.bulk;
import java.math.BigDecimal;
import minesweeper.gamestate.GameStateModel;
public class BulkRequest {
protected final static BulkRequest WAIT = new BulkRequest() {
{
action = BulkAction.WAIT;
}
};
protected final static BulkRequest STOP = new BulkRequest() {
{
action = BulkAction.STOP;
}
};
public enum BulkAction {
STOP,
WAIT,
RUN;
}
protected BulkAction action;
protected int sequence; // the sequence number for this request
protected int slot; // the slot the request is to be store in the buffer
protected GameStateModel gs;
protected int guesses = 0;
protected BigDecimal fairness = BigDecimal.ZERO;
protected BigDecimal gameValue = BigDecimal.ONE;
protected boolean startedOkay = true;
public GameStateModel getGame( ) {
return this.gs;
}
public int getGuesses() {
return this.guesses;
}
}

View File

@ -0,0 +1,47 @@
package minesweeper.solver.bulk;
import java.util.Random;
import minesweeper.gamestate.GameStateModel;
import minesweeper.solver.RolloutGenerator;
import minesweeper.solver.settings.SolverSettings;
import minesweeper.structure.Action;
import minesweeper.structure.Location;
public class BulkRollout extends BulkController {
protected final RolloutGenerator generator;
protected final Location safeTile;
protected final Location startTile;
/**
* Use the bulk controller to play games from the beginning
*/
public BulkRollout(Random seeder, int gamesToPlay, RolloutGenerator generator, Location startTile, boolean safeStart, SolverSettings solverSettings, int workers) {
super(seeder, gamesToPlay, solverSettings, workers);
this.generator = generator;
this.startTile = startTile;
if (safeStart) {
this.safeTile = startTile;
} else {
this.safeTile = null;
}
}
protected GameStateModel getGameState(long seed) {
GameStateModel gs = generator.generateGame(seed, safeTile);
// play the start tile and return the game
gs.doAction(new Action(startTile, Action.CLEAR));
return gs;
}
}

View File

@ -0,0 +1,142 @@
package minesweeper.solver.bulk;
import java.math.BigDecimal;
import minesweeper.gamestate.GameStateModel;
import minesweeper.solver.Solver;
import minesweeper.solver.bulk.BulkRequest.BulkAction;
import minesweeper.solver.settings.SolverSettings;
import minesweeper.structure.Action;
public class BulkWorker implements Runnable {
private boolean stop = false;
private final BulkController controller;
private final SolverSettings solverSettings;
protected BulkWorker(BulkController controller, SolverSettings solverSettings) {
this.controller = controller;
this.solverSettings = solverSettings;
}
@Override
public void run() {
//System.out.println(Thread.currentThread().getName() + " is starting");
BulkRequest request = controller.getNextRequest(null);
while (!stop) {
if (request.action == BulkAction.STOP) {
stop = true;
break;
} else if (request.action == BulkAction.WAIT) { // wait and then ask again
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
request = controller.getNextRequest(null);
} else {
//System.out.println("Playing game sequence " + request.sequence);
// play the game
playGame(request);
// return it to the controller
request = controller.getNextRequest(request);
}
}
//System.out.println(Thread.currentThread().getName() + " is stopping");
}
private void playGame(BulkRequest request) {
int state;
// if the game is won or lost already then nothing to do. This can be the case since we don't know what state the Game State model is in.
if (request.gs.getGameState() == GameStateModel.WON || request.gs.getGameState() == GameStateModel.LOST) {
return;
}
Solver solver = new Solver(request.gs, this.solverSettings, false);
solver.setPlayStyle(controller.getPlayStyle());
//solver.setFlagFree(controller.getPlayStyle().flagless);
//solver.setPlayChords(controller.getPlayStyle().useChords);
int loopCounter = 0;
play: while (true) {
loopCounter++;
if (loopCounter % 1000 == 0) {
System.err.println("Game " + request.gs.showGameKey() + " is looping");
break play;
}
Action[] moves;
try {
solver.start();
moves = solver.getResult();
} catch (Exception e) {
System.err.println("Game " + request.gs.showGameKey() + " has thrown an exception!");
e.printStackTrace();
return;
}
if (moves.length == 0) {
System.err.println(request.gs.getSeed() + " - No moves returned by the solver");
return;
}
// play all the moves until all done, or the game is won or lost
for (int i=0; i < moves.length; i++) {
BigDecimal prob = moves[i].getBigProb();
if (prob.compareTo(BigDecimal.ZERO) <= 0 || prob.compareTo(BigDecimal.ONE) > 0) {
System.err.println("Game (" + request.gs.showGameKey() + ") move with probability of " + prob + "! - " + moves[i].toString());
}
boolean result = request.gs.doAction(moves[i]);
state = request.gs.getGameState();
// only monitor good guesses (brute force, probability engine, zonal, opening book and hooks)
if (state == GameStateModel.STARTED || state == GameStateModel.WON) {
if (!moves[i].isCertainty() ) {
request.guesses++;
//request.fairness = request.fairness + 1d;
request.fairness = request.fairness.add(BigDecimal.ONE).subtract(prob);
}
} else { // otherwise the guess resulted in a loss
if (!moves[i].isCertainty()) {
request.guesses++;
//request.fairness = request.fairness - prob.doubleValue() / (1d - prob.doubleValue());
request.fairness = request.fairness.subtract(prob);
}
}
if (state == GameStateModel.LOST && moves[i].isCertainty()) {
System.err.println("Game (" + request.gs.showGameKey() + ") lost on move with probability = " + prob + " :" + moves[i].toString());
}
if (state == GameStateModel.LOST || state == GameStateModel.WON) {
break play;
}
}
}
request.gameValue = solver.getWinValue();
}
protected void stop() {
stop = true;
}
}

View File

@ -0,0 +1,18 @@
package minesweeper.solver.bulk;
/**
* The "postAction" method is run after each game finishes
*/
public abstract class GamePostListener {
/**
* This is run after each game finishes
*/
abstract public void postAction(BulkRequest request);
/**
* Place the results you want to show here
*/
abstract public void postResults();
}

View File

@ -0,0 +1,12 @@
package minesweeper.solver.bulk;
import minesweeper.gamestate.GameStateModel;
public abstract class GamePreListener {
/**
* This is run before each game starts
*/
abstract public void preAction(GameStateModel game);
}