I have a simple application of a Tic-Tac-Toe game.
Each time I press a button it calls a presser
method that changes the current state of who's turn it is, and checks if there's a winner etc..
The buttons are all made from the .kv
file.
I want to create a bot that chooses a random position - it returns a tuple of two random values between 0-2 (including).
So if for example it returned 1-1, I want it to press the middle button:
The presser method gets the button itself as a callback from the .kv
file:
def presser(self, btn):
if self.turn == Players.PLAYER_X:
if self.bot_starting:
# Here I am stuck :(
bot_move = Bot.play_move()
btn.text = Players.PLAYER_X
btn.disabled = True
self.root.ids.score.text = Messages.TURN.format(player=Players.PLAYER_O)
self.turn = Players.PLAYER_O
else:
btn.text = Players.PLAYER_O
btn.disabled = True
self.root.ids.score.text = Messages.TURN.format(player=Players.PLAYER_X)
self.turn = Players.PLAYER_X
See the comment "Here I am stuck"
The .kv
file looks like this (with 9 different buttons)
Button:
id: btn7
text: ""
font_size: "45sp"
on_release: app.presser(btn7)
This is the bot.py
file:
from random import randint
class Bot:
@staticmethod
def play_move():
return randint(0, 2), randint(0, 2)
How can I use the output (such as (1,1,)
) to simulate the pressing of the middle button (for example)?
I thought it would be easy but then I realized I am using a .kv
file and had no idea how to continue thinking about it, because it's the one that has the callback function for every button press, so simulating it would be impossible no?
Minimal working reproducible example:
https://pastebin.com/Y4n3hQRa - this is the .py file of the main logic
https://pastebin.com/S53Xibhm - this is the .kv
file
https://pastebin.com/e2pn1qyx - constants file
I would appreciate your kind help! Thank you
CodePudding user response:
You could try automatically calling the Bot.play_move method upon completion of the users turn after it checks to make sure that the end of the game has been reached. Then in the play_move method you would iterate through the ids and find buttons that have not yet been selected and randomly choose one of those remaining squares.
For example: I only included the methods where I made changes.
bot.py
import random
class Bot:
@staticmethod
def play_move(widget):
vals = []
for key, value in widget.ids.items():
if "btn" in key and not value.text:
vals.append(value)
chosen = random.choice(vals)
chosen.text = Players.PLAYER_O
chosen.disabled = True
app file
class MainApp(MDApp):
def presser(self, btn):
if self.turn == self.bot_symbol:
Bot.play_move(self.root)
self.root.ids.score.text = Messages.TURN.format(player=self.bot_symbol)
self.turn = Players.PLAYER_X
self.win()
else:
btn.text = Players.PLAYER_X
btn.disabled = True
self.root.ids.score.text = Messages.TURN.format(player=Players.PLAYER_O)
self.turn = Players.PLAYER_O
if not self.win():
self.presser(None)
def no_winner(self):
if self.winner:
return False
if self.root.ids.btn1.disabled and \
self.root.ids.btn2.disabled and \
self.root.ids.btn3.disabled and \
self.root.ids.btn4.disabled and \
self.root.ids.btn5.disabled and \
self.root.ids.btn6.disabled and \
self.root.ids.btn7.disabled and \
self.root.ids.btn8.disabled and \
self.root.ids.btn9.disabled:
self.root.ids.score.text = Messages.TIE
return False
return True
def win(self):
# this method stays the same except the last line
return not self.no_winner()
def no_winner(self):
if self.winner:
return False
if all([value.disabled for (key, value) in self.root.ids.items() if 'btn' in key]):
self.root.ids.score.text = Messages.TIE
return False
return True