Tried this just for fun.
Cheers,
Tried this just for fun.
Cheers,
Dammmm! That's fast! Thank you soooo much. Now I can turn my attention to important things!
Connexion, I have a quick question. Can you describe how the routine operates?
Awww, it looks like someone beat me too it!
Oh well, I'll post my solution anyway. After all, I've already put all this work into it!
I'll attach my DB (Access 2000), but the core of what I did is the following Function:
All you do is pass the function the names of your Table and the Field (in that table) that holds the "Sort By" ID.Code:Function ShuffleTheDeck(strTableName, strSortIDField) On Error GoTo Error_ShuffleTheDeck Dim work As Workspace Dim rstDeck As DAO.Recordset Dim rstVerify As DAO.Recordset Dim fldDeck As DAO.Field Dim boolTransActive As Boolean Dim nbrCards, nbrRandom As Integer Dim strCriteria As String ' Initialize our Transaction tracking variable boolTransActive = False Set work = DBEngine(0) Set rstDeck = CurrentDb().OpenRecordset(strTableName, dbOpenDynaset) Set rstVerify = CurrentDb().OpenRecordset(strTableName, dbOpenDynaset, dbSeeChanges) ' Catch updates made from the rstDeck Recordset Set fldDeck = rstDeck.Fields(strSortIDField) ' Count the number of Records in the table and then jump back to the first ' one rstDeck.MoveLast nbrCards = rstDeck.RecordCount rstDeck.MoveFirst ' Start the Transaction and set our tracking variable work.BeginTrans boolTransActive = True ' Reset all the Field values so we don't run into an infinite loop! Do Until rstDeck.EOF With rstDeck .Edit fldDeck.Value = 0 .Update End With ' Go to the next Record rstDeck.MoveNext Loop ' Jump back to the first Record for the next loop and update our other rstDeck.MoveFirst ' Loop through our table assigning a new random number to each Record Do Until rstDeck.EOF ' Generate a random number nbrRandom = Int((nbrCards - 1 + 1) * Rnd + 1) ' Try and find that number in the table strCriteria = "[" & strSortIDField & "]=" & nbrRandom rstVerify.FindFirst strCriteria ' If we find the number, keep generating random numbers until we find one ' not in use Do While Not rstVerify.NoMatch nbrRandom = Int((nbrCards - 1 + 1) * Rnd + 1) strCriteria = "[" & strSortIDField & "]=" & nbrRandom rstVerify.FindFirst strCriteria Loop ' Once we have a unique random number, assign it to the current Record With rstDeck .Edit fldDeck.Value = nbrRandom .Update End With ' Move to the next Record rstDeck.MoveNext Loop ' Save the database changes and unset the Transaction tracking variable work.CommitTrans boolTransActive = False ' Alert the user MsgBox "Done!" FunctionClosing: ' CLean up the variables and exit the function rstDeck.Close Set fldDeck = Nothing Set rstDeck = Nothing Exit Function Error_ShuffleTheDeck: ' If the transaction has been started, revert all the changes If boolTransActive = True Then work.Rollback boolTransActive = False End If ' Alert the user there was an error MsgBox "An error occurred while attempting to shuffle the deck of cards!" & vbCrLf & _ vbCrLf & "Message: " & Err.Description ' Exit the function Resume FunctionClosing End Function
P.S.
Because ConneXion has a newer version of Access than I do, I probably can't look at his DB (but I'm gonna try anyway!), but I would assume we came up with similar solutions.
Last edited by Rawb; 07-20-2010 at 09:06 AM. Reason: Attachments are so HARD!
Thank you, Rawb, for the time and effort you put into your solution. I checked out the db you sent and it does indeed work much, much faster than what I had, but in all honesty the solution that Connexion offered is faster still. His takes no longer than half a second to do it all, and probably even less time than that.
I don't know any way of "downconverting" to 2k from 2003, but I can at least describe what he did as best I can. You deserve it for the effort you put in!
He has two tables, tblCard and tblShoe, each with 5 fields, Card ID, Suit, Kind, Deck, and Seed, with CardID as an AutoNumber field in the tblCard. The highest seed number is 96,621, and it appears that they are all random numbers between 1-100,000.
There are two queries, qmakShoe, and qupdSaveSeed.
Here is the SQL of the qupdSaveSeed.
SELECT tblCard.CardID, tblCard.Suit, tblCard.Kind, tblCard.Deck, Int((97344-1+1)*Rnd([Seed])+1) AS ShuffleSeed INTO tblShoe
FROM tblCard
ORDER BY Int((97344-1+1)*Rnd([Seed])+1);
And the SQL of qmakShoe.
UPDATE tblCard INNER JOIN tblShoe ON tblCard.CardID = tblShoe.CardID SET tblCard.Seed = tblShoe!ShuffleSeed;
Here is the event code attached to the shuffle button.
Private Sub cmdShuffle_Click()
On Error GoTo Err_cmdShuffle_Click
Dim stDocName As String
DoCmd.SetWarnings False
stDocName = "qmakShoe"
DoCmd.OpenQuery stDocName, acNormal, acEdit
stDocName = "qupdSaveSeed"
DoCmd.OpenQuery stDocName, acNormal, acEdit
DoCmd.SetWarnings True
Exit_cmdShuffle_Click:
Exit Sub
Err_cmdShuffle_Click:
MsgBox Err.Description
Resume Exit_cmdShuffle_Click
End Sub
Private Sub cmdShoe_Click()
On Error GoTo Err_cmdShoe_Click
Dim stDocName As String
Dim stLinkCriteria As String
Forms![fmnuMain].Visible = False
stDocName = "frmShoe"
DoCmd.OpenForm stDocName, , , stLinkCriteria
Exit_cmdShoe_Click:
Exit Sub
Err_cmdShoe_Click:
MsgBox Err.Description
Resume Exit_cmdShoe_Click
End Sub
Private Sub cmdQuit_Click()
On Error GoTo Err_cmdQuit_Click
DoCmd.Quit
Exit_cmdQuit_Click:
Exit Sub
Err_cmdQuit_Click:
MsgBox Err.Description
Resume Exit_cmdQuit_Click
End Sub
I hope that explains it well enough for you to reproduce it. I sincerely appreciate everyone's efforts on my behalf, and I finally have what I need and can move on to finishing writing the program and doing the evaluations that I've been longing to do for some time.
Connexion, if you're looking in I'd like to ask something. Each card has a random seed number, the lowest being 244 and the highest mentioned at 96,621. Is there ever a possibility where the card with the lowest seed would ever be the first card in the deck, and conversely whether the one with the highest seed number could ever be the lowest card in the deck?
Hi again,
I'll attach a "2000" version for Rawb, et al.
Thanks for posting the SQL and code.
After a second look, I thought it would be better to modify the make table query slightly, here's the new SQL of the qupdSaveSeed:
SELECT tblCard.CardID, tblCard.Suit, tblCard.Kind, tblCard.Deck, Int((97344-1+1)*Rnd([Seed]+[CardID])+1) AS ShuffleSeed INTO tblShoe
FROM tblCard
ORDER BY Int((97344-1+1)*Rnd([Seed]+[CardID])+1);
The form code doesn't do much. The action is in the queries. Each Shuffle generates a new "seed" for each card, then just sorts on the seed. As Ted C explained, you don't need to use 1-312 for this. In fact I used 312x312=97344 as a lark. After that, each new seed is based on the previous one plus the ID just to ensure they'd be different each time. If you choose, you could number the records after the fact; it's up to you.
"Is there ever a possibility where the card with the lowest seed would ever be the first card in the deck?" Yes, every time (because I sort the seed in the shuffle), but as I mentioned, each card gets a new random seed for each shuffle.
Cheers,
Thanks again, Connexion. I updated the query with the new sql. It's really, really fast. It takes me more time to blink than it does to shuffle six decks.
I've got another question. I never intended this to be anything more than an analytical program, so graphics were never a consideration. In fact, in my original shuffle routine I made no provision for suit, since for analytical purposes a six of clubs or diamonds is still a six, but your routine does include suits. I'm just wondering if you know how I can interface cards.dll with the routine. If I can utilize the graphics without too much effort it would be nice.
Why not just use a legit programming language?
Porque no los hablo.
Parce-que je n'en parle pas.
Ummm...because I don't speak them!![]()
I originally asked for, and received, a great routine that shuffles the cards very fast and stores the shuffle in a table. After posting it in this and other forums, the suggestion was made that a deck could be represented with a single number, something I had not considered before. Since one of my interests is to be able to store many given decks, I'm now realizing that it would be 312 times easier to store one number than a six-deck shuffle.
Does anyone know of a way to convert that shuffle into a single number, and then convert the number back into the shuffle?
Connexion, it's been several months since I started this thread and since you provided a really good solution for me. I've been sidetracked with a million different things and haven't really focused on using what you sent until now. I modified the routine slightly to provide an additional field which contains a numerical value for each card for the purpose of calculations, converting JQK to 10 and A to 11.
The routine you sent works perfectly to shuffle the shoe. However, I'm still trying to sort out the best way to save the shoe so that it can be recalled and replayed as a constant. I want to evaluate the exact same card sequence played and/or betted differently to determine the best play. After playing through a shoe the number of wins and losses can be determined and a decision made as to whether that was a good or bad shoe.
Ultimately I'd like to group a whole bunch of really bad, good, and average shoes for the player together, so that the most advantageous strategy can be developed for that scenario. I would run 20, 50, 100, or more known bad shoes one after the other, or a similar number of good shoes. That isn't meant to simulate casino play, but instead to develop the optimum way to play when the cards are going for me or against me.
Either way it would require creating three new tables, calling them GoodShoe, AverageShoe, and BadShoe, as an example, and saving the shoe to the appropriate table after playing through the cards and deciding which category it falls into.
I see two ways to accomplish saving the shoes. One is to save the entire shoe as a 624 character string in a Memo field in those tables. That obviously requires conversion of the shoe into a string, and then the reversal of that process to convert the string back into a shoe for play.
The other way is to run an append query which would just add the current shoe to the records already stored in the appropriate table. Since each shoe is exactly 312 records long, a select query could later be run, selecting as an example records 313-624, which would isolate only the second shoe from the larger list.
The second option seems cleaner, yet ultimately I would have many more records to sort through. 100 shoes would be over 31k records, which seems cumbersome even if it's not an unmanageable number. The first option would have only as many records in each table as there are shoes stored, so that seems to be an advantage. I'd like to ask your feedback as to which you think is more practical, and of course, whether you can think of another alternate method of accomplishing the same thing.
I'd also like to ask for some assistance in the conversion to string and reconversion back to shoe should you believe the first option more practical. I'm not certain of how to proceed with this, and any ideas or solutions you can provide to accomplish it would be appreciated.
There are always dozens of ways of accomplishing something. Since I'm in the very early stages I have the option to select the most logical before moving farther along, and I'd like to choose wisely.
Hi,
Access doesn't fret over the number of records, only the size of the database, and 31K records isn't much. Look up the word "specifications" in the Access Help and check the table size limit.
I recommend the append method, but I only see you needing one other table [tblShoeBox] unless you want to get fancy with descriptors for each shoe (if so, then you'll want to use two more tables and probably rename some of my original tables). I also suggest you add a field [ShoeID] in [tblShoeBox] to identify the shoes for each 312 card shuffle (so if you call for [ShoeID]="GoodShoe88", you will get the 312 cards from that shoe.
Cheers,
Thanks. The append query it is. There's surely no extra messing around with it!
I've got another question. Of course a shuffle is a shuffle, but casinos always do two other steps. They cut the deck and burn the first card after the cut. I want to take my cards through the exact same steps. Of course I don't think it matters one way or the other as far as outcome is concerned, but I want to duplicate what is done in real life as closely as possible, and if they do it I should too.
I already accomplished both tasks, but I'm sure there's a better way, and I'm just asking for advice. After the shuffle, I select a random number between 52 and 260 (you can't cut closer than one deck to either end), go to that number record, select the record, and then use SendKeys (yeah, I know the purists hate that) to select all the records from the cut record to the top. I then use a Cut command and move to a new record, select it and then paste. That's the cut.
The process is virtually identical for the burn. I just select the first record, cut it, go to a new record and paste it. I still have all 312 records in the set and I'm ready to play.
I don't particularly like using the SendKeys and cut and paste. Any easier ways you can think of? The sequence works very well, of course, and ultimately what matters is that the job is done consistently, but it does slow the process down a bit. It's not much, and the whole operation takes maybe a second or second and a half, but it's still slow compared with the extremely fast shuffle routine you provided.
Again, any feedback would be appreciated.
Shuffling cards
For the cut; I guess I'm not clear on what you mean. Are you cutting the cards/deck or cutting the shoe?
If you're cutting the deck, then I can't help but think you're wasting your efforts. Access isn't likely going to cheat, so there's nothing to gain from building a procedure to simulate a cut. But if you insist, and you already have a procedure to "cut", then the second you spend waiting for the "cut" sequence to run should satisfy your suspicions, or desire for "luck".
"The practice of cutting is primarily a method of reducing the likelihood of someone cheating by manipulating the order of cards to gain advantage. Even if the dealer does not plan on cheating, cutting will prevent suspicions, thus many rules require it. Some players also consider the cut to be lucky."
However, if you're cutting the shoe, then simply selecting a random card number to stop play would be sufficient; no need to "move" the cards around for that.
The burn card seems to be a different matter. I'm suprised to learn you return the burn card to the end of the deck; unless you also "cut the shoe" so the burn card is never played.
Of course I know that Access isn't going to cheat, and a shuffle is a shuffle. I just want to simulate everything, and for the amount of effort involved (maybe a half hour) I know that when I'm actually playing the game, everything involved in the shuffle is as close to the real thing as possible. It's probably a really anal attitude, but as I said, it wasn't much effort and although there is a small efficiency sacrifice I like knowing that I've reproduced every step of the casino's efforts as closely as I possibly can.
I'm cutting the entire shuffled shoe. In the casino after all six decks are shuffled together the last thing that's done is to randomly insert a cut card into the shuffled stack of cards, move all the cards from the front of the cut card around to the back of the shoe, insert the red card, place the stack into the shoe, and burn the top card. I'm reproducing all of that.
I already have a red card instruction written in, the point beyond which no new hands are dealt and only the play already on the table is continued until completed. My local casino advised me that their red card point is about 2 1/2 decks from the end, so it was a simple matter to generate a random number in that range and set it as my red card point, which will be stored along with the rest of the shoe information. As cards are dealt from the shoe they are counted, and when the count exceeds the red card point a new shoe is called. If I didn't save the red card point, I would not be able to consistently and reliably reproduce the play even though I saved the shoe sequence, since I might deal less or more cards out of it one time through vs. another.
I returned the burn card to the bottom of the shoe just to keep the 312 card shoe intact. It's really irrelevant, since the card is never played. I could just as easily delete it.
Do you have any thoughts on a better way to cut the shoe? What I have works just fine, as I'm sure you can visualize from the description. I just thought there might be another way besides the SendKeys, and cut/paste operation I selected.