如何将这个NetHack函数移植到Python?

2024-10-02 18:16:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图编写一个Python函数,它返回与NetHack游戏中相同的月相值。这可以在hacklib.c中找到。在

我试图简单地从NetHack代码中复制相应的函数,但我不相信我得到了正确的结果。在

我写的函数是phase_of_the_moon()。在

我在网上找到的函数position()和{},我用它们来表示我的功能是否成功。它们非常精确,给出的结果与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中):

^{pr2}$

下面是我的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()

Tags: ofthe函数posltdatetimereturndays
3条回答

编写的代码在很大程度上是不稳定的,您需要使其具有可测试性。所以,你需要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读取,然后转换成适当的结构(通过C中的localtime())。然后你就能看到事情的偏差。在

编辑:我发现的两个“问题”都是基于对tm结构的误解。为了评论中的讨论,我将保留完整的答案,但请将您的投票留给可能真正正确的人。;—)


注意:我对C时结构不太熟悉;我主要是不熟悉为strftime提供的字段文档。在

我在你的港口看到两个“虫子”。首先,我认为tm_year应该是没有世纪的年份,而不是减去1900年的年份,因此,goldn应该是{}。其次,您对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

同样,这主要是猜测。请客气点。:-)

我已经迟到很久了,但是alt.org公司服务器通过网络显示的pom每天只在cron上更新几次,所以如果你离它稍微远一点,这可能是原因。游戏本身从nethack代码本身运行,因此不会遭受同样的缓存问题。-德鲁(alt.org公司业主)

相关问题 更多 >