Skip to content
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,52 @@
Marco? Polo!

[A data structures and algorithms Ruby challenge from the Viking Code School](http://www.vikingcodeschool.com)


What data structure is used to implement DFS?
Stack
What data structure is typically used to implement BFS?
Queue
Which one can be done recursively? (the clue should be the data structure)
DFS
Which one would you use to print a list of all the nodes in a tree or graph, starting with depth 1, then depth 2, then depth 3 etc.? BFS
What is the difference between a tree and a graph? Nodes in a tree can only have one parent, whereas nodes can have unlimited parents and children( though they're not called that in graphs
)
Next, pseudocode the following processes with enough detail to be clear:

Searching a simple tree of nodes where each Node has an array of child nodes (some_node.children) using DFS.

- Put Root in Stack
- Until Root does not have a left or right child (depending on your criteria)
- Check for match
- Put Root's First Child (left or right, or whatever your criteria is) in Stack
- Check for match (this is the end of a branch)

Searching the same tree using BFS.
- Put Root in Queue
- Put Root's Children in Queue
- Check root for match
- Check First Child for match
- Put First Child's children in queue
- Repeat with other children
- Repeat until match is found or there are no children left to search


Searching a graph (represented however you feel most comfortable -- Edge List, Adjacency List or Adjacency Matrix) using DFS.
- Start at first vertex
- Compare value with each link of that vertex
- If not found, move on to next vertex
- Continue until match is found or you've exhausted the vertices



Searching the same graph using BFS.
- You look at the first vertex, add it's children to queue, then pop off the front of the list and compare.
- Keep repeating until you've found your match or gone through every possibility







93 changes: 93 additions & 0 deletions knight.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#psuedocode knights
# start tree(root) at whatever position
# iterate through possible nexst moves and add those as children to root
# => move qualifies if position (x,y) + (+-2, +-1) or (+-1, +-2) > -1 && < 5
# => add next qualifying move to be child of that node if node hasn't been visitited
# => continue process until no more moves
# => if no moves available, go to starting position's next child node (DFS)
require 'pry'

Move = Struct.new(:x, :y, :depth, :children, :parent)
class Move

def to_s
"#{x}, #{y}:\n #{children}"
end

def inspect
"#{x}, #{y}:\n #{children}"
end

end

class MoveTree

attr_reader :coordinates, :depth, :root
def initialize(coordinates, depth)
starting_point = Move.new(coordinates[0], coordinates[1], 0, [], [])
@max_depth = depth
@root = starting_point
@directions = [[2,1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2]]
@visited_coordinates = {}
build_tree
# @root.children[0].children[0].children[0].children.each do |child|
# puts "#{child.x}, #{child.y}\n"
puts @root
end


def build_tree
current_node = @root
stack_arr = []
stack_arr.push(current_node)
# until stack_arr.empty?
while current_node = stack_arr.shift
populate(current_node)
current_node.children.each do |child|
stack_arr.push(child)
end
end

end

def populate(current_node)
possible_moves = find_next_move(current_node)
return false if possible_moves.empty?
# binding.pry
possible_moves.each do |move|
current_node.children << move
end
end

def find_next_move(current_node)
possible_moves = []
next_coordinates(current_node).each do |next_x, next_y|
if valid_move?(next_x, next_y) && not_visited?(next_x, next_y)
possible_moves << Move.new(next_x, next_y, current_node.depth + 1, [], current_node)
@visited_coordinates[[next_x, next_y]] = "Hooray!"
end
end
possible_moves
end

def next_coordinates(current_node)
@directions.map {|x,y| [current_node.x + x, current_node.y + y]}
end

def not_visited?(next_x, next_y)
!@visited_coordinates.include?([next_x, next_y])

end


def valid_move?(next_x, next_y)
next_x >= 0 && next_x < 5 && next_y >= 0 && next_y < 5
end



end


t = MoveTree.new([2, 2], 4)

88 changes: 88 additions & 0 deletions knight2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#this knight just moves to a certain depth, instead of optimizing

Move = Struct.new(:x, :y, :depth, :children, :parent)
# class Move

# def to_s
# if children.size > 0
# "#{x}, #{y}:\n #{children}"
# else
# "#{x}, #{y}\n"
# end
# end

# def inspect
# if children.size > 0
# "#{x}, #{y}\n"
# else
# "#{x}, #{y}\n"
# end
# end

# end

class MoveTree

attr_reader :coordinates, :depth, :root, :max_depth
def initialize(coordinates, depth)
starting_point = Move.new(coordinates[0], coordinates[1], 0, [], [])
@max_depth = depth
@root = starting_point
@directions = [[2,1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2]]
@visited_coordinates = {}
build_tree
end


def build_tree
current_node = @root
stack_arr = []
stack_arr.push(current_node)
while current_node = stack_arr.shift
populate(current_node)
current_node.children.each do |child|
stack_arr.push(child)
end
end

end

def populate(current_node)
possible_moves = find_next_move(current_node)
return false if possible_moves.empty?
# binding.pry
possible_moves.each do |move|
current_node.children << move
end
end

def find_next_move(current_node)
possible_moves = []
next_coordinates(current_node).each do |next_x, next_y|
if valid_move?(next_x, next_y) && current_node.depth < @max_depth
possible_moves << Move.new(next_x, next_y, current_node.depth + 1, [], current_node)
@visited_coordinates[[next_x, next_y]] = "Hooray!"
end
end
possible_moves
end

def next_coordinates(current_node)
@directions.map {|x,y| [current_node.x + x, current_node.y + y]}
end

# def not_visited?(next_x, next_y)
# !@visited_coordinates.include?([next_x, next_y])

# end


def valid_move?(next_x, next_y)
next_x >= 0 && next_x < 8 && next_y >= 0 && next_y < 8
end



end

t = MoveTree.new([2, 2], 4)
138 changes: 138 additions & 0 deletions knight_searcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
require_relative 'knight2'

class KnightSearcher
require 'pry'

def initialize(tree)
@t = tree
end

def bfs_for(target_coords)
@x = target_coords[0]
@y = target_coords[1]
moves = []
no_find = 0
queue = [@t.root]
until queue[0].x == @x && queue[0].y == @y
queue[0].children.each do |child|
queue.push(child)
end
moves << [queue[0].x, queue[0].y]
queue.shift
if queue.empty?
no_find += 1
return [moves.size, @t.max_depth, no_find]
end
end
moves << [queue[0].x, queue[0].y]
# print_results(moves, queue[0].depth)
[moves.size, queue[0].depth, no_find]
end


def dfs_for(target_coords)
@x = target_coords[0]
@y = target_coords[1]
moves = []
stack = [@t.root]
# binding.pry
while stack.length > 0
if stack.last.x == @x && stack.last.y == @y
return [moves.size, stack.last.depth, 0]
end
moves << [stack.last.x, stack.last.y]
if stack.last.children.size > 0
stack.pop.children.each do |child|
stack.push(child)
end
else
stack.pop
end
end
[moves.size, @t.max_depth, 1]
end




# old and wrong DFS
# until (stack.last.x == @x && stack.last.y == @y)
# moves << [stack.last.x, stack.last.y]
# if stack.last.children.size > 0
# stack.last.children.each do |child|
# stack.push(child)
# end
# end
# stack.pop
# if stack.empty?
# no_find += 1
# return [moves.size, @t.max_depth, no_find]
# else
# until moves.include?([stack.last.x, stack.last.y]) == false
# stack.pop
# if stack.empty?
# no_find += 1
# return [moves.size, @t.max_depth, no_find]
# end
# end
# end
# end
# moves << [stack.last.x, stack.last.y]
# # print_results(moves, stack.last.depth)
# [moves.size, stack.last.depth, no_find]

# end

def print_results(moves, depth)
puts "#{moves.size} moves:"
moves.each do |move|
print "#{move}\n"
end
puts "Depth of result: #{depth}"
puts
end

def bfs_benchmark
results = []
1000.times do
results << bfs_for([rand(7), rand(7)])
end
total_moves = 0
total_depth = 0
no_finds = 0
results.each do |search|
total_moves += search[0]
total_depth += search[1]
no_finds += search[2]
end
puts "Average moves for BFS: #{total_moves/1000.00}"
puts "Average depth for BFS: #{total_depth/1000.00}"
puts "Number of searches returning no result: #{no_finds}"
end

def dfs_benchmark
results = []
1000.times do
results << dfs_for([rand(7),rand(7)])
end
total_moves = 0
total_depth = 0
no_finds = 0
results.each do |search|
total_moves += search[0]
total_depth += search[1]
no_finds += search[2]
end
puts "Average moves for DFS: #{total_moves/1000.00}"
puts "Average depth for DFS: #{total_depth/1000.00}"
puts "Number of searches returning no result: #{no_finds}"
end


end

t = MoveTree.new([4, 4], 4)
k = KnightSearcher.new(t)

puts k.dfs_benchmark
puts k.bfs_benchmark