Sprague–Grundy theorem / Game of nim / Bowlingpins hackerrank - mex

I've been trying to solve this problem in HackerRank called Bowling Pins. Basically you have to code a program that predicts who will win and who will lose. The rules of the game are:
Bowling pins are position horizontally and they are represented as a string of capital i's --> "IIII" (4 pins)
You can either knock down one single pin "I" or two pins that are next to each other "II"
"X" will represent the pins that are knocked down
Last player to knock down wins
Given the string "XIIX", this is a win, since I can knock down the two middle pins.
Given the string "IXIX", is a lose, since the next player will make the last move
Now, I tried to get a basic understanding of combination game theorem. I know that I have to calculate the mex and grundy in order to know who wins and who loses without actually playing the game.
Now my question is if I have three pins "III"
mex {1,2} = 0 , this means a lose. But what happens in the case that I knock down the single middle pin?
The next player turns will look like this "IXI", he/she can either knock down the left or the right pin, regardless I get the last pin and win. right?
I'm very new to these concepts and I'm not sure if I'm implementing the Sprague–Grundy theorem correctly for this game. Can someone explain this to me?
Here's a link to the problem I'm trying to solve --> https://www.hackerrank.com/challenges/bowling-pins/problem

I think this document has a really good explanation of the relevant theory: https://web.mit.edu/sp.268/www/nim.pdf. Personally, it took me a bit to get through the document; at first I just tried reading, but I got much more out of it by writing some of the lemmas/theorems down and practicing a bit myself. I think the graphs under "From Games to Graphs" and "The Sprague-Grundy Function" sections were particularly helpful for this problem.
Here's how I started thinking about it: a set of pins is a game position. Each game position is either terminal or has followers, which are other game positions that can be moved to. Each game position can be assigned a Sprague-Grundy (SG) based on the SG values of its followers. Now try creating a graph of game positions:
The only terminal position is no pins left, which we can write as X or XX or however many X based on the number of pins you start with. This position has SG(X) = 0 because it's terminal.
The game position I can have one pin knocked down, which would make it X. So X is a follower of I, and SG(I) = mex{SG(X)} = mex{0} = 1
The game position II can have either one pin knocked down, making it XI == IX == I, or two, making it XX == X. So it has these two followers, and SG(II) = mex{SG(I), SG(X)} = mex{0, 1} = 2.
Now let's look at III, since this is where we get to start using some of the other information from the document. The followers are XII (SG of 2), XXI (SG of 1), and IXI. For this last one, we could see that it only has one follower and get the SG like that, OR we could use the Sprague-Grundy theorem. This says "the SG function for a sum of games on a graph is just the Nim sum of the SG functions of its components". For IXI, it has two games of I, each with SG of 1. When we take the nim sum (xor the binary representations), we get 0. So in conclusion, SG(III) = mex{2, 1, 0} = 3.
And you can keep going bottom to top to get other SGs by taking 1 or 2 II away at a time and calculating nim sums.

Related

Problem with programming a basic hardware

I have an animation shown on LEDs. When the button is pressed, the animation has to stop and then continue after the button is pressed again.
There is a method that processes working with the button:
void checkButton(){
GPIO_PinState state;
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_RESET) {
while(1){
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_SET){
break;
}
}
//while (state == GPIO_PIN_RESET) {
//state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
//}
}
}
GPIO_PIN_SET is the default button position. GPIO_PIN_RESET is the condition when the button is pressed. The commented section is what I tried instead of the while(1){...} loop. The checkButton() method is called in the main loop from time to time to be run. The program runs on STM32 with an extension module (here the type of an extension module does not matter).
The fact is that this method stops animation just for a moment and does not work as I would like it to. Could you correct anything about this program to make it work properly?
Could you correct anything about this program to make it work
properly?
My guess is that you are trying to add a 'human interaction' aspect to your design. Your current approach relies on a single (button position) sample randomly timed by a) your application and b) a human finger. This timing is simply not reliable, but the correction is possibly not too difficult.
Note 1: A 'simple' mechanical button will 'bounce' during it's activation or release (yes, either way). This means that the value which the software 'sees' (in a few microseconds) is unpredictable for several (tbd) milliseconds(?) near the button push or release.
Note 2: Another way to look at this issue, is that your state value exists two places: in the physical button AND in the variable "GPIO_PinState state;". IMHO, a state value can only reside in one location. Two locations is always a mistake.
The solution, then (if you believe) is to decide to keep one state 'record', and eliminate the other. IMHO, I think you want to keep the button, which seems to be your human input. To be clear, you want to eliminate the variable "GPIO_PinState state;"
This line:
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
samples the switch state one time.
HOWEVER, you already know that this design can not rely on the one read being correct. After all, your user might have just pressed or released the button, and it is simply bouncing at the time of the sample.
Before we get to accumulating samples, you should be aware that the bouncing can last much more than a few microseconds. I've seen some switches bounce up to 10 milliseconds or more. If test equipment is available, I would hook it up and take a look at the characteristics of your button. If not, well, you can try the adjusting the controls of the following sample accumulator.
So, how do we 'accumulate' enough samples to feel confident we can know the state of the switch?
Consider multiple samples, spaced-in-time by short delays (2 controls?). I think you can simply accumulate them. The first count to reach tbr - 5 (or 10 or 100?) samples wins. So spin sample, delay, and increment one of two counters:
stateCount [2] = {0,0}; // state is either set or reset, init both to 0
// vvv-------max samples
for (int i=0; i<100; ++i) // worst case how long does your switch bounce
{
int sample = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15); // capture 1 sample
stateCount[sample] += 1; // increment based on sample
// if 'enough' samples are the same, kick out early
// v ---- how long does your switch bounce
if (stateCount[sample] > 5) break; // 5 or 10 or 100 ms
// to-be-determined --------vvv --- how long does switch bounce
std::this_thread::sleep_for(1ms); // 1, 3, 5 or 11 ms between samples
// C++ provides, but use what is available for your system
// and balanced with the needs of your app
}
FYI - The above scheme has 3 adjustments to handle different switch-bounce durations ... You have some experimenting to do. I would start with max samples at 20. I have no recommendation for sleep_for ... you provided no other info about your system.
Good luck.
It has been a long time, but I think I remember the push-buttons on a telecom infrastructure equipment bounced 5 to 15 ms.

Running three functions at once?

C++
Working on a problem for school, running 3 horses in a race and whoever finishes first is the winner. the 3 horses are supposed to run in sync like this
1|--------H------|
2|-------H-------|
3|---------H-----|
However my code runs the program correctly (generate a random number between 1 and 100 and if that number is less than 50 move the horse 1 space up). but it runs the first horse, then the 2nd and the 3rd last.
tried to look this up but none of the methods seem to work (using codeblocks (latest version Windows 10) for C++).
srand(time(NULL));
Horse1();
Horse2();
Horse3();
Github file: https://gist.github.com/EthanA2020/f16a699f1b8136a1da0350ab48acdda0
I don't think your issue is with the type of function but instead the structure of your program. No matter how you program, one operation must come before the next. Developers work with this by running each operation of the object (in your case the horse movement) side by side and checking later to see the outcome.
For example, lets use your horse scenario:
while "all horses" are less than "finish"
horse 1 moves
horse 2 moves
horse 3 moves
I am sure that you are familiar with loops so we'll use that here. Some set distance must exist to determine when a horse has finished. So you'll want to continue that loop while all horses have a distance less than that value. During each loop, each horse's movement value must either change or not (determined by your random movement function).
Now once this while loop has ended, you can be sure that at least one horse has crossed the finish line. All operations have been completed and you have a data set of the horses positions. This is the point where you check to see which horses have finished (I say horses plural because there is a chance that more than one horse or even all 3 finish at the same time, so be sure to factor that in at the end).
With that, your program structure should be something like:
while "all horses" are less than "finish"
horse 1 moves
horse 2 moves
horse 3 moves
//movement of horses complete
check and print the horses with a movement value of "finish"
I think you should do:
while (!horse(rand() % 100)) {
usleep(100);
}
Where horse(int n) moves horse n 1 position and if it reached the end, it returns true (to end the race). It does nothing if an invalid n (only 1 to 3 is valid) is passed to it.

Trying to decode a FM like signal encoded on audio

I have an audio signal that has a kind of FM encoded signal on it. The encoded signal is using this Biphase mark coding technique <-- see at the end of this page.
This signal is a digital representation of a timecode, in hours, minutes, seconds and frames. It basically works like this:
lets consider that we are working in 25 frames per second;
we know that the code is transmitting 80 bits of information every frame (that is 80 bits per frame x 25 frames per second = 2000 bits per second);
The wave is being sampled at 44100 samples per second. So, if we divide 44100/2000 we see that every bit uses 22,05 samples;
A bit happens when the signal changes sign.
If the wave changes sign and keeps its sign during the whole bit period it is a ZERO. If the wave changes sign two times over one bit period it is a ONE;
What my code does is this:
detects the first zero crossing, that is the clock start (to)
measures the level for to = to + 0.75*bitPeriod... 0.75 to give a tolerance.
if that second level is different, we have a 1, if not we have a 0;
This is the code:
// data is a C array of floats representing the audio levels
float bitPeriod = ceil(44100 / 2000);
int firstZeroCrossIndex = findNextZeroCross(data);
// firstZeroCrossIndex is the value where the signal changed
// for example: data[0] = -0.23 and data[1] = 0.5
// firstZeroCrossIndex will be equal to 1
// if firstZeroCrossIndex is invalid, go away
if (firstZeroCrossIndex < 0) return
float firstValue = data[firstZeroCrossIndex];
int lastSignal = sign(firstValue);
if (lastSignal == 0) return; // invalid, go away
while (YES) {
float newValue = data[firstZeroCrossIndex + 0.75* bitPeriod];
int newSignal = sign(newValue);
if (lastSignal == newSignal)
printf("0");
else
printf("1");
firstZeroCrossIndex += bitPeriod;
// I think I must invert the signal here for the next loop interaction
lastSignal = -newSignal;
if (firstZeroCrossIndex > maximuPossibleIndex)
break;
}
This code appears logical to me but the result coming from it is a total nonsense. What am I missing?
NOTE: this code is executing over a live signal and reads values from a circular ring buffer. sign returns -1 if the value is negative, 1 if the value is positive or 0 if the value is zero.
Cool problem! :-)
The code fails in two independent ways:
You are searching for the first (any) zero crossing. This is good. But then there is a 50% chance, that this transition is the one which occurs before every bit (0 or 1) or whether this transition is one which marks a 1 bit. If you get it wrong in the beginning you end up with nonsense.
You keep on adding bitPeriod (float, 22.05) to firstZeroCrossIndex (int). This means that your sampling points will slowly run out of phase with your analog signal and you will see strange effects when you sample point gets near the signal transitions. You will get nonsense, periodically at least.
Solution to 1: You must search for at least one 0 first, so you know which transition indicates just the next bit and which indicates a 1 bit. In practice you will want to re-synchronize your sampler at every '0' bit.
Solution to 2: Do not add bitPeriod to your sampling point. Instead search for the next transition, like you did in the beginning. The next transition is either 'half a bit' away, or a 'complete bit' away, which gives you the information you want. After a 'half a bit' period you must see another 'half a bit' period. If not, you must re-synchronize since you took a middle transition for a start transition by accident. This is exactly the re-sync I was talking about in 1.

How can I use multiple timers to plan events in my program?

I am building a rockband-like program using C++ and SDL, and want to be able to time events so I can orchestrate a song in the program. Here is what I have accomplished so far:
4 circles which fall from the top of the window to the middle into 4 designated hitting spots.
The circles drop at random intervals (not using time, a random number generator determines how far from the top of the window they begin to fall)
I am able to determine when a note is hit, and a score is displayed in the top right hand corner
Simple sparks are applied around a marker to let you know a note was hit
I can open a file and read text from it
Now I want to be able to use that file to write songs for the program to read and execute. I was thinking something along the lines of "1g,2g,4y,3r etc. etc. etc." the numbers being milliseconds to wait until the next note and the letters designating which color should fall.
You don't really need (or want) multiple timers; just the single timer that drives your window refresh (at 30fps or whatever) is sufficient.
When you load in your song file, for each note in the song you should store the number of milliseconds that should elapse between the moment the song starts playing and the moment that particular note is played, e.g (pseudocode):
int millisCounter = 0;
int note, noteLengthMillis;
while(ReadNextNoteFromSongFile(note, noteLengthMillis))
{
songNoteRecordsVector.push_back(NoteRecord(millisCounter, note));
millisCounter += noteLengthMillis;
}
Then, when you start the game level going, at the instant the song starts playing, record the current time in milliseconds. You will use this value as your time-zero reference for as long as the song keeps playing.
Now at every video-frame (or indeed at any time), you can calculate the number of milliseconds until a given note will be played, relative to the current system-clock-time:
int NoteRecord :: GetMillisecondsUntilNoteIsPlayed(int songStartTimeMillis, int currentTimeMillis) const
{
return this->myNoteOffsetMillis - (currentTimeMillis - songStartTimeMillis);
}
Note that the value returned will be negative if the note's time-to-be-played has already passed.
Once you have that, it's just a matter of converting each note's current milliseconds-until-note-is-played result into a corresponding on-screen position, and you know where to draw the note-circle for the current frame:
int millisUntilNotePlayTime = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
int circleY = someFixedOffsetY + (millisUntilNotePlayTime/(1000/pixelsScrolledPerSecond));
DrawCircleAt(circleX, circleY);
... and if the user presses a key, you can calculate how far off the user was from the correct time for a given note using the same function, e.g.:
int errorMillis = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
if (errorMillis < -50)
{
printf("You're too slow!\n");
}
else if (errorMillis > 50)
{
printf("You jumped the gun!\n");
}
else
{
printf("Good job!\n");
}

Creating gamemaker's gravity in c++

in c++ ive been trying to recreate gamemakers gravity, so far i have this:
double script::lengthdir_y(double len,double dir){
return -sin(dir* M_PI/180) * len;//Create equivalent GM lengthdir_y code.
}
double script::scr_findhsy(player* plr){
if(plr->jumping==true)
{
int rpt, vspeed, y;//Make variables.
rpt=(clock()-plr->LastMovingYUpdate)/(1000/30);
//Check the current time, and take away the time when the player started jumping to find the time inbetween, then divide it by 1000/30 to find the ammount of GM steps.
vspeed=plr->vspeed;//Players vspeed.
y=plr->y;//players y.
for(int i=0; i<rpt; i++)//Plus the lengthdir_y code to the vspeed, and plus the vspeed to the y the correct ammount of times.
{
vspeed+=lengthdir_y(0.5, 270);
y+=vspeed;
}
return y;//return the new value.
}
else
return plr->y;//If the player isnt falling, send the original Y value, because nothing needs to be updated.
}
The y value of the player doesn't get updated every step (or 33.33 milliseconds in c++) to reduce lagg, so i created this script to get the right Y value when its needed instead of every step.
But it never seems to come out properly O.O
Here some test results of debugging in the client
In these test the client sends the proper Y value, and the server sends what it thinks is the Y value of the player:
Test 1
-Server: 384
-Client: 384
-Description: Standing completely still, works perfectly.
Test 2
-Server: 373
-Client: 349.50
-Description: Standing completely still, works perfectly.
Test 3
-Server: 318
-Client: 279.50
-Description: Standing completely still, works perfectly.
So when the player jumps, the value is meant to decrease because the Y is getting smaller, which works on both the client and the server, besides that the vaules are way off.
After the player starts falling down because of gravity, the server keeps reading "318" until the player hits the ground and the values get updated.
It could be because of integer division:
1000/30 in this sample will equal 33 not 33.333.
You'll need to change it to
1000.0/30.0
Furthermore, you save the result into an int, maybe rpt should be a double type.
I'll admit, I'm grasping at straws here.