Dynamically add arguments in if condition in python - python

I am writing code for a tick-tac-toe game that uses grids. I am trying to check the condition where all the cells of a row match with each other, so that players can be declared winners. Also, I am using dynamic sizes of board (3 by 3, 4 by 4, 5 by 5....). So for this purpose, I am using if condition in a for loop. The problem is that I can't figure out a way to add arguments dynamically to the if statement. The if defined is static and I can't add the condition of the last cell of the row (if size increases from 3 by 3 to 4 by 4).
expected outcome:
3 by 3 grid:
count = 0
for i in range(dim):
if grid[count]=="X" and grid[1+ count]=="X" and grid[2+ count]=="X":
count1 = 0
print ("Player X win")
print ("-------------")
for i in range(dim):
print(grid[0 + count1 : dim + count1])
count1 =+ dim
print ("-------------")
count += dim
I am trying to loop for all rows to check the match.
What I want is to change the if grid[count]=="X" and grid[1+ count]=="X" and grid[2+ count]=="X": to incorporate the change of the grid size from
3 by 3 to
4 by 4
if grid[count]=="X" and grid[1+ count]=="X" and grid[2+ count]=="X" and grid[3+ count]=="X":
5 by 5
if grid[count]=="X" and grid[1+ count]=="X" and grid[2+ count]=="X" and grid[3+ count]=="X" and grid[4+ count]=="X":
is there a better way to do this?

You can use all() with a generator expression for this:
if all(grid[count + i] == "X" for i in range(dim)):
There's also a corresponding any() for or conditions
There are three pieces to how this works:
Comprehensions; we can write a list like [grid[count + i] == "X" for i in range(dim)] which will evaluate to something like [True, True, False, True]; it's basically a short-hand for a for loop that produces a list (or a dict or a set, if we use {}).
The all() (or any()) function; this is a simple function that takes a list and returns whether all (any) are true; all([True, True, False, True]) is False
Generator expressions; generators are basically single-use lists where the values are calculated only as they're needed. The syntax for a generator expression is either omitting the [], as here; or using round brackets (), where the syntax would otherwise be too confusing.
Because the values are calculated only as needed, this can be very valuable if we don't want to (or can't) store all the values in memory at once, or if we're likely to only look at the first few values. The all() function does that - it only checks as far as needed for the result to become clear. That's not much of an advantage when there's only going to be 3-5 values with a simple calculation, of course; here, it's mostly that it looks neater without the [].
One downside of generators is that they don't print well; you have to convert them to list to print out the items, and then you've used them up and can't use them again in the calculation. For debugging, the [grid[count + i] == "X" for i in range(dim)] form is more convenient.

Related

Array index manipulation with multiple conditions

I want to change every index in an array that lies between two values.
I know:
a[a>10]=0
will change every index to 0 if the condition that the index itself is greater than 10 is true.
What I now want to achieve is to set every array index to 0 if two conditions are true, if for example the index is greater than 10 and less than 50, but
a[a>10 and a<50]=0
won't work.
I know I can to do this with a few lines of code but I feel like this wouldn't be the most elegant solution therefore my question is something like this possible in a similar elegant way as my simple example above, maybe also a one-liner?
You need parenthesis around each condition:
a[(i > 10) & (i < 50)]
Try this code:
a=np.arange(1,10)
a[(a>3) & (a<7)] = 0
# output: [1 2 3 0 0 0 7 8 9]
You can write this (click for a demo):
if i>10 and i<50: l[:] = [0]*len(l)
Let's break this down:
l[:] = value is a slicing statement. This is equivalent to l.__setitem__(slice(), value). For the built-in list object, leaving out the index to the left of the colon implies an index of zero, and leaving out the index to the right of the colon implies an index of the length of the array minus one. In other words, by leaving out both numbers on the sides of :, we implicitly set the entire list.
[0]*len(l) takes a list with a single element, 0, and multiplies it by the length of the list, creating a list of [0, 0, 0, 0, ... 0] as long as the original list.
If you want to do this in a single expression, you can write:
an empty parameter list (slice() uses None,None without arguments
| (which is equivalent to `l[:]`)
|
| ternary operator
v vv
l[slice(**([] if (i > 10 and i < 50) else 0,0))] = [0]*len(l)`
^ ^^
| kwargs argument unpacking
|
create a slice object -- l[x:y] is just syntactic sugar for l[slice(x, y)
This is syntactic sugar for setting [0]*len(l) to l[:] if the condition within holds true and [0]*len(l) to l[0:0] when the condition is false. l[0:0] is an empty range, so nothing changes.

Python Coin Change Dynamic Programming

I am currently trying to implement dynamic programming in Python, but I don't know how to setup the backtracking portion so that it does not repeat permutations.
For example, an input would be (6, [1,5]) and the expected output should be 2 because there are 2 possible ways to arrange 1 and 5 so that their sum is equivalent to 6. Those combinations are {1,1,1,1,1,1} and {1,5} but the way my program currently works, it accounts for the combinations displayed above and the combination {5,1}. This causes the output to be 3 which is not what I wanted. So my question is "How do I prevent from repeating permutations?". My current code is shown below.
import collections as c
class DynamicProgram(object):
def __init__(self):
self.fib_memo = {}
# nested dictionary, collections.defaultdict works better than a regular nested dictionary
self.coin_change_memo = c.defaultdict(dict)
self.__dict__.update({x:k for x, k in locals().items() if x != 'self'})
def coin_change(self, n, coin_array):
# check cache
if n in self.coin_change_memo:
if len(coin_array) in self.coin_change_memo[n]:
return [n][len(coin_array)]
# base cases
if n < 0: return 0
elif n == 1 or n == 0: return 1
result = 0
i = 0
# backtracking (the backbone of how this function works)
while i <= n and i < len(coin_array):
result += self.coin_change(n-coin_array[i], coin_array)
i += 1
# append to cache
self.coin_change_memo[n][len(coin_array)] = result
# return result
return result
One of the way of avoiding permutation is to use the numbers in "non-decreasing" order. By doing so you will never add answer for [5 1] because it is not in "non-decreasing" order.And [1 5] will be added as it is in "non-decreasing" order.
So the change in your code will be if you fix to use the ith number in sorted order than you will never ever use the number which is strictly lower than this.
The code change will be as described in Suparshva's answer with initial list of numbers sorted.
Quick fix would be:
result += self.coin_change(n-coin_array[i], coin_array[i:]) # notice coin_array[i:] instead of coin_array
But you want to avoid this as each time you will be creating a new list.
Better fix would be:
Simply add a parameter lastUsedCoinIndex in the function. Then always use coins with index >= lastUsedCoinIndex from coin array. This will ensure that the solutions are distinct.
Also you will have to make changes in your memo state. You are presently storing sum n and size of array(size of array is not changing in your provided implementation unlike the quick fix I provided, so its of no use there!!) together as a state for memo. Now you will have n and lastUsedCoinIndex, together determining a memo state.
EDIT:
Your function would look like:
def coin_change(self,coin_array,n,lastUsedCoinIndex):
Here, the only variables changing will be n and lastUsedCoinIndex. So you can also modify your constructor such that it takes coin_array as input and then you will access the coin_array initialized by constructor through self.coin_array. Then the function would become simply:
def coin_change(self,n,lastUsedCoinIndex):

Python 3 - Check if digit inside a number with if comprehension

This currently works for what I'm trying to do (where 'i' is the number I'm checking to see if it is in the 'digit'):
if str(digit) in str(i):
count += 1
However, when I try to use an if comprehension I get an invalid syntax:
count = [count + 1 if str(digit) in str(i)]
I've tried it several different ways using just [count +=1 if...] and count = [count + 1 if...]
Can anyone provide some insight into this issue?
There currently is nothing wrong with the way you are doing it now. Just want to point that out. If you are simply trying to do this in one line, maybe this solution is what you are looking for.
But to answer your comprehension issue:
You have two problems going on here that I'd like to point out.
You should not check if you have a digit in your string by trying to cast it to str. Simply use isdigit against the character you are checking.
You cannot use a comprehension the way you are trying to use it. What you have to understand about a comprehension, is that you are creating a new list and taking that new list and assigning it to a variable (in your case count). So doing this:
count = [count + 1....]
Really does not make much sense to do.
What you should do instead if you are looking to do this in a comprehension,
Iterate over each character in a, for each character, check if it is a digit:
[c.isdigit() for c in a]
Now, with that above part done. You will have a list of 1s for all digits in your word. The next step is to sum everything together. Now, the extra bit of information to pay attention to, is that when calling sum on this, we will lose the square brackets, because we will instead use what is called a generator expression.
So, with all that said and done. Your final solution is:
a = "fjf7jjjf77773jf3j"
print(sum(c.isdigit() for c in a))
# outputs 7
List comprehension (with square brackets) is used to generate list. But, in your case, you are not really generating any list. However, if you are trying to write an inline if, try -
count = count + 1 if str(digit) in str(i) else count
You can just sum the boolean value from character.isdigit() where character is each character in the string.
Consider:
>>> s='abc123def456'
>>> [c.isdigit() for c in s]
[False, False, False, True, True, True, False, False, False, True, True, True]
>>> sum(c.isdigit() for c in s)
6
I'm not sure why you would want to get a list in this case because in your first example, you simply incremented an integer variable called count. If you're looking for something more nuanced, you could try using a function like this:
def count_letter_instance(string="", letter=""):
return sum([1 for x in string if x == letter])
print(count_letter_instance("hello hello hello", "h"))
print(count_letter_instance("cat cat cat", "c"))
Output:
3
3

How to determine if a two dimensional list is "rectangular"

Right now I'm trying to get my program to take a two dimensional list as an input and return either true or false depending on whether or not it is "rectangular" (eg. [[2,3],[1,5],[6,9]] is rectangular whereas [[2,3],[1,8,6]] is not.) So far I've come up with this:
def rectangular(List):
n = List
for i in n:
if len(i) != len(n[0]):
return False
elif len(i) == len(n[0]):
i
I can't seem to figure out how to create a "true" case. Using the elif above I'm able to cycle through the list but if i were to add a return true portion it stops as soon as that is the case. Would a while loop work better in this case? All help is appreciated! Thanks.
If you get to the end without finding a false case, then you know it's true, right? There aren't any other possibilities.
So, you can just remove the elif entirely, and just add a return True at the end:
def rectangular(List):
n = List
for i in n:
if len(i) != len(n[0]):
return False
return True
As a side note, your elif that has exactly the opposite condition as the if is better written as just else:. That way, there's no chance of you getting the opposite condition wrong, no need for your readers to figure out that it's the opposite, etc.
Also, there's no reason to take the argument as List, then bind the same value to n and use that. Why not just take n in the first place?
def rectangular(n):
for i in n:
if len(i) != len(n[0]):
return False
return True
You can make this more concise, and maybe more Pythonic, by replacing the for statement with a generator expression and the all function:
def rectangular(n):
return all(len(i) == len(n[0]) for i in n)
But really, this isn't much different from what you already have. You should learn how this works, but if you don't understand it yet, there's no problem doing it the more verbose way.
If you want to get clever:
def rectangular(n):
lengths = {len(i) for i in n}
return len(lengths) == 1
We're making a set of all of the lengths. Sets don't have duplicates, so this is a set of all of the distinct lengths. If there's only 1 distinct length, that means all of the lengths are the same.
However, note that for an empty list, this will return False (because there are 0 lengths, not 1), while the other two will return True (because a condition is vacuously true for all values if there are no values to test). I'm not sure which one you want, but it should be relatively easy to figure out how to change whichever one you choose to do the opposite.
Try using the all function with a generator:
def rectangular(lst):
first_len = len(lst[0])
# I used lst[1:] to skip the 0th element
return all(len(x) == first_len for x in lst[1:])
The all function returns True if all elements of an iterable are True, and False otherwise.
It's good that you didn't call your variable list, but capitalized names usually represent a class in Python, so lst is a better choice than List.
NOTE: I made the assumption that "rectangular" means each sublist is the same length. If in reality each sublist should be (say) 2 elements long, just replace first_len with the literal 2 and remove the [1:] on lst[1:]. You may also want to add some exception handling in case you pass a list with only one element.
You can make sure that the lengths of all elements of the list are the same length. Or in Python:
all(map(lambda m: len(m) == len(x[0]), x))
Where x is what you want to check.
The only problem with this solution is that the if the list looks like [ [1,2], [1,[1,2]], 'ab' ], it is still going to return True. So you additionally need to do some type checking.

Permuting Magic Squares

I'm having a little bit of trouble writing a recursive permutation function for solving Magic Squares. For this function, I'm not allowed to use two-dimensional arrays, only lists. Below is what I have currently:
def permute(size):
magicSquare = []
for value in permute(size**2):
for pos in range(size**2 + 1):
magicSquare.append(value)
return magicSquare
size is defined by the user through command-line argument.
I'm slightly confused if the function written above accomplishes the task of permuting the values.
It wouldn't appear to and in fact should basically never terminate the way it is currently written.
An easy way to start thinking about this problem is that a magic square can be represented by a list of size n**2, so a 3x3 magic square can be represented by a 9-length list. Since it is a magic square, you then need to permute over the values range(1,n+1), e.g., for a 3x3:
1 2 3
4 5 6
7 8 9
Check to see if this is a magic square (it isn't since the rows don't sum to the same value) and if it is, add it to your magic squares list. Either way, try the next permutation:
1 2 3
4 5 6
7 9 8
…until you are out of permutations. This is, of course, a non-optimal route because the trouble row (1, 2, 3) still won't sum to 15, so there is clear room for optimization and easily discarding possibilities that won't work.
An easy tool to either check your work or do the permutation piece for you is itertools.permutations. Which will create a generator that will yield each additional permutation until there aren't any more.
Note that for anything beyond a trivial square size you are going to exceed the maximum recursion limit if you try to make another recursive call each time using this method. You'll need to find a way to manage that situation once size=3. There are a couple of ways to handle that of varying degrees of complexity, depending on what exactly you are trying to do.
Here is a simple method for checking magic square or not.
Note: please try by using 3*3 grid.
def magic():
print "maximam 9 values"
a=[]
for i in range(3):
a.append([])
for j in range(3):
a[i].append(input('Enter the values'))
print a
l1= a[0][0]+a[1][0]+a[2][0]
l2=a[0][1]+a[1][1]+a[2][1]
l3=a[0][2]+a[1][2]+a[2][2]
r1=sum(a[0])
r2=sum(a[1])
r3=sum(a[2])
if l1 == l2 == l3 == r1 == r2 == r3:
print a,"Its magic square"
else:
print a,"not magic square"
magic()

Categories

Resources