I create a boogle game, and I need to build a function that receives in input: the letter board (list of lists), the list of legal words and an integer n.
The function must return all n-length tracks of valid words.
For example n = 3 then the function must return all the paths on the three-length board which are actually valid words.
I wrote a code that returns in a particular example one route out of three routes that must be returned.
Input:
board1 = [['Q', 'O', 'Q', 'Q'],
['D', 'O', 'G', 'Q'],
['Q', 'O', 'Q', 'Q'],
['Q', 'Q', 'Q', 'Q']]
word_dict = {'DOG': True}
n = 3
board = Board(board1)
length_n_paths(3, board, word_dict)
My Output:
[((1, 0), (1, 1), (1, 2))]
Wanted Output:
[[(1, 0), (0, 1), (1, 2)], [(1, 0), (1, 1), (1, 2)], [(1, 0), (2, 1), (1, 2)]]
I used a combination, first I found all the possible combinations of letters of length n, then I went through a coordinate coordinate and checked if each coordinate is in a valid position according to the coordinate in front of it, and then I checked if the word coming out of the letter combination is a word from the word list.
If so - I will return its path in a list with the other legal words paths.
my code:
direct_lst=['Up','Down','Right','Left','Up_right','Up_left','Down_right','Down_left']
class Board:
def __init__(self, board):
self.board = board
def get_board_coordinate(self):
cord_lst = []
row = len(self.board)
col = len(self.board[0])
for i in range(row):
for j in range(col):
cord_lst.append((i, j))
return cord_lst
def possible_directions(self, coordinate, next_coordinate):
y, x = coordinate
directions_funcs = {
# A dictionary that matches between a letter and the direction of the desired search
'Up': (y - 1, x),
'Down': (y + 1, x),
'Right': (y, x + 1),
'Left': (y, x - 1),
'Up_right': (y - 1, x + 1),
'Up_left': (y - 1, x - 1),
'Down_right': (y + 1, x + 1),
'Down_left': (y + 1, x + 1)
}
it_ok = False
for direction in direct_lst:
if directions_funcs[direction] == next_coordinate:
it_ok = True
return it_ok
def is_valid_path(board, path, words):
word = board.board[path[0][0]][path[0][1]]
board_coordinates = board.get_board_coordinate()
for cord in range(len(path)-1):
if path[cord] in board_coordinates and path[cord+1] in board_coordinates:
if not board.possible_directions(path[cord], path[cord + 1]):
return None
else:
word += board.board[path[cord + 1][0]][path[cord + 1][1]]
else:
return None
if word in set(words):
return word
import itertools
def create_dict(board, n):
new_dict = dict()
row = len(board.board)
col = len(board.board[0])
for i in range(row):
for j in range(col):
new_dict[(i, j)] = board.board[i][j]
result_list = list(map(list, itertools.combinations(new_dict.items(), n)))
return result_list
def coordinates_lst_and_str_lst(board, n):
combine = create_dict(board, n)
all_cord_dic = dict()
for lst in combine:
is_it_ok = True
cord_lst = []
str_l = ""
for i in range(n):
cord_lst.append(lst[i][0])
str_l += lst[i][1]
try:
if not board.possible_directions(lst[i][0], lst[i + 1][0]):
is_it_ok = False
break
except IndexError:
break
if is_it_ok:
all_cord_dic[tuple(cord_lst)] = str_l
all_cord_dic[tuple(cord_lst)[::-1]] = str_l[::-1]
return all_cord_dic
def length_n_paths(n, board, words):
possible_words = coordinates_lst_and_str_lst(board, n)
my_dict = {key:val for key, val in possible_words.items() if val in words}
return list(my_dict.keys())
I think the problem is in the combination but I dont know how to fix it.
I would be happy for any help.
After debugging, it's apparent that the result possible_words does not contain the key (1, 0), (0, 1), (1, 2), so that explains why it's not part of the answer - so the question becomes why doesn't the call to coordinates_lst_and_str_lst() generate that tuple (and the other 'missing' one)
If you break after constructing combine in coordinates_lst_and_str_lst, you will find that [((1, 0), 'D'), ((0, 1), 'O'), ((1, 2), 'G')] is not in combine, this means coordinates_lst_and_str_lst can't find it as a solution.
So, the problem must be in create_dict, which apparently isn't creating all the legal moves.
And indeed, in create_dict, you use itertools.combinations(), which gives you all the unique combinations of n items from a collection, disregarding their order, but you care about the order.
So, you don't want itertools.combinations(new_dict.items(), n), you want itertools.permutations(new_dict.items(), n). Have a closer look at the difference between combinations and permutations (of size n).
Related
I want to be able to find all the different ways a set of numbers(x) can be summed into a certain value, y but I'm having trouble even getting the base case right.
For example:
If I have x = set (1,2,3,4,5) and I want to see how many different ways y = 5 can be summed up using numbers from x:
my recursive function would return 7 because:
'''
5
4+1
3+2
3+1+1
2+2+1
2+1+1+1
1+1+1+1+1
'''
def recur(x,y):
if y == x:
v += 1
if y > x:
v += 0
else:
#call recursively
This does not use recursion, but itertools.combinations_with_replacement:
def all_combs(y, x=range(1, 5+1)):
all_combs = []
for i in range(1, y+1):
combs = combinations_with_replacement(x, i)
all_combs.extend([comb for comb in combs if sum(comb) == y])
return all_combs
combs = all_combs(5)
# [(5,), (1, 4), (2, 3), (1, 1, 3), (1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1)]
num_combs = len(combs) # 7
Hello i was wonder how to create list of integer / tuples for a list of positions for string slicing.
same example as in title just make a list of this format:
(1, (2, 3), 4, (5, 6))
# my current attempt:
str_input, decrypted_str = "tester", ""
num_lists = [[x] + [(x + 1, x + 2)] for x in range(0, len(str_input), 4)]
for clist in num_lists:
for position in clist:
if isinstance(position, int):
decrypted_str += str_input[position]
else:
decrypted_str += str_input[position[0]:position[1]+1]
print(decrypted_str)
this results in "teser", but output should be tester.
The line before the last one should be
decrypted_str += str_input[position[0]:position[1]+1]
because 1:5 means 1,2,3,4 but not 5
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have an array that contains strings, like this:
l = ["abc",
"def",
"hij",
"klm",
"nop",
"qrs"]
and another one with words:
word = ["abc","knq","knop"]
What I need to find the word in the list and return the respecting coordinates.
The particularity is that the searching must be in horizontal or vertical or together.
For examples the first word:
abc return the index [(0,0)(0,1)(0,2)]
knq return [(3,0)(4,0)(5,0)]
knop return [(3,0)(4,0),(4,1),(4,2)]
The char in the string are not unique and I need to save the movement for example move one char down or move one char right.
is not a diagonal word finder.
I'm guessing characters have to be directly connected. You save the indices of each character in l in a dict, where key is the character and value is a list of indices tuples. Then you could loop through the words in word and do something like DFS from each position in the list of word's first character (going right or down). An example (follow the comments):
l = ["abc",
"def",
"hij",
"klm",
"nop",
"qrs"]
word = ["abc","knq","knop"]
# save the indices of each character
indices = {}
for i, w in enumerate(l):
for j, c in enumerate(w):
indices[c] = indices.get(c, []) + [(i, j)]
def check(i, j, k, cur, w, tmp):
# if current word matches return the list of indices
if cur == w:
return tmp
# otherwise return false if we reach either end of l
if i == len(l) or j == len(l[i]):
return False
# if the current position is inside l and the character maches the k'th character of w
if l[i][j] == w[k]:
# add the current character and appent it's position to tmp
cur += l[i][j]
tmp.append((i, j))
# check the next position either right or down
chk1 = check(i+1, j, k+1, cur, w, tmp)
if (chk1):
return chk1
chk2 = check(i, j+1, k+1, cur, w, tmp)
if (chk2):
return chk2
return False
# loop through each word and then through each position of the first character
for w in word:
for idx in indices[w[0]]:
# if word is found print the indices and break
chk = check(idx[0], idx[1], 0, '', w, [])
if chk:
print(w, chk)
break
output
abc [(0, 0), (0, 1), (0, 2)]
knq [(3, 0), (4, 0), (5, 0)]
knop [(3, 0), (4, 0), (4, 1), (4, 2)]
If you have a lot of words and build those type of list repeatedly, I would build a translation dictionary first:
tr = dict()
for r,w in enumerate(l):
for c,ch in enumerate(w):
tr[ch] = (r,c)
Having that, you can easily create the lists with a list comprehension:
for w in word:
res = [tr[ch] for ch in w]
print(w)
print(res)
OUTPUT:
abc
[(0, 0), (0, 1), (0, 2)]
knq
[(3, 0), (4, 0), (5, 0)]
knop
[(3, 0), (4, 0), (4, 1), (4, 2)]
Having a string='january' ,
how can I generate following cases:
case1(Replacing 1 character) => taking j and replace it with all ASCII letters(a-z). then do the same with: a , n , u , a , r , y.
Basically we would have
(Aanuary , Banuary ,..... ,Zanuary )+ (jAnuary , jBanuary .....jZanuary) + ....+(januarA , januarB , ....., januarZ)
I have done this part using following code, However, I have no idea how to do it for more than one letter since there are lots of permutations.
monthName= 'january'
asci_letters = ['a' , 'b' , .... , 'z']
lst = list(monthName)
indxs = [i for i , _ in enumerate(monthName)]
oneLetter=[]
for i in indxs:
word = monthName
pos = list(word)
for j in asci_letters:
pos[i] = j
changed = ("".join(pos))
oneLetter.append(changed)
Case2: Taking 2 characters and replacing them:
(AAnuary , ABnuary ,.....,AZanuary) + (BAnuary , BBanuary, .... , BZanuary) + (AaAuary , AaBuary,.....,AaZuary) + ...... + (januaAB , .... , januaAZ)
Case3 : doing the same for 3 characters
Case7: doing the same for 7 characters(length of string)
To summarize, I want to create all possible cases of replacing, 1 letter, 2 letters,3 letters, up to all letters of a string.
It's very likely that you can't hold all these permutations in memory because it will quickly become very crowded.
But to get all indices for the cases you can use itertools.combinations. For 1 it will give the single indices:
from itertools import combinations
string_ = 'january'
length = len(string_)
print(list(combinations(range(length), 1)))
# [(0,), (1,), (2,), (3,), (4,), (5,), (6,)]
Likewise you can get the indices for case 2-7:
print(list(combinations(range(length), 2)))
# [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 2), (1, 3), (1, 4),
# (1, 5), (1, 6), (2, 3), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6),
# (4, 5), (4, 6), (5, 6)]
Then it's just a matter of inserting the itertools.product of string.ascii_uppercase at the given indices:
from itertools import product
import string
print(list(product(string.ascii_uppercase, repeat=1)))
# [('A',), ('B',), ('C',), ('D',), ('E',), ('F',), ('G',), ('H',), ('I',),
# ('J',), ('K',), ('L',), ('M',), ('N',), ('O',), ('P',), ('Q',), ('R',),
# ('S',), ('T',), ('U',), ('V',), ('W',), ('X',), ('Y',), ('Z',)]
Likewise for different repeats given the "case".
Putting this all together:
def all_combinations(a_string, case):
lst = list(a_string)
length = len(lst)
for combination in combinations(range(length), case):
for inserter in product(string.ascii_uppercase, repeat=case):
return_string = lst.copy()
for idx, newchar in zip(combination, inserter):
return_string[idx] = newchar
yield ''.join(return_string)
Then you can get all desired permutations for each case by:
list(all_combinations('january', 2)) # case2
list(all_combinations('january', 4)) # case4
list(all_combinations('january', 7)) # case7
Or if you need all of them:
res = []
for case in [1, 2, 3, 4, 5, 6, 7]:
res.extend(all_combinations('january', case))
But that will require a lot of memory.
You can use itertools.combinations_with_replacement for this, which gives you an iterator with all permutations:
from itertools import combinations_with_replacement
# First Param is an iterable of possible values, second the length of the
# resulting permutations
combinations = combinations_with_replacement('ABCDEFGHIJKLMNOPQRSTUVWXYZ',7)
# Then you can iterate like this:
for combination in combinations:
#Do Stuff here
Don't try to convert this iterator to a list of all values, because you probably gonna get a MemoryException.
For your distance you might want to use python distance package. (You need to install it via pip first).
For your case, that you want to get all combinations for Characters a-z with length = 7 (because of January):
import distance
from itertools import combinations_with_replacement
str_to_compary_with = "JANUARY"
for i in range(len(str_to_compare_with):
combinations = combinations_with_replacement('ABCDEFGHIJKLMNOPQRSTUVWXYZ', i+1)
# Then you can iterate like this:
for combination in combinations:
# This is calculating the hamming distance for the combination with the string you want to compare to
# Here you have to figure out yourself if you want to save that output to a file or whatever you wanna do with the distance
hamming_dist = distance.hamming(''.join(combination), str_to_compare_with)
This should do everything that you wanted with help of product and permutations:
from itertools import product, permutations
monthName= 'january'
letters = list('abcdefghijklmnopqrstuvwxyz')
n = len(monthName)
indxs = range(n)
mn = list(monthName)
cases = {k: [] for k in range(2, n+1)}
for num in range(2, n+1):
letter_combos = list(product(*[letters for _ in range(num)]))
positions = permutations(indxs, num)
for p in positions:
for l in letter_combos:
l = iter(l)
for i in p:
mn[i] = next(l)
mn = ''.join(mn)
cases[num].append(mn)
mn = list(monthName)
If you want to know how it is working, you can test this with a subset of letters, say from A-F:
x = []
for i in range(65,70): #subset of letters
x.append(chr(i))
def recurse(string,index,arr):
if(index>len(string)-1):
return
for i in range(index,len(string)):
for item in x:
temp = string[:i]+item+string[i+1:]
arr.append(temp)
recurse(temp,i+1,arr)
arr = []
recurse('abc',0,arr)
print arr
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Problem
I have to get elements from a text file to a list, diagonally, and from top to buttom. It should work on any dimension of the letters.txt. The file would look like this:
Text file: letters.txt (thought it would be hard, I removed 'Y', and 'Z' from my original post
A B C D E F
G H I J K L
M N O P Q R
S T U V W X
the lists should look like this:
topButtom_List = ['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
bLeftToURight = ['A', 'GB', 'MHC', 'SNID', 'TOJE', 'UPKF', 'VQL', 'WR', 'X']
My current code for top to buttom:
# top to buttom
topButtom_List = [] #should be ['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
openFile = open("letters.txt")
for i in openFile:
i = i.replace(" ","")
length = len(i)
openFile.close()
openFile = open("letters.txt")
counter = 0
for eachIterration in range(length):
for line in openFile:
line = line.replace(" ","")
# counter should be added by 1 each time inner loop itterates x4, and outter loop x1.
topButtom_List.append(line[counter])
counter = counter + 1
openFile.close()
What I was trying to do with the code above:
I was trying to get the top to buttom characters from the text file and get it in a list called topButtom_List. I used counter to define the index that for every iteration the outer loop does, the index would be added by 1. The way I see it is, the outerloop will start, the inner loop will iterate x4 adding AGMS in the topButtom_List on the first iteration, the outer loop will iterate again and add 1 to counter. BHNTZ will be added on the second iteration and so on, the outer loop will iterate again and add 1 to counter.
From the text file: letters.txt
I want to populate topButtom_List
Output I am getting:
['A', 'G', 'M', 'S']
Expected output:
['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
#!/usr/bin/python3
field = """A B C D E F
G H I J K L
M N O P Q R
S T U V W X"""
arr = [col.split(' ') for col in [row.strip() for row in field.split('\n')]]
len_x, len_y = len(arr[0]), len(arr)
len_all = len_x + len_y - 1
lines, groups = [], []
for i in range(len_all):
start = (i, 0) if i < len_y else (len_y-1, i-len_y+1)
end = (0, i) if i < len_x else (i-len_x+1, len_x-1)
lines.append([start, end])
print('List of start and end points: ', lines)
for start, end in lines:
group = ''
for i in range(len_x):
y, x = start[0] - i, start[1] + i
if y >= 0 and y < len(arr) and x < len(arr[y]):
group += arr[y][x]
else:
groups.append(group)
break
print(groups)
Returns
List of start and end points: [[(0, 0), (0, 0)], [(1, 0), (0, 1)],
[(2, 0), (0, 2)], [(3, 0), (0, 3)], [(3, 1), (0, 4)], [(3, 2), (0, 5)],
[(3, 3), (1, 5)], [(3, 4), (2, 5)], [(3, 5), (3, 5)]]
and
['A', 'GB', 'MHC', 'SNID', 'TOJE', 'UPKF', 'VQL', 'WR', 'X']