Intelligent Systems, Spring 2015

Programming Assignment 1 - An Example Solution


Table of Contents

Introduction

This page presents a solution to the first programming assignment, which involves solving the tee puzzle using state-space graph search.

The Tee-Puzzle Solver

The tee-puzzle solver is a straightforward implementation of the generic search algorithm given in section 3.4 of Poole and Mackworth.
<TeePuzzleSolver.java>=
class TeePuzzleSolver
implements TeePuzzle

  public Jump []
  solve(int size, TeePuzzle.Coordinate holes[], StateGraph g)

    final Frontier frontier = 
      new Frontier(new Path(new TeePuzzleInstance(size, holes)))

    do
      final Path path = frontier.pick()
      final TeePuzzleInstance endState = path.endState()

      if endState.isGoalState()
        return path.asJumpArray()

      for TeePuzzleInstance neighbor : endState.neighbors()
        frontier.add(path.extend(neighbor))
    while (!frontier.isEmpty())

    return null
Defines TeePuzzleSolver, TeePuzzleSolver.java (links are to index).

The Frontier

Poole and Mackworth define a frontier as a set of paths, and there's no reason to contradict that definition.
<Frontier.java>=
import java.util.HashSet

class Frontier
extends HashSet<Path>

  /**
     Create a frontier containing a path.

     path: The path in the new frontier.
   */

  Frontier(Path path)
    add(path)


  /**
     Pick a path from this frontier.

     Returns:  The path picked from this frontier.  The path is removed from this
     frontier.
  
     @exception NoSuchElementException if this frontier is empty.
   */

  Path
  pick()
    final Path path = iterator().next()
    remove(path)
    return path
Defines Frontier, Frontier.java (links are to index).

Paths

<Path.java>=
class Path

  /**
     Find the array of jumps that created this path.

     Returns:  The array of jumps that created this path.
  */

  TeePuzzle.Jump []
  asJumpArray()

    final TeePuzzle.Jump jumps[] = new TeePuzzle.Jump [length - 1]
    
    for Path a = this; a.length > 1; a = a.ancestors
      jumps[a.length - 2] = a.state.creatorJump()

    return jumps


  /**
     Create a new path by adding a state to the end of this path.

     state: The state to be appended.
  
     Returns:  A new path that has this path as the prefix and the given state as
     the suffix.
  */

  Path
  extend(TeePuzzleInstance state)
    return new Path(this, state, length + 1)


  /**
     Find the end state on this path.

     Returns:  The end state on this path.
  */

  TeePuzzleInstance
  endState()
    return state


  /**
     Create a empty path.
  */

  Path(TeePuzzleInstance s)
    state = s
    ancestors = null
    length = 1



  private
  Path(Path a, TeePuzzleInstance s, int l)
    state = s
    ancestors = a
    length = l

  private final TeePuzzleInstance state
  private final Path ancestors
  private final int length
Defines Path, Path.java (links are to index).

Tee-Puzzle Instances

A straight-forward implementation of an instance of the tee puzzle.
<TeePuzzleInstance.java>=
import java.util.Set
import java.util.HashSet


class 
TeePuzzleInstance

  /**
     Add a peg to this tee-puzzle instance.

     peg: The peg to add.
   */

  private void
  add(TeePuzzle.Coordinate peg)

    assert (0 ≤ peg.x) : "0 <= peg.x = " + peg.x + " failed"
    assert (0 ≤ peg.y) : "0 <= peg.y = " + peg.y + " failed"
    assert peg.x + peg.y < holesPerSide 
      : peg.x + " + " + peg.y + " < " + holesPerSide + " failed"
    assert !pegs.contains(peg) : "!pegs.contains(" + peg + ") failed"

    pegs.add(peg)


  /**
     Find the move that created this tee-puzzle instance.

     Returns:  The move that created this tee-puzzle instance.
   */

  TeePuzzle.Jump
  creatorJump()
    return move


  /**
     Determine if this tee-puzzle instance is a solution.

     Returns:  true iff this tee-puzzle instance is a solution.
   */

  boolean
  isGoalState()
    return pegs.size() ≡ 1


  /**
     Find pegs suitable for jumping.

     jumpingPeg: The peg that will be doing the jumping.

     Returns:  The set of pegs adjacent to the given peg that are suitable for
     jumping by the given peg; that is, the adjacent pegs that have empty holes
     on the side opposite the jumping peg.
   */

  private Set<TeePuzzle.Coordinate>
  jumpablePegs(TeePuzzle.Coordinate jumpingPeg)

    final Set<TeePuzzle.Coordinate> jumpable = 
      new HashSet<TeePuzzle.Coordinate>()

    final int
      deltaXs [] = new int [] { -1, 0, -1, 1,  0,  1 },
      deltaYs [] = new int [] {  1, 1,  0, 0, -1, -1 }

    for int i = 0; i < deltaXs.length; ++i
      final int
        deltaX = deltaXs[i],
        deltaY = deltaYs[i]

      final TeePuzzle.Coordinate jumped = 
        new TeePuzzle.Coordinate(jumpingPeg.x + deltaX, jumpingPeg.y + deltaY)
      if pegs.contains(jumped)
        final int
          landingX = jumpingPeg.x + 2*deltaX,
          landingY = jumpingPeg.y + 2*deltaY
        if    (0 ≤ landingX
            ∧ (0 ≤ landingY) 
            ∧ (landingX + landingY < holesPerSide))
          if !pegs.contains(new TeePuzzle.Coordinate(landingX, landingY))
            jumpable.add(jumped)

    return jumpable


  /**
     Make a move in this tee-puzzle instance.

     jumpingPeg: The peg making the jump.

     jumpedPeg: The peg being jumped.

     Returns:  The tee-puzzle instance resulting fron the move.
   */

  private TeePuzzleInstance 
  jumpPeg(TeePuzzle.Coordinate jumpingPeg, TeePuzzle.Coordinate jumpedPeg)
    return new TeePuzzleInstance(this, jumpingPeg, jumpedPeg)


  /**
     Find this tee-puzzle instance's children.

     Returns:  The set of tee-puzzle instances that result from legal moves made
     in this instance.
   */

  Set<TeePuzzleInstance>
  neighbors()

    final Set<TeePuzzleInstance> children = new HashSet<TeePuzzleInstance>()

    for TeePuzzle.Coordinate jumping : pegs
      for TeePuzzle.Coordinate jumped : jumpablePegs(jumping)
        children.add(jumpPeg(jumping, jumped))

    return children


  /**
     Remove a peg from this tee-puzzle instance.

     peg: The peg to remove.
   */

  private void
  remove(TeePuzzle.Coordinate peg)

    assert pegs.contains(peg) : "pegs.contains(" + peg + ") failed"

    pegs.remove(peg)


  /**
     Create a new tee-puzzle instance.

     size: The number of holes on a side of the puzzle.

     holes: Which holes in the puzzle are empty; all others are assumed
     to contain pegs.
   */

  TeePuzzleInstance(int size, TeePuzzle.Coordinate holes[])

    assert size > 0 : size + " > 0 failed"

    move = null
    holesPerSide = size
    pegs = new HashSet<TeePuzzle.Coordinate>()
    for int x = 0; x < size; ++x
      for int y = 0 ; y < size - x; ++y
        add(new TeePuzzle.Coordinate(x, y))
    
    for TeePuzzle.Coordinate hole : holes
      remove(hole)


  /**
     Create a new tee-puzzle instance by making a move on an old tee-puzzle
     instance.

     parent: The tee-puzzle instance within which the jump is being made.

     jumpingPeg: The peg that is doing the jumping.

     jumpedPeg: The peg that is being jumped and removed from the puzzle.
   */

  private                          
  TeePuzzleInstance(
    TeePuzzleInstance parent, 
    TeePuzzle.Coordinate jumpingPeg,
    TeePuzzle.Coordinate jumpedPeg)

    holesPerSide = parent.holesPerSide
    pegs = new HashSet<TeePuzzle.Coordinate>(parent.pegs)

    final TeePuzzle.Coordinate newPeg = 
      new TeePuzzle.Coordinate(
        jumpedPeg.x + (jumpedPeg.x - jumpingPeg.x),
        jumpedPeg.y + (jumpedPeg.y - jumpingPeg.y))

    move = new TeePuzzle.Jump(jumpingPeg, newPeg)

    remove(jumpingPeg)
    remove(jumpedPeg)
    add(newPeg)


  @Override
  public String
  toString()
    final StringBuffer stringBuffer = new StringBuffer()

    for int y = holesPerSide - 1; -1 < y ; --y
      for int i = 0; i < y; ++i
        stringBuffer.append(" ")
      for int x = 0; x < holesPerSide - y; ++x
        final TeePuzzle.Coordinate hole = new TeePuzzle.Coordinate(x, y)
        stringBuffer.append(pegs.contains(hole) ? " *" : " o")
      stringBuffer.append("\n")

    return stringBuffer.toString()


  /**
     The move that created this tee-puzzle instance.
   */

  private final TeePuzzle.Jump move


  /**
     The tee-puzzle instance size.
   */

  private final int holesPerSide


  /**
     The holes in this tee-puzzle instance that contain pegs.
   */

  private final Set<TeePuzzle.Coordinate> pegs
Defines TeePuzzleInstance, TeePuzzleInstance.java (links are to index).

Index


This page last modified on 2012 March 17.