מימוש משחק סנייק עם AI (טייק 2)

14/07/2025

לפני כמה שבועות פרסמתי פה ניסיון לכתוב משחק סנייק עם AI. אותו ניסוי לא עבד מספיק טוב ודרש הרבה התערבות בעבודה של ה AI. עם קצת שיפור במודלים והרבה שיפור בפרומפטים הצלחתי להגיע לאחרונה לתוצאה הרבה יותר טובה שרק מחדדת את הכח והמגבלות של AI. בואו נפתח את הקוד.

1. הפרויקט

אפשר לשחק אונליין בסנייק כאן: https://snake-2-five.vercel.app

זה רץ קצת מהר להתחלה. אתם יכולים לבחור בצד שמאל משחק "קלאסי" או "נגד המחשב". במשחק נגד המחשב יש שני נחשי אויב. כרגע הם מתים מאוד מהר. המשחק נראה סביר על מסך רגיל אבל די גרוע על מובייל.

את הקוד אפשר לראות כאן: https://github.com/ynonp/snake-2

את כל הקוד כתב AI וכל קומיט כתבתי את הודעת או הודעות הפרומפט שיצרו את הקוד. סך הכל יש 7 קומיטים הראשון יצר את תבנית הפרויקט הריקה ובואו נעבור על ה 6 הנותרים.

2. קומיט 1 - יצירת קובץ readme

את הפרויקט התחלתי עם קובץ readme. ביקשתי מקופיילוט ליצור לי כזה קובץ שמתאר את הפרויקט וציינתי שזה פרויקט עבור תיק עבודות שאני בונה. אחרי הטיוטה הראשונה הוספתי הערה שאני מעדיף לקבל משחק עם נחשי אויב ופידבק אחרון היה בקשה לא לכתוב את רשימת הקבצים בקובץ ה readme.

זה הקומיט עם הקובץ שנוצר: https://github.com/ynonp/snake-2/commit/b6d06f2bad457cd482ebf76e7dc1c2288e7fea9d

3. קומיט 2 - יצירת קובץ ארכיטקטורה

את ה readme המשכתי לתרגם לקובץ ארכיטקטורה. הקובץ כלל הסבר על כל הספריות בהן אני משתמש בפרויקט, החלקים השונים באפליקציה ותפקיד כל חלק, רשימת כל הקבצים בפרויקט, התיחסות לנושא הביצועים וגם הצעה למימוש אלגוריתם התנועה של נחש האויב. סך הכל קופיילוט לקח פרומפט של 10 שורות שלי והפך אותו לקובץ ארכיטקטורה של כמעט 400 שורות טקסט.

זה הקומיט עם הקובץ שנוצר: https://github.com/ynonp/snake-2/commit/a0589cd9493c6b2e5d1e9940f09255210a1ccef8

4. קומיט 3 - יצירת כללי קידוד

אני יודע שקופיילוט לא זוכר מה אמרתי לו בהודעה הקודמת וכל פרומפט יוצר חלון קונטקסט חדש. בשביל שלא יהיו אי הבנות ביקשתי ממנו ליישם קובץ style.md שיכיל את ה Coding Style של הפרויקט. לא היה לי חשוב מה יהיו הסטנדרטים רק שיהיו ויהיו אחידים.

הפרומפט שלי הפעם היה שורה, קופיילוט יצר קובץ של אלף שורות שכולל דוגמאות איך צריך לכתוב ואיך לא נכון לכתוב בפרויקט שלנו.

זה הקומיט עם הקובץ שנוצר: https://github.com/ynonp/snake-2/commit/61db1cdcd177d247239268fad2aaba30f62dd109

5. קומיט 4 - התקנת זוסטנד

הקומיט הידני היחיד שעשיתי. לא רציתי שקופיילוט יתחיל להתקין ספריות וחששתי שיתבלבל. בדיעבד אני לא בטוח שזה היה הכרחי. בכל מקרה זה הקומיט בלי פרומפט הפעם:

https://github.com/ynonp/snake-2/commit/9228e31c4c8d5ce023a65fcd149b65f9ea592dd7

6. קומיט 5 - מימוש המשחק

אחרי כל ההכנה אפשר להמשיך למימוש המשחק וזה הקומיט שבו ה AI עבד הכי קשה. הפרומפט היה:

Implement the game using only installed libraries. For the moment don't implement tests and don't add new dependencies. Use #playwright to verify the game works.

Create a work plan and execute it step by step

זה כבר יצר 18 קבצים עם קוד מלא של המשחק. בניסיון הראשון הקוד לא עבד אז הוספתי הודעת דיבג במקום אסטרטגי ושלחתי אותו לתקן עם הפרומפט הבא:

pressing the arrow keys just moves the "eyes" of the snake but snake doesn't actually move. also the console.count('tick'); is printed just once

זה הספיק לו ואחרי כמה דקות של הדפסת טקסט למסך המשחק התחיל לעבוד.

7. קומיט 6 - תיקון סוכני AI

בסוף הקומיט הקודם המשחק עבד אבל עדיין בלי נחשי AI אז המשכנו עם פרומפט נוסף שגרם לקופיילוט לזהות את הבאג ולתקן, אחריו הופיעו נחשי ה AI.

8. מסקנות

מימוש משחק הסנייק היה לי הרבה יותר מהיר הפעם וגם הביא לתוצאה טובה יותר. הנה כמה נקודות חשובות לקחת מהדוגמה. נתחיל עם מה שלא עבד:

  1. למרות שהוזכר בארכיטקטורה, אלגוריתם תנועת נחשי האויב נשאר בגדר רעיון. ה AI מימש קובץ שלם של אלגוריתם AStar אבל לא השתמש בו.

  2. קובץ Coding Style של אלף שורות לא עוזר לאף אחד. אפילו ל AI יהיה קשה לזכור את כולו.

  3. ה"תיקון" של נחשי האויב היה חובבני. הוא זיהה שהנחשים מתים מיד כי אין להם מנגנון פעולה הגיוני ורק הוסיף מעקף שנתן להם לא להתנגש בעצמם במהלך הראשון. כל מה שדיברנו על אלגוריתמי תנועה נשאר בגדר חלום.

  4. אין תאימות למובייל והמשחק מהיר מדי.

  5. המימוש עצמו ארוך ולא מתועד. למרות אלף שורות של style.md עדיין חלקים בקוד לא קונסיסטנטיים. לדוגמה למרות שיש הגדרה לטיפוס בשם Direction שמכיל את איחוד הכיוונים up, down, left, right עדיין במקום אחר בקוד אני מוצא:

export const predictCollision = (
  snake: Snake,
  direction: string,
  boardSize: BoardSize,
  otherSnakes: readonly Snake[]
): boolean => {
  const head = snake.positions[0];
  const vectors = {
    up: { x: 0, y: -1 },
    down: { x: 0, y: 1 },
    left: { x: -1, y: 0 },
    right: { x: 1, y: 0 },
  };

  const vector = vectors[direction as keyof typeof vectors];
  if (!vector) return true;

  const nextPosition = {
    x: head.x + vector.x,
    y: head.y + vector.y,
  };

  return isPositionOccupied(nextPosition, otherSnakes, boardSize);
};

כלומר פונקציה שמקבלת direction בתור string.

עכשיו מה שכן עבד:

  1. הדגש בפיתוח עבר מכתיבת הקוד להגדרת ארכיטקטורה ברורה וקבלת החלטות. לקח יותר זמן לכתוב את קבצי התיעוד מאשר את הקוד עצמו.

  2. היכולת לייצר אוטומטית קוד מתוך קבצי ארכיטקטורה ותיאור אומרת שאפשר בקלות לשנות את תיאורי הארכיטקטורה ולקבל גירסאות אחרות של המשחק. זה יהיה ניסוי מעניין.

שורה תחתונה - הבעיה המרכזית שנשארה ולא הולכת להשתפר בקוד AI בלי קשר לשיפור במודלים היא ניפוח הקוד וחוסר עקביות. קופיילוט לא יודע לכתוב קוד יפה כי קוד יפה הוא קוד שנכתב מתוך מחשבה והבנה של המערכת, וקופיילוט לא משחק את המשחק הזה. הבנה, מחשבה וקבלת החלטות יישארו המשימות שלנו בעתיד הנראה לעין.

יחד עם זאת כשהפרויקט הוא AI Friendly, כשאנחנו דואגים לכתוב במסמכי איפיון מסודרים את הדרישות שלנו וכשאנחנו נותנים לקופיילוט אפשרות לאיטרציה על המוצר "עד שזה יעבוד" הוא ימצא דרך למלא את החורים ולממש את החלקים. המשחק שלנו Going Forward יהיה להבין מה הקוד ש AI הולך לייצר, להגדיר ארכיטקטורה בצורה מספיק מדויקת כדי שהתוצאה תתאים לציפיות וכך לצמצם קוד מיותר ולהיות מסוגלים לעבור על התוצרים מהר.