Site Search:

PuzzleSolver.java

Back>

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

class Position {
    public final int x;
    public final int y;
    public Position(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
    public boolean equals(Object o) {
        if(!(o instanceof Position))
            return false;
        Position that = (Position)o;
        if(this.x == that.x && this.y == that.y)
            return true;
        return false;
    }
    
    @Override
    public int hashCode() {
        return x*31 + y;
    }
    
    @Override
    public String toString() {
        return "[" + x + "," + y + "]";
    }
}
public class PuzzleSolver <P,M> extends ConcurrentPuzzleSolver<P, M> {
    private static final Set<Position> positions = new HashSet<>();
    private static Random r = new Random();
    private static int SIZE = 10;
    private static final Position GOAL = new Position(SIZE/2, SIZE/2);
    static {
        for(int i = 0; i < SIZE; i++) {
            for(int j = 0; j < SIZE; j ++) {
                positions.add(new Position(i, j));
            }
        }
        for(int i = 0; i < SIZE * SIZE/2; i++) {
            positions.remove(new Position(r.nextInt(SIZE), r.nextInt(SIZE)));
        }
    }
    public static void main(String[] args) {
        Puzzle<Position, Position> p = new Puzzle<Position, Position>(){

            @Override
            public Position initialPosition() {
                return new Position(0, 0);
            }

            @Override
            public boolean isGoal(Position position) {
                return position.equals(GOAL);
            }

            @Override
            public Set<Position> legalMoves(Position position) {
                Set<Position> adj = new HashSet<>();
                for(int i = -1; i <= 1; i++) {
                    for(int j = -1; j <= 1; j++) {
                        if(i*j != 0) 
                            continue;
                        Position p = new Position(position.x + i, position.y + j);
                        if(positions.contains(p)) {
                            adj.add(p);
                        }
                    }
                }
                return adj;
            }

            @Override
            public Position move(Position position, Position move) {
                return move;
            }};
            
        PuzzleSolver<Position, Position> ps = new PuzzleSolver<>(p);
        try {
            List<Position> solution = ps.solve();
            System.out.println("puzzle board:");
            System.out.println(positions);
            System.out.println("goal: " + GOAL);
            System.out.println("path from " + p.initialPosition() + " to goal: " + GOAL);
            System.out.println(solution);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    PuzzleSolver(Puzzle<P, M> puzzle) {
        super(puzzle);
    }

    private final AtomicInteger taskCount = new AtomicInteger(0);

    protected Runnable newTask(P p, M m, PuzzleNode<P, M> n) {
        return new CountingSolverTask(p, m, n);
    }

    class CountingSolverTask extends SolverTask {
        CountingSolverTask(P pos, M move, PuzzleNode<P, M> prev) {
            super(pos, move, prev);
            taskCount.incrementAndGet();
        }

        public void run() {
            try {
                super.run();
            } finally {
                if (taskCount.decrementAndGet() == 0)
                    solution.setValue(null);
            }
        }
    }
}

interface Puzzle <P, M> {
    P initialPosition();

    boolean isGoal(P position);

    Set<M> legalMoves(P position);

    P move(P position, M move);
}

class SequentialPuzzleSolver <P, M> {
    private final Puzzle<P, M> puzzle;
    private final Set<P> seen = new HashSet<P>();

    public SequentialPuzzleSolver(Puzzle<P, M> puzzle) {
        this.puzzle = puzzle;
    }

    public List<M> solve() {
        P pos = puzzle.initialPosition();
        return search(new PuzzleNode<P, M>(pos, null, null));
    }

    private List<M> search(PuzzleNode<P, M> node) {
        if (!seen.contains(node.pos)) {
            seen.add(node.pos);
            if (puzzle.isGoal(node.pos))
                return node.asMoveList();
            for (M move : puzzle.legalMoves(node.pos)) {
                P pos = puzzle.move(node.pos, move);
                PuzzleNode<P, M> child = new PuzzleNode<P, M>(pos, move, node);
                List<M> result = search(child);
                if (result != null)
                    return result;
            }
        }
        return null;
    }
}

class ConcurrentPuzzleSolver <P, M> {
    private final Puzzle<P, M> puzzle;
    private final ExecutorService exec;
    private final ConcurrentMap<P, Boolean> seen;
    protected final ValueLatch<PuzzleNode<P, M>> solution = new ValueLatch<PuzzleNode<P, M>>();

    public ConcurrentPuzzleSolver(Puzzle<P, M> puzzle) {
        this.puzzle = puzzle;
        this.exec = initThreadPool();
        this.seen = new ConcurrentHashMap<P, Boolean>();
        if (exec instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor tpe = (ThreadPoolExecutor) exec;
            tpe.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        }
    }

    private ExecutorService initThreadPool() {
        return Executors.newCachedThreadPool();
    }

    public List<M> solve() throws InterruptedException {
        try {
            P p = puzzle.initialPosition();
            exec.execute(newTask(p, null, null));
            // block until solution found
            PuzzleNode<P, M> solnPuzzleNode = solution.getValue();
            return (solnPuzzleNode == null) ? null : solnPuzzleNode.asMoveList();
        } finally {
            exec.shutdown();
        }
    }

    protected Runnable newTask(P p, M m, PuzzleNode<P, M> n) {
        return new SolverTask(p, m, n);
    }

    protected class SolverTask extends PuzzleNode<P, M> implements Runnable {
        SolverTask(P pos, M move, PuzzleNode<P, M> prev) {
            super(pos, move, prev);
        }

        public void run() {
            if (solution.isSet()
                    || seen.putIfAbsent(pos, true) != null)
                return; // already solved or seen this position
            if (puzzle.isGoal(pos))
                solution.setValue(this);
            else
                for (M m : puzzle.legalMoves(pos))
                    exec.execute(newTask(puzzle.move(pos, m), m, this));
        }
    }
}

class ValueLatch <T> {
    private T value = null;
    private final CountDownLatch done = new CountDownLatch(1);

    public boolean isSet() {
        return (done.getCount() == 0);
    }

    public synchronized void setValue(T newValue) {
        if (!isSet()) {
            value = newValue;
            done.countDown();
        }
    }

    public T getValue() throws InterruptedException {
        done.await();
        synchronized (this) {
            return value;
        }
    }
}

class PuzzleNode <P, M> {
    final P pos;
    final M move;
    final PuzzleNode<P, M> prev;

    public PuzzleNode(P pos, M move, PuzzleNode<P, M> prev) {
        this.pos = pos;
        this.move = move;
        this.prev = prev;
    }

    List<M> asMoveList() {
        List<M> solution = new LinkedList<M>();
        for (PuzzleNode<P, M> n = this; n.move != null; n = n.prev)
            solution.add(0, n.move);
        return solution;
    }

}


The above example creates a set of positions in a square area with SIZE x SIZE, put the goal at the middle of the square. Then the program start the search at position [0,0], then finally reach the goal and returns the path.