Bulls & cows (mastermind) in Python. Should be relatively easy - python

I'm new to python and I need to write a "Bulls and Cows" game (a.k.a Mastermind)
and it goes like this:
You get 2 inputs at first; 1 for the length of the secret (can be 4-6), and 2nd for the base (can be 6-10).
You can ASSUME that both the secret and the guess will have the given length (you don't need to make sure of that)
Later on, you have another 2 inputs. 1 for the secret (a chain of numbers seperated by space) and the 2nd for the base (a chain of numbers seperated by space).
if the secret has a number that equals or exceeds the base, the program will output ERROR and exit.
An example to clarify:
5 (First input, length should be 5)
8 (The base. It means that no number is allowed to be 8 or beyond. You are only allowed to use 0,1,2,3,4,5,6,7)
1 2 7 2 5 (this is the secret)
7 2 2 1 1 (this is the guess)
OUTPUT:
1 BULLS 3 COWS
Another example:
6
9
0 0 0 0 0 6
8 22 2 2 1 4
OUTPUT:
0 BULLS 0 COWS
Ok, so I started writing the code, and I wasn't sure what exactly I am supposed to be using, so I did this so far:
#get the length of the guess - an int number between 4(included) to 6(included)
secretL = input()
#get the base of the guess - an int number between 6(included) to 10(included)
secretB = input()
#get the guess
secret = raw_input()
secretsNumbers = map(int, secret.split())
#turn the chain into a singular INT number in order to make sure each of its digits does not equal or exceeds base, using %10
secretsNumbersMerge = int(''.join(map(str,secretsNumbers)))
newSecretsNumbersMerge = secretsNumbersMerge
while newSecretsNumbersMerge!= 0:
checker = newSecretsNumbersMerge%10
if checker<secretBase:
newSecretsNumbersMerge = newSecretsNumbersMerge/10
else:
print ("ERROR")
quit()
guess = raw_input()
guessNumbers = map(int, guess.split())
So far it's all good. This really makes sure the secret meets the base demands. Now I'm at the main point where I should check for bulls and cows and I'm not quite sure how to proceed from this point.
My idea is to first check for Bulls and then remove them (so it won't get mixed with cows), and then check for cows but yeah.. I'm clueless.
I'm not even sure what to use in Python.
Thanks in advance!

Here is your code but please try to understand each line of it:
import re
from collections import Counter
#get the length of the guess - an int number between 4(included) to 6(included)
secretL = input()
#get the base of the guess - an int number between 6(included) to 10(included)
secretB = input()
#get the guess
secret = raw_input()
# Check if the secret input uses a digit greater than the base value
# You can use the check on the next line as well. It can be made faster
# too by using search in place of findall.
# if len(re.findall(r"[^0-" + str(secretB) + r" ]+", secret)):
if sum((i!=" " and int(i) > secretB) for i in secret) > 0:
print("Error")
quit()
secretNumbers = map(int, secret.split())
guess = raw_input()
guessNumbers = map(int, guess.split())
# Count the bulls by iterating over the two lists simultaneously
bulls = sum(i==j for i, j in zip(secretNumbers, guessNumbers))
# Remove the bulls before counting the cows
secretNumbers, guessNumbers = zip(*((x,y) for x,y in zip(secretNumbers, guessNumbers) if x!=y))
# Cows are defined as the elements present in both the guess and the secret
# but not in the right position.
# If we ignore duplicates, this is the way to go about it.
cows = len(set(secretNumbers) & set(guessNumbers))
## If we count [1,1,2,4] and [5,3,1,1] to have 2 cows, this is how it should be:
#counter = Counter(secretNumbers) & Counter(guessNumbers)
#cows = sum(counter.itervalues())
print(str(bulls) + " BULLS " + str(cows) + " COWS")
Let me know if something is not clear and I'll add an explanation.
Also, I'm not aware of the rules of mastermind and have inferred them from your description. I'm not clear how you got 0 cows for the second example.
UPDATE 1
if sum(i > secretB for i in secretNumbers) > 0: how does it work exactly?
Please use backticks(`) to quote small pieces of code in comments, questions or answers. Now, let's break down this code into easily understandable pieces:
Consider the list comprehension [ i > secretB for i in secretNumbers]. This will generate a list of booleans. So, if your base is 6 and secretNumbers is [1,2,7,2,5], it will return [False, False, True, False, False]. (As per your comment, this is not the way the rule was to be interpreted but I'm pretty sure you can modify this condition to be what you need.)
sum() is a very useful standard built-in function in Python which returns the sum of any iterable passed to it. So, sum([1,2,3,4]) will return 10.
It remains to answer what does it mean to sum a list of boolean values. It might initially seem strange to add True to True this way, but I don't think it's unpythonic; after all, bool is a subclass of int in all versions since 2.3:
_
>>>issubclass(bool, int)
True
So, there you go: sum(i > secretB for i in secretNumbers) tells you exactly how many numbers in the input exceed the base.
UPDATE 2:
After a clarification from OP, the explanation of Update 1 doesn't apply to the code under discussion anymore though OP now understands the general technique of using list comprehensions to do powerful things.
Also, the condition for counting the COWS has been clarified and the code now lists two ways to do it - one, while ignoring duplicates; and second by counting each duplicate instance.

Related

Creating a function that returns prime numbers under a given maximum?

Instructions are to write a function that returns all prime numbers below a certain max number. The function is_factor was already given, I wrote everything else.
When I run the code I don't get an error message or anything, it's just blank. I'm assuming there's something I'm missing but I don't know what that is.
def is_factor(d, n):
""" True if `d` is a divisor of `n` """
return n % d == 0
def return_primes(max):
result = []
i = 0
while i < max:
if is_factor == True:
return result
i += 1
You should test each i against all divisors smaller than math.sqrt(i). Use the inner loop for that. any collects the results. Don't return result right away, for you should fill it first.
def return_primes(max):
result = []
for i in range(2, max):
if not any(is_factor(j, i) for j in range(2, int(math.sqrt(i)) + 1)):
result.append(i)
return result
print(return_primes(10))
As a side note, use for and range rather than while to make less mistakes and make your code more clear.
The reason that your code is returning blank when you run it, is because you are comparing a function type to the value of True, rather than calling it.
print(is_factor)
<function is_factor at 0x7f8c80275dc0>
In other words, you are doing a comparison between the object itself rather than invoking the function.
Instead, if you wanted to call the function and check the return value from it, you would have to use parenthesis like so:
if(is_factor(a, b) == True):
or even better
if(is_factor(a, b)):
which will inherently check whether or not the function returns True without you needing to specify it.
Additionally, you are not returning anything in your code if the condition does not trigger. I recommend that you include a default return statement at the end of your code, not only within the condition itself.
Now, in terms of the solution to your overall problem and question;
"How can I write a program to calculate the prime numbers below a certain max value?"
To start, a prime number is defined by "any number greater than 1 that has only two factors, 1 and itself."
https://www.splashlearn.com/math-vocabulary/algebra/prime-number
This means that you should not include 1 in the loop, otherwise every single number is divisible by 1 and this can mess up the list you are trying to create.
My recommendation is to start counting from 2 instead, then you can add 1 as a prime number at the end of the function.
Before going over the general answer and algorithm, there are some issues in your code I'd like to address:
It is recommended to use a different name for your variable other than max, because max() is a function in python that is commonly used.
Dividing by 0 is invalid and can break the math within your program. It is a good idea to check the number you are dividing by to ensure it is not zero to make sure you do not run into math issues. Alternatively, if you start your count from 2 upwards, you won't have this issue.
Currently you are not appending anything into your results array, which means no results will be returned.
My recommendation is to add the prime number into the results array once it is found.
Right now, you return the results array as soon as you have calculated the first result. This is a problem because you are trying to capture all of the prime numbers below a specific number, and hence you need more than one result.
You can fix this by returning the results array at the end of the function, not in between, and making sure to append each of the prime numbers as you discover them.
You need to check every single number between 2 and the max number to see if it is prime. Your current code only checks the max number itself and not the numbers in between.
Now I will explain my recommended answer and the algorithm behind it;
def is_factor(d, n):
print("Checking if " + str(n) + " is divisible by " + str(d))
print(n % d == 0)
return n % d == 0
def return_primes(max_num):
result = []
for q in range(2, max_num+1):
count_number_of_trues = 0
for i in range(2, q):
if(i != q):
if(is_factor(i, q)):
print("I " + str(i) + " is a factor of Q " + str(q))
count_number_of_trues += 1
if(q not in result and count_number_of_trues == 0):
result.append(q)
result.append(1)
return sorted(result)
print(return_primes(10))
The central algorithm is that you want to start counting from 2 all the way up to your max number. This is represented by the first loop.
Then, for each of these numbers, you should check every single number from 2 up to that number to see if a divisor exists.
Then, you should count the number of times that the second number is a factor of the first number, and if you get 0 times at the end, then you know it must be a prime number.
Example:
Q=10
"Is I a factor of Q?"
I:
9 - False
8 - False
7 - False
6 - False
5 - True
4 - False
3 - False
2 - True
So for the number 10, we can see that there are 2 factors, 5 and 2 (technically 3 if you include 1, but that is saved for later).
Thus, because 10 has 2 factors [excluding 1] it cannot be prime.
Now let's use 7 as the next example.
Example:
Q=7
"Is I a factor of Q?"
I:
6 - False
5 - False
4 - False
3 - False
2 - False
Notice how every number before 7 all the way down to 2 is NOT a factor, hence 7 is prime.
So all you need to do is loop through every number from 2 to your max number, then within another loop, loop through every number from 2 up to that current number.
Then count the total number of factors, and if the count is equal to 0, then you know the number must be prime.
Some additional recommendations:
although while loops will do the same thing as for loops, for loops are often more convenient to use in python because they initialize the counts for you and can save you some lines of code. Also, for loops will take care of the incrementing process for you so there is no risk of forgetting.
I recommend sorting the list when you return it, it looks nicer that way.
Before adding the prime factor into your results list, check to see if it is already in the list so you don't run into a scenario where multiples of the same number is added (like [2,2,2] for example)
Please note that there are many different ways to implement this, and my example is but one of many possible answers.

Uva The 3n + 1 Problem - Wrong Answer Python

I'm trying to solve a question from onlinejudge.org - The 3n + 1 Problem using Python.
in case the link doesn't load (that happens quite frequently), below is my summarized version of the question:
Consider the following algorithm:
input n
print n
if n = 1 then STOP
if n is odd then n <- 3n + 1
else n <- n/2
GOTO 2
Given the input 22, the following sequence of numbers will be printed
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
In the example above, the cycle length of 22 is 16.
For any two numbers i and j you are to determine the maximum cycle length over all numbers between and including both i and j.
Sample Input
1 10
100 200
201 210
900 1000
Sample Output
1 10 20
100 200 125
201 210 89
900 1000 174
And here's my code.
inputText = input().split()
inputList=[]
for index in range(0, len(inputText), 2):
inputList.append([int(inputText[index]), int(inputText[index + 1])])
def CycleLen(num):
chainList = [num]
while num > 1:
chainList.append(num//2 if num % 2 == 0 else 3*num + 1)
num = chainList[-1]
return len(chainList)
for listSet in inputList:
countList = []
minRange = min(listSet[0], listSet[1])
maxRange = max(listSet[0], listSet[1])
for num in range(minRange, maxRange + 1):
countList.append(CycleLen(num))
countList.sort()
print(listSet[0], listSet[1], countList[-1])
I'm aware of the memorization solution to make it more time efficient, but I planned to implement that only if the question rejected my answer for exceeding time limit. However, I'm straight up getting the wrong answer, and I have no idea why.
I used uDebug to see if there are any mistakes, and searched for other's solution. The most confusing part is how the online judge submits its input - single line by line, or all the lines at once. Different sources claim different things.
Please help. Thank you!
Given your code actually does generate the correct results for the given samples, it's a safe bet you're not handling the input data correctly. And, in fact, the problem statement states (my emphasis):
The input will consist of a series of pairs of integers i and j, one pair of integers per line. All integers will be less than 10,000 and greater than 0.
You should process all pairs of integers and for each pair determine the maximum cycle length over all integers between and including i and j.
Hence, your code that gets only one pair will not be sufficient. The reason it only processes one pair has to do with a misunderstanding that the code:
inputText = input().split()
will give you all the input lines, as evidenced by the loop that tries to create a list of 2-tuples from each pair:
inputList=[]
for index in range(0, len(inputText), 2):
inputList.append([int(inputText[index]), int(inputText[index + 1])])
However, input() only reads one line of input (see here for details), meaning that you will only process the first line of the multi-line input. To do it properly, you need to continuously read and process lines until the file ends.
Having said that, you may also want to reconsider the use of a lists as your first step if it exceeds the time limit. It would be far easier to just maintain the current item and a count for calculating the cycle length (for the individual cycles) and just processing input lines sequentially (for the different ranges). Neither of those aspects requires an actual list.
Addressing both those list issues (and the one-line-of-input issue mentioned above), you would end up with something like:
def CycleLen(num):
# Start with one number, the one given.
curr = num
count = 1
# Until you get to 1, increase count and calculate next.
while curr > 1:
count += 1
curr = curr // 2 if curr% 2 == 0 else curr * 3 + 1
return count
while True:
# Reads a single line and splits into integers. Any problem, exit loop.
try:
inputInt = [int(item) for item in input().split()]
if len(inputInt) != 2: break
except:
break
# Find value in the range with the longest cycle then print it.
maxCycle = 0
for number in range(min(inputInt), max(inputInt) + 1):
maxCycle = max(maxCycle, CycleLen(number))
print(inputInt[0], inputInt[1], maxCycle)

"Given a list of numbers, find and print all its elements with even indices"

This is a repl.it problem I have been working on for the past 6 hours and as far as I can tell my code works properly from the output point of view. for example if I input 56789 my program outputs 5 7 9. Repl.it tests fail every time and tell me my output is 56789 when theirs is 5 7 9. If I define the input as a specified list instead of a user input then repl.it passes the first test and fails the second....because it indicates that the program fails with a new set of numbers. So I know stack overflow is not repl.it but not finding any help there. note...I am sure my code is clunky and inefficient but I am still learning how to walk here
def picky():
position = 0
a = input("") #input asking for a number
b = list(a) #converts my input into a list
c = [] #list for storing even index values
d = [] #list for converting c to string
while position <= len(b):
if position % 2 == 0:
c += b[position]
position += 1
for x in c:
d += str(x)
preOutput = str(d).strip('[]').replace('\'','').replace(',','')
# removes brackets, commas, and quotes from output.
print(preOutput)
picky()
using 3.6.4

Algorithm counting

I am a bit confused in this condition: lets say we have a number from 1 to 9 its numerical code from the beginning 1 to the 9. If the number already reach number 9, then it is loop again from 1 to 9 again, and it begin again from number 1. Each number from that range continue appear with a random number continue like this example:
1111122222333334444445555556666667777777888888999999111111222222333333444445555566677778899....
then the problem is I need an algorithm or a code to detect if number 4 appear I want count it as 1, but I want to eliminate the continue appear from the number 4 other (I didnt want to count the continuing of number 4). sorry if its not a clearly explaination. It maybe better explain from the example. the example:
if the data is
1111222334445556667788999111222333444455566677788899
so the counting program must be 2 because number 4 appear twice (I mark the first appear of number 4), and the continuing for that number not be count. oh yes all of that number (1-9) it always continue appear with a random number continue.
Thank you for your help :-)
If the number appear at least once you could count the "edges" ie
n = numstr.count("34")
(Obviously need to test starting number in sequence as special case (i.e. if looking at number of "91"s)
EDIT. Following significant revision mentioned in comments, you would have to do something along the lines of this:
a = [66,69,69,77,78,80,84,84,91,91,96,96,100,100,109,116,116,124,124,137,137,140,66,66,66,78,78,80,80,80,84]
c = {a[0]:1}
for i in range(1, len(a)):
if a[i] != a[i-1]:
if a[i] in c:
c[a[i]] += 1
else:
c[a[i]] = 1
Lets assume that you have the numbers as a string.
Let the string be str be comprising of all these numbers.
I'll propose a code in c.
int count=0,flag=0;
for(int i=0;i<strlen(str);i++){
if(flag==0){
if(str[i]=='4'){
count=count+1;
flag=1;
continue;
}
}
if(flag==1 && str[i]=='4')
continue;
flag=0;
}
printf("%d\n",count);
Count is the answer to your problem.

Define a function using a variable?

I am trying to define a function that will include a variable n where n will be a string of numbers e.g. "3884892993", the definition of the function starts as is_true(n), however if n is going to be a string should it be is_true(n) and then once the string is defined I can test the function with an example string such as n = "3884892993". I get a syntax error when I use is_true(n) however. And I am just wondering how I would go about testing this function with an example string for n.
My entire function to define is shown here: http://oi44.tinypic.com/282i3qo.jpg but bear in mind I am an absolute novice so there will most probably be many mistakes, but I would appreciate some help from some experts if at all possible :)
def is_valid("n"): #n is the number to be checked.
number =
[int(y) for y in A] #converts the string into a list of useable digits.
altern1 = integer[-2::-2] #sets altern1 as one set of alternating digits.
double = [x*2 for x in altern1] #doubles each element of the list altern1.
sum1 = sum(double) # adds together all the doubled items of the list.
altern2 = integer[-1::-2] #sets altern2 as the other set of alternating digits.
return sum2 = sum(altern2)#sums the other set of alternating digits.
sumtotal = sum1 + sum2 #works out the total sum to be worked with.
for mod = sumtotal % 10: #works out remainder when sumtotal is divided by 10
if mod == 0 : #if remainder is zero sumtotal is a multiple of 10
print 'True' #sumtotal is a multiple of 10 therefore n is a credit card number
else:
print 'False' #sumtotal is NOT a multiple of 10 therefore not a valid credit card number
Here is the actual question:
The algorithm for verifying a number is as follows:
(a) Starting with the penultimate digit, and working towards the rst digit, double each alternating digit.
(b) Sum the doubled digits, treating 13 as 1+3, etc, and add the result to the sum of the undoubled
digits
(c) If the sum is divisible by 10 the number is a valid credit card number.
Write and test a function is_valid() which takes as an argument a credit card number as a string
(eg is valid("49927398716")) and returns True or False depending on whether the number is a
valid credit card number.
Quotes are only used for string literals, you wouldn't enclose a variable or parameter name in quotes to indicate that it will be a string. The function definition would look like:
def is_true(n):
And then in the body of the function you use n to reference the value that is passed in by the caller.
To call the function on a specific value, you do:
is_true("3884892993")
Side suggestion: Think of more explanatory names for your functions and variables. For instance, it seems like your function might be reasonably called is_valid_card_number.
I am not sure what is your question, but if you are trying to:
correctly define the function:
pay attention to the indentation (this is required by Python!),
see here for examples of function definitions,
convert a string variable into integer, you can do this:
new_var = int(old_var)
Generally please pay attention to types, because it is not like in some other dynamically typed languages and strings are not dynamically converted into numbers - you should do it explicitly.
read the value of the variable, based on its name:
my_var = vars().get('variable_name')
(where variable_name is the name of the variable and optionally you can give context within brackets after vars - see help(vars) for details)
Did any of the above solve your problem?
EDIT (based on the clarification):
This should solve your problem:
def is_true(my_variable):
# Here the variable named "my_variable" is accessible
If you want to do something "in-place" on the passed variable, I have a bad news: strings and integers are immutable in Python, thus you are not able to simply change them - you should probably return them as a result of the function (there are at least two workarounds, but I do not recommend them if you are a novice in Python).
EDIT (for proper code styling):
You should probably read PEP 8 to get familiar with what is the coding standard for Python scripts - this is commonly used across Python community and you should follow that (at some point you should appreciate it).
From the Wikipedia article on the Luhn algorithm:
def is_luhn_valid(cc):
num = map(int, str(cc))
return sum(num[::-2] + [sum(divmod(d * 2, 10)) for d in num[-2::-2]]) % 10 == 0
I have no idea what your function is supposed to do, but here are some remarks.
First of all, if you define the function then you use the following syntax
def is_true(n):
# do something
you can call this function like this is_true("3884892993"), i.e. you can pass string as n. Your function now need to treat variable n as a string. So you can use
number = [int(d) for d in n]
which will result in converting string into a list of digits.
One more remark: you used a return statement inside your is_true function. This statement will stop executing the function and return the value. Every code below return will never be executed.
May be like this. I leave your comments
def is_valid(n): #n is the number to be checked.
numbers = [int(y) for y in n] #converts the string into a list of useable digits.
double_alt = [sum([int(i) for i in str(x*2)]) for x in numbers[-2::-2]] #doubles and sum if more than 10each element of the list altern1.
sum1 = sum(double_alt) # adds together all the doubled items of the list.
sum2 = sum(numbers[-1::-2]) #sums the other set of alternating digits.
sumtotal = sum1 + sum2 #works out the total sum to be worked with.
return not sumtotal % 10
Here an implementation of the luhn algorithm that I had to make recently.
def is_valid_luhn(cc):
return not sum([sum(divmod(int(d) * 2, 10)) for d in cc[-2::-2]] + [int(d) for d in cc[-1::-2]]) % 10
# | double | |--- every -2th --| |--- every -1th --|
# |--------- step 1 -----------------|
# |------------- sum doubled digits --------------| |-- sum undoubled digits --|
# |---------------------- step 2: sum doubled/undoubled digits -----------------------|
# |-------------------------- step 3: sum % 10 == 0 --> not sum % 10 --------------------------|
Or if you'd like a more verbose version:
def is_valid_luhn(cc):
total = 0
# Double and sum every 2nd digit starting at -2.
for d in cc[-2::-2]:
# divmod(d*2, 10) returns (d*2 // 10, d*2 % 10)
# sum(divmod) return (d*2 // 10) + (d*2 % 10)
total += sum(divmod(int(d) * 2, 10))
# Sum every 2nd digit starting at -1.
for d in cc[-1::-2]:
total += int(d)
# Check module 10 of total: total % 10 == 0 --> not total % 10
return not total % 10

Categories

Resources