Skip to content

Instantly share code, notes, and snippets.

@zgracem
Created September 27, 2018 14:26
Show Gist options
  • Save zgracem/545d8922fe47d730835c419aa9a43621 to your computer and use it in GitHub Desktop.
Save zgracem/545d8922fe47d730835c419aa9a43621 to your computer and use it in GitHub Desktop.
Calculating the phase of the moon in Ruby, based on NetHack's source code
### Based on NetHack's phase_of_the_moon()
# > http://nethack.wikia.com/wiki/Hacklib.c#phase_of_the_moon
### See also:
# > https://en.wikipedia.org/wiki/Epact
# > https://en.wikipedia.org/wiki/Golden_number_(time)
# > https://en.wikipedia.org/wiki/Metonic_cycle
# > https://en.wikipedia.org/wiki/Computus
def phase(lt = Time.now)
# The solar year is about 365¼ days, while the lunar month is slightly longer
# than 29½ days, on average; both are non-integers.
solar_year = 365.242190
lunar_month = 29.530588 # ≈ 30
lunar_year = lunar_month * 12 # ≈ 354.367056
# The Metonic cycle is the period which corresponds to a common multiple of
# solar years (~19) and lunar months (235). Two calendar years at the same
# point in the cycle will have the same phases of the moon on the same dates.
metonic_cycle = (235 * lunar_month) / solar_year # ≈ 19
# The sequence number of the current year in the Metonic cycle is called the
# golden number. The golden number of any Julian or Gregorian calendar year
# can be calculated by dividing the year by 19, taking the remainder, and
# adding 1.
golden = (lt.year % metonic_cycle.round) + 1
# If a solar and lunar year start on the same day, then after one year, the
# start of the solar year is 11 days after the start of the lunar year; after
# two years, it is 22 days after. These excess days are epacts, and are added
# to the day of the solar year to determine the day of the lunar year.
advance = (solar_year - lunar_year).round # ≈ 11
epact = (golden * advance + 18) % lunar_month.round
# Within a Metonic cycle, years that are 11 years apart have epacts that
# differ by one day. Because lunar months are either 29 or 30 days, if epacts
# 24 and 25 both occur within one Metonic cycle, the new moon in some months
# will fall on the same dates in both years. To ensure dates repeat only
# after 19 years, in years with an epact of 25 and a golden number > 11, the
# reckoned new moon in short months will fall one day earlier.
if (epact == 25 && golden > 11) || epact == 24
epact += 1
end
# current phase in days = phase on January 1 + days elapsed in year
current_phase = epact + lt.yday
moon_phases = %i{new_moon waxing_crescent first_quarter waxing_gibbous
full_moon waning_gibbous last_quarter waning_crescent}
six = 6
moons = lunar_month * six # ≈ 177
phases = moons / moon_phases.size
# 0-7, with 0: new, 4: full
(((((lt.yday + epact) * 6) + 11) % 177) / 22).round & 7
# (((((lt.yday + epact) * six) + advance) % moons) / phases).round & (moon_phases.index(moon_phases.last))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment