מוצאים טיסה בדולר

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

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

התחרות

בסוף שנת 2015 ראיתי מודעה של הדקה ה-90 (חברת שיווק טיסות) אשר רצתה לקדם את האפליקציה החדשה שלה "Loco". במודעה היה כתוב על תחרות של כעשרה ימים בה יפרסמו מדי יום חידה שהתשובה אליה היא יעד מסוים בעולם.

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

ניסיונות ראשונים

כאדם שאוהב חידות ממש ציפיתי לתחילת התחרות וחיכיתי לראות איזה סוג של חידות יהיו (חשבתי שיהיו חידות בסגנון של חמיצר), אבל כשהחידה הראשונה פורסמה התאכזבתי לגלות שהיא הייתה :"באיזה עיר נמצא מגדל אייפל?".

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

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

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

הנדסה לאחור של האפליקציה

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

את התהליך של ההנדסה-לאחור (באנגלית זה נשמע יותר טוב, reverse engineering) עושים בעזרת כלים מוכנים שיודעים לקחת אפליקצייה, "לפרק" אותה לאבני הבניין שלה וכך אפשר להסתכל על קוד המקור שלה (או משהו שיחסית דומה לו).

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

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

כמה רמזים דקים

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

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

זה היה רגע שבו הרגשתי שיכול להיות שמצאתי את היתרון שאני צריך, כמובן שזאת הייתה רק השערה אבל זה היה כיוון שהרגיש לי נכון ללכת איתו וגם לא היה כיוון אחר מבטיח יותר.

יכולות תכנות

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

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

זהו לבינתיים ואעמיק עוד על הנקודה הזו בהמשך הפוסט.

התוכנה

המטרה שלי הייתה לנסות להפוך את החיפוש של התאריכים ליעיל יותר כאשר היו לי כמה הנחות בסיס:

  • ליעד צריכה להיות טיסה ישירה של EasyJet
  • סביר יותר שיבחרו טיסה זולה
  • האורך של הטיסה לא יהיה יותר משבוע

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

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

הנה רוב הקוד שכתבתי (הייתי מאוד מופתע למצוא אותו במייל, כנראה שהחלטתי לתעד אותו בגלל מה שקרה אחר כך)


int [][] Dep= new int [][]{
            new int []{},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29},

            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},};

        int [][] Ret= new int [][]{
            new int []{},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30},
            new int [] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31},};
           int m=0;int q=0;
        System.out.println("הכנס מחירי טיסות יוצאות, אם אין טיסה הכנס 0"); 
        //enter depurture filght prices, if no flight, enter 0
        for (int i=1;i<Dep.length;i++)
        for (int j=0;j<Dep[i].length;j++)
            {q=j+1;
                System.out.println("מחיר טיסה בחודש "+i+" ביום "+q);
                //enter price of flight in day j in month i
                Dep[i][j]=reader.nextInt();
            }
          System.out.println("הכנס מחירי טיסות חוזרת, אם אין טיסה הכנס 0");
          //enter return filght prices, if no flight, enter 0
            for (int i=1;i<Ret.length;i++)
        for (int j=0;j<Ret[i].length;j++)
            {q=j+1;
                System.out.println("מחיר טיסה בחודש "+i+" ביום "+q);
                //enter price of flight in day j in month i
                Ret[i][j]=reader.nextInt();
            }
        String [] Calc= new String [320];
        int s=0;
        for(int i=0;i<320;i++)
            {
                Calc[i]="";
            }
            int y=0;int f=0;int l=0;
        for (int i=1;i<Dep.length;i++)
        for(int j=0;j<Dep[i].length;j++)
        for (int k=1;k<7;k++)
            {q=j+1;
                 m=j+k+1;
                  if (m>=Dep[i].length)
                 { s=m-Dep[i].length;
                     if(Dep[i][j]!=0 && Ret[i+1][s]!=0)
                     {y=i+1;
                         l=s+1;

                         Calc[Dep[i][j]+Ret[i+1][s]]+=q+"/"+i+"-"+l+"/"+y+", ";
                        }
                   }
                   else
                 if(Dep[i][j]!=0 && Ret[i][j+k]!=0)
                        {
                            f=m+1;
                            Calc[Dep[i][j]+Ret[i][j+k]]+=q+"/"+i+"-"+f+"/"+i+", ";
                        }
                }

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

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

מציאת הטיסה בדולר!

מה שהתוכנה נתנה לכל יעד היה רשימה של תאריכים שמסודרים מהזול ליקר, ולדוגמה

Paris

23/1-25/1,  Price:150
23/1-30/1,  Price:181
16/1-23/1,  Price:184
18/1-25/1, 20/1-25/1,  Price:205
25/1-30/1,  Price:212
12/3-14/3,  Price:214
27/1-30/1,  Price:229
13/1-16/1,  Price:230
9/1-10/1, 9/1-16/1, 18/1-23/1, 20/1-23/1,  Price:232
11/1-16/1,  Price:238
6/1-10/1,  Price:290

לאחר מכן לקחתי את הטלפונים של המשפחה והתחלתי לחפש במקביל לפי הטבלת תאריכים שהכנתי.

אחרי כמה ימים ללא הצלחה במציאת הטיסה, ב 13.12.2015 היעד היה ז'נבה והצלחתי למצוא את הטיסה בדולר בעזרת טבלת התאריכים שהכנתי!!!

זכייה #1 זכייה #2

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

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

בכל אופן, ייאמר לזכותם שהם הבינו את הטענות שלי ונתנו לי הנחה של 100 דולר לזוג כרטיסי טיסה, ובהמשך אותה השנה השובר מומש כאשר ההורים טסו לחמישה ימים בפראג בחצי מחיר 🙂

מחשבות ותובנות

תכנות ככלי עזר בכל רמה

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

מה שיותר אהבתי זה שרמת התכנות הייתה מאוד בסיסית וכל מי שלמד טיפה תכנות היה יכול לכתוב את התוכנה הזו.

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

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

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

תמשיכו ליצור ועד לפרויקט הבא, רותו