تجزیهی رشتهی Smart Game Format.
SGF یک قالب استاندارد برای ذخیرهی فایلهای بازیهای رومیزی است، بهویژه بازی گو.
SGF قالبی نسبتاً ساده است. یک فایل SGF معمولاً شامل یک درخت از گرههاست که هر گره یک فهرست ویژگی است. فهرست ویژگی شامل جفتهای کلید-مقدار است؛ هر کلید فقط یکبار میتواند بیاید اما میتواند چندین مقدار داشته باشد.
در این تمرین باید یک رشتهی SGF را تجزیه کنید و یک ساختار درختی از ویژگیها برگردانید.
یک فایل SGF میتواند به این شکل باشد:
(;FF[4]C[root]SZ[19];B[aa];W[ab])
این درختی با سه گره است:
همانطور که میتوان تصور کرد، یک فایل SGF گرههای زیادی دارد که هرکدام یک فرزند دارند؛ به همین دلیل یک نوشتار کوتاه برایش وجود دارد.
SGF میتواند شاخههای مختلف بازی را کدگذاری کند. بازیکنان گو هنگام مرور بازیهایشان زیاد به عقب برمیگردند (بیایید این را امتحان کنیم، جواب نمیدهد، بیایید آن یکی را امتحان کنیم) و SGF از شاخههای مختلف دنبالههای بازی پشتیبانی میکند. مثلاً:
(;FF[4](;B[aa];W[ab])(;B[dd];W[ee]))
اینجا گره ریشه دو شاخه دارد. شاخهی اول (که بنا بر رسم نشان میدهد واقعاً چه بازی شده است) جایی است که سیاه روی ۱-۱ بازی میکند. معلم سیاه این فایل را برایش فرستاده بود و به حرکتی معقولتر در فرزند دوم گره ریشه اشاره کرده بود: B[dd] (نقطهی ۴-۴، شروعی بسیار استاندارد برای گرفتن گوشه).
یک کلید میتواند چندین مقدار متناظر با خود داشته باشد. مثلاً:
(;FF[4];AB[aa][ab][ba])
اینجا از AB (افزودن سیاه) برای افزودن سه مهرهی سیاه به صفحه استفاده شده است.
همهی مقادیر ویژگیها از نوع Text در SGF خواهند بود. لازم نیست نوع مقدار دیگری را پیادهسازی کنید. میتوانید مستندات کامل نوع Text را بخوانید، اما خلاصهای از نکات مهم در ادامه آمده است:
\ بیاید حذف میشود، در غیر این صورت به عنوان خط جدید باقی میماند.\ نویسهی فرار است.
هر نویسهی غیرفاصلهی سفید بعد از \ همانطور که هست درج میشود.
هر نویسهی فاصلهی سفید بعد از \ از قواعد بالا پیروی میکند.
توجه کنید که SGF برای نویسههای فاصلهی سفید مانند \t یا \n توالی فرار ندارد.مراقب باشید که این دو را با هم اشتباه نگیرید:
ممکن است توالیهای فرار در مقدارهای رشتهای، پیش از آنکه به تجزیهگر SGF داده شوند، توسط تجزیهگر زبان برنامهنویسی پردازش شده باشند.
در SGF (و بهطور کلی در تجزیه) پیچیدگیهای دیگری هم وجود دارد که میتوانید بیشترشان را نادیده بگیرید. فرض کنید که ورودی با UTF-8 کدگذاری شده است؛ تستها ویژگی charset ندارند، پس نگران آن نباشید. بهعلاوه میتوانید فرض کنید که همهی خطهای جدید به سبک یونیکس هستند (\n؛ هیچ \r یا \r\n در تستها نخواهد بود) و اینکه هیچ فاصلهی سفید اختیاری میان ویژگیها، گرهها و غیره در تستها وجود ندارد.
گاهی لازم است استثنا ایجاد کنید. در این حالت، همیشه باید یک پیام خطای معنادار بنویسید تا نشان دهید منبع خطا چیست. این کار کد شما را خواناتر میکند و به Debug کمک زیادی میکند. در موقعیتهایی که میدانید منبع خطا از یک نوع مشخص است، میتوانید یکی از انواع استثنای توکار را ایجاد کنید، اما باز هم باید پیام معناداری بنویسید.
این تمرین بهطور خاص میخواهد که از دستور raise استفاده کنید تا اگر ورودی جداکنندههای مناسب را نداشته باشد، با حروف بزرگ نوشته نشده باشد، درختی با گرهها نسازد، یا اصلاً درختی نسازد، یک ValueError «پرتاب» کنید. Testها فقط زمانی قبول میشوند که هم exception را raise کنید و هم پیامی همراه آن بفرستید.
برای اینکه یک ValueError همراه با پیام ایجاد کنید، پیام را بهعنوان آرگومان به نوع exception بدهید:
# if the tree properties as given do not have proper delimiters.
raise ValueError("properties without delimiter")
# if the tree properties as given are not all in uppercase.
raise ValueError("property must be in uppercase")
# if the input does not form a tree, or is empty.
raise ValueError("tree missing")
# if the input is a tree without any nodes.
raise ValueError("tree with no nodes")