Files
Syntrel/cogs/fun/minesweeper.py
2025-11-22 19:51:46 -05:00

276 lines
9.1 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import random
import discord
from discord.ext import commands
import asyncio
import time
class RowButton(discord.ui.Button):
def __init__(self, ctx, label, custom_id, bombs, board):
super().__init__(
label=label, style=discord.ButtonStyle.grey, custom_id=custom_id
)
self.ctx = ctx
self.bombs = bombs
self.board = board
async def callback(self, interaction):
assert self.view is not None
view: MsView = self.view
current_time = time.time()
if current_time - view.last_interaction < 0.5:
try:
return await interaction.response.send_message(
"Please wait before clicking again.", ephemeral=True
)
except:
return
view.last_interaction = current_time
try:
await interaction.response.defer()
except discord.errors.HTTPException as e:
if e.status == 429:
await asyncio.sleep(1)
return
raise
if interaction.user.id != self.ctx.author.id:
return await interaction.followup.send(
"You cannot interact with these buttons.", ephemeral=True
)
b_id = self.custom_id
if int(b_id[5:]) in view.moves:
return await interaction.followup.send(
"That part is already taken.", ephemeral=True
)
if not view.bombs_generated:
view.generate_bombs(int(b_id[5:]))
self.bombs = view.bombs
if int(b_id[5:]) in self.bombs:
await view.RevealBombs(b_id, view.board)
else:
count = []
rawpos = int(b_id[5:])
pos = view.GetBoardPos(rawpos)
def checkpos(count, rawpos, pos):
pos = view.GetBoardPos(rawpos)
if rawpos - 1 not in self.bombs or pos == 0:
count.append(rawpos - 1)
if rawpos + 1 not in self.bombs or pos == 4:
count.append(rawpos + 1)
if rawpos - 6 not in self.bombs or pos == 0:
count.append(rawpos - 6)
if rawpos - 4 not in self.bombs or pos == 4:
count.append(rawpos - 4)
if rawpos + 6 not in self.bombs or pos == 4:
count.append(rawpos + 6)
if rawpos + 4 not in self.bombs or pos == 0:
count.append(rawpos + 4)
if rawpos - 5 not in self.bombs:
count.append(rawpos - 5)
if rawpos + 5 not in self.bombs:
count.append(rawpos + 5)
return count
count = checkpos(count, rawpos, pos)
number = 8 - len(count)
self.label = str(number) if number > 0 else "0"
self.style = discord.ButtonStyle.green
pos = int(b_id[5:])
view.board[view.GetBoardRow(pos)][view.GetBoardPos(pos)] = (
str(number) if number > 0 else "0"
)
view.moves.append(pos)
if len(view.moves) + len(self.bombs) == 25:
await view.EndGame()
else:
try:
await interaction.edit_original_response(view=view)
except discord.errors.HTTPException as e:
if e.status == 429:
await asyncio.sleep(1)
else:
raise
class MsView(discord.ui.View):
def __init__(self, ctx, options, bomb_count, board):
super().__init__(timeout=300)
for i, op in enumerate(options):
self.add_item(RowButton(ctx, op, f"block{i}", [], board))
self.board = board
self.bombs = []
self.bomb_count = bomb_count
self.bombs_generated = False
self.moves = []
self.ctx = ctx
self.message = None
self.last_interaction = 0
def generate_bombs(self, first_move_pos):
"""Generate bombs excluding the first clicked position"""
bombpositions = []
excluded_positions = [0, 4, 20, 24, first_move_pos]
while len(bombpositions) < self.bomb_count:
random_index = random.randint(0, 24)
if (
random_index not in bombpositions
and random_index not in excluded_positions
):
bombpositions.append(random_index)
self.bombs = bombpositions
self.bombs_generated = True
for button in self.children:
if isinstance(button, RowButton):
button.bombs = self.bombs
async def on_timeout(self):
for button in self.children:
button.disabled = True
embed = discord.Embed(
title="Minesweeper", description="Game timed out!", color=0xFF0000
)
embed.set_author(
name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp"
)
try:
await self.message.edit(embed=embed, view=self)
except:
pass
async def EndGame(self):
for button in self.children:
button.disabled = True
pos = int(button.custom_id[5:])
if pos in self.bombs:
button.label = "💣"
button.style = discord.ButtonStyle.red
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = "💣"
embed = discord.Embed(
title="Minesweeper", description="Game Ended. You won!", color=0x00FF00
)
embed.set_author(
name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp"
)
try:
await self.message.edit(embed=embed, view=self)
except discord.errors.HTTPException as e:
if e.status == 429:
await asyncio.sleep(1)
else:
raise
self.stop()
def GetBoardRow(self, pos):
if pos in [0, 1, 2, 3, 4]:
return 0
if pos in [5, 6, 7, 8, 9]:
return 1
if pos in [10, 11, 12, 13, 14]:
return 2
if pos in [15, 16, 17, 18, 19]:
return 3
if pos in [20, 21, 22, 23, 24]:
return 4
return False
def GetBoardPos(self, pos):
if pos in [0, 1, 2, 3, 4]:
return pos
if pos in [5, 6, 7, 8, 9]:
for i, num in enumerate(range(5, 10)):
if pos == num:
return i
if pos in [10, 11, 12, 13, 14]:
for i, num in enumerate(range(10, 15)):
if pos == num:
return i
if pos in [15, 16, 17, 18, 19]:
for i, num in enumerate(range(15, 20)):
if pos == num:
return i
if pos in [20, 21, 22, 23, 24]:
for i, num in enumerate(range(20, 25)):
if pos == num:
return i
return False
async def RevealBombs(self, b_id, board):
bombemo = "💣"
for button in self.children:
button.disabled = True
if button.custom_id == b_id:
button.label = bombemo
button.style = discord.ButtonStyle.red
pos = int(b_id[5:])
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = bombemo
for button in self.children:
if int(button.custom_id[5:]) in self.bombs:
button.label = bombemo
button.style = discord.ButtonStyle.red
pos = int(button.custom_id[5:])
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = bombemo
embed = discord.Embed(
title="Minesweeper",
description=f"💥 BOOM! You hit a bomb. Game Over!\n-# gg {self.ctx.author.mention}",
color=0xE02B2B,
)
embed.set_author(
name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp"
)
try:
await self.message.edit(embed=embed, view=self)
except discord.errors.HTTPException as e:
if e.status == 429:
await asyncio.sleep(1)
else:
raise
self.stop()
def minesweeper_command():
@commands.hybrid_command(
name="minesweeper", description="Play a buttoned minesweeper mini-game."
)
async def minesweeper(self, context):
board = [[" "] * 5 for _ in range(5)]
bomb_count = random.randint(4, 11)
def ExtractBlocks():
new_b = []
for x in board:
for y in x:
new_b.append(y)
return new_b
embed = discord.Embed(
title="Minesweeper",
description=f"💣 Total Bombs: `{bomb_count}`\n\nClick the buttons to reveal the grid. Avoid the bombs!",
color=0x7289DA,
)
embed.set_author(
name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp"
)
view = MsView(context, ExtractBlocks(), bomb_count, board)
message = await context.send(embed=embed, view=view)
view.message = message
return minesweeper