2009-06-02 15 views
10

Sto provando a scrivere una funzione Python che restituisce lo stesso valore di fase lunare del gioco NetHack. Questo si trova in hacklib.c.Come portare questa funzione NetHack in Python?

Ho provato a copiare semplicemente la funzione corrispondente dal codice NetHack ma non credo di ottenere i risultati corretti.

La funzione che ho scritto è phase_of_the_moon().

Le funzioni position() e phase(), che ho trovato in rete, e le sto usando come indicazione del successo della mia funzione. Sono molto accurati e danno risultati che corrispondono approssimativamente al server nethack.alt.org (vedere http://alt.org/nethack/moon/pom.txt). Quello che sto cercando è comunque una replica esatta della funzione originale di NetHack, intere idiosincrasie.

mi aspetterei la mia funzione e la funzione di 'controllo' per dare la stessa fase lunare almeno, ma attualmente non lo fanno e non sono sicuro perché!

ecco il codice 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); 
} 

Ecco la funzione getlt() (anche 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 
} 

Ecco il mio codice 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() 
+0

Ah il codice nethack ... ora è un codice complesso. – Craig

+0

Lo so, ma sicuramente posso occuparmi di una teensy weensy leetle function !! – nakedfanatic

+0

Per uno, la riga che definisce 'epact' termina con un punto e virgola. – Zifre

risposta

4

Il codice come scritto è in gran parte non testabile - e devi renderlo testabile. Quindi, è necessario il codice 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); 
} 

Ora è possibile eseguire test con più valori di tempo. Il modo alternativo per farlo è quello di simulare getlt() invece.

Quindi sono necessarie modifiche parallele nel codice Python. Quindi si crea un file di valori time_t che può essere letto sia da Python che da C, e quindi convertito in una struttura appropriata (tramite localtime() in C). Quindi puoi vedere dove le cose stanno deviando.

+1

Sono d'accordo; a questo punto, ciò che è necessario è più test e basare quel test su una copia locale della funzione originale piuttosto che fare affidamento su alt.org, che inizia a sembrare che non stia nemmeno eseguendo il codice Nethack. (Non vedo alcun modo per ottenere una percentuale fuori da phase_of_the_moon(), ma alt.org lo fornisce.) –

3

Modifica: Risulta entrambi i "problemi" che ho individuato e si basavano su un fraintendimento della struttura tm. Lascio la risposta intatto per il bene della discussione nei commenti, ma salvare i tuoi voti per qualcuno che potrebbe in realtà essere corretta. ;-)


Avvertenza: non ho molta familiarità con i costrutti del tempo C; Sto principalmente uscendo dalla documentazione sul campo fornita per strftime.

vedo due "cimici" nella propria porta. In primo luogo, credo tm_year è destinato ad essere l'anno senza secolo, non l'anno meno 1900, quindi, dovrebbe essere goldn((lt.year % 100) % 19) + 1. In secondo luogo, il calcolo per diy è a base zero, mentre tm_yday appare (ancora una volta, dalla documentazione) per essere uno-based. Tuttavia, io non sono certo su queste ultime, come il fissaggio solo la linea goldn dà un risultato corretto (almeno per oggi), dove, come il fissaggio sia dà la risposta sbagliata:

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

Ancora una volta, questo è per lo più congetture . Per favore sii gentile. :-)

+0

Grazie per il tuo aiuto! Mi riferivo a un tavolo come http://www.cplusplus.com/reference/clibrary/ctime/tm/ Questo descrive tm_year come "anni dal 1900" e tm_yday come "giorni dal 1 gennaio \t (0-365) " Sembra che tu stia ottenendo il risultato giusto! Se dovessi indovinare, direi che la tua correzione "goldn" è corretta, ma mi piacerebbe essere sicuro che mi piacerebbe una funzione esatta di duplicazione. Forse farò qualche prova in qualche modo. – nakedfanatic

+0

Forse variazioni su stdc? La funzione getlt() di Nethack sembra certamente passare attraverso molti circuiti diversi per ottenere il valore in primo luogo. Qualunque cosa, ecco il riferimento che ho trovato che cerca di mappare i codici strftime ai membri di TM: http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html –

+0

Oh! Un'altra possibilità: i risultati delle versioni originale e Python della funzione saranno influenzati dal fuso orario. È possibile che il risultato corretto sia 2 nel fuso orario del tuo computer, ma 3 in alt.org. –

1

Curiosamente, quando ho compilare ed eseguire l'esempio nethack ottengo "2" come la risposta ("Primo trimestre", che è lo stesso che la 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()); 
} 

uscita:

> a.out 
phase of the moon 2 

Ma quella non sembra la risposta giusta, come oggi, weatherunderground.com e alt.org riportano la fase lunare come "Waxing Gibbous" (aka 3).

Ho provato a rimuovere il "-1900" ma non ha dato la risposta giusta.

+0

Questo è curioso. Forse ho bisogno di aggiustare il mio orologio di sistema e vedere quali risultati NetHack riporta su quali date. Forse quella funzione è semplicemente imprecisa. Intendo dire che la funzione potrebbe essere stata scritta intorno al 1987, tutti quei valori arrotondati (come descritto nel commento del preambolo della funzione) potrebbero aver causato la deriva dei risultati da dove dovrebbero essere di circa un giorno. Solo un pensiero. – nakedfanatic

0

Mi piace pensare di conoscere una cosa o due sui calendari, quindi vediamo se riesco a chiarire alcune cose.

La Chiesa cattolica definisce la data della Pasqua in termini di fasi lunari (ecco perché la data salta di anno in anno). Per questo motivo, deve essere in grado di calcolare la fase lunare approssimativa, e il suo algoritmo per farlo è spiegato here.

Non ho eseguito un controllo molto dettagliato, ma sembra che l'algoritmo NetHack sia basato pesantemente sull'algoritmo della Chiesa. L'algoritmo NetHack sembra, come l'algoritmo della Chiesa, prestare attenzione solo alla data del calendario, ignorando i fusi orari e l'ora del giorno.

L'algoritmo NetHack utilizza solo l'anno e il giorno dell'anno. Posso capire dall'esame del codice che, per essere compatibile con Y2K, che tm_year deve essere l'anno meno 1900.

1

Il codice seguente è borrowed from this site, incollandolo qui per un facile riferimento (e nel caso in cui l'altro sito non funzioni). Sembra fare quello che vuoi.

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

È possibile utilizzare il modulo per ottenere il timeore. Ecco come ho fatto (incollo qui sotto il codice scritto sul 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, '%') 

uscita:

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

Luna roba è divertente. :)

1

Ecco la mia conversione di esso, e ho provato questo contro il codice C passando valori da xrange (0, 1288578760, 3601), ed entrambi restituiscono gli stessi valori. Nota che l'ho modificato in modo da poter passare i secondi da epoch, in modo da poterlo testare contro la versione C per un terzo di un milione di valori diversi. Il valore "secondi" è facoltativo

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
2

Sono molto in ritardo su questa discussione, ma FWIW, visualizzazione del server alt.org del pom via web solo gli aggiornamenti su cron un paio di volte al giorno, quindi se siete fuori solo da un po ', quella potrebbe essere la ragione. Il gioco stesso viene eseguito da qualunque cosa si trovi nel codice nethack stesso, quindi non subisce lo stesso problema di memorizzazione nella cache. -drew (proprietario di alt.org)

Problemi correlati