Random Button text

anaylor01

Well-Known Member
Licensed User
Longtime User
I have a database I pull a single record from. In the record is a question and 4 answers. 3 being incorrect and one being correct. I have 4 buttons. I need to populate the text of each button with random answer values. Right now I am places the values into 4 variables. I was thinking of doing something like this:
Dim question As String
Dim a(4) As Int

question = SQL1.ExecQuerySingleResult("SELECT question FROM trivia where ID = 116")
a(0) = SQL1.ExecQuerySingleResult("SELECT answer FROM trivia where ID = 116")
a(1) = SQL1.ExecQuerySingleResult("SELECT wrong1 FROM trivia where ID = 116")
a(2) = SQL1.ExecQuerySingleResult("SELECT wrong2 FROM trivia where ID = 116")
a(3) = SQL1.ExecQuerySingleResult("SELECT wrong3 FROM trivia where ID = 116")
lblQuestion.Text = question
Dim i, j, k, x As Int
Dim answers(4) As Int

For i = 0 To 3
answers(i) = i + 1
Next

For i = 0 To 3
k = Rnd(i, 4)

x = answers(i)
answers(i) = answers(k)
answers(k) = x
Next

' For j = 0 To 3
' Holdings(j).Clear
' k = j*13
' For i = 0 To 12
' Holdings(j).Add(answers(i + k))
' Next
' Holdings(j).Sort(True)

answer1.text = a(answers(k))
answer2.Text = "answer" + rand
answer3.Text = "answer" + rand
answer4.Text = "answer" + rand
I know that this will duplicate. I already have a process to prevent that but didn't want to put all the code here.
 

kickaha

Well-Known Member
Licensed User
Longtime User
It seems basically right, although I suspect that answer, wrong1, etc are text rather than numbers. Also not sure what you are doing with the button text.

I have modified it (slightly), and this should run with no duplications of answers.
B4X:
Dim question As String
Dim a(4) As String ' I assume the answers you retrieve are text

question = SQL1.ExecQuerySingleResult("SELECT question FROM trivia where ID = 116")
a(0) = SQL1.ExecQuerySingleResult("SELECT answer FROM trivia where ID = 116")
a(1) = SQL1.ExecQuerySingleResult("SELECT wrong1 FROM trivia where ID = 116")
a(2) = SQL1.ExecQuerySingleResult("SELECT wrong2 FROM trivia where ID = 116")
a(3) = SQL1.ExecQuerySingleResult("SELECT wrong3 FROM trivia where ID = 116")
lblQuestion.Text = question
Dim i, k, x As Int
Dim answers(4) As Int

For i = 0 To 3
answers(i) = i + 1
Next

For i = 0 To 3
k = Rnd(i, 4)

x = answers(i)
answers(i) = answers(k)
answers(k) = x
Next 

answer1.Text = a(answers(0))  'assuming answer1 etc are button names
answer2.Text = a(answers(1))
answer3.Text = a(answers(2))
answer4.Text = a(answers(3))
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
If the answers are integers, it is even simpler:
B4X:
Dim question As String
Dim a(4) As Int

question = SQL1.ExecQuerySingleResult("SELECT question FROM trivia where ID = 116")
a(0) = SQL1.ExecQuerySingleResult("SELECT answer FROM trivia where ID = 116")
a(1) = SQL1.ExecQuerySingleResult("SELECT wrong1 FROM trivia where ID = 116")
a(2) = SQL1.ExecQuerySingleResult("SELECT wrong2 FROM trivia where ID = 116")
a(3) = SQL1.ExecQuerySingleResult("SELECT wrong3 FROM trivia where ID = 116")
lblQuestion.Text = question
Dim i, k, x As Int

For i = 0 To 3
k = Rnd(i, 4)
x = a(i)
a(i) = a(k)
a(k) = x
Next 

answer1.text = "answer " & a(0) 'assuming answer1 etc are button names
answer2.Text = "answer " + a(1)
answer3.Text = "answer " + a(2)
answer4.Text = "answer " + a(3)
 
Upvote 0

anaylor01

Well-Known Member
Licensed User
Longtime User
I am getting duplicate triplicate answers and also an ArrayIndexOutOfBoundsException on line 128 but the line number changes on different runs.
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
Just spotted the out of bound problem:
B4X:
k = Rnd(i, 4)
should be
B4X:
k = Rnd(i, 3)
as you want an int between i and 3.
Cannot see why you are getting duplicates, post up your code.
 
Upvote 0

anaylor01

Well-Known Member
Licensed User
Longtime User
Dim question As String
Dim a(4) As String ' I assume the answers you retrieve are text

question = SQL1.ExecQuerySingleResult("SELECT question FROM trivia where ID = 116")
a(0) = SQL1.ExecQuerySingleResult("SELECT answer FROM trivia where ID = 116")
a(1) = SQL1.ExecQuerySingleResult("SELECT wrong1 FROM trivia where ID = 116")
a(2) = SQL1.ExecQuerySingleResult("SELECT wrong2 FROM trivia where ID = 116")
a(3) = SQL1.ExecQuerySingleResult("SELECT wrong3 FROM trivia where ID = 116")
lblQuestion.Text = question
Dim i, k, x As Int
Dim answers(4) As Int

For i = 0 To 3
answers(i) = i + 1
Next

For i = 0 To 3
k = Rnd(i, 3)

x = answers(i)
answers(i) = answers(k)
answers(k) = x
Log(a(answers(k)))
Next


answer1.Text = a(answers(0)) 'assuming answer1 etc are button names
answer2.Text = a(answers(1))
answer3.Text = a(answers(2))
answer4.Text = a(answers(3))
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
Now I am getting Line 117
K= Rnd(i,3)
java.lang.illegalargumentexception

Very sorry but I have led you astray.

I have just checked on Rnd and the second argument is exclusive, so it should be

B4X:
k = Rnd(i, 4)
You also need to change
B4X:
For i = 0 To 3
answers(i) = i + 1
Next
to
B4X:
For i = 0 To 3
answers(i) = i
Next
as it is being used as a pointer to the array (starts at zero, not 1)
 
Upvote 0

anaylor01

Well-Known Member
Licensed User
Longtime User
Another question. I have 73 questions. Generating the random number is not a problem. What I need to do is once that number is used it gets placed into an array or variable or something and the next time the button is clicked it generates a random number between 0 and 73 excluding the numbers that have already been generated. How would I do that?
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
Another question. I have 73 questions. Generating the random number is not a problem. What I need to do is once that number is used it gets placed into an array or variable or something and the next time the button is clicked it generates a random number between 0 and 73 excluding the numbers that have already been generated. How would I do that?

Create a list (lets call it UsedNumbers)
When you generate a number, check if the list size>0, and if it is then step through the list and see if the generated number is already there, if it is then go back to generating a number.
If it is not there then add it to the list and use it.

Something like this:
B4X:
   Dim NotUsed As Boolean 
   Dim i As Int
   NotUsed = False
   
   Do Until NotUsed = True
      NewNumber = Rnd (0, 74)
      NotUsed = True
      If UsedNumbers.Size > 0 Then
         For i = 0 To UsedNumbers.Size -1
            If UsedNumbers.Get (i) = NewNumber Then NotUsed = False
         Next
      End If
   Loop
   UsedNumbers.Add (NewNumber)
   ' you can now use NewNumber as it is unique
 
Last edited:
Upvote 0

agraham

Expert
Licensed User
Longtime User
For efficiency you should short-circuit the inner For loop with Exit after setting NotUsed to False but using a Map pre-initialized with the range of numbers as keys and True as the value for each will be a lot more efficient. For each generated random number check the value in the map with Map.Get(thisnumber) and if True use it and do Map.Put(thisnumber, False).

Generating the final few numbers may take a long time however you do the checking.
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
Even better, Dim the list, add numbers 0 through 74, shuffle and then use in that order.

You already have the shuffle code.
 
Upvote 0

anaylor01

Well-Known Member
Licensed User
Longtime User
Compiling code. Error
Error compiling program.
Error description: Object expected.
Occurred on line: 111
If UsedNumbers.Size > 0 Then
Word: size

Dim NotUsed As Boolean
Dim i As Int
Dim usednumbers As Int
Dim newnumber As Int
Dim a(3) As Int
NotUsed = False

Do Until NotUsed = True
NewNumber = Rnd (0, 74)
NotUsed = True
If UsedNumbers.Size > 0 Then
For i = 0 To UsedNumbers.Size -1
If UsedNumbers.Get (i) = NewNumber Then NotUsed = False
Next
End If

Loop
UsedNumbers.Add (NewNumber)

question = SQL1.ExecQuerySingleResult2("SELECT question FROM trivia where ID = ?", NewNumber)
a(0) = SQL1.ExecQuerySingleResult("SELECT answer FROM trivia where ID = 116")
a(1) = SQL1.ExecQuerySingleResult("SELECT wrong1 FROM trivia where ID = 116")
a(2) = SQL1.ExecQuerySingleResult("SELECT wrong2 FROM trivia where ID = 116")
a(3) = SQL1.ExecQuerySingleResult("SELECT wrong3 FROM trivia where ID = 116")
 
Upvote 0

kickaha

Well-Known Member
Licensed User
Longtime User
You are mixing up different variable types (Lists and arrays).

I have thought of a better solution: First you need to declare a list as a global, so add this to Sub Globals:
B4X:
   Dim NumberList As List

Now we need to populate the list with the numbers 1 to 73 when the program is started or reset. This code needs to be somewhere that is called when the quiz is started:
B4X:
   Dim i As Int
   NumberList.Clear          ' ensure the list is empty
   For i = 1 To 73
      NumberList.Add (i)      ' add the numbers to the list
   Next
Now to get an unused number we use:
B4X:
   Dim i As Int
   Dim NewNumber As Int

   i = Rnd (0, NumberList.Size )   ' random pointer to somewhere in the list
   NewNumber = NumberList.Get (i)   ' get the number
   NumberList.RemoveAt (i)         ' remove the number from the list
   ' you can now use NewNumber for your question. Note this does not check for the list being empty.
What this does is set the pointer to a random place on the list, get the number and then remove the number from the list.

The size of the NumberList is used to set the upper limit of Rnd so that we get a value from the list.

The advantages of this method are:

No shuffling needed.
The list does not need to be searched.
It returns a number first time, so will run quicker.
 
Last edited:
Upvote 0
Top