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]
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)')
if ready:
while count < 6:
print(intro[count])
format_list(num_lists[count])
reply = input("y/n ?")
if reply == "y" :
your_guess += addition_num[count]
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()
2 Answers 2
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.
-
1\$\begingroup\$ Also, in most cases here you should prefer
1 << x
over2 ** x
. \$\endgroup\$Reinderien– Reinderien2020年10月17日 16:35:42 +00:00Commented Oct 17, 2020 at 16:35 -
\$\begingroup\$ Otherwise this is quite good, and basically where I would have landed :) \$\endgroup\$Reinderien– Reinderien2020年10月17日 16:49:59 +00:00Commented Oct 17, 2020 at 16:49
-
1\$\begingroup\$ @Reinderien Why prefer
1 << x
here, and why only in most cases (which ones)? \$\endgroup\$Stefan Pochmann– Stefan Pochmann2020年10月17日 17:19:45 +00:00Commented Oct 17, 2020 at 17:19 -
1\$\begingroup\$ @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. \$\endgroup\$Reinderien– Reinderien2020年10月17日 17:24:10 +00:00Commented Oct 17, 2020 at 17:24 -
1\$\begingroup\$ Nice review. The first print can be
Think of a number between 1 and {UPPER_LIMIT-1}
. \$\endgroup\$Marc– Marc2020年10月18日 03:03:04 +00:00Commented 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)')
if ready:
while count < 6:
print(intro[count])
format_list(num_lists[count])
reply = input("y/n ?")
if reply == "y":
your_guess += addition_num[count]
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.
-
\$\begingroup\$ thanks the conversion to tuples looks clearer. Thanks for taking the time, Joe \$\endgroup\$just_joe– just_joe2020年10月17日 17:42:55 +00:00Commented Oct 17, 2020 at 17:42
1
at n-th bit, so binary lists would be:xxxxx1
,xxxx1x
,xxx1xx
, ... hence 32 elements in each of those \$\endgroup\$