All possible random tiles in 2048 - python

I am trying to make a function that takes in a 2048 board (nested lists making a 4 by 4 grid), like this,
[[0, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]
Note: I know this isn't a realistic 2048 board, but I chose the example for simplicity
and the function should output a list of all of the places a random tile could appear(either a 2 or a 4)
For example, if I run the function on the board above, the output should be.
[[[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]],
[[4, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]]
So anywhere were there is a 0, we want to make one board where there 0 has been replaced by a 2 and another where it has been replaced by a 4.
Here is my code so far.
def all_rand_tiles(state):
states = []
changed = []
old_states = [None]
while old_states != states:
old_states = states
new_state = state
for row in range(4):
for tile in range(4):
if state[row][tile] == 0:
#AREA WITH PROBLEM BELOW THIS LINE
if [row, tile] not in changed:
new_state[row][tile] = 2
states.append(new_state)
new_state[row][tile] = 4
states.append(new_state)
changed.append([row, tile])
return states
The problem is that when I append the two different states in the final if statement, both states have the zero changed to a 4. So the output of the function if you run it on the board at the top will look like this.
[[[4, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]],
[[4, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]]
When I run new_state[row][tile] = 4 the state I just created is changed for having a 2 in it, to have a 4.
You can see the problem demonstrated if you print new_state before and after running new_state[row][tile] = 4 in the bad code area.
I am incredibly confused by this and I have no idea why I am having this stupid problem.
EDIT: Please try out your solutions before posting. I have given you the only function I am working with all_rand_tiles and so far I have been unable to fix the problem with any of the answers that have been presented.

you must create a copy of state and set it with new_state . because of you have a 2D array you must use deepcopy so change your code to this :
from copy import deepcopy
state=[[0, 0, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]
def all_rand_tiles(state):
states = []
changed = []
old_states = [None]
while old_states != states:
old_states = states
new_state = deepcopy(state)
for row in range(4):
for tile in range(4):
if state[row][tile] == 0:
if [row, tile] not in changed:
new_state[row][tile] = 2
states.append(new_state)
new_state = deepcopy(state)
new_state[row][tile] = 4
states.append(new_state)
new_state = deepcopy(state)
changed.append([row, tile])
return states
print all_rand_tiles(state)
DEMO:
[[[2, 0, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]],
[[4, 0, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]],
[[0, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]
[[0, 4, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]]]

The problem is that when you call states.append(new_state) you are appending a reference to new_state to the list, not a copy. Hence, any modifications made to the new_state object will affect all references to it. In order to get the behavior you want, you need to make your modification to new state and then append a copy of the list with that state to the new_states object. There are several ways to do this:
states.append(new_state[:]) # slice the list
states.append(copy.copy(new_state)) # use the copy function
states.append(list(new_state)) # explicitly construct a new list

I don't know Python specifically, but I've been programming about 20 years so my answer might actually make sense. You append the array twice, in the following code:
if [row, tile] not in changed:
new_state[row][tile] = 2
states.append(new_state) <--- appended the first time here
new_state[row][tile] = 4
states.append(new_state) <--- appended the second time here
changed.append([row, tile])
Is it appending the new_state array itself, or just a REFERENCE to the new_state array? If it's a reference to the new_state array, then both instances will be the same, since both references are referring to the latest version of new_state (which has a "4" in that row & tile).
If that's the cause, then the solution is to make an actual copy / clone of the array, or just copy each of the values in new_state individually to a new array, before you use states.append.

I believe you're running into the same issue as found in the question Strange behavior of python's append? . You're appending the same new_state twice, modifying the single copy each time.

Related

Python: list.remove() behave wiredly [closed]

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 10 months ago.
Improve this question
def get_variables(cells):
domains = [1,2,3,4]
variables = {}
for cell in cells:
if(cell == "C11"):
variables[cell] = [1]
elif(cell == "C22"):
variables[cell] = [2]
elif(cell == "C33"):
variables[cell] = [3]
elif(cell == "C44"):
variables[cell] = [4]
else:
variables[cell] = domains
cells = ['C'+x+y for x in "1234" for y in "1234"]
variables = get_variables(cells)
csp = CSP(variables, constraints, assigned)
pprint(csp.variables)
csp.variables["C12"].remove(1)
print(csp.variables["C13"])
output:
{'C11': [1],
'C12': [1, 2, 3, 4],
'C13': [1, 2, 3, 4],
'C14': [1, 2, 3, 4],
'C21': [1, 2, 3, 4],
'C22': [2],
'C23': [1, 2, 3, 4],
'C24': [1, 2, 3, 4],
'C31': [1, 2, 3, 4],
'C32': [1, 2, 3, 4],
'C33': [3],
'C34': [1, 2, 3, 4],
'C41': [1, 2, 3, 4],
'C42': [1, 2, 3, 4],
'C43': [1, 2, 3, 4],
'C44': [4]}
[2, 3, 4]
It is supposed to remove 1 from "C12", instead it did it to "C13". Why is that? I guess something related to memory location? This really drives me crazy. Any suggestion would be appreciated!
I suppose that "C12" and "C13" don't contain different lists, but the same one. Meaning that no matter how you modify either one, the other will be affected in the same way.
To see this clearly, print csp.variables at the end instead of just "CSP12".
The mistake is this line:
variables[cell] = domains
Because every variable[cell] is given the same list. Which is a mutable type. So when it is modified, it will be modified in place.
To fix this you could give the cells copies or slices, which are different objects:
variables[cell] = domains.copy()
or
variables[cell] = domains[:]
try this
desired_key = 'C12'
for key in csp.keys():
if key == desired_key:
del csp[key][0]
print(csp)
``

How to weave two lists using recursion in Python

I want to weave two lists and output all the possible results.
For example,
input: two lists l1 = [1, 2], l2 = [3, 4]
output: [1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [3, 1, 2, 4], [3, 1, 4, 2], [3, 4, 1, 2]
Note: I need to keep the order in each list (e.g. 1 is always before 2, and 3 is always before 4)
The way I am solving this is by removing the head from one list, recursing, and then doing the same thing with the other list. The code is below:
all_possibles = []
def weaveLists(first, second, added):
if len(first) == 0 or len(second) == 0:
res = added[:]
res += first[:]
res += second[:]
all_possibles.append(res)
return
cur1 = first[0]
added.append(cur1)
first = first[1:]
weaveLists(first, second, added)
added = added[:-1]
first = [cur1] + first
cur2 = second[0]
added.append(cur2)
second = second[1:]
weaveLists(first, second, added)
added = added[:-1]
second = [cur2] + second
weaveLists([1, 2], [3, 4], [])
print(all_possibles)
The result I got is:
[[1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [1, 3, 1, 2, 4], [1, 3, 1, 4, 2], [1, 3, 1, 4, 1, 2]]
I couldn't figure out why for the last three lists, the heading 1 from the first list is not removed.
Can anyone help? Thanks!
The reason you get those unexpected results is that you mutate added at this place:
added.append(cur1)
...this will affect the caller's added list (unintentionally). While the "undo" operation is not mutating the list:
added = added[:-1]
This creates a new list, and therefore this "undo" action does not roll back the change in the list of the caller.
The easy fix is to replace the call to append with:
added = added + [cur1]
And the same should happen in the second block.
It is easier if you pass the new values for the recursive call on-the-fly, and replace those two code blocks with just:
weaveLists(first[1:], second, added + [first[0]])
weaveLists(first, second[1:], added + [second[0]])
Here is another way to do it: we generate the possible indices of the items of the first list inside the weaved list, and fill the list accordingly.
We can generate the indices with itertools.combinations: it's the combinations of the indices of the weaved list, taking len(first_list) of them each time.
from itertools import combinations
​
def weave(l1, l2):
total_length = len(l1) + len(l2)
# indices at which to put items from l1 in the weaved output
for indices in combinations(range(total_length), r=len(l1)):
out = []
it1 = iter(l1)
it2 = iter(l2)
for i in range(total_length):
if i in indices:
out.append(next(it1))
else:
out.append(next(it2))
yield out
Sample run:
l1 = [1, 2]
l2 = [3, 4]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4]
[1, 3, 2, 4]
[1, 3, 4, 2]
[3, 1, 2, 4]
[3, 1, 4, 2]
[3, 4, 1, 2]
Another sample run with a longer list:
l1 = [1, 2]
l2 = [3, 4, 5]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4, 5]
[1, 3, 2, 4, 5]
[1, 3, 4, 2, 5]
[1, 3, 4, 5, 2]
[3, 1, 2, 4, 5]
[3, 1, 4, 2, 5]
[3, 1, 4, 5, 2]
[3, 4, 1, 2, 5]
[3, 4, 1, 5, 2]
[3, 4, 5, 1, 2]

How do I account for repetition within list elements

The objective of the parent program is to generate permutations and find out with what probability they are generated by the generating process. I would like help in sorting identical permutations through the list.
So each element in the list is of the form [[[a,b,c],[x]],[[d,e,f],[y]],....] where the x represents the probability with which [a,b,c] is generated in one loop. I have one of my outputs below.
[[[1, 2, 3], [0.125]],[[1, 2, 3], [0.125]],[[2, 3, 1], [0.125]],[[2, 1, 3], [0.125]],[[3, 1, 2], [0.125]],[[1, 2, 3], [0.125]],[[1, 3, 2], [0.125]],[[1, 2, 3], [0.125]]]
When my final list contains [[a,b,c],[x]] and [[a,b,c],[y]], I will need it to become [[a,b,c],[x+y]]. How do I do this?
I tried the following code. The output list is called master_list.
for i in range(0,len(master_list)):
count = []
for j in range(i+1,len(master_list)):
if master_list[i][0] == master_list[j][0]:
count.append(j)
master_list[i][1][0] += master_list[j][1][0]
else:
continue
print('count for element',master_list[i][0], 'at number', i, 'is', count)
for k in count:
print(k)
for c in range(0,len(count)):
master_list.remove(master_list[k])
print('master_list now is',master_list)
count[c] += -1
The print statements were only for me to check. However, the problem I faced was that after every .remove() operation, the indices of the list(s) changed so the loop broke down.
Instead of iterating the original list removing element from there (list.remove() is O(n) [ref]), a more efficient way is to pass through an intermediary dictionary for then creating again a list.
from collections import defaultdict
res = [[[1, 2, 3], [0.125]], [[1, 2, 3], [0.125]], [[2, 3, 1], [0.125]], [[2, 1, 3], [0.125]], [[3, 1, 2], [0.125]],
[[1, 2, 3], [0.125]], [[1, 3, 2], [0.125]], [[1, 2, 3], [0.125]]]
grouped_res = defaultdict(float)
for perm, prob in res:
grouped_res[tuple(perm)] += prob[0]
new_res = [[list(k), [v]] for k, v in grouped_res.items()]
# [[[1, 2, 3], [0.5]], [[2, 3, 1], [0.125]], [[2, 1, 3], [0.125]], [[3, 1, 2], [0.125]], [[1, 3, 2], [0.125]]]

In the for-loop, when I use 'append' to add a new element to the list, the one that has added before also change

I want to get transpose of matrix B without using Numpy. When I use 'append' to add a new element to the list, the one that has added before also change. How can I fix it?
from decimal import *
B = [[1,2,3,5],
[2,3,3,5],
[1,2,5,1]]
def shape(M):
r = len(M)
c = len(M[0])
return r,c
def matxRound(M, decPts=4):
for p in M:
for index in range(len(M[0])):
p[index] = round(p[index], decPts)
def transpose(M):
c_trans, r_trans = shape(M)
new_row = [0]*c_trans
trans_M = []
for i in range(r_trans):
for j in range(c_trans):
new_row[j] = M[j][i]
print 'new_row',new_row
print 'trans_M before append',trans_M
trans_M.append(new_row)
print 'trans_M after append',trans_M
return trans_M
print transpose(B)
The output is here:
new_row [1, 2, 1]
trans_M before append []
trans_M after append [[1, 2, 1]]
new_row [2, 3, 2]
trans_M before append [[2, 3, 2]]
trans_M after append [[2, 3, 2], [2, 3, 2]]
new_row [3, 3, 5]
trans_M before append [[3, 3, 5], [3, 3, 5]]
trans_M after append [[3, 3, 5], [3, 3, 5], [3, 3, 5]]
new_row [5, 5, 1]
trans_M before append [[5, 5, 1], [5, 5, 1], [5, 5, 1]]
trans_M after append [[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
[[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
I will complete #glibdud comment's answer :
What you are doing now is creating a list that fits your needs for your Transpose.
You are creating your new matrix.
You are, then, appending your transposed value into your new matrix... without creating a new Transpose list.
What happens then is that you modify the last list you just appended, and try to append it again.
So in the end, you added the 4 same lists to your new matrix. As the 4 lists point to the same address in memory as they are the same object, your new matrix have 4 identical rows.
The most pythonic way I know to perform matrix transposition without using Numpy (that should be the preferred way), is by using list unpacking (list expansion) and the builtin zip function transposed = list(zip(*B)).
However, zip() return tuples while your original matrix is a list of lists. So, if you want to keep your structure, you can use transposed = [list(i) for i in zip(*B)]

python random shuffle while loop

I'm trying to fill lists with permutations of the same initial list. I don't understand why the following is not working.
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(newval)
Expected result would be something like:
[[[6,7,8,5],[1,3,4,2]],[[5,8,6,7],[4,2,3,1]]]
random.shuffle works in-place and consequently modifies newval. You have to make a copy when appending to part otherwise the same list (or list reference) is shuffled and stored in part.
import random
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(newval[:])
print(parts)
possible outputs:
[[[3, 1, 2, 4], [5, 7, 6, 8]], [[1, 2, 4, 3], [6, 7, 5, 8]]]
[[[1, 3, 2, 4], [4, 2, 1, 3]], [[2, 4, 3, 1], [4, 3, 2, 1]]]
[[[7, 5, 6, 8], [3, 2, 4, 1]], [[8, 5, 6, 7], [1, 4, 3, 2]]]
Because in Python everything is reference. When you append the value to the array, in fact you add the reference to the place in memory where the value is stored.
Say, you have assigned the list to the first element. When on the next iteration you re-shuffle this list, you change the value in the memory. Thus, the value you will when accessing the element you appended on previous step is also changed.
To fix this, try appending copy.copy(newval) instead of just newval (do not forget to import copy)
Here is your code changed accordingly:
import copy
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(copy.copy(newval))

Categories

Resources