omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Crash when using Sleep() in Scene module

    Pythonista
    4
    9
    6489
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • chriswilson
      chriswilson last edited by

      Hi all,

      I have been using Pythonista to learn Python for a few months (I'm new to coding) and have been exploring the Scene module since the recent update.

      I've made a grid-based game in which players make a white path across the grid, and if successful, all the squares making up the path turn green.

      I use a function (see below) to recursively check for success (not sure if this is the best way or not) by checking the neighbours of each tile as the "path"
      progresses through the grid.

      It worked well until introducing time.sleep() as a very brief delay in order to "animate" the progress of the squares turning green. Now it crashes Pythonista but not in a predictable way, though it does seem to be when the function is running. The function is decorated with @ui.in_background.

      I'm just looking for any advice on how to improve my code to prevent this, or whether other people have had a similar problem.

      I can post the whole code if needed or if anyone interested.

      	@ui.in_background
      	def go(self, start_square):
      		try:
      			self.green_list.remove(start_square)
      		except:
      			pass
      		for square in start_square.white_neighbours(self.squares):
      			self.green_list.append(square)
      			square.run_action(go_action)
      			square.state = 3
      			square.color = color4
      			sleep(0.004)
      			self.go(square)
      		if len(self.green_list) == 0:
      			sleep(0.004)
      			self.check_win()
      
      
      1 Reply Last reply Reply Quote 0
      • Cethric
        Cethric last edited by

        Can you please post your code (preferably on GitHub or other git hosting service) as I can not get enough infomation out of this code to be able to find where the issue occurs.
        Can I also recommend that instead of a try: except: clause you check to see if start_square is in self.green_list

        1 Reply Last reply Reply Quote 1
        • JonB
          JonB last edited by

          not sure why sleep would be an issue, but one problem you will find when using in_background is that calls are queued up, rather than executing in order. So, ehile you might think you are doing a depth first search, you code really does a breadth first. If your go_action is also in_backgrounded, you might have issues, where code does not execute in the order you planned.

          I suspect if you get another touch event while go is running, this could cause issues, because you might be inserting a call to go where it doesn't belong. i.e the loop has queued up 4 calls to go(), but then a touch event wueues up another square to go(), then the 4 calls run, each queueing up other calls, then the extra touch runs, on the original square. Now everything is in a funky state.

          You might consider a non-backgrounded function, with ui.animate() to set colors. perhaps with a completion argument to call the next iteration.

          Another option would be to use a deque, and rather than recursing, you would add neighbors to the deque. Though recursion makes for nice compact code, it is often frowned upon in real world applications. A method I like for this sort of problem is a single worker loop with a deque. You populate the start square, then loop until the deque is empty. Depending on which side you pop/append you can make this do depth or breadth first.

          while len(mydeque):
              square =mydeque.pop()
              #process square.......... change color, etc
              for n in square.white_neighbors():
                   if n not in mydeque:
                       mydeque.append(n) 
          
          1 Reply Last reply Reply Quote 2
          • chriswilson
            chriswilson last edited by

            Thanks @Cethric and @JonB for the advice.

            I've posted the code here.

            1 Reply Last reply Reply Quote 1
            • chriswilson
              chriswilson last edited by chriswilson

              I thought I should post the end result of this discussion.

              I incorporated the deque idea from @JonB and got the animation effect I needed using the Action.call() method without having to use time.sleep() or a @ui.in_background function.

              Here it is in a shortened form to show the concept without unnecessary application-specific stuff:

              def go(self, start_square):
              	
              	def cascade(node, progress):  # Nested animation function
              		if progress == 1:
              			node.color = color4 if self.win else color3
              				
              	self.green_list.append(start_square)
              	index = 0.01
              	while self.green_list:
              		square = self.green_list.pop(randint(0, len(self.green_list) - 1))
              		square.run_action(A.call(cascade, index))
              		index += 0.01
              			
              		for n in square.white_neighbours(self.squares):
              			if n not in self.green_list:
              				self.green_list.append(n)
              	
              	self.check_win()  # Once list is empty, check win status
              
              1 Reply Last reply Reply Quote 0
              • ccc
                ccc last edited by ccc

                        if progress == 1:
                            node.color = color4 if self.win else color3
                

                Could green_list instead be green_set to automatically avoid duplicates?

                chriswilson 1 Reply Last reply Reply Quote 1
                • chriswilson
                  chriswilson @ccc last edited by

                  @ccc
                  I'm not familiar with that concept.

                  For simplicity here I have omitted that I used a square.state variable which changes without any additional delay and prevents a square being added to the list once it has already been 'dealt with'. The colour-changing animation then cascades along behind this. The self.check_win method then checks the state of the square at the exit of the grid to determine a win or lose.

                  Perhaps I should not have omitted this from the code snippet. Mainly wanted to show use of the Action.call() method which was new to me.

                  Thanks for your input as always! :)

                  1 Reply Last reply Reply Quote 0
                  • ccc
                    ccc last edited by ccc

                    That concept means conditional expressions or sets?

                    chriswilson 1 Reply Last reply Reply Quote 0
                    • chriswilson
                      chriswilson @ccc last edited by chriswilson

                      @ccc
                      Ah I see what you mean. I often forget to use conditional expressions when I could. As regards sets, I guess I know the concept but I'm not familiar with how to use them. I'll look at this. Thanks.

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post
                      Powered by NodeBB Forums | Contributors