25
HW 6: Problems 2&3 Simulating Connect 4

HW 6: Problems 2&3

Embed Size (px)

DESCRIPTION

HW 6: Problems 2&3. Simulating Connect 4. Problem 2: Connect 4 Board. Connect Four is a variation of tic-tac-toe played on a 7x6 rectangular board: The game is played by two players, alternating turns, with each trying to place four checkers in a row vertically, horizontally, or diagonally. - PowerPoint PPT Presentation

Citation preview

Page 1: HW 6: Problems 2&3

HW 6: Problems 2&3

Simulating Connect 4

Page 2: HW 6: Problems 2&3

Problem 2: Connect 4 Board

•Connect Four is a variation of tic-tac-toe played on a 7x6 rectangular board:

•The game is played by two players, alternating turns, with each trying to place four checkers in a row vertically, horizontally, or diagonally.

•One constraint in the game is that because the board stands vertically, the checkers cannot be placed into an arbitrary position. A checker may only be placed at the top of one of the currently existing columns (or it may start a new column).

Page 3: HW 6: Problems 2&3

Problem 2: Connect 4 Board

So how do we represent a connect 4 board in python???

Well, we would need a 2d data structure (i.e. list o’ lists) to symbolize the board and width and height variables (assuming these values could be something other than 6 x 7)

So our board class should have…..•A variable data storing the two-dimensional array (list of lists), which is the game board

•A variable height storing the number of rows on the game board

•A variable width storing the number of columns on the game board

Page 4: HW 6: Problems 2&3

And we’ll need methods to perform actions…__init__, the constructor

# do not need to return inside a constructor!This is a constructor for Board objects that takes two arguments. This constructor takes in a number of columns and rows. The constructor will set the values of the data members of the object.

Problem 2: Connect 4 Board

def __init__( self, width, height ): self.width = width self.height = height self.data = [] # this will be the board for row in range( self.height ): boardRow = [] for col in range( self.width ): boardRow += [' '] # add a space to this row self.data += [boardRow] # add this row to the board

Page 5: HW 6: Problems 2&3

And we’ll need methods to perform actions…__repr__, for printing or any string representation

Problem 2: Connect 4 Board

def __repr__(self): #print out rows & cols s = '' # the string to return for row in range( self.height ): s += '|' # add the spacer character for col in range( self.width ): s += self.data[row][col] + '|' s += '\n'

#print out separator (your code here)

#print out numbers of each column #using mod if greater than 9, for spacing issues (your code here)

return s # the board is complete, return it6 X 7 grid

| | | | | | | || | | | | | | || | | | | | | || | | | | | | || | | | | | | || | | | | | | |--------------- 0 1 2 3 4 5 6

Page 6: HW 6: Problems 2&3

And we’ll need methods to perform actions…•This method takes two inputs: the first input col represents the index of the column to which the checker will be added; the second input ox either an ‘X’ or ‘O’•In addMove you do not have to check that col is a legal column number or that there is space in column col. That checking is important, however. The next method, which is called allowsMove, will do just that.

Problem 2: Connect 4 Board

def addMove(self, col, ox ): #find the first row in the col without #a checker in it and #then add one there…

#do this by checking values in self.data…

>>> b = Board(7,6)>>> b.addMove(0, 'X')>>> b.addMove(0, 'O')>>> b.addMove(0, 'X')>>> b.addMove(3, 'O') 

| | | | | | | || | | | | | | || | | | | | | || | | | | | | || |X| | | | | ||X|O|O| | | | |--------------- 0 1 2 3 4 5 6

Page 7: HW 6: Problems 2&3

And we’ll need methods to perform actions…

clear (self), should clear the board that calls it. Not much to say about clear( self ). It's useful, though!

delMove(self, c) removes a checker from the board:

This method should do the opposite of addMove. It should remove the top checker from the column c. If the column is empty, then delMove should do nothing. This function may not seem useful now, but it will become very useful when you try to implement your own Connect Four player.

Problem 2: Connect 4 Board

Page 8: HW 6: Problems 2&3

And we’ll need methods to perform actions…

setBoard, sets the board with the configuration you give it, useful for debugging…

Problem 2: Connect 4 Board

def setBoard( self, moveString ):

nextCh = 'X' for colString in moveString: col = int(colString) if 0 <= col <= self.width: self.addMove(col, nextCh) if nextCh == 'X': nextCh = 'O' else: nextCh = 'X'

| | | | | | | || | | | | | | || | | | | | | || | | | | | | || | | |X| |O| ||O| |X|O| |X| |--------------- 0 1 2 3 4 5 6

b.setBoard('023553')

Page 9: HW 6: Problems 2&3

And we’ll need methods to perform actions…

allowsMove(self, c), for checking if a column is a legal move:

This method should return True if the calling object (of type Board) does allow a move into column c. It returns False if column c is not a legal column number for the calling object. It also returns False if column c is full.

isFull(self), checks if the board is full :

This method should return True if the calling object (of type Board) is completely full of checkers. It should return False otherwise. Notice that you can leverage allowsMove to make this method very concise! Unless you're supernaturally patient, you'll want to test this on small boards:

Problem 2: Connect 4 Board

Page 10: HW 6: Problems 2&3

And we’ll need methods to perform actions…

winsFor (self, ox): checks if someone has won the gameIt should return True if there are four checkers of type ox in a row on the board. It should return False otherwise.

One way to approach this is to consider each possible anchor checker that might start a four-in-a-row run. for example, all of the "anchors" that might start a horizontal run (going from left to right) must be in the columns at least four places from the end of the board.

Problem 2: Connect 4 Board

# check for horizontal winsfor row in range(0,self.height): for col in range(0,self.width-3): if self.data[row][col] == ox and \ self.data[row][col+1] == ox and \ self.data[row][col+2] == ox and \ self.data[row][col+3] == ox:

return True

Important – Must check1) Horizontally (shown here)2) Vertically3) Diagonally NE – SW4) Diagonally NW - SE

Page 11: HW 6: Problems 2&3

And we’ll need methods to perform actions…

hostGame(self): This method brings everything together into the familiar game. It should alternate turns between 'X' and 'O‘. It should ask the user to select a column number for each move. Here are a few important points to keep in mind:

1) This method should print the board before prompting for each move. 2) After each input, you should check if the column chosen is a valid one.If invalid prompt for another move instead. 3) This method should place the checker into its (valid) column. Then it should check if that player has won the game or if the board is now full. 4) If the game is over for either reason, the game should stop, the board should be printed out one last time, and the program should report who won (or that it was a tie.)

Problem 2: Connect 4 Board

Page 12: HW 6: Problems 2&3

Problem 2: Connect 4 BoardhostGame example…

Page 13: HW 6: Problems 2&3

Extra Credit Problem 3 Connect 4 Board

The Player class -- a preview

Well now that we have the board, lets have an automated player!

This player class will “examine” the board and determinethe appropriate move to make

The player will be programmed to look at the next move, or look up to several moves ahead

Page 14: HW 6: Problems 2&3

Your Player class should have at least these three data members:

ox: A one-character string representing the checker, either 'X' or 'O', being used by the connect-four Player.

tbt: A string, either 'LEFT', 'RIGHT', or 'RANDOM', representing the tiebreaking type of the player. This is the name for one of the three strategiesto be described later.

ply: A nonnegative integer representing how many moves into the future the player will look in order to evaluate possible moves.

EC Problem 3: Connect 4 Board

Page 15: HW 6: Problems 2&3

__init__(self, ox, tbt, ply):

This is a constructor for Player objects that takes three arguments.

__repr__(self):

This method returns a string representing the Player object that calls it. This should simply print the three important characteristics of the object: its checker string, its tiebreaking type, and its ply.

EC Problem 3: Connect 4 Board

Page 16: HW 6: Problems 2&3

EC Problem 3: Connect 4 Board

>>> p = Player('X', 'LEFT', 2)>>> pPlayer for Xwith tiebreak: LEFTand ply == 2 >>> p = Player('O',

'RANDOM', 0)>>> pPlayer for Owith tiebreak: RANDOMand ply == 0

class Player:""" an AI player for Connect Four """ def __init__( self, ox, tbt, ply ): """ the constructor """ self.ox = ox self.tbt = tbt self.ply = ply def __repr__( self ): """ creates an appropriate string """ s = "Player for " + self.ox + "\n" s += "with tiebreak type: " + self.tbt + "\n" s += "and ply == " + str(self.ply) + "\n\n" return s

Example

Page 17: HW 6: Problems 2&3

EC Problem 3: Connect 4 Board

oppCh(self):

This method should return the other kind of checker or playing piece, i.e., the piece being played by self's opponent. In particular, if self is playing 'X', this method returns 'O' and vice-versa.

>>> p = Player('X', 'LEFT', 3)>>> p.oppCh()'O'

Page 18: HW 6: Problems 2&3

scoreBoard(self, b):

This method should return a single float value representing the score of the input b, which you may assume will be an object of type Board. This should return 100.0 if the board b is a win for self, 50.0 if it is neither a win nor a loss for self, and it should return 0.0 if it is a loss for self

>>> b = Board(7,6)>>> b.setBoard( '01020305' )>>> b  

 >>> p = Player( 'X', 'LEFT', 0 )>>> p.scoreBoard(b)100.0 >>> Player('O', 'LEFT', 0).scoreBoard(b)0.0 

EC Problem 3: Connect 4 Board

Page 19: HW 6: Problems 2&3

EC Problem 3: Connect 4 Board

tiebreakMove(self, scores):

This method takes in scores, which will be a nonempty list of floating-point numbers. If there is only one highest score in that scores list, this method should return its COLUMN number, not the actual score! If there is more than one highest score because of a tie, this method should return the COLUMN number of the highest score appropriate to the player's tiebreaking type.

>>> scores = [0, 0, 50, 0, 50, 50, 0]>>> p = Player('X', 'LEFT', 1)>>> p2 = Player('X', 'RIGHT', 1)>>> p.tiebreakMove(scores)2>>> p2.tiebreakMove(scores)5

Page 20: HW 6: Problems 2&3

EC Problem 3: Connect 4 Board

scoresFor(self, b):

This method is the heart of the Player class! Its job is to return a list of scores, with the cth score representing the "goodness" of the input board after the player moves to column c. And, "goodness" is measured by what happens in the game after self.ply moves.

Page 21: HW 6: Problems 2&3

EC Problem 3: Connect 4 Board

Base Case If a particular column is full, it assigns a -1.0 score for that column. Another Base Case Next, if the object's ply is 0, no move is made. What's more, the column, if not full, is evaluated for the self player. (Which method in the Player class will do this?) When self.ply is 0, this means that all of the non-full columns will have the same score. After all, this is to be expected if the player is not looking at all into the future. And Another Base Case If the game is already over, then there's no point in making any additional moves (indeed, it's not allowed!) -- simply evaluate the board and use that score for the current column under consideration. Recursive Case But, if the object's ply is greater than 0 and the game isn't over, the code should make a move into the column that is under consideration. This will use some of the methods in Board, such as addMove.

Page 22: HW 6: Problems 2&3

0-ply scores for O:col 0 col 1 col 2 col 3 col 4 col 5 col 6

1-ply scores for O:col 0 col 1 col 2 col 3 col 4 col 5 col 6

2-ply scores for O:col 0 col 1 col 2 col 3 col 4 col 5 col 6

3-ply scores for O:col 0 col 1 col 2 col 3 col 4 col 5 col 6

-1

-1

-1

-1

50 50 50 50 50 50

50 50 100

50 50 50

0

0

100

100

0

0

0 0 50

100

00

b ‘X’‘O’EC Problem

3: Connect 4 Board

Page 23: HW 6: Problems 2&3

nextMove(self, b):

This method takes in b, an object of type Board and returns an integer -- namely, the column number that the calling object (of class Player) chooses to move to. This is the primary interface to Player, but it is really just a "wrapper" for the heavy lifting done by the other methods, particularly scoresFor. Thus, nextMove should use scoresFor and tiebreakMove to return its move.

def nextMove( self, b): #get list of scores’’’ # use self.tiebreakMove(L) to figure out which column to use

EC Problem 3: Connect 4 Board

Page 24: HW 6: Problems 2&3

playGame(self, px, po): Copy and change your hostGame method from hw6pr2 in order to create this playGame method. playGame does just that -- it calls on the nextMove method in px and po, which will be objects of type Player in order to play a game.

One additional twist: you should handle the case in which either px or po is the string 'human' instead of an object of type Player. In this 'human' case, the playGame method should simply puase and ask the user to input the next column to move for that player, with error-checking just as in hostGame.

EC Problem 3: Connect 4 Board

Page 25: HW 6: Problems 2&3

Hints for playgame…Using nextMove to add the best move…

self.addMove(po.nextMove(self), nextCheckerToMove)

Testing if won:

if self.winsFor( nextCheckerToMove )

EC Problem 3: Connect 4 Board