-
Notifications
You must be signed in to change notification settings - Fork 2
/
13_maze.rb
73 lines (62 loc) · 1.58 KB
/
13_maze.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
require_relative 'lib/search'
def has_flag?(flag)
ARGV.any? { |a| a.start_with?(?-) && a.include?(flag) }
end
INPUT = Integer(ARGV.find { |x| !x.start_with?(?-) } || ARGF.read)
def ones(n)
ones = 0
while n > 0
n &= n - 1
ones += 1
end
ones
end
def wall?(x, y)
ones(x * x + 3 * x + 2 * x * y + y + y * y + INPUT) % 2 == 1
end
def adjacents((x, y))
[
[x - 1, y],
[x + 1, y],
[x, y - 1],
[x, y + 1],
].select { |nx, ny| nx >= 0 && ny >= 0 && !wall?(nx, ny) }.map(&:freeze)
end
GOAL = if (garg = ARGV.find { |a| a.start_with?('-g') })
ARGV.delete(garg)
garg[2..-1].split(?,, 2).map(&method(:Integer))
else
[31, 39]
end.freeze
results = Search.bfs(
[1, 1],
neighbours: ->pos { adjacents(pos) },
goal: ->pos { pos == GOAL },
)
goal_dist = results[:goals][GOAL]
puts goal_dist
dists = if goal_dist > 50
# We can use the existing search.
results[:dist]
else
# We have to do another search.
# Search.bfs doesn't have a "count nodes at distance or less",
# but we can upper-bound it by the number of reachable tiles without walls.
# (1..50).sum to the lower-right:
# 50 tiles if we go down all the way, 49 if we go right 1 down 49, etc.
# 49 to the lower-left
# 49 to the upper-right
# 1 to the upper-left
Search.bfs(
[1, 1],
num_goals: (1..50).sum + 49 + 49 + 1,
neighbours: ->pos { adjacents(pos) },
goal: ->_ { true },
)[:dist]
end
puts dists.values.count { |steps| steps <= 50 }
(0..50).each { |y|
puts (0..50).map { |x|
wall?(x, y) ? ?# : dist[[x, y]] && dist[[x, y]] <= 50 ? ?O : ' '
}.join
} if has_flag?(?f)