TheAlgorithms-Python
395 строк · 14.8 Кб
1"""
2Creates a random wordsearch with eight different directions
3that are best described as compass locations.
4
5@ https://en.wikipedia.org/wiki/Word_search
6"""
7
8from random import choice, randint, shuffle9
10# The words to display on the word search -
11# can be made dynamic by randonly selecting a certain number of
12# words from a predefined word file, while ensuring the character
13# count fits within the matrix size (n x m)
14WORDS = ["cat", "dog", "snake", "fish"]15
16WIDTH = 1017HEIGHT = 1018
19
20class WordSearch:21"""22>>> ws = WordSearch(WORDS, WIDTH, HEIGHT)
23>>> ws.board # doctest: +ELLIPSIS
24[[None, ..., None], ..., [None, ..., None]]
25>>> ws.generate_board()
26"""
27
28def __init__(self, words: list[str], width: int, height: int) -> None:29self.words = words30self.width = width31self.height = height32
33# Board matrix holding each letter34self.board: list[list[str | None]] = [[None] * width for _ in range(height)]35
36def insert_north(self, word: str, rows: list[int], cols: list[int]) -> None:37"""38>>> ws = WordSearch(WORDS, 3, 3)
39>>> ws.insert_north("cat", [2], [2])
40>>> ws.board # doctest: +NORMALIZE_WHITESPACE
41[[None, None, 't'],
42[None, None, 'a'],
43[None, None, 'c']]
44>>> ws.insert_north("at", [0, 1, 2], [2, 1])
45>>> ws.board # doctest: +NORMALIZE_WHITESPACE
46[[None, 't', 't'],
47[None, 'a', 'a'],
48[None, None, 'c']]
49"""
50word_length = len(word)51# Attempt to insert the word into each row and when successful, exit52for row in rows:53# Check if there is space above the row to fit in the word54if word_length > row + 1:55continue56
57# Attempt to insert the word into each column58for col in cols:59# Only check to be made here is if there are existing letters60# above the column that will be overwritten61letters_above = [self.board[row - i][col] for i in range(word_length)]62if all(letter is None for letter in letters_above):63# Successful, insert the word north64for i in range(word_length):65self.board[row - i][col] = word[i]66return67
68def insert_northeast(self, word: str, rows: list[int], cols: list[int]) -> None:69"""70>>> ws = WordSearch(WORDS, 3, 3)
71>>> ws.insert_northeast("cat", [2], [0])
72>>> ws.board # doctest: +NORMALIZE_WHITESPACE
73[[None, None, 't'],
74[None, 'a', None],
75['c', None, None]]
76>>> ws.insert_northeast("at", [0, 1], [2, 1, 0])
77>>> ws.board # doctest: +NORMALIZE_WHITESPACE
78[[None, 't', 't'],
79['a', 'a', None],
80['c', None, None]]
81"""
82word_length = len(word)83# Attempt to insert the word into each row and when successful, exit84for row in rows:85# Check if there is space for the word above the row86if word_length > row + 1:87continue88
89# Attempt to insert the word into each column90for col in cols:91# Check if there is space to the right of the word as well as above92if word_length + col > self.width:93continue94
95# Check if there are existing letters96# to the right of the column that will be overwritten97letters_diagonal_left = [98self.board[row - i][col + i] for i in range(word_length)99]100if all(letter is None for letter in letters_diagonal_left):101# Successful, insert the word northeast102for i in range(word_length):103self.board[row - i][col + i] = word[i]104return105
106def insert_east(self, word: str, rows: list[int], cols: list[int]) -> None:107"""108>>> ws = WordSearch(WORDS, 3, 3)
109>>> ws.insert_east("cat", [1], [0])
110>>> ws.board # doctest: +NORMALIZE_WHITESPACE
111[[None, None, None],
112['c', 'a', 't'],
113[None, None, None]]
114>>> ws.insert_east("at", [1, 0], [2, 1, 0])
115>>> ws.board # doctest: +NORMALIZE_WHITESPACE
116[[None, 'a', 't'],
117['c', 'a', 't'],
118[None, None, None]]
119"""
120word_length = len(word)121# Attempt to insert the word into each row and when successful, exit122for row in rows:123# Attempt to insert the word into each column124for col in cols:125# Check if there is space to the right of the word126if word_length + col > self.width:127continue128
129# Check if there are existing letters130# to the right of the column that will be overwritten131letters_left = [self.board[row][col + i] for i in range(word_length)]132if all(letter is None for letter in letters_left):133# Successful, insert the word east134for i in range(word_length):135self.board[row][col + i] = word[i]136return137
138def insert_southeast(self, word: str, rows: list[int], cols: list[int]) -> None:139"""140>>> ws = WordSearch(WORDS, 3, 3)
141>>> ws.insert_southeast("cat", [0], [0])
142>>> ws.board # doctest: +NORMALIZE_WHITESPACE
143[['c', None, None],
144[None, 'a', None],
145[None, None, 't']]
146>>> ws.insert_southeast("at", [1, 0], [2, 1, 0])
147>>> ws.board # doctest: +NORMALIZE_WHITESPACE
148[['c', None, None],
149['a', 'a', None],
150[None, 't', 't']]
151"""
152word_length = len(word)153# Attempt to insert the word into each row and when successful, exit154for row in rows:155# Check if there is space for the word below the row156if word_length + row > self.height:157continue158
159# Attempt to insert the word into each column160for col in cols:161# Check if there is space to the right of the word as well as below162if word_length + col > self.width:163continue164
165# Check if there are existing letters166# to the right of the column that will be overwritten167letters_diagonal_left = [168self.board[row + i][col + i] for i in range(word_length)169]170if all(letter is None for letter in letters_diagonal_left):171# Successful, insert the word southeast172for i in range(word_length):173self.board[row + i][col + i] = word[i]174return175
176def insert_south(self, word: str, rows: list[int], cols: list[int]) -> None:177"""178>>> ws = WordSearch(WORDS, 3, 3)
179>>> ws.insert_south("cat", [0], [0])
180>>> ws.board # doctest: +NORMALIZE_WHITESPACE
181[['c', None, None],
182['a', None, None],
183['t', None, None]]
184>>> ws.insert_south("at", [2, 1, 0], [0, 1, 2])
185>>> ws.board # doctest: +NORMALIZE_WHITESPACE
186[['c', None, None],
187['a', 'a', None],
188['t', 't', None]]
189"""
190word_length = len(word)191# Attempt to insert the word into each row and when successful, exit192for row in rows:193# Check if there is space below the row to fit in the word194if word_length + row > self.height:195continue196
197# Attempt to insert the word into each column198for col in cols:199# Only check to be made here is if there are existing letters200# below the column that will be overwritten201letters_below = [self.board[row + i][col] for i in range(word_length)]202if all(letter is None for letter in letters_below):203# Successful, insert the word south204for i in range(word_length):205self.board[row + i][col] = word[i]206return207
208def insert_southwest(self, word: str, rows: list[int], cols: list[int]) -> None:209"""210>>> ws = WordSearch(WORDS, 3, 3)
211>>> ws.insert_southwest("cat", [0], [2])
212>>> ws.board # doctest: +NORMALIZE_WHITESPACE
213[[None, None, 'c'],
214[None, 'a', None],
215['t', None, None]]
216>>> ws.insert_southwest("at", [1, 2], [2, 1, 0])
217>>> ws.board # doctest: +NORMALIZE_WHITESPACE
218[[None, None, 'c'],
219[None, 'a', 'a'],
220['t', 't', None]]
221"""
222word_length = len(word)223# Attempt to insert the word into each row and when successful, exit224for row in rows:225# Check if there is space for the word below the row226if word_length + row > self.height:227continue228
229# Attempt to insert the word into each column230for col in cols:231# Check if there is space to the left of the word as well as below232if word_length > col + 1:233continue234
235# Check if there are existing letters236# to the right of the column that will be overwritten237letters_diagonal_left = [238self.board[row + i][col - i] for i in range(word_length)239]240if all(letter is None for letter in letters_diagonal_left):241# Successful, insert the word southwest242for i in range(word_length):243self.board[row + i][col - i] = word[i]244return245
246def insert_west(self, word: str, rows: list[int], cols: list[int]) -> None:247"""248>>> ws = WordSearch(WORDS, 3, 3)
249>>> ws.insert_west("cat", [1], [2])
250>>> ws.board # doctest: +NORMALIZE_WHITESPACE
251[[None, None, None],
252['t', 'a', 'c'],
253[None, None, None]]
254>>> ws.insert_west("at", [1, 0], [1, 2, 0])
255>>> ws.board # doctest: +NORMALIZE_WHITESPACE
256[['t', 'a', None],
257['t', 'a', 'c'],
258[None, None, None]]
259"""
260word_length = len(word)261# Attempt to insert the word into each row and when successful, exit262for row in rows:263# Attempt to insert the word into each column264for col in cols:265# Check if there is space to the left of the word266if word_length > col + 1:267continue268
269# Check if there are existing letters270# to the left of the column that will be overwritten271letters_left = [self.board[row][col - i] for i in range(word_length)]272if all(letter is None for letter in letters_left):273# Successful, insert the word west274for i in range(word_length):275self.board[row][col - i] = word[i]276return277
278def insert_northwest(self, word: str, rows: list[int], cols: list[int]) -> None:279"""280>>> ws = WordSearch(WORDS, 3, 3)
281>>> ws.insert_northwest("cat", [2], [2])
282>>> ws.board # doctest: +NORMALIZE_WHITESPACE
283[['t', None, None],
284[None, 'a', None],
285[None, None, 'c']]
286>>> ws.insert_northwest("at", [1, 2], [0, 1])
287>>> ws.board # doctest: +NORMALIZE_WHITESPACE
288[['t', None, None],
289['t', 'a', None],
290[None, 'a', 'c']]
291"""
292word_length = len(word)293# Attempt to insert the word into each row and when successful, exit294for row in rows:295# Check if there is space for the word above the row296if word_length > row + 1:297continue298
299# Attempt to insert the word into each column300for col in cols:301# Check if there is space to the left of the word as well as above302if word_length > col + 1:303continue304
305# Check if there are existing letters306# to the right of the column that will be overwritten307letters_diagonal_left = [308self.board[row - i][col - i] for i in range(word_length)309]310if all(letter is None for letter in letters_diagonal_left):311# Successful, insert the word northwest312for i in range(word_length):313self.board[row - i][col - i] = word[i]314return315
316def generate_board(self) -> None:317"""318Generates a board with a random direction for each word.
319
320>>> wt = WordSearch(WORDS, WIDTH, HEIGHT)
321>>> wt.generate_board()
322>>> len(list(filter(lambda word: word is not None, sum(wt.board, start=[])))
323... ) == sum(map(lambda word: len(word), WORDS))
324True
325"""
326directions = (327self.insert_north,328self.insert_northeast,329self.insert_east,330self.insert_southeast,331self.insert_south,332self.insert_southwest,333self.insert_west,334self.insert_northwest,335)336for word in self.words:337# Shuffle the row order and column order that is used when brute forcing338# the insertion of the word339rows, cols = list(range(self.height)), list(range(self.width))340shuffle(rows)341shuffle(cols)342
343# Insert the word via the direction344choice(directions)(word, rows, cols)345
346
347def visualise_word_search(348board: list[list[str | None]] | None = None, *, add_fake_chars: bool = True349) -> None:350"""351Graphically displays the word search in the terminal.
352
353>>> ws = WordSearch(WORDS, 5, 5)
354>>> ws.insert_north("cat", [4], [4])
355>>> visualise_word_search(
356... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE
357# # # # #
358# # # # #
359# # # # t
360# # # # a
361# # # # c
362>>> ws.insert_northeast("snake", [4], [4, 3, 2, 1, 0])
363>>> visualise_word_search(
364... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE
365# # # # e
366# # # k #
367# # a # t
368# n # # a
369s # # # c
370"""
371if board is None:372word_search = WordSearch(WORDS, WIDTH, HEIGHT)373word_search.generate_board()374board = word_search.board375
376result = ""377for row in range(len(board)):378for col in range(len(board[0])):379character = "#"380if (letter := board[row][col]) is not None:381character = letter382# Empty char, so add a fake char383elif add_fake_chars:384character = chr(randint(97, 122))385result += f"{character} "386result += "\n"387print(result, end="")388
389
390if __name__ == "__main__":391import doctest392
393doctest.testmod()394
395visualise_word_search()396