/** Returns the best move to make from here during a playout. */ short bestSearchMove(SearchNode node, McRunnable runnable) { final Board runnableBoard = runnable.getBoard(); final MersenneTwisterFast random = runnable.getRandom(); short result = node.getWinningMove(); if (result != NO_POINT && runnableBoard.isLegal(result)) { // The isLegal() check is necessary to avoid superko violations return result; } float bestSearchValue = searchValue(node, PASS); result = PASS; final ShortSet vacantPoints = runnableBoard.getVacantPoints(); int start; start = random.nextInt(vacantPoints.size()); int i = start; final int skip = PRIMES[random.nextInt(PRIMES.length)]; do { final short move = vacantPoints.get(i); final float searchValue = searchValue(node, move); if (searchValue > bestSearchValue) { if (runnable.isFeasible(move) && runnableBoard.isLegal(move)) { bestSearchValue = searchValue; result = move; } else { node.exclude(move); } } // Advancing by a random prime skips through the array // in a manner analogous to double hashing. i = (i + skip) % vacantPoints.size(); } while (i != start); return result; }
/** Some nodes may have their biases updated. */ @Override public void descend(McRunnable runnable) { SearchNode node = getRoot(); assert node != null : "Fancy hash code: " + board.getFancyHash(); while (runnable.getBoard().getPasses() < 2) { selectAndPlayMove(node, runnable); final SearchNode child = table.findIfPresent(runnable.getBoard().getFancyHash()); if (child == null) { return; // No child } if (child.getTotalRuns() > biasDelay && !child.biasUpdated()) { child.updateBias(runnable); } node = child; } }
@Override public void fakeDescend(McRunnable runnable, short... moves) { runnable.copyDataFrom(board); final SearchNode node = getRoot(); assert node != null : "Fancy hash code: " + board.getFancyHash(); for (final short move : moves) { System.out.println("Passing " + move + " to runnable"); runnable.acceptMove(move); final SearchNode child = table.findIfPresent(runnable.getBoard().getFancyHash()); if (child == null) { return; // No child } if (child.getTotalRuns() > biasDelay && !child.biasUpdated()) { child.updateBias(runnable); } } }
/** Selects and plays one move in the search tree. */ short selectAndPlayMove(SearchNode node, McRunnable runnable) { final short move = bestSearchMove(node, runnable); runnable.acceptMove(move); return move; }