التجميع النهائي: تشغيل لعبة إكس-أو المتكاملة
حان وقت جمع كل الأجزاء في لعبة كاملة.
الكود التالي يحتوي على تنفيذات للدوال التي بنيناها في الصفحات السابقة، وبعضها يستخدم اختصارات لم تتعلمها بعد لجعل الكود أقصر. لا تغيّر هذه الدوال. مهمتك هي تنفيذ play_game بصورة صحيحة.
التنفيذ الحالي يوضح بداية اللعبة فقط، لكنه غير مكتمل. يجب أن يعمل الحل مع أي حجم لوحة، ويستمر حتى تنتهي اللعبة. آخر شيء تفعله play_game يجب أن يكون أحد أمرين:
- استدعاء
print_winner(player)إذا أصبحتwinner(board)هيTrue. - أو استدعاء
print_draw()إذا امتلأت اللوحة من دون فائز.
يمكنك افتراض أن المستخدم سيدخل مدخلات صالحة فقط: أرقامًا من 1 إلى board_size لاختيار خانة غير مشغولة.
def winning_line(strings):
strings = set(strings)
return len(strings) == 1 and ' ' not in strings
def row_winner(board):
return any(winning_line(row) for row in board)
def column_winner(board):
return row_winner(zip(*board))
def main_diagonal_winner(board):
return winning_line(row[i] for i, row in enumerate(board))
def diagonal_winner(board):
return main_diagonal_winner(board) or main_diagonal_winner(reversed(board))
def winner(board):
return row_winner(board) or column_winner(board) or diagonal_winner(board)
def format_board(board):
size = len(board)
line = f'\n {"+".join("-" * size)}\n'
rows = [f'{i + 1} {"|".join(row)}' for i, row in enumerate(board)]
return f' {" ".join(str(i + 1) for i in range(size))}\n{line.join(rows)}'
def play_move(board, player):
print(f'{player} to play:')
row = int(input()) - 1
col = int(input()) - 1
board[row][col] = player
print(format_board(board))
def make_board(size):
return [[' '] * size for _ in range(size)]
def print_winner(player):
print(f'{player} wins!')
def print_draw():
print("It's a draw!")
def play_game(board_size, player1, player2):
board = make_board(board_size)
print(format_board(board))
play_move(board, player1)
play_move(board, player2)
play_move(board, player1)
play_move(board, player2)
play_game(3, 'X', 'O')
استخدم الدوال winner وformat_board وplay_move وmake_board وprint_winner وprint_draw بدل إعادة تنفيذ عملها. تحتاج إلى حلقة تلعب عددًا أقصى من الحركات يساوي عدد خانات اللوحة. في كل دورة تُنفذ حركة لاعب واحد فقط، ثم تتحقق من الفوز، وتبدّل اللاعب إذا استمرت اللعبة.
إذا ظهر فائز، اطبع الفائز وأنهِ play_game فورًا باستخدام return. وإذا انتهت كل الحركات من دون فائز، استدعِ print_draw() بعد الحلقة مرة واحدة فقط. تأكد أن player1 يبدأ أولًا، وأنك لا تطبع التعادل داخل الحلقة قبل امتلاء اللوحة.
def winning_line(strings):
strings = set(strings)
return len(strings) == 1 and ' ' not in strings
def row_winner(board):
return any(winning_line(row) for row in board)
def column_winner(board):
return row_winner(zip(*board))
def main_diagonal_winner(board):
return winning_line(row[i] for i, row in enumerate(board))
def diagonal_winner(board):
return main_diagonal_winner(board) or main_diagonal_winner(reversed(board))
def winner(board):
return row_winner(board) or column_winner(board) or diagonal_winner(board)
def format_board(board):
size = len(board)
line = f'
{"+".join("-" * size)}
'
rows = [f'{i + 1} {"|".join(row)}' for i, row in enumerate(board)]
return f' {" ".join(str(i + 1) for i in range(size))}
{line.join(rows)}'
def play_move(board, player):
print(f'{player} to play:')
row = int(input()) - 1
col = int(input()) - 1
board[row][col] = player
print(format_board(board))
def make_board(size):
return [[' '] * size for _ in range(size)]
def print_winner(player):
print(f'{player} wins!')
def print_draw():
print("It's a draw!")
def play_game(board_size, player1, player2):
board = make_board(board_size)
print(format_board(board))
play_move(board, player1)
play_move(board, player2)
play_move(board, player1)
play_move(board, player2)
play_game(3, 'X', 'O')
يجب أن تستخدم الدوال
winnerوformat_board— دون احتساب استخدامها داخلplay_move— وplay_moveوmake_boardوprint_winnerوprint_drawفي مكان ما.يكفي أن تذكر كل دالة من هذه مرة واحدة في الكود، حتى لو استُدعيت بعض الدوال عدة مرات أثناء التشغيل.
ستحتاج إلى حلقة
forلتكرار اللعب.لا تحتاج إلى التحقق هل امتلأت اللوحة، لأنك تستطيع حساب عدد النقلات اللازمة لملئها.
استخدم حلقة بعدد دورات ثابت، وداخلها تحقق هل يجب إنهاؤها مبكرًا.
ما أقصى عدد نقلات يمكن لعبها على لوحة 3×3؟ وماذا عن 4×4؟
المرور على
rangeطريقة سهلة لتكرار شيء عددًا ثابتًا من المرات.يمكنك استخدام
for _ in range(N):للعب بحد أقصىNنقلة.عند وجود فائز، يجب إنهاء الحلقة واللعبة.
يجب استدعاء
print_winnerأوprint_draw، وليس الاثنتين.أي دالة تُستدعى منهما يجب أن تُستدعى مرة واحدة فقط.
طريقة سهلة لتجنب استدعاء دالة عدة مرات هي وضع استدعائها خارج أي حلقة.
تعلمنا طريقتين لإيقاف الحلقة.
إحداهما
break، وهي تنهي الحلقة الحالية فقط.الطريقة الثانية تنهي الحلقة واستدعاء الدالة كله.
الطريقة الثانية هي
return.لا تلعب النقلات في أزواج مثل
play_move(board, player1)ثمplay_move(board, player2)كما في المثال.اجعل كل دورة من الحلقة تلعب نقلة واحدة فقط.
تحتاج إلى متغير يتابع دور أي لاعب.
يجب تبديل اللاعب في كل دورة.
جملة
ifمناسبة لذلك.خصوصًا مع
else.تأكد أن
player1يلعب النقلة الأولى.لا تستدعِ
print_winnerإلا بعد التحقق منwinnerبجملةif.يجب فحص وجود فائز داخل الحلقة لأنك لا تعرف متى قد يفوز أحد اللاعبين.
بعد استدعاء
print_winnerيمكنك استخدامreturnلإنهاء الدالة.يكفي
returnوحدها؛ الدالةplay_gameلا يُفترض أن تعيد قيمة.لا تستخدم
elseبعد فحص الفائز لاستدعاءprint_drawعند عدم وجود فائز؛ عدم وجود فائز حتى الآن لا يعني أن اللعبة تعادل.لا تستدعِ
print_drawإلا بعد لعب كل النقلات وعدم وجود فائز.إذًا استدعها بعد الحلقة وخارجها.
راجع المسافات البادئة وتأكد أن
print_drawليست داخل جسم حلقةfor.
تهانينا!
لقد أنهيت اللعبة بنجاح!