Back to General discussions forum
I spent last 3 days trying to solve this problem by mimicking the logic of a human player and I got a wall of code that is useless because it doesn't give correct answer:
with open("data.txt", "r") as f:
data = f.read().splitlines()
new_data = []
_num_of_guesses = int(data.pop(0))
solution = ["X","X","X","X"]
first_num = ["0","1","2","3","4","5","6","7","8","9"]
second_num = ["0","1","2","3","4","5","6","7","8","9"]
third_num = ["0","1","2","3","4","5","6","7","8","9"]
fourth_num = ["0","1","2","3","4","5","6","7","8","9"]
counter = 0
for line in data:
guess, answer = line.split()
guess, answer = list(guess), int(answer)
if answer == 0:
if guess[0] in first_num:
first_num.remove(guess[0])
if guess[1] in second_num:
second_num.remove(guess[1])
if guess[2] in third_num:
third_num.remove(guess[2])
if guess[3] in fourth_num:
fourth_num.remove(guess[3])
else:
new_data.append(line)
print("first_num ", first_num)
print("second_num ", second_num)
print("third_num ", third_num)
print("fourth_num ", fourth_num)
for line in new_data:
guess, answer = line.split()
guess, answer = list(guess), int(answer)
def is_one_and_only(x):
if x == guess[0]:
if guess[0] in first_num and guess[0] == solution[0]:
first_num.remove(guess[0])
if {guess[0] in first_num and
(guess[1] not in second_num or solution[1] != "X") and
(guess[2] not in third_num or solution[2] != "X") and
(guess[3] not in fourth_num or solution[3] != "X")}:
return True
elif x == guess[1]:
if guess[1] in second_num and guess[1] == solution[1]:
second_num.remove(guess[1])
if {guess[1] in second_num and
(guess[0] not in first_num or solution[0] != "X") and
(guess[2] not in third_num or solution[2] != "X") and
(guess[3] not in fourth_num or solution[3] != "X")}:
return True
elif x == guess[2]:
if guess[2] in third_num and guess[2] == solution[2]:
third_num.remove(guess[2])
if {guess[2] in third_num and
(guess[0] not in first_num or solution[0] != "X") and
(guess[1] not in second_num or solution[1] != "X") and
(guess[3] not in fourth_num or solution[3] != "X")}:
return True
elif x == guess[3]:
if guess[3] in fourth_num and guess[3] == solution[3]:
fourth_num.remove(guess[3])
if {guess[3] in fourth_num and
(guess[0] not in first_num or solution[0] != "X") and
(guess[1] not in second_num or solution[1] != "X") and
(guess[2] not in third_num or solution[2] != "X")}:
return True
while "X" in solution and counter < 20:
counter += 1
for n in range(0,3):
if is_one_and_only(guess[n]):
solution[n] = guess[n]
if guess[0] == solution[0]:
if guess[1] in second_num:
second_num.remove(guess[1])
if guess[2] in third_num:
third_num.remove(guess[2])
if guess[3] in fourth_num:
fourth_num.remove(guess[3])
if guess[1] == solution[1]:
if guess[0] in first_num:
first_num.remove(guess[0])
if guess[2] in third_num:
third_num.remove(guess[2])
if guess[3] in fourth_num:
fourth_num.remove(guess[3])
if guess[2] == solution[2]:
if guess[0] in first_num:
first_num.remove(guess[0])
if guess[1] in second_num:
second_num.remove(guess[1])
if guess[3] in fourth_num:
fourth_num.remove(guess[3])
if guess[3] == solution[3]:
if guess[0] in first_num:
first_num.remove(guess[0])
if guess[1] in second_num:
second_num.remove(guess[1])
if guess[2] in third_num:
third_num.remove(guess[2])
temp = guess
print(solution)
I saw solution of somebody else and my heart sank because it's so short:
def compare(G, answer):
count=0
for i in range(len(G)):
if G[i]==answer[i]: count+=1
return count
def search(numbers):
All = [str(num).zfill(4) for num in range(10000)]
for G, A in numbers:
All = [num for num in All if compare(G, num) == A]
return All[0]
n = int(input())
numbers=[]
for _ in range(n):
G, A = input().split()
numbers.append((G, int(A)))
print(search(numbers))
and also because I don't understand why it works. Why is there only one position in the list "All"? I get extra confused when I replace that one-liner of list comprehension with this:
for num in All:
if compare(G, num) == A:
All.append(num)
return All[0]
And I get different result. Please help, I get really frustrated by this magic.
The list comprehension All = [num for num in All if compare(G, num) == A]
works as follows:
first, [num for num in All if compare(G, num) == A]
is computed,
and afterwards the results is assigned to All
overwriting whatever it was before.
Your code appends new numbers to All
: All.append(num)
while iterating over All
itself,
i.e. the original content of All
always remains.
You would need to create a new list All_new = []
, append to it All_new.append(num)
and finally set All = All_new
to mimic the original code.
PS Appending new elements to a list while iterating over it is a bit obscure but absolutely fine in Python; in fact it allows for a nice BFS implementation.
My bad, I copied earlier version. I already did what you said:
def compare(G, answer):
count=0
for i in range(len(G)):
if G[i]==answer[i]: count+=1
return count
newAll =[]
def search(numbers):
All = [str(num).zfill(4) for num in range(10000)] #all possible numbers from 0001 to 9999
for G, A in numbers:
# All = [num for num in All if compare(G, num) == A]
for num in All:
if compare(G, num) == A:
newAll.append(num)
All = newAll
print(len(All))
return All
with open("data.txt", "r") as f:
data = f.read().splitlines()
_num_of_guesses = int(data.pop(0))
numbers=[]
for line in data:
G, A = line.split() #make a list of tuples (guess, hint)
A = int(A)
numbers.append((G, int(A)))
print(search(numbers))
But still the result is completely different. Instead of one, I get 69984 different results. 7 times more than possible combinations. In the original version with list comprehension, there is only one. How does that code get to this conclusion, is beyond me.
The All = newAll
line is in the wrong scope. It should be inside the for G, A ...
loop.
If you need a hint re: how this code works, feel free to ask. But also please don't post solutions in the forum (especially other people's solutions); we can see your latest submitted code.
Man, I'm all over the place! Sorry. List comprehension aside, I think my main problem is that compare function. With this input I've been working with:
9009 0 2465 1 9058 0 7764 0 8901 0 7839 1 8041 0 3544 1 0817 0 0526 1 2146 1 3570 1 7117 0 0519 0
It takes each guess and compares it with every one of 10 000 numbers on the list: if G[i]==num[i]: count+=1. Then, if the A == 1, so only one digit in that guess is the correct digit, it keeps that guess in the All list. If it works, for example, with guess 2465, how does it know which digit is correct? In my code I started by eliminating all digits that I knew were INcorrect (because for example a guess 9009 was marked wih 0, so we know the first digit can't be 9), but this code doesn't do that. It checks that 2465 guess, then it should return every number that is 2XXX, every number that is X4XX and so on.
Unless. Unless it DOES do that, but then takes the next guess, 7839, and compares that guess only with numbers that are still in the list after iterating through 2465? Sorry, it's difficult to wrap your head around this combination (and mine is fried now after my own fruitless attempts at this problem) but I think I finally got it. Yes. It sort of makes a list of possible matches for every guess and then compares those lists of possible matches with each other.
Let's look at the shorter example with three digits.
There are 10^3 = 1000 candidate solutions.
The first guess in the input data states 402 0
.
Now if the solution was 000
, then you would get a match of 1
digit
(both have a 0
as the second digit but differ in the first and last digit).
What can you conclude about candidate 000
?
Can this principle be extended to all candidate solutions utilising all guesses provided in the input?
And what has Sherlock Holmes' guiding principle to do with it?