Generate the initial game board of a Candy-Crush-like game - python

I need to implement a function,
which returns a 6 by 6 matrix that fulfills the following requirements:
The 36 numbers on the board must be 9 ones, 9 twos, 9 threes and 9 fours
Any row or column must not contain 3 or more direct neighbours that are the same number
The function return value must not be a constant
Obviously it’s not allowed to use pre-calculated answers
correct answer:
[[3, 2, 4, 1, 3, 2],
[2, 2, 1, 1, 4, 4],
[4, 4, 1, 3, 3, 2],
[4, 1, 3, 2, 2, 4],
[3, 1, 2, 4, 3, 1],
[3, 3, 1, 1, 2, 4]]
[[3, 3, 1, 2, 2, 4],
[1, 1, 3, 3, 2, 4],
[4, 4, 2, 1, 1, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
wrong answer:
[[3, 3, 3, 2, 2, 4],
[1, 1, 1, 3, 2, 4],
[4, 4, 2, 1, 1, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
[[3, 3, 1, 2, 2, 4],
[1, 1, 2, 3, 2, 4],
[4, 4, 1, 1, 2, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
Don’t need to worry too much about the academic time/space complexity. Focus more on the engineering point of view. Is there any good idea?

This should work. Note that this solution just generates a random board, checks if the conditions hold, and if not, generates another, so is not the most elegant solution.
Code:
from random import shuffle
def check_board(board):
for row in board:
if check_list(row):
return False
for i in range(len(board[0])):
col = [row[i] for row in board]
if check_list(col):
return False
return True
def check_list(lst):
return any(lst[i]==lst[i+1] and lst[i]==lst[i+2] for i in range(len(lst)-2))
board = [[]]
while check_board(board):
board = [1,2,3,4]*9
shuffle(board)
board = [board[i:i + 6] for i in range(0, len(board), 6)]
print(board)
Example boards generated:
[[3, 2, 4, 3, 3, 2],
[1, 1, 2, 3, 1, 3],
[1, 3, 3, 2, 2, 2],
[4, 4, 1, 4, 1, 2],
[1, 1, 4, 4, 2, 4],
[2, 4, 4, 3, 3, 1]]
[[2, 3, 4, 1, 4, 1],
[3, 4, 1, 1, 3, 4],
[3, 1, 4, 1, 3, 4],
[3, 4, 2, 4, 2, 1],
[2, 1, 4, 2, 3, 2],
[2, 2, 1, 3, 3, 2]]

Create an array of size 36 and fill it with your desired values => [1,1,1....4,4,4]
Apply Fisher-Yates shuffle to create a permutation of that array in O(n)
Check for the "3 in a row" rule and swap a random value if nesseccary, check again until the grid is free of that.

Related

Appending to lists within loops in Python3 (again)

I'm having difficulty adding to a list iteratively.
Here's a MWE:
# Given a nested list of values, or sets
sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]
# add a value to each sublist giving the number of that set in the list.
n_sets = len(sets)
for s in range(n_sets):
(sets[s]).insert(0, s)
# Now repeat those sets reps times
reps = 4
expanded_sets = [item for item in sets for i in range(reps)]
# then assign a repetition number to each occurance of a set.
rep_list = list(range(reps)) * n_sets
for i in range(n_sets * reps):
(expanded_sets[i]).insert(0, rep_list[i])
expanded_sets
which returns
[[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 0, 1, 2, 3],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 1, 1, 2, 4],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5],
[3, 2, 1, 0, 2, 1, 2, 5]]
instead of the desired
[[0, 0, 1, 2, 3],
[1, 0, 1, 2, 3],
[2, 0, 1, 2, 3],
[3, 0, 1, 2, 3],
[0, 1, 1, 2, 4],
[1, 1, 1, 2, 4],
[2, 1, 1, 2, 4],
[3, 1, 1, 2, 4],
[0, 2, 1, 2, 5],
[1, 2, 1, 2, 5],
[2, 2, 1, 2, 5],
[3, 2, 1, 2, 5]]
Just for fun, the first loop returns an expected value of sets
[[0, 1, 2, 3], [1, 1, 2, 4], [2, 1, 2, 5]]
but after the second loop sets changed to
[[3, 2, 1, 0, 0, 1, 2, 3], [3, 2, 1, 0, 1, 1, 2, 4], [3, 2, 1, 0, 2, 1, 2, 5]]
I suspect the issue has something to do with copies and references. I've tried adding .copy() and slices in various places, but with the indexed sublists I haven't come across a combo that works. I'm running Python 3.10.6.
Thanks for looking!
Per suggested solution, [list(range(reps)) for _ in range(n_sets)] doesn't correctly replace the list(range(reps)) * n_sets, since it gives [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] instead of
the desired [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]. Do I need to flatten, or is there a syntax with the _ notation that gives me a single list?
Further update . . .
replacing
rep_list = list(range(reps)) * n_sets
with
rep_list_nest = [list(range(reps)) for _ in range(n_sets)]
rep_list = [i for sublist in rep_list_nest for i in sublist]
gives the same undesired result for expanded_sets.
The problem is here:
expanded_sets = [item
for item in sets
for i in range(reps)]
This list now contains the same element of sets four times in a row, followed by the next element repeated four times, and so on.
Creating copies of item fixes the issue:
expanded_sets = [item.copy()
for item in sets
for i in range(reps)]
Try it online
If you want a more pythonic approach, then recognize that the result is a product of two ranges, and your original sets all concatenated together:
from itertools import product
sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]
expanded_sets = [[inner_counter, outer_counter] + sets_elem
for sets_elem, outer_counter, inner_counter in product(sets, range(len(sets)), range(4))]
Try it online

Form groups in a list based on condition

(Edited based on feedbacks)
I've got a list like this:
my_list = [1,2,3,1,2,4,1,3,5,1,4,6,1,4,7]
That I'm struggling to turn into that:
result = [[1,2,3,1,2,4],[1,3,5],[1,4,6,1,4,7]]
I want to group my_list elements in sublists of 3 elements unless my_list[i] = my_list[i+3] in this case I want to merge those in bigger sublists.
Here is what I've tried:
result = []
for i in range(1,len(my_list),3):
try:
print(my_list[i],my_list[i+3])
if my_list[i] == my_list[i+3]:
result.extend(my_list[i-1:i+5])
else:
result.append(my_list[i-1:i+2])
FWIW, the description of your logic isn't quite clear. However, if I understand your code correctly, I think this is at least something in the correct direction:
def stepper(my_list, step, bigger_step):
res = []
idx = 0
while idx <= len(my_list)-1:
if idx + step > len(my_list)-1:
# Remove this append if you don't want the "leftovers"
res.append(my_list[idx:])
break
if my_list[idx] != my_list[idx+step]:
res.append(my_list[idx:idx+step])
idx += step
else:
res.append(my_list[idx:idx+bigger_step])
idx += bigger_step
return res
my_list = [1,2,3,1,2,4,1,3,5,1,3,6,1,2,7]
print(stepper(my_list, step=3, bigger_step=6)) # Output: [[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Note that the above output is different from your given example, because of your given logic that you've provided makes the second sub-list extended as well as the first.
Using the above code, we can check the results if we change bigger_step easily with a for-loop:
for big in range(4, 10):
print(f"Step: 3, Bigger_Step: {big}, Result:{stepper(my_list, step=3, bigger_step=big)}")
Output:
Step: 3, Bigger_Step: 4, Result:[[1, 2, 3, 1], [2, 4, 1], [3, 5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 5, Result:[[1, 2, 3, 1, 2], [4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 6, Result:[[1, 2, 3, 1, 2, 4], [1, 3, 5, 1, 3, 6], [1, 2, 7]]
Step: 3, Bigger_Step: 7, Result:[[1, 2, 3, 1, 2, 4, 1], [3, 5, 1, 3, 6, 1, 2], [7]]
Step: 3, Bigger_Step: 8, Result:[[1, 2, 3, 1, 2, 4, 1, 3], [5, 1, 3], [6, 1, 2], [7]]
Step: 3, Bigger_Step: 9, Result:[[1, 2, 3, 1, 2, 4, 1, 3, 5], [1, 3, 6, 1, 2, 7]]

How to return indices from sorting a 2d numpy array row-by-row?

Input: A 2D numpy array
Output: An array of indices that will sort the array row by row (or column by column)
E.g.: Say the function is get_sorted_indices(array, axis=0)
a = np.array([[1,2,3,4,5]
,[2,3,4,5,6]
,[1,2,3,4,5]
,[2,3,4,6,6]
,[2,3,4,5,6]])
ind = get_sorted_indices(a, axis=0)
Then we will get
>>> ind
[0, 2, 1, 4, 3]
>>> a[ind] # should be equals to a.sort(axis = 0)
array([[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[2, 3, 4, 5, 6],
[2, 3, 4, 6, 6]])
>>> a.sort(axis=0)
array([[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[2, 3, 4, 5, 6],
[2, 3, 4, 6, 6]])
I've looked at argsort but I don't understand its output and reading the documentation doesn't help:
>>> a.argsort()
array([[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]])
>>> a.argsort(axis=0)
array([[0, 0, 0, 0, 0],
[2, 2, 2, 2, 2],
[1, 1, 1, 1, 1],
[3, 3, 3, 4, 3],
[4, 4, 4, 3, 4]])
I can do this manually but I think I'm misunderstanding argsort or I'm missing something from numpy.
Is there a standard way to do this or I have no choice but to do this manually?

Creating a dictionary given values from a list and dictionary

So, I am given a list
a =[[[0, 0, 3, 3, 3, 3], [0, 0, 1, 3, 3, 3, 3]], [[0, 1]], [[2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3]], [[0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3]]]
and a dictionary d.
d = {0:2,1:1,2:3,3:4}
For the output, I want a dictionary:
output = {0:[0,3],1;[1],2:[2,3],3:[0,2]}
This output is formed by passing through each sublist of a and checking the number of times each element appears in d.
Let's look at index 0 of a. Now we look at a[0][0]and
a[0][1] and since 0 appears twice in both and 3 appears 4 times (comparing it to d), [0,3] are added to index 0. Similarly, at index 1, 0 appears just once and is not added to the dictionary at index 1.
What I tried so far:
def example(a,d):
for i in range(len(a)):
count = 0
for j in range(len(a[i])):
if j in (a[i][j]):
count+=1
if count == d[i]:
print(i,j)
Edit: A version that work
from collections import Counter
a = [[[0, 0, 3, 3, 3, 3], [0, 0, 1, 3, 3, 3, 3]], [[0, 1]],
[[2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3]],
[[0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3]]]
d = {0: 2, 1: 1, 2: 3, 3: 4}
output = {i: [] for i in range(len(a))}
for j, sublist in enumerate(a):
counts = [Counter(i) for i in sublist]
for k,v in d.items():
try:
if all(counts[i][k] == v for i in range(len(counts))):
output[j].append(k)
except: continue
print(output)
output:
{0: [0, 3], 1: [1], 2: [2, 3], 3: [0, 2]}
The try except block is merely for convenience, If you insist you can if your way around this by checking if a key is in all counters (which is a requirement for it to be add)

Find all possible combinations/partitions of 2 numbers for a given number

I found this code on the Internet (Find all possible subsets that sum up to a given number)
def partitions(n):
if n:
for subpart in partitions(n-1):
yield [1] + subpart
if subpart and (len(subpart) < 2 or subpart[1] > subpart[0]):
yield [subpart[0] + 1] + subpart[1:]
else:
yield []
I was wondering if someone could find a way to pull out of the answer only the answers, that are 2 digit addition?
For example: I type in 10. It gives me:
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 2], [1, 1, 1, 1, 1, 1, 2, 2], [1, 1, 1, 1, 2, 2, 2], [1, 1, 2, 2, 2, 2], [2, 2, 2, 2, 2], [1, 1, 1, 1, 1, 1, 1, 3], [1, 1, 1, 1, 1, 2, 3], [1, 1, 1, 2, 2, 3], [1, 2, 2, 2, 3], [1, 1, 1, 1, 3, 3], [1, 1, 2, 3, 3], [2, 2, 3, 3], [1, 3, 3, 3], [1, 1, 1, 1, 1, 1, 4] , [1, 1, 1, 1, 2, 4], [1, 1, 2, 2, 4], [2, 2, 2, 4], [1, 1, 1, 3, 4], [1, 2, 3, 4], [3, 3, 4], [1, 1, 4, 4], [2, 4, 4], [1, 1, 1, 1, 1, 5], [1, 1, 1, 2, 5], [1, 2, 2, 5], [1, 1, 3, 5], [2, 3, 5], [1, 4, 5], [5, 5], [1, 1, 1, 1, 6], [1, 1, 2 , 6], [2, 2, 6], [1, 3, 6], [4, 6], [1, 1, 1, 7], [1, 2, 7], [3, 7], [1, 1, 8], [2, 8], [1, 9], [10]]
I would like it only gives:
[[5, 5], [4, 6], [3, 7], [2, 8], [1, 9]]
Since you only want partitions of length 2 (and the products of the elements of each partition), we can use a simpler approach:
#! /usr/bin/env python
''' Find pairs of positive integers that sum to n, and their product '''
def part_prod(n):
parts = [(i, n-i) for i in xrange(1, 1 + n//2)]
print parts
print '\n'.join(["%d * %d = %d" % (u, v, u*v) for u,v in parts])
def main():
n = 10
part_prod(n)
if __name__ == '__main__':
main()
output
[(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)]
1 * 9 = 9
2 * 8 = 16
3 * 7 = 21
4 * 6 = 24
5 * 5 = 25
You could use itertools.combinations_with_replacement
from itertools import combinations_with_replacement
n = 10
print([x for x in combinations_with_replacement(range(1,n), 2) if sum(x) == n])
[(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)]
Just for fun with list comprehension without using itertools.
num = 10
[[x, y] for x in range(1, num) for y in range(1, num) if x + y == num and x <= y]
# [[1, 9], [2, 8], [3, 7], [4, 6], [5, 5]]

Categories

Resources