এক বৃষ্টিভেজা বিকেলে আপনি রান্নাঘরের টেবিলে আপনার ঠাকুমার সঙ্গে তাস খেলতে খেলতে বসে আছেন। খেলাটি আসলে Camicia-এর তার নিজস্ব একটি সংস্করণ।
শুরুতে ব্যাপারটা মনে হয় আর দশটা বন্ধুত্বপূর্ণ খেলার মতোই: টেবিলে ছুঁড়ে ফেলা তাস, হাসির রোল, আর মাঝেমধ্যে Nonna-র জয়ের হাসি। কিন্তু খেলা যত গড়াতে থাকে, ততই অদ্ভুত কিছু একটা ঘটতে শুরু করে। একই তাসগুলো বারবার ঘুরে ফিরে আসতেই থাকে। আপনি একের পর এক তাস খেলতে থাকেন, তবু যেন শেষ আসতেই চায় না।
আপনার মনে প্রশ্ন জাগতে শুরু করে। এই খেলা কি কখনও শেষ হবে? নাকি আমরা চিরকাল খেলতেই থাকতে পারি?
পরে কৌতূহলের বশে আপনি অনলাইনে খোঁজ নেন, আর অবাক হয়ে আবিষ্কার করেন যে যা ঘটেছে তা নিছক খারাপ ভাগ্য ছিল না। আপনি আর আপনার ঠাকুমা হয়তো সম্ভাব্য সবচেয়ে দীর্ঘ সিকোয়েন্সগুলোর একটি পেয়ে ফেলেছেন! হঠাৎ করেই আপনি এর প্রতি আকৃষ্ট হয়ে পড়েন। যা শুরু হয়েছিল একটি সাধারণ খেলা হিসেবে, তা এখন এক অভিযানে পরিণত হয়েছে: এমন একটি খেলা সত্যিই কতক্ষণ চলতে পারে? আপনি কি রান্নাঘরের টেবিলে খেলা সিকোয়েন্সটির চেয়েও দীর্ঘ কোনো সিকোয়েন্স খুঁজে বের করতে পারবেন? হয়তো এতটাই দীর্ঘ যে একটি নতুন বিশ্ব রেকর্ড গড়া যায়?
আর তাই, হাতে শুধু এক ডেক তাস আর অ্যালগরিদমিক বুদ্ধিমত্তা নিয়েই, আপনি বিষয়টি খতিয়ে দেখার সিদ্ধান্ত নেন...
এই অনুশীলনীতে আপনি ক্লাসিক কার্ড গেম কামিচিয়া-এর সাথে খুব মিল এমন একটি গেম সিমুলেট করবেন। আপনার প্রোগ্রাম দুজন খেলোয়াড়ের ডেকের প্রাথমিক অবস্থা পাবে এবং গেমটি শেষ হওয়া পর্যন্ত সিমুলেট করবে (অথবা গেমটি কখনোই শেষ হবে না বলে শনাক্ত করবে)।
শেষ হয়ে যাওয়া একটি ম্যাচের ছোট উদাহরণ।
| রাউন্ড | খেলোয়াড় A | খেলোয়াড় B | পাইল | প্রদেয় পেনাল্টি |
|---|---|---|---|---|
| 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 | খেলোয়াড় B: 4 |
| 1 | 7 8 Q 10 | 5 6 K 9 J | 2 3 A 4 | খেলোয়াড় B: 3 |
| 1 | 7 8 Q 10 | 6 K 9 J | 2 3 A 4 5 | খেলোয়াড় B: 2 |
| 1 | 7 8 Q 10 | K 9 J | 2 3 A 4 5 6 | খেলোয়াড় B: 1 |
| 1 | 7 8 Q 10 | 9 J | 2 3 A 4 5 6 K | খেলোয়াড় A: 3 |
| 1 | 8 Q 10 | 9 J | 2 3 A 4 5 6 K 7 | খেলোয়াড় A: 2 |
| 1 | Q 10 | 9 J | 2 3 A 4 5 6 K 7 8 | খেলোয়াড় A: 1 |
| 1 | 10 | 9 J | 2 3 A 4 5 6 K 7 8 Q | খেলোয়াড় B: 2 |
| 1 | 10 | J | 2 3 A 4 5 6 K 7 8 Q 9 | খেলোয়াড় B: 1 |
| 1 | 10 | - | 2 3 A 4 5 6 K 7 8 Q 9 J | খেলোয়াড় A: 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
এটি লুপে পড়ে যাওয়া একটি ম্যাচের ছোট উদাহরণ।
| রাউন্ড | খেলোয়াড় A | খেলোয়াড় B | পাইল | প্রদেয় পেনাল্টি |
|---|---|---|---|---|
| 1 | J 2 3 | 4 J 5 | - | - |
| 1 | 2 3 | 4 J 5 | J | খেলোয়াড় B: 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 | খেলোয়াড় A: 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 | খেলোয়াড় B: 1 |
| 3 | 4 | J 3 | 5 J 2 | - |
| 4 | 4 5 J 2 | J 3 | - | - |
৪ নম্বর রাউন্ডের শুরু ২ নম্বর রাউন্ডের শুরুর সাথে মিলে যায়। মনে রাখবেন, সংখ্যা কার্ডগুলোর মান কোনো ব্যাপার নয়।
status: "loop", cards: 8, tricks: 3
"finished" অথবা "loop"
যারা আরও রোমাঞ্চকর চ্যালেঞ্জ নিতে চান, তাদের জন্য সুখবর হলো, শেষ হয় এমন দীর্ঘতম গেমের আরও রেকর্ডের খোঁজ এখনো চলছে। এমন 653,534,134,886,878,245,000টি (প্রায় ৬৫৪ কুইন্টিলিয়ন) সম্ভাবনা আছে, আর আমরা এখনো সেগুলোর সবগুলো হিসাব করে উঠতে পারিনি!
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 ব্যবহার করতে পারেন।
খালি ডেককে তার পরের কার্ড ও বাকি কার্ডগুলো থেকে আলাদা করতে [] এবং [card, .. as rest] দিয়ে match ব্যবহার করুন।
{ ..state, pile: new_pile }-এর মতো রেকর্ড আপডেট মিউটেবল স্টেট ছাড়াই গেমের ট্রানজিশনগুলো স্পষ্ট রাখতে পারে।
আগে দেখা গেম স্টেটগুলো ট্র্যাক করার সময় একটি Set টাপল বা অ্যারেকে কী (key) হিসেবে সংরক্ষণ করতে পারে।
Exercism-এ সাইন আপ করুন, Roc ট্র্যাকের 135টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।