The Order of the Stick: Utterly Dwarfed
The Order of the Stick: Utterly Dwarfed - Coming in December and available for pre-order now
Results 1 to 11 of 11
  1. - Top - End - #1
    Bugbear in the Playground
    Join Date
    Jan 2010
    Location
    Creamy, creamy Milky Way

    Default 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
    Show

    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()
    Last edited by thirsting; 2019-08-08 at 02:58 PM.
    Got bitten by a human once. Ever since, been turning into one during daytime. It's exhausting.

  2. - Top - End - #2
    Bugbear in the Playground
     
    DeTess's Avatar

    Join Date
    May 2017
    Gender
    Female

    Default 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
    Show

    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.
    Last edited by DeTess; 2019-08-08 at 03:47 PM.
    Jasnah avatar by Zea Mays

  3. - Top - End - #3
    Bugbear in the Playground
     
    Excession's Avatar

    Join Date
    Jun 2008
    Location
    New Zealand
    Gender
    Male

    Default 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()
    Last edited by Excession; 2019-08-08 at 07:41 PM.

  4. - Top - End - #4
    Bugbear in the Playground
    Join Date
    Jan 2010
    Location
    Creamy, creamy Milky Way

    Default 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..
    Got bitten by a human once. Ever since, been turning into one during daytime. It's exhausting.

  5. - Top - End - #5
    Firbolg in the Playground
     
    Jasdoif's Avatar

    Join Date
    Mar 2007
    Location
    Oregon, USA

    Default Re: This smol snek is bamboozling (Python help)

    It looks your main question's been answered, so....

    Quote Originally Posted by thirsting View Post
    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. - Top - End - #6
    Barbarian in the Playground
     
    PaladinGuy

    Join Date
    Sep 2016

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by Excession View Post
    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
    Show

    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. - Top - End - #7
    Bugbear in the Playground
     
    Excession's Avatar

    Join Date
    Jun 2008
    Location
    New Zealand
    Gender
    Male

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by thirsting View Post
    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. - Top - End - #8
    Titan in the Playground
     
    BlackDragon

    Join Date
    Feb 2007
    Location
    Manchester, UK
    Gender
    Male

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by Excession View Post
    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. - Top - End - #9
    Troll in the Playground
     
    Griffon

    Join Date
    Jun 2013
    Location
    Bristol, UK

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by factotum View Post
    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).
    The end of what Son? The story? There is no end. There's just the point where the storytellers stop talking.

  10. - Top - End - #10
    Titan in the Playground
     
    BlackDragon

    Join Date
    Feb 2007
    Location
    Manchester, UK
    Gender
    Male

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by halfeye View Post
    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. - Top - End - #11
    Troll in the Playground
     
    Griffon

    Join Date
    Jun 2013
    Location
    Bristol, UK

    Default Re: This smol snek is bamboozling (Python help)

    Quote Originally Posted by factotum View Post
    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.
    The end of what Son? The story? There is no end. There's just the point where the storytellers stop talking.

Posting Permissions

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