private Board solve_dfs(Board b, int depthLimit, int depth) { if (depth >= depthLimit) { // Cutoff test return b; } Board best = null; int best_score = -1; for (int i = 0; i < BOARD_WIDTH; i++) { Board next = new Board(b); if (next.move(tileSequence, directions[i])) { Board candidate = solve_dfs(next, depthLimit, depth + 1); if (candidate == null && next.finished()) { candidate = next; } if (candidate != null) { if (candidate.finished()) { updateBest(candidate); } else { int score = evaluate(candidate); if (score > best_score) { best_score = score; best = candidate; } } } } } return best; }
/** * Single threaded depth-limited depth first search, with no backtracking. * * @param s The tile sequence * @param b The board to search * @return The final best board state that it can find. */ private Board solve_ldfs(Board b) { Board input = b; fbest_score = -1; fbest = null; currentfactors = factors; while (b != null && !b.finished()) { b = solve_dfs(b, MAX_DEPTH, 0); if (b != null) { log_info(b); } } // Super edge-case: The input board can't be moved... return fbest == null ? input : fbest; }
/** * Multi-threaded depth-limited depth first search, with no backtracking. * * @param s The tile sequence * @param b The board to search * @param pool The thread pool * @return The final best board state that it can find. */ private Board solve_pndfs(Board b, ExecutorService pool) { Board input = b; fbest_score = -1; fbest = null; currentfactors = factors; while (b != null && !b.finished()) { b = solve_pdfs(b, pool); if (b != null) { // log_info(b); } } // Super edge-case: The input board can't be moved... return fbest == null ? input : fbest; }
/** * Solves a board game using a depth-limited depth first search. Also makes use of some * backtracking to further optimise the result. Will also attempt to run multithreadedly. * * @param s The tile sequence * @param b The board to search * @return The final best board state that it can find. */ private Board solve_mdfs(Board b) { Ringbuffer<Board> rb = new Ringbuffer<>(6); Board current = b, choke_best = null; char fc = 0, foff = 0; // VTEC just kicked in yo ExecutorService pool = Executors.newFixedThreadPool(nThreads); fbest_score = -1; fbest = null; while (current != null && !current.finished()) { rb.push(current); current = solve_pdfs(current, pool); if (choke_best != null && choke_best != fbest) { log_info( "Recovery!: [%d:%s] %d(%d) --> %d(%d)", (int) fc, Arrays.toString(currentfactors), choke_best.score(), choke_best.nMoves(), fbest.score(), fbest.nMoves()); choke_best = null; // Test: Is it always best to stick to 18,2,2,9 where possible? // Maybe not, but some factors shouldn't be used for extended periods of time. if (fc > 3) { log_info( "Volatile weights were used; switching back to %s", Arrays.toString(choicefactors[0])); fc = 0; currentfactors = choicefactors[0]; } } if (current != null) { log_info(current); } else if (fbest != null && fbest.nMoves() < tileSequence.length) { current = rb.pop(); if (choke_best != fbest) { choke_best = fbest; foff = 1; fc = (char) ((fc + 1) % choicefactors.length); currentfactors = choicefactors[fc]; log_info("Dead-end, back-tracking two steps and trying with different weights!"); log_info( "Starting index: %d (current score %d/%d)", (int) fc, current.score(), current.nMoves()); } else if (foff++ < choicefactors.length - 1) { fc = (char) ((fc + 1) % choicefactors.length); currentfactors = choicefactors[fc]; log_info("No improvement, trying factor index %d...", (int) fc); } else { current = null; log_info("Factor exhaustion"); } } } pool.shutdown(); return fbest == null ? b : fbest; }