24
Mar
10

Tic-Tac-Toe: Python

Lock up the women and children — a rubyist is writing python. I’m curious what the herpetophiles in the audience will think of the code, because I know the Python community spends a reasonable amount of brainpower debating “pythonicness.”

Broadly speaking, there were more places I found commonality between Ruby and Python than differences. This may have been more harmful than helpful in the short run, because it meant I kept reaching for familiar tools a finding they had been moved, or replaced. For example, you can see in several places that I do this: if type(cell) == type(1), meaning if cell is a number. Is this a common Python idiom? It seems unlikely.

Differences I liked:

  • Syntactically-significant indentation.  Makes short functions look great and longer ones look weird, thus encouraging short functions.  :)
  • List comprehensions.  Especially with multiple lists.
  • Real named arguments.
  • Excellent documentation.
  • TIOOWTDI

Differences I didn’t like:

  • Distinction between builtins and methods.  I kept looking for any and map in all the wrong places.
  • No implicit expression value / required explicit return.
  • More difficult reflection/metaprogramming.
  • TIOOWTDI

With just a little practice, I’m sure I could be quite happy and productive in it.

What do you think, Python people?  What should I have done differently?

#!/usr/bin/env python2.6

from __future__ import print_function
import sys

X = 'X'
O = 'O'

starting_player = X
starting_board = [ [1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9] ]

# Input/Output

def format(cell):
    if type(cell) == type(1):
        return "(" + repr(cell) + ")"
    else:
        return " " + cell + " "

def print_board(board):
    print("\n" + "\n---+---+---\n".join(["|".join([format(c) for c in row]) for row in board]) + "\n")

def print_prompt(player):
    print("Select a square, " + player + ": ", end='')

def print_winner(player):
    print(player + " Wins!")

def print_draw():
    print("It's a Draw!")

def read_answer():
    str = sys.stdin.readline()
    return int(str)

# Winner Detection

def rows(board):
    return board[:] # clone

def cols(board):
    return [[row[i] for row in board] for i in [0,1,2]]

def diagonals(board):
    return [[row[i] for row, i in zip(board,[0,1,2])],
            [row[i] for row, i in zip(board,[2,1,0])]]

def lines(board):
    return rows(board) + cols(board) + diagonals(board)

def complete_line(line):
    return line[0] == line[1] and line[1] == line[2]

def winner(board):
    return any([complete_line(line) for line in lines(board)])

def full(board):
    return not any([type(row[i]) == type(1) for row in board for i in [0,1,2]])

# Game Play

def other_player(player):
    if player == X:
        return O
    else:
        return X

def legal_move(space, board):
    return any([row[i] == space for row in board for i in [0, 1, 2]])

def make_move(player, space, board):
    new_board = [row[:] for row in board]
    new_board[(space-1)/3][(space-1)%3] = player
    return new_board

# Main loop

def loop(player, board):
    print_board(board)
    if winner(board):
        print_winner(other_player(player))
        return
    elif full(board):
        print_draw()
        return
    else:
        print_prompt(player)
        space = read_answer()
        if legal_move(space, board):
            loop(other_player(player), make_move(player, space, board))
        else:
            loop(player, board)

loop(starting_player, starting_board)
Advertisement

1 Response to “Tic-Tac-Toe: Python”


  1. April 12, 2010 at 11:51 pm

    You could use isinstance(cell, str) to determine whether or not cell is a string.

    However, I prefer replacing the contents of format() with this:

    return ” {0} “.format(cell) if str(cell) in “XO” else “({0})”.format(cell)

    By the way, this is a great blog series!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.