Crea un nuevo proyecto, añade un formulario con un solo botón y añade una clase llamada "cuestionario"...
No he tenido tiempo de probarlo (creando un fichero de ejemplo y tal) si te sale algún error trata de solucionarlo y si no puedes me comentas... ahora tengo que salir...
Este sería el código para la clase:
Public Class Cuestionario
Private p_Preguntas() As CuestionProfesor
Private p_NumPreguntas As Integer
Private p_Next As Integer
Private p_Aciertos As Integer
Private Rnd_M As Integer
Private Rnd_Q As Integer
Private Rnd_P As Integer
Public Enum RespuestasPosibles
RESPUESTA_A = 0
RESPUESTA_B = 1
RESPUESTA_C = 2
RESPUESTA_D = 3
End Enum
''' <summary>
''' La pregunta que rellena el profesor.
''' </summary>
''' <remarks>Al hacer el test, se envía al alumno, una copia de la pregunta y respuestas únicamente.</remarks>
Public Structure CuestionProfesor
Public Pregunta As String
Public Respuesta1 As String
Public Respuesta2 As String
Public Respuesta3 As String
Public Respuesta4 As String
Public Solucion As RespuestasPosibles '
Friend Acierto As Boolean ' para no hacer trampas...
''' <summary>
''' Convierte una pregunta del profesor en una del alumno.
''' </summary>
''' <returns>Devuelve una copia idéntica de la pregunta y las posibles respuestas.</returns>
''' <remarks></remarks>
Friend Function Convertir() As CuestionAlumno
With Convertir
.Pregunta = Me.Pregunta
.Respuesta1 = Me.Respuesta1
.Respuesta2 = Me.Respuesta2
.Respuesta3 = Me.Respuesta3
.Respuesta4 = Me.Respuesta4
End With
Return Convertir
End Function
End Structure
''' <summary>
''' La pregunta que se envía para ser respondida.
''' </summary>
''' <remarks>Es una copia de la "pregunta del profesor"</remarks>
Public Structure CuestionAlumno
Public Pregunta As String
Public Respuesta1 As String
Public Respuesta2 As String
Public Respuesta3 As String
Public Respuesta4 As String
Public Function Convertir(ByRef Texto As String) As RespuestasPosibles
Select Case Texto
Case "0" : Return RespuestasPosibles.RESPUESTA_A
Case "1" : Return RespuestasPosibles.RESPUESTA_B
Case "2" : Return RespuestasPosibles.RESPUESTA_C
Case "3" : Return RespuestasPosibles.RESPUESTA_D
Case Else : Return RespuestasPosibles.RESPUESTA_A
End Select
End Function
End Structure
''' <summary>
''' Envía al cliente la pregunta actual del test.
''' </summary>
''' <param name="Pregunta">Una estructura conteniendo la pregunta y las posibles respuestas.</param>
''' <param name="Respuesta">El índice de respuesta que dió el usuario del cliente.</param>
''' <remarks></remarks>
Public Event Test(ByRef Pregunta As CuestionAlumno, ByRef Respuesta As RespuestasPosibles)
''' <summary>
''' Solicita al cliente los datos de una cuestión.
''' </summary>
''' <param name="Pregunta">Pregunta que se formula</param>
''' <param name="Respuesta1">Posible respuesta -A-</param>
''' <param name="Respuesta2">Posible Respuesta -B-</param>
''' <param name="Respuesta3">Posible respuesta -C-</param>
''' <param name="Respuesta4">Posible respuesta -D-</param>
''' <param name="Solucion">Indice de la solución correcta a la pregunta entre las 4 (rango 0-3)</param>
''' <remarks></remarks>
Public Event EntrarPregunta(ByRef Pregunta As String, ByRef Respuesta1 As String, ByRef Respuesta2 As String, ByRef Respuesta3 As String, ByRef Respuesta4 As String, ByRef Solucion As RespuestasPosibles)
''' <summary>
''' Informa al cliente que se ya se han realizado todas las preguntas del cuestionario.
''' </summary>
''' <param name="Aciertos">Entrega el número de aciertos que se obtuvo.</param>
''' <param name="RepetirTest">Pregunta al cliente si se desea repetir (reiniciar) el mismo test</param>
''' <remarks></remarks>
Public Event FinDeCuestionario(ByVal Aciertos As Byte, ByRef RepetirTest As Boolean)
''' <summary>
''' Solicita el cliente si desea rellenar el cuestionario actual
''' </summary>
''' <param name="NumPreguntas">Un valor 0, expresa que no se desea realizar otro cuestionario.</param>
''' <remarks></remarks>
Public Event IntroducirNuevoCuestionario(ByRef NumPreguntas As Byte)
''' <summary>
''' Devuelve el número de preguntas que tiene el cuestionario actual.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property CantidadDePreguntas As Integer
Get
Return p_NumPreguntas
End Get
'Set(ByVal value As Byte)
' Call ComenzarTest(value)
'End Set
End Property
''' <summary>
''' Solicita la entrega de una nueva cuestión.
''' </summary>
''' <remarks>Si alcanza el final, devuelve un evento para solicitar si se desea barajar de nuevo el test actual.</remarks>
Public Sub Siguiente()
Dim r As RespuestasPosibles
Dim ca As CuestionAlumno
Dim np As Byte = 0
If (p_Next < p_NumPreguntas) Then
ca = p_Preguntas(p_Next).Convertir
RaiseEvent Test(ca, r)
If (p_Preguntas(p_Next).Solucion = r) Then
p_Preguntas(p_Next).Acierto = True
p_Aciertos += 1
End If
p_Next += 1
End If
If (p_Next = p_NumPreguntas) Then
Dim barajaOtraVez As Boolean = False
RaiseEvent FinDeCuestionario(p_Aciertos, barajaOtraVez)
If (barajaOtraVez = True) Then
Call Barajar()
ca = p_Preguntas(p_Next).Convertir
RaiseEvent Test(ca, r) ' aquí, p_next vale ahora mismo 0.
If (p_Preguntas(p_Next).Solucion = r) Then
p_Preguntas(p_Next).Acierto = True
p_Aciertos += 1
End If
Else
RaiseEvent IntroducirNuevoCuestionario(np)
If (np > 0) Then
Call ComenzarTest(np)
End If
End If
'Call MessageBox.Show("No existe la pregunta número: " & p_next.ToString & vbNewLine & "Realice una nueva petición, el número máximo de pregunta es: " & (p_NumPreguntas-1).ToString)
End If
End Sub
'Public Sub New()
' Me.New(6)
'End Sub
'Public Sub New(ByVal NumPreguntas As Byte)
' Call ComenzarTest(NumPreguntas)
'End Sub
''' <summary>
''' Solicita al cliente que introduzca los datos del nuevo cuestionario.
''' </summary>
''' <param name="NumPreguntas">Cantidad de preguntas que tendrá el cuestionario</param>
''' <remarks>Se invoca al crear la clase y cuendo se ha finalizado el cuestionario actual</remarks>
Public Sub ComenzarTest(ByVal NumPreguntas As Byte)
Dim k As Integer, q As CuestionProfesor
If (p_NumPreguntas = 0) Then
Rnd_M = 5 : Rnd_Q = 11 : Rnd_P = 767
Else ' evitamos números pares...
Rnd_M = ((Rnd_M Mod 81) + 2)
Rnd_Q = ((Rnd_Q Mod 353) + 4)
Rnd_P = ((Rnd_P Mod 2311) + 8)
End If
p_NumPreguntas = NumPreguntas
ReDim p_Preguntas(0 To p_NumPreguntas)
For k = 0 To NumPreguntas - 1
With q
.Pregunta = ""
.Respuesta1 = ""
.Respuesta2 = ""
.Respuesta3 = ""
.Respuesta4 = ""
.Solucion = 0
' ahora se ofrece la oportunidad de rellenar el cuestionario (que quizás esté guardado en un fichero)...
RaiseEvent EntrarPregunta(.Pregunta, .Respuesta1, .Respuesta2, .Respuesta3, .Respuesta4, .Solucion)
p_Preguntas(k) = q ' y se almacena...
End With
Next
Call Barajar()
End Sub
''' <summary>
''' Reinicia el cuestionario. Baraja los test del cuestionario actual y restablece a 0 el número de aciertos.
''' </summary>
''' <remarks></remarks>
Public Sub Reset()
Call Barajar()
End Sub
''' <summary>
''' Baraja las preguntas del test y se obtiene una lista en orden aleatorio.
''' </summary>
''' <remarks>El 'siguiente' se resetea al índice 0 del cuestionario.</remarks>
Private Sub Barajar() ' es mejor que sea un método privado, Y a su vez Se crea el método Reset, más explícito...
Dim k As Integer, az As Integer, tmp As CuestionProfesor
For k = p_NumPreguntas - 1 To 1 Step -1
az = (Random(az) Mod (k + 1)) ' proporciona un valor entre 0 y k.
tmp = p_Preguntas(az)
p_Preguntas(az) = p_Preguntas(k)
p_Preguntas(k) = tmp
Next
p_Next = 0
p_Aciertos = 0
End Sub
''' <summary>
''' Genera un número random pseudoaleatorio.
''' </summary>
''' <param name="n">Semilla desde la que generar el siguiente número</param>
''' <returns>Devuelve un entero entre 0 y (Rnd_P -1)</returns>
''' <remarks>Hay que ser cuidadoso al elegir el valor de los parámetros internos que utiliza la función. Si todos los parámetros son congruentes entre sí, podría devolver números predecibles, incluso siempre el mismo (típicamente el 0).</remarks>
''' Puede recurrirse al propio entorno NET para generar los números aleatorios.
Private Function Random(ByVal n As Integer) As Integer
Random = (((n * Rnd_M) + Rnd_Q) Mod Rnd_P)
End Function
End Class
Y éste el código para el formulario:
Public Class Form1
Private WithEvents Test As New Cuestionario
Private Fios As IO.StreamReader
' Realizar la siguiente pregunta
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Test.Siguiente()
End Sub
' Hacer la pregunta.
' La rpopia clase, verifica la respuesta y lleva la cuenta de aciertos y al final del test, notifica el resultado.
Private Sub Test_Test(ByRef Pregunta As Cuestionario.CuestionAlumno, ByRef Respuesta As Cuestionario.RespuestasPosibles) Handles Test.Test
With Pregunta
Dim R As String = InputBox(.Respuesta1 & vbNewLine & .Respuesta2 & vbNewLine & .Respuesta3 & vbNewLine & .Respuesta4 & vbNewLine & vbTab & "( ELIJA COMO RESPUESTA 0, 1,2 Ó 3)", .Pregunta)
Respuesta = Pregunta.Convertir(R)
End With
End Sub
' El cliente debe indicar las preguntas que tendrá el nuevo cuestionario.
' un valor de 0, indica que ya no se desea hacer más test.
' En el ejemplo, se lee la primera línea del fichero, que contiene el número de preguntas que contiene el cuestionario dentro del fichero.
' Uno debe proveer
Private Sub Test_IntroducirNuevoCuestionario(ByRef NumPreguntas As Byte) Handles Test.IntroducirNuevoCuestionario
Fios.Close()
Dim fin As Boolean = MessageBox.Show("¿Desea dar por finalizado la sesión de tests (S/N)?", "Nuevo cuestionario...", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If (fin = False) Then
Fios = New IO.StreamReader(Application.ExecutablePath & "\Cuestionario.txt")
NumPreguntas = Convert.ToByte(Fios.ReadLine) ' ojo: Cambiar al gusto y necesidad...
Else
NumPreguntas = 0
End If
End Sub
' Solicita al cliente cada test, uno a uno. debe introducirse los datos procedentes de donde sea...
' en este ejemplo se proveen de un simple fichero de texto, que está formateado así:
' NumPreguntas: ' tiene el número de preguntas que aloja el fichero, en el ejemplo no se verifica si esto es correcto.
' Pregunta X
' Respuesta A
' Respuesta B
' Respuesta C
' Respuesta D
' Solución A-D
' En el ejemplo provisto no se verifica que todo esté correcto, se da por hecho que es así.
Private Sub Test_EntrarPregunta(ByRef Pregunta As String, ByRef Respuesta1 As String, ByRef Respuesta2 As String, ByRef Respuesta3 As String, ByRef Respuesta4 As String, ByRef Solucion As Byte) Handles Test.EntrarPregunta
Pregunta = Fios.ReadLine
Respuesta1 = Fios.ReadLine
Respuesta2 = Fios.ReadLine
Respuesta3 = Fios.ReadLine
Respuesta4 = Fios.ReadLine
Solucion = Fios.ReadLine
End Sub
Private Sub Test_FinDeCuestionario(ByVal Aciertos As Byte, ByRef RepetirTest As Boolean) Handles Test.FinDeCuestionario
Dim numPreg As Integer = Test.CantidadDePreguntas
Dim btn As Windows.Forms.MessageBoxButtons = vbYesNo
Dim ico As Windows.Forms.MessageBoxIcon
' Seleccionamos un icono en función del número de aciertos podría personalizarse mejor con iconos específicos creados al efecto).
If (Aciertos < (numPreg / 2)) Then
ico = MessageBoxIcon.Warning
Else
ico = MessageBoxIcon.Information
End If
RepetirTest = MessageBox.Show("El número de aciertos fue: " & Aciertos.ToString & vbNewLine & "Sobre " & numPreg.ToString & " preguntas" & vbNewLine & vbNewLine & vbTab & "¿Desea repetir el mismo cuestionario (S/N)?", "Se ha finalizado el cuestionario.", btn, ico)
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Fios = New IO.StreamReader(Application.StartupPath & "\Cuestionario.txt")
Test.ComenzarTest(Convert.ToByte(Fios.ReadLine))
End Sub
Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
Fios.Close()
Fios = Nothing
End Sub
End Class
Para probarlo debes crear un fichero de texto llamado cuestionario.txt en la ruta del ejecutable (nota que si está en debug, la carpeta es distinta de si está compilado), con la siguiente estructura (OJO un dato por cada línea, y por línea se entiende Un SALTO de LINEA...
' NumPreguntas: ' tiene el número de preguntas que aloja el fichero, en el ejemplo
(por cada pregunta que indique numpreguntas debe haber esta estructura)
' Pregunta X el texto de la pregunta, por ejemplo: Test 01: De qué color es la hierba?
' Respuesta A el texto de una posible respuesta, por ejemplo: 0 - Azul
' Respuesta B por ejemplo 1 - Blanco
' Respuesta C por ejemplo 2 - verde
' Respuesta D por ejemplo 3 - Gris
' Solución 0-3 un valor numérico entre 0 y 3 , por ejemplo 2
---------------------------------------------------
p.d.: no se utiliza ningún formulario extra, todo la operación de mostrar los test recáe sobre un objeto inputbox.... que para el caso de ejemplo que se trata, es más que suficiente... ya tu tendrás que poner algo de tu parte y modificarlo a tus necesidades.
Lo que se hace aleatoriamente es reordenar las preguntas en el array y lueo se procede a mostrarlas en ese orden (desordenado, barajado), como cuando barajas un mazo de cartas y luego las muestras una a una...