Sunday, February 9, 2014

std::seed_seq example doesn’t compile with VS2010



There’s a real surprise for you! The examples for using std::seed_seq often go along the lines of:

std::seed_seq seed2 = {102,406,7892};

Too bad it doesn’t compile on VS2010. Compiler Error C2552.

Somebody actually had it right and I found it here:


They give you a number of ways to do it. This is the one that worked:
 
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::seed_seq s4(a, a + 10); // can use iterators

I didn’t try the stream one.

Why bother with std::seed_seq? I came to the conclusion that a 32 bit seed only offers a measly 4294967296 unique deals. In real life, a deal of a deck of cards can take on 52! unique permutations. That’s a very big number.  This programmer says there are 2^225 unique ways to deal a deck of cards.


I would need a 225 bit seed to approximate real life but I don’t feel like tackling that problem right now. I hope to get to it. A good intermediate step is to seed std::mersenne_twister with 64 bits. As best I can tell, you need to use std::seed_seq. Here’s my seed code:

   QueryPerformanceCounter( &lCount );
   lCount.HighPart += sBaseSeed;
   sStartCtr = lCount.QuadPart;
   if (sLastStartCtr == sStartCtr)
   {
      sStartCtr = sLastStartCtr+1;
   }
   sLastStartCtr = sStartCtr;
   if (!mSeed)
   {
      mSeed = sStartCtr;
   }

   int lPart1(static_cast<int>(mSeed));
   int lPart2(static_cast<int>(mSeed>>32));
   int lArray[2] = {lPart1, lPart2};
   std::seed_seq lSeq(lArray, lArray + 2); // can use iterators
   mGenerator.seed(lSeq);

The s variables are statics and mSeed is __int64.  sBaseSeed is time(NULL) with the sum of the characters of the computer name added to it. This gives a different playing experience for most computers even if you power up your computers at the exact same time! Haha! I think you would have to have a really fast computer to get sLastStartCtr == sStartCtr but I put that in just to make sure. With 64 bits, I can get over 18,446,744,073,709,551,616 unique deals. I think that is in excess of 18 quintillion deals. Still far short of 52 prime but what the heck!

Needless to say, I’m skeptical of the whole process of generating pseudo-random numbers using algorithms that are generally available. This gives you pause:

The Guardian and The New York Times have reported that the National Security Agency (NSA) inserted a CSPRNG into NIST SP 800-90A that had a backdoor which allows the NSA to readily decrypt material that was encrypted with the aid of Dual_EC_DRBG. Both papers report[7][8] that, as independent security experts long suspected,[9] the NSA has been introducing weaknesses into CSPRNG standard 800-90; this being confirmed for the first time by one of the top secret documents leaked to the Guardian by Edward Snowden. The NSA worked covertly to get its own version of the NIST draft security standard approved for worldwide use in 2006. The leaked document states that "eventually, NSA became the sole editor." In spite of the known potential for a backdoor and other known significant deficiencies with Dual_EC_DRBG, several companies such as RSA Security continued using Dual_EC_DRBG until the backdoor was confirmed in 2013.[10] RSA Security received a $10 million payment from the NSA to do so.[11]


Which really calls into question the whole business of attempting to generate unique solitaire deals! Who knew? So I play a million hands and give it different seeds and I have no good way of verifying that the same deal didn’t come up twice. The odds of this happening are astronomical and if that happens, you can rest assured your random number generator is either crap or the NSA has their fingers in that pie. The next step is to verify that I get unique deals. With 8 gig RAM, the most I can verify with available memory is around 4 million deals. If I want to verify more, I’ll have to swap to disk and I won’t be doing that soon. Nonetheless, I don’t want to leave the computer running for hours and wait for the result. I am impatient. So I’m back to std::map and using a checksum to create lists of deals. The checksum is just the cards in the play stack added up. Each card in the deck is assigned a number from 1 to 52 and then I add up the cards. I posit that if a deal matches, the checksums would be identical. I use this as my key to my std::map. Here it is:
std::map<short, std::vector<std::pair <__int64, std::vector<Stack>>>> sStackList;

As games are played, I put the deal into this list as follows:

      short lSum(calcChecksum());
      sStackList[lSum].push_back(std::make_pair(getSeed(), mCardStacks));

I wanted to log the potentially offending seeds which is why it is an std::pair. After however many games are played, I then look for duplicates:

void Game::checkForDuplicates()
{
   size_t lStacksCompared(0);
   bool lDupFound(false);
   std::map<short, std::vector<std::pair <__int64, std::vector<Stack>>>>::iterator lIter = sStackList.begin();
   for (; lIter != sStackList.end(); ++lIter)
   {
      std::vector<std::pair <__int64, std::vector<Stack>>> &lStackList(lIter->second);
      for (size_t i = 0; i < lStackList.size()-1; i++)
      {
         for (size_t j = i+1; j < lStackList.size(); j++)
         {
            if (lStackList[j].second == lStackList[i].second)
            {
               std::wstringstream lStream;
               lStream << _T("Duplicate deal! ") << lStackList[j].first << _T(" ") << lStackList[i].first;
               displayString(lStream.str());
               lDupFound = true;
               break;
            }
            lStacksCompared++;
            if ((lStacksCompared%100000000) == 0)
            {
               std::wstringstream lStream;
               lStream << _T("Stacks compared ") << lStacksCompared;
               displayString(lStream.str());
            }
         }
         if (lDupFound)
         {
            break;
         }
      }
      if (lDupFound)
      {
         break;
      }
   }
   if (!lDupFound)
   {
      std::wstringstream lStream;
      lStream << _T("No duplicates found! ");
      displayString(lStream.str());
   }
}

Both std::tr1::mt19937 and std::tr1::mt19937_64 passed a million deal check (i.e. no duplicates were found). What a hobby this has become! It has it all. Coding challenges galore and international intrigue and skullduggery to boot!


Sunday, February 2, 2014

A call for mediocrity?

I started tracking consecutive wins and losses in the solitaire player. The results are easily predictable. After playing hundreds of millions of games, my game player wins one game in 5.36 games played. Conversely, it loses one game in about 1.234 games played. Thus the odds of winning two consecutive games is about 1 in 28.729 games played. 28.729 being calculated by taking 5.36 to the power of 2 (5.36^2).  The odds of winning three consecutive games is one in 153.99 games played (5.36^3). So on and so forth. If there is a problem with my random number generation, we should see some extreme variance in these odds. That is, my player will have some outrageous number of consecutive wins or losses given the total number of games played.

Let's examine a continuous run of game playing.
Win one in <5.42888> Ave cards remain <10.1497> One color deal one in <103.093> No play one in <52.356> No play/move one in <625> Total played <10000> Won <1842> Max consecutive wins <5> Max consecutive losses <51> seed <-1752695715>
After 10000 games played, the player had won 5 consecutive games and lost 51 in a row.
5.36^5 = 4424.09
1.234^51 =  45401.80
Thus the first 10000 games were quite "unlucky".

Win one in <5.54939> Ave cards remain <10.1738> One color deal one in <102.041> No play one in <51.4139> No play/move one in <526.316> Total played <20000> Won <3604> Max consecutive wins <6> Max consecutive losses <51> seed <-621770119>
After 20000 games played, it had won 6 consecutive games in a row.
5.36^6 = 23713.12

Win one in <5.43833> Ave cards remain <10.1819> One color deal one in <105.932> No play one in <52.1376> No play/move one in <413.223> Total played <50000> Won <9194> Max consecutive wins <7> Max consecutive losses <51> seed <-137475027>
After 50000 games played, it won 7 consecutive games.
5.36^7 = 127102.33
Now a "lucky" run.

Win one in <5.37109> Ave cards remain <10.1601> One color deal one in <105.263> No play one in <53.3204> No play/move one in <413.534> Total played <110000> Won <20480> Max consecutive wins <7> Max consecutive losses <53> seed <-1242274590>
After 110000 games played it had lost 53 consecutive games.
1.234^53 = 69135.87

Win one in <5.38089> Ave cards remain <10.1668> One color deal one in <105.105> No play one in <53.9291> No play/move one in <398.482> Total played <210000> Won <39027> Max consecutive wins <8> Max consecutive losses <53> seed <1626910630>
After 210000 games played, it had won 8 consecutive games.
5.36^8 = 681268.51

Win one in <5.38029> Ave cards remain <10.1704> One color deal one in <105.213> No play one in <53.9877> No play/move one in <402.194> Total played <220000> Won <40890> Max consecutive wins <8> Max consecutive losses <56> seed <-1955633737>
After playing 220000 games, it had lost 56 consecutive games.
1.234^56 = 129911.90

Win one in <5.36085> Ave cards remain <10.1561> One color deal one in <101.571> No play one in <54.1258> No play/move one in <391.144> Total played <530000> Won <98865> Max consecutive wins <9> Max consecutive losses <56> seed <711853774>
After playing 530000 games, it won 9 consecutive games. Lucky!
5.36^9 = 3651599.23

Win one in <5.36107> Ave cards remain <10.1572> One color deal one in <101.317> No play one in <54.1467> No play/move one in <387.597> Total played <600000> Won <111918> Max consecutive wins <9> Max consecutive losses <69> seed <1213694581>
My "luck" ran out after 600000 games played as it lost 69 consecutive games.
1.234^69 = 1998692.66

Win one in <5.36843> Ave cards remain <10.1576> One color deal one in <101.961> No play one in <54.2194> No play/move one in <384.399> Total played <2730000> Won <508529> Max consecutive wins <9> Max consecutive losses <75> seed <2049738009>
After 2.73 million games played, my bad luck continued with 75 consecutive losses.
1.234^75 = 7057273.96

When can we expect 76 consecutive losses and 10 consecutive wins?
1.234^76 = 8708676.07
5.36^10 = 19572571.89
Somewhere around 8 million games played, we should see 76 consecutive losses.
Somewhere around 19 million games played, we should see 10 consecutive wins.

Here's a run of 11 consecutive wins after 67 million games played. I wasn't logging consecutive losses with this version.
Win one in <5.35934> Ave cards remain <10.1514> One color deal one in <101.449> No play one in <54.2281> No play/move one in <381.972> Total played <67229791> Won <12544422> Max consecutive wins <11> seed <-1352972818>

Sometimes they come early, sometimes late, but we won't see extreme variance from these numbers. Casinos bank on this "reality" and this is why they are profitable. When we see extreme variance, we can be fairly certain it's cheating. For those of religious or spiritual persuasion, defying the odds in the extreme can be referred to as a miracle.

What about exceptional people? The world population stands at above 7 billion. The number of people born in the history of humanity is growing quite quickly. As more and more humans are produced, we should see extreme runs of "good" luck and "bad" luck. This would make that person exceptional. Was Adolf Hitler a run of 200 consecutive lost deals? Was Einstein a run of 20 consecutive wins?

I only offer this up as a rebuttal to the history channel alien theory. Einstein was an alien? I can only scoff. Furthermore, what constitutes good and bad luck as opposed to just runs of events that defy long odds? Einstein is viewed as a demigod in our culture but what real good has come from his knowledge? Piling up mounds of radioactive waste. Chernobyl? Fukushima? Hiroshima and Nagasaki? It's unclear what horrors await us. Stephen Hawking is a truly exceptional person but what good has he brought humanity? Visions of "whitey on Mars" does exactly what for the planetary condition?

So what does a "20 consecutive win" person look like?

“For me, I am driven by two main philosophies: know more today about the world than I knew yesterday and lessen the suffering of others. You'd be surprised how far that gets you.”
― Neil deGrasse Tyson

On a planet where one in two people live on $2 a day or less and one billion are starving, I don't see a lot of lessening the suffering of others. In terms of putting aside human exceptionalism, look at all the death and destruction wrought on the planet by humans. That is a lot of suffering life. Where are the "20 consecutive win" people? Instead, I see our most exceptional people on the planet engaged in the wholesale raping and pillaging of the planet. The Bill Gateses, Warren Buffets and Kock brothers held up as demigods. Gordon Gecko, invented as a parody, has become a role model.

Today is Ground Hog Bowl day. It's a ritualistic orgy of consumerist excess that this nation celebrates annually. Victim and predator alike rally to the cause of new commercials and QB success. Oh happy day! As for Mr. Tyson, how exactly do wage and debt slaves lessen the suffering of others?  Cura te ipsum (physician heal thyself)?

Tuesday, January 21, 2014

Odds of predicting the results of all 67 games in the NCAA tourney

With 67 games played, trying to predict the outcome of 67 coin tosses is 1 over 2^67 or 1 in 147,573,952,589,676,412,928. That's about one in 147 quintillion (less likely than winning 24 consecutive games of solitaire). Let's assume over half the games are indeed predictable with assorted nags taking on the better teams. 1 over 2^33 is only 1 in 8,589,934,592 (less likely than winning 13 consecutive games of solitaire). Hot diggity,  I think their money is safe.

Friday, January 3, 2014

Mistakes were made

I'm playing this solitaire game and solving games. Watching the mistakes that are made to solve a game makes me think of Hawking and his ideas about the imperfection of the universe. You can watch it here:

https://www.youtube.com/watch?v=DECAorZYErk

Watching mistakes result in favorable outcomes makes me wonder if a mistake-prone player could beat the winning rate of the "perfect" player. Could a formulation of mistake frequency do such a thing? It's worth investigating.

Another interesting aspect of this solver is watching how any given mistake really doesn't cause any changes until consequences present themselves. You can ignore moving a black queen any number of times but it really doesn't do anything until the other black queen shows up on the Talon and you play it. Life is a lot like this. We keep making the same mistakes over and over until real consequences present themselves. The river denial runs deep.

I find the game solving option extremely useful. I can play a hand and then solve it afterwards. If I lost and it yields a result of solutions or no solutions, I feel vindicated. I don't even look at the solutions, I'm just checking my play. If I won and it yields solutions, I can rest assured I made some fortunate mistakes.

There is something obscene about playing only winning hands. One game after another where the cards come up golden. I can't spot any trend on the deals and they all play like regular hands until good card keep coming up at opportune times. It's pure cheating the odds with technology and I can't help but think this might be a metaphor for our modern existence. It seems real but it truly is unreal. It's unsettling.

My son had me read some of David Lynch's book about fishing.

http://en.wikipedia.org/wiki/Catching_the_Big_Fish

It's a great book. He talks about how ideas can branch out into all sorts of directions. I'm not sure where this is leading. One thing is for certain, I'm clicked out and I still prefer to play a game of solitaire with a real deck of cards.

Thursday, January 2, 2014

Let's play some solitaire!

I've made the solver an interactive Solitaire game where you can play 3 card draw solitaire with infinite go rounds. In addition to playing Solitaire, it allows you to have have the computer solve games that you have lost. It also allows you to play only winning and solvable hands. It has full undo/redo functionality in addition to an option to have the computer make the next play.  This version of Solitaire is developed for Windows 7 and Windows XP. The x64 version is for 64 bit Windows. If you don't know if you have an x64 system, just download the non-x64 version. You can download it from here:

http://webpages.charter.net/jstefan/SolitaireSetup_1_0_0.zip
http://webpages.charter.net/jstefan/SolitaireSetup_x64_1_0_0.zip

Once you have dowloaded either zip, uncompress it and run the setup.exe found in the zip file. Feedback is welcome.

Below are the instructions included in the ReadMe.rtf file that is included as part of the install:

After installing the program, you will find a program item off your start menu. Running it the first time, it will try to size the window based on the resolution of your screen. It's set up for a minimum of 1280x1024. If your resolution is less, do not fret. The first thing to do is to go to the menu "Options/Window Layout" and reduce the zoom percentage. This will allow you to see the whole deal on lower screen resolutions. This is particularly useful for laptops which now have less resolution.

Menus:
File/New - Start a new game.
File/Open - Open a previously stored game.
File/Save/Save As - Save games.
File/Print - Print stuff.
Edit/Undo - Undo a previous move.
Edit/Redo - Redo a previous move.
Games/Computer Next Move - Have the computer make your next move.
Games/Replay Game - Replay a game.
Games/Solve Game - Attempt to solve a deal.
Start Continuous/Stop Continuous - Have the computer just play games and collect statistics.
Options/Play Only Winning Deals - Deals will only be ones that the computer can win. This excludes solvable and losing deals. This option is not sticky and will be off the next time you run the program. This is "as designed". Select "File/New" after turning on this option to get a new deal.
Options/Play Only Solvable Deals - Deals will only be ones that the computer can solve. This excludes winning and losing deals. This option is not sticky and will be off the next time you run the program. This is "as designed". Select "File/New" after turning on this option to get a new deal.
Options/Window Layout - Allows for the customization of the stack layout.
Options/Clear Won/Loss – Zeros out the won/loss counters.

After solving a deal or having dealt a game with the "Options/Play Only Solvable Deals" turned on, the "Games/Computer Next Move" command will play off a series of game snapshots which lead you to the solution. Unfortunately, they do not show all intermediate moves and it's left to your intellect as to how the game is actually solved. If you decide to make a move at any point in a solved game, the list of snapshots will be discarded and you will be on your own. This includes clicking on the stock stack. Not to worry though, choose "Games/Replay Game" to go back to the solution.

Moving cards:
Regular drag and drop to move cards around the deal. For ace cards, just move the ace into your ace stack area (by default, upper left of the window) and release the left mouse button. Unfortunately, I order the ace stacks for this version. Click on the stock to get the next three cards. When the stock is exhausted and you want to start back over, just left click in the stock area. To expose hidden cards on the Talon, middle click or double left click on the hidden card and it will show it (no cheating please). Useful when you win/lose a game and wish to see those cards. Right click will undo a move.

Output tabs:
Status tab displays informational message from the solver.
Statistics tab displays the win/loss counter.





 

Saturday, December 21, 2013

A program solving at 83.6%

I haven't posted in a while but this project will never die until I'm dead. I can usually put a few hours a month on it and the payback is great. I got an email from another computer solver and his program is solving at 83.6% on a thousand deals. His results can be found here along with his source code:

https://github.com/ShootMe/Klondike-Solver/blob/master/Statistics.txt

His program gives up after 60 million states so his deal processing rate is much higher than mine. 8.2 seconds a deal versus my 19 seconds a deal (hardware considerations aside). Here are my results on a thousand games I ran today as a comparison:

80.2% solved. Roughly 19 seconds on average to process a deal. Some deals are ridiculously difficult to process. I've seen some take about 10 minutes before they become unsolvable.

Why the discrepancy? If we have bugs in either of our programs, that would certainly result in the discrepancy. Another potential problem is generating random numbers. I use stl:mersenne_twister and he used his home grown version to generate numbers. I'll try his random number generator. I asked him to try stl:mersenne_twister.

I'm working on making my solver interactive and hope to have it ready to post in a month or two. This will be a fully interactive installable program that you can play Klondike Solitaire on (draw 3, infinite retries). In addition, you will be able to ask the computer to attempt to solve any given deal that you lost and allow you to step through the solution. In addition, you'll be able to have the computer make a move at any given time in the game. Undo/Redo functionality to back up and go forward on moves. Version 2 or the end product will model after Dr. BlackJack. You can have it warn you when the computer thinks you are making a mistake. I don't have a lot of time to spend on this but surprisingly, I've made quite a bit of progress on it just looking at it a few hours a month.

Sunday, June 9, 2013

Random dot org

As much as I like STL mersenne_twister, I feel the need to verify my win percentages with a different random number generator. Of course, you can get random numbers online from http://www.random.org/. This is a pretty cool site and he uses atmospheric noise to generate random numbers. How good is that! It's a free service and he doles out "bit allowances" for generating numbers. His API is described here:

http://www.random.org/clients/http/

Of course he will sell additional allowances and I like his scheme. That said, I doubt I can pony up the dough for running a million deals. Like most Americans, I am nothing more than a wage/debt slave. The free bit allowance allows me to play about 600 hands so I played 500 and the game player won about 1 in 5.7 deals. This is within the ball park of my usual 1 in 5.37 deals so I am pretty happy with the results. I hope to email him with my hat in hand and see if he'll give me a million deals in the off hours or something.

So how hard was it to get random numbers off the internet? Not very. He has some samples posted and of course the link is stale. I googled the sample and found a good link. Here is a C++ program written by Doug Hague which will download the numbers:

https://github.com/doughague/random-dot-org

I pulled this down and tried to build it in VS2010. Of course it uses something called LibCurl. Édouard Tallent's blog has a nice write up on how to get it up and running under VS2010.

http://quantcorner.wordpress.com/2012/04/08/using-libcurl-with-visual-c-2010/

The thing also uses GNU option parsing but I know I don't need that. #ifdef NOTUSED. haha. I manually hack in the options I want. So I build the thing and of course there's a missing dll. The ole libsasl.dll is missing and I hunt this down and find it here:

libsasl.dll

Run and test this standalone. Works a treat! I add a function in place of main(), change the project to be a static library. I still need all the bonus dll's though. I add the static library project to my solitaire solution and I'm pretty close. I modify my deck object to deal using a sequence of numbers from random.org and there you have it. Solitaire dealt off of atmospheric noise.