Question

Je suis en train d'écrire une fonction Python qui renvoie la même valeur de phase de lune comme dans le jeu NetHack. Il se trouve dans hacklib.c .

J'ai essayé de copier simplement la fonction correspondante à partir du code NetHack mais je ne crois pas que je reçois les résultats corrects.

La fonction que je l'ai écrit est phase_of_the_moon().

Les fonctions position() et phase(), je trouve sur le net, et je les utilise comme une indication du succès de ma fonction. Ils sont très précis et donnent des résultats qui correspondent à peu près au serveur nethack.alt.org (voir http: / /alt.org/nethack/moon/pom.txt ). Ce que je suis après est cependant une réplique exacte de la fonction NetHack originale, idiosyncrasies intacte.

Je me attends à ma fonction et la fonction « contrôle » pour donner la même phase de lune au moins, mais actuellement ils ne sont pas et je ne sais pas pourquoi!

Voici le code 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 );
}

Voici la fonction getlt() (également 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
}

Voici mon code 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()
Était-ce utile?

La solution

Le code tel qu'il est écrit est en grande partie invérifiable - et vous devez faire testable. Donc, vous devez le code C pour être:

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

Maintenant, vous pouvez exécuter des tests avec plusieurs valeurs de temps. L'alternative façon de le faire est de faux getlt() à la place.

Vous devez ensuite des changements parallèles dans votre code Python. Ensuite, vous créez un fichier de valeurs de time_t qui peuvent être lus aussi bien par Python et C, puis transformé en une structure appropriée (via localtime() en C). Ensuite, vous pouvez voir où les choses dévient.

Autres conseils

Modifier Active à la fois des « problèmes » je repéré ici étaient fondées sur une mauvaise compréhension de la struct tm. Je laisse la réponse intacte pour le bien de la discussion dans les commentaires, mais enregistrer votre voix pour quelqu'un qui pourrait effectivement être correcte. ; -)


Caveat: Je ne connais pas terriblement avec des constructions de temps C; Je suis surtout hors de la documentation va sur le terrain fourni pour strftime.

Je vois deux "bugs" dans votre port. Tout d'abord, je crois tm_year est destiné à être l'année sans le siècle, pas moins de l'année 1900, donc, goldn devrait être ((lt.year % 100) % 19) + 1. En second lieu, votre calcul pour diy est basé sur zéro, alors que tm_yday apparaît (encore une fois, des docs) d'être un base. Cependant, je ne suis pas certain de ce dernier, comme fixation juste la ligne de goldn donne un résultat correct (au moins pour aujourd'hui), alors que la fixation à la fois donne la mauvaise réponse:

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

Encore une fois, ce sont surtout devinettes. S'il vous plaît être gentil. : -)

Je suis longtemps en retard sur ce fil, mais FWIW, l'affichage du serveur alt.org de pom via le web que des mises à jour sur Cron une ou deux fois par jour, donc si vous êtes hors de juste un peu de lui, qui pourrait être la raison. Le jeu lui-même va de tout ce qui est dans le code nethack lui-même ne souffre pas si le même problème de mise en cache. -drew (propriétaire de alt.org)

Chose curieuse, quand je compiler et exécuter l'exemple nethack je reçois « 2 » comme la réponse ( « premier trimestre », qui est le même que votre 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());
}

sortie:

> a.out
phase of the moon 2

Mais cela ne semble pas être la bonne réponse, comme aujourd'hui, weatherunderground.com et alt.org rapports de la phase de la Lune « gibbeuse croissante » (a.k.a 3).

J'ai essayé enlever la « -1900 », mais cela n'a pas donné lieu à la bonne réponse non plus.

Après le code est emprunté à ce site , coller ici pour référence facile ( et dans le cas de l'autre site tombe en panne). Semble faire ce que vous voulez.

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

Vous pouvez utiliser le module time pour obtenir l'heure locale actuelle . Voici comment je l'ai fait (coller ci-dessous le code affiché à 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, '%')

Sortie:

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

Moon choses est amusant. :)

Voici ma conversion de celui-ci, et je l'ai testé ce contre le code C en passant dans les valeurs de xrange (0, 1288578760, 3601), et ils reviennent tous les deux les mêmes valeurs. Notez que je l'ai changé pour que vous puissiez passer les secondes depuis l'époque, pour que je puisse le tester contre la version C pour un tiers d'un million de valeurs différentes. La valeur « secondes » est en option

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

Je me plais à penser que je sais une chose ou deux sur les calendriers, donc nous allons voir si je peux effacer quelques choses.

L'Eglise catholique définit la date de Pâques en termes de phases lunaires (ce qui est la raison pour laquelle la date sautille d'année en année). À cause de cela, il doit être en mesure de calculer la phase de lune approximative et son algorithme pour le faire est expliqué .

Je ne l'ai pas fait la vérification très détaillée, mais il semble que l'algorithme NetHack est basé en grande partie sur l'algorithme de l'Eglise. L'algorithme NetHack semble, comme l'algorithme de l'Eglise, attention uniquement à la date du calendrier, en ignorant les fuseaux horaires et l'heure du jour.

L'algorithme NetHack utilise seulement l'année et le jour de l'année. Je peux dire d'examiner le code qui, pour être compatible Y2K, que tm_year doit être l'année moins 1900.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top