-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy path6-concurrent-tvar-lee.rb
143 lines (108 loc) · 3.37 KB
/
6-concurrent-tvar-lee.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Solves a board using TVar actually on two uncontrolled concurrent threads.
require 'set'
require_relative 'lib/lee'
board_filename, output_filename, expansions_dir, *rest = ARGV
raise 'no input filename' unless board_filename
raise 'too many arguments' unless rest.empty?
if expansions_dir
system 'rm', '-rf', expansions_dir
Dir.mkdir expansions_dir
end
board = Lee.read_board(board_filename)
obstructed = Lee::Matrix.new(board.height, board.width)
board.pads.each do |pad|
obstructed[pad.y, pad.x] = 1
end
# Would be good if we could have TArray and TMatrix instead of a Matrix of TVars!
depth = Lee::Matrix.new(board.height, board.width) { Thread::TVar.new(0) }
def expand(board, obstructed, depth, route)
start_point = route.a
end_point = route.b
# From benchmarking - we're better of allocating a new cost-matrix each time rather than zeroing
cost = Lee::Matrix.new(board.height, board.width)
cost[start_point.y, start_point.x] = 1
wavefront = [start_point]
loop do
new_wavefront = []
wavefront.each do |point|
point_cost = cost[point.y, point.x]
Lee.adjacent(board, point).each do |adjacent|
next if obstructed[adjacent.y, adjacent.x] == 1 && adjacent != route.b
current_cost = cost[adjacent.y, adjacent.x]
new_cost = point_cost + Lee.cost(depth[adjacent.y, adjacent.x].value)
if current_cost == 0 || new_cost < current_cost
cost[adjacent.y, adjacent.x] = new_cost
new_wavefront.push adjacent
end
end
end
raise 'stuck' if new_wavefront.empty?
break if cost[end_point.y, end_point.x] > 0 && cost[end_point.y, end_point.x] < new_wavefront.map { |marked| cost[marked.y, marked.x] }.min
wavefront = new_wavefront
end
cost
end
def solve(board, route, cost)
start_point = route.b
end_point = route.a
solution = [start_point]
loop do
adjacent = Lee.adjacent(board, solution.last)
lowest_cost = adjacent
.reject { |a| cost[a.y, a.x].zero? }
.min_by { |a| cost[a.y, a.x] }
solution.push lowest_cost
break if lowest_cost == end_point
end
solution.reverse
end
def lay(depth, solution)
solution.each do |point|
depth[point.y, point.x].increment
end
end
worklist = Queue.new
board.routes.each do |route|
worklist.push route
end
solutions = {}
$committed = 0
$aborted = 0
class Thread
LOG_LOCK = Mutex.new
def self.committed
LOG_LOCK.synchronize do
$committed += 1
end
end
def self.aborted
LOG_LOCK.synchronize do
$aborted += 1
end
end
end
threads = 2.times.map {
Thread.new {
loop do
route = worklist.pop(non_block: true) rescue break
Thread.atomically do
cost = expand(board, obstructed, depth, route)
solution = solve(board, route, cost)
if expansions_dir
Lee.draw board, solutions.values, [[cost.keys, solution]], File.join(expansions_dir, "expansion-#{route.object_id}.svg")
end
lay depth, solution
solutions[route] = solution
end
end
}
}
threads.each &:join
raise 'invalid solution' unless Lee.solution_valid?(board, solutions)
cost, depth = Lee.cost_solutions(board, solutions)
puts "routes: #{board.routes.size}"
puts "committed: #{$committed}"
puts "aborted: #{$aborted}"
puts "cost: #{cost}"
puts "depth: #{depth}"
Lee.draw board, solutions.values, output_filename if output_filename