البرمجة

برمجة لعبة تخمين الأرقام

برمجة لعبة تخمين الأرقام بلغة رست Rust: دليل شامل ومفصل

تعتبر لعبة تخمين الأرقام من الألعاب الكلاسيكية البسيطة التي تستخدم كتمرين برمجي لتعلم مفاهيم أساسية في لغات البرمجة، إذ يمكن برمجتها بسهولة، لكنها تحتوي في الوقت نفسه على عناصر تجعلها مثالية لتطبيق مفاهيم البرمجة مثل التحكم في التدفق، التكرار، الإدخال والإخراج، والتعامل مع الأخطاء. في هذا المقال، سنتناول برمجة لعبة تخمين الأرقام باستخدام لغة رست (Rust)، وهي لغة برمجة حديثة تجمع بين الأداء العالي والأمان في إدارة الذاكرة، مما يجعلها خياراً ممتازاً لتطوير البرامج ذات الجودة العالية.

مقدمة حول لغة رست Rust

قبل الخوض في تفاصيل برمجة اللعبة، من المهم فهم ما تتميز به لغة رست. طُورت Rust لتكون لغة برمجة نظامية، توفر أمان الذاكرة بدون الحاجة إلى جامع قمامة (Garbage Collector)، بالإضافة إلى أداء عالي يشبه C وC++. تتميز رست بنظام أنواع قوي، وإدارة ذكية للذاكرة، وهي مفضلة بشكل متزايد لتطوير التطبيقات التي تتطلب كفاءة عالية.

من مميزات رست التي تفيدنا في تطوير لعبة تخمين الأرقام:

  • الأمان ضد الأخطاء الشائعة: مثل الوصول غير المصرح به للذاكرة (buffer overflow) والتسريبات.

  • أنظمة التحكم في الأخطاء: مثل نظام Result وOption لإدارة الأخطاء بشكل واضح ومنظم.

  • سهولة التعامل مع السلاسل النصية: والتعامل مع الإدخال والإخراج.

الآن مع هذه الخلفية، نبدأ في شرح كيفية بناء لعبة تخمين الأرقام خطوة بخطوة.

فكرة اللعبة

لعبة تخمين الأرقام تتلخص في أن يقوم البرنامج باختيار رقم عشوائي في نطاق محدد، ثم يطلب من المستخدم إدخال تخمينه لهذا الرقم. البرنامج يعطي المستخدم تلميحات مثل “الرقم أعلى” أو “الرقم أقل” بناءً على تخمين المستخدم، حتى يتمكن المستخدم من تخمين الرقم الصحيح.

هذه اللعبة ستساعد في تعلم العديد من المفاهيم البرمجية الأساسية مثل:

  • توليد الأرقام العشوائية

  • قراءة إدخال المستخدم

  • التحقق من صحة الإدخال

  • استخدام الحلقات والتكرار

  • التعامل مع الأخطاء

إعداد البيئة وتثبيت Rust

لبدء البرمجة بلغة Rust، يجب أولاً تثبيت الأدوات اللازمة:

  1. تنزيل وتثبيت Rust من الموقع الرسمي عبر أداة rustup التي تدير إصدار اللغة بسهولة.

  2. التحقق من تثبيت Rust باستخدام الأمر في الطرفية:

    css
    rustc --version
  3. إنشاء مشروع جديد باستخدام أداة Cargo التي تأتي مع Rust:

    arduino
    cargo new guessing_game cd guessing_game

هذا الإعداد يتيح بيئة منظمة مع ملف Cargo.toml لإدارة الاعتمادات والبناء.

كتابة الكود خطوة بخطوة

1. استيراد الحزم اللازمة

في البداية، نحتاج إلى استيراد مكتبة rand لتوليد الأرقام العشوائية، وهي مكتبة خارجية يجب إضافتها إلى ملف Cargo.toml ضمن قسم [dependencies] كالتالي:

toml
[dependencies] rand = "0.8"

بعد ذلك، يمكننا استخدام المكتبة في الكود عبر:

rust
use rand::Rng; use std::io;

حيث أن rand::Rng يوفر وظائف لتوليد أرقام عشوائية، و std::io تستخدم لقراءة إدخال المستخدم.

2. إنشاء الدالة الرئيسية main

الدالة main هي نقطة البداية لأي برنامج Rust. داخلها سنضع منطق اللعبة.

rust
fn main() { println!("مرحباً بك في لعبة تخمين الأرقام!"); let secret_number = rand::thread_rng().gen_range(1..=100); println!("لقد اخترت رقماً سرياً بين 1 و 100. حاول تخمينه!"); loop { println!("من فضلك أدخل تخمينك:"); let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("فشل في قراءة الإدخال"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => { println!("من فضلك أدخل رقماً صحيحاً."); continue; } }; println!("لقد خمّنت: {}", guess); match guess.cmp(&secret_number) { std::cmp::Ordering::Less => println!("رقمك أقل من الرقم السري."), std::cmp::Ordering::Greater => println!("رقمك أكبر من الرقم السري."), std::cmp::Ordering::Equal => { println!("تهانينا! لقد خمّنت الرقم الصحيح."); break; } } } }

شرح الكود أعلاه:

  • rand::thread_rng().gen_range(1..=100) يقوم بتوليد رقم عشوائي بين 1 و 100.

  • يتم طباعة رسالة ترحيبية وتعليمات اللعبة.

  • يتم بدء حلقة لا نهائية loop لقراءة تخمينات المستخدم.

  • يتم قراءة السطر المدخل من المستخدم عبر io::stdin().read_line(&mut guess).

  • يتم محاولة تحويل النص المدخل إلى رقم صحيح u32 باستخدام parse().

  • في حالة وجود خطأ في التحويل، تطبع رسالة وتستمر الحلقة لطلب إدخال جديد.

  • في حالة نجاح التحويل، تتم مقارنة الرقم المدخل بالرقم السري باستخدام cmp.

  • بناءً على نتيجة المقارنة، يتم إعلام المستخدم إن كان تخمينه أقل، أكبر، أو صحيح.

  • عند تخمين الرقم الصحيح، يتم إنهاء الحلقة باستخدام break.

3. تحسينات وإضافات

يمكن توسيع اللعبة بإضافة ميزات جديدة، مثل:

  • تحديد عدد المحاولات المسموح بها: يمكن إضافة عداد للمحاولات وإيقاف اللعبة عند نفادها.

  • إظهار سجل التخمينات: للاحتفاظ بالتخمينات السابقة وعرضها.

  • توسيع النطاق: إعطاء المستخدم خيار تحديد نطاق الأرقام.

  • التعامل مع استثناءات أكثر تعقيدًا: مثل إدخال نصوص غير صالحة أو أرقام خارج النطاق.

إدارة الأخطاء والتحقق من صحة الإدخال

تعتبر إدارة الأخطاء جانباً أساسياً في كتابة برنامج متين، خصوصاً عند التعامل مع إدخال المستخدم الذي قد يكون غير متوقع. في المثال السابق، استخدمنا طريقة match للتحقق من نجاح تحويل الإدخال إلى رقم. هذه الطريقة فعالة في منع تعطل البرنامج.

كما يمكن استخدام مكتبة clap في حال رغبت في إضافة واجهة سطر أوامر متقدمة مع خيارات مختلفة للعبة، لكنها ليست ضرورية في التطبيق البسيط.

أهمية استخدام الحلقات والتحكم في التدفق

الحلقة loop في Rust تسمح بتكرار جزء معين من الكود إلى ما لا نهاية، وتُستخدم هنا لإعادة طلب التخمين من المستخدم حتى يتم العثور على الرقم الصحيح. يتم استخدام continue لتخطي الدورة الحالية للحلقة عند إدخال غير صالح، وbreak لإنهاء الحلقة عند تخمين الرقم الصحيح.

بالإضافة إلى ذلك، يمكن استخدام حلقات أخرى مثل while أو for بحسب الحاجة، لكن loop مع break و continue توفر تحكماً أفضل في هذه الحالة.

التعامل مع النطاق والحدود

في مثال توليد الرقم العشوائي، استخدمنا الصيغة 1..=100 والتي تعني النطاق من 1 إلى 100 شاملة، وهذا يساعد في ضمان أن الرقم السري يقع دائماً ضمن النطاق المطلوب.

يمكن توسيع نطاق الأرقام حسب متطلبات اللعبة أو حسب رغبة المستخدم، ويمكن إضافة خاصية لتلقي هذا النطاق من المستخدم بداية اللعبة.

كيفية تشغيل اللعبة

بعد كتابة الكود وحفظه في الملف main.rs ضمن مشروع Cargo، يمكن تشغيل اللعبة باستخدام الأمر:

arduino
cargo run

هذا الأمر يقوم ببناء المشروع وتنفيذه مباشرة. سيتمكن المستخدم من إدخال التخمينات والتفاعل مع اللعبة في الطرفية.

الجدول التالي يوضح أهم المكونات المستخدمة في الكود ووظائفها

المكون الوصف
rand::thread_rng() مولد أرقام عشوائية خاص بالخيط الحالي.
gen_range(1..=100) توليد رقم عشوائي في النطاق من 1 إلى 100 شامل.
io::stdin() قراءة بيانات الإدخال من المستخدم عبر الطرفية.
read_line(&mut guess) قراءة سطر نصي وحفظه في المتغير guess.
guess.trim().parse() تحويل النص إلى رقم مع إزالة الفراغات.
match استخدام للتحقق من نجاح أو فشل عملية التحويل.
cmp مقارنة رقمين وإرجاع ترتيبهما (أقل، أكبر، متساوي).
loop حلقة تكرارية لا نهائية تُستخدم لإعادة المحاولة.
continue تخطي الدورة الحالية من الحلقة والانتقال إلى الدورة التالية.
break إنهاء الحلقة والخروج منها.

استعراض بعض المفاهيم المتقدمة في رست ضمن اللعبة

استخدام المتغيرات الثابتة والمتحركة

في الكود السابق، المتغير secret_number معرف باستخدام let فقط، أي أنه متغير غير قابل للتغيير (Immutable). هذا مناسب هنا لأنه لا نريد تغيير الرقم السري أثناء تنفيذ اللعبة.

في المقابل، المتغير guess معرف بـ let mut لأنه يجب أن يسمح بالتغيير لإعادة تعيين قيمة التخمين الجديد في كل دورة.

التعامل مع السلاسل النصية String و &str

  • String هو نوع بيانات يمثل سلسلة نصية مملوكة وقابلة للتغيير.

  • &str هو نوع بيانات يمثل مقطع نصي ثابت غير مملوك (مثل النصوص الثابتة في الكود).

عند قراءة الإدخال من المستخدم، نستخدم String لأنه يتم تخصيص مساحة جديدة للنص المدخل.

استخدام الماكرو println!

الماكرو println! يسمح بطباعة النصوص إلى شاشة المستخدم، ويمكنه تضمين متغيرات عبر {} التي يتم استبدالها بالقيم المناسبة.

إدارة الذاكرة

رغم أن لعبة تخمين الأرقام بسيطة، فإن Rust تتعامل بكفاءة مع الذاكرة دون الحاجة إلى جمع قمامة، بفضل نظام الملكية (Ownership) والإعارة (Borrowing). هذا يجعل الكود آمناً من حيث تسربات الذاكرة أو المشاكل المتعلقة بالإشارات.

توسعة اللعبة: إضافة عداد محاولات

يمكننا تحسين اللعبة بإضافة عداد للمحاولات بحيث يكون للمستخدم عدد محدد من الفرص. إذا تجاوز المستخدم هذا العدد دون تخمين الرقم، تنتهي اللعبة وتعلن خسارته.

إليك مثالاً على ذلك:

rust
fn main() { use rand::Rng; use std::io; println!("مرحباً بك في لعبة تخمين الأرقام!"); let secret_number = rand::thread_rng().gen_range(1..=100); let max_attempts = 10; let mut attempts = 0; println!("لقد اخترت رقماً سرياً بين 1 و 100. لديك {} محاولات.", max_attempts); loop { if attempts >= max_attempts { println!("انتهت المحاولات. الرقم الصحيح كان: {}", secret_number); break; } println!("من فضلك أدخل تخمينك:"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("فشل في قراءة الإدخال"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => { println!("من فضلك أدخل رقماً صحيحاً."); continue; } }; attempts += 1; println!("تخمين رقم {} هو: {}", attempts, guess); match guess.cmp(&secret_number) { std::cmp::Ordering::Less => println!("رقمك أقل من الرقم السري."), std::cmp::Ordering::Greater => println!("رقمك أكبر من الرقم السري."), std::cmp::Ordering::Equal => { println!("تهانينا! لقد خمّنت الرقم الصحيح في محاولة رقم {}.", attempts); break; } } } }

بهذا الشكل، نضمن تجربة أكثر تحدياً، ونستطيع قياس مدى قدرة اللاعب على التخمين خلال عدد محدد من المحاولات.

مقارنة Rust بلغات برمجة أخرى في برمجة الألعاب البسيطة

لغة Rust تتميز بالكثير من الميزات التي تجعلها مناسبة لتطوير الألعاب، حتى البسيطة منها، بالمقارنة مع لغات مثل Python أو C++:

  • الأداء: Rust تقدم أداءً مقارباً لـ C++، أفضل بكثير من Python.

  • الأمان: إدارة الذاكرة في Rust تمنع الكثير من الأخطاء الشائعة.

  • سهولة التعلم: رغم أن Rust قد تكون أكثر تعقيداً في بعض المفاهيم، إلا أنها توفر توثيقاً جيداً وأدوات مساعدة مثل Cargo.

  • التزامن: Rust تقدم دعماً ممتازاً للبرمجة المتزامنة دون تعقيدات أمان البيانات.

لهذا، يمكن استخدام Rust ليس فقط في الألعاب البسيطة بل في مشاريع الألعاب الكبيرة والمعقدة.

الخلاصة

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

يمنح استخدام Rust في برمجة هذه اللعبة تجربة تعليمية مميزة تجمع بين سهولة كتابة الكود، الأمان، والكفاءة، مما يجعلها نقطة انطلاق جيدة للمبرمجين الراغبين في تعلم لغة ذات أداء عالٍ ومجتمع نشط ومتنامي.

المصادر والمراجع