main
 1require "spec_helper"
 2require "ostruct"
 3
 4def dijkstra(graph, source, destination)
 5  heap = Heap.new
 6  heap.push(0, source)
 7  total_cost = 0
 8  until (heap.empty?) do
 9    top = heap.min
10    distance = graph.distance_between(source, top) || 0
11    total_cost += distance
12    p "#{source.name} -> #{top.name} (#{distance})"
13    return total_cost if top == destination
14    neighbors = graph.neighbors(top)
15    neighbors.each do |x|
16      heap.push(graph.distance_between(source, x) || 100, x)
17    end
18    source = top
19  end
20end
21
22describe "dijkstra" do
23  let(:a) { node(:name => "A") }
24  let(:b) { node(:name => "B") }
25  let(:c) { node(:name => "C") }
26  let(:d) { node(:name => "D") }
27  let(:e) { node(:name => "E") }
28  let(:graph) { Graph.new }
29
30  before :each do
31    graph.push(a)
32    graph.push(b)
33    graph.push(c)
34    graph.push(d)
35    graph.push(e)
36    graph.connect(a, b, 5)
37    graph.connect(a, d, 5)
38    graph.connect(a, e, 7)
39    graph.connect(b, c, 4)
40    graph.connect(c, d, 8)
41    graph.connect(c, e, 2)
42    graph.connect(d, c, 8)
43    graph.connect(d, e, 6)
44    graph.connect(e, b, 3)
45  end
46
47  it "A-B-C equals 9" do
48    expect(dijkstra(graph, a, c)).to eql(9)
49  end
50
51  it "A-D equals 5" do
52    expect(dijkstra(graph, a, d)).to eql(5)
53  end
54
55  def node(details)
56    OpenStruct.new(details)
57  end
58end
59
60class Edge
61  attr_reader :source, :destination, :distance
62
63  def initialize(source, destination, distance)
64    @source = source
65    @destination = destination
66    @distance = distance
67  end
68end
69
70class Graph < Array
71  attr_reader :edges
72
73  def initialize(edges = [])
74    @edges = []
75  end
76
77  def connect(source, destination, distance)
78    @edges.push(Edge.new(source, destination, distance))
79  end
80
81  def neighbors(vertex)
82    neighbors = []
83    @edges.each do |edge|
84      if edge.source == vertex
85        neighbors.push(edge.destination)
86      end
87    end
88    neighbors
89  end
90
91  def distance_between(source, destination)
92    @edges.each do |edge|
93      return edge.distance if edge.source == source and edge.destination == destination
94    end
95    nil
96  end
97end