Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--color
--require spec_helper
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source 'https://rubygems.org'

ruby '2.3.0'

gem 'rspec', '~> 3.5.0'
29 changes: 29 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.3)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)

PLATFORMS
ruby

DEPENDENCIES
rspec (~> 3.5.0)

RUBY VERSION
ruby 2.3.0p0

BUNDLED WITH
1.14.6
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,109 @@
Marco? Polo!

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

### Sean Luckett

## Warmup questions

1. What data structure is used to implement DFS? ***A stack***
2. What data structure is typically used to implement BFS? ***A FIFO queue***
3. Which one can be done recursively? ***DFS***
4. 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***
5. What is the difference between a tree and a graph? ***Trees' data are more structured. Also, trees grow from the ground; Graphs are a mathematical concept.***

## Pseudo Code

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

```bash
Start with root node, putting it on the stack

while stack is not empty
pop top node
if node == target return it (done)
else
find "best" child (let's say left for this)
push all other children to stack first
push best child to stack

return not found
```

***
2. Searching the same tree using BFS.
***

```bash
Start with root node, putting it in the queue

while queue is not empty
pop first node
if node == target return it (done)
else push children to queue

return not found
```

***
3. Searching a graph (represented however you feel most comfortable -- Edge List, Adjacency List or Adjacency Matrix) using DFS.
***

```bash
# using adj list (weighted) (implemented as array of linked lists)

Starting with first position in array with a linked list

Evaluate edge weights and push them to stack in descending order

While stack is not empty
pop top node
if visited, next
if node == target, return it (or its edge weight)--done
mark node as visited

if list at node's location in array, get list
Evaluate edge weights and push them to stack with in descending order

return not found
```

***
4. Searching the same graph using BFS.
***

```bash
# using adj list (weighted) (implemented as array of linked lists)
Starting with first position in array with a linked list

With each node in the list, push node to queue

While queue is not empty
pop first node
if visited, next
if node == target, return it (done)
mark node as visited

if list at node's location in array, get list
push nodes to queue

return not found
```

***
Knight's Travails pseudocode
***
```bash
# Using tree whose root node is starting position

Search tree from root node to solution if exists (BFS and DFS)
Each node checked gets a visited count
To prevent infinite search use combo of:
queue/stack emptiness
visited count not to exceed some number (probably 1 or 2)
if not in visited collection, put it in
return move collection or none if tree depth isn't sufficient
```
55 changes: 55 additions & 0 deletions lib/knight_searcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require_relative '../lib/move_tree'

class KnightSearcher
def initialize(move_tree)
@moves = move_tree
@starting_coords = [
@moves.starting_move.x,
@moves.starting_move.y
]
end

def bfs_for(target_coordinates)
moves_queue = [@moves.starting_move]

while move = moves_queue.shift
break if [move.x, move.y] == target_coordinates
move.children.each { |c| moves_queue << c } if move.children
end

results(move)
end

def dfs_for(target_coordinates)
moves_stack = [@moves.starting_move]

while move = moves_stack.pop
break if [move.x, move.y] == target_coordinates
move.children.each { |c| moves_stack << c } if move.children
end

results(move)
end

private

def results(move)
path = [[move.x, move.y]]
while parent = move.parent
path << [parent.x, parent.y]
break if [parent.x, parent.y] == @starting_coordinates
move = parent
end

results_message(path)
end

def results_message(moves_path)
results = "#{moves_path.size - 1} Moves:\n"
moves_path.reverse.each do |coords|
results += "#{coords}\n"
end

results
end
end
65 changes: 65 additions & 0 deletions lib/move_tree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
class MoveTree
attr_reader :starting_move

def initialize(start, depth)
x, y = *start
@max_depth = depth
@starting_move = Move.new(x, y, 0)
create_moves_tree
end

def inspect
"Your tree has #{@move_count} Move nodes and a max depth of #{@max_depth}."
end

private

def add_children(move, moves_queue)
move.children.each do |c|
if c.depth < @max_depth
moves_queue << c
end
end
end

def create_child_moves(parent)
board = (0..7)

candidate_moves(parent).select do |move|
board.include?(move.x) && board.include?(move.y)
end
end

def create_moves_tree
moves_queue = [@starting_move]
@move_count = 1

until moves_queue.empty?
move = moves_queue.shift
move.children = create_child_moves(move)
@move_count += move.children.size

add_children(move, moves_queue)
end
end

def candidate_moves(parent)
x = parent.x
y = parent.y
depth = parent.depth + 1

[
Move.new(x + 1, y + 2, depth, parent),
Move.new(x - 1, y + 2, depth, parent),
Move.new(x + 1, y - 2, depth, parent),
Move.new(x - 1, y - 2, depth, parent),
Move.new(x + 2, y + 1, depth, parent),
Move.new(x - 2, y + 1, depth, parent),
Move.new(x + 2, y - 1, depth, parent),
Move.new(x - 2, y - 1, depth, parent)
]
end

end

Move = Struct.new(:x, :y, :depth, :parent, :children, :visited)
24 changes: 24 additions & 0 deletions spec/knight_searcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require_relative '../lib/knight_searcher'

RSpec.describe KnightSearcher do
let(:move_tree) { MoveTree.new([0, 0], 2)}
let(:kniggit) { KnightSearcher.new(move_tree) }

describe '#bfs_for' do
let(:target_square) { [3, 3] }

it 'returns the depth of the final move and moves to get there' do
expect(kniggit.bfs_for(target_square))
.to eq "2 Moves:\n[0, 0]\n[1, 2]\n[3, 3]\n"
end
end

describe '#dfs_for' do
let(:target_square) { [3, 3] }

it 'returns the depth of the final move and moves to get there' do
expect(kniggit.dfs_for(target_square))
.to eq "2 Moves:\n[0, 0]\n[2, 1]\n[3, 3]\n"
end
end
end
66 changes: 66 additions & 0 deletions spec/move_tree_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require_relative '../lib/move_tree'

RSpec.describe MoveTree do
context 'Starting position is (0,0)' do
let(:starting_pt) { [0, 0] }

context 'depth is 1' do
let(:tree) { MoveTree.new(starting_pt, 1) }

it 'has a root node' do
expect(tree.starting_move.x).to eq 0
expect(tree.starting_move.y).to eq 0
expect(tree.starting_move.depth).to eq 0
end

it 'has 2 children' do
children = tree.starting_move.children
expect(children.size).to eq 2

move1, move2 = *children
expect([move1.x, move1.y]).to match [1, 2]
expect([move2.x, move2.y]).to match [2, 1]
end
end

context 'depth is 2' do
let(:tree) { MoveTree.new(starting_pt, 2) }

it 'sets depth of root children to 1' do
children = tree.starting_move.children
expect(children.size).to eq 2
expect(children.map(&:depth)).to eq [1, 1]
end

it 'sets the depth of the root children children to 2' do
root_children = tree.starting_move.children
move1, move2 = *root_children

move1.children.each do |c|
expect(c.depth).to eq 2
expect(c.children).to be_nil
end

move2.children.each do |c|
expect(c.depth).to eq 2
expect(c.children).to be_nil
end
end

it 'has a root with children who have children' do
root_children = tree.starting_move.children
move1, move2 = *root_children

expect(move1.children.size).to eq 6
expect(move2.children.size).to eq 6
end
end
end

describe '#inspect' do
it 'returns info about the tree' do
tree = MoveTree.new([0,0], 2)
expect(tree.inspect).to eq 'Your tree has 15 Move nodes and a max depth of 2.'
end
end
end
Loading