<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
DefinesTeePuzzleSolver
,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
DefinesFrontier
,Frontier.java
(links are to index).
<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
DefinesPath
,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
DefinesTeePuzzleInstance
,TeePuzzleInstance.java
(links are to index).