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

Clean code and SOLID principles for a simple Python TicTacToe game

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
principlessimpletictactoeandgameforpythoncodesolidclean

Problem

I recently read the book Clean Code and I also did some research on the SOLID principles. I'm looking for general feedback on if I was able to transpose the examples (written in Java) to Python while still maintaining a "Pythonic" program.

I wrote a simple TicTacToe game in Python using Tkinter as the GUI and I tried to strike a balance between readable, clean code and avoiding code cluttering with useless functions (get(), set(), isEmpty() and other 1-liners)

Having a generic BoardGUI class specializing in TicTacToeBoardGUI is my attempt at the open/close principle.

The 3 modules (models, views, game) implement a minimalistic MVC design where GameApplication controls the flow and events.

The part I don't know how to handle is using polymorphism to have different levels of AI (right now the AI only checks a random empty box)

http://www.filedropper.com/tictactoe (~200 lines total)

main.py

#!/usr/bin/env

import Tkinter as tk
from game import GameApplication

def convert_images():
    """Returns a dictionary with the images converted into PhotoImages Tkinter can use"""
    img_dict = {}
    img_dict.update({'cross': tk.PhotoImage(file='images/cross.gif')})
    img_dict.update({'circle': tk.PhotoImage(file='images/circle.gif')})
    return img_dict

def main():
    root = tk.Tk()
    root.title('Tic-Tac-Toe v1.0')

    images = convert_images()
    GameApplication(root, images)

    root.mainloop()

if __name__ == '__main__':
    main()


models.py:

```
#!/usr/bin/env
from collections import namedtuple

class TicTacToeBoard():
"""
Keeps track of the status of the boxes of a standard 3x3 TicTacToe game

I don't particularly like the victory, draw and _find_lines functions, any better solutions?
"""

ROWS = 3
COLUMNS = 3
EMPTY = 0
O = 1
X = 2

def __init__(self):
self.boxes = {(column, row): namedtuple('box', 'value') \
for row in range(TicTacToeBoard.ROWS) \

Solution

Looks to me that you want to have a box with various buttons that allows you to select, say, easy, medium, or hard. Return that and then say

if AIChoose == "Easy":
    getEasyAIMove()
elif AIChoose == "Medium":
    GetMedAIMove()
elif AIChoose == "Hard":
    GetHardAIMove()


Now, that's obviously the easy part, but a Tic-Tac-Toe AI is actually not at all hard:

def GetHardAIMove():
    # first check if you're about to win. If you are, move there
    # now check if your opponent is about to win. If he is, block him
    # check if any of the corners are free. If they are, go to a random one of them
    # check if the middle is free. If it is, go to it
    #otherwise, go to a random open spot
def GetMedAIMove():
    # here's where we cheat. generate a random number between 1 and 3. if its <= 2, GetHardAIMove. otherwise, go easy.
def GetEasyAIMove():
    # If you're about to win, go there
    # If you're about to lose, go there
    # Otherwise, go to a random spot.


See? This is a quick and painless AI that can beat decent players who don't know how it's made about 50-60% of the time.

EDIT: Since Tic-Tac-Toe can always be won or tied by the player that moves first, you may wish to add this into the AI by doing something like this:

def GetHardAIMove():
    # if you move first:
        # after making sure you or your opponent isn't about to win:
        # move to a corner
        # then move to the corner opposite that
        # then move to a random corner
        # at this point, you have either won or tied
    # first check if you're about to win. If you are, move there
    # now check if your opponent is about to win. If he is, block him
    # check if any of the corners are free. If they are, go to a random one of them
    # check if the middle is free. If it is, go to it
    #otherwise, go to a random open spot

Code Snippets

if AIChoose == "Easy":
    getEasyAIMove()
elif AIChoose == "Medium":
    GetMedAIMove()
elif AIChoose == "Hard":
    GetHardAIMove()
def GetHardAIMove():
    # first check if you're about to win. If you are, move there
    # now check if your opponent is about to win. If he is, block him
    # check if any of the corners are free. If they are, go to a random one of them
    # check if the middle is free. If it is, go to it
    #otherwise, go to a random open spot
def GetMedAIMove():
    # here's where we cheat. generate a random number between 1 and 3. if its <= 2, GetHardAIMove. otherwise, go easy.
def GetEasyAIMove():
    # If you're about to win, go there
    # If you're about to lose, go there
    # Otherwise, go to a random spot.
def GetHardAIMove():
    # if you move first:
        # after making sure you or your opponent isn't about to win:
        # move to a corner
        # then move to the corner opposite that
        # then move to a random corner
        # at this point, you have either won or tied
    # first check if you're about to win. If you are, move there
    # now check if your opponent is about to win. If he is, block him
    # check if any of the corners are free. If they are, go to a random one of them
    # check if the middle is free. If it is, go to it
    #otherwise, go to a random open spot

Context

StackExchange Code Review Q#38789, answer score: 4

Revisions (0)

No revisions yet.