[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--