# guess your number

Simple script that asks you to think of a number between 1 and 63 then offers list of numbers and asks if your number is in that list. Can this be refactored further? Are there better ways to create lists? Any improvements?

x= [xx for xx in range(1,64)]
num_i = [i for i in range(1,64,2)]
num_ii = x[1::16]+ x[2::16]+ x[5::16]+ x[6::16]+ \
x[9::16]+ x[10::16]+ x[13::16]+ x[14::16]
num_ii.sort()
num_iii= x[3::16]+ x[4::16]+ x[5::16]+ x[6::16]+ \
x[11::16]+ x[12::16]+ x[13::16]+ x[14::16]
num_iii.sort()
num_iv=  x[7::16]+ x[8::16]+ x[9::16]+ x[10::16]+ \
x[11::16]+ x[12::16]+ x[13::16]+ x[14::16]
num_iv.sort()
num_v= x[15::32]+ x[16::32]+ x[17::32]+ x[18::32]+ \
x[19::32]+ x[20::32]+ x[21::32]+ x[22::32]+ \
x[23::32]+ x[24::32]+ x[25::32]+ x[26::32]+ \
x[27::32]+ x[28::32]+ x[29::32]+ x[30::32]
num_v.sort()
num_vi= [x for x in range(32,64)]
num_lists=[num_i,num_ii,num_iii,num_iv,num_v,num_vi]
start_end= ["Think of a number between 1 and 63","I think the number you thought of was ..."]
intro= ["Is your number in the group of numbers below?",
"Is your number in this group of numbers ?","Is your number in this third group of numbers ?",
"Half way there. Is your number in this group of numbers ?",
"One more after this one. Is your number in this group of numbers ?",
"Last one. Is your number in this group of numbers ?"]
def format_list(xx):
for a,b,c,d,e,f,g,h in zip(xx[::8],xx[1::8],xx[2::8],xx[3::8],xx[4::8],xx[5::8],xx[6::8],xx[7::8]):
print('{:<8}{:<8}{:<8}{:<8}{:<8}{:<8}{:<8}{:<}'.format(a,b,c,d,e,f,g,h))
print('-'*62)
def main():
count=0
your_guess= 0
print(start_end[0])
ready= input('are you ready?(enter to quit or any key)')
while count < 6:
print(intro[count])
format_list(num_lists[count])
reply = input("y/n ?")
if reply == "y" :
count +=1
print(start_end[1] + str(your_guess))
replay= input('Play again? (enter to quit or any key)')
if replay:
main()
if __name__ == '__main__':
main()

• What's the basis by which you've chosen 1, 2, 5, 6, 9, 10, 13, 14? Are these basically randomly selected? Commented Oct 17, 2020 at 13:12
• @Reinderien it's a common boolean algebra problem. the lists are numbers with binary expressions having 1 at n-th bit, so binary lists would be: xxxxx1, xxxx1x, xxx1xx, ... hence 32 elements in each of those Commented Oct 17, 2020 at 14:38

## Magic numbers

As already mentioned in comments, the numbers $$\ 1, 2, \cdots \$$ appear to be completely random to anyone not sure about how the summation is going to (or supposed to) work. The generation of the lists is also very cumbersome. The same trick could be expanded to a selection upto $$\ 127, 255, 511, \cdots, 2^{n} - 1 \$$.

## Recursive main

You have a recursive call to main in case the player wants to replay the game. Recursive calls when you never return might lead to stack overflows. In this scenario, recursion is not really needed anyway.

## Overusage of lists

You have almost everything put inside lists. This includes all the numbers from $$\ 1-63 \$$, all the generated charts from these numbers, aggregation of all the above lists, list of powers of $$\ 2 \$$, and so on.

For some of those, a tuple (immutable) is good, others do not need to be placed in a list anyway.

## Variable naming

x, xx, xxx, xxxx, .. are not really meaningful. Use better variable names.

For generation of the numerical lists:

for start in (2 ** i for i in range(6)):
l = [_ for _ in range(start, 64) if _ % (start * 2) >= start]


Rewrite:

INTROS = (
"Is your number in the group of numbers below?",
"Is your number in this group of numbers?",
"Is your number in this third group of numbers?",
"Half way there. Is your number in this group of numbers?",
"One more after this one. Is your number in this group of numbers?",
"Last one. Is your number in this group of numbers?",
# "Or maybe the following?",
# "Add more intro strings here",
# "to get larger guessing range",
)
COUNT = len(INTROS)
UPPER_LIMIT = 2 ** COUNT

def display_chart(index: int, columns: int = 8):
print(INTROS[index])
start = 1 << index
chart = [str(x) for x in range(start, UPPER_LIMIT) if x % (start * 2) >= start]
for i in range(len(chart) // columns):
print("\t".join(chart[i * columns : (i + 1) * columns]))

def game():
guess = 0
print(f"Think of a number between 1 and {UPPER_LIMIT - 1}")
for current in range(COUNT):
display_chart(current)
reply = input("y/n? ")
if reply == "y":
guess += 1 << current
print(f"I think the number you thought of was ... {guess}")

def main():
while True:
game()
character = input("Play again? (enter to quit or any key)")
if not character:
break

if __name__ == "__main__":
main()


The rewrite is completely dynamic in nature now. If you want to increase the range, just add another section to the INTROS tuple.

• Also, in most cases here you should prefer 1 << x over 2 ** x. Commented Oct 17, 2020 at 16:35
• Otherwise this is quite good, and basically where I would have landed :) Commented Oct 17, 2020 at 16:49
• @Reinderien Why prefer 1 << x here, and why only in most cases (which ones)? Commented Oct 17, 2020 at 17:19
• @StefanPochmann I'm on the fence as to whether UPPER_LIMIT  should use exponentiation or shift, because I don't properly understand the algorithm being applied; this is a style choice and certainly not a performance factor. However, for the other instances (that @hjpotter92 has already changed) they should be shifts because conceptually, they are actually bit-wise operations and not "math operations", so the shift shows intention more clearly. Commented Oct 17, 2020 at 17:24
• Nice review. The first print can be Think of a number between 1 and {UPPER_LIMIT-1}.
– Marc
Commented Oct 18, 2020 at 3:03

First pass:

• Move most of the list creation stuff into a function
• PEP8 fixes: standardize spaces, get rid of continuation escapes, spaces between functions
• Some immutable tuples instead of lists
def make_num_lists():
x = [xx for xx in range(1, 64)]
num_i = [i for i in range(1, 64, 2)]
num_ii = (x[ 1::16] + x[ 2::16] + x[ 5::16] + x[ 6::16] +
x[ 9::16] + x[10::16] + x[13::16] + x[14::16])
num_ii.sort()
num_iii = (x[ 3::16] + x[ 4::16] + x[ 5::16] + x[ 6::16] +
x[11::16] + x[12::16] + x[13::16] + x[14::16])
num_iii.sort()
num_iv = (x[ 7::16] + x[ 8::16] + x[ 9::16] + x[10::16] +
x[11::16] + x[12::16] + x[13::16] + x[14::16])
num_iv.sort()
num_v = (x[15::32] + x[16::32] + x[17::32] + x[18::32] +
x[19::32] + x[20::32] + x[21::32] + x[22::32] +
x[23::32] + x[24::32] + x[25::32] + x[26::32] +
x[27::32] + x[28::32] + x[29::32] + x[30::32])
num_v.sort()
num_vi = [x for x in range(32, 64)]
return [num_i, num_ii, num_iii, num_iv, num_v, num_vi]

num_lists = make_num_lists()
addition_num = (1, 2, 4, 8, 16, 32)
start_end = (
"Think of a number between 1 and 63",
"I think the number you thought of was ...",
)
intro = (
"Is your number in the group of numbers below?",
"Is your number in this group of numbers ?",
"Is your number in this third group of numbers ?",
"Half way there. Is your number in this group of numbers ?",
"One more after this one. Is your number in this group of numbers ?",
"Last one. Is your number in this group of numbers ?",
)

def format_list(xx):
for a,b,c,d,e,f,g,h in zip(xx[::8],xx[1::8],xx[2::8],xx[3::8],xx[4::8],xx[5::8],xx[6::8],xx[7::8]):
print('{:<8}{:<8}{:<8}{:<8}{:<8}{:<8}{:<8}{:<}'.format(a,b,c,d,e,f,g,h))
print('-'*62)

def main():
count = 0
your_guess = 0
print(start_end[0])
ready = input('are you ready?(enter to quit or any key)')
while count < 6:
print(intro[count])
format_list(num_lists[count])
reply = input("y/n ?")
if reply == "y":
count += 1
print(start_end[1] + str(your_guess))
replay = input('Play again? (enter to quit or any key)')
if replay:
main()

if __name__ == '__main__':
main()


But this is not sufficient. Do what @hjpotter92 suggested.

• thanks the conversion to tuples looks clearer. Thanks for taking the time, Joe Commented Oct 17, 2020 at 17:42