To find same row in tic tac toe using a formula - python

I have numbered my tic-tac-toe game cells as:
0 1 2
3 4 5
6 7 8
Now, I want to create a formula or function (preferable formula) in Python such that when passed with a cell number, it gives a list of all cells of that row.
I created a formula for solving the same for the columns: i, (i + 3) % 9 and (i + 6) % 9.
Similarly, I want a general expression for the rows. Any ideas?

You can use a similar expression to create a row list. The code below creates both row and column lists using a pair of similar functions that both use the built-in divmod() function to convert the grid index into (X, Y) coordinates and a helper function xy_to_i() to convert such coordinates back into a grid index.
#!/usr/bin/env python
grid = [range(i, i+3) for i in range(0, 9, 3)]
for row in grid:
print row
print
def xy_to_i(x, y): return 3 * (y % 3) + (x % 3)
def samerow(i):
y, x = divmod(i, 3)
return [xy_to_i(x + j, y) for j in (0, 1, 2)]
def samecol(i):
y, x = divmod(i, 3)
return [xy_to_i(x, y + j) for j in (0, 1, 2)]
for i in range(9):
print i, samerow(i), samecol(i)
output
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
0 [0, 1, 2] [0, 3, 6]
1 [1, 2, 0] [1, 4, 7]
2 [2, 0, 1] [2, 5, 8]
3 [3, 4, 5] [3, 6, 0]
4 [4, 5, 3] [4, 7, 1]
5 [5, 3, 4] [5, 8, 2]
6 [6, 7, 8] [6, 0, 3]
7 [7, 8, 6] [7, 1, 4]
8 [8, 6, 7] [8, 2, 5]
Now see if you can make a function that handles the diagonals properly. :)

For a square n x n grid, the indexes of the cells in the row containing index i are returned by:
def row_indexes(i, n):
"""
Return the indexes or cells in the row containing index i for a
n x n grid.
"""
rowi = n * (i // 3)
return list(range(rowi,rowi+n))
Similarly for columns:
def col_indexes(i, n):
"""
Return the indexes or cells in the column containing index i for a
n x n grid.
"""
coli = i % n
return list(range(coli, n*n, n))
(omit the list() if you're using Python 2, since range will already return you a list).
For example,
>>> row_indexes(5,3)
[3, 4, 5]
>>> col_indexes(5,3)
[2, 5, 8]

Related

How to shift items in an array by a "K" number of times?

Shift the items in the given array, by some number of times, as shown in the below examples;
array = [1, 2 ,3 , 4, 5, 6]
k1 = 2
k2 = -3
k3 = 20
test1:
cirShift(array, k1)
Result: [5, 6, 1, 2, 3, 4]
test2:
cirShift(array, k2)
Result: [4, 5, 6, 1, 2, 3]
test3:
cirShift(array, k3)
Result: [5, 6, 1, 2, 3, 4]
I have used the below to achieve the right-rotate a list by k positions;
def rightRotateByOne(A):
Fin= A[-1]
for i in reversed(range(len(A) - 1)):
A[i + 1] = A[i]
A[0] = Fin
def rightRotate(A, k):
if k < 0 or k >= len(A):
return
for i in range(k):
rightRotateByOne(A)
if __name__ == '__main__':
A = [1, 2, 3, 4, 5, 6, 7]
k = 3
rightRotate(A, k)
print(A)
As of now, able to obtain results for test1 but would like to achieve the test2 and test3
Even easier, split the array in half given the boundary, swap and glue back:
def cirShift(a, shift):
if not shift or not a:
return a
return a[-shift%len(a):] + a[:-shift%len(a)]
Courtesy of #KellyBundy, a short-circut one-liner:
def cirShift(a, shift):
return a and a[-shift%len(a):] + a[:-shift%len(a)]
I think this question may be an exercise in self learning ('how to do X using just python'), so my answer is auxiliary, but you can always use np.roll():
#test 1
import numpy as np
a = [1, 2 ,3, 4, 5, 6]
np.roll(a, 2)
gives output
[5, 6, 1, 2, 3, 4]
and
#test 2
np.roll(a, -2)
gives output
[3, 4, 5, 6, 1, 2]
Even if you give a number that is larger than the array size, it handles the overflow:
#test 3
np.roll(a, 10)
gives output
[3, 4, 5, 6, 1, 2]
Rolling also works in multiple dimension arrays and across specified axes, which is pretty neat.
def shift(l, shift_t):
r = [*l]
for n in range(abs(shift_t)):
if shift_t < 0:
r = r[1:] + [r[0]]
else:
r = [r[-1]] + r[:-1]
return r
The key is to take one item of the list and place it on the opposite side, which is essentially all that shifting is doing. If you shift negative, you put the first one at the end, and if you shift positive, you put the last one at the beginning.

How would I generate solutions to fill in a 3x3 matrix with the numbers 1-9 and all rows and columns adding up to 1 number?

I am trying to find a way to get as many solutions as possible for a 3x3 matrix to have all rows and columns add up to the same number. It must use the numbers 1-9. I figured out that I need to use 1 big, med, and small number in each row for it to work.
Ex:
2 4 9 = 15
6 8 1 = 15
7 3 5 = 15
= = =
15 15
I have a dict with the usable numbers grouped by size and a matrix with the 3 biggest numbers each of which are in a separate row because nothing would add up to them if they were in the same row.
nums = {
"small" : [1, 2, 3],
"med" : [4, 5, 6],
"big" : [7, 8, 9]
}
m = [
[0, 0, 9],
[0, 8, 0],
[7, 0, 0]
]
What would be the best way to find all possible solutions to this?
There are two problems to solve:
Generate new possible solutions
Verify that the solution is valid
Step one is easy; python includes a permutation function which will generate every single arrangement of numbers for you. Then you need to verify that the sums all agree. We can simplify that by using #JohanC's observation that each row and column must sum to 15.
from itertools import permutations
def all_sums():
# Generate all possible grids
r = range(1, 10)
grids = permutations(r)
# Only keep grids that are valid solutions
solutions = [g for g in grids if _all_sums_are_15(g)]
return solutions
def _all_sums_are_15(grid):
"""Check that each row and column of the grid sums to 15"""
return (_sum_is_15(grid, 0, 1, 2) and
_sum_is_15(grid, 3, 4, 5) and
_sum_is_15(grid, 6, 7, 8) and
_sum_is_15(grid, 0, 3, 6) and
_sum_is_15(grid, 1, 4, 7) and
_sum_is_15(grid, 2, 5, 8))
def _sum_is_15(grid, a, b, c):
"""Determine if the given 3 cells in the grid sum up to 15"""
sum_ = grid[a] + grid[b] + grid[c]
return sum_ == 15
if __name__ == '__main__':
for s in all_sums():
print(s)
First, note that the sum of each row/column must necessarily be 15, because the sum of the 3 rows together must be equal to the sum of the numbers from 1 to 9, so 45.
Here is a way to generate all 72 solutions using Z3, an open source SAT/SMT solver. Note that Z3 is a powerful solver for this kind of combinatorial problems, and probably a bit overkill for this specific one. But it can be used as an example of how such combinatorial problems can be handled, also much trickier ones. See e.g. this long list of examples.
from z3 import *
# get 9 integer variables for the matrix elements
M = [[Int(f"m{i}{j}") for j in range(3)] for i in range(3)]
# create a Z3 solver instance
s = Solver()
# all numbers must be between 1 and 9
s.add([And(M[i][j] >= 1, M[i][j] <= 9) for i in range(3) for j in range(3)])
# all the rows must sum to 15
s.add([And([Sum([M[i][j] for j in range(3)]) == 15]) for i in range(3)])
# all the columns must sum to 15
s.add([And([Sum([M[i][j] for i in range(3)]) == 15]) for j in range(3)])
# all 9 numbers must be distinct
s.add(Distinct([M[i][j] for i in range(3) for j in range(3)]))
res = s.check()
num_solutions = 0
while res == sat:
num_solutions += 1
m = s.model()
print(num_solutions, ":", [[m[M[i][j]] for j in range(3)] for i in range(3)])
# add a new condition that at least one of the elements must be different to the current solution
s.add(Or([m[M[i][j]].as_long() != M[i][j] for i in range(3) for j in range(3)]))
res = s.check()
Output:
1 : [[3, 4, 8], [5, 9, 1], [7, 2, 6]]
2 : [[5, 7, 3], [9, 2, 4], [1, 6, 8]]
3 : [[6, 8, 1], [7, 3, 5], [2, 4, 9]]
...
72 : [[7, 5, 3], [6, 1, 8], [2, 9, 4]]
All solutions are equivalent to each other. You can permute rows and or columns of a solution to get another. And you can mirror a solution. 3! row permutations times 3! column permutations times 2 for the mirroring, 72 altogether.

Numpy concatenate + merge 1D arrays

I need to concatenate arrays but also merge the end of A with the start of B if they are overlapping.
[1, 2, 4] + [2, 4, 5] -> [1, 2, 4, 5]
[1, 2, 4] + [2, 5, 4] -> [1, 2, 4, 2, 5, 4]
[1, 2, 4] + [1, 2, 4, 5] -> [1, 2, 4, 5]
Note: Order of elements must be preserved, [4, 5] is not the same as [5, 4].
Note 2: The question can be understood like this too: We need the shortest possible extension of A such that the output ends with B.
Of course I can iterate over the second array and compare element-by element, but I am looking for a nice Numpy solution.
Originally misunderstood the problem. The problem, is from my understanding:
Two item suffix of A matches 2 item prefix of B:
[1, 2, 4] +
[2, 4, 5] =>
[1, 2, 4, 5]
No suffix of A matches a prefix of B:
[1, 2, 4] +
[2, 5, 4] ->
[1, 2, 4, 2, 5, 4]
Then we can use this terribly inefficient function:
def merge(A,B):
i = 0
m = 0
# Find largest suffix of A that matches the prefix of B with the same length
while i <= len(A):
if A[-i:] == B[:i] and i > m:
m = i
i += 1
return A + B[m:]
Below is a solution using NumPy. It's not ideal, since it requires a (possibly unneeded) sort, and an iteration. Both the sorting and iteration should be over a relatively small array (or even a single element).
import numpy as np
def merge(left, right):
"""Concatenating two arrays, merging the overlapping end and start of
the left and right array"""
# We can limit the search to the maximum possible overlap between
# the arrays, which is the minimum of the two lengths
l = min(len(left), len(right))
# Find all indices in `right` where the element matches the last element of `left`.
# Need to sort, since the `nonzero` documentation doesn't
# explicitly state whether the returned indices follow the order
# as in `right`
# As long as there are few matches, sorting will not be a showstopper
# Need to reverse the sorted array, to start from the back of the
# right array, work towards the front, until there is a proper match
for i in np.sort(np.nonzero(right[:l] == left[-1])[0])[::-1]:
# Check if the subarrays are equal
if np.all(left[-i-1:] == right[:i+1]):
return np.concatenate([left, right[i+1:]])
# No match
return np.concatenate([left, right])
a = np.array([1, 2, 4])
b = np.array([2, 4, 5])
c = np.array([2, 5, 4])
d = np.array([1, 2, 4, 5])
e = np.array([1, 2, 4, 2])
f = np.array([2, 4, 2, 5])
print(merge(a, b))
print(merge(a, c))
print(merge(a, d))
print(merge(e, b))
print(merge(e, f))
which yields
[1 2 4 5]
[1 2 4 2 5 4]
[1 2 4 5]
[1 2 4 2 4 5]
[1 2 4 2 5]
I have an O(n) solution, albeit without Numpy:
def merge(a, b):
n_a = len(a)
n = min(n_a, len(b))
m = 0
for i in range(1, n + 1):
if b[n - i] == a[n_a - 1 - m]:
m += 1
else:
m = 0
return a + b[m:]
You could do it like this.
def concatenate(a,b):
ret = a.copy()
for element in b:
if not element in ret:
ret.append(element)
return ret
This keeps the order in a + b formation.

List append in recursion

I am unable to make a pascal triangle. I need to append to list elements in recursion, but the result of my work is list appending in list. Can u help me to do it?
My testing code is:
def list(row):
if(row is 0):
return 0
return [row, list(row-1)]
If I use it, I will return list in list. I need elements in list
print(list(10))
Output:
[10, [9, [8, [7, [6, [5, [4, [3, [2, [1, 0]]]]]]]]]]
Expected output:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Regarding your specific function, here's one way you could write it
def foo (row):
if row == 0:
return [0]
else:
return [row] + foo (row - 1)
print(foo(10))
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Perhaps a more "pythonic" way of writing that would be
print([10 - x for x in range (0, 11)])
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Regarding pascal's triangle, here's one way you can write the program using an auxiliary helper and continuation passing style – for those wondering, this choice was made so that the result could be assembled in a straightforward manner and the recursive call is in tail position, meaning this function could easily be put on a trampoline to be made stack-safe if necessary
def sliding (n,xs):
if n > len(xs):
return []
else:
return [xs[0:n]] + sliding(n, xs[1:])
def pascal (n):
def aux (m, prev, k):
if n == m:
return k([prev])
else:
return aux(m + 1, [1] + [x + y for (x,y) in sliding(2, prev)] + [1], lambda rest: k([prev] + rest))
return aux(1, [1], lambda x: x)
for line in pascal(5):
print(line)
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
What you want is probably this:
return [row] + list(row-1)
This creates a list with one element row and adds the other list on top of it.
It sounds like you want to concatenate the list [x] with the list returned as a result of calling list(x-1). The nice thing about lists in python is that the + operator does exactly this. If we change the final return statement to return [x] + list(x-1) we're headed in the right direction. You'll then notice that you run into trouble when x is 0 becuase you cant add a list to an integer, so you'll want to change your base case to return [0]. Finally, as mentioned, it's best to avoid declaring names that overwrite python built in functions (list is one), so let's rename your function to my_list:
def my_list(row):
if row == 0:
return [0]
return [row] + my_list(row-1)
This doesn't quite get you to pascal's triangle, but hopefully you're off on the right course.
You can create a recursive range function that outputs your expected results:
def rec_range(n):
return [n] + rec_range(n-1) if n else [0]
>>> rec_range(10)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Hope it helps.
Or just use range (xrange in python2.x):
>>> list(range(10+1))[::-1] # reverse the list
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
You can create a Pascal's triangle using the given way
def pascal(rows):
for i in range (rows):
value=1
List = [value]
for j in range (i):
value = value * ( i-j ) * 1 / ( j + 1 )
List.append(int(value))
print(List)
pascal(5)
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]

random generation of list elements with rules

Let list l consists of n elements, where
each element should be either 0 or a positive integer less than or equal to r, and
the sum of list should be equal to m
Example:
Given n = 5, r = 4, m = 10
l = [4, 3, 2, 0, 1]
It is easy to fulfill rule(1), but I wonder if there is any good idea/algo to fulfill both rules?
Here's a simple brute force solution. Basically, you want to generate samples where random integers less than r are equally likely. Samples that do not meet the criteria (sum to m) are rejected.
import numpy as np
def rand_with_rules(n, r, m):
if n*(r-1) < m:
raise ValueError
while True:
l = np.random.randint(0, r, size=(n))
if l.sum() == m:
return l
Note that the rejection of samples will necessarily bias your "random" numbers. Because of your constraints, you can't have a purely set of random and certain will tend to be over or underrepresented.
Take, for example, the case n, r, m = 2, 3, 4. The only series that matches this criteria is (2, 2), so the likelihood of drawing 2 is 100% and 0% for other values.
In essence, this solution states that you have no prior knowledge of which integers are most likely. However, through the constraints, your posterior knowledge of the numbers will almost never be truly uniform.
There's probably a clever analytic solution for the distribution of integers given this constraint, which you could use to generate samples. But I don't know what it is!
Without validation that all rules can be meet at once, I suggest following solution
import random
def dis(n,m,r):
l = [0] * n
for inc in range(1,m):
l.sort()
for ind_less_r, less_r in enumerate(l):
if less_r > r:
break
l[random.randint(0,ind_less_r-1)] += 1
random.shuffle(l)
return l
for i in range(1,10):
print dis(10,50,9)
result is
[7, 7, 6, 1, 6, 3, 5, 4, 4, 6]
[5, 6, 7, 5, 4, 7, 4, 4, 4, 3]
[4, 3, 2, 4, 7, 7, 5, 7, 5, 5]
[4, 4, 5, 6, 4, 6, 6, 4, 5, 5]
[6, 6, 4, 6, 5, 6, 2, 5, 4, 5]
[2, 8, 4, 2, 6, 5, 4, 4, 6, 8]
[6, 6, 3, 4, 5, 5, 5, 5, 6, 4]
[6, 4, 5, 6, 7, 3, 1, 5, 6, 6]
[4, 5, 4, 7, 6, 6, 3, 2, 6, 6]
A reasonable interpretation would be to (i) find and count all unique lists satisfying the rules; and (ii) pick one of those lists at random with equal probability. The problem with this method is that it is complicated to code the list of lists.
A simpler way to do it (less code) is to expose each correct list to the same probability of being picked. That algorithm is:
check that m<=nr If not, return None or raise an error.
Loop: repeatedly generate lists with random numbers in [0,r]. Break the loop and return the first list of sum m.
Note: The tradeoff here for less code is potentially longer execution time. if r is large, or m is an improbable sum, this may take a while. We can mitigate this a little by checking the limits where the answer can only be zeros or r-1's.
from numpy.random import randint
def known_sum_random_list(size,limitint,knownsum):
if knownsum>size*(limitint-1):
return None
if knownsum==size*(limitint-1):
return size*[limitint-1]
s=0
l = size*[0]
while (s!=knownsum):
l = randint(0,limitint,size)
s = sum(l)
return l
for t in xrange(10):
print known_sum_random_list(5,4,10)
output:
[3 2 1 2 2]
[1 1 2 3 3]
[3 0 3 1 3]
[3 2 0 3 2]
[2 2 0 3 3]
[1 3 2 3 1]
[3 3 0 3 1]
[2 0 2 3 3]
[3 1 2 3 1]
[3 2 0 3 2]
Since you responded in comments that it can have many 0s and numbers can repeat, I infer that it need not be all that random. With that in mind, here is a basic solution without any loops or includes. It assumes n, r, and m have valid values and types. But such checks are simple enough to add, I'll edit them in upon request.
def create_list(n, r, m):
output = [r] * (m/r)
remainder = m - r * len(output)
if remainder != 0:
output += [m - r * len(output)]
output += [0] * (n - len(output))
return output
output = create_list(5, 4, 10) # gives [4, 4, 2, 0, 0]
output = create_list(5, 2, 10) # gives [2, 2, 2, 2, 2]
P.S. - request was for values less than r, but example showed values equaling r, so this is going by the example

Categories

Resources