ট্র্যাক
/
C++
C++
/
অনুশীলনী
/
ট্রলদের ট্রল
ট্রলদের ট্রল

ট্রলদের ট্রল

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

ভূমিকা

এনামারেশন

এখন পর্যন্ত আমরা মূলত সংখ্যা আর স্ট্রিং নিয়েই কাজ করেছি। বাস্তব জগৎকে মডেল করতে আমরা চাইতে পারি, একটি ভ্যারিয়েবল যেন কেবল সীমিত সংখ্যক মানের যেকোনো একটি গ্রহণ করে। আপনি হয়তো একটি আলাদা টাইপ চাইতে পারেন, যার কয়েকটি স্বতন্ত্র মান থাকবে আলাদা আলাদা নামের। উদাহরণস্বরূপ, একটি স্কেটবোর্ড কারখানায় ডেকের উপাদান কেবল maple, bamboo বা plastic-এর মধ্যে একটি বেছে নেওয়ার সুযোগ থাকতে পারে।

ওই মানগুলো এনকোড করতে আপনি ইন্টিজার ব্যবহার করতে পারেন, তবে উপাদানের জন্য সিস্টেম থেকে কোনো অবৈধ মান আসছে কি না তা যাচাই করতে বাড়তি কোড লিখতে হবে। সোর্স কোডের ওপর দিয়ে দেখতে গেলে ওই ম্যাজিক নাম্বারগুলোর অর্থ খুঁজে পাওয়া কঠিন, আর এগুলোতে ভুল হয়ে যাওয়ার ঝুঁকি থাকে। enumerations ব্যবহার করে প্রকাশভঙ্গিময় কোড উৎসাহিত করা যায় এবং অনিচ্ছাকৃত তুলনার ভুল সীমিত করা যায়।

ব্যবহারের উদাহরণ

এই ধরনের এনামারেশনের নির্দিষ্ট পরিভাষা হলো scoped enumeration। নিচের স্নিপেটে দেখানো হয়েছে কীভাবে একটি DeckMaterial enumeration লিখতে হয়। সংজ্ঞার শেষে enum class কিওয়ার্ড আর ; খেয়াল করুন:

enum class DeckMaterial {
    maple,
    bamboo,
    plastic
};

এখন স্কেট শপের একটি দাম নির্ধারণের ফাংশন দেখুন এবং খেয়াল করুন, স্কোপ রেজলিউশন অপারেটর (::) কীভাবে enumeration থেকে একটি enumerator নির্দিষ্ট করছে:

double deck_price(double base_price, DeckMaterial material) {
    if(material == DeckMaterial::plastic) {
        return base_price * 0.9;
    }
    return base_price * 1.3;
}

স্কোপিং

ভাবুন, চাকার উপাদানের জন্য আপনার আরেকটি enumeration আছে:

enum class WheelMaterial {
    steel,
    clay,
    plastic
};

যদিও চাকা আর ডেক দুটোই plastic দিয়ে তৈরি হতে পারে, তবু এ দুটোকে গুলিয়ে ফেলার সুযোগ নেই। এগুলো ভিন্ন ভিন্ন টাইপ: DeckMaterial plastic আর WheelMaterial plastic। প্রতিটি enumeration-এর enumerators থাকবে তার নিজের স্কোপে, নিজের namespace-এ। এ কারণেই এগুলোকে scoped enumerations বলা হয়।

Advanced

আনস্কোপড এনামারেশন

নামটা যখন scoped, আপনি হয়তো ভাবছেন নিশ্চয়ই unscoped এনামারেশনও আছে, আর আপনি ঠিকই ভাবছেন। Unscoped enumerations ক্রমশ কম জনপ্রিয় হয়ে উঠছে, কারণ এগুলো সবই একই গ্লোবাল নেমস্পেস শেয়ার করে। এই শেয়ারিংয়ের কারণেই উপরের উদাহরণের মতো plastic-এর অনুরূপ enumerators নিয়ে দুটি unscoped enumerations থাকতে পারে না।

এছাড়া unscoped enumerations নিজে থেকেই ইন্টিজারে রূপান্তরিত হয়। একটি চমকপ্রদ ফলাফলের জন্য নিচের উদাহরণটি দেখুন:

enum CitrusFruits {
    lemons,  // 0
    oranges, //1
};

enum IceCream {
    walnut,  // 0
    apples,  // 1
};

bool comparison{apples == oranges};
// => true

// Example from above:
bool comparison{DeckMaterial::plastic == WheelMaterial::plastic};
// => Does not compile!

আপনি যদি scoped enumerations-কে ইন্টিজারে রূপান্তর করতে চান, তাহলে static_cast<int> ব্যবহার করতে পারেন।

সুইচ স্টেটমেন্ট

অন্য ভাষাগুলোর মতো C++-ও একটি switch স্টেটমেন্ট দেয়। দীর্ঘ if ... else if স্টেটমেন্ট লেখার একটি ছোট উপায় হলো সুইচ স্টেটমেন্ট। সুইচ বানাতে আমরা প্রথমে switch কিওয়ার্ডটি ব্যবহার করি, তারপরে একটি ইন্টিজার দিই। এরপর case কিওয়ার্ড দিয়ে প্রতিটি শর্ত আলাদা করে ঘোষণা করি। আমরা একটি default কেসও ঘোষণা করতে পারি, যা আগের কোনো case শর্ত মিল না গেলে চলবে। প্রতিটি কেস break (বা return) স্টেটমেন্ট দিয়ে শেষ হওয়া উচিত।

int price{0};
int adults{3};
int kids{2};

switch (int group_size{adults + kids}) {
    case 1:
        price = 50;
        break;
    case 2:
        price = 70;
        break;
    default:
        price = group_size * 30;
}

ফল-থ্রু

সুইচ কনস্ট্রাক্ট নিয়ে একটি গুরুত্বপূর্ণ বিষয় হলো, কোড চলতেই থাকবে যতক্ষণ না কোনো break (বা return) স্টেটমেন্ট দিয়ে তা থামানো হয়। এতে অপ্রত্যাশিত আচরণ হতে পারে।

int adults{1};
int kids{0};
switch (int group_size{adults + kids}) {
    case 1:
        price = 50;
    case 2:
        price = 70;
    default:
        price = group_size * 30;
}
// price will be 30!

এই ধারাবাহিকভাবে চলার বৈশিষ্ট্যের প্রধান ব্যবহার হলো এমন একটি স্টেটমেন্ট, যার একাধিক লেবেল থাকে। সুইচের একাধিক ফলাফল একই কোডখণ্ডে নির্দেশ করতে পারে, যা নির্বাহ করা হবে। এভাবে, উদাহরণস্বরূপ একটি বুকিং অ্যাপে, ২ ও ৩ মাপের গ্রুপের জন্য কল করা ফাংশন একই হতে পারে:

switch (group_size) {
case 1:
    book_room();
    break;
case 2:
case 3:
    book_apartment(group_size);
    break;
default:
    book_house(group_size);
}
// book_apartment happens when group_size is 2 or 3

নির্দেশনা

আপনার বন্ধু হেলমা একটি ছোট অনলাইন গেম বানিয়েছিলেন, যা দ্রুত জনপ্রিয়তা পায়। এর নাম HellMath। ছোট্ট এই কমিউনিটিতে কিছু ট্রল ভিড় করে, যারা গেম আর ফোরামকে বেশ অপ্রীতিকর করে তোলে। ঝামেলাকারীদের আলাদা করার জন্য হেলমা আপনাকে একটি নতুন পারমিশন সিস্টেম নিয়ে কাজ করতে অনুরোধ করেছেন।

ফোরাম তিনটি আলাদা অ্যাকশন সমর্থন করে:

  • Read
  • Write
  • Remove

চার ধরনের অ্যাকাউন্ট আছে, প্রতিটির ডিফল্ট পারমিশন আলাদা:

  • গেস্ট: পোস্ট পড়তে পারে।
  • ইউজার ও ট্রল: পোস্ট পড়তে ও লিখতে পারে।
  • মডারেটর: পোস্ট পড়তে, লিখতে ও মুছে ফেলতে পারে, তাদের সব পারমিশন থাকে।

হেলমা লক্ষ্য করেছেন যে ট্রল অ্যাকাউন্ট ব্যান করা কোনো কাজে আসে না। তার কৌশল হলো তাদের এই ভ্রম দেওয়া যে তাদের সময় "ভালোভাবে বিনিয়োগ" হয়েছে, কিন্তু তাদের পোস্ট কেবল অন্য ট্রলদেরই দেখানো হয়। অগ্রাধিকার নির্ধারণের প্রয়োজন হয় এমন যেকোনো ক্ষেত্রে, ট্রলরা যেকোনো ক্রমে সবার শেষে থাকে। তারা যখন কোনো গেমে ঢোকে, তখন পাওয়া যাওয়া প্লেয়ারদের তালিকাও কেবল অন্য ট্রলদের মধ্যে সীমাবদ্ধ থাকে।

এই পদ্ধতিটিকে বলা হয় শ্যাডো-ব্যানিং।

1. ইউজার টাইপ ও পারমিশন সেট আপ করুন।

প্রথমে, চারটি অ্যাকাউন্ট টাইপ বোঝাতে একটি AccountStatus এনামারেশন ডিফাইন করুন: troll, guest, user, এবং mod।

এরপর, তিনটি পারমিশন টাইপ বোঝাতে একটি Action এনামারেশন ডিফাইন করুন: read, write, এবং remove।

2. ট্রলদের কেবল অন্য ট্রলদের কাছেই দেখান।

ফোরামের প্রতিটি পোস্ট তার মেটাডেটায় পোস্টকারীর AccountStatus সংরক্ষণ করে। নিশ্চিত করুন যে ট্রলদের পোস্ট কেবল অন্য ট্রলদেরই দেখানো হয়।

হেলমার একটি display_post ফাংশন দরকার, যা AccountStatus টাইপের দুটি আর্গুমেন্ট নেয় এবং একটি bool রিটার্ন করে। প্রথম আর্গুমেন্টটি পোস্টকারীর স্ট্যাটাস, দ্বিতীয়টি দর্শকের স্ট্যাটাস।

using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true

3. বিভিন্ন ইউজারের পারমিশন যাচাই করুন।

কোনো নির্দিষ্ট অ্যাকশন একটি ইউজারের জন্য অনুমোদিত কি না, তা যাচাই করার একটি উপায় হেলমার দরকার।

অনুগ্রহ করে একটি permission_check ফাংশন ইমপ্লিমেন্ট করুন, যা প্রথম আর্গুমেন্ট হিসেবে একটি Action নেয় এবং যাচাই করার জন্য একটি AccountStatus নেয়। ভূমিকায় তালিকাভুক্ত পারমিশন অনুযায়ী এটি একটি bool রিটার্ন করবে।

permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true

4. গেমের অ্যাক্সেস দিন এবং প্লেয়ারদের জোড়া মেলান।

গেমের প্রকৃত প্লেয়ারদের তাদের কাজের জবাবদিহির মধ্যে রাখতে, Hellmath গেস্ট ইউজারদের অ্যাক্সেস দেয় না। উপরে বলা হয়েছে, হেলমা চান ট্রলরা অন্য ট্রলদের ট্রল করুক। অন্য ইউজারদের মধ্যে গেম সংযোগে কোনো বিধিনিষেধ নেই।

valid_player_combination ফাংশনটি ইমপ্লিমেন্ট করুন, যা যাচাই করে দুজন প্লেয়ার একই গেমে যোগ দিতে পারে কি না। ফাংশনটির AccountStatus টাইপের দুটি প্যারামিটার আছে এবং এটি একটি bool রিটার্ন করে।

valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true

5. অগ্রাধিকারভিত্তিক কিউ তৈরি করুন।

গেম ও ফোরামের বিশাল প্রসারের সাথে, হেলমাকে এখন ইউজারদের মধ্যে কম্পিউটিং পাওয়ার আর ব্যান্ডউইথ ভাগ করে দিতে হয়। জরুরি পরিস্থিতি সামলাতে মডারেটরদের সর্বোচ্চ অগ্রাধিকার দেওয়া হয়। গেস্টরা সাধারণ ইউজারদের পেছনে কিউতে থাকে, আর ট্রলরা সবার পেছনে চলে যায়।

has_priority ফাংশনটি ইমপ্লিমেন্ট করুন, যা দুটি AccountStatus আর্গুমেন্ট নেয় এবং true রিটার্ন করে যদি এবং কেবল যদি প্রথম অ্যাকাউন্টের অগ্রাধিকার দ্বিতীয়টির চেয়ে কঠোরভাবে বেশি হয়।

has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true
GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
C++ Exercism

ট্রলদের ট্রল শুরু করতে প্রস্তুত?

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