Assignment 3: Sokoban

This assignment is worth 20 points and has two parts.

1. A* search (10 pts)

Implement A* search in Java. Write a general-purpose implementation that can search any problem that implements this interface:

// S = state type, A = action type
public interface Problem<S, A> {
    S initialState();
    List<A> actions(S state);
    S result(S state, A action);
    boolean isGoal(S state);
    int cost(S state, A action);

    int estimate(S state);  // optimistic estimate of cost from state to goal
}

Provide a search() method that returns a Node<S, A> representing the goal node that was found, or null if no goal was found. Node<S, A> should contain at least these fields:

class Node<S, A> {
  public S state;
  public Node<S, A> parent;  // parent node, or null if this is the start node
  public A action;  // the action we took to get here from the parent
  
}

Your code should live in a class that looks like this:

// A* search
class AStar<S, A> {
  public static <S, A> Node<S, A> search(Problem<S, A> prob, Stats stats) {
     your implementation here 
  }     
}

The search should fill in the Stats object with statistics about the search. Stats should have at least this field:

public class Stats {
    public int expanded;  // number of nodes expanded in the search
}

You may add any other statistics that you like.

A caller will be able to invoke your search method like this:

  Problem<Integer, Integer> p = new MyPuzzle();

  Stats stats = new Stats();

  Node<Integer, Integer> result = AStar.search(p, stats);
  System.out.format("%d nodes were expanded\n", stats.expanded);

Here are some Problem instances that you can use for testing:

Be sure that your A* implementation returns optimal solutions to these test problems before you apply it to Sokoban.

hints

2. Sokoban (10 pts)

Use A* search to solve Sokoban puzzles.

Download the Sokoban code from the Sokoban4J repository. Write your agent in the class cz.sokoban4j.playground.MyAgent. MyAgent.java which contains a simple depth-first search implementation that you can delete and replace with your own solver.

In your agent code, create an instance of the Problem<S, A> interface above and use an A* search to hunt for an optimal solution. Report the number of nodes that were expanded in the search by writing to standard output.

You wil want to use the Sokoban API.

To receive full credit for this assignment, implement the following:

Your agent should be able to solve all of the puzzles in the Sokoban4J/levels/Easy directory while expanding fewer than 10,000 nodes for each puzzle.

hints

System.out.println("dead squares: ");
for (int y = 0 ; y < board.height() ; ++y) {
    for (int x = 0 ; x < board.width() ; ++x)
        System.out.print(CTile.isWall(board.tile(x, y)) ? '#' : (dead[x][y] ? 'X' : '_'));
    System.out.println();
}
===== BOARD =====
#####
#p  #
# a #
#  1#
#####
dead squares: 
#####
#XXX#
#X__#
#X__#
#####

tournament

All entries received before the soft deadline will automatically be entered into a Sokoban tournament. In the tournament, I will run your solver on a series of Sokoban levels of increasing difficulty. Your solver will have 10 seconds to solve each level in a process with 2 Gb of RAM (specified with the -Xmx Java flag), on a machine with a 2.8 Ghz Intel CPU.

As soon as a program fails to solve 3 levels, its evaluation ends. Your solver's score will be the number of levels that it successfully solved before it reached the third unsolvable level.

Note that solutions in the tournament need not be optimal! I will, however, resolve ties by choosing the solver that found a shorter solution.

I will use the levels in Evaluate.java or a similar set of levels for the tournament. If you want to replicate the tournament conditions on your own machine, change the maxFail constant in main() in that file to 3 so the evalulation will continue until the 3rd unsolved level.