11

ゲームの NetHack と同じムーンフェイズ値を返す Python 関数を作成しようとしています。これはhacklib.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()
4

7 に答える 7

5

書かれたコードはほとんどテスト不可能です - そしてそれをテスト可能にする必要があります。したがって、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 コードを並行して変更する必要があります。time_t次に、Python と C の両方で読み取ることができる値のファイルを作成し、 ( localtime()C 経由で) 適切な構造に変換します。そうすれば、どこがずれているかがわかります。

于 2009-06-03T00:54:48.757 に答える
3

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


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

ポートに 2 つの「バグ」が見られます。まず、 はtm_year1900 年から 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

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

于 2009-06-02T23:44:57.557 に答える
2

私はこのスレッドに遅れをとっていますが、fwiw、web を介した alt.org サーバーの pom の表示は、cron で 1 日に数回しか更新されないため、少しずれている場合は、それが理由である可能性があります。ゲーム自体は nethack コード自体にあるものから実行されるため、同じキャッシュの問題は発生しません。-ドリュー (alt.org 所有者)

于 2011-03-01T22:58:43.530 に答える
1

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

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
于 2010-11-01T02:56:56.820 に答える
1

不思議なことに、nethack の例をコンパイルして実行すると、答えとして「2」が返されます (「第 1 四半期」はポートと同じです)。

#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) と報告しています。

「-1900」を外してみましたが、正解にもなりませんでした。

于 2009-06-03T00:27:40.170 に答える
1

次のコードはこのサイトから借用したもので、簡単に参照できるようにここに貼り付けます (また、他のサイトがダウンした場合に備えて)。あなたが望むことをするようです。

# 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このモジュールを使用して、現在の現地時間を取得できます。私がやった方法は次のとおりです(投稿されたコードの下に貼り付けてテストを実行します):

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%

月ネタは楽しいです。:)

于 2009-12-22T09:42:18.220 に答える
0

私はカレンダーについて 1 つまたは 2 つのことを知っていると思いたいので、いくつかクリアできるか見てみましょう。

カトリック教会は、イースターの日付を月の満ち欠けで定義しています (これが、日付が年々変動する理由です)。このため、おおよその月の満ち欠けを計算できる必要があり、そのためのアルゴリズムについてはこちらで説明されています。

私はあまり詳細なチェックを行っていませんが、NetHack アルゴリズムは Church のアルゴリズムに大きく基づいているようです。NetHack アルゴリズムは、Church のアルゴリズムと同様に、タイム ゾーンと時刻を無視して、カレンダーの日付のみに注意を払っているようです。

NetHack アルゴリズムは、年と通算日のみを使用します。コードを調べると、Y2K 互換性を保つには、tm_year が年から 1900 年を引いたものでなければならないことがわかります。

于 2009-12-22T09:30:24.207 に答える