문제

나는 게임 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
}

내 파이썬 코드는 다음과 같습니다.

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() 대신에.

그런 다음 파이썬 코드에서 병렬 변경이 필요합니다. 그런 다음 파일을 만듭니다 time_t 파이썬과 C 모두에 의해 읽을 수있는 값을 이후에 적절한 구조로 변환 할 수있는 값 localtime() C). 그런 다음 상황이 어디에서 벗어나고 있는지 알 수 있습니다.

다른 팁

편집하다: 내가 여기서 발견 한 두 가지 "문제"는 tm 구조. 나는 의견에서 토론을 위해 답을 그대로 두지 만, 실제로 올바른 사람에게 투표를 저장하겠습니다. ;-)


경고 : 나는 C 시간 구성에 대해 크게 익숙하지 않습니다. 나는 주로 제공된 현장 문서를 벗어납니다 strftime.

포트에서 두 개의 "버그"가 보입니다. 첫째, 나는 믿습니다 tm_year 1900 년 마이너스가 아닌 세기가없는 해가되기위한 것입니다. goldn 해야한다 ((lt.year % 100) % 19) + 1. 둘째, 계산 diy 반면 0 기반입니다 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

다시, 이것은 대부분 추측입니다. 제발 친절하십시오. :-)

나는이 스레드에서 오래 늦었지만 FWIW, Alt.org 서버의 웹을 통해 POM 디스플레이 인 FWIW는 하루에 몇 번 CRON에 대한 업데이트 만 업데이트되므로 조금만 꺼져 있다면 그 이유가 될 수 있습니다. 게임 자체는 Nethack 코드 자체의 모든 것에서 실행되므로 동일한 캐싱 문제를 겪지 않습니다. -Drew (alt.org 소유자)

흥미롭게도 Nethack 예제를 컴파일하고 실행할 때 "2"를 답으로 얻습니다 (포트와 같은 "1/4")

#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는 달의 단계를 "왁스 gibbous"(일명 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 얻기위한 모듈 현재 현지 시간. 그녀는 내가 어떻게했는지 (아래에 붙여 넣은 코드를 testrun에 붙여 넣습니다) :

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%

달 물건은 재미 있습니다. :)

다음은 IT 전환이 있으며 XRange (0, 1288578760, 3601)의 값을 전달하여 C 코드에 대해 이것을 테스트했으며 둘 다 동일한 값을 반환합니다. Epoch 이후 초를 통과 할 수 있도록 변경하여 C 버전에 대해 백만 가지 값 중 3 분의 1에 대해 테스트 할 수 있도록 변경했습니다. "초"값은 선택 사항입니다

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