2025-09-15 17:20:59 -04:00
import random
from itertools import repeat
import discord
2025-10-26 18:38:19 -04:00
from discord import app_commands
2025-09-15 17:20:59 -04:00
from discord . ext import commands
2025-10-08 16:03:54 -04:00
import asyncio
import time
2025-09-15 17:20:59 -04:00
2025-10-26 19:20:27 -04:00
class ChallengeConfirmView ( discord . ui . View ) :
def __init__ ( self , context , challenger , challenged , bomb_count , board ) :
super ( ) . __init__ ( timeout = 60 )
self . context = context
self . challenger = challenger
self . challenged = challenged
self . bomb_count = bomb_count
self . board = board
self . accepted = False
@discord.ui.button ( label = " Confirm " , style = discord . ButtonStyle . grey , emoji = " <:Check:1417250703407186051> " )
async def confirm_button ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
if interaction . user . id != self . challenged . id :
return await interaction . response . send_message ( " Only the challenged player can accept! " , ephemeral = True )
self . accepted = True
self . stop ( )
embed = discord . Embed (
title = " Challenge Accepted! " ,
description = f " 🎮 ** { self . challenger . mention } ** vs ** { self . challenged . mention } ** \n \n 💣 Total Bombs: ` { self . bomb_count } ` \n \n { self . challenger . mention } goes first! Click the buttons to reveal the grid. Avoid the bombs! " ,
color = 0x00FF00
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
view = MsView ( self . context , self . ExtractBlocks ( ) , self . bomb_count , self . board , self . challenged )
message = await interaction . response . edit_message ( embed = embed , view = view )
view . message = message
@discord.ui.button ( label = " Cancel " , style = discord . ButtonStyle . grey , emoji = " <:Cross:1417250649514446849> " )
async def cancel_button ( self , interaction : discord . Interaction , button : discord . ui . Button ) :
if interaction . user . id != self . challenged . id :
return await interaction . response . send_message ( " Only the challenged player can decline! " , ephemeral = True )
embed = discord . Embed (
title = " Challenge Declined " ,
description = f " <a:woeisnuke:1432138229628276927> { self . challenged . mention } declined the minesweeper challenge from { self . challenger . mention } " ,
color = 0xE02B2B
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
await interaction . response . edit_message ( embed = embed , view = None )
self . stop ( )
async def on_timeout ( self ) :
embed = discord . Embed (
title = " Challenge Expired " ,
description = f " ⏰ { self . challenged . mention } didn ' t respond to the minesweeper challenge from { self . challenger . mention } " ,
color = 0xFFA500
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
try :
await self . message . edit ( embed = embed , view = None )
except :
pass
def ExtractBlocks ( self ) :
new_b = [ ]
for x in self . board :
for y in x :
new_b . append ( y )
return new_b
2025-09-15 17:20:59 -04:00
class RowButton ( discord . ui . Button ) :
2025-10-26 18:38:19 -04:00
def __init__ ( self , ctx , label , custom_id , bombs , board , opponent = None ) :
2025-09-15 17:20:59 -04:00
super ( ) . __init__ ( label = label , style = discord . ButtonStyle . grey , custom_id = custom_id )
self . ctx = ctx
self . bombs = bombs
self . board = board
2025-10-26 18:38:19 -04:00
self . opponent = opponent
2025-09-15 17:20:59 -04:00
async def callback ( self , interaction ) :
assert self . view is not None
view : MsView = self . view
2025-10-08 16:03:54 -04:00
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
2025-10-26 18:38:19 -04:00
if view . opponent :
if interaction . user . id not in [ self . ctx . author . id , view . opponent . id ] :
return await interaction . followup . send (
" You cannot interact with these buttons. " , ephemeral = True
)
if interaction . user . id != view . current_player . id :
return await interaction . followup . send (
f " It ' s { view . current_player . mention } ' s turn! " , ephemeral = True
)
else :
if interaction . user . id != self . ctx . author . id :
return await interaction . followup . send (
" You cannot interact with these buttons. " , ephemeral = True
)
2025-09-15 17:20:59 -04:00
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 )
2025-10-11 11:55:53 -04:00
if not view . bombs_generated :
view . generate_bombs ( int ( b_id [ 5 : ] ) )
self . bombs = view . bombs
2025-09-15 17:20:59 -04:00
if int ( b_id [ 5 : ] ) in self . bombs :
2025-10-26 18:38:19 -04:00
await view . RevealBombs ( b_id , view . board , interaction )
2025-09-15 17:20:59 -04:00
else :
count = [ ]
rawpos = int ( b_id [ 5 : ] )
pos = view . GetBoardPos ( rawpos )
def checkpos ( count , rawpos , pos ) :
pos = view . GetBoardPos ( rawpos )
if not rawpos - 1 in self . bombs or pos == 0 :
count . append ( rawpos - 1 )
if not rawpos + 1 in self . bombs or pos == 4 :
count . append ( rawpos + 1 )
if not rawpos - 6 in self . bombs or pos == 0 :
count . append ( rawpos - 6 )
if not rawpos - 4 in self . bombs or pos == 4 :
count . append ( rawpos - 4 )
if not rawpos + 6 in self . bombs or pos == 4 :
count . append ( rawpos + 6 )
if not rawpos + 4 in self . bombs or pos == 0 :
count . append ( rawpos + 4 )
if not rawpos - 5 in self . bombs :
count . append ( rawpos - 5 )
if not rawpos + 5 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 )
2025-10-26 18:21:08 -04:00
2025-10-26 18:38:19 -04:00
if view . opponent :
if interaction . user . id == view . ctx . author . id :
view . player1_score + = 1
else :
view . player2_score + = 1
2025-10-26 18:21:08 -04:00
if number == 0 :
await view . auto_reveal_safe_squares ( rawpos , interaction )
2025-10-26 18:38:19 -04:00
if view . opponent :
view . current_player = view . opponent if view . current_player . id == view . ctx . author . id else view . ctx . author
2025-09-15 17:20:59 -04:00
if len ( view . moves ) + len ( self . bombs ) == 25 :
await view . EndGame ( )
2025-10-08 16:03:54 -04:00
else :
try :
2025-10-26 18:38:19 -04:00
await view . update_embed ( interaction )
2025-10-08 16:03:54 -04:00
except discord . errors . HTTPException as e :
if e . status == 429 :
await asyncio . sleep ( 1 )
else :
raise
2025-09-15 17:20:59 -04:00
class MsView ( discord . ui . View ) :
2025-10-26 18:38:19 -04:00
def __init__ ( self , ctx , options , bomb_count , board , opponent = None ) :
2025-10-08 16:03:54 -04:00
super ( ) . __init__ ( timeout = 300 )
2025-09-15 17:20:59 -04:00
for i , op in enumerate ( options ) :
2025-10-26 18:38:19 -04:00
self . add_item ( RowButton ( ctx , op , f " block { i } " , [ ] , board , opponent ) )
2025-09-15 17:20:59 -04:00
self . board = board
2025-10-11 11:55:53 -04:00
self . bombs = [ ]
self . bomb_count = bomb_count
self . bombs_generated = False
2025-09-15 17:20:59 -04:00
self . moves = [ ]
self . ctx = ctx
self . message = None
2025-10-08 16:03:54 -04:00
self . last_interaction = 0
2025-10-26 18:38:19 -04:00
self . opponent = opponent
self . current_player = ctx . author
self . player1_score = 0
self . player2_score = 0
2025-10-08 16:03:54 -04:00
2025-10-11 11:55:53 -04:00
def generate_bombs ( self , first_move_pos ) :
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
2025-10-08 16:03:54 -04:00
async def on_timeout ( self ) :
for button in self . children :
button . disabled = True
2025-09-15 17:20:59 -04:00
embed = discord . Embed (
2025-10-08 16:03:54 -04:00
title = " Minesweeper " ,
description = " Game timed out! " ,
color = 0xFF0000
2025-09-15 17:20:59 -04:00
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
2025-10-08 16:03:54 -04:00
try :
2025-10-26 19:20:27 -04:00
await interaction . edit_original_response ( embed = embed , view = self )
2025-10-08 16:03:54 -04:00
except :
pass
async def EndGame ( self ) :
2025-09-15 17:20:59 -04:00
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 ) ] = " 💣 "
2025-10-08 16:03:54 -04:00
2025-10-26 18:38:19 -04:00
if self . opponent :
if self . player1_score > self . player2_score :
winner = self . ctx . author
elif self . player2_score > self . player1_score :
winner = self . opponent
else :
winner = None
if winner :
description = f " 🎉 ** { winner . mention } ** won! "
else :
description = f " 🤝 It ' s a tie! "
else :
description = " Game Ended. You won! "
2025-10-08 16:03:54 -04:00
embed = discord . Embed (
title = " Minesweeper " ,
2025-10-26 18:38:19 -04:00
description = description ,
2025-10-08 16:03:54 -04:00
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
2025-10-10 10:20:38 -04:00
self . stop ( )
2025-09-15 17:20:59 -04:00
2025-10-26 18:38:19 -04:00
async def update_embed ( self , interaction ) :
if self . opponent :
embed = discord . Embed (
title = " Minesweeper - Multiplayer " ,
description = f " 💣 Total Bombs: ` { self . bomb_count } ` \n \n 🎮 **Players:** \n { self . ctx . author . mention } vs { self . opponent . mention } \n \n **Current Turn:** { self . current_player . mention } " ,
color = 0x7289DA
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
else :
embed = discord . Embed (
title = " Minesweeper " ,
description = f " 💣 Total Bombs: ` { self . bomb_count } ` \n \n Click 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 " )
await interaction . edit_original_response ( embed = embed , view = self )
2025-10-26 18:21:08 -04:00
async def auto_reveal_safe_squares ( self , center_pos , interaction ) :
positions_to_check = [ center_pos ]
revealed_positions = set ( )
2025-10-26 18:38:19 -04:00
current_player_id = interaction . user . id
2025-10-26 18:21:08 -04:00
while positions_to_check :
current_pos = positions_to_check . pop ( 0 )
if current_pos in revealed_positions :
continue
revealed_positions . add ( current_pos )
adjacent_positions = [ ]
pos = self . GetBoardPos ( current_pos )
if pos > 0 and current_pos - 1 not in self . bombs :
adjacent_positions . append ( current_pos - 1 )
if pos < 4 and current_pos + 1 not in self . bombs :
adjacent_positions . append ( current_pos + 1 )
if current_pos > = 5 and current_pos - 5 not in self . bombs :
adjacent_positions . append ( current_pos - 5 )
if current_pos < = 19 and current_pos + 5 not in self . bombs :
adjacent_positions . append ( current_pos + 5 )
if pos > 0 and current_pos > = 5 and current_pos - 6 not in self . bombs :
adjacent_positions . append ( current_pos - 6 )
if pos < 4 and current_pos > = 5 and current_pos - 4 not in self . bombs :
adjacent_positions . append ( current_pos - 4 )
if pos > 0 and current_pos < = 19 and current_pos + 4 not in self . bombs :
adjacent_positions . append ( current_pos + 4 )
if pos < 4 and current_pos < = 19 and current_pos + 6 not in self . bombs :
adjacent_positions . append ( current_pos + 6 )
for adj_pos in adjacent_positions :
if adj_pos not in self . moves and adj_pos not in revealed_positions :
adj_count = [ ]
adj_pos_obj = self . GetBoardPos ( adj_pos )
def checkpos_adj ( count , rawpos , pos ) :
pos = self . GetBoardPos ( rawpos )
if not rawpos - 1 in self . bombs or pos == 0 :
count . append ( rawpos - 1 )
if not rawpos + 1 in self . bombs or pos == 4 :
count . append ( rawpos + 1 )
if not rawpos - 6 in self . bombs or pos == 0 :
count . append ( rawpos - 6 )
if not rawpos - 4 in self . bombs or pos == 4 :
count . append ( rawpos - 4 )
if not rawpos + 6 in self . bombs or pos == 4 :
count . append ( rawpos + 6 )
if not rawpos + 4 in self . bombs or pos == 0 :
count . append ( rawpos + 4 )
if not rawpos - 5 in self . bombs :
count . append ( rawpos - 5 )
if not rawpos + 5 in self . bombs :
count . append ( rawpos + 5 )
return count
adj_count = checkpos_adj ( adj_count , adj_pos , adj_pos_obj )
adj_number = 8 - len ( adj_count )
for button in self . children :
if int ( button . custom_id [ 5 : ] ) == adj_pos :
button . label = str ( adj_number ) if adj_number > 0 else " 0 "
button . style = discord . ButtonStyle . green
break
self . board [ self . GetBoardRow ( adj_pos ) ] [ self . GetBoardPos ( adj_pos ) ] = str ( adj_number ) if adj_number > 0 else " 0 "
self . moves . append ( adj_pos )
2025-10-26 18:38:19 -04:00
if self . opponent :
if current_player_id == self . ctx . author . id :
self . player1_score + = 1
else :
self . player2_score + = 1
2025-10-26 18:21:08 -04:00
if adj_number == 0 :
positions_to_check . append ( adj_pos )
try :
2025-10-26 18:38:19 -04:00
await self . update_embed ( interaction )
2025-10-26 18:21:08 -04:00
except discord . errors . HTTPException as e :
if e . status == 429 :
await asyncio . sleep ( 1 )
else :
raise
2025-09-15 17:20:59 -04:00
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
2025-10-26 18:38:19 -04:00
async def RevealBombs ( self , b_id , board , interaction ) :
2025-09-15 17:20:59 -04:00
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
2025-10-08 16:03:54 -04:00
2025-10-26 18:38:19 -04:00
if self . opponent :
loser = self . ctx . author if interaction . user . id == self . ctx . author . id else self . opponent
winner = self . opponent if loser . id == self . ctx . author . id else self . ctx . author
description = f " 💥 BOOM! { loser . mention } hit a bomb! \n 🎉 ** { winner . mention } ** wins! "
else :
description = f " 💥 BOOM! You hit a bomb. Game Over! \n -# gg { self . ctx . author . mention } "
2025-10-08 16:03:54 -04:00
embed = discord . Embed (
title = " Minesweeper " ,
2025-10-26 18:38:19 -04:00
description = description ,
2025-10-08 16:03:54 -04:00
color = 0xE02B2B
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
try :
2025-10-26 19:20:27 -04:00
await interaction . edit_original_response ( embed = embed , view = self )
2025-10-08 16:03:54 -04:00
except discord . errors . HTTPException as e :
if e . status == 429 :
await asyncio . sleep ( 1 )
else :
raise
2025-10-10 10:20:38 -04:00
self . stop ( )
2025-09-15 17:20:59 -04:00
2025-09-28 22:48:10 -04:00
def minesweeper_command ( ) :
2025-09-15 17:20:59 -04:00
@commands.hybrid_command (
name = " minesweeper " ,
description = " Play a buttoned minesweeper mini-game. "
)
2025-10-26 18:38:19 -04:00
@app_commands.describe (
opponent = " Optional user to play against in multiplayer mode. "
)
async def minesweeper ( self , context , opponent : discord . User = None ) :
2025-09-28 22:48:10 -04:00
board = [ [ " ឵ ឵ " ] * 5 for _ in range ( 5 ) ]
2025-10-11 11:55:53 -04:00
bomb_count = random . randint ( 4 , 11 )
2025-09-15 17:20:59 -04:00
def ExtractBlocks ( ) :
new_b = [ ]
for x in board :
for y in x :
new_b . append ( y )
return new_b
2025-10-26 18:38:19 -04:00
if opponent :
if opponent . id == context . author . id :
embed = discord . Embed (
title = " Error! " ,
description = " You cannot play against yourself! " ,
color = 0xE02B2B
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
return await context . send ( embed = embed , ephemeral = True )
embed = discord . Embed (
2025-10-26 19:20:27 -04:00
title = " Minesweeper Challenge " ,
description = f " 🎮 ** { opponent . mention } **, you are challenged by ** { context . author . mention } ** to a round of Minesweeper! \n \n 💣 Total Bombs: ` { bomb_count } ` " ,
2025-10-26 18:38:19 -04:00
color = 0x7289DA
)
embed . set_author ( name = " Fun " , icon_url = " https://yes.nighty.works/raw/eW5lLm.webp " )
2025-10-26 19:20:27 -04:00
view = ChallengeConfirmView ( context , context . author , opponent , bomb_count , board )
message = await context . send ( embed = embed , view = view )
view . message = message
2025-10-26 18:38:19 -04:00
else :
embed = discord . Embed (
title = " Minesweeper " ,
description = f " 💣 Total Bombs: ` { bomb_count } ` \n \n Click 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 " )
2025-10-26 19:20:27 -04:00
view = MsView ( context , ExtractBlocks ( ) , bomb_count , board , opponent )
message = await context . send ( embed = embed , view = view )
view . message = message
2025-09-28 22:48:10 -04:00
return minesweeper