KR-IST - Lecture 4b Heuristic search in Java

Chris Thornton


Introduction

This lecture (which may be skipped if we are behind time) works through an implementation of heurstic search for the 8-puzzle.

Node class

  import java.util.*;

  class Node {
     int[] state = new int[9];
     int cost;
     Node parent = null;
     Vector<Node> successors = new Vector<Node>();

     Node(int s[], Node parent) {
        this.parent = parent;
        for (int i = 0; i < 9; i++) state[i] = s[i];
     }

     public String toString() {
        String s = "";
        for (int i = 0; i < 9; i++) {
           s = s + state[i] + " "; }
        return s;
     }

Node class cont.

     public boolean equals(Node n) {
        boolean result = true;
        for (int i = 0; i < 9; i++) {
           if (n.state[i] != state[i]) result = false; }
        return result;
     }

     Vector<Node> getPath(Vector<Node> v) {
        v.insertElementAt(this, 0);
        if (parent != null) v = parent.getPath(v);
        return v;
     }

     Vector<Node> getPath() {
        return getPath(new Vector<Node>()); }
  }

Space representation

  class EightPuzzleSpace {

     Node getRoot() {
        int ex[] = {3, 1, 2, 4, 7, 5, 6, 8, 0};
        int rn[] = {7, 2, 4, 5, 0, 6, 8, 3, 1}; // the Russell and Norvig eg
        return new Node(ex, null);
     }

     Node getGoal() {
        int state[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
        return new Node(state, null);
     }

     Node transformState(int r0, int c0, int r1, int c1, Node parent) {
        int[] s = parent.state;
        int[] newState = {s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8]};
        newState[(r1 * 3) + c1] = s[(r0 * 3) + c0];
        newState[(r0 * 3) + c0] = 0;
        return new Node(newState, parent);
     }

Successsor function

     Vector<Node> getSuccessors(Node parent) {
        Vector<Node> successors = new Vector<Node>();
        for (int r = 0; r < 3; r++) {
           for (int c = 0; c < 3; c++) {
              if (parent.state[(r * 3) + c] == 0) { /* hole here */
                 if (r > 0) { /* move tile from left */
                    successors.add(transformState(r-1, c, r, c, parent)); }
                 if (r < 2) { /* move tile from right */
                    successors.add(transformState(r+1, c, r, c, parent)); }
                 if (c > 0) { /* move tile from below */
                    successors.add(transformState(r, c-1, r, c, parent)); }
                 if (c < 2) { /* move tile from above */
                    successors.add(transformState(r, c+1, r, c, parent)); }
              }
           }
        }
        parent.successors = successors; /* used in getTree */
        return successors;
     }
  }

Search representation

  public class EightPuzzleSearch {
     EightPuzzleSpace space = new EightPuzzleSpace();
     Vector<Node> open = new Vector<Node>();
     Vector<Node> closed = new Vector<Node>();

     int h1Cost(Node node) {
        int cost = 0;
        for (int i = 0; i < node.state.length; i++) {
           if (node.state[i] != i) cost++; }
        return cost;
     }

The h2 heuristic

  int h2Cost(Node node) {
     int cost = 0;
     int state[] = node.state;
     for (int i = 0; i < state.length; i++) {
        int v0 = i, v1 = state[i];
        if (v1 == 0) continue; /* don't count the hole */
        int row0 = v0 / 3, col0 = v0 % 3, row1 = v1 / 3, col1 = v1 % 3;
        int c = (Math.abs(row0 - row1) + Math.abs(col0 - col1));
        cost += c; }
     return cost;
  }

  int hCost(Node node) { /* set to call either h1 or h2 */
     return h2Cost(node);
  }

Node selection

  Node getBestNode(Vector nodes) {
     int index = 0, minCost = Integer.MAX_VALUE;
     for (int i = 0; i < nodes.size(); i++) {
        Node node = (Node)nodes.elementAt(i);
        if (node.cost < minCost) {
           minCost = node.cost;
           index = i; } }
     Node bestNode = (Node)nodes.remove(index);
     return(bestNode);
  }
  Node getUniqueNode(Node node) {
     int i = open.indexOf(node);
     if (i != -1) {
        node = open.get(i); }
     else if ((i = closed.indexOf(node)) != -1) {
        node = closed.get(i); }
     return(node);
  }

run method

  void printPath(Vector path) {
     for (int i = 0; i < path.size(); i++) {
        System.out.print(" " + path.elementAt(i) + "\n"); }
  }
  void run() {
     Node root = space.getRoot();
     Node goal = space.getGoal();
     Node solution = null;
     open.add(root);
     System.out.print("\nRoot: " + root + "\n\n");

Main loop

  while (open.size() > 0) {
     Node node = getBestNode(open);
     int pathLength = node.getPath().size();
     closed.add(node);
     if (node.equals(goal)) { solution = node; break; }
     Vector<Node> successors = space.getSuccessors(node);
     for (int i = 0; i < successors.size(); i++) {
        Node successor = getUniqueNode(successors.get(i));
        int cost = hCost(successor) + pathLength + 1;
        int previousCost = successor.cost;
        boolean inClosed = closed.contains(successor);
        boolean inOpen = open.contains(successor);
        if (!(inClosed || inOpen)
        || cost < previousCost) {
           if (inClosed) closed.remove(successor);
           if (!inOpen) open.add(successor);
           successor.cost = cost;
           successor.parent = node;
        }
     }
  }

Solution printing

        // new TreePrint(getTree(root));
        if (solution != null) {
           Vector path = solution.getPath();
           System.out.print("\nSolution found\n");
           printPath(path); }
     }

     public static void main(String args[]) { // do the search
        new EightPuzzleSearch().run();
     }
  }

Search space explored

  Root: 3 1 2 4 7 5 6 8 0

   3 1 2 4 7 5 6 8 0
   |-- 3 1 2 4 7 0 6 8 5
   |-- 3 1 2 4 7 5 6 0 8
       |-- 3 1 2 4 0 5 6 7 8
       |   |-- 3 0 2 4 1 5 6 7 8
       |   |-- 3 1 2 4 7 5 6 0 8
       |   |-- 3 1 2 0 4 5 6 7 8
       |   |   |-- 0 1 2 3 4 5 6 7 8
       |   |   |-- 3 1 2 6 4 5 0 7 8
       |   |   |-- 3 1 2 4 0 5 6 7 8
       |   |-- 3 1 2 4 5 0 6 7 8
       |-- 3 1 2 4 7 5 0 6 8
       |-- 3 1 2 4 7 5 6 8 0

Solution path

  3 1 2 4 7 5 6 8 0
  3 1 2 4 7 5 6 0 8
  3 1 2 4 0 5 6 7 8
  3 1 2 0 4 5 6 7 8
  0 1 2 3 4 5 6 7 8
.

Summary

Questions

Exercises