لوّنت ست خانات، ثم بدأت تغيير كل خانة فور حساب جيرانها. يبدو ذلك اختصارًا، لكنه قد يعطي نتيجة تعتمد على اتجاه مرور قلمك. في «لعبة الحياة» سنحتفظ بصورة البداية حتى ننتهي من صنع صورة الجيل التالي. سنراجع خانة بخانة، ثم نجرب برنامجًا صغيرًا يعيد النتيجة نفسها.
الخلاصة السريعة
- نعد الجيران الثمانية، بما فيهم الأقطار، ولا نعد الخلية نفسها.
- جميع قرارات الجيل الجديد تعتمد على الحالة القديمة.
- ترتيبنا الأول يحوي ست خلايا؛ الجيل التالي خمس، والذي يليه سبع.
- الخلية عند (3,3) تبقى ميتة رغم وجود خمسة جيران أحياء.
- في المرور الخاطئ الذي سنحدده تظهر ثماني خلايا بدل خمس.
- حافة الصورة ليست بالضرورة حافة العالم الذي نحسبه.
اكتب القاعدة قبل بدء العد
يعرض توثيق Golly قاعدة كونواي بالرمز B3/S23: تولد خلية ميتة إذا كان لها ثلاثة جيران أحياء، وتبقى الخلية الحية إذا كان لها جاران أو ثلاثة. الحالات الأخرى ميتة في الجيل التالي. الجوار هنا ثماني خانات تحيط بالخلية. [1]
يؤكد شرح بول كالاهان أن أعداد الجيران تؤخذ قبل تطبيق التغييرات. لذلك نحدد النتائج كلها من نسخة ثابتة، ثم نستبدل الجيل دفعة واحدة. [2]
نستخدم «حية» و«ميتة» اسمي حالتين رياضيتين. لا نصف خلايا حيوية حقيقية أو نمو كائن. لا تختار ضغطة أو حركة جديدة داخل كل جيل؛ بعد تحديد البداية والقاعدة تخرج الحالات اللاحقة بالحساب. هدف النشاط أن تقدر على تفسير كل تغيير، لا أن تحصل على شكل يعجبك فقط.
ثبّت الإحداثيات ونطاق الرسم
ليكن x رقم العمود الذي يزيد يمينًا، وy رقم الصف الذي يزيد إلى الأسفل. الزوج (2,3) يعني العمود 2 والصف 3؛ ابدأ الترقيم بصفر. هذه طريقة اختيارنا للرسم، وليست إحداثيات موضع جغرافي.
الحالة الأولى هي المجموعة {(2,2), (3,2), (4,2), (2,3), (4,4), (5,4)}. كل موضع خارجها ميت في البداية. نفترض شبكة تمتد بلا حد في الاتجاهات كلها، ويمكن أن تولد لاحقًا خلية خارج إطار الصورة. نافذتنا تعرض الأعمدة من 0 إلى 6 والصفوف من 0 إلى 5؛ وهي تكفي لإظهار الجيلين المعروضين.
ابدأ بورقتين تحملان الشبكة نفسها. ضع على الأولى البداية، واترك الثانية للنتيجة. لكل موضع افحص المربع المحيط به، وعد الخانات الحية الثماني حوله. استثن المركز حتى لو كان حيًا. ولا تمح علامة من الورقة الأولى عندما تقرر أنها ستختفي.
راجع حالات من البداية
| الموضع | حالته القديمة وعدد جيرانه | القرار |
|---|---|---|
| (2,2) | حية؛ 2 | تبقى حية |
| (3,2) | حية؛ 3 | تبقى حية |
| (4,2) | حية؛ 1 | تموت |
| (3,1) | ميتة؛ 3 | تولد |
| (3,3) | ميتة؛ 5 | تبقى ميتة |
| (5,3) | ميتة؛ 3 | تولد |
في الموضع (3,1) يأتي الجيران الأحياء الثلاثة من الصف 2: الأعمدة 2 و3 و4. وفي الموضع (5,3) هم (4,2), (4,4), (5,4). لاحظ أثر القطرين في هذين المثالين؛ عد الجهات الأربع وحدها يغير المسألة.
أما (3,3) فيجاور الخلايا (2,2), (3,2), (4,2), (2,3), (4,4). العدد خمسة، فلا تولد فيه خلية. عبارة «ثلاثة أو أكثر» ستكون قاعدة أخرى؛ الرقم المطلوب للولادة في هذا النشاط ثلاثة بالضبط.
النتيجة الكاملة للجيل الأول هي {(2,2), (2,3), (3,1), (3,2), (5,3)}. بقيت ثلاث من الست القديمة، واختفت ثلاث، وظهرت اثنتان؛ لذلك 6 − 3 + 2 = 5. عدد الخلايا وحده لا يكفي للمراجعة؛ قارن المواضع أيضًا.
نفذ الخطوة ببرنامج قصير
from collections import Counter
def next_generation(live):
neighbours = Counter(
(x + dx, y + dy)
for x, y in live
for dx in (-1, 0, 1)
for dy in (-1, 0, 1)
if (dx, dy) != (0, 0)
)
return {
cell for cell, n in neighbours.items()
if n == 3 or (n == 2 and cell in live)
}
live = {(2, 2), (3, 2), (4, 2), (2, 3), (4, 4), (5, 4)}
new = next_generation(live)
print(sorted(new))
print(len(live), len(new))
يطبع البرنامج القائمة [(2,2), (2,3), (3,1), (3,2), (5,3)] ثم العددين 6 5. احتفظت المجموعة live بالبداية، وأنشأت الدالة مجموعة جديدة. ترتيب القائمة المطبوعة للقراءة فقط؛ المجموعة نفسها لا تعتمد على ترتيب الخلايا.
يقوم العداد بتجميع مساهمة كل خلية حية في جيرانها. لذلك لا نزور شبكة لا نهائية، بل المواضع المجاورة للحالة الحية المحدودة. خلية حية منعزلة تمامًا قد لا تظهر في العداد، وهذا مناسب هنا: لن تنتقل إلى مجموعة الجيل الجديد. لا ننقل هذا الاختصار إلى قاعدة تسمح بالولادة مع صفر جيران دون إعادة التفكير فيه.
إذا طبقت الدالة على الجيل الأول، تكون النتيجة {(2,1), (2,2), (2,3), (3,1), (3,2), (3,3), (4,2)} وعددها سبعة. نفذ هذا الجيل على ورقة جديدة أيضًا. ظهور سبع بعد خمس لا يثبت نموًا دائمًا، كما أن نقصان العدد في الخطوة الأولى لا يثبت انقراض النمط.
انظر إلى أول أثر للتحديث الفوري
صنعنا تنفيذًا خاطئًا محددًا: يمر على الصفوف 0 إلى 7 من الأعلى للأسفل، وعلى الأعمدة 0 إلى 7 من اليسار لليمين، ويعدل المجموعة نفسها بعد كل خانة. تولد (3,1) في وقت مبكر. عندما يصل بعد ذلك إلى (4,1) يعد المولودة الجديدة مع (3,2) و(4,2)، فيجد ثلاثة.
لكن في النسخة الأصلية لم يكن للموضع (4,1) إلا الجاران الأخيران؛ كان يجب أن يبقى ميتًا. هذا أول اختلاف في المرور المختار. تستمر التغييرات الخاطئة فتخرج ثماني خلايا في النهاية. لم نغير نص قاعدة العد؛ غيّرنا اللحظة التي نقرأ منها الحالة، فصارت تجربة مختلفة.
فرق بين نافذة عرض وحد دائم
ابدأ تجربة منفصلة بثلاث خلايا {(0,1), (0,2), (0,3)}. على الشبكة الممتدة يصبح الجيل التالي {(-1,2), (0,2), (1,2)}. إذا عرضت فقط الأعمدة غير السالبة سترى اثنتين، لكن الثالثة موجودة في الحساب خارج النافذة.
أما إذا أعلنت أن كل موضع ذي x سالب ممنوع ويظل ميتًا، فستحتفظ في الجيل الأول بخليتين فقط. في الخطوة التالية تموتان لأن لكل منهما جارًا واحدًا. على الشبكة الممتدة تعود الثلاث إلى العمود صفر. قص العرض وقص الحالة ليسا العملية نفسها؛ اكتب سياسة الحد قبل مقارنة صورتين.
اختبر خطوة تستطيع تفسيرها
غيّر خلية واحدة من البداية، وأعد الحساب على نسختين. اكتب الخلايا التي بقيت، والتي اختفت، والتي ولدت. إذا اختلف عملك عن البرنامج، اختر أول موضع مختلف وراجع جيرانه في الصورة القديمة، ثم راجع ترتيب الإحداثيين وسياسة الحدود. لا تحاول تصحيح النتيجة بتعديل الخانات حتى تشبه الرسم فقط.
فحصنا البرنامج الوارد، وأعداد الجيران والرسوم، وقارنا الناتج بحساب مستقل يمر على نطاق يحيط بالخلايا. هذه مراجعة حسابية وليست تجربة بيولوجية أو اختبارًا لقدرة القارئ. يمكنك تغيير البداية بحرية؛ المقصود أن يبقى معنى «جيل واحد» واضحًا في كل محاولة.