HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Tic Tac Toe with Python and Kivy

Submitted by: @import:stackexchange-codereview··
0
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

Solution

On comments

# 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 move


The 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()) < 1


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

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 = board


This 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 move
if 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.