patternpythonMinor
Tic Tac Toe with Python and Kivy
Viewed 0 times
withtoetictackivypythonand
Problem
Accomplishment: Tic Tac Toe game with Python & Kivy
I have created a simple tic tac toe game with a random bot logic (really random after a user selects a mark). Next step would be to insert the minimax algorithm for example, but I would like to know if the path I've taken to construct my code is correct and could it be improved.
I have two files: main.py and ai.py
main.py
```
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.stacklayout import StackLayout
from kivy.config import Config
from ai import Ai
from random import randint
class TicTacToe(App):
title = 'Tic Tac Toe'
board = []
choices = ["X","O"]
# On application build handler
def build(self):
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '600')
self.layout = StackLayout()
for x in range(9): # range() explanation: http://pythoncentral.io/pythons-range-function-explained/
bt = Button(text='', font_size=200, width=200, height=200, size_hint=(None, None), id=str(x))
bt.bind(on_release=self.btn_pressed)
self.board.append(bt)
self.layout.add_widget(bt)
return self.layout
# On application start handler
def on_start(self):
self.init_players();
greeting = "Hello Player! You are playing with \"" + self.player + "\""
self.popup_message(greeting)
# On button pressed handler
def btn_pressed(self, button):
if len(button.text.strip()) < 1: # Continue only if the button has no mark on it...
button.text = self.player
self.bot.make_move(self.board)
self.check_winner()
# Initializes players
def init_players(self):
rand_choice = randint(0,1);
self.bot = Ai(self.choices[rand_choice]);
self.player = self.choices[0] if rand_choice == 1 else self.choices[1]
# Checks winner after ev
I have created a simple tic tac toe game with a random bot logic (really random after a user selects a mark). Next step would be to insert the minimax algorithm for example, but I would like to know if the path I've taken to construct my code is correct and could it be improved.
I have two files: main.py and ai.py
main.py
```
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.stacklayout import StackLayout
from kivy.config import Config
from ai import Ai
from random import randint
class TicTacToe(App):
title = 'Tic Tac Toe'
board = []
choices = ["X","O"]
# On application build handler
def build(self):
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '600')
self.layout = StackLayout()
for x in range(9): # range() explanation: http://pythoncentral.io/pythons-range-function-explained/
bt = Button(text='', font_size=200, width=200, height=200, size_hint=(None, None), id=str(x))
bt.bind(on_release=self.btn_pressed)
self.board.append(bt)
self.layout.add_widget(bt)
return self.layout
# On application start handler
def on_start(self):
self.init_players();
greeting = "Hello Player! You are playing with \"" + self.player + "\""
self.popup_message(greeting)
# On button pressed handler
def btn_pressed(self, button):
if len(button.text.strip()) < 1: # Continue only if the button has no mark on it...
button.text = self.player
self.bot.make_move(self.board)
self.check_winner()
# Initializes players
def init_players(self):
rand_choice = randint(0,1);
self.bot = Ai(self.choices[rand_choice]);
self.player = self.choices[0] if rand_choice == 1 else self.choices[1]
# Checks winner after ev
Solution
On comments
I think you could rename the function to
There are several other comments that doesn't add anything useful, like
and
and
The comments describing the
I would extract the above check into a function, like this:
thus eliminating the need for a comment.
You should strive to make the code self-documenting, and use comments when you fail to do that. I recommend reading rolfl's excellent answer to a codereview question about comments.
Constructor vs method injection
I would inject the game board into the Ai constructor
and set it as you do with the choice
This way you can do
instead of
which is easier to understand. As the board is used by all the functions in Ai, there's currently no good reason not to inject it in the constructor.
Reuse
Your
# On button pressed handler
def btn_pressed(self, button):I think you could rename the function to
on_btn_pressed and just remove the comment, as it doesn't add any information that isn't already apparent from the function name.There are several other comments that doesn't add anything useful, like
# Initializes players
def init_players(self):and
# Checks winner after every move...
def check_winner(self):and
self.make_random_move(board) # Do a random moveThe comments describing the
build and on_start functions could do with some more explanation, as it's not apparent to me how these are used (I'm assuming they are used by kivy.app.App?).if len(button.text.strip()) < 1: # If the button has no mark, stripping spaces...I would extract the above check into a function, like this:
if is_button_unmarked(button):
free_buttons.append(button)
...
def is_button_unmarked(self, button):
return len(button.text.strip()) < 1thus eliminating the need for a comment.
You should strive to make the code self-documenting, and use comments when you fail to do that. I recommend reading rolfl's excellent answer to a codereview question about comments.
Constructor vs method injection
I would inject the game board into the Ai constructor
self.bot = Ai(self.choices[rand_choice], board)and set it as you do with the choice
class Ai:
def __init__(self, choice, board):
self.choice = choice
self.board = boardThis way you can do
self.bot.make_move()instead of
self.bot.make_move(self.board)which is easier to understand. As the board is used by all the functions in Ai, there's currently no good reason not to inject it in the constructor.
Reuse
Your
popup_message function lets you pass any message, but the title is hardcoded. You should send the title in as an argument as well, to make the popup_message function useable by more than welcome messages (maybe you'd want to use it after deciding a winner in check_winner?).Code Snippets
# On button pressed handler
def btn_pressed(self, button):# Initializes players
def init_players(self):# Checks winner after every move...
def check_winner(self):self.make_random_move(board) # Do a random moveif len(button.text.strip()) < 1: # If the button has no mark, stripping spaces...Context
StackExchange Code Review Q#142337, answer score: 3
Revisions (0)
No revisions yet.