The "IT DOESN'T WORK!" thread

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

Re: The "IT DOESN'T WORK!" thread

Postby TheChewanater » Mon Oct 10, 2011 12:03 am UTC

In Python 3, input() returns a string, but you're comparing it to integers. Instead, you should compare the input to the strings '1' and '2'.

Try this:
Code: Select all
if mode == '1':

and
Code: Select all
elif mode == '2':
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.
User avatar
TheChewanater
 
Posts: 1261
Joined: Sat Aug 08, 2009 5:24 am UTC

Re: The "IT DOESN'T WORK!" thread

Postby xGeovanni » Mon Oct 10, 2011 6:10 pm UTC

Thanks, the solution was simple when you told me, can't believe I didn't think of it.
xGeovanni
 
Posts: 32
Joined: Thu Oct 06, 2011 7:03 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby mister_m » Tue Oct 11, 2011 7:09 pm UTC

I am trying to compute the possible paths from node 0 on this graph, to node 2. I have the graph stored in the following adjacency matrix:
Code: Select all
      0 1 2
    0 1 1 1
    1 1 1 1 
    2 1 1 1


The method I read about was multiplying the matrix against itself n times, where n is the maximum path length that you want to find, and then you look up matrix(i, j), where i is the start node, and j is the end node.

My following algorithm keeps giving me 3, but it should be 2. What am I doing incorrectly?
Code: Select all
def matrix_mult(matrix, n, m_len):
        matrix_new = [[1 for x in range(m_len)] for x in range(m_len)]
        for x in range(n):
            for i in range(m_len):
                for j in range(m_len):
                    #to get matrix(i, j), compute sum of the products in the ith row in the first matrix, and the jth row in the 2nd
                    matrix_new[i][j] = sum(map(lambda x : x[0]*x[1], zip(matrix[i], matrix[j])))
 
        return matrix_new             

    n = graph[0][num_cities-1]
    m = matrix_mult(graph, n, num_cities)

    print m[0][num_cities-1] 



Is this algorithm faulty? It returns a graph that has threes for each element. If I understand correctly, the original graph should have two possible paths from 0 to 2. Ideas are welcome.
User avatar
mister_m
 
Posts: 33
Joined: Fri Feb 04, 2011 2:51 am UTC

Re: The "IT DOESN'T WORK!" thread

Postby jaap » Tue Oct 11, 2011 7:53 pm UTC

I've not looked at your code, but I think you are counting these three paths:
0-0-2, 0-1-2, and 0-2-2.
Because you put ones on the matrix diagonal, you are allowed to stay in the same city at any stage of the path.
I would put zeroes on the diagonal, and then you will only count the number of paths that are exactly n steps long. You will then obviously have to sum up the results for 1..n if you want any length path up to a maximum of n.
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Tue Oct 11, 2011 8:10 pm UTC

Your code also seems to require that the matrix in question be symmetrical. As it happens, it is -- but that seems wrong to assume.

I'd personally avoid reusing a variable like x in your loop and lambda. Needlessly confusing.

You aren't feeding matrix_new back into your multiplication. I don't see how n could change the result of your function.

Practically, you want 3 matrices to keep it easy, or write an actual matrix_mult function that multiplies two matrices and returns the result.

To rewrite:
Code: Select all
def matrix_transpose(matrix, m_len):
        matrix_result = [[0 for x in range(m_len)] for x in range(m_len)]
        for i in range(m_len):
            for j in range(m_len):
                matrix_result[i][j] = matrix[j][i]
        return matrix_result             

def matrix_mult(left, right, m_len):
        right = matrix_transpose(right, m_len)
        matrix_result = [[0 for x in range(m_len)] for x in range(m_len)]
        for i in range(m_len):
            for j in range(m_len):
                #to get matrix(i, j), compute sum of the products in the ith row in the first matrix, and the jth row in the 2nd
                matrix_result[i][j] = sum(map(lambda x : x[0]*x[1], zip(left[i], right[j])))

        return matrix_result

def matrix_pow(matrix, n, m_len):
        matrix_result = [[0 for x in range(m_len)] for x in range(m_len)]
        for i in range(m_len):
            # identity matrix!  Handles n=0 case
            matrix_result[i][i] = 1
        for unused in range(n):
            matrix_result = matrix_mult( matrix_result, matrix, m_len )

        return matrix_result

which can be simplified muchly, but better to have correct code that is easy to simplify than incorrect code that is lean.

For larger n, breaking down its bits and doing a logarithmic number of multiplications is better. But that would make the algorithm more complex to start with, and it isn't as if I compiled or tested the above.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby phlip » Thu Oct 13, 2011 2:26 am UTC

It Doesn't Work: Internet Explorer edition...
While no one overhear you quickly tell me not cow cow.
but how about watch phone?
User avatar
phlip
Restorer of Worlds
 
Posts: 6739
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia

Re: The "IT DOESN'T WORK!" thread

Postby jawdisorder » Fri Nov 11, 2011 6:16 am UTC

Compiling the following code the first function doesn't give any problems, but the second (copyList) gives "SortedList.cpp:184:1: error: ‘ListNode’ does not name a type." I don't understand how the first function can compile and it can have a problem with the second.
Code: Select all
void SortedList::freeList(ListNode* L){
   ListNode* next;
   while (L != 0){
      next = L -> next;
      delete L -> student;
      delete L;
      L = next;
   }
}

ListNode* SortedList::copyList(ListNode* L){
   if (L == 0){
      return 0;
   }
   ListNode* n, temp;   
   n = temp = new ListNode;
   n -> student = L -> student;
   L = L -> next;
   while (L != 0){
      temp = new ListNode;
      temp -> student = L -> student;
      L = L -> next;
   }
   return n;
}



This is how they are in the SortedList.h file:
Code: Select all
struct ListNode {   
    Student *student;
    ListNode *next;
};
static ListNode* copyList(ListNode* L);
static void freeList(ListNode* L);


Any suggestions would be greatly appreciated.
User avatar
jawdisorder
 
Posts: 195
Joined: Sat Feb 05, 2011 2:44 am UTC
Location: Badgerland

Re: The "IT DOESN'T WORK!" thread

Postby jaap » Fri Nov 11, 2011 6:32 am UTC

jawdisorder wrote:
Code: Select all
ListNode* n, temp;

This line does not say what you think it says. It is equivalent to
Code: Select all
ListNode* n;
ListNode temp;

whereas you want
Code: Select all
ListNode* n;
ListNode* temp;

or
Code: Select all
ListNode* n, * temp;
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: The "IT DOESN'T WORK!" thread

Postby jawdisorder » Fri Nov 11, 2011 6:43 am UTC

Okay thanks for that much but the error is still there. As far as I can tell it doesn't check anything in the function's body since I've changed it to many different things and it still gives me the Does not name a type error. I don't see what is wrong with the function header either though when as far as I can tell it's typed exactly as it should be.
User avatar
jawdisorder
 
Posts: 195
Joined: Sat Feb 05, 2011 2:44 am UTC
Location: Badgerland

Re: The "IT DOESN'T WORK!" thread

Postby phlip » Fri Nov 11, 2011 6:48 am UTC

I'm assuming that all of the declarations in the SortedList.h you gave are actually inside some SortedList class? Given your function definitions are SortedList::etc. If so, then that's your problem (and it's weird that you would have left that detail out of your post...).

In the parameter declarations and inside the body of the function, it can use the context of the function to determine that by "ListNode" you mean "SortedList::ListNode", but in the return-type declaration of the whole function it won't do that. So you need to define the function as:
Code: Select all
SortedList::ListNode * SortedList::copyList(ListNode *n)
{
  //code
}
While no one overhear you quickly tell me not cow cow.
but how about watch phone?
User avatar
phlip
Restorer of Worlds
 
Posts: 6739
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia

Re: The "IT DOESN'T WORK!" thread

Postby jawdisorder » Fri Nov 11, 2011 6:56 am UTC

Thank you! I was starting to become rather angry. I don't think I would have ever thought of that... I'm fairly sure it was never even mentioned any time during class either.

PS: Yes there is a SortedList class defined... now that you mentioned it I'm not entirely sure why I left that detail out.
User avatar
jawdisorder
 
Posts: 195
Joined: Sat Feb 05, 2011 2:44 am UTC
Location: Badgerland

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Fri Nov 11, 2011 4:04 pm UTC

If you have a C++0x compliant compiler, this might work:
auto SortedList::copyList(ListNode *n)->ListNode *
but I lack such a compiler, and I'm not sure if it would work, and there aren't any fully compliant C++0x compilers, so...
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby sourmìlk » Sat Nov 26, 2011 1:25 am UTC

So, I'm trying to get GLUT to work using Cygwin on Windows. I refuse to use VC++, as my code needs to be cross platform, and because fuck Microsoft, that's why.

However, when I create a very simple program that does nothing but use GLUT to open a window and initializes GLEW, I get this error at runtime:
Code: Select all
freeglut ([project file path]): failed to open display ''


The code, in it's entirety, is as follows:

Code: Select all
#include <iostream>

#include <glew.h>
#include <GL/glut.h>

using namespace std;

int main(int argc, char **argv)
{
   glutInit(&argc, argv);

   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);

   glutInitWindowPosition(0, 0);
   glutInitWindowSize(1920, 1080);
   glutCreateWindow("TEST");

   GLenum err = glewInit();

   if(err != GLEW_OK)
   {
      cout <<"ERROR: could not load GLEW. " <<glewGetErrorString(err) <<endl;
      return 0;
   }

   cout <<"GLEW loaded, running version " <<glewGetString(GLEW_VERSION) <<endl;

   return 0;
}


I made sure I linked to GL, glut, and glew32. I have no compile-time errors. Is there something wrong with the code? Am I going to have to go into some obscure config file and screw with something that has the potential to destroy the OS' rendering capabilities?
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.
User avatar
sourmìlk
If I can't complain, can I at least express my fear?
 
Posts: 6405
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong

Re: The "IT DOESN'T WORK!" thread

Postby TheChewanater » Sat Nov 26, 2011 2:07 am UTC

This has something to do with how the x window system is emulated on WIndows. Using the MSVC version of GLUT would probably fix the problem. Also, remember that the point of libraries like GLUT is that you could write code for either compiler and it should work across platforms.
ImageImage
http://internetometer.com/give/4279
No one can agree how to count how many types of people there are. You could ask two people and get 10 different answers.
User avatar
TheChewanater
 
Posts: 1261
Joined: Sat Aug 08, 2009 5:24 am UTC

Re: The "IT DOESN'T WORK!" thread

Postby sourmìlk » Sat Nov 26, 2011 3:18 am UTC

I thought the point of GLUT was that I could write code for either OS and it would still compile. God damn microsoft.

EDIT: Just realized I know have to go through the trouble of installing freeglut and OpenGL for MSCV++. Oy.

OTHER EDIT: Okay, installing GLUT and OpenGL and debugging the project for MSVC is looking like way more trouble than it's worth. Is there a way I can compile a GLUT program using Cygwin? Will that impact performance?
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.
User avatar
sourmìlk
If I can't complain, can I at least express my fear?
 
Posts: 6405
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong

Re: The "IT DOESN'T WORK!" thread

Postby jareds » Sat Nov 26, 2011 4:50 pm UTC

GLUT may provide cross-platform compatibility, but that doesn't mean you can build a single executable that supports all of GLUT's targets: it just means that the source code you pasted will compile for multiple platforms. It sounds like you built an application with GLUT for Cygwin. If you run it under Cygwin and have X set up for Cygwin, I expect it will work. In any event, you can run "ldd myprogram.exe" and see if cygwin*.dll is listed; if so, you have built a Cygwin application. It is important to understand that Cygwin and native Windows are, for most practical purposes, distinct platforms: it just happens that Cygwin applications will run under Windows with Cygwin installed.

It sounds like you want to build an application with GLUT for native Windows, and you don't want to use Visual Studio. While this is probably possible using Cygwin, the way to do it is not to install GLUT with the Cygwin setup utility and use that. That library will only support Cygwin. Personally, for development of native Windows applications using GNU tools, I would very strongly recommend MinGW instead of Cygwin, as that is the purpose of MinGW, whereas the purpose of Cygwin is to provide a POSIX layer on top of Windows. It is possible to build native Windows applications with Cygwin, but I have never tried this. (If you do manage to build a native Windows GLUT application using Cygwin, that will not impact performance compared to MinGW.)

If you google "glut mingw" you get a number of useful top results. I have never used GLUT myself so I can't offer any better advice. Logically, you want to install MinGW and the native Windows version of GLUT (you may be calling this the "Visual Studio version"), and use -I and -L so that MinGW compiles against that.
jareds
 
Posts: 317
Joined: Wed Jan 03, 2007 3:56 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby sourmìlk » Sat Nov 26, 2011 10:15 pm UTC

Okay, I'll try that. Anything to avoid using Visual C++

EDIT: I'm using mingw, and it mostly works but there are a few oddities. One is that the compiler can't find references to GLEW functions except in main.cpp, and the other is that, every time I run a program compiled with mingw, the thread is immediately suspended and I have to use the debugger to manually resume it. Any reason those things are happening?
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.
User avatar
sourmìlk
If I can't complain, can I at least express my fear?
 
Posts: 6405
Joined: Mon Dec 22, 2008 10:53 pm UTC
Location: permanently in the wrong

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Fri Dec 02, 2011 3:54 am UTC

Here's one I haven't yet figured out.

In Python, using 'time.sleep()' pauses the program for a certain period of time.

But when using GUI (created using Tkinter), it freezes the entire GUI until the code finishes running. Even if you put time.sleep() inside of the function called by a button press, it freezes the GUI. The real problem is that not even the x button in the upper left works (This is obviously Windows).

I google around and find about about the 'yield' function. It works in 2.x but apparently the function of yield has changed drastically in 3.x because I keep getting this error:
'return' with argument inside generator


Also, something else that happened to me.
When I was VERY new at programming, I created a program to calculate my final grade for a class. I wanted to get in as many different possibilities as I could. Including checking if the final exam was optional.
I didn't know about functions then, so I had nested if statements all over everywhere.
Funnily enough, it almost ran. Almost.

It would fail at the last nested if statement (about 5 layers down). But only if the if was true. The else that was paired with it ran fine. All other statements ran without a hitch and it even did the math correctly.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby phlip » Fri Dec 02, 2011 4:16 am UTC

The "yield" statement is completely unrelated with what you're trying to do... it's a way of progressively generating a list of numbers, returning them one at a time. Nothing to do with sleeping.

There is also a yielding option in most thread-based libraries - though IIRC it's not called "yield" specifically in Python. It won't do anything that sleep() doesn't already do, though.

The way GUIs work is very different to the way CLI programs work, at a basic level... somewhere in the Tk code there'll be an event loop, which will look something like (in pseudocode):
Code: Select all
while the program is running:
  if there are any events:
    e = get an event
    handle_event(e)
  else:
    sleep a little bit
So when you click a button, for instance, the "get an event" thing will return a button-click event, and then handle_event will call your button-click handler function. Then, when your handler function returns, it will loop and handle the next event.

What this means is that if your event handler function takes a long time to return, the program won't handle events during that time. What you need to do instead is have your button-click handler return, so it can keep processing events, and then arrange for some function to be called later to do the delayed stuff.

So, for instance, instead of:
Code: Select all
def buttonClick():
  do stuff
  time.sleep(2000)
  do more stuff
you want to instead have:
Code: Select all
def buttonClick():
  do stuff
  tk_root.after(2000, delayCallback)

def delayCallback():
  do more stuff
So that way your code will do the first stuff, and then return control to the event loop (so other events can be handled)... and then, 2 seconds later, it'll pick up the delay event and call the second callback function.

Coding in an event-based system takes a lot of getting used to, and can get quite complicated, but that's the basics.
While no one overhear you quickly tell me not cow cow.
but how about watch phone?
User avatar
phlip
Restorer of Worlds
 
Posts: 6739
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Fri Dec 02, 2011 1:11 pm UTC

Amusingly, yield can still be used here to make your coding easier.

But you have to work at it.

The "worker" procedure becomes a generator of sleep timeouts, which it returns on a yield. The "wrapper" function iterates over the worker generator, and sets a wakeup timer with the messaging infrastructure, then returns.

... While this leads to a relatively pretty "worker" code, I can't think of a way to make the "wrapper" code pretty. Hurm. And it isn't for novices either.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Fri Dec 02, 2011 1:40 pm UTC

phlip wrote:The "yield" statement is completely unrelated with what you're trying to do... it's a way of progressively generating a list of numbers, returning them one at a time. Nothing to do with sleeping.


I actually found the yield from an example code and it was used to pause the program for a short time.

Also, I need it to sleep inside a while loop inside the function.

Something like this:
Code: Select all
def function():
     while health > 0:
          time.sleep(4) #sleep time in seconds
          text.insert(END, "Health is " + str(health))
          do some stuff

The reason I need to do it this way is the function only outputs text to the textbox in the GUI. Unfortunately, it spits all of it out at once and I need to slow it down a bit.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby EvanED » Fri Dec 02, 2011 1:53 pm UTC

Jof16's wrote:The real problem is that not even the x button in the upper left works (This is obviously Windows).

No it's not. The close button belongs to your process, and your process gets to handle it being clicked. (This is how editors and such throw up "would you like to save?" dialogs.) Your process is sleeping instead of handling the button click message, so the program is never exiting.

(That being said, Windows should be throwing up a "this program isn't responding" dialog after a few seconds of waiting. If you understand the example event loop philip posted, you can understand the technical meaning of that dialog: the process hasn't picked up any messages for a while. (Essentially a "message" is the Windows name for an event.) What version of Windows are you on?)
EvanED
 
Posts: 3767
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI

Re: The "IT DOESN'T WORK!" thread

Postby Jplus » Fri Dec 02, 2011 1:56 pm UTC

Also, since when does Windows have an X button in the upper left? :P
Hey, like coding? Perhaps you should check out the red spider project.
Feel free to call me Julian. J+ is just an abbreviation.
User avatar
Jplus
 
Posts: 1091
Joined: Wed Apr 21, 2010 12:29 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Fri Dec 02, 2011 2:01 pm UTC

Jplus wrote:Also, since when does Windows have an X button in the upper left? :P


*Slaps himself*

Upper right. I'm stupid.


Also, Evan, I kinda understand what you're saying.
It only throws up a "this program isn't responding" when I try to click on the close button.
The first few times I tried the time.sleep, I didn't know the GUI was just sleeping, so I tried to close it after a few seconds.

I'm using Windows 7.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Xanthir » Fri Dec 02, 2011 6:30 pm UTC

Jof16's wrote:Also, I need it to sleep inside a while loop inside the function.

Something like this:
Code: Select all
def function():
     while health > 0:
          time.sleep(4) #sleep time in seconds
          text.insert(END, "Health is " + str(health))
          do some stuff

The reason I need to do it this way is the function only outputs text to the textbox in the GUI. Unfortunately, it spits all of it out at once and I need to slow it down a bit.

You need to do what was described before - refactor your code to use callbacks with delays.

Evented programming ends up looking kinda like recursive programming in these circumstances. Instead of a loop, you just check the condition with an if(), do your work, and set yourself as a callback again after a certain amount of time. The event loop will call you when your timeout is up.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))
User avatar
Xanthir
My HERO!!!
 
Posts: 3999
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Fri Dec 02, 2011 7:17 pm UTC

Jof16's wrote:
phlip wrote:The "yield" statement is completely unrelated with what you're trying to do... it's a way of progressively generating a list of numbers, returning them one at a time. Nothing to do with sleeping.
I actually found the yield from an example code and it was used to pause the program for a short time.
The yield statement in python doesn't mean the same thing as the yield function in other languages.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sat Dec 03, 2011 12:22 am UTC

Xanthir wrote:You need to do what was described before - refactor your code to use callbacks with delays.

Evented programming ends up looking kinda like recursive programming in these circumstances. Instead of a loop, you just check the condition with an if(), do your work, and set yourself as a callback again after a certain amount of time. The event loop will call you when your timeout is up.


Okay, I get it now.
One question though. does the rest of the code continue running while waiting for the delay time to finish?
Because I keep getting an error for a line that shouldn't run until the tk_root.after() has.

If so, how would I stop this?
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby RoadieRich » Sat Dec 03, 2011 2:12 am UTC

Fixing the error is the usual solution.
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: The "IT DOESN'T WORK!" thread

Postby Xanthir » Sat Dec 03, 2011 7:53 am UTC

Jof16's wrote:
Xanthir wrote:You need to do what was described before - refactor your code to use callbacks with delays.

Evented programming ends up looking kinda like recursive programming in these circumstances. Instead of a loop, you just check the condition with an if(), do your work, and set yourself as a callback again after a certain amount of time. The event loop will call you when your timeout is up.


Okay, I get it now.
One question though. does the rest of the code continue running while waiting for the delay time to finish?
Because I keep getting an error for a line that shouldn't run until the tk_root.after() has.

If so, how would I stop this?


Do you mean something like (JS here, because I dunno how tkinter works):
Code: Select all
function foo() {
  bar();
  settimeout(foo, 5000);
  baz();  // error from this line
}

If so, then yes, the rest of the code keeps running after your timeout. Setting a timeout just schedules some later code; it doesn't interfere with the current code that's running. This is *unlike* recursive code, where you can count on the recursive call to run before the code continues on.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))
User avatar
Xanthir
My HERO!!!
 
Posts: 3999
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Sat Dec 03, 2011 3:25 pm UTC

Do bar(), then wait 5000 units, then do baz(), then wait 5000 units, then do bez():
Code: Select all
function foo() {
  bar();
  settimeout(foo2, 5000);
}
function foo2() {
  baz();  // error from this line
  settimeout(foo3, 5000);
}
function foo3() {
  bez();  // error from this line
}

If you want to, say, process a list of contents at a rate of 1 every second, you need to "hide" the list somewhere that the timeout call can get at it.

This can be done through a lot of methods. One of the simplest (if probably less advisable ones) is to stuff it into a global list. Then repeatedly call a function that grabs the front element from the global list (removing it from the list), processes it (displays it), then if the list is non-empty induces a callback on itself that will execute in 1 second.

There are other ways to do this, often involving handing a callback that "carries" the list information. You can do this via a lambda type closure, a class method, or a myriad of other ways. These techniques are trickier, but better in the long run.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sat Dec 03, 2011 5:40 pm UTC

Xanthir wrote:...

Do you mean something like (JS here, because I dunno how tkinter works):
Code: Select all
function foo() {
  bar();
  settimeout(foo, 5000);
  baz();  // error from this line
}

If so, then yes, the rest of the code keeps running after your timeout. Setting a timeout just schedules some later code; it doesn't interfere with the current code that's running. This is *unlike* recursive code, where you can count on the recursive call to run before the code continues on.


I need to pause the code for a pre-determined amount of time rather than schedule some code to run later.

Basically, I'm writing a simple game. It's a zombie apocalypse survival game. It's text based but uses a GUI.
One of the buttons ( 'fight' in this case ) calls on a function. This function checks the values of certain global variables and, if all the conditions are met, calls on the actual fight function (makes the code more readable and gives me the ability to add more code to create levels for the game later on). The fight function does all the necessary calculations and determines the winner using a while loop. The problem is, it does this all at once. I want to slow down the function so it doesn't do that. That way, I can give the user a chance to break the fight and retreat if it isn't going well for them.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Dason » Sat Dec 03, 2011 5:43 pm UTC

It still sounds like you need to reschedule because if you just pause the game then how is the user going to enter anything to escape from the fight?
double epsilon = -.0000001;
User avatar
Dason
 
Posts: 1264
Joined: Wed Dec 02, 2009 7:06 am UTC
Location: ~/

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sat Dec 03, 2011 7:29 pm UTC

You're right. I need to find a way to slow down the code instead of pausing it.

root.after() is close to what I need, but I don't need the rest of the code to run until after the delayed code has run.

I was thinking of defining an event (either a keyboard event or a button click) that will change a global variable and either use an if statement to check the variable or put everything inside a while loop. I know that's not the best way, but I know it would work.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Yakk » Sat Dec 03, 2011 7:46 pm UTC

I need to pause the code for a pre-determined amount of time rather than schedule some code to run later.

What is the difference between this:
Code: Select all
function foo() {
  bar()
  pause_for_5_seconds()
  baz()
}

And this:
Code: Select all
function foo() {
  bar()
  call_in_5_seconds(foo2);
}
function foo2() {
  baz()
}

?

Answer carefully.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sat Dec 03, 2011 9:33 pm UTC

Either I'm not understanding or I'm not making myself clear (probably the former).

Here's the code for the fight function:

Spoiler:
Code: Select all
    while playerHealth and zombieHealth > 0:
        playerRoll = randint(1,6)
        zombieRoll = randint(1,6)
       
        if weaponAmmo > 0:
            if playerRoll >= 4:
                if weaponDamage >= 6:
                    zDamage = randint(5, weaponDamage)
                else:
                    zDamage = randint(1, weaponDamage)
                text.insert(END, "You hit the zombie for " + str(zDamage) + " points of damage.\n")

                zombieHealth = zombieHealth - zDamage
                if zombieHealth <= 0:
                    text.insert(END, "The zombie has died! \n\n")
                    totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                    return totalHealth
                    break
               
            else:
                text.insert(END, "You missed the zombie. \n")
        else:
            text.insert(END, "\nYou are out of ammo.\n")
            text.insert(END, "It will take a moment to reload.\n\n")
            weaponAmmo = clips(weaponDamage, text)
            if weaponAmmo == 0:
                text.insert(END, "It would be wise to retreat.\n\n")
                if count == 1:
                    zombie1health = zombieHealth
                elif count == 2:
                    zombie2health = zombieHealth
                elif count == 3:
                    zombie3health = zombieHealth
                elif count == 4:
                    zombie4health = zombieHealth
                elif count == 5:
                    zombie5health = zombieHealth
                count -= 1
                totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                return totalHealth
                break

        if zombieRoll >= 4:
            zWdamage = zWeapon(text)
            if zWdamage >= 6:
                pDamage = randint(4,zWdamage)
            else:
                pDamage = randint(1, zWdamage)
            text.insert(END, "The zombie hits you for " + str(pDamage) + " points of damage.\n")

            playerHealth = playerHealth - pDamage
            if playerHealth <= 0:
                text.insert(END, "The zombie has killed you!  You Lose!\n\n")
                totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                return totalHealth
                break

        else:
            text.insert(END, "The zombie missed you. \n")

        #I want to pause for a certain amount of time at this point before going back to the beginning of the loop.


Using root.after() would require me to write multiple functions.

Unless I called the first function after the second one is finished. But, how would I return totalHealth to the fightButton function.. Plus, I'd be writing the same function twice, just with a very slightly different name.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Xanthir » Sun Dec 04, 2011 1:28 am UTC

Jof16's wrote:Either I'm not understanding or I'm not making myself clear (probably the former).

Here's the code for the fight function:

Spoiler:
Code: Select all
    while playerHealth and zombieHealth > 0:
        playerRoll = randint(1,6)
        zombieRoll = randint(1,6)
       
        if weaponAmmo > 0:
            if playerRoll >= 4:
                if weaponDamage >= 6:
                    zDamage = randint(5, weaponDamage)
                else:
                    zDamage = randint(1, weaponDamage)
                text.insert(END, "You hit the zombie for " + str(zDamage) + " points of damage.\n")

                zombieHealth = zombieHealth - zDamage
                if zombieHealth <= 0:
                    text.insert(END, "The zombie has died! \n\n")
                    totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                    return totalHealth
                    break
               
            else:
                text.insert(END, "You missed the zombie. \n")
        else:
            text.insert(END, "\nYou are out of ammo.\n")
            text.insert(END, "It will take a moment to reload.\n\n")
            weaponAmmo = clips(weaponDamage, text)
            if weaponAmmo == 0:
                text.insert(END, "It would be wise to retreat.\n\n")
                if count == 1:
                    zombie1health = zombieHealth
                elif count == 2:
                    zombie2health = zombieHealth
                elif count == 3:
                    zombie3health = zombieHealth
                elif count == 4:
                    zombie4health = zombieHealth
                elif count == 5:
                    zombie5health = zombieHealth
                count -= 1
                totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                return totalHealth
                break

        if zombieRoll >= 4:
            zWdamage = zWeapon(text)
            if zWdamage >= 6:
                pDamage = randint(4,zWdamage)
            else:
                pDamage = randint(1, zWdamage)
            text.insert(END, "The zombie hits you for " + str(pDamage) + " points of damage.\n")

            playerHealth = playerHealth - pDamage
            if playerHealth <= 0:
                text.insert(END, "The zombie has killed you!  You Lose!\n\n")
                totalHealth = str(str(playerHealth)+','+str(zombieHealth))
                return totalHealth
                break

        else:
            text.insert(END, "The zombie missed you. \n")

        #I want to pause for a certain amount of time at this point before going back to the beginning of the loop.


Using root.after() would require me to write multiple functions.

Unless I called the first function after the second one is finished. But, how would I return totalHealth to the fightButton function.. Plus, I'd be writing the same function twice, just with a very slightly different name.

Yes, you need multiple functions. No, they won't be identical. It's like I said above - transforming a while loop into an event loop is almost the same as transforming it into a recursive function. Changing your function would be trivial. Just make the function take the playerHealth and zombieHealth as arguments, remove the while loop, then put the while loop's check at the end, using it in an if() to see if you need to schedule a callback, like this:

(again, I don't know tkinter, so I'm using javascript syntax)
Code: Select all
if( playerHealth > 0 && zombieHealth > 0 ) {
  settimeout(function(){fight(playerHealth, zombieHealth);}, 1000);
} else {
  doSomethingWhenTheFightIsOver(totalHealth);
}


Now, if you call the fight() function, it will run through its body once, and if both people are still alive, schedule itself to be run again in one second. When everything's finished it will instead call the other function and *not* schedule another run.

Note that this does *not* return anything to the function that gets called when they click the fight button. That function ended a long time ago; it's dead and gone. You have to pass the values you want into a *new* function. In general, whenever you have this structure in imperative code:
Code: Select all
function foo() {
  A();
  while(needToRun) {
    B();
    sleep(delay);
  }
  C();
}


You need to transform it into this structure in event-driven code:
Code: Select all
function foo() {
  A();
  B();
}

function B() {
  stuff();
  if( needToRunAgain )
    setTimeout(B,delay);
  else
    C();
}


The model to wrap your head around is that it's the *language* that calls your functions for you; you don't do it yourself. You just tell the language to call another function with some delay before your current function ends.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))
User avatar
Xanthir
My HERO!!!
 
Posts: 3999
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sun Dec 04, 2011 2:32 am UTC

I think I understand what you're saying now. I'm rewriting the code now.
One more problem. If I define the global variables in the main file before I import the module, shouldn't those global variables be available to use in the module? Because I keep getting a 'global name 'playerHealth' is not defined' error when using the fight function.
Or am I barking up the wrong branch of code?
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby Xanthir » Sun Dec 04, 2011 5:36 am UTC

I forget Python's exact details, but I believe it uses lexical scope, which means that functions capture the variables in scope *when they are defined*. That means that global variables in main won't be around when the module is loaded, so they won't see them.

A quick google search about sharing globals across modules brought up this, which seems reasonable.
(defun fibs (n &optional (a 1) (b 1)) (take n (unfold '+ a b)))
User avatar
Xanthir
My HERO!!!
 
Posts: 3999
Joined: Tue Feb 20, 2007 12:49 am UTC
Location: The Googleplex

Re: The "IT DOESN'T WORK!" thread

Postby Jof16's » Sun Dec 04, 2011 5:58 am UTC

But the GUI doesn't start until the mainloop() is run. So shouldn't all the global variables already be defined?
I only get the error when I press the fight button.

Still, that link looks like the better idea. It would make it easier to keep up with the global variables that way.
Thanks for the link and the help.
Jof16's
 
Posts: 84
Joined: Mon Jun 14, 2010 4:08 pm UTC

Re: The "IT DOESN'T WORK!" thread

Postby EvanED » Sun Dec 04, 2011 6:53 am UTC

Python doesn't have true global variables, only module-level variables. "Globals" defined in one module aren't visible in another.

If module m defines a "global" v (just with something like v=10 outside of a function), then other modules that import m can refer to it via m.v, but you need the module qualification just like anything else. (I think that the generally-ill-advised import * from m would make v available unqualified.)

I'm not sure the best way to allow the module you're importing to access variables of the importing module though. Possibly the best way is to not, and just pass any information you need to functions from that module. Global variables are a design smell anyway.
EvanED
 
Posts: 3767
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI

PreviousNext

Return to Coding

Who is online

Users browsing this forum: No registered users and 11 guests