## The threeInARowBoard class defines a board to be used with the ## three in a row game ## This will use tkinter to display the board graphically # Import the tkinter tools import tkinter as tk #Import the newer themed tk from tkinter import ttk ## This will allow us to ask for a filename from tkinter import filedialog as fd # These will just make it a little easier to handle similar names validBlue = ["B", "b", "Blue", "blue"] validWhite = ["W", "w", "White", "white"] validDash = ["-", "G", "g", "Grey", "grey"] # What color do we want to use for each name, it is pretty self explanitory charToColor = {"W": "White", "B": "Blue", "-":"Grey"} # How wide to have the colored tiles. 2 seems about right # Since it is a label object for now, it is not sized in pixles, but for fonts tileWidth = 2 class threeInARowBoard : """Maintain the data for an instance of the three in a row problem. As data is updated, by default it will update an image representing the state of the board.""" def __init__(self): """Create an instance of the board after asking the user for what file to read""" # Opening the root window first directly here, will allow us to # close it after we get the file dialog before creating the board root = tk.Tk() # Now get the file they want and close the window it used. filename = fd.askopenfilename() fileHandle = open(filename) self.n = int(fileHandle.readline()) root.destroy() # Create an array of "colors" that will be text representation of # the board to initialize the game boards self.textBoard = [] for row in range(self.n): self.textBoard.append([c for c in fileHandle.readline().strip()]) # We will need a root window in which to display things self.root = tk.Tk() self.root.title("Three in a row") # We will have both a graphical board and text board # This will allow the option to only update the text board # if we want to speed it along self.updateGraphical = True # Initially, we will set it up with all items grey, then call ourself to set the colors self.board = [] for r in range(self.n) : currentRow = [] for c in range(self.n): newLabel = ttk.Label(self.root, width=tileWidth, background="Grey", borderwidth=2, relief="ridge") newLabel.grid(column=c, row=r) currentRow.append(newLabel) self.board.append(currentRow) self.setAllColors(self.textBoard) def __str__(self): """Return the text representation of the game board.""" returnString = "" for row in range(self.n): returnString += ("".join(self.textBoard[row])) + '\n' return returnString def validColor(self, color:str)->bool: """This is an error checking method to make sure the color is valid.""" return ((color in validBlue) or (color in validWhite) or (color in validDash)) # Here we have some setting procedures to set the colors def setColorAt(self, row: int, col: int, color: str): """Set the color at row col to be color Color should be one of 'B', 'W' or '-'""" if (self.validColor(color)): self.board[row][col].config(background=charToColor[color]) self.textBoard[row][col] = color if(self.updateGraphical): self.root.update() else: print(f"{color} is not a valid color") def setAllColors(self, textBoard: list): """Given board indicating the state of the game and set set the colors on the board appropriately. The board is a list of list of strings. Each string is either 'B', 'W' or '-'""" # print(f"About to set color to \n{textBoard}") # print(f"\nInitial self.textBoard is {self.textBoard}") if(self.n != len(textBoard)): print(f"The board sizes do not match, I am {self.n} not {n}") return for row in range(self.n): if(len(textBoard[row]) != self.n): print("The row and column sizes to not match") return for col in range(self.n): self.setColorAt(row, col, textBoard[row][col]) def findNextDash(self) -> tuple: # , row:int, col:int """Return the location of the next "-" as a tuple, (row,col) If no dash is found, return (-1, -1)""" # Start looking from the 'beginning (0,0)' row = 0 col = 0 while ((row < self.n) and (col < self.n)): if (self.getColorAt(row,col) == "-"): return [row,col] col += 1 if(col == self.n): # We got to the end of one row, time for a carriage return col = 0 row += 1 # If we made it here without finding one, we did not find a '-' in the board # return [-1, -1], which is what indicates that we did not find anything return [-1,-1] # Here we have some getting procedures to get the colors at a location def getColorAt(self,row:int, col:int): """ return the color value at the given location""" return self.textBoard[row][col] # Do we want to be updating the graphics every time? def updatingGraphics(self, value:bool): """Set the toggle to whichver they choose. When this is true, which is the default, each change is displayed. When this is false, it will not update the display. When is called to set the value is to True, the board display is refreshed immediately.""" self.updateGraphical = value if(value): # If it was set to true, we should do an update now self.root.update() def close(self): """Close the board and destroy the display window""" self.root.destroy()