[Tutor] Dots-And-Boxes
Gregor Lingl
glingl@aon.at
Mon, 3 Jun 2002 02:50:06 +0200
This is a multi-part message in MIME format.
------=_NextPart_000_0016_01C20AA9.5E03E6E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Hi!
Until now I didn't know "The Dots and Boxes" Game, but Dannies
program made me interested in it.
So I wrote the beginning of a gui version. I tried hard to use
Dannies code as is - so the gui code is (nearly) completely
separated from the game machinery. I consider this a proof for
the exceptionally good design of Dannies program (imho).
O.k., maybe my extension of the program still needs some more
buttons or textfields or so, but perhaps it could be a starting
point.
Critical remarks are strongly welcome.
I'm especially interested in a comparison with an implementation
using Pygame - this is what Danny has in mind to do?
Making the computer play the game is still another point ...
Gregor
P.S.: There is one point, which is not completely clear to me:
Danny's algorithm always assigns only one square to the player
who did the last move, even if his draw completes two squares,
So in the end the sum of the points may be less than (w-1)*(h-1).
Is this according to the rules of the game?
-------------- here follows the code, which is also attached:
"""Graphic user interface to Danny Yoo's 'dots and boxes' -program.
I tried to avoid changes to this program as far as possible.
So there remain the following changes:
Line 222: Return statement in play() now returns the
square_corner instead of 1 (this was the only
necessary one (in m opinion)
Line 255/256: head of for-loop --- this was the easiest way
to accomodate text-output to graphical one.
( Bad design decision for the graphics display,
may be reverted if there is time ...)
Lines 305-318, 332, 333: A somewhat safer input method for
more convenient testing
Lines 354ff: Incorporate graphics mode into __main__()
"""
from Tkinter import *
from Canvas import Rectangle
def cartesian( v1, v2 ):
""" Helper function
returns cartesian product of the two
'sets' v1, v2"""
return tuple([(x,y) for x in v1 for y in v2])
def right(x):
"""Helper function: argument x must be a dot.
Returns dot right of x."""
return (x[0]+1,x[1])
def upper(x):
"""Helper function: argument x must be a dot.
Returns dot above (actually below) x."""
return (x[0], x[1]+1)
class GameGUI:
def __init__(self, board):
"""Initializes graphic display of a rectangular gameboard."""
# Properties of gameboard
dw = self.dotwidth = 6
sw = self.squarewidth = 60
sk = self.skip = 4
fw = self.fieldwidth = dw + sw + 2*sk
ins = self.inset = sw/2
self.barcolors = ['red','blue']
self.squarecolors = ['orange', 'lightblue']
# Construct Canvas
self.board = board
width, height = board.width, board.height
# compute size of canvas:
w = width * fw
h = height * fw
self.root = Tk()
cv = self.cv = Canvas(self.root, width=w, height=h, bg='white')
cv.bind('<Button-1>', self._callback)
cv.pack()
# Put geometrical objects - dots, bars and squares - on canvas
self.bars = {}
self.squares = {}
for dot in cartesian(range(width), range(height)):
# dots. Never used again
Rectangle( cv, ins+dot[0]*fw, ins+dot[1]*fw,
ins+dot[0]*fw + dw, ins+dot[1]*fw + dw,
fill='black', outline='' )
# horizontal bars
if dot[0] < width - 1:
x0 = ins+dot[0]*fw+dw+sk
y0 = ins+dot[1]*fw
self.bars[(dot,right(dot))] =\
Rectangle(cv,x0,y0,x0+sw,y0+dw,fill='lightgray',outline='')
# vertical bars
if dot[1] < height - 1:
x0 = ins+dot[0]*fw
y0 = ins+dot[1]*fw + dw + sk
self.bars[(dot,upper(dot))] =\
Rectangle(cv,x0,y0,x0+dw,y0+sw,fill='lightgray',outline='')
# squares
if (dot[0] < width - 1) and (dot[1] < height - 1):
x0 =ins+dot[0]*fw + dw + sk
y0 =ins+dot[1]*fw + dw + sk
self.squares[dot] =\
Rectangle(cv,x0,y0,x0+sw,y0+sw,fill='lightyellow',outline='')
cv.update()
self.root.mainloop()
def _coord(self,x):
"""returns pixel-coordinate corresponding to
a dot-coordinate x"""
return self.inset + self.dotwidth/2 + self.fieldwidth*x
def _find_bar(self,event):
"""returns bar next to mouse-position when clicked,
if applicable, otherwise None"""
ex, ey = event.x, event.y
for bar in self.bars:
((x1,y1),(x2,y2))=bar
mx, my = (self._coord(x1)+self._coord(x2))/2,
(self._coord(y1)+self._coord(y2))/2
if abs(ex-mx)+abs(ey-my) < self.squarewidth/2:
return bar
def _callback(self, event):
"""Action following a mouse-click"""
hit = self._find_bar(event)
board = self.board
print "Hit:", hit
if not hit or board.isGameOver() or board.board.has_key(hit):
return
# Do a move
player = board.getPlayer()
print "Turn %d (Player %s)" % (board.turn, player)
self.bars[hit]['fill']=self.barcolors[player]
target = board.play(hit)
print "Target:", target
if target:
print "Square completed.", board.squares[target]
self.squares[target]['fill'] = self.squarecolors[player]
board.scores[player] += 1
board.turn = board.turn + 1
print "\n"
if board.isGameOver():
print "Game over!"
print "Final board position:"
print board
print
print "Final score:\n\tPlayer 0: %s\n\tPlayer 1: %s" % \
tuple(board.scores)
def _gtest(width, height):
"""A small driver to make sure that the board works. It's not
safe to use this test function in production, because it uses
input()."""
print "Running _gtest... "
board = GameBoard(width, height)
board.turn = 1
board.scores = [0, 0]
gui = GameGUI(board)
##### Danny Yoo's board.py #########################################
import types
class GameBoard:
def __init__(self, width=5, height=5):
"""Initializes a rectangular gameboard."""
self.width, self.height = width, height
assert 2 <= self.width and 2 <= self.height,\
"Game can't be played on this board's dimension."
self.board = {}
self.squares = {}
self.player = 0
def isGameOver(self):
"""Returns true if no more moves can be made.
The maximum number of moves is equal to the number of possible
lines between adjacent dots. I'm calculating this to be
$2*w*h - h - w$; I think that's right. *grin*
"""
w, h = self.width, self.height
return len(self.board.keys()) == 2*w*h - h - w
def _isSquareMove(self, move):
"""Returns a true value if a particular move will create a
square. In particular, returns the lower left corner of the
created square."""
b = self.board
mmove = self._makeMove ## just to make typing easier
((x1, y1), (x2, y2)) = move
if self._isHorizontal(move):
for j in [-1, 1]:
if (b.has_key(mmove((x1, y1), (x1, y1-j)))
and b.has_key(mmove((x1, y1-j), (x1+1, y1-j)))
and b.has_key(mmove((x1+1, y1-j), (x2, y2)))):
return min([(x1, y1), (x1, y1-j),
(x1+1, y1-j), (x2, y2)])
else:
for j in [-1, 1]:
if (b.has_key(mmove((x1, y1), (x1-j, y1)))
and b.has_key(mmove((x1-j, y1), (x1-j, y1+1)))
and b.has_key(mmove((x1-j, y1+1), (x2, y2)))):
return min([(x1, y1), (x1-j, y1),
(x1-j, y1+1), (x2, y2)])
return None
def _isHorizontal(self, move):
"Return true if the move is in horizontal orientation."
return abs(move[0][0] - move[1][0]) == 1
def _isVertical(self, move):
"Return true if the move is in vertical orientation."
return not self.isHorizontal(self, move)
def play(self, move):
"""Place a particular move on the board. If any wackiness
occurs, raise an AssertionError."""
assert (self._isGoodCoord(move[0]) and
self._isGoodCoord(move[1])),\
"Bad coordinates, out of bounds of the board."
move = self._makeMove(move[0], move[1])
assert(not self.board.has_key(move)),\
"Bad move, line already occupied."
self.board[move] = self.player
## Check if a square is completed.
square_corner = self._isSquareMove(move)
if square_corner:
self.squares[square_corner] = self.player
# return 1
return square_corner # CHANGED to get information which
# square to colour
else:
self._switchPlayer()
return 0
def _switchPlayer(self):
self.player = (self.player + 1) % 2
def getPlayer(self): return self.player
def getSquares(self):
"""Returns a dictionary of squares captured. Returns
a dict of lower left corner keys marked with the
player who captured them."""
return self.squares
def __str__(self):
"""Return a nice string representation of the board."""
buffer = []
## do the top line
for i in range(self.width-1):
if self.board.has_key(((i, self.height-1), (i+1,
self.height-1))):
buffer.append("+--")
else: buffer.append("+ ")
buffer.append("+\n")
## and now do alternating vertical/horizontal passes
# for j in range(self.height-2, -1, -1): # CHANGED for
corresponence
for j in range(self.height-1): # with graphical display
## vertical:
for i in range(self.width):
if self.board.has_key(((i, j), (i, j+1))):
buffer.append("|")
else:
buffer.append(" ")
if self.squares.has_key((i, j)):
buffer.append("%s " % self.squares[i,j])
else:
buffer.append(" ")
buffer.append("\n")
## horizontal
for i in range(self.width-1):
if self.board.has_key(((i, j), (i+1, j))):
buffer.append("+--")
else: buffer.append("+ ")
buffer.append("+\n")
return ''.join(buffer)
def _makeMove(self, coord1, coord2):
"""Return a new "move", and ensure it's in canonical form.
(That is, force it so that it's an ordered tuple of tuples.)
"""
## TODO: do the Flyweight thing here to reduce object creation
xdelta, ydelta = coord2[0] - coord1[0], coord2[1] - coord1[1]
assert ((abs(xdelta) == 1 and abs(ydelta) == 0) or
(abs(xdelta) == 0 and abs(ydelta) == 1)),\
"Bad coordinates, not adjacent points."
if coord1 < coord2:
return (coord1, coord2)
else:
return (tuple(coord2), tuple(coord1))
def _isGoodCoord(self, coord):
"""Returns true if the given coordinate is good.
A coordinate is "good" if it's within the boundaries of the
game board, and if the coordinates are integers."""
return (0 <= coord[0] < self.width
and 0 <= coord[1] < self.height
and isinstance(coord[0], types.IntType)
and isinstance(coord[1], types.IntType))
def getMove(self): # <=== NEW NEW NEW
"""Found this little bit of error-checking useful
for testing, because I tend to mistype everything
rather often.
Moreover now it's more safe"""
done = 0
while not done:
try:
x1, y1, sep, x2, y2 = raw_input("Move?").split()
move = ((int(x1),int(y1)),(int(x2),int(y2)))
done = 1
except:
pass
return move
def _test(width, height):
"""A small driver to make sure that the board works. It's not
safe to use this test function in production, because it uses
input()."""
board = GameBoard(width, height)
turn = 1
scores = [0, 0]
while not board.isGameOver():
player = board.getPlayer()
print "Turn %d (Player %s)" % (turn, player)
print board
# move = input("Move? ")
move = board.getMove() # <== CHANGED !!!
if board.play(move):
print "Square completed."
scores[player] += 1
turn = turn + 1
print "\n"
print "Game over!"
print "Final board position:"
print board
print
print "Final score:\n\tPlayer 0: %s\n\tPlayer 1: %s" % \
(scores[0], scores[1])
if __name__ == "__main__":
"""If we're provided arguments,
look if first one equals 't', in which case
textmode only is invoked.
try using rest of the arguments as the
width/height of the game board."""
import sys
if len(sys.argv[1:]) > 0 and sys.argv[1] == 't':
# textmode
if len(sys.argv[1:]) == 3:
_test(int(sys.argv[2]), int(sys.argv[3]))
elif len(sys.argv[1:]) == 2:
_test(int(sys.argv[2]), int(sys.argv[2]))
else:
_test(5, 5)
else:
# grachics mode
if len(sys.argv[1:]) == 2:
_gtest(int(sys.argv[1]), int(sys.argv[2]))
elif len(sys.argv[1:]) == 1:
_gtest(int(sys.argv[1]), int(sys.argv[1]))
else:
_gtest(5, 5)
------=_NextPart_000_0016_01C20AA9.5E03E6E0
Content-Type: text/plain;
name="gboard.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="gboard.py"
"""Graphic user interface to Danny Yoo's 'dots and boxes' -program.
I tried to avoid changes to this program as far as possible.
So there remain the following changes:
Line 222: Return statement in play() now returns the
square_corner instead of 1 (this was the only
necessary one (in m opinion)
Line 255/256: head of for-loop --- this was the easiest way
to accomodate text-output to graphical one.
( Bad design decision for the graphics display,
may be reverted if there is time ...)
Lines 305-318, 332, 333: A somewhat safer input method for
more convenient testing
Lines 354ff: Incorporate graphics mode into __main__()
"""
from Tkinter import *
from Canvas import Rectangle
def cartesian( v1, v2 ):
""" Helper function
returns cartesian product of the two
'sets' v1, v2"""
return tuple([(x,y) for x in v1 for y in v2])
def right(x):
"""Helper function: argument x must be a dot.
Returns dot right of x."""
return (x[0]+1,x[1])
def upper(x):
"""Helper function: argument x must be a dot.
Returns dot above (actually below) x."""
return (x[0], x[1]+1)
class GameGUI:
def __init__(self, board):
"""Initializes graphic display of a rectangular gameboard."""
# Properties of gameboard
dw =3D self.dotwidth =3D 6
sw =3D self.squarewidth =3D 60
sk =3D self.skip =3D 4
fw =3D self.fieldwidth =3D dw + sw + 2*sk
ins =3D self.inset =3D sw/2
self.barcolors =3D ['red','blue']
self.squarecolors =3D ['orange', 'lightblue']
# Construct Canvas =20
self.board =3D board
width, height =3D board.width, board.height
# compute size of canvas:
w =3D width * fw=20
h =3D height * fw=20
self.root =3D Tk()
cv =3D self.cv =3D Canvas(self.root, width=3Dw, height=3Dh, =
bg=3D'white')
cv.bind('<Button-1>', self._callback)
cv.pack()
# Put geometrical objects - dots, bars and squares - on canvas
self.bars =3D {}
self.squares =3D {}
for dot in cartesian(range(width), range(height)):
# dots. Never used again
Rectangle( cv, ins+dot[0]*fw, ins+dot[1]*fw,
ins+dot[0]*fw + dw, ins+dot[1]*fw + dw,
fill=3D'black', outline=3D'' )
# horizontal bars
if dot[0] < width - 1:
x0 =3D ins+dot[0]*fw+dw+sk
y0 =3D ins+dot[1]*fw
self.bars[(dot,right(dot))] =3D\
=
Rectangle(cv,x0,y0,x0+sw,y0+dw,fill=3D'lightgray',outline=3D'')
# vertical bars
if dot[1] < height - 1:
x0 =3D ins+dot[0]*fw
y0 =3D ins+dot[1]*fw + dw + sk
self.bars[(dot,upper(dot))] =3D\
=
Rectangle(cv,x0,y0,x0+dw,y0+sw,fill=3D'lightgray',outline=3D'')
# squares
if (dot[0] < width - 1) and (dot[1] < height - 1):
x0 =3Dins+dot[0]*fw + dw + sk
y0 =3Dins+dot[1]*fw + dw + sk=20
self.squares[dot] =3D\
=
Rectangle(cv,x0,y0,x0+sw,y0+sw,fill=3D'lightyellow',outline=3D'')
cv.update()
self.root.mainloop() =20
def _coord(self,x):
"""returns pixel-coordinate corresponding to
a dot-coordinate x"""
return self.inset + self.dotwidth/2 + self.fieldwidth*x
def _find_bar(self,event):
"""returns bar next to mouse-position when clicked,
if applicable, otherwise None"""
ex, ey =3D event.x, event.y
for bar in self.bars:
((x1,y1),(x2,y2))=3Dbar
mx, my =3D (self._coord(x1)+self._coord(x2))/2, =
(self._coord(y1)+self._coord(y2))/2
if abs(ex-mx)+abs(ey-my) < self.squarewidth/2:
return bar
=20
def _callback(self, event):
"""Action following a mouse-click"""
hit =3D self._find_bar(event)
board =3D self.board
print "Hit:", hit
if not hit or board.isGameOver() or board.board.has_key(hit):
return
# Do a move
player =3D board.getPlayer()
print "Turn %d (Player %s)" % (board.turn, player)
self.bars[hit]['fill']=3Dself.barcolors[player]
target =3D board.play(hit)
print "Target:", target
if target:
print "Square completed.", board.squares[target]
self.squares[target]['fill'] =3D self.squarecolors[player]
board.scores[player] +=3D 1
board.turn =3D board.turn + 1
print "\n"
if board.isGameOver():
print "Game over!"
print "Final board position:"
print board
print
print "Final score:\n\tPlayer 0: %s\n\tPlayer 1: %s" % \
tuple(board.scores)
def _gtest(width, height):=0A=
"""A small driver to make sure that the board works. It's not=0A=
safe to use this test function in production, because it uses=0A=
input()."""
print "Running _gtest... "=0A=
board =3D GameBoard(width, height)=0A=
board.turn =3D 1=0A=
board.scores =3D [0, 0]
gui =3D GameGUI(board)
##### Danny Yoo's board.py #########################################
import types=0A=
=0A=
class GameBoard:=0A=
def __init__(self, width=3D5, height=3D5):=0A=
"""Initializes a rectangular gameboard."""=0A=
self.width, self.height =3D width, height=0A=
assert 2 <=3D self.width and 2 <=3D self.height,\=0A=
"Game can't be played on this board's dimension."=0A=
self.board =3D {}=0A=
self.squares =3D {}=0A=
self.player =3D 0=0A=
=0A=
=0A=
def isGameOver(self):=0A=
"""Returns true if no more moves can be made.=0A=
=0A=
The maximum number of moves is equal to the number of possible=0A=
lines between adjacent dots. I'm calculating this to be=0A=
$2*w*h - h - w$; I think that's right. *grin*=0A=
"""=0A=
w, h =3D self.width, self.height=0A=
return len(self.board.keys()) =3D=3D 2*w*h - h - w=0A=
=0A=
=0A=
=0A=
def _isSquareMove(self, move):=0A=
"""Returns a true value if a particular move will create a=0A=
square. In particular, returns the lower left corner of the=0A=
created square."""=0A=
b =3D self.board=0A=
mmove =3D self._makeMove ## just to make typing easier=0A=
((x1, y1), (x2, y2)) =3D move=0A=
if self._isHorizontal(move):=0A=
for j in [-1, 1]:=0A=
if (b.has_key(mmove((x1, y1), (x1, y1-j)))=0A=
and b.has_key(mmove((x1, y1-j), (x1+1, y1-j)))=0A=
and b.has_key(mmove((x1+1, y1-j), (x2, y2)))):=0A=
return min([(x1, y1), (x1, y1-j),=0A=
(x1+1, y1-j), (x2, y2)])=0A=
else:=0A=
for j in [-1, 1]:=0A=
if (b.has_key(mmove((x1, y1), (x1-j, y1)))=0A=
and b.has_key(mmove((x1-j, y1), (x1-j, y1+1)))=0A=
and b.has_key(mmove((x1-j, y1+1), (x2, y2)))):=0A=
return min([(x1, y1), (x1-j, y1),=0A=
(x1-j, y1+1), (x2, y2)])=0A=
return None=0A=
=0A=
=0A=
=0A=
def _isHorizontal(self, move):=0A=
"Return true if the move is in horizontal orientation."=0A=
return abs(move[0][0] - move[1][0]) =3D=3D 1=0A=
=0A=
=0A=
def _isVertical(self, move):=0A=
"Return true if the move is in vertical orientation."=0A=
return not self.isHorizontal(self, move)=0A=
=0A=
=0A=
def play(self, move):=0A=
"""Place a particular move on the board. If any wackiness=0A=
occurs, raise an AssertionError."""=0A=
assert (self._isGoodCoord(move[0]) and=0A=
self._isGoodCoord(move[1])),\=0A=
"Bad coordinates, out of bounds of the board."=0A=
move =3D self._makeMove(move[0], move[1])=0A=
assert(not self.board.has_key(move)),\=0A=
"Bad move, line already occupied."=0A=
self.board[move] =3D self.player=0A=
## Check if a square is completed.=0A=
square_corner =3D self._isSquareMove(move)=0A=
if square_corner:=0A=
self.squares[square_corner] =3D self.player=0A=
# return 1
return square_corner # CHANGED to get information which
# square to colour=0A=
else:=0A=
self._switchPlayer()=0A=
return 0=0A=
=0A=
=0A=
def _switchPlayer(self):=0A=
self.player =3D (self.player + 1) % 2=0A=
=0A=
=0A=
def getPlayer(self): return self.player=0A=
=0A=
=0A=
def getSquares(self):=0A=
"""Returns a dictionary of squares captured. Returns=0A=
a dict of lower left corner keys marked with the=0A=
player who captured them."""=0A=
return self.squares=0A=
=0A=
=0A=
def __str__(self):=0A=
"""Return a nice string representation of the board."""=0A=
buffer =3D []=0A=
=0A=
## do the top line=0A=
for i in range(self.width-1):=0A=
if self.board.has_key(((i, self.height-1), (i+1, =
self.height-1))):=0A=
buffer.append("+--")=0A=
else: buffer.append("+ ")=0A=
buffer.append("+\n")=0A=
=0A=
## and now do alternating vertical/horizontal passes=0A=
# for j in range(self.height-2, -1, -1): # CHANGED for =
corresponence =0A=
for j in range(self.height-1): # with graphical display=0A=
## vertical:=0A=
for i in range(self.width):=0A=
if self.board.has_key(((i, j), (i, j+1))):=0A=
buffer.append("|")=0A=
else:=0A=
buffer.append(" ")=0A=
if self.squares.has_key((i, j)):=0A=
buffer.append("%s " % self.squares[i,j])=0A=
else:=0A=
buffer.append(" ")=0A=
buffer.append("\n")=0A=
=0A=
## horizontal=0A=
for i in range(self.width-1):=0A=
if self.board.has_key(((i, j), (i+1, j))):=0A=
buffer.append("+--")=0A=
else: buffer.append("+ ")=0A=
buffer.append("+\n")=0A=
=0A=
return ''.join(buffer)=0A=
=0A=
=0A=
=0A=
def _makeMove(self, coord1, coord2):=0A=
"""Return a new "move", and ensure it's in canonical form.=0A=
(That is, force it so that it's an ordered tuple of tuples.)=0A=
"""=0A=
## TODO: do the Flyweight thing here to reduce object creation=0A=
xdelta, ydelta =3D coord2[0] - coord1[0], coord2[1] - coord1[1]=0A=
assert ((abs(xdelta) =3D=3D 1 and abs(ydelta) =3D=3D 0) or=0A=
(abs(xdelta) =3D=3D 0 and abs(ydelta) =3D=3D 1)),\=0A=
"Bad coordinates, not adjacent points."=0A=
if coord1 < coord2:=0A=
return (coord1, coord2)=0A=
else:=0A=
return (tuple(coord2), tuple(coord1))=0A=
=0A=
=0A=
def _isGoodCoord(self, coord):=0A=
"""Returns true if the given coordinate is good.=0A=
=0A=
A coordinate is "good" if it's within the boundaries of the=0A=
game board, and if the coordinates are integers."""=0A=
return (0 <=3D coord[0] < self.width=0A=
and 0 <=3D coord[1] < self.height=0A=
and isinstance(coord[0], types.IntType)=0A=
and isinstance(coord[1], types.IntType))=0A=
def getMove(self): # <=3D=3D=3D NEW NEW NEW
"""Found this little bit of error-checking useful
for testing, because I tend to mistype everything
rather often.
Moreover now it's more safe"""
done =3D 0
while not done:
try:
x1, y1, sep, x2, y2 =3D raw_input("Move?").split()
move =3D ((int(x1),int(y1)),(int(x2),int(y2)))
done =3D 1
except:
pass
return move
=0A=
=0A=
def _test(width, height):=0A=
"""A small driver to make sure that the board works. It's not=0A=
safe to use this test function in production, because it uses=0A=
input()."""=0A=
board =3D GameBoard(width, height)=0A=
turn =3D 1=0A=
scores =3D [0, 0]=0A=
while not board.isGameOver():=0A=
player =3D board.getPlayer()=0A=
print "Turn %d (Player %s)" % (turn, player)=0A=
print board=0A=
# move =3D input("Move? ")
move =3D board.getMove() # <=3D=3D CHANGED !!!=0A=
if board.play(move):=0A=
print "Square completed."=0A=
scores[player] +=3D 1=0A=
turn =3D turn + 1=0A=
print "\n"=0A=
print "Game over!"=0A=
print "Final board position:"=0A=
print board=0A=
print=0A=
print "Final score:\n\tPlayer 0: %s\n\tPlayer 1: %s" % \=0A=
(scores[0], scores[1])=0A=
=0A=
if __name__ =3D=3D "__main__":
"""If we're provided arguments,
look if first one equals 't', in which case
textmode only is invoked.
try using rest of the arguments as the
width/height of the game board."""
import sys
if len(sys.argv[1:]) > 0 and sys.argv[1] =3D=3D 't':
# textmode
if len(sys.argv[1:]) =3D=3D 3:
_test(int(sys.argv[2]), int(sys.argv[3]))
elif len(sys.argv[1:]) =3D=3D 2:
_test(int(sys.argv[2]), int(sys.argv[2]))
else:
_test(5, 5)
else:
# grachics mode
if len(sys.argv[1:]) =3D=3D 2:
_gtest(int(sys.argv[1]), int(sys.argv[2]))
elif len(sys.argv[1:]) =3D=3D 1:
_gtest(int(sys.argv[1]), int(sys.argv[1]))
else:
_gtest(5, 5)
------=_NextPart_000_0016_01C20AA9.5E03E6E0--