All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.k0zka.finder4j.backtrack.Backtrack.kt Maven / Gradle / Ivy

The newest version!
package com.github.k0zka.finder4j.backtrack

import org.slf4j.LoggerFactory
import java.util.LinkedList

object Backtrack {

	private val logger = LoggerFactory
			.getLogger(Backtrack::class.java)

	internal class BackTrackState>(val state: X, val steps: Iterator)

	/**
	 * Run a backtracking algorithm on a problem
	 *
	 * @param state
	 * @param factory
	 * @param terminationStrategy
	 * @param listener
	 */
	fun > backtrack(
			state: X,
			factory: StepFactory,
			terminationStrategy: TerminationStrategy,
			listener: SolutionListener) {
		backtrack(state, factory, terminationStrategy, listener, null)
	}

	fun > backtrack(
			state: X,
			factory: StepFactory,
			terminationStrategy: TerminationStrategy,
			listener: SolutionListener,
			parallelTrack: ParallelTrack?) {
		backtrack(state, factory, terminationStrategy, listener, parallelTrack, { it.complete })
	}

	/**
	 * Run a backtracking algorithm on a problem
	 *
	 * @param state        the start state for the search
	 * @param factory    produces new steps from a state
	 * @param terminationStrategy    decides when to terminate the search
	 * @param listener        the listener receives the results
	 * @param parallelTrack    (optional) service to run search tasks in parallel
	 * @param check        check if a state is complete
	 */
	fun > backtrack(
			state: X,
			factory: StepFactory,
			terminationStrategy: TerminationStrategy,
			listener: SolutionListener,
			parallelTrack: ParallelTrack? = null,
			check: (X) -> Boolean) {

		logger.debug("Starting backtrack")

		val stack = LinkedList>()
		var head = state
		// initial state
		var btState = BackTrackState(state, factory
				.produce(state).iterator())

		while (!terminationStrategy.stop(btState.state)) {
			if (btState.steps.hasNext()) {
				// take next step
				val step = btState.steps.next()
				// just a short check if the other's have enough to do.
				// if taking multiple steps is enabled
				if (parallelTrack != null) {
					val steps = btState.steps
					fork(parallelTrack,
						 head, steps)
				}

				logger.debug("Taking step {}", step)
				// save the state for possible return
				stack.push(btState)
				// create new state
				head = step.take(head)
				// move to new state
				btState = BackTrackState(head, factory.produce(head)
						.iterator())
				logger.debug("Step ahead to {}")

				// check if new state is complete, notify if so
				if (check(head)) {
					listener.onSolution(head)
				}
			} else {
				// no steps available, try to step back
				if (stack.isEmpty()) {
					// if stepping back is not possible, there is no solution
					logger.debug("no (more) solution possible, exiting")
					return
				} else {
					// step back
					btState = stack.pop()
					head = btState.state
					logger.debug("Steping back to {}", head)
				}
			}
		}
		logger.debug("termination strategy decided to stop, exiting backtrack")
	}

	private fun > fork(
			parallelTrack: ParallelTrack,
			head: X,
			steps: Iterator) {
		// as long as
		// there are possible next steps
		// and computing resources available
		while (steps.hasNext() && parallelTrack.available()) {
			// get the next step
			val parallelStep = steps.next()
			// take a next step
			val forkedStep = parallelStep.take(head)
			// leave the parallel process running
			parallelTrack.start(forkedStep)
		}
	}
}