Вопрос

Я пытаюсь написать функцию Python, которая возвращает то же значение фазы луны, что и в игре NetHack.Это находится в hacklib.c.

Я попытался просто скопировать соответствующую функцию из кода NetHack, но не уверен, что получаю правильные результаты.

Функция, которую я написал, phase_of_the_moon().

Функции position() и phase(), я нашел в сети и использую их как показатель успеха моей функции.Они очень точны и дают результаты, примерно соответствующие данным сервера nethack.alt.org (см. http://alt.org/nethack/moon/pom.txt).Однако мне нужна точная копия оригинальной функции NetHack с сохранением особенностей.

Я ожидал, что моя функция и функция «управление» дадут по крайней мере одну и ту же фазу луны, но в настоящее время это не так, и я не знаю, почему!

Вот код NetHack:

/*
 * moon period = 29.53058 days ~= 30, year = 365.2422 days
 * days moon phase advances on first day of year compared to preceding year
 *  = 365.2422 - 12*29.53058 ~= 11
 * years in Metonic cycle (time until same phases fall on the same days of
 *  the month) = 18.6 ~= 19
 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
 *  (29 as initial condition)
 * current phase in days = first day phase + days elapsed in year
 * 6 moons ~= 177 days
 * 177 ~= 8 reported phases * 22
 * + 11/22 for rounding
 */
int
phase_of_the_moon()     /* 0-7, with 0: new, 4: full */
{
    register struct tm *lt = getlt();
    register int epact, diy, goldn;

    diy = lt->tm_yday;
    goldn = (lt->tm_year % 19) + 1;
    epact = (11 * goldn + 18) % 30;
    if ((epact == 25 && goldn > 11) || epact == 24)
        epact++;

    return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
}

Здесь getlt() функция (также в hacklib.c):

static struct tm *
getlt()
{
    time_t date;

#if defined(BSD) && !defined(POSIX_TYPES)
    (void) time((long *)(&date));
#else
    (void) time(&date);
#endif
#if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || (defined(BSD) && !defined(POSIX_TYPES))
    return(localtime((long *)(&date)));
#else
    return(localtime(&date));
#endif
}

Вот мой код Python:

from datetime import date

def phase_of_the_moon():
   lt = date.today()

   diy = (lt - date(lt.year, 1, 1)).days
   goldn = ((lt.year - 1900) % 19) + 1
   epact = (11 * goldn + 18) % 30;
   if ((epact == 25 and goldn > 11) or epact == 24):
      epact += 1
   return ( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 )

import math, decimal, datetime
dec = decimal.Decimal

def position(now=None): 
   if now is None: 
      now = datetime.datetime.now()

   diff = now - datetime.datetime(2001, 1, 1)
   days = dec(diff.days) + (dec(diff.seconds) / dec(86400))
   lunations = dec("0.20439731") + (days * dec("0.03386319269"))

   return lunations % dec(1)

def phase(pos): 
   index = (pos * dec(8)) + dec("0.5")
   index = math.floor(index)
   return {
      0: "New Moon", 
      1: "Waxing Crescent", 
      2: "First Quarter", 
      3: "Waxing Gibbous", 
      4: "Full Moon", 
      5: "Waning Gibbous", 
      6: "Last Quarter", 
      7: "Waning Crescent"
   }[int(index) & 7]

def phase2(pos): 
   return {
      0: "New Moon", 
      1: "Waxing Crescent", 
      2: "First Quarter", 
      3: "Waxing Gibbous", 
      4: "Full Moon", 
      5: "Waning Gibbous", 
      6: "Last Quarter", 
      7: "Waning Crescent"
   }[int(pos)]

def main():
   ## Correct output
   pos = position()
   phasename = phase(pos)
   roundedpos = round(float(pos), 3)
   print "%s (%s)" % (phasename, roundedpos)

   ## My output
   print "%s (%s)" % (phase2(phase_of_the_moon()), phase_of_the_moon())

if __name__=="__main__": 
   main()
Это было полезно?

Решение

Написанный код по большей части не поддается тестированию, и вам необходимо сделать его тестируемым.Итак, вам нужен код C:

int
phase_of_the_moon()     /* 0-7, with 0: new, 4: full */
{
    register struct tm *lt = getlt();
    return testable_potm(lt);
}

static int
testable_potm(const struct tm *lt)
{
    register int epact, diy, goldn;

    diy = lt->tm_yday;
    goldn = (lt->tm_year % 19) + 1;
    epact = (11 * goldn + 18) % 30;
    if ((epact == 25 && goldn > 11) || epact == 24)
        epact++;

    return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
}

Теперь вы можете запускать тесты с несколькими значениями времени.Альтернативный способ сделать это — подделать getlt() вместо.

Затем вам потребуются параллельные изменения в вашем коде Python.Затем вы создаете файл time_t значения, которые могут быть прочитаны как Python, так и C, а затем преобразованы в соответствующую структуру (через localtime() в С).Тогда вы увидите, где что-то отклоняется.

Другие советы

Редактировать: Оказывается, обе «проблемы», которые я здесь заметил, были основаны на неправильном понимании tm структура.Я оставлю ответ без изменений ради обсуждения в комментариях, но приберегите ваши голоса для тех, кто действительно может быть прав.;-)


Предостережение:Я не очень хорошо знаком с конструкциями времени C;Я в основном исхожу из полевой документации, поставляемой для strftime.

Я вижу два "бага" в вашем порту.Во-первых, я верю tm_year предполагается, что это год без столетия, а не год минус 1900, поэтому goldn должно быть ((lt.year % 100) % 19) + 1.Во-вторых, ваш расчет на diy отсчитывается от нуля, тогда как tm_yday кажется (опять же, из документации), что он основан на единице.Однако я не уверен в последнем, так как исправление только goldn строка дает правильный результат (по крайней мере, на сегодняшний день), тогда как исправление обоих дает неправильный ответ:

>>> def phase_of_the_moon():
    lt = date.today()

    diy = (lt - date(lt.year, 1, 1)).days
    goldn = ((lt.year % 100) % 19) + 1
    epact = (11 * goldn + 18) % 30
    if ((epact == 25 and goldn > 11) or epact == 24):
        epact += 1
    return ( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 )

>>> phase_of_the_moon():
3

Опять же, это в основном догадки.Пожалуйста, будьте добры.:-)

Я сильно опоздал с этой веткой, но, кстати, отображение pom на сервере alt.org через Интернет обновляется в cron только пару раз в день, поэтому, если вы немного отклонились от этого, это может быть причиной.Сама игра запускается из всего, что есть в самом коде nethack, поэтому не имеет такой проблемы с кэшированием.-Дрю (владелец alt.org)

Любопытно, что когда я компилирую и запускаю пример nethack, я получаю в качестве ответа «2» («Первая четверть», что совпадает с вашим портом)

#include <time.h>

static struct tm *
getlt()
{
        time_t date;
        (void) time(&date);
        return(localtime(&date));
}
/*
 * moon period = 29.53058 days ~= 30, year = 365.2422 days
 * days moon phase advances on first day of year compared to preceding year
 *  = 365.2422 - 12*29.53058 ~= 11
 * years in Metonic cycle (time until same phases fall on the same days of
 *  the month) = 18.6 ~= 19
 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
 *  (29 as initial condition)
 * current phase in days = first day phase + days elapsed in year
 * 6 moons ~= 177 days
 * 177 ~= 8 reported phases * 22
 * + 11/22 for rounding
 */
int
phase_of_the_moon()     /* 0-7, with 0: new, 4: full */
{
    register struct tm *lt = getlt();
    register int epact, diy, goldn;

    diy = lt->tm_yday;
    goldn = (lt->tm_year % 19) + 1;
    epact = (11 * goldn + 18) % 30;
    if ((epact == 25 && goldn > 11) || epact == 24)
        epact++;

    return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
}

int main(int argc, char * argv[]) {
    printf ("phase of the moon %d\n\n", phase_of_the_moon());
}

выход:

> a.out
phase of the moon 2

Но это не похоже на правильный ответ, поскольку сегодня сайты WeatherUnderground.com и alt.org сообщают о фазе Луны как о «Растущей Луне» (также известной как 3).

Я попытался удалить «-1900», но это тоже не привело к правильному ответу.

Следующий код взято с этого сайта, вставив его сюда для удобства использования (и на случай, если другой сайт выйдет из строя).Кажется, делаешь то, что хочешь.

# Determine the moon phase of a date given
# Python code by HAB

def moon_phase(month, day, year):
    ages = [18, 0, 11, 22, 3, 14, 25, 6, 17, 28, 9, 20, 1, 12, 23, 4, 15, 26, 7]
    offsets = [-1, 1, 0, 1, 2, 3, 4, 5, 7, 7, 9, 9]
    description = ["new (totally dark)",
      "waxing crescent (increasing to full)",
      "in its first quarter (increasing to full)",
      "waxing gibbous (increasing to full)",
      "full (full light)",
      "waning gibbous (decreasing from full)",
      "in its last quarter (decreasing from full)",
      "waning crescent (decreasing from full)"]
    months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

    if day == 31:
        day = 1
    days_into_phase = ((ages[(year + 1) % 19] + ((day + offsets[month-1]) % 30) + (year < 1900)) % 30)
    index = int((days_into_phase + 2) * 16/59.0)
    if index > 7:
        index = 7
    status = description[index]

    # light should be 100% 15 days into phase
    light = int(2 * days_into_phase * 100/29)
    if light > 100:
        light = abs(light - 200);
    date = "%d%s%d" % (day, months[month-1], year)

    return date, status, light

# put in a date you want ...
month = 5
day = 14
year = 2006  # use yyyy format

date, status, light = moon_phase(month, day, year)
print "moon phase on %s is %s, light = %d%s" % (date, status, light, '%')

Вы можете использовать time модуль, чтобы получить текущее местное время.Вот как я это сделал (вставьте приведенный ниже код для тестового запуска):

import time
tm = time.localtime()
month = tm.tm_mon
day = tm.tm_mday
year = tm.tm_year
date, status, light = moon_phase(month, day, year)
print "moon phase on %s is %s, light = %d%s" % (date, status, light, '%')

Выход:

moon phase on 22Dec2009 is waxing crescent (increasing to full), light = 34%

Лунные штучки — это весело.:)

Вот мое преобразование, и я проверил его на коде C, передав значения из xrange(0, 1288578760, 3601), и они оба возвращают одни и те же значения.Обратите внимание, что я изменил его так, чтобы вы могли передавать секунды с начала эпохи, чтобы я мог протестировать его на версии C для трети из миллиона различных значений.Значение «секунды» не является обязательным.

def phase_of_the_moon(seconds = None):
   '0-7, with 0: new, 4: full'
   import time

   if seconds == None: seconds = time.time()
   lt = time.localtime(seconds)

   tm_year = lt.tm_year - 1900
   diy = lt.tm_yday - 1
   goldn = (tm_year % 19) + 1
   epact = (11 * goldn + 18) % 30

   if (epact == 25 and goldn > 11) or epact == 24: epact += 1

   return (((((diy + epact) * 6) + 11) % 177) / 22) & 7

Мне нравится думать, что я кое-что знаю о календарях, так что давайте посмотрим, смогу ли я прояснить кое-что.

Католическая церковь определяет дату Пасхи по лунным фазам (вот почему дата меняется из года в год).По этой причине необходимо иметь возможность рассчитать приблизительную фазу Луны, а алгоритм этого расчета объясняется. здесь.

Я не проводил очень детальной проверки, но похоже, что алгоритм NetHack во многом основан на алгоритме Чёрча.Алгоритм NetHack, как и алгоритм Чёрча, похоже, обращает внимание только на календарную дату, игнорируя часовые пояса и время суток.

Алгоритм NetHack использует только год и день года.Изучая код, я могу сказать, что для совместимости с Y2K tm_year должен быть годом минус 1900.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top