פונקציית גיבוב היא פונקציה המקבלת קלט כלשהו ומחזירה ערך באורך קבוע.
קיימות פונקציות גיבוב רבות, ולהן שימושים מגוונים.
נמנה כמה תכונות נפוצות של פונקציות גיבוב:
בתרגיל זה נממש גרסה פשוטה של פונקציית גיבוב (hash function).
פונקציית הגיבוב שלנו תקבל מחרוזת ותחזיר תמיד פלט באורך זהה.
תחילה נכיר את הפונקציה ord(תו).
פונקציה זו מקבלת תו, ומחזירה ערך מספרי המייצג אותו:
In [1]:
ord('a')
Out[1]:
פונקציית הגיבוב שלנו תעבוד כך:
ord(letter) ונכפיל במשתנה העזר hash.פונקציית הגיבוב שיצרנו מחזירה תמיד ערכים באורך קבוע (בין 0 ל־100,297), כפי שפונקציית גיבוב צריכה להחזיר (לאו דווקא באורך זה, אבל הפלט חייב להיות באורך קבוע).
דוגמה:
myhash('aba')
62242
החישוב התבצע כך:
temp_hash = 1
temp_hash = (temp_hash * ord('a') * 1) % 397643
# temp_hash = (1 * 97 * 1) % 397643 = 97
שימו לב שכאן הכפלנו ב־1, כיוון שמיקום האות הוא 0 ואנו מכפילים באינדקס האות הבאה.
temp_hash = (temp_hash * ord('b') * 2) % 397643
# temp_hash = (97 * 98 * 2) % 397643 = 19012
temp_hash = (temp_hash * ord('a') * 3) % 397643
# temp_hash = (19012 * 97 * 3) % 397643 = 363133
return temp_hash % 100297
# temp_hash = 363133 % 100297 = 62242
ממשו את פונקציית הגיבוב.
כדי לבדוק את עצמכם ודאו שקיבלתם את הערכים הבאים עבור המחרוזות הבאות:
myhash('python course')myhash('hashing is sababa')myhash('i calculate hashes for fun')
שימו לב שזוהי פונקציית גיבוב מומצאת.
לעולם לא נשתמש בפונקציות גיבוב שהמצאנו בסביבות אמיתיות שאנחנו מתכנתים(!), משום שדבר כזה יסכן בוודאות את המשתמשים במערכת.
זה עולם שלם שנחקר רבות, ואנחנו תמיד נשתמש רק בפונקציות גיבוב שנחקרו במוסדות הרלוונטיים ופורסמו מטעמם.
In [3]:
# כתבו את הפונקציה שלכם כאן
נזכיר את החוקים של המשחק המוכר איקס־עיגול:
את הלוח נייצג באמצעות רשימה של רשימות.
כל רשימה תייצג שורה בלוח שלנו: הרשימה במיקום 0 תייצג את השורה הראשונה בלוח, הרשימה בשורה 1 את השורה השנייה וכך הלאה.
הרשימות המייצגות את השורות יהיו רשימות של תווים, כאשר בכל תא יהיה אחד מבין התווים האפשריים – 'O', 'X' או '-'
לדוגמה, כך נראה לוח ריק:
[['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]
נוח לדמיין את הרשימה הזו כתובה במאונך:
[ ['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-'] ]
כעת נראה איך נראה הלוח כאשר יש 'X'־ים באלכסון:
[ ['X', '-', '-'], ['-', 'X', '-'], ['-', '-', 'X'] ]
וללא ההדפסה לאורך:
[['X', '-', '-'], ['-', 'X', '-'], ['-', '-', 'X']]
תחילה נממש פונקציה המקבלת את הלוח ובודקת אם יש מנצח כלשהו (X או O), ומחזירה את האות של המנצח ('X' או 'O') אם יש מנצח, או '' (מחרוזת ריקה) אחרת.
חִשבו אילו בדיקות נידרש לבצע כדי למצוא אם יש בלוח מצב של ניצחון. ממשו את הפונקציה check_board(board) כך שתשתמש בכמה שיותר פונקציות עזר.
In [3]:
# check_board(board) כתבו את הפונקציה שלכם כאן
לפני שתמשיכו הלאה, חשוב לוודא שהפונקציה שכתבתם עובדת, לכן נכתוב עבורה סדרה של בדיקות.
כתבו בדיקות עבור המקרים הבאים:
בסך הכול תצטרכו לכתוב 10 בדיקות. בכל בדיקה הפעילו את הפונקציה שלכם check_board(board) על לוח כפי שמתואר ובדקו אם הפלט שמוחזר תואם לפלט שציפיתם לקבל.
In [ ]:
# בדיקה עבור אלכסון ראשי
In [ ]:
# בדיקה עבור אלכסון משני
In [ ]:
# בדיקה עבור טור שמאלי
In [ ]:
# בדיקה עבור טור אמצעי
In [ ]:
# בדיקה עבור טור ימני
In [ ]:
# בדיקה עבור שורה עליונה
In [ ]:
# בדיקה עבור שורה אמצעית
In [ ]:
# בדיקה עבור שורה תחתונה
In [ ]:
# בדיקה עבור לוח מלא ללא ניצחון
In [ ]:
# בדיקה עבור לוח לא מלא ללא ניצחון
במהלך המשחק אנו נקלוט מהשחקנים במשחק (המשתמשים) את המקומות בלוח, שבהם הם ירצו למקם את האות שלהם.
מקומות אלו יהיו שני מספרים בתחום 0–2 המציינים את השורה והעמודה שבה יש למקם את האות.
לדוגמה, עבור:
1 2
נמקם את האות המתאימה לשחקן, נניח X, בשורה 1 ובעמודה 2, כך:
[['-', '-', '-'], ['-', '-', 'X'], ['-', '-', '-']]
זכרו כי הספירה מתחילה מ־0, ולכן מדובר בשורה האמצעית ובעמודה הימנית.
כעת כתבו פונקציה המקבלת את הלוח ואת האות שמייצגת את השחקן ('X' או 'O'). כמו כן, הפונקציה תקלוט מהמשתמש שני מספרים.
הפונקציה תבדוק אם התנאים הבאים מתקיימים, ואם הם מתקיימים היא תמקם את האות הנתונה במיקום המבוקש, ותעדכן את הלוח:
שימו לב, עליכם לחייב את המשתמש להכניס ערכים חוקיים. כלומר, המשחק לא ימשיך עד שיתקבל קלט תקין (חשבו על דרך שבה התוכנית תמשיך לבקש מהמשתמש ערכים עד שיוכנסו ערכים חוקיים).
כאשר נגלה בשלב מוקדם יותר שהלוח לא ניתן יותר למילוי – המשחק יסתיים.
רמז: השתמשו בלולאת while
לוח לדוגמה:
board = [['-', '-', '-'], ['-', 'O', 'X'], ['-', '-', '-']]
הזנה חוקית:
make_turn('X', board)
Player 'X' Please choose cell: 0 2 [['-', '-', 'X'], ['-', 'O', 'X'], ['-', '-', '-']]
הזנה לא חוקית:
Player 'X' Please choose cell: 5 -2 Invalid line chosen (5) Invalid column chosen (-2) [['-', '-', '-'], ['-', 'O', 'X'], ['-', '-', '-']]
Player 'X' Please choose cell: 1 2 Cell (1,2) is taken, use other. [['-', '-', '-'], ['-', 'O', 'X'], ['-', '-', '-']]
Player 'X' Please choose cell: 2 -2 Invalid column chosen (-2) [['-', '-', '-'], ['-', 'O', 'X'], ['-', '-', '-']]
Player 'X' Please choose cell: 1 1 Cell (1,1) is taken, use other. [['-', '-', '-'], ['-', 'O', 'X'], ['-', '-', '-']]
שימו לב שלאחר כל פעולה אנו מדפיסים את הלוח, בין אם הצליחה ובין אם לאו.
In [4]:
# make_turn(player_char,board) כתבו את הפונקציה שלכם כאן
בונוס: ממשו פונקציה שמדפיסה את הלוח לאורך.
print_board(board)
[ ['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-'] ]
כאמור המשחק ממשיך כל עוד אין מנצח ונותר מקום פנוי בלוח.
נשים לב כי מספר המהלכים החוקיים יכול להיות לכל היותר כגודל הלוח.
כלומר – אם לא הוכרז מנצח במהלך המשחק, המשחק ייגמר לאחר 9 מהלכים עבור לוח בגודל $3\times3$.
נספור כמה מהלכים חוקיים יש במשחק. עבור מספרי מהלך זוגיים (0, 2, 4, ...) ישחק השחקן O, ועבור מספרי מהלך אי־זוגיים ישחק השחקן X.
נתאר את מהלך המשחק בפסאודו־קוד:
ממשו את המשחק על פי הפונקציות שיצרתם ועל פי תיאור מהלך המשחק.
In [ ]:
# tic_tac_toe()
כעת שחקו עם בני משפחה וחברים ;)
מנהלי הבנק היו מרוצים מאוד מהתוכנה הקודמת לניהול בנק שכתבתם וכעת הם מעוניינים לשפר אותה.
תחילה הם הביעו דאגה מחוזק הסיסמאות. מנגנון הסיסמאות הקודם היה חלש ומנהלי הבנק מפחדים שייעשה בו שימוש לרעה.
שמועות מתפשטות מהר מאוד ומנהלי הבנק שמעו שמימשתם גרסה לפונקציית גיבוב. הם מעוניינים להשתמש בה במנגנון הסיסמאות החדש.
מנגנון הסיסמאות ינוהל כך:
כל סיסמה של משתמש תישמר בקובץ בצורה: username:62242,
כאשר username הוא שם המשתמש שנרשם בפתיחת החשבון, ו־62242 הוא תוצאת ה־hash עבור הסיסמה שבחר.
בסעיף זה נממש את פונקציית פתיחת חשבון הבנק.
הפונקציה תקבל שם משתמש וסיסמה, ותיצור רשומה חדשה בקובץ הסיסמאות עבור המשתמש שביקש להצטרף לבנק.
כיוון שהבנק שלנו לא עבר לעבוד עם מספרי חשבון, עלינו לוודא ששם המשתמש שנבחר לא קיים כבר בבנק. אם קיים כזה, נדפיס שגיאה ולא נבצע דבר.
אם לא קיים שם משתמש כזה, נחשב את ה־hash של הסיסמה שהזין, ונוסיף לסוף הקובץ רשומה חדשה בייצוג שצוין בתחילת השאלה.
תוכן קובץ הסיסמאות (לצורך הדוגמה):
FreddieMercury:56443
BBKing:33471
DonaldDuck:17743
register_to_bank('FreddieMercury', 'ILoveQueen')
An account with name "FreddieMercury" already exists.
Account was not created.
register_to_bank('Simba', 'ILoveNala')
Account with name "Simba" was created and added to the bank.
תוכן קובץ הסיסמאות כעת:
FreddieMercury:56443
BBKing:19463
DonaldDuck:17743
Simba:6362
ממשו את פונקציית הרישום.
רמזים:
In [8]:
# ממשו את פונקציית הרישום כאן register_to_bank(username,password)
בעקבות השיפור במנגנון חוזק הסיסמאות, הצטרפו לקוחות רבים לבנק.
המנהלים מרוצים וכעת רוצים שתממשו עבורם מערכת הזדהות שעובדת עם קובץ הסיסמאות.
מערכת ההזדהות תעבוד כך:
מערכת ההזדהות הינה פונקציה המקבלת שם משתמש וסיסמה.
היא מחזירה True אם הזיהוי הצליח, ו־False אחרת.
תוכן קובץ הסיסמאות (לצורך הדוגמה):
FreddieMercury:61875
BBKing:33471
DonaldDuck:17743
Simba:6362
authenticate('FreddieMercury', 'ILoveQueen')
Wrong password (1/3). # myhash('ILoveQueen') = 99597 != 61875 (in password file)
authenticate('FreddieMercury', 'LetItBe')
Wrong password (2/3). # myhash('LetItBe') = 58060 != 61875 (in password file)
authenticate('FreddieMercury', 'HeyJude')
Wrong password (3/3). 'FreddieMercury' was removed. # myhash('HeyJude') = 8309 != 61875 (in password file)
authenticate('FreddieMercury', 'IHatePasswords!')
Account not registered. # FreddieMercury was removed in the previous example
authenticate('Simba', 'ILoveNala')
Welcome 'Simba'. # myhash('ILoveNala') = 6362 == 6362 (in password file)
תוכן קובץ הסיסמאות כעת:
BBKing:19463
DonaldDuck:17743
Simba:6362
ממשו את מערכת ההזדהות.
In [ ]:
# authenticate(username,password) ממשו את הפונקציה כאן