-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathknight_moves.rb
103 lines (86 loc) · 2.08 KB
/
knight_moves.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
class Space
attr_reader :coords
attr_accessor :parent, :children
def initialize(coords)
@coords = coords
@parent = nil
@children = []
end
end
class Board
def self.on_board?(coords)
y, x = coords
y.between?(0, 7) && x.between?(0, 7)
end
end
class KnightMoves
MOVES = [[2, 1], [2, -1], [1, 2], [1, -2], [-2, 1], [-2, -1], [-1, 2], [-1, -2]].freeze
def initialize(start)
@root = Space.new(start)
@target = nil
@target_node = nil
end
def find_path(target)
@target = target
build_tree_to_target
p dfs_path(@root)
p target_to_root_path
end
private
def next_moves(coords)
y, x = coords
MOVES.each_with_object([]) do |adds, moves|
move = [adds[0] + y, adds[1] + x]
moves << move if Board.on_board?(move)
end
end
def make_children(node)
parent_coords = node.coords
child_coords = next_moves(parent_coords)
child_coords.each do |coords|
next if parent_coords == coords
child = Space.new(coords)
child.parent = node
node.children << child
end
end
def uniq_children(node, visited)
node.children.reject { |child| visited.include?(child.coords) }
end
# Build tree breadth_first, stop at target
def build_tree_to_target
node = @root
queue = []
visited = []
until node.coords == @target
visited << node.coords
make_children(node)
queue.concat(uniq_children(node, visited))
node = queue.shift
end
@target_node = node
end
# Since BFS was used to build tree to target, DFS will find shortest path
def dfs_path(node, path = [])
coords = node.coords
path << coords
return path if coords == @target
node.children.each do |child|
res = dfs_path(child, path.dup)
return res unless res.nil?
end
nil
end
# Or just plot from target_node to root
def target_to_root_path
node = @target_node
path = [node.coords]
until node.parent.nil?
node = node.parent
path.unshift(node.coords)
end
path
end
end
knight = KnightMoves.new([0, 0])
knight.find_path([7, 7])