एक बरसाती दोपहर को आप रसोई की मेज़ पर अपनी दादी के साथ ताश खेलते हुए बैठे हैं। यह खेल Camicia का उनका अपना रूप है।
शुरू में यह बस एक और दोस्ताना मुकाबला ही लगता है: पत्ते पटकना, मेज़ के चारों ओर हँसी, और कभी-कभी Nonna की जीत भरी मुस्कान। लेकिन जैसे-जैसे खेल लंबा खिंचता जाता है, कुछ अजीब होने लगता है। वही पत्ते बार-बार लौटकर आते रहते हैं। आप एक के बाद एक पत्ता खेलते जाते हैं, फिर भी अंत आता ही नहीं दिखता।
आप सोचने लगते हैं। क्या यह खेल कभी खत्म होगा? या हम इसे हमेशा खेलते रह सकते हैं?
बाद में, जिज्ञासा के कारण आप ऑनलाइन खोजते हैं और आश्चर्य के साथ पता चलता है कि जो हुआ वह सिर्फ दुर्भाग्य नहीं था। हो सकता है कि आप और आपकी दादी सबसे लंबे संभव क्रमों में से किसी एक पर पहुँच गए हों! अचानक आप इस खेल में खो जाते हैं। जो एक साधारण खेल के रूप में शुरू हुआ था, वह अब एक खोज बन गया है: ऐसा खेल सचमुच कितनी देर चल सकता है? क्या आप उससे भी लंबा क्रम ढूँढ सकते हैं जो आपने रसोई की मेज़ पर खेला था? शायद इतना लंबा कि एक नया विश्व रिकॉर्ड बन जाए?
और इस तरह, हाथ में सिर्फ ताश की एक गड्डी और एल्गोरिद्म की थोड़ी-सी समझ लेकर, आप जाँच करने का मन बना लेते हैं...
इस अभ्यास में आप ताश के क्लासिक खेल Camicia जैसा एक खेल चलाने वाला प्रोग्राम लिखेंगे। आपका प्रोग्राम दोनों खिलाड़ियों की गड्डियों की शुरुआती स्थिति लेकर यह खेल तब तक चलाएगा जब तक कि वह खत्म न हो जाए, या यह पता न लगा ले कि वह कभी खत्म नहीं होगा।
यह एक छोटा उदाहरण है ऐसे खेल का जो खत्म हो जाता है।
| राउंड | खिलाड़ी 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 | - | - |
राउंड 4 की शुरुआत राउंड 2 की शुरुआत से मेल खाती है। याद रखिए, संख्या वाले पत्तों की वैल्यू से कोई फर्क नहीं पड़ता।
status: "loop", cards: 8, tricks: 3
"finished" या "loop"
जो लोग इससे भी अधिक रोमांचक चैलेंज लेना चाहते हैं, उनके लिए खबर है कि जिस खेल का अंत होता है, उसमें सबसे लंबे खेल के और रिकॉर्ड ढूँढने की तलाश अब भी जारी है। इसकी 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 को रनटाइम पर इस्तेमाल कर सकते हैं।
खाली डेक को उसके अगले कार्ड और बाकी कार्डों से अलग पहचानने के लिए [] और [card, .. as rest] के साथ match का उपयोग कीजिए।
{ ..state, pile: new_pile } जैसे रिकॉर्ड अपडेट खेल में होने वाले बदलावों को बिना म्यूटेबल स्थिति के स्पष्ट रख सकते हैं।
पहले देखी गई खेल स्थितियों पर नज़र रखते समय एक Set टुपल या लिस्ट को की के रूप में संग्रहीत कर सकता है।
Exercism पर साइन अप कीजिए और Roc को 135 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।