Alters

A buscar M!nas: programación

Buenas!

Entramos en la recta final de la parte de VB.net; en esta entrada programaremos "a saco". Al final de ésta os dejaré un link para que podáis bajaros el proyecto y probar.

Os dejaré el código sin más. Este código es la "traducción" de los algoritmos previamente explicados con pseudo-código (hay un par de modificaciones, pero son poca cosa).

Dividiré los bloques por archivos, para que se lea un poco mejor.

BuscaminaX.vb


Public Class BuscaminaX
    Public time As Integer

    Private Sub BuscaminaX_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Diff.ShowDialog()

        If Global_Var.getGen.Equals(0) Then
            Me.Close()
        Else
            BX_module.Cargar()
            BX_module.Dibujar()
            Me.Timer1.Enabled = True
        End If
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Application.DoEvents()

        Me.time += 1

        Me.ToolStripStatusLabel1.Text = "Time: " + Me.time.ToString()
        Me.ToolStripStatusLabel2.Text = "Mines: " + BX_module.minas.ToString()
    End Sub
End Class


Diff.vb

Public NotInheritable Class Diff
    Private Sub Diff_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.Button1.Enabled = False
    End Sub

    Private Sub RadioButton1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged, _
                 RadioButton3.CheckedChanged, RadioButton4.CheckedChanged, _
                 RadioButton5.CheckedChanged
        Dim en As Boolean = False

        If (Me.RadioButton1.Checked Or Me.RadioButton2.Checked) And _
        (Me.RadioButton3.Checked Or Me.RadioButton4.Checked Or Me.RadioButton5.Checked) Then
            en = True
        End If

        Me.Button1.Enabled = en
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim gen As Integer = 1
        Dim dif As Integer = 0

        If Me.RadioButton2.Checked Then
            gen = 2
        End If

        If Me.RadioButton4.Checked Then
            dif = 1
        ElseIf Me.RadioButton5.Checked Then
            dif = 2
        End If

        Global_Var.setDif(dif)
        Global_Var.setGen(gen)

        Me.Close()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Me.Close()
    End Sub
End Class



Global_var.vb

Module Global_Var
    Private dif As Integer
    Private gen As Integer
    Private min As Integer
    Private anc As Integer
    Private alt As Integer
    Private img As Integer
    Private anB As Double
    Private alB As Double
    Private fil As Integer
    Private col As Integer
    Private pos(99) As Integer
    Private coM() As Integer
    Private imF As Image

    Public Sub setDif(ByVal pDif As Integer)
        dif = pDif
    End Sub

    Public Sub setGen(ByVal pGen As Integer)
        gen = pGen
    End Sub

    Public Sub setMin(ByVal pMin As Integer)
        min = pMin
    End Sub

    Public Sub setAnc(ByVal pAnc As Integer)
        anc = pAnc
    End Sub

    Public Sub setAlt(ByVal pAlt As Integer)
        alt = pAlt
    End Sub

    Public Sub setImg(ByVal pImg As Integer)
        img = pImg
    End Sub

    Public Sub setAnB(ByVal pAnB As Double)
        anB = pAnB
    End Sub

    Public Sub setAlB(ByVal pAlB As Double)
        alB = pAlB
    End Sub

    Public Sub setFil(ByVal pFil As Integer)
        fil = pFil
    End Sub

    Public Sub setCol(ByVal pCol As Integer)
        col = pCol
    End Sub

    Public Sub setPos(ByVal pPos() As Integer)
        pos = pPos
    End Sub

    Public Sub setCoM(ByVal pCoM() As Integer)
        coM = pCoM
    End Sub

    Public Sub setImF(ByVal pImF As Image)
        imF = pImF
    End Sub

    Public Function getDif() As Integer
        Return dif
    End Function

    Public Function getGen() As Integer
        Return gen
    End Function

    Public Function getMin() As Integer
        Return min
    End Function

    Public Function getAnc() As Integer
        Return anc
    End Function

    Public Function getAlt() As Integer
        Return alt
    End Function

    Public Function getImg() As Integer
        Return img
    End Function

    Public Function getAnB() As Double
        Return anB
    End Function

    Public Function getAlB() As Double
        Return alB
    End Function

    Public Function getFil() As Integer
        Return fil
    End Function

    Public Function getCol() As Integer
        Return col
    End Function

    Public Function getPos() As Integer()
        Return pos
    End Function

    Public Function getCoM() As Integer()
        Return coM
    End Function

    Public Function getImF() As Image
        Return imF
    End Function
End Module


BX_module.vb

Imports System.IO

Module BX_module
    Public minas As Integer
    Private cuadros As Integer

    Public Sub Cargar()
        Dim min As Integer = 10
        Dim fil As Integer = 9
        Dim col As Integer = 9
        Dim anc As Integer
        Dim alt As Integer
        Dim img As Integer
        Dim anB As Double
        Dim alB As Double
        Dim pos(99) As Integer
        Dim res() As Integer
        Dim imF As Image
        Dim Random As New Random()

        img = Random.Next(0, 12)

        If Global_Var.getGen = 2 Then
            img += 12
        End If

        imF = Image.FromFile(Application.StartupPath.ToString + "\BX_IMG\BX_" + img.ToString + ".jpg")

        anc = imF.Width
        alt = imF.Height

        If Global_Var.getDif = 1 Then
            min = 40
            fil = 16
            col = 16
        ElseIf Global_Var.getDif = 2 Then
            min = 99
            fil = 16
            col = 30
        End If

        anB = anc / col
        alB = alt / fil
        pos = LlenarMinas(fil * col, min)
        res = ComprobarMinas(fil * col, col, pos, min)
        minas = min
        cuadros = (fil * col) - min

        Global_Var.setMin(min)
        Global_Var.setFil(fil)
        Global_Var.setCol(col)
        Global_Var.setAnc(anc)
        Global_Var.setAlt(alt)
        Global_Var.setImg(img)
        Global_Var.setAnB(anB)
        Global_Var.setAlB(alB)
        Global_Var.setPos(pos)
        Global_Var.setImF(imF)
        Global_Var.setCoM(res)
    End Sub

    Private Function LlenarMinas(ByVal max As Integer, ByVal min As Integer) As Integer()
        Dim pos(99) As Integer
        Dim num As Integer
        Dim pass As Boolean
        Dim Random As New Random()

        For i = 0 To min - 1
            pass = True
            num = Random.Next(0, max + 1)

            For j = 0 To pos.Length - 1
                If pos(j) = num Then
                    pass = False
                End If
            Next

            If pass Then
                pos(i) = num
            Else
                i -= 1
            End If
        Next

        Return pos
    End Function

    Private Function ComprobarMinas(ByVal max As Integer, ByVal col As Integer, ByVal pos() As Integer, ByVal min As Integer) As Integer()
        Dim ret(max) As Integer

        For i = 0 To max - 1
            Dim num As Integer

            For j = 0 To min - 1
                If i Mod col = 0 Then
                    If pos(j) = i - col Or pos(j) = i - (col - 1) Or _
                       pos(j) = i + 1 Or _
                       pos(j) = i + col Or pos(j) = i + (col + 1) Then
                        num += 1
                    ElseIf pos(j) = i Then
                        num = 10
                        Exit For
                    End If
                ElseIf i Mod col = col - 1 Then
                    If pos(j) = i - (col + 1) Or pos(j) = i - col Or _
                       pos(j) = i - 1 Or _
                       pos(j) = i + (col - 1) Or pos(j) = i + col Then
                        num += 1
                    ElseIf pos(j) = i Then
                        num = 10
                        Exit For
                    End If
                Else
                    If pos(j) = i - (col + 1) Or pos(j) = i - col Or pos(j) = i - (col - 1) Or _
                       pos(j) = i - 1 Or pos(j) = i + 1 Or _
                       pos(j) = i + (col - 1) Or pos(j) = i + col Or pos(j) = i + (col + 1) Then
                        num += 1
                    ElseIf pos(j) = i Then
                        num = 10
                        Exit For
                    End If
                End If
            Next

            ret(i) = num
            num = 0
        Next

        Return ret
    End Function

    Public Sub Dibujar()
        Dim max As Integer = Global_Var.getFil() * Global_Var.getCol()
        Dim t As Double = 0
        Dim l As Double = 0
        Dim h As Double = Global_Var.getAlB
        Dim w As Double = Global_Var.getAnB
        Dim c As Integer = Global_Var.getCol
        Dim f As Integer = Global_Var.getFil
        Dim o As Label
        Dim p As Button
        Dim con(max * 2) As Control

        For i = 0 To max - 1
            o = New Label()
            p = New Button()

            o.Top = t
            o.Left = l
            o.Height = h
            o.Width = w
            o.Name = "L" + i.ToString
            o.Visible = True
            o.BackColor = Color.Transparent
            o.Font = New Font("Times new Roman", 10, FontStyle.Regular, GraphicsUnit.Pixel)

            p.Top = t
            p.Left = l
            p.Height = h
            p.Width = w
            p.Name = "B" + i.ToString
            p.Visible = True

            AddHandler p.MouseDown, AddressOf p_click
            AddHandler o.MouseEnter, AddressOf o_mouseEnter
            AddHandler o.MouseLeave, AddressOf o_mouseLeave

            con(i) = p
            con(i + max) = o

            If i Mod c = c - 1 Then
                l = 0
                t += h
            Else
                l += w
            End If
        Next

        BuscaminaX.Controls.AddRange(con)

        BuscaminaX.Height = Global_Var.getAlt() + 57
        BuscaminaX.Width = Global_Var.getAnc() + 13
        BuscaminaX.MaximumSize = BuscaminaX.Size
        BuscaminaX.MinimumSize = BuscaminaX.Size
        BuscaminaX.BackgroundImage = Global_Var.getImF()
        BuscaminaX.ToolStripStatusLabel1.Text = Global_Var.getMin.ToString
    End Sub

    Private Sub p_click(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            cuadros -= 1

            If cuadros = 0 Then
                MsgBox("Has ganado")
                BuscaminaX.Controls.Clear()
            End If

            sender.Visible = False

            If Global_Var.getCoM()(sender.name.SubString(1)) = 10 Then
                MsgBox("PUM!")
                Fail.ShowDialog()
            End If
        ElseIf e.Button = MouseButtons.Right Then
            If sender.text.Equals("X") Then
                sender.text = ""
                sender.backColor = Color.WhiteSmoke
                minas += 1
            Else
                sender.Text = "X"
                sender.backColor = Color.Red
                minas -= 1
            End If

            BuscaminaX.ToolStripStatusLabel1.Text = "Time: " + BuscaminaX.time.ToString()
            BuscaminaX.ToolStripStatusLabel2.Text = "Mines: " + minas.ToString()
        End If
    End Sub

    Private Sub o_mouseEnter(ByVal sender As Object, ByVal e As System.EventArgs)
        sender.BackColor = Color.White
        sender.text = Global_Var.getCoM()(sender.name.SubString(1))
    End Sub

    Private Sub o_mouseLeave(ByVal sender As Object, ByVal e As System.EventArgs)
        sender.text = ""
        sender.backcolor = Color.Transparent
    End Sub
End Module

Con esto queda plasmado todo el código respectivo al proyecto de VB.net

Os dejo un link de descarga para el proyecto completo. Recordad leer el "LEEME.TXT", y si compartís el paquete recordad ser buenos y citar el post original (este) y/o su autor orignial (yo).

http://www.mediafire.com/?8y7d8old8blrxh2

La próxima serie de entradas se basarán en una serie de ampliaciones del proyecto, usando MSAccess (por ahora...).

Saludos, y

¡Hasta la próxima!

A buscar M!nas: Conceptos previos

Buenas otra vez!

Esta es la primera entrada con respecto a la programación. Con este punto daremos por finalizado el tema de construir el juego usando VB.net.

Creo que me voy a arriesgar y haré el punto de conceptos previos como una sola entrada...

  • Conceptos previos
    • Estructuración de código
    • "Multi-handler"
    • Controles dinámicos (en tiempo de ejecución)
Estructuración de código

A la hora de programar con orientación a sucesos u orientación a objetos, es muy común la segregación de código, es decir, "aislar" el código por zonas o por utilidades.

Yo soy partidario de estas separaciones, de manera que, en VB.net me gusta tener separado el código propio de las llamadas a eventos de la función del evento. Para ello uso módulos, que son archivos planos destinados a estas mismas funciones.

En dichos módulos se "apilan" las funciones, como (por ejemplo) se pueden "apilar" en un include de PHP.

El comportamiento de los módulos es sencillo: llamas a una función o sub, se ejecuta, y retorna el control. También se pueden comportar como objetos, si así lo deseamos. ¿Cómo? Simplemente creando, en vez de un módulo, una clase. El tipo de archivo es el mismo (*.vb), pero cambian los encabezados del archivo.

Realmente no veo mucha diferencia entre un objeto simple (sin herencia ni nada similar) con un módulo bien definido, por lo que generalmente (siempre que no tenga que usar herencia o cosas más típicas de objetos) uso siempre módulos con estructura de objetos, es decir:

Module mi_modulo
    private var1 as String
    private var2 as Integer
    public var3 as Object

    public sub setVar1(byVal str as String)
        var1 = str
    End Sub

    public function getVar1() as String
        return var1
    End Function
End Module

Parece un objeto en su definición, pero es un simple módulo. A efectos prácticos básicos nos sirve igual.

Otra división que suelo hacer es la siguiente: en un módulo incluyo todas las variables "globales" que va a usar el programa, y en otro todas las funciones "globales".

En el primer módulo hago todas las variables privadas, y les creo "getters" y "setters", tal como sería en una OOP. El segundo módulo tiene partes privadas y partes públicas, por el tema del encapsulamiento.

Para dejar el asunto como zanjado, en este proyecto usaré la jerarquía que muestra la imágen "img1"

img1
Adicionalmente, para que todo quede completo, debéis dirigiros al directorio de la aplicación (BuscaminaX/BuscaminaX/bin/debug/) y crear el directorio "BX_IMG". Ahí tenéis que subir 25 imágenes, a saber:

 - 12 imágenes para la primera opción (a)
 - 12 imágenes para la segunda opción (b)
 - 1 imágen para el fallo (c)

La nomenclatura que yo uso es:

(a) BX_0 hasta BX_11
(b) BX_12 hasta BX_23
(c) Fail

En el SplashScreen, como fondo va la imágen "Fail". Esto creará un link en el explorador de soluciones (img1), en la carpeta "bin > debug> BX_IMG > Fail".

Multi-handler

El tema de los multi-handler consiste en asociar más de un evento a un mismo sub. En JAVA, usando AWT sería como asignar varios controles a un Listener.

En VB.net podemos hacerlo de una manera sencilla. Para ver cómo se consigue lo haremos mediante un ejemplo:

Supongamos que tenemos el siguiente evento genérico:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    'Cosas que hacer
End Sub

Revisemos la sintaxis:


  • Private Sub: crea un Sub, con visibilidad privada
  • Button1_Click: nombre genérico para el sub. Éste se crea mediante la fórmula nombreObjeto_Evento
  • ByVal sender As System.Object: primer parámetro llamado "sender", pasado por valor, como un Objeto
  • ByVal e as System.EventArgs: segundo parámetro llamado "e", pasado por valor, como un objeto
  • Handles Button1.Click: ¡OJO!, aquí está el meollo del asunto. Esta cláusula "junta" el evento Click de Button1 con este sub.
Ahora imaginad que tenemos un Button2, y queremos que se incluya en este sub. Sería tan sencillo como añadir "Button2.Click" al final de este sub.

Podemos añadir cualquier evento a un mismo Handles, siempre separados por comas. Pero, ¿De qué puede servir?

Bien, imaginad 10 buttons, llamados b0  a b9, respectivamente. Podríamos poner los diez Buttons en el mismo handler, y hacer algo como:

Me.TextBox1.Text += sender.substring(1)

Y automáticamente se va el número al TextBox. El otro aspecto del multi-handler es el parámetro "e".

"e" es un parámetro al que podemos acceder para saber la acción que se ha realizado sobre el objeto "sender". Un ejemplo es (sacado del juego):

If e.Button = MouseButtons.Left Then

Esta línea sirve para saber si es un "click" derecho o izquierdo.

Con esto queda por concluido el tema de multi-handler

Controles dinámicos (en tiempo de ejecución)

Crear controles en tiempo de ejecución es un alivio para todas esas circunstancias en las que no sabemos qué tendremos que generar (como en nuestro juego, ya que los controles varían dependiendo de la dificultad).

Crear un control no es demasiado difícil. El tema está en saber tratarlo, adecuarlo e incrustarlo en el panel que toca.

 o = New Label()

 o.Top = t
 o.Left = l
 o.Height = h
 o.Width = w
 o.Name = "L" + i.ToString
 o.Visible = True
 o.BackColor = Color.Transparent
 o.Font = New Font("Times new Roman", 10, FontStyle.Regular, GraphicsUnit.Pixel)

Esta parte debería sonar de la entrada anterior... Comentemos:

La Label se crea en la primera línea, llamando a su constructor. Adicionalmente se le cambian atributos, como el nombre, el ancho, el largo...

Hay un par de sentencias que son prácticamente obligatorias, que son el Visible = True, el Top y el Left.

No obstante, con estas líneas no se agrega al contenedor. Para ello podemos usar algo como

MiForm.Controls.Add(Control)

o

MiForm.Controls.AddRange(Array(Control))

Con esto estamos añadiendo el control (o controles) al formulario especificado.

Y, con este último apartado ha concluido el tema de los conceptos previos.

En la próxima entrega programaremos y os dejaré un link de descarga.

Hasta la próxima!

A buscar M!nas: Diseñando las pantallas del juego

Buenas de nuevo!

Ésta es la segunda entrega de diseño, y también la última. Hoy veremos qué tenemos que insertar en nuestro proyecto de VB.net, y en las próximas entradas nos pondremos a acabar nuestro juego.

Cuando acabe la parte de VB.net, empezaremos con una pequeña parte de "bases de datos" Access (ya que para cosas sencillas nos va bien). Luego ya veréis lo que hacemos ;-)

Bueno, empecemos!

Primero abrimos nuestro Visual Studio (yo uso el 9, supongo que si tenéis otra versión no pasara nada, porque no usaremos controles "raros" ni nada...) y creamos un proyecto nuevo. Yo lo llamé "BuscaminaX". Vosotros le podéis poner el nombre que queráis...

Ahora tendremos nuestro Form1 abierto. Le añadimos un StatusStrip y un Timer. Debería quedar algo así (img1).

img1
El siguiente paso es configurar el Form. Recomiendo que hagáis los siguientes cambios:

  • Name: BuscaminaX
  • Locked: true
  • MaximizeBox: false
  • Text: BuscaminaX
Ahora ya tenemos configurada la ventana principal.

Toca ahora crear los pop-up. A mí me gusta hacerlo con las llamadas splashScreen. Para hacer una solo tenemos que ir al menú de "Explorador de soluciones", pinchar en el nombre del proyecto, "Agregar" > "Elemento existente" > "Pantalla de presentación".

Le ponemos el nombre de diff (diminutivo de "Dificultad"), y aceptamos. Nos saldrá en el editor una pantalla con un diseño por defecto, tal como la imagen muestra (img2). Seleccionamos los elementos y los borramos (img3). Después 
hacemos doble click sobre el panel, y accedemos a la vista de código.
Como se puede ver (img4), tiene algún código por defecto; pero al haber borrado los controles estará plagado de errores. Lo borramos y punto (img5).

img2
Ahora que ya tenemos nuestro panel de dificultad preparado, le añadimos los controles necesarios:

   - 2 GroupBox
   - 5 RadioButton
   - 2 Button

Tal como os comenté, mi juego era "temático", por eso hay 5 RadioButton (3 para la dificultad, 2 para las opciones).

Insertamos los controles de manera que quede bien distribuido. La imágen "img6" muestra la distribución que le dí yo.


img3
Ahora vamos al siguiente panel: Otra SplashScreen, para cuando el jugador falle. Borramos el contenido y el código, y hacemos algo gracioso; dejad volar vuestra imaginación.

Yo puse una imágen un tanto desagradable y bien grande...

El tema está en que esta pantalla se mostrará y no se podrá cerrar, y evitará que el juego se pueda cerrar (solo se podrá hacer abriendo el administrador de tareas).


Ya tenemos todo el tema del diseño listo. Sólo nos queda programar y en unos pocos ratos tendremos diversión asegurada.
img4

img5
Con esto terminamos la sección de diseño. Las próximas entregas tratarán sobre programación. Todavía no se si haré la primera parte en tres secciones o en una entrada muy muy larga (tampoco se con cuanta profundidad entraré en los conceptos...) De todas maneras, falta bien poco para tener nuestro propio juego de M!nas, jejeje.

En fin, sed buenos, y ¡felicidades a todos los programadores!; hoy en España es Santa Tecla, que (almenos para mi) es el patrón de los programadores (chiste muy muy malo, jejeje).







img6












Hasta la próxima!

A buscar M!nas: diseño de algoritmos

Buenas!

Hoy empezamos con el diseño; esta sección durará dos entradas: una dedicada al diseño de algoritmos, y otra dedicada al diseño de pantallas.

En la entrada anterior explicamos el comportamiento básico de nuestro juego de las minas. En esta profundizaremos en ello hasta conseguir buenos algoritmos en pseudo-código.

El modus operandi que uso para generar algoritmos se basa en funciones de profundidad. Es decir: creo una base muy simple (dos o tres instrucciones), y después para cada instrucción voy ampliando el nivel de profundidad del algoritmo. Con esta técnica se consiguen algoritmos compactos y muy modulares.

Empezaremos con la parte del backend, es decir, dibujar minas, colocarlas...:

[Carga la pantalla]

 muestra(pantalla_inicial)

 si gen = 0 cierra
 sino
   carga()
   dibuja()
  fin

Tenemos el aspecto general de carga terminado. Una anotación: los token del pseudo-código los he puesto en rojo y negrita; las variables en azul y las funciones propias en verde.

Sigamos ahora con el algoritmo de la función carga() (no os preocupéis si veis variables "inconexas"; luego lo juntamos todo).

[Función carga]

carga(){
    min = 10
    fil = 9
    col = 9
    img =Random(min, max)

    si gen = 2 img += max

    imF = Imagen(concat(ruta, string(img), '.jpg'))
    anc = width(imF)
    alt = height(imF)

    si dif = 1
        min = 40
        fil = 16
        col = 16
    sino si dif = 2
        min = 99
        fil = 16
        col = 30
    fin

    anB = anc / col
    alB = alt / fil
    pos = LlenarMinas(fil*col, min)
    res = ComprobarMinas(fil*col, col, pos, min)
    minas = min
    cuadros = (fil * col) - min
}

Revisemos rápidamente como funciona esta función:

Primero asumimos los valores para la dificultad fácil (tres primeras líneas). Acto seguido creamos un aleatorio para la imágen de fondo. Si la opción es 2, le sumamos max (esto es porque hay los mismos fondos de la primera opción como de la segunda; los fondos están numerados. Si es la segunda opción, el número más bajo será max, o lo que es lo mismo, el primer fondo de la opción 2).

Lo siguiente es crear la imágen en imF, y capturar su ancho y largo.

Seguidamente consultamos la dificultad. Si no está en fácil variamos los datos pertinentes.
Finalmente, calculamos el ancho y el largo de los controles (anB y alB), llenamos las minas y las comprobamos.

Las funciones LlenarMinas() y ComprobarMinas() las veremos después de la función dibuja(), la cual veremos a continuación:

[Función dibuja]

dibuja(){
    max = fil * col
    h = alB
    w = anB
    c = col
    f = fil
   con = array(max*2)

    de 0 hasta max-1 usando i
        o = label()
        p = button()

        top(o) = t
        left(o) = l
        width(o) = w
        height(o) = h
        name(o) = concat('L', string(i))
        visible(o) = 1
        backColor(o) = color('transparent')
        font(o) = font('Times new Roman', 10, 'regular', 'pixel')
        top(p) = t
        left(p) = l
        width(p) = w
        height(p) = h
        name(p) = concat('B', string(i))
        visible(p) = 1

        Evento(p.MouseDown)
        Evento(o.MouseEnter)
        Evento(o.MouseLeave)

        con(i) = p
        con(i+max) = o

        si i%c = c-1
            l = 0
            t += h
        sino
            l += w
         fin
    sigue

    add(ventana, con)
    height(ventana) = alt + 57
    width(ventana) = anc + 13
    maxSize(ventana) = size(ventana)
    minSize(ventana) = size(ventana)
    backgroundImage(ventana) = imF
    text(strip1) = string(min)
}

Si en la anterior función predominaban la asignación de variables, en esta predominan las asignaciones a controles. Explico rápidamente el algoritmo:

Se reocgen datos (ya se que parece que sean inútiles las asignaciones del principio).

Después se entra en un bucle, en el que vamos definiendo, en cada vuelta, un button y un label. Les damos altura, anchura, color, posición... y los agregamos al array, de manera que quedan agrupados todos los button con los button y las label con las label.

El siguiente paso es calcular la siguiente posición, y volver a empezar.

Cuando termina el bucle, incluimos todos los controles en la ventana y le fijamos un ancho y un alto. Después hacemos que no se pueda modificar el tamaño, y ponemos el número de minas abajo (en un strip).

El siguiente paso es definir la función LlenarMinas():

[Función LlenarMinas]

LlenarMinas(){
    de 0 hasta min-1 usando i
        pass = 1
        num = Random(0, max+1)

        de 0 hasta length(pos)-1 usando j
            si pos(j) = num pass = 0
        sigue

            si pass pos(i) = num
            sino -= 1
    sigue
}

Este algoritmo es corto y conciso: hace "min-1" iteraciones, en cada una de la cual busca un aleatorio que no esté en "pos". En caso de estar, retrocedemos en una unidad el contador, para volver a la misma posición.

 Para el backend solo falta definir el algoritmo ComprobarMinas(). Pero, además, faltan los eventos que definimos en dibuja(). Éstos entrarán en juego cuando el usuario pulse un button, entre en una label o salga de ella.

Por ahora, vamos a por ComprobarMinas():

[Función ComprobarMinas]

ComprobarMinas(){
    de 0 hasta max-1 usando i
        de 0 hasta min-1 usando j
            si i%col = 0
                si (pos(j) = i - col o pos(j) = i - (col - 1) o
                    pos(j) = i + 1 o pos(j) = i + col o pos(j) = i + (col + 1))
                    num++
                sino
                    num = 10
                    break
                fin
            sino si i%col = col - 1
                si (pos(j) = i - (col + 1) o pos(j) = i - col o
                    pos(j) = i - 1 o pos(j) = i + (col - 1) o pos(j) = i + col)
                    num++
                sino
                    num = 10
                    break
                fin
            sino
                si (pos(j) = i - (col + 1) o pos(j) = i - col o pos(j) = i - (col - 1) o
                    pos(j) = i - 1 o pos(j) = i + 1 o   pos(j) = i + (col - 1) o
                    pos(j) = i + col o pos(j) = i + (col + 1))
                    num++
                sino
                    num = 10
                    break
                fin
            fin
        sigue

        ret(i) = num
        num = 0
    sigue
}

Esta función parece complicada, pero es bastante sencilla: recorremos el array "pos", y por cada posición buscamos dentro de él los lugares colindantes (como si fuera un array de dos dimensiones pero en uno). El complejo de condiciones simplemente sirven para delimitar los "bordes" de la martiz.

Entonces, si encuentra una coincidencia, incrementa "num". En caso contrario deja "num" con un valor de "10" (ya veremos para qué sirve esto).

NOTA: Sé que no es precisamente óptimo, pero esto está hecho de hace un año, y, sinceramente, como funciona no me atrevo a tocarlo...

Finalmente nos faltan los pseudo-códigos de los eventos (tranquilos, ya acabo :-P). Empezamos por los eventos de las label (más cortos):

[Label::mouseEnter]

backColor(s) = color('white')
text(s) = coM(substring(name(s), 1))

Simplemente le ponemos un fondo de color blanco y un texto con las minas que tiene alrededor.

Ahora el otro evento de la Label:

[Label::mouseLeave]

text(s) = ''
backColor(s) = color('transparent')

Aquí volvemos a dejar la label en el mismo estado que cuando la definimos.

Por último, el evento del button:

[Button::mouseDown]

si button(e) = mouseButtons(left)
    cuadros--

    si cuadros = 0
        msg('Has ganado')
        clear(ventana, controls)
    fin

    visible(s) = 0

    si coM(substring(name(s), 1)) = 10
        msg('PUM!')
        muestra(fail)
    fin
sino si button(e) = mousebuttons(right)
    si text(s) = 'X'
        text(s) = ''
        backColor(s) = Color('white')
        minas++
    sino
        text(s) = 'X'
        backColor(s) = Color('red')
        minas--
    fin

    text(strip) = string(minas)
fin

Como escribí en la anterior entrada; comprueba el click hecho (izquierdo o derecho). Si es derecho, permuta entre un texto "X" y un color, indicando que está marcado o no como mina. También modifica el texto del strip (que lleva la cuenta de las minas que nos faltan por marcar).

En cambio, si el click es izquierdo, destapa el cuadro, y busca el valor de la label. OJO he marcado el "10" en negrita para que lo veáis; es el misterioso "10" de CompruebaMinas(). ¿Cómo funciona? Sencillo:

Si os fijáis los buttons y los labels tienen nombres del estilo "B1", "L25". En el evento se extrae el número, y se coteja con el array (por eso es necesariamente unidimensional) y recoge el número del array que CompruebaMinas() introjudo (que es el valor de las minas que tiene alrededor; 10 si es mina). ¡Volia!

Ahora, por fin tenemos todos los algoritmos necesarios. Solo nos falta diseñar las pantallas. Esto nos llevará más bien poco, ya que la mitad del trabajo de diseño está aquí incluido (función dibuja()).

Os dejo, como siempre,
Hasta la próxima!

A buscar M!nas: análisis teórico del modo de juego


Hola de nuevo!

Hoy trataremos de ver qué aspectos son necesarios para hacer un juego de buscar minas.

Este paso servirá para que, a la hora de diseñar y codificar, podamos liarnos menos con lo que hacemos.

Para empezar, veamos la interfaz que usa windows (img1)

img1
He marcado algunas áreas de color rojo. Estas son:

 - Título e icono (arriba izquierda)
 - Menú (debajo del título y el icono)
 - Tiempo (abajo izquierda)
 - Contador (abajo derecha)

Aparte de estos elementos, tenemos el campo de juego, hecho con botones y etiquetas.

Es por la gran cantidad de elementos que hay que decidí hacer el proyecto con VB.net; posee una buena interfaz gráfica para diseñar en un "pis-pas". También se que VB.net no es uno de los lenguajes de programación más óptimo, pero es de desarrollo rápdio; y para un juego de minas poco hace falta...

Sigamos con el análisis: ¿Qué tenemos que tener en cuenta? Os dejo una pequeña lista preliminar de los elementos que tendremos que tener en cuenta:

 - Dificultad
 - Minas marcadas
 - Tiempo
 - Posición minas

Por ahora parece todo correcto, ¿no?; necesitamos como mínimos estos datos para construir mentalmente nuestro juego.

Bien, yo puse una pequeña contribución, y, para que fuese más interesante, puse un fondo aleatorio. Este fondo aleatorio varía en función de una opción añadida.

Seré sincero: los fondos son hentai o yaoi, dependiendo de la opción marcada. No obstante, cambiaré los fondos y las opciones a la hora de subir el proyecto; tampoco publicaré los fondos... (para eso tenéis "Ctrl+shift+N" ;-) )

Siguiendo con los cambios que hice, la elección de dificultad la extraje del menú y la puse en un pop-up; con esto me ahorro tener que hacer un menú, dejando más espacio para las minas (y el fondo).

De momento tampoco hay tiempo, ya que el tema de los "timers" con VB.net va algo raro... no obstante se puede incluír sencillamente (es más, en cuanto os vaya mostrando el proyecto, pondré el tiempo).

Finalmente, añadí un pop-up que sale cuando pisas una mina. Este pop-up no se puede cerrar e impide cerrar la ventana (está hecho así a propósito). Así que tocará ir ad task manager y cerrar el proceso...

Ahora que está todo especificado, revisemos lo que nos hace falta para el proyecto:

 - Botones
 - Etiquetas
 - Timer
 - Pop-up
 - Imágenes

El tema de las imágenes es un tanto peliagudo; como queremos un buen "background", modificaremos el tamaño de la ventana acorde el tamaño del "background". Esto a su vez impica que los controles variarán de tamaño.

Además, dependiendo del nivel de dificultad, la cantidad de cuadros variará, por lo que variará también el tamaño de los mismos.

Es por eso que tenemos que guardar también el ancho y el largo de los controles.

Ahora ya tenemos todo listo para empezar con lo duro: el análisis real.

Dejo un esquema de lo que hace a grandes rasgos el juego

  • Pide dificultad y opción
    • Dependiendo de la opción carga una imágen
    • Dependiendo de la dificultad cambia:
      • num. minas
      • num. ancho
      • num. alto
    • Dependiendo de la dificultad y la imagen cambia:
      • alto cuadro
      • ancho cuadro
      • alto etiqueta
      • ancho etiqueta
  • Generamos minas
  • Generamos botones
  • Generamos etiquetas
  • Generamos eventos para botón

El evento principal es el click del ratón. Cuando este suceda, nuestro programa hará lo siguente:

  • Si es click izquierdo
    • Vaciamos botón
    • Si es mina mostramos el fallo
    • Si no es mina y están todas las minas marcadas ganamos
  • Si es click derecho
    • Si el botón está marcado como mina lo desmarcamos y descontamos una mina
    • Sino, lo marcamos y añadimos una mina

Con esto tenemos suficiente para desarrollar unos buenos algoritmos. Pero eso será en la próxima entrada.

Nos vemos,
Hasta la próxima!

VB.net: A buscar M!nas

Buenas a todos!

Después de un par de series de entradas, me he decidido por crear una serie que espero sea extensa.

En ella trataremos varios aspectos de programación; analizaremos, diseñaremos y crearemos un programa completo. Más tarde tengo pensado continuar con este "proyecto" y hacer algo en web, como por ejemplo una pequeña página dinámica que use una base de datos (que incorporaremos al proyecto progresivamente) para hacer estadísticas, competiciones...

Por ahora centrémonos en el primer paso: el juego de las M!nas (ojo, **ironía** no tiene nada que ver con cualquier juego que tengáis en vuestro PC).

Además, me he decidido a hacer BIEN las entradas. Intentaré escribir más lento (esos fallos otrográficos...) y poner algo de estilo en las entradas (cuadros "tchatchis" para el código, y cosas así).

Os dejo un pequeño índice de lo que será el proyecto inicial:



  • VB.net: A buscar M!nas
    • Análisis
      • ¿Cómo se juega?
      • Análisis teórico del modo de juego
    • Diseño
      • Diseño de algoritmos
      • Diseñando las pantallas del juego
    • Programación
      • Conceptos previos
        • Estructuración de código
        • "Multi-handler"
        • Controles dinámicos (en tiempo de ejecución)
      • Creación del programa + ejecutable (link de descarga)

Bien, definido el índice empecemos: contando esta serán seis entradas; aunque es posible que la quinta (Conceptos previos) la divida en tres... ya veremos.

Por ahora, veamos la primera sección

¿Cómo se juega?

Buscaminas es un juego de lógica de aquellos que me gustan. Está disponible desde hace varias versiones de Windows (desde el 95, si no recuerdo mal).

Tip: Si tienes Windows 7 y quieres jugar, prueba esto:

  - Inicio
  - Panel de control
  - Programas y características (si tienes la vista en iconos) / Programas > Programas y características (categorías)
  - Activar o desactivar las características de Windows
  - Buscamos Juegos y seleccionamos
  - Aceptar, y... ya tenemos los juegos activados

Hay varios niveles de dificultad. En Windows ponen 3 niveles básicos y uno "custom". Estudiaremos los niveles básicos:


  • Fácil:
    • 10 minas
    • Cuadrícula 9x9
  • Intermedio:
    • 40 minas
    • Cuadrícula 16x16
  • Difícil
    • 99 minas
    • Cuadrícula 16x30
El nivel de dificultad no influye en la finalidad del juego, pero creo necesario comentarla.

Bien; para jugar solo debemos saber contar un poco y usar el "coco". La finalidad del juego es marcar (con el botón derecho del ratón, o con el izquierdo si lo tienes configurado para zurdos) todas las minas correctamente, sin pisar (con el otro botón) ninguna de ellas.

Pisar una mina termina el juego, marcar una incorrectamente también (aunque de forma indirecta).

Sin embargo, nos dan un par de ventajas:

  1. Cada zona que no es mina tiene un número (entre 0 y 8), que nos dice cuántas minas tiene esa zona alrededor.
  2. Pisar una zona con 0 minas abre un perímetro de seguridad (toda la zona anexa que no tenga mina, recursivamente).
En nuestro proyecto solo daremos la primera ventaja.

Para jugar necesitamos usar solo el ratón. Las acciones permitidas son:

  • Pisar zona: con el botón izquierdo del ratón (derecho si lo tienes para zurdos)
    • Aplicable en zonas sin destapar
    • Si sale número sigue el juego; de lo contrario pierdes
  • Marcar mina / interrogante: botón derecho (o izquierdo si está configurado para zurdos)
    • Aplicable en zonas tapadas
    • Un interrogante es una marca personal para acordarte que quizás hay mina ahí.
    • Si marcas una zona donde no hay mina, a la larga perderás.
  • Comprobar zona: pulsando ambos botones a la vez.
    • Aplicable a cualquier zona; solo tiene efecto en zonas destapadas
    • Si la zona pulsada tiene todas las minas marcadas, destapará las zonas contiguas. ¡OJO!: si has marcado mal una mina y usas esto, perderás, ya que se abrirá una mina... (esta parte tampoco la implementaremos)
Usando bien el "truco" de comprobar zonas, podemos usar (solo en Windows) la siguiente estrategia:

 - Buscamos abrir una zona (img1)
 - Buscamos un 1 en una esquina, y marcamos el único cuadro adyacente como mina (img2)
 - Hacemos una comprobación en un 1 adyacente (img3)...  - Repetir mientras puedas! (img4)

Nota: No hace falta que sea un 1; mientras esté en una esquina (como en img1) se puede usar un poco de lógica y saber muy fácilmente si es mina o no; entonces podemos hacer casi la mitad del juego (en img4, todo lo que hay descubierto está hecho con este sistema).

img1
img2

img3
































img4

















A partir de ese momento hace falta usar algo de lógica... Tenemos que tener siempre en cuenta el número de minas que tenemos marcado y también el número de minas (las que hay y las marcadas) de los vecinos. Este aspecto es similar al sudoku... se basa en conjeturas y especulación...

Así pues, esta es una pequeña introducción al Buscaminas, la base de nuestro proyecto.

En la próxima entrega deduciremos qué nos hace falta para simular el juego.

Hasta la próxima!

COBOL: apuntes finales

Buenas!

Se me olvidó dejar un par de apuntes finales en la última entrada:

Para dudas generales os dejo un enlace a un foro que siempre me ha sido muy útil:

http://www.escobol.com/modules.php?name=Forums

por otra parte, también me podéis plantear dudas usando los comentarios. Dejadme vuestra duda y email y os contestaré a la menor brevedad posible.

Finalmente, un avance; las próximas entradas serán sobre VB.NET. Remodelaremos nuestro propio Buscaminas.

Como en la serie de entradas anteriores, al final os colgaré un ejemplo propio, que hice hace algún tiempo.

Hasta la próxima!


¿Te han resultado útiles mis conocimientos de COBOL?
¡Llévatelos donde quieras!



COBOL: archivos + ejemplo

Hola de nuevo!

Ya que esta será la última entrega "teórica", he decidido poner el ejemplo en esta misma.

Ahora veremos el tema de ficheros. En COBOL hay varios tipos de ficheros, cada uno con sus ventajas y desventajas. Vamos allá

Ficheros:

Como decía, hay varios tipos de ficheros. Esto no significa diversas extensiones (COBOL no mira extensiones), sino organizaciones internas, modos de acceso...

En concreto, hay tres tipos de ficheros:

 * Secuenciales
 * Indexados
 * Relativos

Explicaré un poco las características de cada tipo:

Los secuenciales son accedidos de manera secuencial (obviamente), es decir, de principio a fin. Si quieres acceder al tercer registro tienes que hacer tres lecturas, no hay más.

Los indexados pueden ser accedidos de manera secuencial o bien de manera directa (mediante clave, ya lo veremos)

Los relativos se basan en "registros", pudiendo ser accedidos de manera secuencial o directa (usando clave).

A continuación, una lista de ventajas / inconvenientes de los tipos de archivo:

 * Secuencial

Ventajas:
   - Tamaño mínimo
   - Fácilmente editable (a mano)

Inconvenientes
   - A tamaño grande, la lectura se ralentiza.
   - Acceso puramente secuencial

* Indexado

 Ventajas
   - Rápidamente accesible
   - Sistema de claves

 Inconvenientes
   - Mayor tamaño
   - Apenas editable (a mano)

* Relativo

 Ventajas
   - Claves "invisibles"
   - Acceso rápido

Inconvenientes
   - No se puede ordenar
   - Apenas editable (a mano)


Aparte de los tipos de archivo, hay lo que se denomina modo de acceso: SEQUENTIAL, RANDOM, DYNAMIC. El primero establece un acceso secuencial, el segundo aleatorio (por clave) y el tercero ambos.

Si el fichero es secuencial solo se puede acceder mediante SEQUENTIAL; en cualquier otro caso vale cualquiera.


El uso de las claves funciona de manera similar a las bases de datos; en el caso de los indexados la clave puede ser cualquier cosa (un dni, teléfono...); en los archivos relativos se usa un índice numérico, de manera transparente al usuario y al programador.


Para declarar un fichero, tenemos que extender dos DIVISION: la ENVIRONMENT y la DATA.

La declaración básica se da en la ENVIRONMENT, y en la DATA definimos simplemente la estructura. En la primera parte, la declaración tiene variaciones dependiendo del tipo de fichero; en la segunda no.

  - ENVIRONMENT SECTION:

   Para un archivo SECUENCIAL, debemos poner algo tal que:


       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
            SELECT var ASSIGN TO DISK 'archivo'
                   ORGANIZATION IS SEQUENTIAL
                   ACCESS MODE IS SEQUENTIAL.


Veamos: Se declara la division, una nueva section (Input-Output), y se declara un fichero mediante FILE CONTROL. Finalmente se especifica el fichero:
   * SELECT var -> asigna una variable de "volcado" para el 'archivo' (hasta 8 caracteres!), que está asignado en el disco (otra opción es, por ejemplo PRINTER).
  * ORGANIZATION IS SEQUENTIAL -> le decimos que es secuencial. Podemos no poner esta sentencia, ya que si no está, por defecto COBOL lo tomará como secuencial.

  * ACCESS MODE IS SEQUENTIAL -> le decimos el modo de acceso, en este caso el único disponible: secuencial.

Para un archivo indexado, debemos poner algo similar a esto:


       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
            SELECT var ASSIGN TO DISK 'archivo'
                   ORGANIZATION IS INDEXED
                   ACCESS MODE IS [SEQUENTIAL | RANDOM | DYNAMIC]
                   RECORD KEY IS var1
                   [ALTERNATE KEY IS var2 [WITH DUPLICATES]].


Revisemos el tema:

  * ORGANIZATION IS INDEXED -> le decimos que es un archivo indexado
  * ACCES MODE IS ... -> aquí especificamos el modo de acceso (explicado más arriba)
  * RECORD KEY IS var1 -> le decimos cuál es la clave (como una PK de una tabla) del archivo
  * ALTERNATE KEY IS ... -> podemos especificar una clave alternativa (como un índice). Es opcional. Si ponemos "with duplicates" (también opcional) le decimos a COBOL que aceptamos que haya duplicados en esta clave. Por defecto COBOL no acepta duplicados en ningún tipo de clave.

Finalmente, para un archivo relativo:


       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
            SELECT var ASSIGN TO DISK 'archivo'
                   ORGANIZATION IS RELATIVE
                   ACCESS MODE IS  [SEQUENTIAL | RANDOM | DYNAMIC] .

Como veis, este último no hace falta explicarlo...

Ahora falta el siguiente punto, la data division:


       DATA DIVISION.
       FILE SECTION.

       FD var LABEL RECORD STANDARD.

       01    var1.
          02 var2 PIC X.

Esto es un poco lioso, así que vamos a explicarlo bien...

Primero abrimos la file section, y a partir de ahí describimos los archivos:

 * FD var LABEL RECORD STANDARD: Es la abreviatura de "File Descriptor". var es el nombre de variable usado en la ENVIRONMENT. La label record es una cláusula que especifica una variable de control interna para los ficheros. Yo recomiendo ponerla siempre como STANDARD (la otra opción es OMITTED).

* 01 var1... -> Declaramos la estructura para var. Tal cual, como cualquier otra estructura. Si el fichero es INDEXED, en esta estructura estarán las RECORD KEY y ALTERNATE KEY.




Ahora ya podemos delcarar ficheros; pero falta usarlos.

Para usar un archivo COBOL, debemos seguir la rutina básica de toda la vida, es decir

 - Abrir archivo
 - Manipular archivo
 - Cerrar archivo

Para abrir el archivo, tenemos la siguiente cláusula:

OPEN [INPUT | OUTPUT | EXTEND | IO] var.

Se tiene que especificar una de las 4 opciones, a saber:

 * INPUT: abre el archivo solo para lectura
 * OUTPUT: abre el archivo solo para escritura (lo crea si no existe, si existe lo borra y empieza de nuevo)
 * EXTEND : abre el archivo solo para escritura (si no existe da error, si existe escribe sin borrarlo)
 * I-O: abre el archivo para lectura/escritura (el archivo tiene que existir)

Hay varias operaciones para manipular el archivo. El tema es que COBOL tiene una "tabla" de operaciones disponibles para cada modo de acceso y modo de apertura:

 - ACCESO secuencial
   * INPUT
       · READ: lee un registro
       · START: se posiciona sobre un registro
  * OUTPUT (o EXTEND)
       · WRITE: escribe un registro
  * I-O
       · READ
       · REWRITE: sobreescribe el registro almacenado
       · DELETE: borra el registro almacenado
       · START

- ACCESO aleatorio

   * INPUT
       · READ
  * OUTPUT (o EXTEND)
       · WRITE
  * I-O
       · READ
       · WRITE
       · REWRITE
       · DELETE

 - ACCESO dinamico

   * INPUT
       · READ
       · START
  * OUTPUT (o EXTEND)
       · WRITE
  * I-O
       · READ
       · WRITE
       · REWRITE
       · DELETE
       · START

Finalmente aclarar: las operaciones se hacen sobre var, y las modificaciones sobre la estructura creada.




Para terminar el tema de COBOL, dejo el SRC de mi trabajo final de COBOL. Recordad leer el README, y si vais a compartir el archivo, citad la fuente y/o la autoría, por favor :-)

http://www.mediafire.com/?4algy7xluud6ay4


Para las próximas entradas tengo varios temas pensados, dejo aquí unas posibles opciones
 - Optimización para PHP
 - Uso de .htaccess para mejorar nuestra web
 - Tutorial HPL2: creando escenarios

Siento haber tardado tanto en hacer esta entrega; he estado liado y enfermo...


Saludos, y ¡Hasta la próxima!


¿Te han resultado útiles mis conocimientos de COBOL?
¡Llévatelos donde quieras!



COBOL: flujo externo


Hacía días que no escribía nada aquí... he estado algo liado...

Sin contar esta entrada, quedan solamente dos más para completar este mini - curso. Al final del mismo, siendo un poco audaces y haciendo buen uso de la lógica pragmática seréis capaces de programar en COBOL. Obviamente las técnicas avanzadas no las trataremos en este apartado; quizás más adelante hago un mini - curso de COBOL avanzado...

En fin, esta entrada será relativamente corta, vamos allá

 - Flujo externo

Flujo externo

Como flujo externo se entiende todo aquello que ocurre fuera del código que estamos editando; es decir, está fuera del alcance de la aplicación.

En java, llamar a un objeto es "usar" el flujo externo (normalmente decimos que "llamamos al método X de la Clase Y", o "delegamos en X"...). En COBOL no hay objetos en sí (aunque se pueden simular con tiempo y algo de código), pero un programa externo es algo similar a llamar a un objeto, si lo planteamos bien.

Por supuesto, el flujo externo no se basa solamente en llamar a otro archivo COBOL; podemos llamar a cualquier fichero ejecutable externo, ya veremos.

Para llamar a un archivo COBOL externo, necesitamos usar la sentencia CALL.

CALL "PROGRAMA" USING var.

Analicemos rápidamente: "PROGRAMA" es el ID (PROGRAM-ID, generalmente el mismo nombre que el archivo, por convención siempre se hace) del COBOL al que llamamos.

La parte de "USING var" es opcional. Si la usamos, estamos pasando "var" por referencia, es decir, pasamos el puntero de var (que puede ser una variable compleja) y "PROGRAMA" la recibirá.

Esto debe nos debe resultar muy familiar: FUNCIONES. Sí, pero funciones EXTERNAS, no hay que olvidarlo.

Recalco esto porque las funciones externas consumen un poco más de recursos, ya que se tienen que apliar datos, desapliar, llamar al fichero COBOL, modificar el runtime...

Volviendo al tema; para cuando usamos la cláusula "USING", la variable "var" debe estar declarada en la WSS, sino dará error.

Por otra parte, el programa "PROGRAMA" debe incluir una sección especial en la DATA SECTION:

es la llamada LINKAGE SECTION.

En ella declaramos una variable con la misma PIC que la variable que pasamos.

Finalmente, en "PROGRAMA" debemos incluir otra cláusula en la PROCEDURE, de manera que quedaría algo así:

IDENTIFICATION DIVISION.
    PROGRAM-ID. PROGRA.
    REMARKS. PRUEBA DE FLUJO

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKIN-STORAGE SECTION.

LINKAGE SECTION.
01 PASO.
    02 PA1 PIC X.
    02 PA2 PIC 9.

PROCEDURE DIVISION USING PASO.

El "USING var" indica que debe meter la variable pasada en la variable indicada (PASO, en este caso). Después, podremos acceder a PASO sin problemas.

Si queremos hacer algo similar a un valor de retorno, solo tenemos que ampliar la variable a pasar, y añadir una variable que dependa de ella, coincidente al valor a retornar. Luego, en la procedure, se rellena ese campo y al salir del programa, como se pasa por parámetro, el programa principal lo recibirá.

Para llamar a otro archivo ejectuable (un exe, por ejemplo), debemos hacerlo mediante un "bridge". COBOL permite hacer un call al sistema, del siguiente método:

CALL "SYSTEM.EXE" USING str.

En este caso, USING es obligatorio (en teoría no), y le pasamos como argumento el comando a ejecutar (por ejemplo, "DIR"). De este modo, en str podemos poner una llamada a un exe, y se ejecutará sin problemas.

Como última aclaración, decir que en todo programa que llamemos (programa de COBOL), si no queremos que termine el programa, debemos cambiar el STOP RUN típico por el EXIT PROGRAM. Esto hace que COBOL retorne el flujo de ejecución una posición atrás en la pila, y por tanto, el programa anterior recobra el control.

Tal y como había dicho, esta será una entrada corta, así que aquí acaba el tema del flujo externo. Solo nos queda aprender a usar ficheros, y después haré un ejemplo completo (haré dos versiones, una compacta y la otra diversificada en varios archivos).

Así pues,
Hasta la próxima!


¿Te han resultado útiles mis conocimientos de COBOL?
¡Llévatelos donde quieras!