So say I've got a 2D array like:
[
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 2],
[0, 2, 2, 0, 2],
[1, 1, 2, 1, 1],
]
And I want the two players (player 1 is represented by 1 and player 2 is represented by 2) to be able to place counters down the columns (like connect 4). How can I find the next available row (if there are any)?
I can parse the column number
Thanks!
table = [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 2],
[0, 2, 2, 0, 2],
[1, 1, 2, 1, 1],
]
def move(col, player, table):
available_rows = [i for i in range(len(table)) if table[i][col]==0]
if available_rows == []:
return False # i.e. you can not place on column col. invalid move
target_row = available_rows[-1] # target_row is the next available move on column col
table[target_row][col] = player
return True # i.e. valid move
print(table)
move(0, 2, table) # player 2 has placed its move on column 0 (zero based index)
print(table)
Output
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 2],
[0, 2, 2, 0, 2],
[1, 1, 2, 1, 1]
]
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 2],
[1, 2, 2, 0, 2],
[1, 1, 2, 1, 1]]
You have to check whether the output of move is true or not, to make sure the move is valid.
You can use a for loop to loop through the array, and check if the current row has any zero in it.
for row in array:
if row.count(0) > 0:
# this row has available slots
# Do something here
break
else:
# No free slot at all for all rows
# Do something here
I think I've found a solution:
def findNextRow(self, rowNumber):
for rowIndex in range(len(self._grid), 0, -1):
if self._grid[rowNumber][rowIndex] == 0:
return rowIndex-1
return False
You loop backwards through the 2d array. If the column number you parsed through and the current row is 0, then return the index-1 (since python array indexing starts at 0). If no row can be found, the loop will end without returning anything so you can return False.
Related
the problem that I am facing is simple. I have matrix as list of lists like this:
[[0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 1, 1, 1, 0], [0, 0, 1, 0, 1, 0, 0]]
And I want to get is tuple that has grouped occurrences of 1 for rows on one side and for cols on the other. So for the matrix above it would be like this:
([[1], [1, 1, 1], [7], [2, 1], [3], [1, 1]],[[1], [2], [1, 2, 1], [4], [1, 2], [1, 1], [3]])
As you can see it "sums" adjascent duplicates so list like this [1,0,1,1] would resolve in [[1],[0],[2]]
I tried working on the rows and ended up with this:
for line in matrix:
new_list = []
for value in line:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
result_list.append(new_list)
This splits the list into duplicates but does not sum the adjascent and still leaves 0 in the result.
Does anyone have good solution for both rows and colls to achieve my desired Tuple?
PS: I know i could use itertools.groupby but I am trying to solve it without it.
Assuming your data is in data, here you go:
def grp(data):
result = []
for row in data:
count = 0
row_result = []
for c in row:
if c==0:
if count > 0:
row_result.append(count)
count = 0
else:
count += 1
if count > 0:
row_result.append(count)
result.append(row_result)
return result
data = [[0, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 0, 0, 1],
[0, 0, 0, 1, 1, 1, 0],
[0, 0, 1, 0, 1, 0, 0]]
print((grp(data),grp(zip(*data)))
gives me your desired result (using Python 3).
I have the following dict that I created :
dct = {'T': [[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 1, 0]], 'B': [[0, 1, 0, 0, 0], [0, 0, 0, 0, 1]], 'J': [[0, 0, 1, 0, 0], [0, 0, 0, 0, 1]], 'K': [0, 0, 0, 0, 1]}
Note that each value is either a single list or multiple lists. I am trying to figure out if I can do a conditional zip on the value lists for each key (or some alternative solution that gives me my desired output). The condition is that I only want the value lists to zip together for a given key if the 1s on consecutive value lists are within a distance of 2 index values from each other.
The final output I want from this input dict should look like this:
zp_dict = {'T': [2, 1, 0, 1, 0], 'B': [[0, 1, 0, 0, 0], [0, 1, 0, 0, 0]], 'J': [0, 0, 1, 0, 1], 'K': [0, 0, 0, 0, 1]}
Notice that the value lists for 'T' and 'J' should be zipped together because the non-zero values are no more than 2 indices apart, but the value lists for 'B' should be kept separate because the non-zero values are 3 indices apart, and the 'K' value list should be left alone.
To illustrate what I'm trying to do, consider the following code that almost does what I want to do, but without the condition:
zp_dict = {}
for k, v in dct.items():
zp_dict[k] = [sum(x) for x in zip(*v)]
This produces the following new dict, which is wrong for key 'B':
zp_dict = { 'T': [2, 1, 0, 1, 0], 'B': [0, 1, 0, 0, 1], 'J': [0, 0, 1, 0, 1], 'K': [0, 0, 0, 0, 1]}
This technically achieves the result you are asking for. However, I am assuming you want everything to be added together if at least 2 of the lists have numbers within 2 indexes of each other. For example [[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1]] would result in [1, 1, 0, 0, 1] NOT [[1, 1, 0, 0, 0], [0, 0, 0, 0, 1]] despite the last list having its number more than 2 indexes away from any other list.
The code sums the lists together, then finds how far apart the first 2 non-zero number are. If they are <= 2 indexes apart, then the summed list is added to zp_dict, else the list of lists (v) remains unchanged, and added to the zp_dict
The code is on OnlineGDB, if you want to tinker with it
Note: I assumed 'K' in the dct you provided had a typo, in that it was supposed to be a list within a list (like the others) - if not, sum(x) for x in zip(*v) would break. If not, it doesn't take much to fix - just validate the number of lists in v.
dct = {
'T': [[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 1, 0]],
'B': [[0, 1, 0, 0, 0], [0, 0, 0, 0, 1]],
'J': [[0, 0, 1, 0, 0], [0, 0, 0, 0, 1]],
'K': [[0, 0, 0, 0, 1]]
}
zp_dict = {}
for k, v in dct.items():
sum_list = [sum(x) for x in zip(*v)]
first_non_zero = next((i for i, n in enumerate(sum_list) if n), 0)
second_non_zero = next((i for i, n in enumerate(sum_list[first_non_zero+1:]) if n), 0)
zp_dict[k] = sum_list if second_non_zero < 2 else v
print(zp_dict)
>>{'T': [2, 1, 0, 1, 0], 'B': [[0, 1, 0, 0, 0], [0, 0, 0, 0, 1]], 'J': [0, 0, 1, 0, 1], 'K',: [0, 0, 0, 0, 1]}
EDIT:
You can also add if statements (with functions inline), if that is what you were looking for.
zp_dict[k] = [sum(x) for x in zip(*v) if conditionTest(v)]
If the conditionTest returns True, it would add the lists together. Although if you were fine with adding functions, I would clean it up and just add the for loop into the function:
zp_dict[k] = sumFunction(v)
I need help with diagonal matching in a 2D Array in Python when 3 or more elements are the same.
In the scenario below, the "2"'s formulate a diagonal match. Thus, I want to make them "-2" since they form a match.
Given
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[3, 1, 2, 0, 0], # 2 at index 2
[1, 2, 1, 0, 0], # 2 at index 1
[2, 3, 3, 0, 0]] # 2 at index 0
Result
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[3, 1,-2, 0, 0], # -2 now at index 2
[1, -2, 1, 0, 0], # -2 now at index 1
[-2, 3, 3, 0, 0]] # -2 now at index 0
I presume they are multiple directions for a diagonal match (from the left/right) so I want to cover those cases. Ideally, I want to do this without importing any module.
Thanks (:
Here is something to start from, assuming length of the rows is the same and you want full length diagonals:
#get diagonals, number of diagonals is 2 * additional rows, where
#multiplication by 2 is for left and right diagonal
diagonals = [[] for asymmetry in range((len(test)%len(test[0])+1)*2)]
for i, diagonal in enumerate(diagonals):
for rowidx in range(len(test[0])):
#if left diagonal
if i % 2 == 0:
diagonal.append(test[i//2+rowidx][rowidx])
#if right diagonal
elif i % 2 == 1:
diagonal.append(test[i//2+rowidx][len(test[0])-rowidx-1])
#Now check for same values in diagonals
for i, diagonal in enumerate(diagonals):
for value in set(diagonal):
#indeces of repeating values:
valueidx = [j for j, val in enumerate(diagonal) if val==value]
#if value repeats and is not 0 and that they're ordered:
if len(valueidx) > 2 and value is not 0 and all(a-1 == b for a, b in zip(valueidx[1:], valueidx[:-1])):
for idx in valueidx:
#case of left diagonal
if i % 2 == 0:
test[i//2 + idx][idx] *=-1
#case of right diagonal
if i % 2 == 1:
test[i//2 + idx][len(test[0])-idx-1] *=-1
It should be possible to simplify this code by a lot, but here is something to start from. Using numpy would simplify the code by a lot, using numpy.diag and simple expressions for replacing the values.
test = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[3, 1, 2, 0, 0],
[1, 2, 1, 0, 0],
[2, 3, 3, 0, 0],
[0, 1, 3, 0, 1]]
after running above script:
test
[[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[ 3,-1,-2, 0, 0],
[ 1,-2,-1, 0, 0],
[-2, 3, 3, 0, 0],
[ 0, 1, 3, 0,-1]]
def FitnessSelection(population, relative_fitness):
selected_chromosomes = []
selected1 = random.choices(population, weights = relative_fitness, k = 1)
ind1 = selected1[0][1][0]
del relative_fitness[ind1]
del population[ind1]
selected2 = random.choices(population, weights = relative_fitness, k = 1)
ind2 = selected2[0][1][0]
del relative_fitness[ind2]
del population[ind1]
selected = selected1, selected2
selected_chromosomes = [i[0] for i in selected]
return (selected_chromosomes)
I am trying to perform a random weighted selection from a list, however, I cant have the same item selected twice, therefore I am trying to exclude the selected items immediately after selection so it won't be in the population in the list for the next run of the function.
The problem is that the program is running but I don't think the items are being excluded
I have the items in the following structure
population=[[[0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1], [4], [0]],
[[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1], [3], [1]],
[[0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0], [4], [2]],
[[1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0], [1], [3]]]
Where the last brackets of each string is an index counter. I am trying to isolate the counter and exclude the item in the same position, but when I check the population afterward, they were not excluded.
Does anyone know what could be wrong with this approach?
p.s. - I'm sorry for not posting a standalone program, I couldn't make it work in a small sample like this.
Thanks!
I'd like to find a value in a numpy array given a search pattern. For instance for the given array a, I want to retrieve a result of 1 when using the search pattern s because 1 is the element at index 0 of a[:,1] (=array([1, 0, 0, 1])) and the elements of a[1:,1] match s (i.e. (a[1:,1] == s).all() == True => return a[0,1]).
Another example would be s=[1, 0, 1] for which I would expect a search result of 2 (match at 4th column starting (1-based)). 2 would also be the search result for s=[2, 0, 0], etc.
>>> import numpy as np
>>> a = np.asarray([[0, 1, 2, 2, 2, 2, 2, 2], [0, 0, 1, 1, 2, 2, 3, 3], [0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1]])
>>> a
array([[0, 1, 2, 2, 2, 2, 2, 2],
[0, 0, 1, 1, 2, 2, 3, 3],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 1]])
>>> s = np.asarray([0, 0, 1])
I came up with a[0, np.where((a[1:,:].transpose() == s).all(axis=-1))[0][0]], but thought there must be something more elegant...
Additionally, it would be great if I could do this operation with one call on multiple search patters, so that I retrieve the 0-element for which the values of index 1 to index 3 match.
Single search pattern
Here's one approach with help from broadcasting and slicing -
a[0,(a[1:] == s[:,None]).all(0)]
Multiple search patterns
For multiple search patterns (stored as 2D array), we just need to broadcast as before and look for ANY match at the end -
a[0,((a[1:] == s[...,None]).all(1)).any(0)]
Here's a sample run -
In [327]: a
Out[327]:
array([[0, 1, 2, 2, 2, 2, 2, 2],
[0, 0, 1, 1, 2, 2, 3, 3],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 1]])
In [328]: s
Out[328]:
array([[1, 0, 1],
[2, 0, 0]])
In [329]: a[0,((a[1:] == s[...,None]).all(1)).any(0)]
Out[329]: array([2, 2])