The state (S) is a square grid, modeled as a one-dimensional array of Tile.

read from left to right then top to bottom.

An action (A) is the relative position of the tile to swap with the hole. Therefore, -1 for left, 1 for right, -width for up and width for down.

Introduced properties

init defaultinit(initial_grid: String)

ai :: PuzzleProblem :: defaultinit

fun get_hole(state: Array[Tile]): Int

ai :: PuzzleProblem :: get_hole

What is the position of the hole?
fun hole: Tile

ai :: PuzzleProblem :: hole

The special empty tile for fast retrieval.
protected fun hole=(hole: Tile)

ai :: PuzzleProblem :: hole=

The special empty tile for fast retrieval.
fun initial_grid: String

ai :: PuzzleProblem :: initial_grid

The initial grid. use letters for tiles, and . for the hole.
protected fun initial_grid=(initial_grid: String)

ai :: PuzzleProblem :: initial_grid=

The initial grid. use letters for tiles, and . for the hole.
fun print_plan(plan: Sequence[Int])

ai :: PuzzleProblem :: print_plan

Print the plan with words.
fun print_state(state: Array[Tile])

ai :: PuzzleProblem :: print_state

Print the grid
fun width: Int

ai :: PuzzleProblem :: width

The width of the grid.
protected fun width=(width: Int)

ai :: PuzzleProblem :: width=

The width of the grid.

Redefined properties

redef type SELF: PuzzleProblem

ai $ PuzzleProblem :: SELF

Type of this instance, automatically specialized in every class
redef fun actions(state: ArrayCmp[Tile]): nullable SequenceRead[Int]

ai $ PuzzleProblem :: actions

Get the four available movements, or 3 on a edge, or 2 in a corner.
redef fun apply_action(state: ArrayCmp[Tile], action: Int): ArrayCmp[Tile]

ai $ PuzzleProblem :: apply_action

Return the state where the tile at hole+action has moved
redef fun heuristic(state: ArrayCmp[Tile]): Float

ai $ PuzzleProblem :: heuristic

The sum of the Manhattan distances of each tile to its goal
redef fun initial_state: ArrayCmp[Tile]

ai $ PuzzleProblem :: initial_state

Construct a state form initial_grid
redef fun is_goal(state: ArrayCmp[Tile]): Bool

ai $ PuzzleProblem :: is_goal

Each tile is at its correct position
redef fun make_memory: Array[Map[ArrayCmp[Tile], SearchNode[ArrayCmp[Tile], Int]]]

ai $ PuzzleProblem :: make_memory

Various Map implementations of memory.

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
abstract fun actions(state: S): nullable SequenceRead[A]

ai :: SearchProblem :: actions

The available applicable actions for a given state.
abstract fun apply_action(state: S, action: A): S

ai :: SearchProblem :: apply_action

The new state when applying a given action
fun astar: SearchSolver[S, A]

ai :: SearchProblem :: astar

return a new best-first solver
fun breadth_first: SearchSolver[S, A]

ai :: SearchProblem :: breadth_first

return a new breadth-first solver
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun cost(state: S, action: A, state2: S): Float

ai :: SearchProblem :: cost

The cost for action from old_state to new_state
init defaultinit(initial_grid: String)

ai :: PuzzleProblem :: defaultinit

fun depth_first: SearchSolver[S, A]

ai :: SearchProblem :: depth_first

return a new depth-first solver
fun epsilon: Float

ai :: SearchProblem :: epsilon

Negligible quantity for float comparisons
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun get_hole(state: Array[Tile]): Int

ai :: PuzzleProblem :: get_hole

What is the position of the hole?
fun hash: Int

core :: Object :: hash

The hash code of the object.
fun heuristic(state: S): Float

ai :: SearchProblem :: heuristic

An heuristic of the estimated cost going from state to a goal state.
fun hole: Tile

ai :: PuzzleProblem :: hole

The special empty tile for fast retrieval.
protected fun hole=(hole: Tile)

ai :: PuzzleProblem :: hole=

The special empty tile for fast retrieval.
init init

core :: Object :: init

fun initial_grid: String

ai :: PuzzleProblem :: initial_grid

The initial grid. use letters for tiles, and . for the hole.
protected fun initial_grid=(initial_grid: String)

ai :: PuzzleProblem :: initial_grid=

The initial grid. use letters for tiles, and . for the hole.
fun initial_node: SearchNode[S, A]

ai :: SearchProblem :: initial_node

Create the initial node in the search-tree.
abstract fun initial_state: S

ai :: SearchProblem :: initial_state

The starting state for the problem
fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
fun is_goal(state: S): Bool

ai :: SearchProblem :: is_goal

Is the state a goal state?
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
fun make_memory: Array[Map[S, SearchNode[S, A]]]

ai :: SearchProblem :: make_memory

Various Map implementations of memory.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun print_plan(plan: Sequence[Int])

ai :: PuzzleProblem :: print_plan

Print the plan with words.
fun print_state(state: Array[Tile])

ai :: PuzzleProblem :: print_state

Print the grid
fun run_configs(steps: Int)

ai :: SearchProblem :: run_configs

Run and evaluate solvers with multiple configuration.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
fun width: Int

ai :: PuzzleProblem :: width

The width of the grid.
protected fun width=(width: Int)

ai :: PuzzleProblem :: width=

The width of the grid.
package_diagram ai::PuzzleProblem PuzzleProblem ai::SearchProblem SearchProblem ai::PuzzleProblem->ai::SearchProblem core::Object Object ai::SearchProblem->core::Object ...core::Object ... ...core::Object->core::Object

Ancestors

interface Object

core :: Object

The root of the class hierarchy.

Parents

interface SearchProblem[S: Object, A: nullable Object]

ai :: SearchProblem

Abstract search problem over immutable states (S) and actions (A).

Class definitions

ai $ PuzzleProblem
# The state (`S`) is a square grid, modeled as a one-dimensional array of Tile.
# read from left to right then top to bottom.
#
# An action (`A`) is the relative position of the tile to swap with the hole.
# Therefore, `-1` for left, `1` for right, `-width` for up and `width` for down.
class PuzzleProblem
	super SearchProblem[ArrayCmp[Tile], Int]

	# The initial grid. use letters for tiles, and . for the hole.
	var initial_grid: String

	# The width of the grid.
	# Eg. 3 for a 8-puzzle grid
	var width: Int is noinit

	# Construct a state form `initial_grid`
	redef fun initial_state do
		var g = initial_grid
		var len = g.length
		var width = len.sqrt.to_i
		self.width = width
		if width * width != len then
			print "Error: {g} has {len} tiles. A square number, like {width*width} is needed"
			exit 1
		end
		var res = new ArrayCmp[Tile]
		for i in [0..g.length[ do
			var c = g.chars[i]
			if c == ' ' or c == '.' then
				var hole = new Tile('.', -1)
				self.hole = hole
				res.add hole
			else if c >= '1' and c <= '9' then
				var t = new Tile(c, '1'.distance(c))
				res.add t
			else if c >= 'a' and c <= 'z' then
				var t = new Tile(c, 'a'.distance(c))
				res.add t
			else if c >= 'A' and c <= 'Z' then
				var t = new Tile(c, 'A'.distance(c))
				res.add t
			else
				print "Error: illegal tile {c} in {g}"
				exit 1
			end
		end
		return res
	end

	# Get the four available movements, or 3 on a edge, or 2 in a corner.
	redef fun actions(state)
	do
		var h = get_hole(state)
		var x = h % width
		var y = h / width
		var res = new Array[Int]
		if x >= 1 then res.add(-1)
		if x < width-1 then res.add(1)
		if y >= 1 then res.add(-width)
		if y < width-1 then res.add(width)
		return res
	end

	# Return the state where the tile at hole+action has moved
	redef fun apply_action(state, action)
	do
		# Copy the state
		var res = new ArrayCmp[Tile].with_capacity(state.length)
		res.add_all(state)

		# Get the hole and the tile next to it
		var h = get_hole(res)
		var t = h + action

		# Move (by swapping the tile and the hole)
		res[h] = res[t]
		res[t] = hole

		return res
	end

	# The special empty tile for fast retrieval.
	var hole: Tile is noinit

	# What is the position of the hole?
	fun get_hole(state: Array[Tile]): Int
	do
		return state.index_of(hole)
	end

	# Each tile is at its correct position
	redef fun is_goal(state)
	do
		for i in [0..state.length[ do
			var t = state[i]
			if t != hole and t.goal_idx != i then return false
		end
		return true
	end

	# The sum of the Manhattan distances of each tile to its goal
	# Not the best heuristic but the simplest to implement among the good ones.
	redef fun heuristic(state)
	do
		var p = 0
		var i = -1
		for t in state do
			i += 1

			# The hole does not count
			if t == hole then continue

			var dx = (i % width - t.goal_idx % width).abs
			var dy = (i / width - t.goal_idx / width).abs

			# Add Manhattan distance
			p += dx + dy
		end
		return p.to_f
	end

	# Print the grid
	fun print_state(state: Array[Tile])
	do
		for i in [0..state.length[ do
			var t = state[i]
			if t == hole then
				printn "."
			else
				printn t.symbol
			end
			if (i+1) % width == 0 then print ""
		end
	end

	# Print the plan with words.
	fun print_plan(plan: Sequence[Int])
	do
		var s = new Array[String]
		for i in plan do
			if i == -1 then
				s.add "right(>)"
			else if i == 1 then
				s.add "left(<)"
			else if i == -width then
				s.add "down(v)"
			else if i == width then
				s.add "up(^)"
			else
				abort
			end
		end
		print "Solution in {plan.length} moves: {s.join(" ")}"
	end

	redef fun make_memory do
		var res = super
		res.add new RBTreeMap[ArrayCmp[Tile], SearchNode[ArrayCmp[Tile], Int]]
		res.add new BinTreeMap[ArrayCmp[Tile], SearchNode[ArrayCmp[Tile], Int]]
		return res
	end
end
lib/ai/examples/puzzle.nit:48,1--209,3