Pregunta

Estoy intentando escribir una función de Python que devuelva el mismo valor de fase lunar que en el juego NetHack.Esto se encuentra en hacklib.c.

Intenté simplemente copiar la función correspondiente del código NetHack pero no creo que obtenga los resultados correctos.

La función que he escrito es phase_of_the_moon().

Las funciones position() y phase(), los encontré en la red y los estoy utilizando como indicación del éxito de mi función.Son muy precisos y dan resultados que coinciden aproximadamente con el servidor nethack.alt.org (consulte http://alt.org/nethack/moon/pom.txt).Sin embargo, lo que busco es una réplica exacta de la función NetHack original, con las idiosincrasias intactas.

Esperaría que mi función y la función de 'control' dieran al menos la misma fase lunar, pero actualmente no es así y no estoy seguro de por qué.

Aquí está el código 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 );
}

Aquí está el getlt() función (también en 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
}

Aquí está mi código 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()
¿Fue útil?

Solución

El código escrito como es en gran parte no comprobable - y lo que necesita para que sea comprobable. Por lo tanto, es necesario el código C a ser:

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

Ahora se puede ejecutar pruebas con múltiples valores de tiempo. La manera alternativa de hacer esto es getlt() falsa en su lugar.

A continuación, deberá cambios paralelos en su código Python. A continuación, se crea un archivo de valores time_t que pueden ser leídos por tanto Python y C, y luego transformar en una estructura apropiada (a través de localtime() en C). A continuación se puede ver que las cosas se están desviando.

Otros consejos

Editar Resulta que ambos de los "problemas" vi aquí se basaron en una interpretación errónea de la estructura tm. Voy a dejar intacta la respuesta por el bien de la discusión en los comentarios, pero ahorra votos para alguien que en realidad podría ser correcta. ; -)


Advertencia: No soy terriblemente familiar con construcciones de tiempo C; Lo que más me salirse de la documentación de campo suministrado para strftime.

Veo dos "errores" en su puerto. En primer lugar, creo tm_year está destinado a ser el año sin siglo, no el año menos 1900, por lo que, goldn debe ((lt.year % 100) % 19) + 1. En segundo lugar, su cálculo para diy está basado en cero, mientras que aparece tm_yday (de nuevo, a partir de los documentos) para ser a base de uno. Sin embargo, no estoy seguro sobre este último, como se acaba de fijación de la línea goldn da un resultado correcto (al menos por hoy), en tanto que la fijación tanto da la respuesta incorrecta:

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

Una vez más, esto es sobre todo conjeturas. Por favor se amable. : -)

Estoy larga tarde en este hilo, pero fwiw, la pantalla del servidor alt.org de pom través de la web sólo las actualizaciones sobre cron un par de veces por día así que si estás fuera por sólo un poco de ella, que podría ser la razón. El juego en sí se extiende desde lo que está en el código nethack sí por lo que no sufre el mismo problema de almacenamiento en caché. -Drew (propietario alt.org)

Curiosamente, cuando compilar y ejecutar el ejemplo nethack me sale "2" como la respuesta ( "primer trimestre", que es el mismo que el puerto)

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

salida:

> a.out
phase of the moon 2

Pero eso no parece ser la respuesta correcta, como en la actualidad, y weatherunderground.com alt.org informa de la fase de la luna como "Luna nueva" (a.k.a 3).

He intentado eliminar el "-1900", pero que no dio como resultado la respuesta correcta tampoco.

tomado de este sitio , pegar aquí para una fácil referencia ( y en caso de que el otro sitio se cae). Parece hacer lo que quiera.

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

Puede utilizar el módulo time para obtener el hora local actual . He aquí cómo lo hice (pegar a continuación código publicado a 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, '%')

Salida:

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

Luna cosas es divertido. :)

Aquí está mi conversión, y lo probé con el código C pasando valores de xrange(0, 1288578760, 3601), y ambos devuelven los mismos valores.Tenga en cuenta que lo cambié para que pueda pasar los segundos desde la época, de modo que pueda probarlo con la versión C para un tercio de millón de valores diferentes.El valor de "segundos" es opcional.

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

Me gusta pensar que sé una cosa o dos acerca de los calendarios, así que vamos a ver si puedo aclarar algunas cosas.

La Iglesia Católica define la fecha de la Pascua, en términos de las fases lunares (esta es la razón por la fecha salta alrededor de año en año). Debido a esto, tiene que ser capaz de calcular la fase de la luna aproximada, y su algoritmo para hacerlo se explica aquí .

No he hecho la comprobación muy detallado, pero parece que el algoritmo NetHack se basa en gran medida en el algoritmo de la Iglesia. El algoritmo NetHack parece, al igual que el algoritmo de la Iglesia, prestar atención sólo a la fecha del calendario, haciendo caso omiso de las zonas de tiempo y la hora del día.

El algoritmo NetHack utiliza sólo el año y el día del año. Puedo decir de examinar el código que, para ser compatible con el Y2K, que tm_year tiene que ser el año menos 1900.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top