ট্র্যাক
/
Cairo
Cairo
/
অনুশীলনী
/
রোল প্লেয়িং গেম
রোল প্লেয়িং গেম

রোল প্লেয়িং গেম

লার্নিং অনুশীলনী

ভূমিকা

নাল-রেফারেন্স

আপনি যদি আগে কখনও অন্য কোনো প্রোগ্রামিং ভাষা (C/C++, Python, Java, Ruby, Lisp ইত্যাদি) ব্যবহার করে থাকেন, তাহলে সম্ভবত আগেই null বা nil-এর সাথে আপনার পরিচয় হয়েছে। কোনো নির্দিষ্ট ভ্যারিয়েবলের কোনো মান নেই বোঝাতে এই ভাষাগুলো null বা nil ব্যবহার করে। কিন্তু এর ফলে ভুলে null ভ্যারিয়েবল ব্যবহার করা সহজ (এবং ঘন ঘন ঘটে যাওয়া) একটি ভুল হয়ে দাঁড়ায়। আপনি নিশ্চয়ই অনুমান করতে পারছেন, যে ফাংশনটি নেই সেটি কল করার চেষ্টা করা, বা যে মানটি নেই সেটি অ্যাক্সেস করার চেষ্টা করা নানা রকম bug ও ক্র্যাশের কারণ হতে পারে। null-এর স্রষ্টা তো এটিকে তাঁর 'billion-dollar mistake' নামে ডেকেছেন।

Option টাইপ

এই সমস্যাগুলো এড়াতে Cairo-র ভ্যারিয়েবলগুলো একটি মানের অনুপস্থিতিকে নিরাপদভাবে প্রকাশ করতে Option এনাম ব্যবহার করতে পারে। এই এনামের দুটি ভ্যারিয়েন্ট আছে: None, যা Cairo-তে null-এর সমতুল্য; এবং Some(T), যেখানে T যেকোনো টাইপের একটি মান।

এটি দেখতে এরকম:

enum Option<T> {
    None,
    Some(T),
}

Option-কে আপনি আপনার আর নাল-রেফারেন্সের কারণে সৃষ্ট সমস্যাগুলোর মাঝে নিরাপত্তার একটি স্তর হিসেবে ভাবতে পারেন, যা একই সাথে নাল-রেফারেন্সের ধারণাগত উপযোগিতা ধরে রাখে।

Option ব্যবহার করা

একটি ভ্যারিয়েবলকে None-এ সেট করা বেশ সহজ:

let nothing: Option<u32> = Option::None;  // Variable nothing is set to None

তবে, আপনি যদি চান Option টাইপ কোনো একটি মান বহন করুক, তাহলে সেই মানটি সরাসরি অ্যাসাইন করতে পারবেন না। একটি Option টাইপের ভ্যারিয়েবল আর ধরা যাক একটি i32 টাইপের ভ্যারিয়েবল এক নয়। আপনাকে Some ব্যবহার করতে হবে:

let wrong_way: Option<i32> = -4; // This will not work

let right_way: Option<i32> = Option::Some(-4); // This will work
let another_right_way = Option::Some(-4); // Compiler infers that this is Option<i32>

একই কারণে নিচের কোডটিও কাজ করবে না:

let number = 47;
let option_number = Option::Some(15);

let compile_error = number + option_number; // Cannot add an i32 and an Option<i32> - they are of different types

Some-এ থাকা মানটি পেতে হলে প্রথমে যাচাই করতে হবে যে এটি আছে কি না:

let mut some_words = Option::Some("choose something to say"); // some_words set to something

match some_words {
    Option::Some(str) => println!("Here, we will {}", str),
    Option::None => println!("I've got nothing to say"),
} // Prints, "Here, we will choose something to say"

some_words = Option::None; // some_words now set to None

// Exactly the same match block as above
match some_words {
    Option::Some(str) => println!("Here, we will {}", str),
    Option::None => println!("I've got nothing to say"),
} // Prints, "I've got nothing to say"

Option-এর ভেতরে থাকা মান যাচাই ও অ্যাক্সেস করার জন্য match ছাড়াও Cairo-র আরও কিছু টুল আছে, তবে match এখন আপনার কাছে পরিচিত হওয়ার কথা।

Cairo কেন নাল-রেফারেন্সের বদলে Option ব্যবহার করে, তার একটি প্রদর্শনী হিসেবে এটিকে দেখুন। মূল কথা হলো, Option ভ্যারিয়েবলটি Some (সেক্ষেত্রে আপনি এগিয়ে গিয়ে ভেতরে থাকা মানটি বের করে ব্যবহার করতে পারেন) নাকি None, তা আপনাকে অবশ্যই যাচাই করতে হবে। অন্যথায় আপনার প্রোগ্রাম কম্পাইল হবে না; কম্পাইলার আপনাকে null থেকে নিরাপদ রাখে।

নির্দেশনা

আপনি একটি রোল-প্লেয়িং গেম বানানোর কাজ করছেন। খেলোয়াড়ের চরিত্রটিকে প্রকাশ করা হয় এভাবে:

pub struct Player {
    health: u32,
    mana: Option<u32>,
    level: u32,
}

এই গেমে খেলোয়াড়দের লেভেল ১০-এ পৌঁছাতে হবে, তারপর তারা ম্যানা পুল আনলক করতে পারবে, যা দিয়ে স্পেল কাস্ট করা যায়। তার আগে Player-এর ম্যানা থাকে Option::None.

এই গেমে আপনি দুটি ফিচার নিয়ে কাজ করছেন: রিভাইভ মেকানিক এবং স্পেল কাস্টিং মেকানিক।

revive মেথডটি যাচাই করে নিশ্চিত করবে যে খেলোয়াড় সত্যিই মৃত (তাদের হেলথ ০-এ পৌঁছেছে), আর যদি তাই হয়, মেথডটি ১০০ হেলথ নিয়ে একটি নতুন Player ইনস্ট্যান্স রিটার্ন করবে। revive মেথডটি খেলোয়াড়ের লেভেল অপরিবর্তিত রাখবে। খেলোয়াড়ের লেভেল ১০ বা তার বেশি হলে, তাদের ১০০ ম্যানা নিয়ে রিভাইভ করা হবে। খেলোয়াড়ের লেভেল ১০-এর কম হলে, তাদের ম্যানা হবে Option::None.

let dead_player = Player { health: 0, mana: Option::None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: Option::None, level: 2 }

যদি এমন কোনো Player-এর উপর revive মেথড কল করা হয় যার হেলথ ১ বা তার বেশি, তাহলে মেথডটি Option::None রিটার্ন করবে।

let alive_player = Player { health: 1, mana: Option::Some(15), level: 11 };
alive_player.revive()
// Returns Option::None

cast_spell মেথডটি Player-এর একটি মিউটেবল রেফারেন্স এবং একটি mana_cost প্যারামিটার নেয়, যা বোঝায় স্পেলটির কত ম্যানা খরচ হয়। এটি রিটার্ন করে কাস্ট করা স্পেলটি কতটা ড্যামেজ করে তা।

  • খেলোয়াড়ের যদি ম্যানা পুলে প্রবেশাধিকার না থাকে, তবে স্পেল কাস্ট করার চেষ্টা করলে স্পেলটির ম্যানা খরচের সমান পরিমাণে তাদের হেলথ কমতে হবে। রিটার্ন করা ড্যামেজ অবশ্যই ০ হতে হবে।

    let not_a_wizard_yet = Player { health: 79, mana: Option::None, level: 9 };
    assert_eq!(not_a_wizard_yet.cast_spell(5), 0)
    assert_eq!(not_a_wizard_yet.health, 74);
    assert_eq!(not_a_wizard_yet.mana, Option::None);
    
  • খেলোয়াড়ের ম্যানা পুল থাকলেও যদি পর্যাপ্ত ম্যানা না থাকে, মেথডটি পুলে কোনো প্রভাব ফেলবে না। রিটার্ন করা ড্যামেজ অবশ্যই ০ হতে হবে।

    let low_mana_wizard = Player { health: 93, mana: Option::Some(3), level: 12 };
    assert_eq!(low_mana_wizard.cast_spell(10), 0);
    assert_eq!(low_mana_wizard.health, 93);
    assert_eq!(low_mana_wizard.mana, Option::Some(3));
    
  • অন্যথায়, স্পেলটি সফলভাবে কাস্ট হয়। Player-এর ম্যানা পুল থেকে mana_cost বাদ দিতে হবে। রিটার্ন করা ড্যামেজ অবশ্যই স্পেলটির ম্যানা খরচের দ্বিগুণ হতে হবে।

    let wizard = Player { health: 123, mana: Option::Some(30), level: 18 };
    assert_eq!(wizard.cast_spell(10), 20);
    assert_eq!(wizard.health, 123);
    assert_eq!(wizard.mana, Option::Some(20));
    

শুভকামনা!

GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
Cairo Exercism

রোল প্লেয়িং গেম শুরু করতে প্রস্তুত?

Exercism-এ সাইন আপ করুন, Cairo ট্র্যাকের 25টি কনসেপ্ট68টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।