Camicia

Camicia

متوسط

مقدمة

في أصيل يومٍ ممطر، تجلس إلى طاولة المطبخ تلعب الورق مع جدّتك. اللعبة هي نسختها الخاصة من Camicia.

في البداية تبدو مجرد مباراة ودّية أخرى: أوراق تُلقى على الطاولة، وضحكات تتعالى حولها، وابتسامة انتصار عابرة من نونا. لكن مع استمرار اللعبة، يحدث شيء غريب. الأوراق ذاتها تظل تدور عائدة من جديد. تلعب ورقة تلو الأخرى، ومع ذلك لا تبدو النهاية قادمة أبدًا.

تبدأ تتساءل. هل ستنتهي هذه اللعبة يومًا؟ أم هل يمكن أن نواصل اللعب إلى الأبد؟

لاحقًا، وبدافع الفضول، تبحث على الإنترنت، ولدهشتك تكتشف أن ما حدث لم يكن مجرد سوء حظ. ربما صادفت أنت وجدّتك واحدة من أطول المتتاليات الممكنة! وفجأة تجد نفسك مأسورًا. ما بدأ كلعبة عابرة تحوّل إلى سعي: كم يمكن أن تدوم لعبة كهذه حقًا؟ هل يمكنك أن تجد متتالية أطول مما لعبتها على طاولة المطبخ؟ ربما حتى طويلة بما يكفي لتحقيق رقم قياسي عالمي جديد؟

وهكذا، وأنت لا تملك سوى رزمة أوراق لعب وقليلٍ من البراعة الخوارزمية، تقرّر أن تحقّق في الأمر...

التعليمات

في هذا التمرين، ستُحاكي لعبة شديدة الشبه بلعبة الورق الكلاسيكية Camicia. سيستقبل برنامجك التهيئة الأولية لرزمتي لاعبين، وعليه محاكاة اللعبة حتى تنتهي (أو يكتشف أنها لن تنتهي أبدًا).

القواعد

  • تُقسَّم الرزمة بين لاعبين. تُقرأ بطاقات اللاعب من اليسار إلى اليمين، حيث تكون البطاقة في أقصى اليسار هي أعلى الرزمة.
  • تتكوّن الجولة من قيام كلا اللاعبين بلعب بطاقة واحدة على الأقل.
  • يتناوب اللاعبون على وضع البطاقة العليا من رزمتهم على كومة مركزية.
  • إذا كانت البطاقة بطاقة عددية (2-10)، ينتقل الدور ببساطة إلى اللاعب الآخر.
  • إذا كانت البطاقة بطاقة دفع، فيجب دفع عقوبة:
    • J → يجب على الخصم دفع بطاقة واحدة
    • Q → يجب على الخصم دفع بطاقتين
    • K → يجب على الخصم دفع 3 بطاقات
    • A → يجب على الخصم دفع 4 بطاقات
  • إذا كشف اللاعب الذي يدفع العقوبة عن بطاقة دفع أخرى، يتوقف ذلك اللاعب عن دفع العقوبة. وعندئذ يجب على اللاعب الآخر دفع عقوبة بناءً على بطاقة الدفع الجديدة.
  • إذا دُفعت العقوبة كاملة دون مقاطعة، فإن اللاعب الذي وضع آخر بطاقة دفع يجمع الكومة المركزية ويضعها في أسفل رزمته. ثم يبدأ ذلك اللاعب الجولة التالية.
  • إذا نفدت بطاقات أحد اللاعبين ولم يتمكن من لعب بطاقة (سواء أثناء دفع العقوبة أو عندما يحين دوره)، فإن اللاعب الآخر يجمع الكومة المركزية.
  • اللحظة التي يجمع فيها لاعب البطاقات من الكومة المركزية تسمى لقطة.
  • إذا كان لدى لاعب جميع البطاقات في حوزته بعد لقطة، فإن اللعبة تنتهي.
  • تدخل اللعبة في حلقة بمجرد أن تصبح الرزم مطابقة لما كانت عليه سابقًا أثناء اللعبة، دون احتساب البطاقات العددية!

أمثلة

مثال صغير لمباراة تنتهي.

الجولة اللاعب أ اللاعب ب الكومة العقوبة المستحقة
1 2 A 7 8 Q 10 3 4 5 6 K 9 J -
1 A 7 8 Q 10 3 4 5 6 K 9 J 2 -
1 A 7 8 Q 10 4 5 6 K 9 J 2 3 -
1 7 8 Q 10 4 5 6 K 9 J 2 3 A اللاعب ب: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 اللاعب ب: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 اللاعب ب: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 اللاعب ب: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K اللاعب أ: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 اللاعب أ: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 اللاعب أ: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q اللاعب ب: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 اللاعب ب: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J اللاعب أ: 1
1 - - 2 3 A 4 5 6 K 7 8 Q 9 J 10 -
2 - 2 3 A 4 5 6 K 7 8 Q 9 J 10 - -

status: "finished", cards: 13, tricks: 1

هذا مثال صغير لمباراة تدخل في حلقة.

الجولة اللاعب أ اللاعب ب الكومة العقوبة المستحقة
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J اللاعب ب: 1
1 2 3 J 5 J 4 -
2 2 3 J 4 J 5 - -
2 3 J 4 J 5 2 -
2 3 J 4 5 2 J اللاعب أ: 1
2 J 4 5 2 J 3 -
3 J 4 5 2 J 3 - -
3 J 4 2 J 3 5 -
3 4 2 J 3 5 J اللاعب ب: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

بداية الجولة 4 تطابق بداية الجولة 2. تذكّر أن قيمة البطاقات العددية لا تهم.

status: "loop", cards: 8, tricks: 3

مهمتك

  • باستخدام المدخلات، حاكِ اللعبة وفقًا للقواعد أعلاه.
  • حدّد المعلومات التالية بشأن اللعبة:
    • الحالة: "finished" أو "loop"
    • البطاقات: العدد الإجمالي للبطاقات التي لُعبت طوال اللعبة
    • اللقطات: عدد المرات التي جُمعت فيها الكومة المركزية
Advanced

لمن يرغب في خوض تحدٍ أكثر إثارة، لا يزال البحث عن أرقام قياسية أخرى لأطول لعبة لها نهاية مفتوحًا. هناك 653,534,134,886,878,245,000 (حوالي 654 كوينتليون) احتمال، ولم نحسبها كلها بعد!

مزايا from_quote وعيوبه

تتولّى دالة Card.from_quote تحليل سلسلة نصية إلى Card. هذا يجعل من الممكن كتابة كود مثل هذا:

card : Card
card = "Q"

يستدعي هذا الكود Card.from_quote("Q") ضمنيًا في وقت الترجمة. يمكن أن يؤدي تشغيل الكود في وقت الترجمة بهذا الأسلوب (أو بأساليب أخرى مشروحة أدناه) إلى تسريع البرنامج، لأنه لن يحتاج إلى التعامل مع التحليل والتحقق من صحة البيانات في وقت التشغيل. علاوة على ذلك، يضمن رفض البيانات غير الصالحة (مثل تنسيق تاريخ غير صحيح) في وقت الترجمة بدلًا من وقت التشغيل، مما يجعل تطبيقك أكثر متانة. وأخيرًا، يجعل الكود جميلًا وسهل القراءة. على سبيل المثال، إليك أحد الاختبارات في هذا التمرين:

result = simulate_game({
    player_a: ["2", "A"],
    player_b: ["3", "4", "5", "6", "K"],
})

لكن قد يفترض من يقرأ هذا الكود أن دالة simulate_game تأخذ سجلًا يحتوي على مصفوفتين من النوع List(Str)، بينما هو في الحقيقة سجل يحتوي على مصفوفتين من النوع List(Card). قد يؤدي هذا إلى الالتباس والأخطاء. لهذا السبب، قد تفضّل الصيغة الأطول قليلًا لكن الأكثر وضوحًا: "Q".Card، وهي أيضًا تستدعي from_quote في وقت الترجمة.

في برامجك الخاصة، قد تفضّل استخدام from_str بدلًا من ذلك:

Card := { ... }.{
    from_str : Str -> Try(Card, [InvalidCard])
    from_str = |card_str| ...
}

يمكنك أيضًا استدعاء from_str (أو أي دالة نقية أخرى) في وقت الترجمة، مثل هذا:

Ok(card) = Card.from_str("Q")

ملاحظة: وضع هذا التعريف في المستوى الأعلى يجعله يعمل في وقت الترجمة (لأن الدالة نقية ومدخلها معلوم في وقت الترجمة). داخل دالة، يمكنك استخدام from_str في وقت التشغيل.

تلميحات

إظهار التلميحات

استخدم match مع [] و[card, .. as rest] للتمييز بين رزمة فارغة وبطاقتها التالية وبطاقاتها المتبقية. يمكن أن تُبقي تحديثات السجل مثل { ..state, pile: new_pile } انتقالات اللعبة صريحة دون حالة قابلة للتغيير. يمكن لـ Set تخزين صفوف أو مصفوفات كمفاتيح عند تتبّع حالات اللعبة التي رُئيت سابقًا.


المصدر

Beggar-My-Neighbourيفتح الرابط في نافذة أو علامة تبويب جديدة
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Roc Exercism

مستعد لبدء Camicia؟

سجّل في Exercism لتتعلّم وتتقن Roc عبر 135 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.