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
anyandmapin 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)
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!