I wrote a simple TicTacToe game: user sends cell's index and bot places character('X' or 'O') into that cell and plays back. It works fine as long as it's being used by one person. However, when a few are concurrently playing, bot refuses to process requests and sometimes sends the similar board(which I store in dict for each player individually)
I think that I messed up with async function 'MakeMove'. I haven't used python much and a bit of advise would be highly appreciated. Thank you!
import telebot
import asyncio
#Consists of objects of class Player
players = {}
class Player:
def __init__(self, userName):
self.userName = userName
self.winsAmount = 0
self.loses = 0
self.pickedChar = 'X'
self.isGameRunning = False
self.isTurn = True
self.currentBoard = Board(self.pickedChar)
...
#bot.message_handler(commands=['game'])
def StartGame(message):
chatId = message.chat.id
if chatId not in players:
players[chatId] = Player(message.from_user.username)
if not isGameRunning(chatId):
player = players[chatId]
player.isGameRunning = True
bot.send_message(chatId, f'Hello, *{player.userName}*!\nYour wins amount is _{player.winsAmount}_\nYou\'ve lost _{player.loses}_ times', parse_mode='Markdown')
keyboard = telebot.types.InlineKeyboardMarkup()
keyboard.row(telebot.types.InlineKeyboardButton('X', callback_data='charX'),
telebot.types.InlineKeyboardButton('O', callback_data='charO'))
bot.send_message(chatId, 'Choose your side: X | O', reply_markup=keyboard)
else:
keyboard = telebot.types.InlineKeyboardMarkup()
keyboard.row(telebot.types.InlineKeyboardButton('End the current game', callback_data='endGame'))
bot.send_message(chatId, 'The game is still running!', reply_markup=keyboard)
#bot.message_handler(content_types=['text'])
def HandleMoves(message):
player = players[message.chat.id]
board = player.currentBoard
for i in range(9):
if message.text == (str)(i + 1) and player.isGameRunning and player.isTurn:
if board.desk[i] == '_':
board.desk[i] = board.playerChar
player.isTurn = False
else:
bot.send_message(message.chat.id, 'The cell is already occupied')
.........................................................
# I suppouse these two functions cause the problem
async def CheckForTurn(chatId):
while True:
if not players[chatId].isTurn:
return
await asyncio.sleep(0.2)
async def MakeMove(chatId):
board = players[chatId].currentBoard
player = players[chatId]
while True:
DrawBoard(chatId)
if board.GetState() != States.IN_GAME:
EndGame(chatId, board.GetState())
break
if player.isTurn:
bot.send_message(chatId, 'Make a move (from 1 to 9)')
await CheckForTurn(chatId)
if not player.isTurn:
index = GetBestMove(board)
char = ' '
if player.pickedChar == 'O':
char = 'X'
else:
char = 'O'
board.desk[index] = char
player.isTurn = True
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I am making a Russian roulette function on my discord bot.
You enter a list of players and it bans one of them.
Here is some pseudo-code I wrote:
elif message.content.startswith("!Russian"):
l = []
l.append(input("Enter a name"))
l.append(input("Enter a name"))
l.append(input("Enter a name"))
l.append(input("Enter a name"))
l.append(input("Enter a name"))
l.append(input("Enter a name"))
playertoban = l[random.randint(1, 6)]
ban(playertoban) #I wanna make a function that bans playertoban for one day
Note: It should only ban one of the players for only one day.
Here's the whole code
import discord
import os
import random
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='!')
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user} has connected to Discord!')
#client.event
async def on_message(message):
if message.content.startswith("!calculate"):
var = message.content
var = var.replace("!calculate", "")
var = var.replace(" ", "")
var = var.replace("^", "**")
var = var.replace("x", "*")
var = var.replace("X", "*")
try:
result = eval(var)
await message.channel.send(str(result))
except:
await message.channel.send("That is not a valid equation")
elif message.content.startswith("!spam"):
if "plane" in message.content:
c = -1
for i in range(100):
stre = ""
c = c + 1
for h in range(c):
stre = stre + "-"
stre = stre + ":airplane_departure:"
await message.channel.send(stre)
elif "boat" in message.content:
c = -1
for i in range(100):
stre = ""
c = c + 1
for h in range(c):
stre = stre + "-"
stre = stre + ":man_rowing_boat:"
await message.channel.send(stre)
elif message.content.startswith("!Er") or message.content.startswith("!Har") or message.content.startswith("!Vil") or message.content.startswith("!Hadde") or message.content.startswith("!Skal") or message.content.startswith("!Kommer") or message.content.startswith("!Tenker"):
re = random.randint(1, 10)
if re == 1:
await message.channel.send("Ja")
elif re == 2:
await message.channel.send("Nei")
elif re == 3:
await message.channel.send("Kanskje")
elif re == 4:
await message.channel.send("Vanskelig å si")
elif re == 5:
await message.channel.send("Definetivt!")
elif re == 6:
await message.channel.send("Sannsynligvis")
elif re == 7:
await message.channel.send("Antagelig ikke...")
elif re == 8:
await message.channel.send("Heck no!")
elif re == 9:
await message.channel.send("Hell ye!")
elif re == 10:
await message.channel.send("Vet ikke, men skal tenke litt på det")
I need help with the banning part I already know the picking who part.
You should not be using on_message for commands.
Once you have got the player name, you would want to get the Member object.
Then you would want to ban them, with deleting messages for 0 days. Record the datetime of the time in 1 day. Then add them to a database/json file. Then use a Tasks to check if it has reached that time then unban them then remove them from the database/json file.
Like FluxedScript says, you probably want to use a discord.py command rather than using on_message. If you do so, you could try this:
import random
from discord.ext import commands
from discord.ext import tasks
class DiscordBot(commands.Bot):
def __init__(self):
intents = discord.intents.default()
intents.members = True
self.bot = commands.Bot(prefix="!", intents=intents)
self.bot.add_cog(RussianRoulette(self.bot))
class RussianRoulette(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.banned = {}
#commands.command(name="russian", *names)
async def russian(self, ctx):
members_list = [discord.utils.get(ctx.guild.members, name=n) for n in names]
member = random.choice(member_list)
member.ban(reasion="Russian Roulette")
self.banned.setdefault(
member,
datetime.datetime.now()+datetime.timedelta(days=1)
)
#tasks.loop(minutes=1.0)
async def check_unban(self):
for mem, dtime in self.banned.items():
if datetime.datetime.now() <= dtime:
mem.unban(reason="Ban expired")
if __name__ == '__main__':
loop = asyncio.get_event_loop()
bot = DiscordBot()
loop.add_task(bot.start("TOKEN"))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
Now this assumes that your bot instance is properly set up. You will also need to activate some gateway intents
Sometimes it works fine, sometimes throws an error. Cannot figure it out. No matter how many inputs.
Thats the error. Is it the order of the code or something else?
give_gift.remove(reciever)
ValueError: list.remove(x): x not in list
Code:
import random
import os
import time
random.seed()
buy_gift = []
give_gift = []
names = True
print('Add name, then press ENTER. To finish type "end".')
while names is True:
name = input()
if name == "end":
if len(buy_gift) < 2:
print("Go play with your own gift. Goodbye!")
print()
os.system("PAUSE")
quit()
else:
names = False
else:
buy_gift.append(name)
give_gift.append(name)
exchange = len(give_gift)
os.system("cls")
time.sleep(1)
print("Drawing pairs...")
print()
time.sleep(1)
while exchange > 0:
giver = random.choice(buy_gift)
reciever = random.choice(give_gift)
while giver == reciever:
reciever = random.choice(buy_gift)
print(giver,"buys gift for",reciever)
time.sleep(0.25)
buy_gift.remove(giver)
give_gift.remove(reciever)
exchange -= 1
print()
os.system("PAUSE")
There is an issue with this line:
while giver == reciever:
reciever = random.choice(buy_gift)
It should be:
reciever = random.choice(give_gift)
I am buidling a simple game with pytelegrambotapi.
According to the rules, the user is sent a definition and four buttons with words as text, of which one is correct.
The user then should press the right button. Another routine then compares the user's answer to the right answer.
How do I retrieve the value of the button that the user pressed?
import telebot
from telebot import types
TOKEN = 'XXXXXXXXXXXXXXXXX'
bot = telebot.TeleBot(TOKEN)
kb = types.InlineKeyboardMarkup(row_width=2)
words = {
'Meager' : 'Lacking in quantity',
'Asylum' : 'Shelter',
'Irk' : 'To annoy',
'Kudos' : 'Great glory'
}
def compare(message, word, definition):
if definition == words[word]:
bot.send_message(message.from_user.id, 'Correct!')
else:
bot.send_message(message.from_user.id, 'Wrong!')
for item in words.items():
kb.add(types.InlineKeyboardButton(item[0], callback_data = item[0]))
#bot.message_handler(commands=['start', 's'])
def post_random_article(message):
word = 'Meager'
bot.send_message(message.from_user.id, word, reply_markup = kb)
bot.polling()
Nevermind, I found an answer.
#bot.callback_query_handler(func=lambda call: True)
def handle_query(call):
if call.data.split('#')[0] == call.data.split('#')[1]:
bot.send_message(call.message.chat.id, '✅ Correct!')
else:
bot.send_message(call.message.chat.id, '❌ Wrong!\n♻️ The answer is: %s' % call.data.split('#')[1])
#bot.message_handler(commands=['game', 'g'])
def game(message):
list_of_words = load_my_dictionary(message.from_user.id)
random.shuffle(list_of_words)
while not len(list_of_words) < 4:
current = list_of_words.pop()
word = current[0]
definition = current[1]
all_answers = generate_answers(word, list_of_words)
keyboard = generate_keyboard(all_answers, word)
bot.send_message(message.from_user.id, definition, reply_markup = keyboard)
time.sleep(7)
bot.send_message(message.from_user.id, '📭 List is empty!')
I have a game that is basically two commands, test and test2.
test makes you generate a word that you have to guess in test2, and if you miss the word 6 times you lose.
from collections import defaultdict
word = ""
guessesLeft = 6
blanks = []
guessedLetters = []
lettersFound = 0
bot = commands.Bot(command_prefix=("!"))
bot.gamex = defaultdict(bool)
#bot.command()
async def test(ctx, *, message):
await ctx.message.delete()
global word, guessesLeft, blanks, lettersFound, guessedLetters
if not bot.gamex[ctx.guild.id]:
word = message.lower()
blanks = []
guessedLetters = []
lettersFound = 0
guessesLeft = 6
bot.gamex[ctx.guild.id] = True
for i in range(0, len(word)):
blanks .append("-")
print(i)
await ctx.send(embed=discord.Embed(title="hangman: " + " ".join(blanks)))
#bot.command()
async def test2(ctx, *, guess):
global word, guessesLeft, blanks, lettersFound, guessedLetters
if bot.gamex[ctx.guild.id]:
if str.isalpha(guess) and len(guess) is 1 and str.lower(guess) not in guessedLetters:
if str.lower(guess) in word:
await ctx.send(guess + " is in the word. Good job!")
for i in range(0, len(word)):
if word[i] == str.lower(guess):
blanks[i] = str.lower(guess)
lettersFound += 1
else:
await ctx.send(guess + " is NOT in the word.")
guessesLeft -= 1
guessedLetters.append(str.lower(guess))
await ctx.send(" ".join(blanks))
await ctx.send("Guessed letters: " + " ".join(guessedLetters))
await ctx.send("Guesses left: " + str(guessesLeft))
if guessesLeft == 0:
await ctx.send("No guesses left. You lose!")
bot.gamex[ctx.guild.id] = False
if lettersFound == len(word)-1:
await ctx.send("You've won! The word was: " + word)
bot.gamex[ctx.guild.id] = False
It's a hangman game, but the game variables are mixing on every server the bot is on, if I guess a word on one server, it appears on another server, I want each server to be individual and have commands individual.
Only those in the global are mixing.
What would the command look like so that the variables don't get mixed up between the servers?
Make a class that represents the state of the game, and replace your gamex mapping with a mapping of guild ids to games:
games = {}
class Game:
def __init__(self, word, guesses=6):
self.word = word.lower()
self.blanks = ["-"]*len(word)
self.guessedLetters = []
self.lettersFound = 0
self.guessesLeft = guesses
Then in your commands, you get the Game storing the state for that guild.
#bot.command()
async def create_game(ctx, *, word):
await ctx.message.delete()
if ctx.guild.id in games:
await ctx.send("Game already in progress")
else:
games[ctx.guild.id] = Game(word)
await ctx.send(embed=discord.Embed(title="hangman: " + " ".join(games[ctx.guild.id].blanks)))
And you would call del games[ctx.guild.id] to remove entries when the game is complete.
A well-designed Game object would mean that the logic about how the game is played would be pulled out of your commands and into the Game object. Ideally, you would be able to take the Game class from this code and use it to implement the same game in a browser or other interface with minimal changes.
I am creating a feature in my chat bot that changes the bot agent name. I declare the name of the bot at the top.
bot = "Bot"
Then I create a function that takes input from the user and changes the name of the bot
elif "c-a" in inp:
settt = True
print(f"Choose agent(1-7):", end=' ')
while settt:
s_c = input()
try:
s = int(s_c)
except ValueError:
s = str(s_c)
sv = type(s)
if sv is int:
if s == 1:
bot = "Bhaskar"
return bot
elif s == 2:
bot = "Divya"
return bot
elif s == 3:
bot = "Nayan"
return bot
elif s == 4:
bot = "Sruti"
return bot
elif s == 5:
bot = "Gagan"
return bot
elif s == 6:
bot = "Ruchi"
return bot
elif s == 7:
bot = "Abhishek"
return bot
else:
a()
print("I didn't get it. Chose between 1 to 7 or type /h for help & /q for discard")
q()
else:
if s == "/h":
bot_list()
elif s == "/q":
settt = False
else:
a()
print("I didn't get it. Chose between 1 to 7 or type /h for help & /q for discard")
q()
But the value of the bot remains the same. It will not change.
That is because the variable bot is a global.
Inside your function before the if else statements add this line
global bot
So the code would look like this:
bot = "bot"
def name():
global bot
#if else statements begin here
You should add global bot line to your function. But I suggest to avoid global variables. I think you should give it as parameter and return the changed variable.
def your_function(param=None):
... Some code (Here you can use the value of bot variable which name is param inside this function.) ...
return param
bot = your_function(param=bot)
It means you overwrite the bot variable with return value of your function.
An example:
bot = 5
print("Before function: %s" % bot)
def your_function(param=None):
param = param*2
return param
bot = your_function(param=bot)
print("After function: %s" % bot)
Output:
python test.py
Before function: 5
After function: 10