Related
I'm trying create custom shapes using the Tetris technique. For now I have 2 approaches:
L = [['.....',
'...0.',
'.000.',
'.....',
'.....'],
['.....',
'..0..',
'..0..',
'..00.',
'.....'],
['.....',
'.....',
'.000.',
'.0...',
'.....'],
['.....',
'.00..',
'..0..',
'..0..',
'.....']]
L = [[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]]
Is there any more efficient way to create grid? Since I got more than 10 of those and I'll have to create at most 80 of nested lists(10 * 4directions * 2 mirrored)
I prefer to use matrices as follows. It will contain a list of all the rotations of a tetris block.
I_Tetromino = [
[
[1, 1, 1, 1]
],
[
[1],
[1],
[1],
[1]
]
]
J_Tetromino = [
[
[0, 1],
[0, 1],
[1, 1]
],
[
[1, 0, 0],
[1, 1, 1]
],
[
[1, 1],
[1, 0],
[1, 0]
],
[
[1, 1, 1],
[0, 0, 1]
]
]
You could make a class of block, then draw the block on the grid by its color.
Then you can render it on your screen.
This may not be the most efficient method but I prefer to use a method which is not very complicated and readable.
Edit: You can make a function also to rotate it.
def rot90(matrix):
return list(zip(*matrix[::-1]))
def rot180(matrix):
return rot90(rot90(matrix))
def rot270(matrix):
return rot90(rot180(matrix))
you could start with the basic shapes and build a list by applying rotations and flips to them and keeping only distinct resulting shapes:
def rot(M,n):
return rot(list(map(list,zip(*reversed(M)))),n-1) if n else M
L = [ [1,1,1],
[0,0,1] ]
I = [ [1,1,1,1] ]
S = [ [0,1,1],
[1,1,0] ]
T = [ [1,1,1],
[0,1,0] ]
B = [ [1,1],
[1,1] ]
blocks = [ rot(fb,r) for b in (L,I,S,T,B)
for fb in (b,b[::-1])
for r in range(4) ]
blocks = [ b for i,b in enumerate(blocks) if blocks.index(b)==i ] # distincts
output:
def printShape(M):
for r in M: print(r)
for block in blocks:
printShape(block)
print()
[1, 1, 1]
[0, 0, 1]
[0, 1]
[0, 1]
[1, 1]
[1, 0, 0]
[1, 1, 1]
[1, 1]
[1, 0]
[1, 0]
[0, 0, 1]
[1, 1, 1]
[1, 0]
[1, 0]
[1, 1]
[1, 1, 1]
[1, 0, 0]
[1, 1]
[0, 1]
[0, 1]
[1, 1, 1, 1]
[1]
[1]
[1]
[1]
[0, 1, 1]
[1, 1, 0]
[1, 0]
[1, 1]
[0, 1]
[1, 1, 0]
[0, 1, 1]
[0, 1]
[1, 1]
[1, 0]
[1, 1, 1]
[0, 1, 0]
[0, 1]
[1, 1]
[0, 1]
[0, 1, 0]
[1, 1, 1]
[1, 0]
[1, 1]
[1, 0]
[1, 1]
[1, 1]
Note that defining these blocks as variable size 2D masks should make it much easier to implement block rendering, movements and collision detection in the game later on.
I need to find all the possible combinations of a list with a defined length and numbers of defined size, but subject to a restriction that does not allow having the same repeated number in the list (except 0).
Currently I have a recursive code that generates all the combinations, but when trying to create the constraints I lose many options.
I have tried the options that appear commented to solve it, but it still does not work for me. Thank you in advance for your help.
size=3
l=[0 for x in range(size)]
def recursive(l, index, num, final):
if index == len(l) or num == final:
return
# if num!=0 and num in l:
# return
l[index]=num
print(l)
recursive(l, index+1, 0, final)
recursive(l, index, num+1, final)
recursive(l, 0, 0, 4)
the expected output for a list of size 3 and final number 3 would be something like this
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 0]
[0, 1, 2]
[0, 1, 3]
[0, 2, 0]
[0, 2, 1]
[0, 2, 3]
[0, 3, 0]
[0, 3, 1]
[0, 3, 2]
[1, 0, 0]
[1, 0, 2]
[1, 0, 3]
[1, 2, 0]
[1, 2, 3]
[1, 3, 0]
[1, 3, 2]
[2, 0, 0]
[2, 0, 1]
[2, 0, 3]
[2, 1, 0]
[2, 1, 3]
[2, 3, 0]
[2, 3, 1]
[3, 0, 0]
[3, 0, 1]
[3, 0, 2]
[3, 1, 0]
[3, 1, 2]
[3, 2, 0]
[3, 2, 1]
Start with the powerset of the non-zero values (a function for computing the powerset of a list can be found in the itertools documentation.)
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
>>> list(powerset([1,2,3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
(You could compute the power set including 0, but you would still need to zero-pad the shorter elements, as well as discard (0,1,2,3)).
Each element of the powerset should next be padded with zeros so that they are all the same length.
Finally, you want the list of permutations (using itertools.permutations) of each 0-padded permutation of the original input.
Use itertools.combinations_with_replacement(), and then filter out the elements with duplicates.
def has_duplicates(l):
'''Return True if list has duplicate elements other than 0'''
l = [x in l if x != 0]
return len(l) != len(set(l))
result = [comb for comb in itertools.combinations_with_replacement(l, 3) if not has_duplicates(comb)]
You can make this lazy by using itertools.filterfalse(); this way you won't fill up memory with all combinations of a large list.
result = itertools.filterfalse(has_duplicates, itertools.combinations_with_replacement(l, 3))
Here is a solution based on counters, I am using a base 4 counter:
def charcnt(s, a):
ctr = [1 if x == a else 0 for x in s ]
return sum(ctr)
def my_range(start,end,base,step=1):
def Convert(n,base):
string = "0123"
if n < base:
return string[n]
else:
return Convert(n//base,base) + string[n%base]
return (Convert(i,base) for i in range(start,end,step))
# base-10 58 will produce base-4 321
counter = [x.zfill(3) for x in my_range(0, 58, 4)]
result = []
for s in counter:
if charcnt(s, '1') > 1 or charcnt(s, '2') > 1 or charcnt(s, '3') > 1:
continue
else:
result.append(s)
result = [[int(n) for n in list(s)] for s in result]
#print(len(result))
for i in result:
print(i)
Will produce:
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 0]
[0, 1, 2]
[0, 1, 3]
[0, 2, 0]
[0, 2, 1]
[0, 2, 3]
[0, 3, 0]
[0, 3, 1]
[0, 3, 2]
[1, 0, 0]
[1, 0, 2]
[1, 0, 3]
[1, 2, 0]
[1, 2, 3]
[1, 3, 0]
[1, 3, 2]
[2, 0, 0]
[2, 0, 1]
[2, 0, 3]
[2, 1, 0]
[2, 1, 3]
[2, 3, 0]
[2, 3, 1]
[3, 0, 0]
[3, 0, 1]
[3, 0, 2]
[3, 1, 0]
[3, 1, 2]
[3, 2, 0]
[3, 2, 1]
Task
I have an array with sizes
sizes = [5, 3, 1, 2]
and based on the sizes I want to create the following array
mapping = [0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Solution
My first attempt
mapping = []
ctr = 0
for i in range(len(sizes)):
for j in range(sizes[i]):
mapping.append(i)
ctr += 1
Shorter version
mapping = [[i for _ in range(sizes[i])] for i in range(len(sizes))]
mapping = list(itertools.chain(*mapping))
Question
One line version?
Is it possible to do it in just one line with a neat code?
Using enumerate
Ex:
sizes = [5, 3, 1, 2]
result = [i for i, v in enumerate(sizes) for _ in range(v)]
print(result)
Output:
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Another approach would be to multiply the indices into sublists [[0, 0, 0, 0, 0], [1, 1, 1], [2], [3, 3]]
then flatten the result with itertoo.chain.from_iterable:
>>> from itertools import chain
>>> sizes = [5, 3, 1, 2]
>>> list(chain.from_iterable([i] * x for i, x in enumerate(sizes)))
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
I have a list ls of length n and want to get all lists of length m (where m > n) that contain ls in the same order, plus (m - n) zeroes, inserted at each possible combination of positions.
For example: ls = [1, 2, 3] and m = 4 should return
[[1, 2, 3, 0],
[1, 2, 0, 3],
[1, 0, 2, 3],
[0, 1, 2, 3]]
and ls = [1, 2, 3] and m = 5 should return
[[1, 2, 3, 0, 0],
[1, 2, 0, 3, 0],
[1, 2, 0, 0, 3],
[1, 0, 2, 3, 0],
[1, 0, 2, 0, 3],
[1, 0, 0, 2, 3],
[0, 1, 2, 3, 0],
[0, 1, 2, 0, 3],
[0, 1, 0, 2, 3],
[0, 0, 1, 2, 3]]
The solution should be fast and memory efficient - in particular, it should avoid generating duplicate solutions. Any help is appreciated!
A working (but inefficient) attempt:
ls = [1, 2, 3]
m = 4
from itertools import permutations
n = len(ls)
results = []
for t in set(permutations('1' * n + '0' * (m - n))):
idxs = [i for i, j in enumerate(t) if j == '1']
result = [0] * m
for idx, value in zip(idxs, ls):
result[idx] = value
results.append(result)
Use itertools.combinations to generate every combination of places to insert zeroes. Then use a list comprehension to select 0 or the next original element to build the new list.
# Pad list orig with zeroes, out to "m" total elements.
from itertools import combinations
orig = [1, 2, 3]
m = 5
n = len(orig)
padded = []
for pad_idx in combinations(range(m), m-n):
t = orig[:]
padded.append( [0 if i in pad_idx else t.pop(0)
for i in range(m)] )
print(padded)
Output (formatted for readability):
[[0, 0, 1, 2, 3],
[0, 1, 0, 2, 3],
[0, 1, 2, 0, 3],
[0, 1, 2, 3, 0],
[1, 0, 0, 2, 3],
[1, 0, 2, 0, 3],
[1, 0, 2, 3, 0],
[1, 2, 0, 0, 3],
[1, 2, 0, 3, 0],
[1, 2, 3, 0, 0]]
I'd like to enumerate the solution with a specific order. Currently, with the below code:
def balls_in_baskets(balls=1, baskets=1):
if baskets == 1:
yield [balls]
elif balls == 0:
yield [0]*baskets
else:
for i in range(balls+1):
for j in balls_in_baskets(balls-i, 1):
for k in balls_in_baskets(i, baskets-1):
yield j+k
x=[t for t in balls_in_baskets(3,3)][::-1]
for i in x:
print(i)
I get this:
[0, 0, 3]
[0, 1, 2]
[0, 2, 1]
[0, 3, 0]
[1, 0, 2]
[1, 1, 1]
[1, 2, 0]
[2, 0, 1]
[2, 1, 0]
[3, 0, 0]
However, I would like this order:
[0, 0, 3]
[0, 1, 2]
[1, 0, 2]
[0, 2, 1]
[1, 1, 1]
[2, 0, 1]
[0, 3, 0]
[1, 2, 0]
[2, 1, 0]
[3, 0, 0]
How can I achieve this correct order?
You already lose the memory-efficiency of your generator by consuming it in a list comprehension so you could also sort the result:
x = sorted(balls_in_baskets(3,3), key=lambda x: x[::-1], reverse=True)
which then prints the expected output:
[0, 0, 3]
[0, 1, 2]
[1, 0, 2]
[0, 2, 1]
[1, 1, 1]
[2, 0, 1]
[0, 3, 0]
[1, 2, 0]
[2, 1, 0]
[3, 0, 0]