Solved

# computer interaction in the game of Nim!

Posted on 2001-08-10
Medium Priority
334 Views
I am creating a game called nim using VB. This game involves having 4 rows of counters. It is a two player game with one player being the computer. The two players take turns taking counters away and the person taking the last counter wins. A player can take any number of counters away on their go but only from one row out of the four at a time. So on one go a player CAN take all the counters from row 3 if they want to, but from no other row until their next go.

The rows are as follows:

row1 = 1 counter
row2 = 3 counters
row3 = 5 counters
row4 = 7 counters

I used labels to represent the counters. There are fifteen counters in total and the names of the counters start from; cmdCounter and end at cmdCounter15.

I put the following code in the click event of each counter, adjusting the counter name accordingly:

Private Sub cmdCounter_Click()
cmdCounter.Visible = False

End Sub

Having done that for each counter I can now take the counter away when I click on it, which is good. Heres wher i get to my problem.

As i said previously the game is two player with one player being the computer. I want to add a command button so that when the user has taken their turn they click on this command button, so that the computer takes its turn and removes counters. I am not very good at writing code and i was wondering if anyone out their can help me with this problem. I really have no idea where to begin in even thinking of this sort of code!!

Anyone got any ideas??

0
Question by:giiggsy
[X]
###### Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

• Help others & share knowledge
• Earn cash & points
• 17
• 12

LVL 11

Expert Comment

ID: 6372020
If you don't want your computer to play smart, you can just use a random.

i=int(rnd*50)+1 will get you a random chosen number ranging from 1-50

0

Author Comment

ID: 6372049
Otana,

I put that in the click event of the command button and nothing happend.Am i meant to put that in the click event?Im sorry if i sound thick but im being serious when I say that VB skills are not that good.
0

LVL 11

Expert Comment

ID: 6372070
after you make a counter invisible, it's the computers turn. So, after making a counter invisible, make loop that generates a random number which could be any number between 1 and the number of counters you have. Keep looping until you have a number of a counter that's still visible. If you have one, make that counter invisible.

Are you using a control array for your cmdCounters?

If you want I can post a bit of code, but I need to know whether you have a control array or separate counters.

0

LVL 11

Expert Comment

ID: 6372075
Sorry, I just noticed the info is already in your first post.

Do you know how to create a control array? It would be much easier, since you have to put code in only one event.
0

Author Comment

ID: 6372086
Hi Otana,

No I havent used a control array I've set up sixteen seperate counters.
0

LVL 11

Expert Comment

ID: 6372094
This is how you make a control array:

Place a label on your form, name it cmdCounter. Right-click on it, select copy. Now right-click on the form and select paste. You will asked if you want to create a control array, say "yes". The new label will appear in the upper left corner of your form. It will be the same as the first, but it will have an index of 1, whereas your first had an index of 0. Place the new label where you want it to.

Now paste all the other labels as well, you don't have to copy anymore.

Private Sub cmdCounter_Click(index as integer)
dim i as integer

cmdCounter(index).Visible = False

Do until cmdCounter(i).visible=true
i=int(rnd*15)+1
Loop

cmdCounter(i).visible=false

End Sub

That should do the trick.

0

LVL 11

Expert Comment

ID: 6372102
make this:

i=int(rnd*15)+1

into:

i=int(rnd*16)

The +1 is not necessary either, because your index goes from 0 to 15.

Hope I could be of help...
0

Author Comment

ID: 6372130
Hi Otana

what you did worked but i think i need the computer to be smarter.

With the code you gave me, as soon as i click on a counter another dissapears indicating the computer has taken its turn. But remeber when i said earlier that when its the users go (or the computers go) they can take away as many counters they like in one go as long as their on the same row, the user cannot do that with the code you gave me.

Thats why i need a command button to ensure the computer has its go. So if a user decides to take all the counters away from row four, the user can then press the comman dbutton so the computer will make its move in reply.

I hope i havent confused you too much.
0

LVL 11

Expert Comment

ID: 6372160
Ok, no problem, just move the following code to the onclick event of a command button:

Private Sub cmdComputerTurn_Click()
dim i as integer

Do until cmdCounter(i).visible=true
i=int(rnd*15)+1
Loop

cmdCounter(i).visible=false
End Sub

This way you can take as much counters as you like before letting the computer play.

For a bit more intelligence on the computers' part, I would say you need to use two randoms:

1 to pick a row that has still visible counters, and 1 to pick any number of counters on that row.

So I would use 4 variables to store the number of counters left on a row.

dim Row(1 to 4) as integer

At the beginning of the game, set:
Row(1)=1
Row(2)=3
Row(3)=5
Row(4)=7

Every time a player clicks a counter, subtract 1 from that row.

So, on the computers' turn, do the following:

Do until row(i)>0
i=int(rnd*4)+1
Loop

j=int(rnd*row(i))+1

So j gives the number of counters the computer will take away.

0

Author Comment

ID: 6372255
Otana,

I applied the code to the command button and it now lets me take at urn and give the computer a turn.

However, i didnt quite understand the following part of your answer, when you wrote

"So I would use 4 variables to store the number of counters left on a row.

dim Row(1 to 4) as integer

At the beginning of the game, set:
Row(1)=1
Row(2)=3
Row(3)=5
Row(4)=7

Every time a player clicks a counter, subtract 1 from that row.

So, on the computers' turn, do the following:

Do until row(i)>0
i=int(rnd*4)+1
Loop

j=int(rnd*row(i))+1"

Where do i set the variables at the beginning of the game? And wher would i put the code that you gave me as the last part of your answer?

Thanks for your help it really is appreciated.

0

LVL 11

Expert Comment

ID: 6372322
make Row(1 to 4) a global variable, declare it at form level. (before you declare any subs or functions)

I suppose you have a button that resets all counter to visible, to start a new game?

That's where this code should be:

Private sub cmdNewGame_Click()
Row(1)=1
Row(2)=3
Row(3)=5
Row(4)=7
End sub

These variables indicate the number of counters left on a row, so it is the maximum numbers of counters the computer may choose to remove. Therefor, you should put this also in your counter click event:

private sub cmdCounter_click(index as integer)
dim ActiveRow as integer
select case index
case 0
ActiveRow=1
case 1,2,3
ActiveRow=2
case 4,5,6,7,8
ActiveRow=3
case else
ActiveRow=4
end select

'Here comes the code I already gave you

Row(ActiveRow)=Row(ActiveRow)-1
end sub

So, at the end of the event, you tell your program there's one less counter on that row.

This does require you to place your indexes on the form in this order:

0
1  2  3
4  5  6  7  8
9  10 11 12 13 14 15

Because otherwise, the program will not know from what line you took a counter.

I hope this is still clear. : )

The following piece of code is to make the program pick a row that has counters on it:

Do until row(i)>0
i=int(rnd*4)+1
Loop

Then, you need to make the program pick a random number of coutners to take away, but it may not exceed the number of counters still visible.

j=int(rnd*row(i))+1

So j gives the number of counters the computer will take away.

Those last two pieces of code will come in the Click event of the cmdComputerTurn.

It's also important to decide which counters will be taken then, so I need to know this:

Can you take any counter(s) away or will you always have to take the last in a row first?

0

Author Comment

ID: 6372356
You can take ANY amount of counters from one row on one go. You are not allowed to take counters from one row and then take away counters from another row in the same go.

Thanks for your help Otana. I'm going to have to go now but I will be back in tomorrow and check any response you may have given.

Thanks for all the help you've given me! it really is appreciated.

Thanks
0

LVL 11

Expert Comment

ID: 6372376
No problem. If you have an e-mail adress, I can send some more code to you, but I understand if you do not want to put your address here in public.
0

LVL 11

Expert Comment

ID: 6372404
Still one more question!

I understand you can take only counters from the same row, and that it can be any number of counters.

What I mean is, can it be the first in the row if there are still others in that row?

This is important to decide the way in which the program has to decide which counters to take away on the computers' turn.

example:

0
1  2  3
4  5  6  7  8
9  10 11 12 13 14 15

if you have this on the board, and you take two counters from row 2, can it become:

0
3
4  5  6  7  8
9  10 11 12 13 14 15

or should it become:

0
1
4  5  6  7  8
9  10 11 12 13 14 15

0

Author Comment

ID: 6379304
Hi Otana,

Im back again.

In the last response you gave me before i left on Friday, you said:

"make Row(1 to 4) a global variable, declare it at form level. (before you declare any subs or functions)"

How do I make a global variable? I cant even do the simplest things!

0

LVL 11

Expert Comment

ID: 6379401
If you look at the code of your program, you have a series of subs, functions, events, ... programmed. Above all code, you can declare variables, these will then be known throughout your form, in all events, subs, functions.

So declaring a global variable is like declaring any other variable, only you do it outside your subs, functions, events,...

Type following line at the top of your code page:

dim Row(1 to 4) as integer
0

Author Comment

ID: 6379419
I put that at the top of my code page and I made all the other changes to the code like you suggested, but now when i run the game and select the "computers turn" command button the following error comes up:

"run Time Error 9
subscript out of range"

I selected "De-Bug" and it highlights the following piece of code in "cmdCompTurn"

Do Until Row(i) > 0
i = Int(Rnd * 4) + 1
Loop

Any Ideas?
0

LVL 11

Expert Comment

ID: 6379444
Normally that shouldn't happen, but you can build a safety in your loop:

Do Until Row(i) > 0
i = Int(Rnd * 4) + 1
Select case i
case 0
i=1
case 5
i=4
End Select
Loop

Subscript out of range happens when the index is a number outside your defined range. In this case, your range goes from 1 to 4, so 0 or 5 would give the error. Normally, with the Rnd function you would not get those results, but it's always possible.

Another remark, in your form_load event, you might want to put following command:

randomize

Otherwise, you may notice that the computer will often make identical moves.
0

Author Comment

ID: 6379459
Otana I did what you said and the same thing happend. It said subscript out of range.

Here is the full code i have put there:

Private Sub cmdCompTurn_Click()
Dim i As Integer

Do Until cmdCounter(i).Visible = True
i = Int(Rnd * 15) + 1
Loop
Do Until Row(i) > 0
i = Int(Rnd * 4) + 1
Select Case i
Case 0
i = 1
Case 5
i = 4
End Select
Loop

cmdCounter(i).Visible = False
j = Int(Rnd * Row(i)) + 1
End Sub

When i select DEBUG it highlights:"Do Until Row(i) > 0"

Have i written something wrong in the code?
0

LVL 11

Expert Comment

ID: 6379478
Sorry, my mistake. Place the "Until ..." parts, that are now behind Do, after Loop.

So, you should get (for both loops):

Do
...
Until ...
0

LVL 11

Expert Comment

ID: 6379480
Also, this line:

cmdCounter(i).Visible = False

should be between the two loops, not after the second.
0

Author Comment

ID: 6379546
I made the changes. However, now I can remove a counter but when i select "computers turn" a counter goes white then the game freezes and nothing responds so i have to stop it through the task manager. In the task manager it says its not responding.

Have i written the right code for the "computers turn"?

Private Sub cmdCompTurn_Click()
Dim i As Integer

Do
i = Int(Rnd * 15) + 1
Loop Until cmdCounter(i).Visible = True

cmdCounter(i).Visible = False
Do
i = Int(Rnd * 4) + 1
Loop Until Row(i) > 0
j = Int(Rnd * Row(i)) + 1

End Sub

0

LVL 11

Expert Comment

ID: 6379568
make sure you have this code also, otherwise your Row() values won't be set, and your loops will continue eternally.

Private sub cmdNewGame_Click()
Row(1)=1
Row(2)=3
Row(3)=5
Row(4)=7
End sub

I'm writing the code for computerturn, give me a few minutes to finish it, and then I can give the entire code at once, because now it's getting too difficult with all those little pieces of code.
0

LVL 11

Expert Comment

ID: 6379614
Ok, this should be ot. Remove all code I already gave you from the cmdCompTurn_Click event, then place all following code in its place.

Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim l As Integer

'/// Decide which row to take stones from
Do Until Row(i) > 0
i = Int(Rnd * 4) + 1
Loop Until Row(i) > 0

'/// Decide how many stones to take
j = Int(Rnd * Row(i)) + 1

'/// Decide which stones to take
Select Case i
Case 1
cmdCounter.Visible = False
Row(1) = 0
Case 2
For k = 1 To j
Do
l = Int(Rnd * 3) + 1
Loop Until cmdCounter(l).Visible = True
cmdCounter.Visible = False
Row(2) = Row(2) - 1
Next k
Case 3
For k = 1 To j
Do
l = Int(Rnd * 5) + 4
Loop Until cmdCounter(l).Visible = True
cmdCounter.Visible = False
Row(3) = Row(3) - 1
Next k
Case 4
For k = 1 To j
Do
l = Int(Rnd * 5) + 9
Loop Until cmdCounter(l).Visible = True
cmdCounter.Visible = False
Row(4) = Row(4) - 1
Next k
End Select

I'll explain a bit how it works:

First, you want to pick a row where the ocmputer is going to take stones from. So, you loop until you find a row that still has stones.

Second, you want to know how much stones the computer will take. So you create a random that picks a number between 1 and the number of stones left on that row.

Three, you decide which stones the computer will take. So you create a random that loops through the stones on the chosen row, until you find a stone that's visible. Make it invisible, and subtract 1 from Row(i), to indicate that there is one less stone on that row.

Repeat step three for the number of stones you have to remove.

I know this is a lot of code, so if there's anything you don't understand, just ask, I'll be happy to give some more explanation.
0

Author Comment

ID: 6379648
I did the changes like you said but now when i select Computers turn this error message comes up:

I select ok and it highlights the following line of code:
"cmdCounter.Visible = False"

"visible" becomes highlighted.
0

LVL 11

Accepted Solution

Otana earned 1200 total points
ID: 6379660
Oops. Sorry, my mistake. I haven't tested the entire code myself, because I would have to set up the entire code as well.

Because the Counters are in an array now, you amways have to specify the index.

Change the last block of code (the select part) into this:

Select Case i
Case 1
cmdCounter(0).Visible = False
Row(1) = 0
Case 2
For k = 1 To j
Do
l = Int(Rnd * 3) + 1
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(2) = Row(2) - 1
Next k
Case 3
For k = 1 To j
Do
l = Int(Rnd * 5) + 4
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(3) = Row(3) - 1
Next k
Case 4
For k = 1 To j
Do
l = Int(Rnd * 5) + 9
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(4) = Row(4) - 1
Next k
End Select

0

Author Comment

ID: 6379703

I did what you said again, and this time when I select computerturn, it freezes on me again. Ive put the following code in "cmdNewTurn" so i dont think it would have anything to do with the loops.

Private sub cmdNewGame_Click()
Row(1)=1
Row(2)=3
Row(3)=5
Row(4)=7
End sub

Would it be easier if i emailed you what ive done so far, because i might be missing something out. If you dont want to put your email address on here you can email me on sukhdeep.rai1@orange.net If you send me an email i'll reply to it and send you what ive done so far. Is that Ok?
0

LVL 11

Expert Comment

ID: 6379722
ok, I've sent you an e-mail.
0

Author Comment

ID: 6392490
After help through email Otana resolved my question.

The final code looked like this:

Dim Row(1 To 4) As Integer
Private Sub cmdCompTurn_Click()
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim l As Integer

Do
i = Int(Rnd * 4) + 1
Loop Until Row(i) > 0

j = Int(Rnd * Row(i)) + 1

Select Case i
Case 1
cmdCounter(0).Visible = False
Row(1) = 0
Case 2
For k = 1 To j
Do
l = Int(Rnd * 3) + 1
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(2) = Row(2) - 1
Next k
Case 3
For k = 1 To j
Do
l = Int(Rnd * 5) + 4
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(3) = Row(3) - 1
Next k
Case 4
For k = 1 To j
Do
l = Int(Rnd * 7) + 9
Loop Until cmdCounter(l).Visible = True
cmdCounter(l).Visible = False
Row(4) = Row(4) - 1
Next k
End Select

End Sub

Private Sub cmdCounter_Click(Index As Integer)
Dim ActiveRow As Integer
Select Case Index
Case 0
ActiveRow = 1
Case 1, 2, 3
ActiveRow = 2
Case 4, 5, 6, 7, 8
ActiveRow = 3
Case Else
ActiveRow = 4
End Select

cmdCounter(Index).Visible = False
Row(ActiveRow) = Row(ActiveRow) - 1

End Sub

Private Sub cmdNewGame_Click()
Row(1) = 1
Row(2) = 3
Row(3) = 5
Row(4) = 7
Dim i As Integer
'cmdCounter(Index).Visible = True
'cmdCounter(3).Visible = True
'cmdCounter(1).Visible = True
'cmdCounter(2).Visible = True
'cmdCounter(4).Visible = True
'cmdCounter(5).Visible = True
'cmdCounter(6).Visible = True
'cmdCounter(7).Visible = True
'cmdCounter(8).Visible = True
'cmdCounter(9).Visible = True
'cmdCounter(10).Visible = True
'cmdCounter(11).Visible = True
'cmdCounter(12).Visible = True
'cmdCounter(13).Visible = True
'cmdCounter(14).Visible = True
'cmdCounter(15).Visible = True
For i = 0 To 15
cmdCounter(i).Visible = True
Next i
End Sub

Row(1) = 1
Row(2) = 3
Row(3) = 5
Row(4) = 7

Randomize

End Sub

Thanks Otana
0

## Featured Post

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
This article describes how to use a set of graphical playing cards to create a Draw Poker game in Excel or VB6.
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). Uâ€¦
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applicâ€¦
###### Suggested Courses
Course of the Month11 days, 9 hours left to enroll