Last active
November 18, 2019 18:00
-
-
Save zerowidth/89396097a0f7e023f37b0c48c18b4194 to your computer and use it in GitHub Desktop.
Calculations for siting a Minecraft portal in the nether
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "set" | |
STDERR.sync | |
exclusion = 8 # one nether block distance, or 8 overworld | |
existing = [[-743, 63, -39], [-743, 63, -40]] | |
targets = [[-791, 152, -48], [-792, 152, -48], [-793, 152, -48]] | |
existing_nether = [[-87, 67, -4]] | |
search_from = existing_nether.first | |
# need to find minimal x, y, z in the nether that preferentially chooses the | |
# target portal instead of the existing overworld portal blocks | |
def euclidean(from, to) | |
Math.sqrt(from.zip(to).map { |a,b| (a - b)**2 }.sum) | |
end | |
def over(point) | |
x, y, z = *point | |
[8 * x, y, 8 * z] | |
end | |
def nether(point) | |
x, y, z = *point | |
[x / 8, y, z / 8] | |
end | |
def in_search_range?(target, point) | |
(target[0] - point[0]).abs <= 127 && (target[1] - point[1]).abs <= 127 | |
end | |
# search nearby portal locations in the nether in order to: | |
# * minimize distance from the existing nether portal | |
# * while still exiting to the overworld in preference to the target instead of | |
# the existing portal blocks. | |
range = 48 # how far out to search, this doesn't need to ever be above 127 | |
best = Set.new | |
(-range).upto(range).each do |ox| | |
x = search_from[0] + ox | |
(-range).upto(range).each do |oz| | |
z = search_from[2] + oz | |
121.downto(6).each do |y| # clamped to ignore bedrock above/below | |
# starting at a test point in the nether: is this a location that will | |
# prefer the target portal over my existing portal? | |
test = [x, y, z] | |
# where this test portal will exit into the overworld: | |
in_overworld = over(test) | |
next unless targets.any? { |t| in_search_range?(t, in_overworld) } | |
# how close is the preferred exit to the existing portals? | |
to_existing = existing.map { |orig| euclidean(in_overworld, orig) } | |
# and how close is it to the target portal? | |
to_target = targets.map { |target| euclidean(in_overworld, target) } | |
if to_target.all? { |tt| to_existing.all? { |te| tt < te } } | |
# reject it if it's not a round trip: | |
nearest_target = targets.sort_by { |t| euclidean(t, in_overworld) }.first | |
back = nether(nearest_target) | |
min_range = euclidean(back, test) | |
next if existing_nether.any? { |en| euclidean(back, en) <= min_range } | |
min_dist = to_target.flat_map { |tt| to_existing.flat_map { |to| (tt-to).abs } }.min | |
if min_dist >= exclusion | |
best << [test, min_dist] | |
end | |
end | |
end | |
end | |
STDERR.print "." | |
end | |
STDERR.puts | |
# find the nearest candidate nether portals, by closest vertical distance first | |
# and then overall distance: | |
best = best.to_a.sort_by do |point, dist| | |
existing_nether.map do |n| | |
[(point[1] - n[1]).abs, (point[2] + 4).abs, point[2], euclidean(n, point), dist] | |
# [euclidean(n, point), dist] | |
end.min | |
end | |
best.each do |point, min_dist| | |
in_overworld = over(point) | |
to_existing = existing.map { |o| euclidean(in_overworld, o)} | |
to_target = targets.map { |t| euclidean(in_overworld, t) } | |
puts "#{point.inspect} #{min_dist} -> #{in_overworld} : #{to_existing} to existing and #{to_target} to target" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment