質問

ゲーム NetHack と同じ月齢値を返す Python 関数を作成しようとしています。これは次の場所にあります ハックリブ.c.

NetHack コードから対応する関数を単純にコピーしようとしましたが、正しい結果が得られるとは思えません。

私が書いた関数は phase_of_the_moon().

機能 position() そして phase(), 、ネットで見つけたので、関数の成功の指標として使用しています。これらは非常に正確で、nethack.alt.org サーバーとほぼ一致する結果が得られます (「 http://alt.org/nethack/moon/pom.txt)。ただし、私が求めているのは、オリジナルの NetHack 機能を、特異性をそのままに正確に複製したものです。

私の関数と「コントロール」関数が少なくとも同じ月相を与えることを期待していますが、現在はそうではなく、その理由はわかりません。

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

ここにあります getlt() 関数 (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
}

私の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()
役に立ちましたか?

解決

書かれたように、コードの大部分は試験不能である - そしてあなたはそれがテスト可能にする必要があります。だから、あなたがする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 );
}

今、あなたは時間の複数の値を持つテストを実行することができます。これを行うための別の方法ではなく、偽のgetlt()にある。

次に、あなたのPythonコードで並列変更を必要とします。次に、PythonとCの両方で読み取り可能time_t値のファイルを作成し、(Cでlocaltime()介して)適切な構造に変換されます。物事が逸脱しているところ次に、あなたが見ることができます。

他のヒント

編集: ここで私が発見した「問題」は両方とも、 tm 構造体。コメントでの議論のために答えはそのままにしておきますが、実際に正しいかもしれない人に投票を保存してください。;-)


警告:私は C の時間構造についてはあまり詳しくありません。私は、提供されているフィールドドキュメントからほとんど逸脱しています。 strftime.

ポートに 2 つの「バグ」が見られます。まず、私は信じます tm_year は、1900 年を引いた年ではなく、世紀のない年を意図しているため、 goldn あるべきです ((lt.year % 100) % 19) + 1. 。第二に、あなたの計算は、 diy はゼロベースですが、 tm_yday (やはりドキュメントから) は 1 から始まるようです。ただし、後者については定かではありません。 goldn 行は (少なくとも今日に関しては) 正しい結果を返しますが、両方を修正すると間違った答えが得られます。

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

繰り返しますが、これはほとんど推測です。親切にしてください。:-)

私はあなたがそれからちょうどビットずつオフにしているので、もし、それは可能性が一日あたりの数回をのcronの更新のみ、長い故このスレッドが、FWIWにWeb経由でポンポンのalt.orgサーバのディスプレイです理由。ゲーム自体は、それ自体が非常に同じキャッシングの問題を被らないのnethackコードにあるものから実行されます。 -drew(alt.org所有者)

不思議なことに、私はコンパイルしてネットハックの例を実行すると、私は答え(あなたのポートと同じである「第一四半期」)

として「2」を取得
#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());
}

出力:

> a.out
phase of the moon 2

しかし、それは、今日のように、正しい答えのように思えるweatherunderground.comとalt.orgは(3 a.k.a)「ワックスがけGibbous」として、月の位相を報告しません。

私は「-1900」を削除しようとしたが、それはどちらかの正しい答えにはなりませんでした。

次のコードは、(容易に参照のためにここでそれを貼り付け、このサイトでから借りていますケースには他のサイト)がダウンしました。あなたがやりたいようです。

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

あなたはのの現在のローカルタイムを取得するためにtimeモジュールを使用することができます。私はそれをやった相続人(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, '%')

出力:

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

ムーンものは楽しいです。 :)

ここでの私の転換である、と私ははxrange(0、1288578760、3601)から値を渡すことによってCコードに対してこれをテストしてきたが、両者は同じ値を返します。あなたはエポックからの秒数を渡すことができるように、私は万人の異なる値の第三のCバージョンに対してそれをテストすることができるように、私は、それを変更したことに注意してください。 「秒」の値はオプションです。

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を知っていると思うので、私はいくつかのことを片付けることができるかどうかを見てみましょうしたい。

カトリック教会は(日付は毎年の周りにジャンプし、なぜこれがある)月相の面で復活祭の日付を定義します。このため、それはおおよそのムーンフェイズを計算できるようにする必要があり、そうするためのアルゴリズムを説明するここします。

私は非常に詳細なチェックを行っていないが、NetHackのアルゴリズムは、教会のアルゴリズムに大きく基づいていることが表示されます。 NetHackのアルゴリズムは、タイムゾーンと時刻を無視して、教会のアルゴリズムのように、唯一のカレンダーの日付に注意を払うようです。

NetHackのアルゴリズムは年と年の日付を使用しています。私はY2K互換性があるために、コードを調べてから伝えることができ、そのtm_yearが1900年にマイナスにする必要があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top