# Thread: This smol snek is bamboozling (Python help)

1. ## This smol snek is bamboozling (Python help)

I'm trying to teach myself some coding. Python 3.7. Lots of coffee. Still very new at this, and I'm finding it really hard to wrap my brain around the logic of how and why things work, embarrassingly often. But it's pretty fun. Often infuriating, but fun.

This code (putting it here in it's entirety, sorry for the numerous #comments) has two terms that are used to keep count of things, one that should reset after every loop (las), and one that shouldn't (pis).
What do I have to do to 'pis' to keep it from resetting? I'm quessing at least putting it outside of the loop somewhere, but that's ended in error messages so far, no matter what I try...

Spoiler: Code

Code:
```from random import *

def quessgame():
s = randint(1, 100) #generates a random number
q = 0 #this is needed, because terms just have to have a value before you do things with them?
las = -1 #sets the starting point for quess count
pis = 0
print ("Your secret random number is", s) #displays the random number, so testing code is easier
while q != s:
las += 1 #same as las = las+1. Bit more confusing this way? Any benefits?
print("Quesses:", las) #this shows how many quesses you've had this time
q = int(input("Quess the number between 1 - 100:"))
if q < s-5:
print("Number is higher")
elif q > s+5:
print("Number is lower")
elif q < s:
print("Close, but the number is slightly higher")
elif q > s:
print("Almost, but the number is a bit smaller")
else:
pis += 1
print("You quessed right,", s ," was correct")
print("Correct answers so far: ", pis) #shows the number of right answers.. in theory
print(".....................................")
return quessgame() #starts the "game" over
quessgame()```

2. ## Re: This smol snek is bamboozling (Python help)

Note: I'm quite familiar with python 2.7, but the differences with python 3.7 are mostly superficial, and shouldn't matter for this question.

If I understand correctly, 'pis' is supposed to track the total number of times you've won the game, correct?

Currently, you've got a function that seems to run the game once(you call it again at the end of the function to keep going, but fundamentally this function runs it only once), and you define PIS within the function. If you want PIS to keep tracking victories, you need to move it outside of the 'single game' function, as it gets reset whenever the function is called.

A very simple change that should probably work:

Spoiler: Code

Code:
```from random import *
pis = 0

def quessgame(pis):
s = randint(1, 100) #generates a random number
q = 0 #this is needed, because terms just have to have a value before you do things with them?
las = -1 #sets the starting point for quess count

print ("Your secret random number is", s) #displays the random number, so testing code is easier
while q != s:
las += 1 #same as las = las+1. Bit more confusing this way? Any benefits?
print("Quesses:", las) #this shows how many quesses you've had this time
q = int(input("Quess the number between 1 - 100:"))
if q < s-5:
print("Number is higher")
elif q > s+5:
print("Number is lower")
elif q < s:
print("Close, but the number is slightly higher")
elif q > s:
print("Almost, but the number is a bit smaller")
else:
pis += 1
print("You quessed right,", s ," was correct")
print("Correct answers so far: ", pis) #shows the number of right answers.. in theory
print(".....................................")
return quessgame(pis) #starts the "game" over
quessgame()```

In python 2.7, calling the function in itself will also eventually give you an error related to recursion depth (and I'd expect the same behaviour in 3.7), so I'd create a separate loop that keeps calling your function again and again, rather than re-calling it within the same function.

edit: I did a quick barebones test using code similar to yours, and it might be necessary to end the nested functions to be able to read out 'pis', post game (IE: outside the nested loop), as right now pis never gets returned from the function. That wouldn't matter in game-play, but will give a result of pis=0 if you call up the value of pis after a run for bug-fixing purposes.

3. ## Re: This smol snek is bamboozling (Python help)

The problem comes down to this line:
Code:
`return quessgame() #starts the "game" over`
That does exactly what the comment says, it starts the entire game over, with completely fresh state, including "pis". Each time you call a function it gets a fresh set of local variables. As DeTess already said, it's also a poor way to implement a loop.

One general recommendation is to use longer variable names, real words rather than abbreviations. Faster to type may be nice, but easier to read is more important. It will help you in the long run.

I would also recommend splitting your code up into more functions. Have each function do one thing. Each part should be easier to understand in isolation, and you can get each part working one at a time. An important benefit is reducing the amount of shared state that each line of code has access to. Docstrings are also a good habit to get into.

Below I introduce two new things. First is the "itertools.count(n)" function, which produces a stream of numbers starting from "n", or from zero if not passed an argument. I find it easier to read than having "n += 1" somewhere in the loop body. The second is the "break" keyword, which immediately exits the containing loop. Compared to a while loop, using break in the for loop doesn't require any state to be set up before first entering the loop.

Code:
```from random import randint
from itertools import count

def input_guess():
"""Prompts for a guess, returning the integer. No error checking yet."""
return int(input("Quess the number between 1 - 100: "))

def compare(guess, goal):
"""Compares a guess to the goal, printing a message and returning whether it matched."""
if guess < goal - 5:
print("Number is higher")
return False
elif guess > goal + 5:
print("Number is lower")
return False
elif guess < goal:
print("Close, but the number is slightly higher")
return False
elif guess > goal:
print("Almost, but the number is a bit smaller")
return False
else:
print("You quessed right,", goal, "was correct")
return True

def one_round(goal):
"""Runs a single round, returning true if the guess was matches the given goal."""
guess = input_guess()
return compare(guess, goal)

def one_game():
"""Runs a single game to completion."""
goal = randint(1, 100)
print("Debug: the goal is ", goal)
for guess_count in count(1):
print("Quesses:", guess_count)
if one_round(goal):
break

def game_loop():
"""Keeps running games forever."""
for game_count in count(1):
one_game()
print("Correct answers so far: ", game_count)
print(".....................................")

game_loop()```

4. ## Re: This smol snek is bamboozling (Python help)

Wow, thank you. That does look more clear and clean. Now I just don't understand why or how anything works again, unlike in my own attempt which I could understand. :p But it works. Guess got to get tinkering with it until it doesn't seem like magic anymore..

5. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by thirsting
Code:
`q = 0 #this is needed, because terms just have to have a value before you do things with them?`
Yes. Many programming languages require you to separately declare variables before you can use them; Python takes care of that for you when you give them values.

6. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by Excession
The problem comes down to this line:
Code:
`return quessgame() #starts the "game" over`
That does exactly what the comment says, it starts the entire game over, with completely fresh state, including "pis". Each time you call a function it gets a fresh set of local variables. As DeTess already said, it's also a poor way to implement a loop.
It should be noted that there are some programming styles that do something similar. The second call should always however be something simpler.

E.g
Code:
```def fib(n):
if n<=1:
return 1
else:
return fib(n-1)+fin(n-2)```
works it's way down.
Python isn't really set up to make full use of it. Because it makes it harder to track down errors when they do occur, (but for the right kind of function easier to mathematically prove). It's fairly easy to restructure differently for complex cases (which in this case would be much more efficient), and pythons stack is big enough to handle simple uses.

Another thing to mention is classes. Again they break some of the nice assumptions you can make if you keep things functional. But they do provide a bit of a middle ground with regard to variables. You get to wrap things upnicely though in practice choosing the right boundaries is difficult.

Spoiler: TBH a bit forced, they come in their own when things get a bit bigger

Code:
```from random import randint

class Game:
from random import randint

class Game:
"""A running (or finished) game"""
def __init__(self,mintarget,maxtarget):

self.goal=randint(mintarget,maxtarget)
self.won=False
self.lost=False
self.guesses=0

def input_guess(self):
"""Prompts for a guess, returning the integer. No error checking yet."""
return int(input("Quess the number between 1 - 100: "))

def compare(self,guess):
"""Compares a guess to the goal, printing a message and returning whether it matched."""
if guess < self.goal - 5:
print("Number is higher")
return False
elif guess > self.goal + 5:
print("Number is lower")
return False
elif guess < self.goal:
print("Close, but the number is slightly higher")
return False
elif guess > self.goal:
print("Almost, but the number is a bit smaller")
return False
else:
print("You quessed right,", self.goal, "was correct")
return True

def takeTurn(self):
"""Makes a single guess, has an interesting edge case"""
self.guesses=self.guesses+1
if self.compare(self.input_guess()):
self.won=True
if self.guesses>=50:
self.lost=True
print("You took too long,", self.goal, "was the answer")

def stopped(self):
return self.won or self.lost

class Game_Host:
def __init__(self):
self.wins=0
self.losses=0
self.currentgame=Game(1,100)
def play(self):
self.currentgame.takeTurn()
if self.currentgame.stopped():
if self.currentgame.won:
self.wins=self.wins+1
if self.currentgame.lost:
self.losses=self.losses+1

self.currentgame=Game(1,100)

def start(self):
"""plays the game forwever"""
while True:
print("Current Score is ",self.wins,",",self.losses)
print("I don't know why I tell you this each turn")
self.play()

Game_Host().start()```

7. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by thirsting
Wow, thank you. That does look more clear and clean. Now I just don't understand why or how anything works again, unlike in my own attempt which I could understand. :p But it works. Guess got to get tinkering with it until it doesn't seem like magic anymore..
Working out the best way to split up code is a complex skill to learn. So is reading someone else's code I'm afraid.

Being sure you understand everything and it should all work perfectly is the normal starting point for a long, frustrating hour of debugging. Welcome to the wonderful world of computer programming.

8. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by Excession
Being sure you understand everything and it should all work perfectly is the normal starting point for a long, frustrating hour of debugging. Welcome to the wonderful world of computer programming.
But...debugging is the *fun* part? It's like a murder mystery and you're the detective--how do I track down the rogue characters that are killing my code?

9. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by factotum
But...debugging is the *fun* part? It's like a murder mystery and you're the detective--how do I track down the rogue characters that are killing my code?
That's a nasty trap, that is very easy to fall into. If you're not 100% uncrazy, it's easy to write bugs so you can spend time finding them, and that's very bad. Binary chop on your code is your friend, it's not exciting, but it gets you to the relevant area fast (won't work that well if you've got two or more areas of code that are only buggy in interaction, but should find random characters).

10. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by halfeye
That's a nasty trap, that is very easy to fall into. If you're not 100% uncrazy, it's easy to write bugs so you can spend time finding them, and that's very bad.
But if you deliberately wrote the bug you already know where it is, so it would be like a murder mystery where you know the murderer right at the start...which may work for Columbo, but not for programmers.

11. ## Re: This smol snek is bamboozling (Python help)

Originally Posted by factotum
But if you deliberately wrote the bug you already know where it is, so it would be like a murder mystery where you know the murderer right at the start...which may work for Columbo, but not for programmers.
Not deliberately in your conscious mind, but how many bugs are subconsciously known? It's a knot, and the Gordion solution is the best by far.

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•