Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نظام شخصيات الألعاب

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 107 من 107.

challenge icon

التحدي

سهل

لنقم ببناء نظام شخصيات ألعاب يجمع بين تضمين الهياكل (struct embedding)، والواجهات (interfaces)، ونمط الاستراتيجية (Strategy pattern) لإنشاء آليات قتال مرنة! ستصمم أنواعًا مختلفة من الشخصيات التي يمكنها الهجوم والدفاع بطرق فريدة، مع قدرات يمكن تبديلها أثناء وقت التشغيل.

ستقوم بتنظيم الكود الخاص بك عبر خمسة ملفات:

  • character.go: قم بتعريف هيكل أساسي باسم Character مع حقول مشتركة: Name (string)، و Health (int)، و MaxHealth (int). قم بتضمين منشئ NewCharacter والدوال التالية:
    • IsAlive() bool — ترجع true إذا كانت الصحة أكبر من 0
    • Status() string — ترجع التنسيق التالي: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: قم بتعريف واجهات القتال التي تسمح بتعدد الأشكال (polymorphism):
    • واجهة Attacker مع دالة Attack() int — ترجع الضرر الملحق
    • واجهة Defender مع دالة TakeDamage(damage int) int — تطبق تقليل الضرر وترجع الضرر الفعلي المتلقى
    • واجهة Ability مع دالة Use() string — ترجع وصفًا لتأثير القدرة
  • characters.go: قم بإنشاء ثلاثة أنواع من الشخصيات التي تتضمن Character وتنفذ كلاً من Attacker و Defender:

    Warrior — يحتوي على حقول Strength (int) و Armor (int). ترجع دالة Attack قيمة Strength كضرر. تقوم دالة TakeDamage بتقليل الضرر الوارد بمقدار Armor (بحد أدنى 1 من الضرر المتلقى).

    Mage — يحتوي على حقول Intelligence (int) و MagicResist (int). ترجع دالة Attack قيمة Intelligence * 2 كضرر. تقوم دالة TakeDamage بتقليل الضرر الوارد بمقدار MagicResist / 2 (بحد أدنى 1 من الضرر المتلقى).

    Archer — يحتوي على حقول Agility (int) و Evasion (int). ترجع دالة Attack قيمة Agility + 5 كضرر. دالة TakeDamage: إذا كانت Evasion > 10، يتم تقليل الضرر بنسبة 25% (قسمة عدد صحيح)، وإلا يتم تلقي الضرر كاملاً (بحد أدنى 1 من الضرر المتلقى).

    قم بتضمين منشئات لكل منها: NewWarrior، NewMage، NewArcher التي تأخذ الاسم، والصحة، وقيم الإحصائيات الخاصة بها.

  • abilities.go: قم بتنفيذ نمط الاستراتيجية مع قدرات قابلة للتبديل:

    هيكل FireSpell مع Power (int). دالة Use() الخاصة به ترجع: Casts fireball for [Power] damage

    هيكل IceSpell مع Power (int). دالة Use() الخاصة به ترجع: Casts ice shard for [Power] damage

    هيكل PowerStrike مع Multiplier (int). دالة Use() الخاصة به ترجع: Performs power strike at [Multiplier]x damage

    أضف حقل ability (من نوع Ability) إلى هيكل Mage، مع دوال SetAbility(a Ability) و UseAbility() string. ترجع UseAbility القيمة No ability set إذا كانت القدرة nil.

  • main.go: قم بإنشاء الشخصيات واستعراض آليات القتال.

    اقرأ نوع الشخصية (warrior، أو mage، أو archer)، ثم اقرأ اسم الشخصية، وصحتها، والإحصائيات الخاصة بنوعها. قم بإنشاء الشخصية المناسبة.

    ثم اقرأ عدد الإجراءات. كل إجراء هو واحد مما يلي:

    • status — طباعة حالة الشخصية
    • attack — طباعة [Name] attacks for [damage] damage
    • defend [amount] — تطبيق الضرر وطباعة [Name] takes [actual] damage
    • setability [type] [power] — للسحرة (mages) فقط، تعيين القدرة (النوع هو fire، أو ice، أو strike)
    • useability — للسحرة (mages) فقط، طباعة نتيجة Use() للقدرة

سيتم توفير المدخلات التالية:

  • نوع الشخصية، والاسم، والصحة، والإحصائيات الخاصة بالنوع
  • عدد الإجراءات، ثم كل إجراء في سطر منفصل

على سبيل المثال، إذا كان المدخل:

warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100

يجب أن يكون مخرجك:

Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage

وإذا كان المدخل:

mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40

يجب أن يكون مخرجك:

Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage

وإذا كان المدخل:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

يجب أن يكون مخرجك:

Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP

لاحظ كيف يسمح تضمين الهياكل (struct embedding) لكل نوع شخصية بمشاركة حقول ودوال Character الأساسية، بينما تتيح الواجهات قتالاً متعدد الأشكال حيث يمكن لأي Attacker إلحاق الضرر ولأي Defender تلقيه. يبرز نمط الاستراتيجية مع قدرات Mage القابلة للتبديل — يمكن لنفس الشخصية استخدام تعاويذ مختلفة دون تغيير تنفيذها الأساسي!

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// قراءة نوع الشخصية
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// قراءة اسم الشخصية
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// قراءة الصحة (health)
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// قراءة الإحصائية الأولى الخاصة بالنوع
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// قراءة الإحصائية الثانية الخاصة بالنوع
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: إنشاء الشخصية المناسبة بناءً على charType
	// استخدام المتغيرات: charType، name، health، stat1، stat2
	// بالنسبة لـ warrior: stat1 = Strength، stat2 = Armor
	// بالنسبة لـ mage: stat1 = Intelligence، stat2 = MagicResist
	// بالنسبة لـ archer: stat1 = Agility، stat2 = Evasion

	// قراءة عدد الإجراءات
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// معالجة كل إجراء
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: التعامل مع كل نوع من أنواع الإجراءات:
		// - "status": طباعة Status() الخاصة بالشخصية
		// - "attack": طباعة "[Name] attacks for [damage] damage"
		// - "defend [amount]": تطبيق الضرر وطباعة "[Name] takes [actual] damage"
		// - "setability [type] [power]": بالنسبة لـ mages، تعيين القدرة (fire، ice، أو strike)
		// - "useability": بالنسبة لـ mages، طباعة نتيجة UseAbility()

		_ = action // قم بإزالة هذا السطر عند التنفيذ
		_ = fmt.Println // قم بإزالة هذا السطر عند التنفيذ
	}
}

جميع دروس Object Oriented Programming

16التحديات النهائية

منصة تعليم إلكترونينظام بنكينظام شخصيات الألعاب