Pergunta

Eu estou tentando escrever uma função Python que retorna o mesmo valor das fases da lua como no jogo NetHack. Isto é encontrado no hacklib.c .

Eu tentei simplesmente copiar a função correspondente do código NetHack mas eu não acredito que eu estou recebendo os resultados corretos.

A função que eu escrevi é phase_of_the_moon().

O funções position() e phase(), eu encontrei na net, e estou usando-os como uma indicação do sucesso da minha função. Eles são muito precisos e dar resultados que aproximadamente correspondem ao servidor nethack.alt.org (ver http: / /alt.org/nethack/moon/pom.txt ). O que eu sou depois, porém, é uma réplica exata da função NetHack original, idiossincrasias intacta.

Eu esperaria minha função ea função de 'controle' para dar a mesma fase da lua, pelo menos, mas atualmente eles não fazer e eu não sei porquê!

Aqui está o 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 );
}

Aqui é a função getlt() (também em 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
}

Aqui está o meu 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()
Foi útil?

Solução

O código como escrito é em grande parte não testável - e você precisa fazê-lo testável. Assim, é necessário o código C para 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 );
}

Agora você pode executar testes com vários valores de tempo. A maneira alternativa de fazer isso é falso getlt() vez.

Você precisa então mudanças paralelas no seu código Python. Em seguida, você cria um arquivo de valores time_t que podem ser lidos por ambos Python e C, e depois convertidos em uma estrutura adequada (via localtime() em C). Então você pode ver onde as coisas estão se desviando.

Outras dicas

Editar: Acontece que ambos os "problemas" Vi aqui foram baseadas em um mal-entendido da estrutura tm. Vou deixar a resposta intacta por causa da discussão nos comentários, mas salvar seus votos para alguém que poderia realmente ser correta. ; -)


Aviso: Eu não sou terrivelmente familiarizados com C construções de tempo; Eu estou indo principalmente fora da documentação de campo fornecido para strftime.

Eu vejo dois "erros" em sua porta. Em primeiro lugar, acredito tm_year se destina a ser o ano sem o século, não o menos ano de 1900, por isso, goldn deve ser ((lt.year % 100) % 19) + 1. Em segundo lugar, o seu cálculo para diy é baseado em zero, enquanto tm_yday aparece (de novo, desde os docs) para ser um-based. No entanto, eu não estou certo sobre o último, como a fixação apenas a linha goldn dá um resultado correto (pelo menos por hoje), onde, como fixação de ambos dá a resposta errada:

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

Mais uma vez, este é principalmente adivinhação. Por favor seja gentil. : -)

Eu sou longa tarde sobre este tópico, mas fwiw, a tela do servidor alt.org de pom via web atualiza apenas no cron um par de vezes por dia por isso, se você está fora por apenas um pouco dele, que poderia ser o motivo. O jogo em si vai de tudo o que está no código nethack-se de modo não sofre o mesmo problema de cache. -drew (alt.org proprietário)

Curiosamente, quando eu compilar e executar o exemplo nethack eu recebo "2" como a resposta ( "First Quarter", que é o mesmo que o seu porta)

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

saída:

> a.out
phase of the moon 2

Mas isso não parece ser a resposta certa, como hoje, weatherunderground.com e alt.org relata a fase da lua como "Lua crescente" (a.k.a 3).

Eu tentei remover o "-1900", mas que não resultou na resposta correcta.

A seguir código é emprestado a partir deste site , colando-lo aqui para fácil referência ( e no caso de outro site vai para baixo). Parece fazer o que quiser.

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

Você pode usar o módulo time para obter o tempo corrente local . Heres como eu fiz isso (cole abaixo código postado para 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, '%')

Output:

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

material Lua é divertido. :)

Aqui está minha conversão dele, e eu testei este contra o código C, passando em valores de xrange (0, 1288578760, 3601), e ambos retornam os mesmos valores. Note que eu mudei-lo de modo que você pode passar os segundos desde época, para que eu pudesse testá-lo contra a versão C por um terço de um milhão de diferentes valores. O valor "segundos" é 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

Eu gosto de pensar que eu sei uma coisa ou duas sobre calendários, então vamos ver se eu posso esclarecer algumas coisas.

A Igreja Católica define a data da Páscoa, em termos de fases lunares (é por isso que a data salta ao redor de ano para ano). Devido a isso, ele precisa ser capaz de calcular a fase da lua aproximada, e seu algoritmo para fazer isso é explicado aqui .

Eu não fiz a verificação muito detalhado, mas parece que o algoritmo NetHack é fortemente baseada em algoritmo da Igreja. O algoritmo NetHack parece, como o algoritmo da Igreja, prestar atenção apenas para a data do calendário, ignorando os fusos horários e a hora do dia.

O algoritmo NetHack usa apenas o ano eo dia do ano. Eu posso dizer de examinar o código que, para ser Y2K compatível, que tm_year tem que ser o ano menos 1900.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top