سؤال

أحاول كتابة وظيفة بيثون التي ترجع نفس قيمة مرحلة القمر كما في لعبة Nethack. تم العثور على هذا في Hackleib.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
}

هنا رمز بيثون الخاص بي:

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 الخاص بك. ثم تنشئ ملف time_t القيم التي يمكن قراءتها من قبل كل من Python و C، ثم تم تحويلها إلى هيكل مناسب (عبر localtime() في ج). ثم يمكنك أن ترى أين تنحرف الأمور.

نصائح أخرى

يحرر: اتضح كل من "المشاكل" التي رصدتها هنا تستند إلى سوء فهم tm بنية. سأترك الإجابة سليمة من أجل المناقشة في التعليقات، ولكن حفظ أصواتك لشخص قد يكون صحيحا بالفعل. ؛-)


التحذير: أنا لست على دراية بشكل رهيب ب C Time Builds؛ أنا في الغالب أخرج من الوثائق الميدانية المقدمة strftime.

أرى اثنين من "الأخطاء" في المنفذ الخاص بك. أولا، أعتقد tm_year يهدف إلى أن يكون العام دون قرن، وليس العام ناقص 1900، لذلك، goldn يجب ان يكون ((lt.year % 100) % 19) + 1. وبعد ثانيا، حساب الخاص بك diy هو صفر القائمة، في حين tm_yday يظهر (مرة أخرى، من المستندات) لتكون مقرها واحد. ومع ذلك، أنا لست متأكدا من الأخير، كما تحديد فقط 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

مرة أخرى، هذا هو في الغالب تخمين. رجاء كن لطيف. :-)

لقد تأخرت لفترة طويلة على هذا الموضوع ولكن FWIW، عرض خادم بوم Alt.org من POM عبر تحديثات الويب فقط على Cron مرتين في اليوم، لذلك إذا كنت خارجا قليلا، فقد يكون ذلك السبب. تعمل اللعبة نفسها من كل ما هو في كود Nethack نفسه، لذلك لا يعاني نفس مشكلة التخزين المؤقت. --DREW (مالك Alt.org)

بفضول، عندما تقوم بتجميع وتشغيل مثال Nethack، أحصل على "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 و Alty.org بتقارير مرحلة القمر "مشمع Gibbous" (AKA 3).

حاولت إزالة "-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%

أشياء القمر هي متعة. :)

إليكم تحويلي، وقد اختبرت هذا مقابل رمز C من خلال المرور في قيم من Xrange (0، 1288578760، 3601)، وكلاهما يعودان نفس القيم. لاحظ أنني قمت بتغييرها حتى تتمكن من اجتياز الثواني منذ العصر، حتى أتمكن من اختبارها ضد إصدار 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

أحب أن أعتقد أنني أعرف شيئا أو شيئين حول التقويمات، لذلك دعونا نرى ما إذا كان يمكنني مسح بعض الأشياء.

تعرف الكنيسة الكاثوليكية تاريخ عيد الفصح من حيث المراحل القمرية (وهذا هو السبب في أن التاريخ يقفز من العام إلى السنة). لهذا السبب، يجب أن تكون قادرا على حساب مرحلة القمر التقريبية، وتم تفسير خوارزميها للقيام بذلك هنا.

لم أفعل فحص مفصل للغاية، ولكن يبدو أن خوارزمية Nethack تعتمد بشدة على خوارزمية الكنيسة. يبدو أن خوارزمية Nethack، مثل خوارزمية الكنيسة، تولي اهتماما فقط لتاريخ التقويم، وتجاهل المناطق الزمنية ووقت اليوم.

تستخدم خوارزمية Nethack العام وعلى مدته العام. أستطيع أن أقول من فحص الكود الذي يتوافق مع Y2K، أن TM_YEAR يجب أن يكون العام ناقص 1900.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top