11 Stimmen

Wie kann man diese NetHack-Funktion nach Python portieren?

Ich versuche, eine Python-Funktion zu schreiben, die den gleichen Mondphasenwert wie im Spiel NetHack zurückgibt. Dies findet sich in hacklib.c .

Ich habe versucht, die entsprechende Funktion einfach aus dem NetHack-Code zu kopieren, aber ich glaube nicht, dass ich die richtigen Ergebnisse erhalte.

Die Funktion, die ich geschrieben habe, lautet phase_of_the_moon() .

Die Funktionen position() y phase() die ich im Netz gefunden habe, und ich verwende sie als Indikator für den Erfolg meiner Funktion. Sie sind sehr genau und liefern Ergebnisse, die ungefähr mit denen des nethack.alt.org-Servers übereinstimmen (siehe http://alt.org/nethack/moon/pom.txt ). Was ich jedoch suche, ist eine exakte Replikation der ursprünglichen NetHack-Funktion, mit intakten Eigenheiten.

Ich würde erwarten, dass meine Funktion und die "Kontroll"-Funktion zumindest dieselbe Mondphase anzeigen, aber im Moment tun sie das nicht und ich bin mir nicht sicher, warum!

Hier ist der NetHack-Code:

/*
 * 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 );
}

Hier ist die getlt() Funktion (auch in 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
}

Hier ist mein Python-Code:

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()

5voto

Jonathan Leffler Punkte 694013

Der Code ist in seiner jetzigen Form weitgehend untestbar - und Sie müssen ihn testbar machen. Das heißt, der C-Code muss testbar sein:

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 );
}

Jetzt können Sie Tests mit mehreren Zeitwerten durchführen. Die Alternative dazu ist die Fälschung von getlt() stattdessen.

Sie müssen dann parallel Änderungen in Ihrem Python-Code vornehmen. Dann erstellen Sie eine Datei von time_t Werte, die sowohl von Python als auch von C gelesen und dann in eine geeignete Struktur umgewandelt werden können (über localtime() in C). Dann können Sie sehen, wo es Abweichungen gibt.

3voto

Ben Blank Punkte 52357

Edita: Es stellte sich heraus, dass beide "Probleme", die ich hier entdeckte, auf einem Missverständnis der tm Struktur. Ich lasse die Antwort für die Diskussion in den Kommentaren stehen, aber heben Sie Ihre Stimmen für jemanden auf, der vielleicht tatsächlich richtig liegt ;-)


Caveat: Ich bin nicht sehr vertraut mit C Zeit Konstrukte; Ich bin vor allem aus dem Feld Dokumentation für geliefert gehen strftime .

Ich sehe zwei "Bugs" in Ihrem Port. Erstens, ich glaube tm_year soll das Jahr ohne Jahrhundert sein, nicht das Jahr minus 1900, also, goldn sollte sein ((lt.year % 100) % 19) + 1 . Zweitens: Ihre Berechnung für diy ist nullbasiert, während tm_yday scheint (wiederum aus den Unterlagen) auf einem einzigen Programm zu basieren. Allerdings bin ich mir bei letzterem nicht sicher, da die Behebung nur der goldn Zeile ergibt ein richtiges Ergebnis (zumindest für heute), während die Festlegung beider Zeilen eine falsche Antwort ergibt:

>>> 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

Auch hier handelt es sich größtenteils um Mutmaßungen. Bitte seien Sie freundlich :-)

2voto

Drew Streib Punkte 21

Ich bin schon lange nicht mehr in diesem Thread, aber fwiw, die Anzeige von pom auf dem alt.org-Server wird nur ein paar Mal am Tag per Cron aktualisiert, wenn du also nur ein bisschen davon abweichst, könnte das der Grund sein. Das Spiel selbst läuft auf der Basis des Nethack-Codes, hat also nicht das gleiche Problem mit dem Caching. -drew (alt.org Besitzer)

1voto

Mike Miller Punkte 169

Seltsamerweise erhalte ich, wenn ich das Nethack-Beispiel kompiliere und ausführe, als Antwort "2" ("First Quarter", das ist dasselbe wie Ihr Port)

#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());
}

Ausgabe:

> a.out
phase of the moon 2

Aber das scheint nicht die richtige Antwort zu sein, denn heute melden weatherunderground.com und alt.org die Phase des Mondes als "Waxing Gibbous" (auch bekannt als 3).

Ich habe versucht, die "-1900" zu entfernen, aber auch das hat nicht zur richtigen Antwort geführt.

1voto

Mizipzor Punkte 48351

Der folgende Code lautet entlehnt von dieser Website Ich füge sie hier ein, damit sie leicht zu finden ist (und für den Fall, dass die andere Seite nicht mehr funktioniert). Scheint zu tun, was Sie wollen.

# 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, '%')

Sie können die time Modul, um die aktuelle Ortszeit . Hier ist, wie ich es tat (fügen Sie unten gepostet Code zu testen):

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, '%')

Salida:

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

Das mit dem Mond macht Spaß :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X