Link to home
Start Free TrialLog in
Avatar of churley
churley

asked on

Assigning a number to an image

I am writing a simple Blackjack game using VB 5.0
On Form_Load, I want to call a function called Get_Card that generates a random number from 1 to 52(for the cards) and each number has a card assigned to it which will be returned as a .jpg so that I can assign it to an image control.
The way I have it set up is imgDCard1-imgDCard7
I want to call a function that will essentially generate a random card for imgDCard1 and assign it a card image.
My images are named acesp.jpg,2sp.jpg,3sp.jpg etc
acedi.jpg,2di.jpg etc.
What is the best way to both make the assignment and then insert the picture.
Thanks
ASKER CERTIFIED SOLUTION
Avatar of mdougan
mdougan
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mark2150
mark2150

I'd make a couple of changes. First I'd rename all the card images to 01-52 with 01.jpg being the ace of, say, spades and 52.jpg being the king of, say, diamonds. 01-13 are all the spades, 14-26 hearts, 27-39 clubs, 40-52 diamonds. This will greatly simplify conversion of the random card selection number into a file name.

Additionally, you can internalize the cards by placing them all into an picture clip control. They will be compiled into your application and will be available much faster than if they are loaded from disk. The downside to this is that you lose the ability to change decks simply by replacing 01.jpg-52.jpg with alternate files.

I would also recast the imgDCard as a single control array instead of seven separate controls. This will simplfy your coding as you'll only have one service routine with an index to tell you what card is selected instead of seven more-or-less identical routines.

When you shuffle a desk, have an pair of arrays of 52 elements. zero it out to begin th shuffle and then start generating random numbers in the range of 1-52. As each card is picked, look in one array to see if that card has already been selected. If it has, try another random number. If it hasn't been selected make the array element true to show that the card has been selected and then place the card number in the next sequential slot in the 2nd array. At the end of the run you'll have the 2nd array filled with a unique sequence of cards. You can "deal" from this desk simply by indexing sequentially thru the array. Since the card number matches the file name you can rapidly load the cards in and update the display.

M

I agree wholeheartedly on all of Mark2150's points, with the exception of the one about putting the images in a PicClip control.  It would be a lot more efficient to do it this way, but, depending on the size of the images, it could make your EXE huge.  That being the case, I'd rather read them in from files.

Mark's scheme for shuffling is exactly how I've implemented it in my Blackjack game. Which makes it very easy to keep track of what has been delt.

The naming/numbering convention he recommends would be good, except it gets more complicated if your game allows for more than one deck of cards.  In BlackJack, almost all games have at least two decks of cards, some more.


Avatar of churley

ASKER

Thanks for the super fast and accurate answer
You can do an end run on the multiple deck problem by simply expanding your number range & the arrays to 104 (two decks) 156 (three decks), etc. When you need to figure out what image to show (Card MOD 52) will give the card no matter how many decks CARD can be. (ie if CARD is 53, CARD MOD 52 gives 1, which is to say that you'll feed 01.jpg or the Ace of Spades.)

M

I like that!  It never occured to me to use MOD for that but it would work great, I'm sure.  Have you written a few of these card games mark?
Been coding *forever* (grin). Never done a multiple deck game but these things just come to me!

Drop by my web page sometime - www.cyberchute.com/rvbus/madmark

M

Avatar of churley

ASKER

Just out of curiosity, how did you handle the problem of an ace changing value from 11 to 1.  What I did was set a flag starting at 0 and when an ace comes it sets it to 1 and decrements the total by 10 then sets the flag back to 0.  This works great as long as there are not 2 aces in the players hand.  If there are two, it never sets the second ace to 1(it doesn't decrement the total by ten the second time).  Any ideas?
Well, it's up to the user to decide if the Ace(s) value is a 1 or a 10 (except when a 10 value would cause a bust, then it's automatically a 1).  

I've done my game using classes, and the player has a class called hand, which includes instances of the cards.  The hand class has two values, Value, and AltValue.  If there is one or more aces in the hand, then AltValue = Value + 10 (since you could never have two aces worth 11 each).  

Then, when evaluating the winner of the game, I first check to see if AltValue is less than 21, and if so, I use it (it may be the same as Value if there are no aces in the hand).  If AltValue is greater than 21, then I use Value.
Avatar of churley

ASKER

One last super quick question.  I am saving individual player data so that a person can run an "account"  The players are stored in a .dat file that is accessed through a different form.  It sends the account info to the game screen with no problem.  I run into trouble, trying to send the balance back.
I used a module to store the info in PlayerData which has the elements Name and Balance.  Any ideas on how to get it back to the data file?
.Dat file is a tad cumbersome. Use the registry and you'll not have to track the file or worry about updating. Create a different registry setting for each user and they can come back a month later and that machine will still have the correct total. No files to manage, no directories to track, no initialization.

Look in the help under SaveSetting and GetSetting. Is almost trival to use.

M

To answer the .DAT question, through, I suspect it is how you are opening the file for update.  If you are going to update a record in place, then you need to be opening the file for Random Access Update.  But I think that Random requires you to give a record length - and that means that you will need a fixed length record size (so, it will be a little trickier for you to put your Names into a fixed length string before writing them to the file).

An alternative, though not very efficient way, would be that when you go to save someone's account balance, you read the current .dat file and write out to a .tmp file, up to the record that you want to update, then write the updated record out to the .tmp file, then write the rest of the entries from the .dat to the .tmp file.  When you hit the end of the .dat file, close it, then delete it, then rename the .tmp file to .dat.  This sounds horrible, but it would probably work great for up to several hundred users.  If you were going to have a lot more than that, I wouldn't recommend it.

marks's suggestion about the registery would work very well, and would make the updating very easy, but I cringe every time I hear of people using the registry for application data.  At most, the registery was meant to hold initialization info for an application.  And, now, with almost all applications using it, it is getting really cluttered.  If you are only going to have a handful of users, I'm sure this would work well for you, but if you want to be able to scale the game up to larger numbers of users, it's not a very good option.

The obvious question is, why aren't you considering storing this info in a database?

Also, I'm sure you're doing this for fun, but if you were doing this for real, you would want to encrypt, or somehow obfuscate the name and account balance - where ever you stored it, flat file, registry or database.

MD
Avatar of churley

ASKER

I am doing it for fun and have finished except for one slight bug.  I decided to go ahead and use a .dat file.  The problem I am running into is that when I hit my Exit button, the code is:
SaveCurrentRecord
Close #gFileNum
End

Then problem is, it changes all of the records to the name and amount of the current record.  Any idea why or how to fix that?
I'd have to see the code for SaveCurrentRecord but it should look something like this.  Let's say that you have opened the .dat file for:

Random Access Read Write as gFileNum Len=Len(FileBuffer)

and that you have read up to the record that has the current users data, and that you have kept a count of the number of the records in the file, and that you put the new name and amount into a variable called FileBuffer of user-defined type.  Then, you can issue the following statement to update the record in place.

Put gFileNum,RecNo,FileBuffer


Avatar of churley

ASKER

here is what SaveCurrentRecord looks like:

Public Sub SaveCurrentRecord()
gPerson.Name = txtName.Text
gPerson.Balance = txtBalance.Text
gPerson.Comments = txtComments.Text
Put #gFileNum, gCurrentRecord, gPerson
End Sub


Assuming that you have looked at the value of gCurrentRecord, and verified that it is the record number you wish to update, and excluding the possibility that you are not calling SaveCurrentRecord inside of any kind of loop, it looks like it should work.

Out of curriosity, can you post the definition of the user-defined type for gPerson?

Just to be safe, I make extra special caution to ensure that I use fixed length records, and that as I read and write to the file I take into account if the file has a crlf at the end of each record (if so, I need to include two extra bytes at the end of my data-type)

The only other thing I'd be concerned about is if the user-defined data-type is using fixed length strings (which it should be), then you should really be clearing them out before updating them, so that you don't get garbage.  So:

Type
   Name as String * 10
   Balance  as String * 8
   Comments as String * 10
End Type

Public Sub SaveCurrentRecord()
' this clears out the space with binary zeros
gPerson.Name = String(10, 0)
gPerson.Balance = String(8, 0)
gPerson.Comment = String(10, 0)

' this formats your variable length strings into fix length and alligns them
gPerson.Name = Format$(txtName.Text , "@@@@@@@@@@!")

gPerson.Balance = Format$(txtBalance.Text , "00000.00")

gPerson.Comments = Format$(txtComments.Text , "@@@@@@@@@@!")

Put #gFileNum, gCurrentRecord, gPerson

End Sub

Avatar of churley

ASKER

The problem seems to be that it is not recognizing the current record.  It always assumes record one is the current record and changes that record but leaves the rest unchanged.  You have my gPerson exactly the way that I have it so I did not reproduce it here. Thanks
Avatar of churley

ASKER

I figured it out.  It was kinda dumb actually.  I never passed the current record number between forms.  Thanks for all of the help.
Cheers!