├── README.md ├── app.py ├── gui.png ├── gui.py ├── spellcast.py └── words.txt /README.md: -------------------------------------------------------------------------------- 1 | # Spellcast Word Finder 2 | Attempts to find the best word in the discord activity (game) Spellcast 3 | ![gui image](gui.png?raw=true "GUI") 4 | 5 | ## Spellcast in-game board 6 | ![spellcast_board](https://user-images.githubusercontent.com/57908042/159550063-592d808e-a8bf-4338-b349-0374b169855d.png) 7 | 8 | ## Features 9 | - Can utilize one and two letter swaps 10 | - Will account for long word bonus 11 | - Hover over words to highlight them on the board 12 | 13 | 14 | ## Requirements 15 | - [Python 3.6+](https://www.python.org/downloads/) 16 | 17 | ## Get started 18 | - 1. **Clone the repo** 19 | ```sh 20 | git clone 21 | ``` 22 | - 2. **Run as GUI** 23 | ```sh 24 | python gui.py 25 | ``` 26 | - 3. **Run as CLI** 27 | ```sh 28 | python spellcast.py 29 | 30 | ex: abcde 31 | asdaa 32 | dasda 33 | asdas 34 | kolak 35 | ``` 36 | ## TODO 37 | - Add word/letter multipliers 38 | - Image Recognition 39 | 40 | ## Credits 41 | - [words.txt](https://github.com/jacksonrayhamilton/wordlist-english) 42 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from gui import SpellcastApp 3 | 4 | if __name__ == "__main__": 5 | root = tk.Tk() 6 | app = SpellcastApp(root) 7 | root.mainloop() -------------------------------------------------------------------------------- /gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vscala/Spellcast-Word-Finder/f7801b1a05569ed52130508ce0ec6ef2fe4befaf/gui.png -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides a graphical user interface (GUI) application for the Spellcast Word Finder. 3 | 4 | It utilizes the tkinter library for creating the GUI elements and interacts with the 5 | Spellcast WordBoard class to generate words based on user input. 6 | 7 | Classes: 8 | SpellcastApp: Represents the main application class for the Spellcast Word Finder. 9 | 10 | Functions: 11 | None 12 | 13 | Usage: 14 | To use this module, create an instance of the SpellcastApp class and run the application. 15 | 16 | Example: 17 | import tkinter as tk 18 | from spellcast_app import SpellcastApp 19 | 20 | if __name__ == "__main__": 21 | root = tk.Tk() 22 | app = SpellcastApp(root) 23 | root.mainloop() 24 | """ 25 | 26 | import tkinter as tk 27 | import tkinter.font as tkFont 28 | from spellcast import WordBoard 29 | import threading 30 | 31 | class SpellcastApp: 32 | """ 33 | A class representing the Spellcast Word Finder application. 34 | 35 | Attributes: 36 | word_board (WordBoard): An instance of the WordBoard class. 37 | 38 | Methods: 39 | __init__(self, app_window): Initializes the SpellcastApp object. 40 | on_validate(new_value, row, column): Validates the input in the entry fields. 41 | generate_words_command(self): Generates words based on the input values. 42 | add_multiplier(self, row, col, word=False): Adds a multiplier to a specific cell. 43 | remove_multiplier(self, row, col): Removes a multiplier from a specific cell. 44 | """ 45 | 46 | def __init__(self, app_window): 47 | """ 48 | Initializes the SpellcastApp object. 49 | 50 | Args: 51 | app_window (tk.Tk): The main application window. 52 | """ 53 | self.word_board = WordBoard() 54 | self.app_window = app_window 55 | 56 | app_window.title("Spellcast Word Finder") 57 | width = 600 58 | height = 256 59 | screen_width = app_window.winfo_screenwidth() 60 | screen_height = app_window.winfo_screenheight() 61 | window_position = "%dx%d+%d+%d" % ( 62 | width, 63 | height, 64 | (screen_width - width) / 2, 65 | (screen_height - height) / 2, 66 | ) 67 | app_window.geometry(window_position) 68 | app_window.resizable(width=False, height=False) 69 | 70 | self.values = [ 71 | [tk.StringVar(app_window, value="") for _ in range(5)] for _ in range(5) 72 | ] 73 | self.line_inputs = [] 74 | self.labels = [] 75 | self.btn_generate = tk.Button(app_window) 76 | self.btn_clear = tk.Button(app_window) 77 | 78 | def on_validate(new_value, row, column): 79 | """ 80 | Validates the input in the entry fields. 81 | 82 | Args: 83 | new_value (str): The new value entered in the entry field. 84 | row (int): The row index of the entry field. 85 | column (int): The column index of the entry field. 86 | 87 | Returns: 88 | bool: True if the input is valid, False otherwise. 89 | """ 90 | # new_value only alphabet characters 91 | if not new_value.isalpha() and new_value != "": 92 | return False 93 | 94 | row, column = map(int, (row, column)) 95 | index = (row * 5 + column + 1) % 25 96 | if len(new_value) == 1: 97 | self.line_inputs[index].focus_set() 98 | self.line_inputs[index].select_range(0, "end") 99 | return True 100 | 101 | x_offset, y_offset = 25, 25 102 | for row in range(5): 103 | for column in range(5): 104 | entry = tk.Entry( 105 | app_window, 106 | textvariable=self.values[row][column], 107 | validate="key", 108 | highlightthickness=2, 109 | ) 110 | entry["borderwidth"] = "1px" 111 | entry["font"] = tkFont.Font(family="Times", size=10) 112 | entry["fg"] = "#333333" 113 | entry["justify"] = "center" 114 | entry["validatecommand"] = ( 115 | entry.register(on_validate), 116 | "%P", 117 | row, 118 | column, 119 | ) 120 | entry.place( 121 | x=x_offset + column * 32, y=y_offset + row * 32, width=32, height=32 122 | ) 123 | entry.configure( 124 | highlightbackground="black", 125 | highlightcolor="black", 126 | font=("Roboto", 16), 127 | ) 128 | def navigateEntry(idx): 129 | self.line_inputs[idx].focus_set() 130 | self.line_inputs[idx].select_range(0, "end") 131 | 132 | # when the user presses arrow keys, the focus is moved to the next entry 133 | entry.bind("", lambda _, row=row, column=column: navigateEntry((row * 5 + column - 1) % 25)) 134 | entry.bind("", lambda _, row=row, column=column: navigateEntry((row * 5 + column + 1) % 25)) 135 | entry.bind("", lambda _, row=row, column=column: navigateEntry(((row - 1) * 5 + column) % 25)) 136 | entry.bind("", lambda _, row=row, column=column: navigateEntry(((row + 1) * 5 + column) % 25)) 137 | self.line_inputs.append(entry) 138 | 139 | for row in range(3): 140 | label = tk.Label(app_window) 141 | label["font"] = tkFont.Font(family="Times", size=10) 142 | label["fg"] = "#333333" 143 | label["justify"] = "center" 144 | label["text"] = "" 145 | label.place(x=320, y=80 + row * 30, width=250, height=25) 146 | self.labels.append(label) 147 | 148 | self.btn_generate["bg"] = "#e9e9ed" 149 | self.btn_generate["font"] = tkFont.Font(family="Times", size=10) 150 | self.btn_generate["fg"] = "#000000" 151 | self.btn_generate["justify"] = "center" 152 | self.btn_generate["text"] = "Generate Words" 153 | self.btn_generate.place(x=x_offset, y=y_offset + 160, width=160, height=25) 154 | self.btn_generate["command"] = lambda: threading.Thread(target=self.generate_words_command).start() 155 | self.btn_clear["bg"] = "#e9e9ed" 156 | self.btn_clear["font"] = tkFont.Font(family="Times", size=10) 157 | self.btn_clear["fg"] = "#000000" 158 | self.btn_clear["justify"] = "center" 159 | self.btn_clear["text"] = "Reset Window" 160 | self.btn_clear.place(x=x_offset + 170, y=y_offset + 160, width=160, height=25) 161 | self.btn_clear["command"] = lambda: threading.Thread(target=self.clear_text()).start() 162 | 163 | class LabelHover: 164 | """ 165 | A class representing the hover effect for labels. 166 | 167 | Attributes: 168 | label (tk.Label): The label to apply the hover effect to. 169 | path (str): The path of the word. 170 | skipped (list): The list of skipped cells in the word. 171 | line_inputs (list): The list of entry fields. 172 | values (list): The list of StringVars representing the values in the entry fields. 173 | word (str): The word associated with the label. 174 | 175 | Methods: 176 | hover(self): Applies the hover effect to the label. 177 | unhover(self): Removes the hover effect from the label. 178 | """ 179 | 180 | def __init__(self, label, path, skipped, line_inputs, values, word): 181 | """ 182 | Initializes the LabelHover object. 183 | 184 | Args: 185 | label (tk.Label): The label to apply the hover effect to. 186 | path (str): The path of the word. 187 | skipped (list): The list of skipped cells in the word. 188 | line_inputs (list): The list of entry fields. 189 | values (list): The list of StringVars representing the values in the entry fields. 190 | word (str): The word associated with the label. 191 | """ 192 | self.label = label 193 | self.path = path 194 | self.skipped = skipped 195 | self.skip_set = set(skipped) 196 | self.line_inputs = line_inputs 197 | self.label.bind("", lambda _: self.hover()) 198 | self.label.bind("", lambda _: self.unhover()) 199 | self.values = values 200 | self.temporary = [ 201 | [value.get().lower() for value in line] for line in self.values 202 | ] 203 | self.word = word 204 | 205 | def hover(self): 206 | """ 207 | Applies the hover effect to the label. 208 | """ 209 | for (i, j), c in zip(self.path[::-1], self.word): 210 | entry_index = i * 5 + j 211 | # if first letter of word, highlight in pinkpurple 212 | if (i, j) == self.path[-1]: 213 | self.line_inputs[entry_index].configure( 214 | highlightbackground="#F522EE", 215 | highlightcolor="#F522EE", 216 | background="#43C6E2", 217 | font=("Roboto", 20, tkFont.BOLD), 218 | ) 219 | elif (i, j) == self.path[0]: 220 | self.line_inputs[entry_index].configure( 221 | highlightbackground="purple", 222 | highlightcolor="purple", 223 | background="#43C6E2", 224 | font=("Roboto", 20, tkFont.BOLD), 225 | ) 226 | else: 227 | self.line_inputs[entry_index].configure( 228 | highlightbackground="#43C6E2", 229 | highlightcolor="#43C6E2", 230 | background="#43C6E2", 231 | font=("Roboto", 20, tkFont.BOLD), 232 | fg="white", 233 | ) 234 | 235 | if (i, j) in self.skip_set: 236 | self.values[i][j].set(c) 237 | 238 | for i, j in self.skipped: 239 | entry_index = i * 5 + j 240 | self.line_inputs[entry_index].configure( 241 | highlightbackground="red", 242 | highlightcolor="red", 243 | background="red", 244 | font=("Roboto", 20, tkFont.BOLD), 245 | fg="white", 246 | ) 247 | 248 | def unhover(self): 249 | """ 250 | Removes the hover effect from the label. 251 | """ 252 | for i, j in self.path + self.skipped: 253 | entry_index = i * 5 + j 254 | self.line_inputs[entry_index].configure( 255 | highlightbackground="black", 256 | highlightcolor="black", 257 | background="white", 258 | font=("Roboto", 16, tkFont.NORMAL), 259 | fg="black", 260 | ) 261 | self.values[i][j].set(self.temporary[i][j]) 262 | 263 | def generate_words_command(self): 264 | """ 265 | Generates words based on the input values. 266 | """ 267 | word_label_prefix = ["No swaps", "One swap", "Two swaps"] 268 | for i in range(3): 269 | self.labels[i]["text"] = f"{word_label_prefix[i]}: Generating..." 270 | 271 | self.btn_generate["text"] = "Generating..." 272 | self.btn_generate['state'] = 'disabled' 273 | for line_input in self.line_inputs: 274 | line_input['state'] = 'disabled' 275 | 276 | board = [[v.get().lower() for v in line] for line in self.values] 277 | 278 | for line_input in self.line_inputs: 279 | line_input['state'] = 'normal' 280 | 281 | self.word_board.set_board(board) 282 | 283 | for i in range(3): 284 | best = self.word_board.best_word(i) 285 | self.labels[i]["text"] = f"{word_label_prefix[i]}: {best[:2]}" 286 | self.LabelHover( 287 | self.labels[i], best[2], best[3], self.line_inputs, self.values, best[0] 288 | ) 289 | 290 | self.btn_generate['state'] = 'normal' 291 | self.btn_generate["text"] = "Generate Words" 292 | 293 | def clear_text(self): 294 | self.app_window.destroy() 295 | root = tk.Tk() 296 | self.app_window = SpellcastApp(root) 297 | root.mainloop() 298 | 299 | def add_multiplier(self, row, col, word=False): 300 | """ 301 | Adds a multiplier to a specific cell. 302 | 303 | Args: 304 | row (int): The row index of the cell. 305 | col (int): The column index of the cell. 306 | word (bool): Whether the multiplier is for a word or a letter. Default is False. 307 | """ 308 | self.word_board.add_multiplier(row, col, 1, word) 309 | 310 | def remove_multiplier(self, row, col): 311 | """ 312 | Removes a multiplier from a specific cell. 313 | 314 | Args: 315 | row (int): The row index of the cell. 316 | col (int): The column index of the cell. 317 | """ 318 | self.word_board.remove_multiplier(row, col) 319 | 320 | 321 | if __name__ == "__main__": 322 | root = tk.Tk() 323 | app = SpellcastApp(root) 324 | root.mainloop() 325 | -------------------------------------------------------------------------------- /spellcast.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module represents a word board game. It provides a class called `WordBoard` 3 | that manages the logic for the game. The `WordBoard` class has various methods 4 | for setting up the game board, finding the best word that can be formed on the board, 5 | adding and removing multipliers, and performing preliminary checks for word validity. 6 | 7 | Attributes: 8 | - `words` (list): A list of words loaded from the "words.txt" file. 9 | - `letter_values` (dict): A dictionary containing letter values considering multipliers. 10 | - `board_value` (dict): A dictionary containing values for each cell on the board. 11 | 12 | Methods: 13 | - `__init__()`: Initializes a `WordBoard` instance by reading words from a file 14 | and initializing various attributes needed for managing the game board. 15 | - `recalculate()`: Recalculates letter values and board values based on the 16 | current state of the board. 17 | - `set_board(board)`: Sets the game board and recalculates attributes based 18 | on the new board configuration. 19 | - `precheck(word)`: Performs a preliminary check for word validity 20 | by checking if the required letters for the word are available on the board. 21 | - `board_contains(word, skips=0)`: Checks if the board contains 22 | a given word, considering skips and multipliers. 23 | - `best_word(skips=0)`: Finds the highest scoring word that can be 24 | formed on the board, considering skips and multipliers. 25 | - `add_multiplier(row, column, multiplier, word)`: Adds a multiplier 26 | to a specific cell on the board and updates multipliers. 27 | - `remove_multiplier(row, column)`: Removes multipliers from a specific 28 | cell on the board and recalculates attributes. 29 | 30 | Usage: 31 | - Create an instance of the `WordBoard` class. 32 | - Set the game board using the `set_board()` method. 33 | - Find the best words that can be formed on the board using the `best_word()` method. 34 | - Add or remove multipliers using the `add_multiplier()` and `remove_multiplier()` methods. 35 | """ 36 | 37 | from collections import Counter, defaultdict 38 | from functools import reduce 39 | from itertools import product 40 | import threading 41 | 42 | LETTERS_AND_VALUES = { 43 | "a": 1, 44 | "b": 4, 45 | "c": 5, 46 | "d": 3, 47 | "e": 1, 48 | "f": 5, 49 | "g": 3, 50 | "h": 4, 51 | "i": 1, 52 | "j": 7, 53 | "k": 3, 54 | "l": 3, 55 | "m": 4, 56 | "n": 2, 57 | "o": 1, 58 | "p": 4, 59 | "q": 8, 60 | "r": 2, 61 | "s": 2, 62 | "t": 2, 63 | "u": 4, 64 | "v": 5, 65 | "w": 5, 66 | "x": 7, 67 | "y": 4, 68 | "z": 8, 69 | } 70 | 71 | 72 | class WordBoard: 73 | """ 74 | A class representing a word board game. 75 | 76 | This class manages the logic for a word board game, where players try to form words using 77 | letters on the board with different multipliers applied. 78 | 79 | Attributes: 80 | words_set (set): A set of words loaded from the "words.txt" file. 81 | letter_multipliers (defaultdict): A dictionary containing letter multipliers. 82 | word_multipliers (defaultdict): A dictionary containing word multipliers. 83 | board (list): A 2D list representing the game board. 84 | row_count (int): Number of rows on the board. 85 | column_count (int): Number of columns on the board. 86 | total_count (Counter): Count of each letter available on the board. 87 | word_values (list): A list of tuples containing word values and words. 88 | skips (int): Number of letters that can be skipped in forming a word. 89 | """ 90 | 91 | def __init__(self): 92 | """ 93 | Initializes a WordBoard instance. 94 | 95 | Reads words from a file and initializes various attributes needed for 96 | managing the game board. 97 | """ 98 | self.words_set = set() 99 | self.letter_multipliers = defaultdict(lambda: 1) 100 | self.word_multipliers = defaultdict(lambda: 1) 101 | self.board = [] 102 | self.row_count = 0 103 | self.column_count = 0 104 | self.total_count = Counter() 105 | self.word_values = [] 106 | self.skips = 0 107 | 108 | with open("words.txt", encoding="utf-8") as file: 109 | self.words_set = {word[:-1] for word in file.readlines()} 110 | 111 | def recalculate(self): 112 | """ 113 | Recalculate board-related attributes. 114 | 115 | This method recalculates letter values and board values based on the current state of the board. 116 | """ 117 | max_global_multiplier = max(self.word_multipliers.values(), default=1) 118 | max_character_multiplier = defaultdict(lambda: 1) 119 | board_row_count = 5 120 | board_column_count = 5 121 | for row, column in product(range(board_row_count), range(board_column_count)): 122 | max_character_multiplier[self.board[row][column]] = max( 123 | max_character_multiplier[self.board[row][column]], 124 | max_global_multiplier, 125 | self.letter_multipliers[(row, column)], 126 | ) 127 | 128 | letter_values = { 129 | letter: val * max_character_multiplier[letter] 130 | for letter, val in LETTERS_AND_VALUES.items() 131 | } 132 | 133 | long_word_bonus_points = 10 134 | long_word_minimum_letter_count = 6 135 | 136 | def calculate_value(word): 137 | value = sum( 138 | letter_values[character.lower()] 139 | for character in word 140 | if character.lower() in LETTERS_AND_VALUES 141 | ) + ( 142 | long_word_bonus_points if len(word) > long_word_minimum_letter_count else 0 143 | ) 144 | return value 145 | 146 | self.word_values = [(calculate_value(word), word) for word in self.words_set] 147 | self.word_values.sort(reverse=True) 148 | 149 | def set_board(self, game_board): 150 | """ 151 | Set the game board. 152 | 153 | Args: 154 | game_board (list of lists): A 2D list representing the game board. 155 | 156 | Sets up the game board and recalculates attributes based on the new board configuration. 157 | """ 158 | self.board = game_board 159 | self.row_count = len(game_board) 160 | self.column_count = len(game_board[0]) 161 | self.total_count = Counter(cell for row in game_board for cell in row) 162 | 163 | self.word_multipliers = defaultdict(lambda: 1) 164 | self.letter_multipliers = defaultdict(lambda: 1) 165 | self.recalculate() 166 | 167 | def precheck(self, word): 168 | """ 169 | Perform a preliminary check for word validity. 170 | 171 | Args: 172 | word (str): The word to be checked. 173 | 174 | Returns: 175 | bool: True if the word can be formed on the current board, False otherwise. 176 | 177 | Checks if the required letters for the word are available on the board. 178 | """ 179 | word_count = Counter(word) 180 | for character in word_count: 181 | if word_count[character] > self.total_count[character]: 182 | return False 183 | return True 184 | 185 | def board_contains(self, word, skips=0): 186 | """ 187 | Check if the board contains a given word. 188 | 189 | Args: 190 | word (str): The word to check for. 191 | skips (int, optional): The number of letters that can be skipped. Default is 0. 192 | 193 | Returns: 194 | tuple: A tuple containing the path of letters forming the word, its value, and skipped letters. 195 | 196 | Checks if the board contains the specified word considering skips and multipliers. 197 | """ 198 | row_count, column_count = self.row_count, self.column_count 199 | if not skips and not self.precheck(word): 200 | return [], 0, [] 201 | 202 | def backtrack(starting_row, starting_column, remaining_letters): 203 | if not remaining_letters: 204 | return True 205 | if self.board[starting_row][starting_column] != remaining_letters[0]: 206 | if self.skips and self.board[starting_row][starting_column] != ".": 207 | self.skips -= 1 208 | else: 209 | return False 210 | 211 | temp, self.board[starting_row][starting_column] = self.board[starting_row][starting_column], "." 212 | end_loop = False 213 | if starting_row + 1 < row_count: 214 | end_loop = end_loop or backtrack(starting_row + 1, starting_column, remaining_letters[1:]) 215 | if starting_column + 1 < column_count: 216 | end_loop = end_loop or backtrack(starting_row + 1, starting_column + 1, remaining_letters[1:]) 217 | if starting_column > 0: 218 | end_loop = end_loop or backtrack(starting_row + 1, starting_column - 1, remaining_letters[1:]) 219 | if starting_row > 0: 220 | end_loop = end_loop or backtrack(starting_row - 1, starting_column, remaining_letters[1:]) 221 | if starting_column + 1 < column_count: 222 | end_loop = end_loop or backtrack(starting_row - 1, starting_column + 1, remaining_letters[1:]) 223 | if starting_column > 0: 224 | end_loop = end_loop or backtrack(starting_row - 1, starting_column - 1, remaining_letters[1:]) 225 | if starting_column + 1 < column_count: 226 | end_loop = end_loop or backtrack(starting_row, starting_column + 1, remaining_letters[1:]) 227 | if starting_column > 0: 228 | end_loop = end_loop or backtrack(starting_row, starting_column - 1, remaining_letters[1:]) 229 | 230 | self.board[starting_row][starting_column] = temp 231 | if self.board[starting_row][starting_column] != remaining_letters[0]: 232 | self.skips += 1 233 | 234 | if end_loop: 235 | path.append((starting_row, starting_column)) 236 | if self.board[starting_row][starting_column] != remaining_letters[0]: 237 | skipped.append((starting_row, starting_column)) 238 | return end_loop 239 | 240 | best = 0 241 | out = ([], 0, []) 242 | for row in range(row_count): 243 | for column in range(column_count): 244 | self.skips = skips 245 | path = [] 246 | skipped = [] 247 | if self.board[row][column] == word: 248 | value = ( 249 | self.letter_multipliers[(row, column)] 250 | * self.word_multipliers[(row, column)] 251 | * LETTERS_AND_VALUES[word[0].lower()] 252 | ) 253 | if value > best: 254 | out = ([(row, column)], value, []) 255 | best = value 256 | if self.board[row][column] == word[0]: 257 | if backtrack(row, column, word): 258 | word_multiplier = reduce( 259 | lambda accumulator, current: accumulator * self.word_multipliers[current], 260 | path, 261 | 1, 262 | ) 263 | value = ( 264 | sum( 265 | self.letter_multipliers[cell] 266 | * LETTERS_AND_VALUES[word[letter_index].lower()] 267 | for letter_index, cell in enumerate(path[::-1]) 268 | ) 269 | * word_multiplier 270 | ) 271 | if value > best: 272 | out = (path, value, skipped) 273 | best = value 274 | return out 275 | 276 | def best_word(self, skips=0): 277 | """ 278 | Find the best word that can be formed on the board. 279 | 280 | Args: 281 | skips (int, optional): The number of letters that can be skipped. Default is 0. 282 | 283 | Returns: 284 | tuple: A tuple containing the best word, its value, path, and skipped letters. 285 | 286 | Finds the highest scoring word that can be formed on the board, considering skips and multipliers. 287 | """ 288 | current_best = ("", 0, [], []) 289 | current_value = 0 290 | for _, word in self.word_values: 291 | path, actual_value, skipped = self.board_contains(word, skips) 292 | if path and actual_value > current_value: 293 | current_value = actual_value 294 | current_best = (word, actual_value, path, skipped) 295 | return current_best 296 | 297 | def add_multiplier(self, row, column, multiplier, word): 298 | """ 299 | Add a multiplier to a specific cell on the board. 300 | 301 | Args: 302 | row (int): The row of the cell to which the multiplier is applied. 303 | column (int): The column of the cell to which the multiplier is applied. 304 | multiplier (int): The multiplier value to be added. 305 | word (bool): True if the multiplier is for a word, False if it's for a letter. 306 | 307 | Updates multipliers and recalculates attributes based on the new multiplier. 308 | """ 309 | if word: 310 | self.word_multipliers[(row, column)] = multiplier 311 | else: 312 | self.letter_multipliers[(row, column)] = multiplier 313 | self.recalculate() 314 | 315 | def remove_multiplier(self, row, column): 316 | """ 317 | Remove multipliers from a specific cell on the board. 318 | 319 | Args: 320 | row (int): The row of the cell from which the multipliers are removed. 321 | column (int): The column of the cell from which the multipliers are removed. 322 | 323 | Resets multipliers for the specified cell and recalculates attributes. 324 | """ 325 | self.word_multipliers[(row, column)] = 1 326 | self.letter_multipliers[(row, column)] = 1 327 | self.recalculate() 328 | 329 | 330 | if __name__ == "__main__": 331 | # Read board and create wordboard 332 | board = [[character.lower() for character in input()] for _ in range(5)] 333 | word_board = WordBoard() 334 | word_board.set_board(board) 335 | def findBestWords(): 336 | # Find best words 337 | best_word_with_no_swaps = word_board.best_word() # no swaps 338 | print("No swaps:\n", best_word_with_no_swaps) 339 | best_word_with_one_swap = word_board.best_word(1) # one swap 340 | print("One swap:\n", best_word_with_one_swap) 341 | best_word_with_two_swaps = word_board.best_word(2) # two swaps 342 | print("Two swaps:\n", best_word_with_two_swaps) 343 | 344 | threading.Thread(target=findBestWords).start() 345 | 346 | #x, y = map(int, input().split()) 347 | #wgXY = word_board.generateWords(x=x, y=y) --------------------------------------------------------------------------------