private static int visitAllReachableStates(final T3Board board, final ArrayList<T3Board> states) {
    states.add(board);
    int leaves = 0;
    if (!board.isTerminal()) {
      // look for reachable states
      for (T3Board next : directlyReachableStates(board))
        leaves += visitAllReachableStates(next, states);

      return leaves;
    } else {
      return 1;
    }
  }
  public static void main(String[] args) throws IOException {
    ArrayList<T3Board> states = new ArrayList<T3Board>();
    int leaves = visitAllReachableStates(new T3Board(), states);
    System.out.println("Leaf nodes in game tree: " + leaves);
    System.out.println("Branches in game tree: " + states.size());
    HashSet<String> unique = new HashSet<String>();
    for (T3Board state : states) unique.add(state.toString());
    System.out.println("Unique reachable states: " + unique.size());
    HashSet<T3Board> reduced = new HashSet<T3Board>();
    for (T3Board board : states)
      if (!board.isTerminal()) reduced.add(new T3Board(board.lowestEquivalentBoard()));
    System.out.println("Reduced states: " + reduced.size());
    int saPairs = countStatesAndActions(reduced);
    System.out.println("Reduced state-action pairs: " + saPairs);
    System.out.println();

    System.out.println("Small sample of reduced states");
    System.out.println();
    Random random = new Random();
    T3Board check = null;
    File statespace = new File("output/t3statespace");
    if (statespace.exists()) statespace.delete();
    statespace.getParentFile().mkdirs();
    FileWriter out = new FileWriter(statespace);
    for (T3Board board : reduced) {
      out.write(board.toString() + "\n");
      if (random.nextDouble() < 0.005) {
        System.out.println(board.toGlyph());
        check = board;
      }
    }
    out.close();

    System.out.println("Symmetry check of last sample");
    System.out.println();
    for (String sym : check.equivalentBoards()) {
      System.out.println((new T3Board(sym)).toGlyph());
    }
  }