كانت خرائطنا تضيع في المتاهة: كيف أنقذتنا خوارزمية A* من جحيم المسارات غير الفعالة؟

يا مية أهلا وسهلا فيكم يا جماعة. اسمي أبو عمر، وبدي أحكيلكم قصة صارت معي قبل كم سنة، يوم كنا شغالين على مشروع طموح شوي. كنا بنبني نظام توصيل باستخدام طائرات بدون طيار (درونز) صغيرة، والحلم كان كبير: نوصل الطلبات بين الأحياء بسرعة وبدون زحمة السير. بس زي ما بقولوا، “مش كلشي بالتمني”.

في البداية، استخدمنا خوارزمية بسيطة عشان الطيارة تلاقي طريقها من المستودع لبيت الزبون. كانت الطيارات المسكينة “بتتخبط”؛ مرات بتلف وبتدور في أماكن غريبة، ومرات بتختار أطول طريق ممكن، كأنها بتستمتع بالمشوار على حساب البطارية! كنا بنشوفها على الخريطة وبنتحسر، وواحد من الشباب صاح فيي مرة: “يا أبو عمر، الدرون تبعنا شكله ضايع زي الأطرش بالزفة!”. كانت معنويات الفريق بالأرض، والمشروع على وشك الفشل. يومها، قعدت مع حالي، وفنجان القهوة السادة بإيدي، وتذكرت أيام الجامعة ومادة الذكاء الاصطناعي… تذكرت “البطل المنسي”، خوارزمية كانت دايماً تبهرني بذكائها: خوارزمية A*.

ما هي مشكلة البحث عن المسار أصلاً؟

قبل ما نغوص في الحل، خلينا نتفق على المشكلة. مشكلة “البحث عن المسار” (Pathfinding) هي ببساطة إيجاد أفضل طريق بين نقطة بداية (أ) ونقطة نهاية (ب). فكر فيها زي لعبة المتاهة، أو لما تستخدم خرائط جوجل عشان تروح على مكان جديد. الهدف مش بس توصل، الهدف توصل بأقصر وقت، أو بأقل تكلفة (بنزين، وقت، طاقة بطارية، إلخ).

في عالم البرمجة، بنمثل الخريطة عادة على شكل “رسم بياني” (Graph) مكون من نقاط (Nodes) ووصلات بينها (Edges)، وكل وصلة إلها “وزن” أو “تكلفة”. والنقاط ممكن تكون تقاطعات شوارع، مربعات في لعبة، أو مواقع جغرافية.

قبل A*: جولة في الخوارزميات “العمياء”

قبل ما أقرر أستخدم A*، كنا جربنا خوارزميات أخرى، ولكل واحدة كانت مشكلتها.

خوارزمية البحث بالاتساع أولاً (Breadth-First Search – BFS)

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

خوارزمية دكسترا (Dijkstra’s Algorithm)

هون الوضع تحسن شوي. دكسترا أذكى من BFS، لأنها بتاخذ “تكلفة” المسار بعين الاعتبار. هي دايماً بتختار الطريق اللي تكلفته التراكمية أقل حتى الآن. بتضمنلك تلاقي الطريق الأفضل والأقل تكلفة… لكنها لا تزال “عمياء”! كيف يعني؟ يعني ما عندها فكرة وين الهدف موجود. هي بتتمدد في كل الاتجاهات زي بقعة الزيت، وبتضيع وقت وجهد في استكشاف طرق واضحة إنها بتبعدنا عن الهدف. وهذا بالضبط اللي كان يصير مع طياراتنا، كانت تستكشف طرق في شمال المدينة مع إنه بيت الزبون في الجنوب!

نصيحة من أبو عمر: دكسترا ممتازة جدًا لما تكون ما بتعرف وين وجهتك النهائية، وبدك تحسب أقصر طريق من نقطة لكل النقاط الأخرى في الخريطة. لكن لو عندك هدف محدد، فهي مش الخيار الأكفأ.

ولادة البطل: خوارزمية A* (نجمة A)

وهون بيجي دور المنقذ، خوارزمية A*. اللي بيميز A* إنها مش بس بتشوف الطريق اللي قطعته، لأ، هي كمان عندها “حدس” أو “تخمين” عن الطريق المتبقي. هي بتجمع بين دقة دكسترا و”بصيرة” بتوجهها نحو الهدف. هي خوارزمية “مُطّلعة” (Informed Search).

المعادلة السحرية: f(n) = g(n) + h(n)

كل سحر A* يكمن في هذه المعادلة البسيطة. لما الخوارزمية تفكر تنتقل لنقطة جديدة (خلينا نسميها n)، هي بتحسبلها قيمة اسمها f(n) عشان تقرر هل هي خيار جيد أو لا. المعادلة مكونة من جزأين:

  • g(n): هي التكلفة الفعلية اللي مشيناها من نقطة البداية لحد النقطة الحالية n. هذا الجزء هو اللي بتعمله خوارزمية دكسترا. هو يمثل الماضي، الطريق اللي بنعرفه بالتأكيد.
  • h(n): هي التكلفة “المُقدّرة” أو “المُخمّنة” من النقطة الحالية n لحد نقطة النهاية. هذا الجزء هو اللي بنسميه “دالة التكهن” أو الـ Heuristic. هو يمثل المستقبل، مجرد تخمين ذكي بيوجهنا.

f(n) هي ببساطة مجموعهم: التكلفة الكلية المُقدّرة للمسار إذا مر من خلال النقطة n. خوارزمية A* في كل خطوة بتختار النقطة اللي عندها أقل قيمة f، يعني النقطة اللي “بتبدو واعدة أكثر”.

كيف نختار دالة التكهن (Heuristic)؟

هذا هو قلب A* النابض. دالة التكهن هي اللي بتعطي الخوارزمية “الإحساس بالاتجاه”. لازم تكون سريعة في الحساب، والأهم، لازم تكون “مقبولة” (Admissible)، يعني لازم لا تبالغ أبداً في تقدير التكلفة. لو قدرت التكلفة بأكثر من الحقيقة، ممكن A* تتجاهل الطريق الأمثل وتختار طريق أسوأ.

أشهر دالتين للتكهن في الخرائط الشبكية:

  • مسافة مانهاتن (Manhattan Distance): بنستخدمها لما تكون الحركة مسموحة فقط أفقيًا وعموديًا (زي المشي في شوارع مدينة نيويورك). هي ببساطة مجموع الفرق في الإحداثيات السينية (x) والصادية (y).
  • المسافة الإقليدية (Euclidean Distance): هي المسافة المستقيمة بين نقطتين (“كما يطير الغراب”). بنستخدمها لما تكون الحركة مسموحة في كل الاتجاهات.

شوية كود يا جماعة

عشان الصورة توضح أكثر، خلينا نشوف كيف ممكن يبدو شكل خوارزمية A* بالكود (هذا شبه كود بلغة بايثون للتوضيح).


# هذه مجرد بنية توضيحية، وليست كود كامل للنسخ واللصق
def a_star_search(start_node, goal_node):
    # قائمة النقاط المفتوحة (اللي لسا ما زرناها بس بنفكر نزورها)
    # بنستخدم "طابور أولوية" عشان دايماً نسحب النقطة اللي f(n) تبعها أقل
    open_list = PriorityQueue()
    open_list.put(start_node, 0)

    # قاموس لتتبع المسار
    came_from = {}
    
    # قاموس لتخزين تكلفة g(n) لكل نقطة
    g_score = {node: float('inf') for node in grid}
    g_score[start_node] = 0

    # قاموس لتخزين تكلفة f(n) لكل نقطة
    f_score = {node: float('inf') for node in grid}
    f_score[start_node] = heuristic(start_node, goal_node)

    while not open_list.empty():
        current = open_list.get() # اسحب النقطة ذات الأولوية القصوى (أقل f_score)

        if current == goal_node:
            return reconstruct_path(came_from, current) # وجدنا الطريق!

        for neighbor in current.get_neighbors():
            # g_score المؤقت هو تكلفة الوصول للنقطة الحالية + تكلفة الانتقال للجار
            tentative_g_score = g_score[current] + distance(current, neighbor)

            # إذا كان هذا المسار للجار أفضل من أي مسار سابق
            if tentative_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g_score
                f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal_node)
                
                # إذا الجار مش في القائمة المفتوحة، ضيفه
                if neighbor not in open_list:
                    open_list.put(neighbor, f_score[neighbor])

    # لم نجد مسارًا
    return None

نصيحة من أبو عمر: أهم جزء في تطبيق A* هو استخدام هيكل بيانات مناسب للقائمة المفتوحة (Open List). استخدام قائمة عادية والبحث فيها عن العنصر الأقل في كل مرة هو أمر كارثي من ناحية الأداء. استخدم دائمًا “طابور أولوية” (Priority Queue) أو (Min-Heap)، فهذا سيحدث فرقًا هائلاً في السرعة.

الخلاصة: من المتاهة إلى الطريق السريع 💡

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

خوارزمية A* هي مثال رائع على كيف يمكن لمفهوم رياضي أنيق أن يحل مشاكل عملية معقدة. هي حجر أساس في تطوير الألعاب (لتحريك الأعداء والشخصيات)، وفي الروبوتات، واللوجستيات، وحتى في توجيه حزم البيانات عبر الشبكات.

لهيك نصيحتي إلكم: لا تستهينوا بقوة الخوارزميات الأساسية. افهموها جيدًا، اعرفوا متى تستخدمون كل واحدة منها، وما تخافوا من “شوية” رياضيات. يلا يا جماعة، ما تخلوا مشاريعكم تضيع في المتاهة. استخدموا عقلكم وحدسكم، زي ما بتعمل A* بالضبط. ويعطيكم ألف عافية! 💪

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

​معمارية البرمجيات

كانت خدماتنا تتحدث في نفس الوقت: كيف أنقذتنا ‘المعمارية القائِمَة على الأحداث’ (EDA) من جحيم الاقتران المحكم؟

في ليلة إطلاق عصيبة، كادت خدماتنا المترابطة أن تُغرق المشروع بأكمله. أروي لكم كيف تحولنا من فوضى الاقتران المحكم إلى مرونة المعمارية القائمة على الأحداث...

15 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كانت نماذجنا تموت بصمت: كيف أنقذتنا ‘مراقبة تعلم الآلة’ (ML Monitoring) من كارثة التنبؤات الفاسدة؟

أشارككم قصة حقيقية من الميدان، حين كادت نماذج الذكاء الاصطناعي التي بنيناها بجهد أن تنهار بصمت. اكتشفوا معنا ما هي "مراقبة تعلم الآلة" (ML Monitoring)،...

15 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

نقرات المستخدمين كانت تذهب في الفراغ: كيف أنقذتنا ‘التفاعلات الدقيقة’ من جحيم التجربة الصامتة؟

أشارككم قصة حقيقية من قلب معركة تطوير أحد التطبيقات، وكيف أن تفاصيل صغيرة تُدعى "التفاعلات الدقيقة" (Microinteractions) حوّلت تجربة مستخدم صامتة ومحبطة إلى حوار ممتع...

15 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت استعلاماتنا تزحف كالسلحفاة: كيف أنقذنا ‘فهرس قاعدة البيانات’ من جحيم البحث الكامل في الجداول (Full Table Scan)؟

أشارككم قصة حقيقية عن يوم كادت فيه استعلاماتنا البطيئة أن تدمر مشروعًا بالكامل، وكيف كان "الفهرس" البسيط في قاعدة البيانات هو طوق النجاة. سنتعلم معًا...

15 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطافات الويب’ (Webhooks) من جحيم الاستطلاع المستمر (Polling)؟

بصفتي أبو عمر، أشارككم قصة حقيقية من معاناتنا مع استنزاف الموارد بسبب الاستطلاع المستمر (Polling). سأشرح كيف كانت خطافات الويب (Webhooks) هي طوق النجاة، مع...

15 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت بيئاتنا تتغير كالحرباء: كيف أنقذتنا ‘البنية التحتية كشيفرة’ (IaC) من جحيم التكوين اليدوي؟

أروي لكم حكايتي كـ "أبو عمر"، مبرمج فلسطيني، مع الفوضى التي كنا نعيشها في إدارة الخوادم يدوياً. سأشارككم كيف كانت 'البنية التحتية كشيفرة' (IaC) وأداة...

15 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت مقابلاتي التقنية فشلاً ذريعاً: كيف أنقذتني ‘المقابلات الوهمية’ من جحيم الرفض المتكرر؟

بعد سلسلة من المقابلات الفاشلة التي كادت أن تدمر ثقتي بنفسي، اكتشفت سلاحاً سرياً غيّر قواعد اللعبة. هذه قصتي مع "المقابلات الوهمية" (Mock Interviews)، وكيف...

15 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت خوادمنا تموت بالتناوب: كيف أنقذنا ‘موازنة الأحمال’ من جحيم النقاط الفردية للفشل؟

واجهنا كابوس تعطل الخوادم المتتالي بسبب نقطة فشل واحدة. في هذه المقالة، أشارككم تجربتي كـ 'أبو عمر' في كيفية الانتقال من موازن أحمال بسيط إلى...

15 مايو، 2026 قراءة المزيد
البودكاست