from random import choice from aiogram import Bot, F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import ( CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, Message, ReplyKeyboardMarkup, ReplyKeyboardRemove, ) from aiogram.utils.formatting import Bold, as_key_value, as_list, as_marked_section router = Router() class GameState(StatesGroup): tic_tac_toy = State() @router.message(F.text.lower() == 'exit game') async def exit_games(message: Message, state: FSMContext): await state.clear() await message.reply('Game is finished', reply_markup=ReplyKeyboardRemove()) async def get_keyboard(field: list): buttons = [] for x in range(3): row = [] for y in range(3): callback_data = f'{x} {y}' text = '' match field[x][y]: case None: text = '⬛️' case False: text = '⭕️' case True: text = '❌' row.append( InlineKeyboardButton(text=text, callback_data=callback_data), ) buttons.append(row) keyboard = InlineKeyboardMarkup(inline_keyboard=buttons) return keyboard async def get_content(bot_wins, user_wins, move): content = as_list( as_marked_section( Bold('Game summary:'), as_key_value('Total games', bot_wins + user_wins), as_key_value('user wins', user_wins), as_key_value('bot wins', bot_wins), as_key_value('Move: ', move), marker=' ', ), sep='\n\n', ) return content async def check_winner(field): winner = None def check_equal_all_in_row(row): if row[0] == row[1] and row[0] == row[2]: return True return False for row in field: if check_equal_all_in_row(row): winner = row[0] for row in zip(*field): if check_equal_all_in_row(row): winner = row[0] row = [] for i in range(3): row.append(field[i][i]) if check_equal_all_in_row(row): winner = row[0] row = [] for i in range(3): row.append(field[i][2 - i]) if check_equal_all_in_row(row): winner = row[0] return winner @router.message(F.text.lower() == 'tic_tac_toy') async def start_tic_tac_toy( message: Message, state: FSMContext, user_wins=0, bot_wins=0 ): tic_tac_toy_kb = [ [KeyboardButton(text='Exit Game')], ] await state.clear() await state.set_state(GameState.tic_tac_toy) data = { 'field': [[None for _ in range(3)] for _ in range(3)], 'availible_moves': [(x, y) for x in range(3) for y in range(3)], 'user_wins': user_wins, 'bot_wins': bot_wins, 'move': 0, } await state.set_data(data) await message.reply('Start Tic Tac Toy', reply_markup=ReplyKeyboardRemove()) await message.reply( 'Make your move', reply_markup=ReplyKeyboardMarkup(keyboard=tic_tac_toy_kb, resize_keyboard=True), ) await make_move(message, state) @router.message(GameState.tic_tac_toy, F.text) async def make_move(message: Message, state: FSMContext): data = await state.get_data() keyboard = await get_keyboard(data['field']) await state.update_data(data) content = await get_content( data['bot_wins'], data['user_wins'], data['move'], ) await message.answer(**content.as_kwargs(), reply_markup=keyboard) @router.callback_query(GameState.tic_tac_toy) async def check_answer(callback: CallbackQuery, state: FSMContext, bot: Bot): data = await state.get_data() x, y = map(int, callback.data.split()) if (x, y) not in data['availible_moves']: await callback.answer(text='Cell is busy', show_alert=True) else: data['move'] += 1 data['field'][x][y] = True data['availible_moves'].remove((x, y)) await state.update_data(data) keyboard = await get_keyboard(data['field']) content = await get_content( data['bot_wins'], data['user_wins'], data['move'], ) await bot.edit_message_text( **content.as_kwargs(), chat_id=callback.from_user.id, message_id=callback.message.message_id, reply_markup=keyboard, ) winner = await check_winner(data['field']) if winner is True: await callback.answer() await start_tic_tac_toy( callback.message, state, data['user_wins'] + 1, data['bot_wins'] ) return if not data['availible_moves']: await callback.answer('A draw game') await start_tic_tac_toy( callback.message, state, data['user_wins'], data['bot_wins'] ) return x, y = choice(data['availible_moves']) data['field'][x][y] = False data['availible_moves'].remove((x, y)) data['move'] += 1 await state.update_data(data) keyboard = await get_keyboard(data['field']) content = await get_content( data['bot_wins'], data['user_wins'], data['move'], ) await bot.edit_message_text( **content.as_kwargs(), chat_id=callback.from_user.id, message_id=callback.message.message_id, reply_markup=keyboard, ) winner = await check_winner(data['field']) if winner is False: await callback.answer(text='Bot is WINNER') await start_tic_tac_toy( callback.message, state, data['user_wins'], data['bot_wins'] + 1 ) return await callback.answer()