I Need A High Resolution Sleep Function.

I am using Sleep() from windows.h and I am calling timeBeginPeriod(1) before I use Sleep() to make it more accurate.

However, using timeBeginPeriod(1) just seems to make Sleep() more sporadic.

I'm on a laptop running vista and am using visual studio 2008 (if that helps).

Quick summary of what I am actually trying to do, then my code:

I have been playing this stupid snake game on Facebook. It has turned into a competition between some of my non-CompSci buddies and me. So I am trying to write a program that will play the snake game for me and get a really high score. Cool, right? Heres the game: http://apps.facebook.com/mindjolt/games/snake

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Winmm.lib")


// Adjust for timing:
const static DWORD TO_TOP_FROM_START = 1775; //time needed to go from starting position to the top of the grid.
const static DWORD TO_EDGE_FROM_MIDDLE = 1895; // time needed to go from start position to left side of grid.
const static DWORD LONG_WAYS = 3937;  // time needed to go horizontally accros the entire grid but one block.
const static DWORD SHORT_WAYS = 1950; // time needed to go vertically accros the entire grid.

using namespace std;

bool isEnterPressed()
// Returns: True if ENTER has been pressed since
//		last call. Otherwise, False.
// Note: You do not need to be focused on the output
//		terminal for the key to be recongnized.
{
	int res = ::GetAsyncKeyState(VK_RETURN);
 	return res < 0;
}

void waitTillSpaceIsPressed()
// Description: Waits until SPACE is pressed.
// Note: You do not need to be focused on the output
//		terminal for the key to be recongnized.
{
	cout << "Press Space To Continue (you do not need to be focused on this window)...";
	int res;

	do{
		res = ::GetAsyncKeyState(VK_SPACE);
	}while (!( res < 0 ));
}

int snake()
// How to use: Call timeBeginPeriod(1) before snake(),
//		and call timeEndPeriod(1) after snake().
// Description: Moves the snake around the entire grid.
//		Starts at top left and weaves its way down,
//		then loops back to the top on the right side.
// Returns: Nothing.
// Warning: Press ENTER to exit this function! 
//		Otherwise, timeBeginPeriod will be called 
//		without timeEndPeriod, thus ending civilization
//		as we know it...
{
	cout << "+-----------------------------------+" << endl;
	cout << "|  Press ENTER At Anytime To Exit.  |" << endl;
	cout << "|                                   |" << endl;
	cout << "|  Note: Don't Exit Any Other Way!  |" << endl;
	cout << "+-----------------------------------+" << endl << endl;

	Sleep(TO_TOP_FROM_START);
	keybd_event(VK_LEFT,0x25,0 , 0); // Left Arrow Press
	keybd_event(VK_LEFT,0x25,KEYEVENTF_KEYUP,0); // Left Arrow Release
	Sleep(TO_EDGE_FROM_MIDDLE);


	for(int j = 0; j < 410; j++)
	{
		for(int i = 0; i < 15; i++)
		{
			cout << j << " X " << i << endl;

			keybd_event(VK_DOWN,0x28,0 , 0); // Down Arrow Press
			keybd_event(VK_DOWN,0x28,KEYEVENTF_KEYUP,0); // Down Arrow Release
			keybd_event(VK_RIGHT,0x27,0 , 0); // Right Arrow Press
			keybd_event(VK_RIGHT,0x27,KEYEVENTF_KEYUP,0); // Right Arrow Release

			if(isEnterPressed())
				return 0;

			Sleep(LONG_WAYS);

			keybd_event(VK_DOWN,0x28,0 , 0); // Down Arrow Press
			keybd_event(VK_DOWN,0x28,KEYEVENTF_KEYUP,0); // Down Arrow Release
			keybd_event(VK_LEFT,0x25,0 , 0); // Left Arrow Press
			keybd_event(VK_LEFT,0x25,KEYEVENTF_KEYUP,0); // Left Arrow Release

			if(isEnterPressed())
				return 0;

			Sleep(LONG_WAYS);
		}

		keybd_event(VK_DOWN,0x28,0 , 0); // Down Arrow Press
		keybd_event(VK_DOWN,0x28,KEYEVENTF_KEYUP,0); // Down Arrow Release
		keybd_event(VK_RIGHT,0x27,0 , 0); // Right Arrow Press
		keybd_event(VK_RIGHT,0x27,KEYEVENTF_KEYUP,0); // Right Arrow Release

		if(isEnterPressed())
			return 0;

		Sleep(LONG_WAYS+62);

		keybd_event(VK_UP,0x26,0 , 0); // Up Arrow Press
		keybd_event(VK_UP,0x26,KEYEVENTF_KEYUP,0); // Up Arrow Release

		if(isEnterPressed())
			return 0;

		Sleep(SHORT_WAYS);

		keybd_event(VK_LEFT,0x25,0 , 0); // Left Arrow Press
		keybd_event(VK_LEFT,0x25,KEYEVENTF_KEYUP,0); // Left Arrow Release

		if(isEnterPressed())
			return 0;

		Sleep(LONG_WAYS+62);
	}
	return 0;
}
int main()
{
	cout << "First, go to the snake game at http://apps.facebook.com/mindjolt/games/snake." << endl << endl;
	cout << "Then, focus on the game and press SPACE to start the game and this program." << endl <<endl;
	waitTillSpaceIsPressed();
	cout << endl << endl;
	timeBeginPeriod(1);
	snake();
	timeEndPeriod(1);

	return 0;
}


Thanks for any help!
I wouldn't try to sleep for less than a few milliseconds. And you can pretty much expect that Sleep is going to take longer than the time you specify.

What I would do is write your own 'Delay' function that sleeps for X-Y milliseconds (where X is the number of ms you want to delay, and where Y is a "safe" padding amount, like 4 or 5 ms).

Then after you sleep, spin on a high resolution timer to wait out the remaining time.
I am confused what you mean. Would 'Delay' have 2 parameters, X and Y?

The 'sleep' or 'delay' doesn't actually need to be accurate, just very consistent. Then I can adjust the delay times until they work.

Would just spinning on a high resolution timers the whole time work?
Last edited on
I am confused what you mean. Would 'Delay' have 2 parameters, X and Y?


You could do that, although I wouldn't. I would just pass it the time you want to delay, just like you do with sleep. Just pick another constant to use as Y.

The 'sleep' or 'delay' doesn't actually need to be accurate, just very consistent.


Sleep won't be consistent. Even if it happens to be consistent on your machine, if you try to run it on a different machine you'll get different results. You might even get different results on your machine when you try to run it on another day.

You should never, ever rely on Sleep alone for timing stuff.

Would just spinning on a high resolution timers the whole time work?


Yes, but you'll suck up 100% CPU time if you do that. Sleeping when you can is recommended. Just know that oversleeping is always a possibilty.
Just pick another constant to use as Y.

I'm still unsure what Y is for. Ideally I want Y to be zero, right?

Sleep won't be consistent.

Sleep has not been consistent at all.

So would something like this work?

1
2
3
4
5
6
7
8
9
void Delay(int millisecond)
{
	Timer t;
	t.startTimer();

	Sleep(.9*millisecond); // sleep most of the time

	while(t.getElapsedTime() < milliseconds){} //spin the rest of the time away
}


Will the spinning at the end correct the inconsistency of 'Sleep'?
I'm still unsure what Y is for. Ideally I want Y to be zero, right?


Yes. Ideally Y would be zero. The lower you can get it, the less CPU time is burned, but the greater the risk of oversleeping.

Will the spinning at the end correct the inconsistency of 'Sleep'?


Yes, that is basically the same thing I was thinking. As long as your Timer is consistent, that should work exactly as you expect.

Although one problem you might have is that Y gets very low if X is low (basically you have Y=0.1*X)... so if you do Delay(1), you'll be calling Sleep(0), which might sleep longer than 1, in which case you're hosed.

Instead of doing Y=0.1*X, I would have a fixed constant for Y. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
void Delay(int millisecond)
{
    Timer t;
    t.startTimer();

    int sleep = millisecond - 5;  // Y=5

    if(sleep > 0)  // only sleep if millisecond>Y
        Sleep(sleep);

    // then spin normally
    while(t.getElapsedTime() < milliseconds){}
}
Wow... I just realised about 5 mins into reading your code that of all of the stuff I've written, read, analysed and trouble shot since I started really working in C++; this is the first real bot I've ever seen. I'm mostly tagging this to my profile (which yes I could have done with the Tracking Options below) but I wanted to say thanks and cool work.

I just wanted to ask, why are we using sleep? I know that the program doesn't return anything to you nativley but is there another way to go about this?

EDIT: Sorry another question, So the keybd_event(...) function here: http://msdn.microsoft.com/en-us/library/ms646304(VS.85).aspx generates a keystroke but you don't need to supply your program with a handle to the target application? So then it just screams at everything all at once then? How does this not mess with the rest of the computer? Sorry it's not often that I get to be the asker on the Windows Forms, and yes I will be messing with these functions right after I hit "Submit". Thank you again!
Last edited on
Thanks!



So then it just screams at everything all at once then?


It probably sends it to whatever window has focus.
yeah, you just have to stay focused on the snake game the whole time. Is there a better way to do it?

The 'Delay' still seems inconsistent.
1
2
3
4
5
6
7
8
9
10
11
12
13
void Delay(int ms)
{
    Timer t;
    t.StartTimer();

    int sleep = ms - 10;  // Y=10

    if(sleep > 0)  // only sleep if millisecond>Y
        Sleep(sleep);

    // then spin normally
    while(t.GetElapsedTime() < ms){}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include "Timer.h"

using namespace std;


Timer::Timer()
{
	PCFreq = 0.0;
	CounterStart = 0;
}

void Timer::StartTimer()
{
    LARGE_INTEGER li;
    if(!QueryPerformanceFrequency(&li))
        cout << "QueryPerformanceFrequency failed!\n";

    PCFreq = double(li.QuadPart)/1000.0;

    QueryPerformanceCounter(&li);
    CounterStart = li.QuadPart;
}
double Timer::GetElapsedTime()
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return double(li.QuadPart-CounterStart)/PCFreq;
}


Could the inconsistency be in the game itself? Would a game like that run at different speeds depending upon the things that are run on my computer?
I changed some times and it is running right now (on a different computer). This is by far the best run!

Score is 250 and counting...

If all goes well it should be finished in about 6 hours.

Edit: crashed at 530 points.
Last edited on
Hi OP, I did some thinking about the issue and I had some questions; when you say it "crashed" what do you mean? Did your process freeze up? Did the game freeze? Was it your entire desktop? BSOD? These details do sort of matter as far as diagnosing a "crash" which now a days could mean almost anything, and it may help us determine if your program is fine and the hardware is acting funny or if it's the otherway around.
Sorry, by "crash", I mean the snake ran into the wall and lost. The program itself kept on running and was just screaming at the "game over" screen. The program worked well for about 30 minutes before it lost.

I am guessing that some error in the timing built up and eventually the snake ran into the wall.
Or it could have been that the flash game froze for a second and threw the timing off. Or maybe the problem is something I haven't even thought of.

I have tried it a few more times and the programs high score is 590 points (For reference, my own high score is 800 points).
Topic archived. No new replies allowed.