Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

6 Deck Card Shuffling in Access 3

Status
Not open for further replies.

MacroScope

Programmer
Joined
Jul 17, 2010
Messages
286
Location
US
I'm doing something not generally associated with Access. I'm writing an analytical blackjack program. Anyone familiar with Access knows that it can be instructed to do the things involved, namely count the hand, stand or hit at a certain level, and anything else required to play the game. My problem is that I've been unable to come up with a practical and fast shuffling routine, but I have special requirements.

I want the shuffled decks stored in a table for several reasons. I want to be able to compare two moves, say standing vs. hitting a 16 v 10. The only way that's possible is to be able to see what the next card would have been and then making a comparison of the results. Another reason is to be able to save really good or really poor decks so that they can be collected and strategies can be developed which are optimized to bad or good runs of cards.

Can anyone offer a means of shuffling 6 decks and storing the results in a table? I have been able to do it, but the wait is interminable, and I know there are much better ways out there. I just can't think of any!

Thanks for your help.
 
I assume your "slowness" is in the randomizing of 52 x 6 iterations without a repeating occurrence, since you'll have to wait until it resolves that. Can you show us the algorithm you are using?


Best Regards,
Scott

"Everything should be made as simple as possible, and no simpler."[hammer]
 
My current tables are simple. I have a table with one deck of cards in it, and a field on my play form for number of decks. The shuffle button (or automatically when the number of cards remaining drops below a set point) runs a macro whose repeat count is equal to the number of decks. Said macro runs an append query to append to another table one deck 4, 5, 6, or any desired number of times.

Once that table contains the correct number of decks, I used a routine with the Rnd() feature. I had a field on a form set to [Decks]*52 as the shuffle opened, so in the case of 6 decks, the field would contain the number 312. I then would generate a random number between 1 and 312 cards, go to that record number in the table that has the unshuffled cards, and set it as the first card in the Shuffled table. The final step in the routine was to decrement the counting field by one. The macro runs 312 times.

Then for the second card I would generate a second random number between 1 and 311 (the count field decremented, and also the number of remaining cards in the unshuffled table), go to that record and move its value to the shuffled table. In this manner I was able to generate a "randomly" shuffled multi-deck group. It works, but the problem is that it takes 30-40 seconds to complete the routine, and that's impractical if I want to run tens of thousands of comparison hands.

There are no relationships between tables since I can see no need for it, as nothing is related for future reference or lookup, and the shuffle decrementer form runs completely on unbound controls.

I hope that explains what I've done up to now. As I said, it does work, but just not efficiently at all.
 
> a means of shuffling 6 decks and storing the results in a table?

Shuffling a deck is pretty easy and quick. And I'm not sure that you need store each and every shuffle in a table. Yes, I know that you want to be able to retrieve particular shuffles at any time - but what if you could do that by storing a single number? Whuch you can ...

Below is a routine of mine that shuffles a single deck of 52 cards. If you provide a parameter it will provide the same shuffle every time for that number. So you just need to store that number to recreate a particular shuffle.

Code:
[blue][green]' Randomly shuffles a deck of 52 cards
' We can select exactly which shuffled deck by providing the WhichDeck parameter [/green]
Public Function ShuffleDeck(Optional Whichdeck As Variant) As Variant
    Dim lp As Long
    Dim tempcard  As Long
    Dim Swapcard1 As Long
    Dim Swapcard2 As Long
    Dim Cards(1 To 52) As Long
    
    [green]' Set up basic deck[/green]
    For lp = 1 To 52
        Cards(lp) = lp
    Next
    
    [green]' Random or specific shuffle?[/green]
    If IsMissing(Whichdeck) Then
        Randomize
    Else
        Rnd -1
        Randomize Whichdeck
    End If
    
    [green]' Simple card swapping shuffle[/green]
    For lp = 1 To 52
        Swapcard1 = 1 + Int(Rnd() * 52)
        Swapcard2 = 1 + Int(Rnd() * 52)
        tempcard = Cards(Swapcard1)
        Cards(Swapcard1) = Cards(Swapcard2)
        Cards(Swapcard2) = tempcard
    Next

    ShuffleDeck = Cards
End Function[/blue]
 
Thanks very much for your help.

Do I only need to change the 52 in the code to 312 to make it valid for 6 decks?

I absolutely agree that a number is far more practical than storing an entire deck, but the one thing it doesn't give (or at least I don't think it gives) is the ability to look forward into the deck to see what would have come up. Maybe it's awkward, but I have 4 controls on the play form, and the next 4 cards are in them. As a card is dealt from the #4 control, the value of that control is set to what was the #3 control, the #3 control takes on the value of the #2 control, the #2 control takes on the value of the #1 control, and the #1 control gets a new card from the deck. In that manner I can see the next 4 cards that will be dealt at any time by just looking at the controls.

As I think about it, that could be accomplished from the code as well, drawing the play card from the last control, moving everything down, and putting the next random card into the first as I'm doing now. Do you agree?

I don't see where in the code the number corresponding to the shuffle is generated to identify that particular shuffle for later use. Can you point that out?

Also, how do I assign the correct value to the cards as they're dealt? I seem to see only random numbers, not card values. Maybe I'm missing it there, but I don't see how that's accomplished.

Thanks again. I'm anxious to try it out but will have to wait until later today.
 
>Do I only need to change the 52 in the code to 312 to make it valid for 6 decks?

More-or-less, yes

>the ability to look forward into the deck to see what would have come up

Yes, it does. It returns the state of the entire deck in an array. You can then examine any card in the deck (the array) to see what it is at any time you like

>I seem to see only random numbers

That's because I haven't provided you with a complete solution to your problem, just the core point I was addressing (lightning fast shuffle of a deck, and no need to store a shuffled deck at all).

I represent the 52 cards as the first 52 integers (not random - just shuffled ...). You would have to write something that maps each integer to a specific card. Or store strings naming the cards in the array instead of integers. Basically there are a number of alternatives that could continue to use the basic solution I have provided.

So here's a fuller example illustrating some of this. But I'm still not going to write your full solution for you ... ;-)
Code:
[blue]Option Explicit

Public Sub Example()
    Dim lp As Long
    Dim MyDeck As Variant
    
    [green]' Ok, choose a specific shuffle[/green]
    MyDeck = ShuffleDeck(26)
    
    [green]' 1. List a single deck in unadulterated form[/green]
    For lp = 1 To 52
        Debug.Print NameCard(lp)
    Next
    Debug.Print
    
    [green]' 2. List our shuffled deck[/green]
    For lp = 1 To 52
        Debug.Print NameCard(MyDeck(lp))
    Next
    Debug.Print
    
    [green]'Shuffle again, randomly
    MyDeck = ShuffleDeck()
    
    [green]' 3. List our shuffled deck[/green]
    For lp = 1 To 52
        Debug.Print NameCard(MyDeck(lp))
    Next
    Debug.Print
    
    [green]'Do our specific shuffle again[/green]
    MyDeck = ShuffleDeck(26)

    [green]' 4. List our shuffled deck - should be the same as example 2 above[/green]
    For lp = 1 To 52
        Debug.Print NameCard(MyDeck(lp))
    Next
    Debug.Print
    
    [green]' Look at a specific card in our shuffled deck[/green]
    Debug.Print NameCard(MyDeck(12))
    [green]' Now look at the next card[/green]
    Debug.Print NameCard(MyDeck(13))
    
End Sub

[green]' Randomly shuffles a deck of 52 cards (or DeckCount number of decks)
' We can select exactly which specific shuffle by providing the WhichDeck parameter[/green]
Public Function ShuffleDeck(Optional Whichdeck As Variant, Optional DeckCount As Long = 1) As Variant
    Dim lp As Long
    'Dim decklp As Long
    Dim tempcard  As Long
    Dim Swapcard1 As Long
    Dim Swapcard2 As Long
    Dim Cards() As Long
    
    [green]' Set up basic deck(s)[/green]
    ReDim Cards(1 To 52 * DeckCount) As Long
    For lp = 1 To 52 * DeckCount
        Cards(lp) = (lp - 1) Mod 52 + 1
    Next
    
    If IsMissing(Whichdeck) Then
        Randomize
    Else
        Rnd -1
        Randomize Whichdeck
    End If
    
    For lp = 1 To 52 * DeckCount
        Swapcard1 = 1 + Int(Rnd() * 52 * DeckCount)
        Swapcard2 = 1 + Int(Rnd() * 52 * DeckCount)
        tempcard = Cards(Swapcard1)
        Cards(Swapcard1) = Cards(Swapcard2)
        Cards(Swapcard2) = tempcard
        [green]' Alternative method below, only using 1 random number
'        Swapcard1 = 1 + Int(Rnd() * 52 * DeckCOunt)
'        tempcard = Cards(Swapcard1)
'        Cards(Swapcard1) = Cards(lp)
'        Cards(lp) = tempcard[/green]
    Next
    ShuffleDeck = Cards
End Function

[green]' Given a whole number between 1 and 52 representing the 52 cards
' in a deck, return a unique card name for that number[/green]
Public Function NameCard(ByVal SelectedCard As Integer) As String
    [green]' Card[/green]    
    Select Case (SelectedCard - 1) Mod 13 + 1
        Case 1
            NameCard = "Ace"
        Case 1 To 10
            NameCard = SelectedCard Mod 13
        Case 11
            NameCard = "Jack"
        Case 12
            NameCard = "Queen"
        Case 13
            NameCard = "King"
        Case Else
            NameCard = "Unknown"
    End Select
        
    [green]' Suit[/green]
    Select Case (SelectedCard - 1) \ 13
        Case 0
            NameCard = NameCard & " of Spades"
        Case 1
            NameCard = NameCard & " of Hearts"
        Case 2
            NameCard = NameCard & " of Diamonds"
        Case 3
            NameCard = NameCard & " of Clubs"
        Case Else
            NameCard = NameCard & " eh???"
    End Select
    
End Function[/blue]
 
I thank you again for your help. I know that you've offered exactly what I need, but the problem for me is that I'm not a code guy. As in most things in life, when you know how to do something it's usually relatively simple, but when you don't it can be very difficult.

You said the following. "So here's a fuller example illustrating some of this. But I'm still not going to write your full solution for you..."

I would like to ask whether you'd be willing to do just that...write the full solution...for a price. I want to get this finished so I can move on to the whole reason for this, the analysis of the game itself, and obviously shuffling is so fundamental that I really can't move forward without it.

I know that you have offered the basic structure and format of the solution I'm seeking, but given my limited knowledge of code I could still spend a large amount of time trying to turn it into a practical and integral part of the program, and frankly I'm not at all confident that even with the direction you've provided that I'd be able to "...write something that maps each integer to a specific card. Or store strings naming the cards in the array instead of integers".

You hit the nail precisely on the head when you described my needs as "a lightning fast shuffle of a deck [actually, six of them] and no need to store the deck at all".

Please let me know if you're interested in working with me to complete this. It's very much worth it to me to get this behind me and move forward.

Thanks.
 
>"...write something that maps each integer to a specific card

I've already done that for you ...
 
I'm afraid that I'm fully occupied with a full time job. Whilst I'm happy to provide assistance where I can on these forums I unfortunately don't have the time to be a gun for hire.
 



bcmarshall(Programmer) said:
but the problem for me is that I'm not a code guy.
Then why bcmarshall (Programmer)?

Skip,

[glasses]Just traded in my old subtlety...
for a NUANCE![tongue]
 
I really do appreciate the time you've put into writing the solution that you offered and I will work with it to try to integrate it into the program.

I want to mention that I am not using graphics in this, so suit is irrelevant to me. I don't care if a six is diamonds or clubs, it's a six for analytical purposes, and I know that eliminating suits from consideration will greatly simplify things.

I have another question that perhaps you'd be willing to comment on. I posted this on several different forums in order to get maximum exposure, and while I think your solution is clearly the best I've seen, the second best is one line of SQL for a query that was offered. It is as follows.

SELECT INTO randCards FROM sixdeck ORDER BY Rnd(<some_number_field>)


The poster of that response did not provide more detail and has not answered a request for clarification. Maybe you understand it well enough to explain.

What is randCards? Would that be a table or field name? The same question applies to the sixdeck reference. And would the <some_number_field> be a table field whose type is a number?

I realize asking for a clarification of someone else's response is not what is generally done, but this seems like a means to the end that I desire that is within my grasp. I unfortunately don't have enough knowledge of code to be able to be able to take the better solution you offered to the point of utility without a very long trial-and-error battle that I would prefer to avoid. If the one line of SQL will get me there, that's really all I care about.

I understand that you have a full plate. Thanks for at least considering the offer I made.
 
Skip, I'll answer your question.

There are several ways to accomplish things in Access as I'm sure you know, and not all involve code. I have been writing complete databases in Access for 15 years starting with Access 2.0. I use macros rather than code, but regardless of how you may perceive that, the first database I wrote is still operating well.

I am also talking about very complete and sophisticated databases, with sales, returns, and orders tracked, and intricate search routines allowing for the isolation of virtually any parameter desired. I have written game programs (Sudoku and Blackjack). I also wrote a math flashcards program for my grandkids when they were small which allows one to select the function (+, -, *, or /), the largest number used (don't want to give little kids big numbers to work with), scoring routines to tell the percentage correct, and many other things.

So while I'm not a code guy, I can communicate with Access with the best of them. I have made it do things that few people have ever even considered using it for.
 
Regarding your SQL question, randCards would be a table here for sure, and <some_number_field> would be a field with numeric value, yes.

But I do have to ask - along with Skip - if you don't know anything about any code, then you shouldn't have chosen Programmer as your description for sure. If that is the case, might I suggest you change your description to TechnicalUser or some other more appropriate term? I know I understand a fair amount of code in a few different languages, but since I don't code for a living, I don't consider myself a programmer.
 
Okay, I knew I should have refreshed the page before posting to be sure. [wink] Either way, bcmarshall, calling yourself a programmer isn't exactly accurate. Sure, you DESIGN databases, but that's not programming, that's database design. There may not be a specific user type for that, so in that case, TechnicalUser may be the most appropriate. Of course, that's my opinion, and I'm not the forum owner.
 
By the way, on SQL structure, in case you're interested, here's a good reference:

And for programmer definition, take a look here:

So, you could stretch it to say that a person who has no idea about code is a programmer, but I believe you'd be far fetched to find a job description given for a programmer that would accept anyone without some working knowledge of the code they're supposedly "programming."

Nothing personal at all, I think we all just would prefer that our "user types" clearly represented what we are, or at least represent as accurately as possible. In general terms, most people (both technical and non-technical) would expect a "programmer" to really know some code.
 
At times, I think I should be labled, "fastest poster in the west" instead of "technical user", myself. [ponder]

;p
 
Thanks, gentlemen. I have bee actively writing in Access for a long time, successfully, and perhaps it's bravado on my part but the databases I've written do exactly what the users need (ant then some), they're reliable, and I have many people who would not call anyone else for their database needs. I acknowledge that I need to focus on studying SQL and VB, but I assure you that my selection of "programmer" as a title was in no way intended to deceive.
 
>SELECT INTO randCards FROM sixdeck ORDER BY Rnd(<some_number_field>)

1. sixdeck would be a table conatining all your cards from 6 decks, each uniquely identified by a numeric field (it might just be that numeric field, thus basically matching what my shufle rotine does to initialise itself

2. [blue]<some_number_field>[/blue] would be a field in sixdeck that (preferably) uniquely identifies each card (see above)

3. randcards would be an empty clone of sixdeck

4 [blue]ORDER BY Rnd(<some_number_field>[/blue] generates a set of unique random numbers, one per row in sixdeck, and then sorts on those random numbers - hence providing a random ordering of the rows (i.e. a shuffle)

5. [blue]SELECT INTO[/blue] inserts that new, random order of sixpack into randCards

The code is short, but actually is a relatively slow and expensive SQL operation. Having said that, it is probably fine for a table with only 312 rows.

The downside is that, as written, you would have to revert to storing the entire shuffled deck (randCards) for the reanalysis part of the problem.
 
Thanks, Strong. I really would prefer to use the code as you suggested initially. I can see that it is the better solution. I'm going to play with both it and the SQL concept until I have a working shuffle routine. As I said, I want to get past this roadblock so I can get on with the reason for writing the program in the first place.

Should you change your mind and decide that you've got the time to do this project, please let me know.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top