Minesweeper

Das bekannte Spiel Minesweeper in der SAP-Version. Leider ist alles in spanisch gehalten. Für das Spielen ist es nicht relevant, aber im Sourcecode ist es schon merkwürdig, wenn man nicht weiss, wofür ein Variablenname stehen könnte…


Minesweeper

REPORT zz_minesweeper .
*&==============================================================
*&                Developed by ROMAN LOPEZ NAVARRO                  *
*&           http://personales.com/espana/madrid/abap/              *
*&           http://www.geocities.com/romlopabap/                   *
*&==============================================================
*&———————————————————————*
*& Report ZBUSCAMIN                                                 *
*&———————————————————————*
* Una celda marcada como bomba ‘ B ‘ no admite ser marcada como detecta-
* da ‘ D ‘ ya que implicitamente esta detectada.
* Una vez que el juego acabo no se muestran mas mensajes aunque si se
* permite seguir explorando las celdas ocultas.
* Si algunas de las celdas marcadas como detectadas son incorrectas, es-
* tas se muestran en un nuevo listado.
* Evidentemente NUM_BOMB debe ser superior o igual a MAX_HERI.
* El boton que aparece debajo del tablero indica la accion actual del
* usuario, y que puede ser una de las dos siguientes:
* 1.- <pisando bombas> : La casilla se marca con el numero de bombas que
*                        rodean a dicha casilla.
* 2.- <marcando bombas>: La casilla se marca con una < D > (aunque el
*                        jugador se haya equivocado y no haya bomba).
* Para marcar bombas (detectar bombas) hay que hacer clic en el boton
* hasta que aparezca el texto ‘<marcando bombas>’.
*&———————————————————————*

INCLUDE <icon>.

CONSTANTS:
  line_ini TYPE i VALUE 2,”   Primera linea de comienzo de escritura
  posini TYPE i VALUE 4,”     Justificacion a la izquierda del tablero
  filas TYPE i VALUE 10,”                    Numero de filas del tablero
  columnas TYPE i VALUE 10,”              Numero de columnas del tablero
  bomba(3) VALUE ‘ B ‘,”                           Simbolo para la bomba
  detect(3) VALUE ‘ D ‘,”                   Simbolo para bomba detectada
  line_msg TYPE i VALUE 25,”           Numero de linea para los mensajes
  line_boton TYPE i VALUE 27,”             Numero de linea para el boton
  boton_on(17)  VALUE ‘<pisando  bombas>’,
  boton_off(17) VALUE ‘<marcando bombas>’,
  icon_on  LIKE icons-l2 VALUE ‘@1A@’,
  icon_off LIKE icons-l2 VALUE ‘@1C@’,
  icon_err LIKE icont-id VALUE ‘@3C@’.
DATA:
  msg_1(45),”                                                    Mensaje
  game_over,”                                  Flag de partida terminada
  str_tmp(255),”                                Variable string temporal
  boton(17),”                                            Texto del boton
  icon_boton LIKE icont-id,”                      Icono adjunto al boton
  conta_radar TYPE i,”          Contador de bombas limitrofes a la celda
  conta_heridas TYPE i,”                             Contador de errores
  conta_detect TYPE i,”      Contador de celdas marcadas como detectadas
  conta_general TYPE i,”                    conta_detect + conta_heridas
  cell_name LIKE dd03d-fieldname,”                   Nombre de una celda
  fila      TYPE i,”                   Fila de una celda
  columna   TYPE i,”                Columna de una celda
  fila_char2(2),
  columna_char2(2).

DATA:
  BEGIN OF itab_let,
    1(3), 2(3), 3(3), 4(3), 5(3), 6(3), 7(3), 8(3), 9(3), 10(3),
  END OF itab_let,

* Estructura con todas las celdas del tablero
  BEGIN OF itab,
    1 LIKE itab_let, 2 LIKE itab_let, 3 LIKE itab_let,
    4 LIKE itab_let, 5 LIKE itab_let, 6 LIKE itab_let,
    7 LIKE itab_let, 8 LIKE itab_let, 9 LIKE itab_let,
    10 LIKE itab_let,
  END OF itab,

* Estructura con las bombas pisadas

  bis_itab LIKE itab,

* Estructura con todas las bombas calculadas iniciales

  bomb_itab LIKE itab.
FIELD-SYMBOLS: <fs1>, <fs2>, <fs3>, <fs4>.
SELECTION-SCREEN BEGIN OF BLOCK bloque1 WITH FRAME TITLE titulo1.
SELECTION-SCREEN: BEGIN OF LINE,
  COMMENT 1(16) coment1,
  POSITION 20.
PARAMETERS num_bomb(2) TYPE n DEFAULT 15.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN: BEGIN OF LINE,
  COMMENT 1(18) coment2.
POSITION 20.
PARAMETERS max_heri(2) TYPE n DEFAULT 1.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK bloque1.

************************************************************************
*                             INITIALIZATION                        *
************************************************************************

INITIALIZATION.
  titulo1 = ‘Opciones de usuario’.
  coment1 = ‘Numero de bombas’.
  coment2 = ‘Errores permitidos’.

************************************************************************
*                          START-OF-SELECTION                       *
************************************************************************

START-OF-SELECTION.
  PERFORM display_tablero.
  PERFORM asignar_bombas.

************************************************************************
*                         AT LINE-OF-SELECTION                      *
************************************************************************

AT LINE-SELECTION.
  GET CURSOR FIELD cell_name.

*———————————————————————-*
*                      Cambiar el texto del  boton                   *
*———————————————————————-*
* Solo se permite si la partida no ha terminado. Cuando una partida ha
* terminado el boton queda permanentemente en estado ON.

  IF cell_name = ‘BOTON’ AND game_over IS INITIAL.
    PERFORM change_boton.
  ENDIF.

*———————————————————————-*
*     Obtener el valor de la celda seleccionada en todas las tablas *
*———————————————————————-*

  CHECK cell_name CS ‘ITAB-‘.
  ASSIGN (cell_name) TO <fs1>.” ——————- Tabla tablero actual
  CONCATENATE ‘BIS_’ cell_name INTO str_tmp.
  ASSIGN (str_tmp) TO <fs2>.” ——————— Tabla bombas pisadas
  CONCATENATE ‘BOMB_’ cell_name INTO str_tmp.
  ASSIGN (str_tmp) TO <fs3>.” ————— Tabla con todas las bombas
  SUBTRACT 1 FROM sy-lsind.

*&====================================================================&*
*&                           Detectar bomba                           &*
*&             Marcar con una ‘D’ la celda seleccionada               &*
*&====================================================================&*

  IF boton = boton_off.

*   Si el juego ha terminado no permitir detectar mas bombas (para evi-
*   tar mensajes cruzados de la rutina de deteccion). Sobra, porque el
*   boton solo puede estar en OFF si la partida no ha acabado.
*   CHECK GAME_OVER IS INITIAL.
*   Comprobar que no es una bomba ya pisada ni ya detectada

    CHECK <fs1> NE bomba AND <fs1> NE detect.
    MODIFY LINE sy-lilli FIELD VALUE  <fs1> FROM detect
                         FIELD FORMAT <fs1> COLOR col_positive.
    <fs1> = detect.
    ADD 1 TO conta_detect.
    conta_general = conta_heridas + conta_detect.
    IF conta_general = num_bomb.
      PERFORM check_success.
    ENDIF.
    EXIT.
  ENDIF.
*&====================================================================&*
*&                            Pisar celdas                            &*
*&  Muestra si la celda es una bomba o bien las bombas que la rodean  &*
*&====================================================================&*

  IF <fs1> = detect. SUBTRACT 1 FROM conta_detect. ENDIF.

* Si la celda es una bomba, marcarla con una ‘ B ‘

  IF <fs3> = bomba.
    PERFORM x_bomba.

* Si la celda no es una bomba, mostrar las bombas limitrofes

  ELSE.
    PERFORM obtain_coordenadas.
    PERFORM obtain_limits.
  ENDIF.
  SET CURSOR 0 0.

*&———————————————————————*
*&      Form  DISPLAY_TABLERO
*&———————————————————————*

FORM display_tablero.
  DATA: cablet(255), longitud TYPE i, numfila(2), n TYPE i.
  SKIP TO LINE line_ini.
  cablet = ‘  | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10|’.
  longitud = strlen( cablet ).
  WRITE AT posini cablet.
  ULINE AT /posini(longitud).
  NEW-LINE.

  DO filas TIMES.” ——————————————-  filas
    MOVE sy-index TO numfila.
    ASSIGN COMPONENT sy-index OF STRUCTURE itab TO <fs1>.
    POSITION posini.
    WRITE: numfila NO-GAP RIGHT-JUSTIFIED.
    WRITE: sy-vline NO-GAP.

    DO columnas TIMES.” ———————————–   columnas
      ASSIGN COMPONENT sy-index OF STRUCTURE <fs1> TO <fs2>.
      WRITE: <fs2> HOTSPOT NO-GAP,
             sy-vline NO-GAP.
    ENDDO.
    ULINE AT /posini(longitud). NEW-LINE.
  ENDDO.

  SKIP TO LINE line_msg.” ———————————  mensaje
  msg_1 = ‘Bombas pisadas: 0  ‘.
  WRITE AT: posini msg_1 INTENSIFIED OFF,
            sy-linsz space.
  SKIP 1.
  icon_boton = icon_on.” —————————– Icono del boton
  WRITE AT posini icon_boton AS ICON.
  boton = boton_on.” ——————————— Texto del boton
  CLEAR n.
  n = posini + 4.
  WRITE AT n boton HOTSPOT COLOR COL_TOTAL.
ENDFORM.                    ” DISPLAY_TABLERO

*&———————————————————————*
*&      Form  ASIGNAR_BOMBAS
*&———————————————————————*
* Llena la tabla BOMB_ITAB con las bombas calculadas
*&———————————————————————*

FORM asignar_bombas.
  DATA: char2(2) TYPE c, fieldname(255), conta_bombas TYPE i.

  PERFORM ini_semilla.
  WHILE conta_bombas LT num_bomb.

*—————————————————————— Fila

    CALL FUNCTION ‘QF05_RANDOM_INTEGER’
         EXPORTING
              ran_int_max = 10
              ran_int_min = 1
         IMPORTING
              ran_int     = fila
         EXCEPTIONS
              OTHERS      = 2.
    MOVE fila TO char2.
    CONCATENATE ‘BOMB_ITAB-‘ char2 INTO fieldname.

*————————————————————— Columna

    CALL FUNCTION ‘QF05_RANDOM_INTEGER’
         EXPORTING
              ran_int_max = 10
              ran_int_min = 1
         IMPORTING
              ran_int     = columna
         EXCEPTIONS
              OTHERS      = 1.
    MOVE columna TO char2.
    CONCATENATE fieldname ‘-‘ char2 INTO fieldname.

    ASSIGN (fieldname) TO <fs1>.
    IF <fs1> IS INITIAL.
      <fs1> = bomba.
      ADD 1 TO conta_bombas.
    ENDIF.
  ENDWHILE.
ENDFORM.                    ” ASIGNAR_BOMBAS

*&———————————————————————*
*&      Form  INI_SEMILLA
*&———————————————————————*
* Asegura que la semilla sera aleatoria.
*———————————————————————-*

FORM ini_semilla.
  DO 5 TIMES.
      CALL FUNCTION ‘QF05_RANDOM_INTEGER’
      EXPORTING
        RAN_INT_MAX         = 10
        RAN_INT_MIN         = 1
      IMPORTING
        RAN_INT             = fila
      EXCEPTIONS
        OTHERS              = 2.
  ENDDO.
ENDFORM.                    ” INI_SEMILLA

*&———————————————————————*
*&      Form  OBTAIN_COORDENADAS
*&———————————————————————*

FORM obtain_coordenadas.

  DATA  string_componentes LIKE dd03d-fieldname OCCURS 0.
  SPLIT cell_name AT ‘-‘ INTO TABLE string_componentes.
  READ TABLE string_componentes INDEX 2 INTO fila.
  READ TABLE string_componentes INDEX 3 INTO columna.
ENDFORM.                    ” OBTAIN_COORDENADAS

*&———————————————————————*
*&      Form  OBTAIN_LIMITS
*&———————————————————————*
* Averigua si las celdas limitrofes a la seleccionada tienen una bomba.
*———————————————————————-*

FORM obtain_limits.

  CLEAR conta_radar.

* Celda N

  fila_char2    = fila – 1.
  columna_char2 = columna.
  PERFORM radar.

* Celda NW

  fila_char2 = fila – 1.
  columna_char2 = columna – 1.
  PERFORM radar.

* Celda NE

  fila_char2 = fila – 1.
  columna_char2 = columna + 1.
  PERFORM radar.

* Celda S

  fila_char2 = fila + 1.
  columna_char2 = columna.
  PERFORM radar.

* Celda SW

  fila_char2 = fila + 1.
  columna_char2 = columna – 1.
  PERFORM radar.

* Celda SE

  fila_char2 = fila + 1.
  columna_char2 = columna + 1.
  PERFORM radar.

* Celda E


  fila_char2 = fila.
  columna_char2 = columna – 1.
  PERFORM radar.

* Celda W

  fila_char2 = fila.
  columna_char2 = columna + 1.
  PERFORM radar.
  <fs1> = conta_radar.
  MODIFY LINE sy-lilli FIELD VALUE <fs1>  FROM conta_radar
                      FIELD FORMAT <fs1> COLOR col_total.
ENDFORM.                    ” OBTAIN_LIMITS

*&———————————————————————*
*&      Form  RADAR
*&———————————————————————*
* Averigua si la celda pasada es una bomba.
*———————————————————————-*

FORM radar.

  CHECK fila_char2 NE ‘0’  AND columna_char2 NE ‘0’  AND
        fila_char2 NE ’11’ AND columna_char2 NE ’11’.
  CONCATENATE ‘BOMB_ITAB-‘ fila_char2 ‘-‘ columna_char2 INTO str_tmp.
  ASSIGN (str_tmp) TO <fs3>.
  IF <fs3> = bomba. ADD 1 TO conta_radar. ENDIF.
ENDFORM.                                                    ” RADAR

*&———————————————————————*
*&      Form  X_BOMBA
*&———————————————————————*
* Marca la bomba en el tablero y muestra el mensaje correspondiente.
*———————————————————————-*

FORM x_bomba.
  DATA conta_char(3).

* Compruebo que la bomba no ha sido pisada con anterioridad

  CHECK <fs1> NE bomba.

* Marcar la bomba en el tablero

  <fs1> = bomba.
  MODIFY LINE sy-lilli FIELD VALUE <fs1>  FROM <fs1>
                       FIELD FORMAT <fs1> COLOR col_negative.

* Seguir solo si la partida no esta acabada

  CHECK game_over IS INITIAL.
  ADD 1 TO conta_heridas.
  MOVE conta_heridas TO conta_char.
  CONCATENATE ‘Bombas pisadas: ‘ conta_char INTO msg_1.
  CLEAR sy-lisel.
  MODIFY LINE line_msg FIELD VALUE msg_1.

  IF conta_heridas = max_heri.
    msg_1 =  ‘Has pisado demasiadas bombas!’.
    MODIFY LINE line_msg FIELD VALUE msg_1
                         FIELD FORMAT msg_1 COLOR col_negative
                         INVERSE.
    game_over = ‘X’.
  ENDIF.

  conta_general = conta_heridas + conta_detect.
  IF conta_general = num_bomb.
    PERFORM check_success.
  ENDIF.
ENDFORM.                    ” X_BOMBA

*&———————————————————————*
*&      Form  CHANGE_BOTON
*&———————————————————————*
* Cambia el titulo del boton de BOTON_ON a BOTON_OFF y vicevera.
*———————————————————————-*

FORM change_boton.

  DATA: boton_tmp LIKE boton, color_tmp TYPE i, new_icon LIKE
icons-l2.

  CASE boton.
    WHEN boton_on.
      boton = boton_off. new_icon = icon_off. color_tmp = 5.
    WHEN boton_off.
      boton = boton_on. new_icon = icon_on. color_tmp = 3.
  ENDCASE.

  WRITE boton TO boton_tmp.
  icon_prepare_for_modify new_icon.

  MODIFY LINE line_boton FIELD VALUE boton FROM boton_tmp
                                     icon_boton FROM new_icon
                         FIELD FORMAT boton COLOR = color_tmp.

  SET CURSOR 1 1.
ENDFORM.                    ” CHANGE_BOTON

*&———————————————————————*
*&      Form  CHECK_SUCCESS
*&———————————————————————*
* La partida ha terminado. Comprueba si se ha ganado.
*———————————————————————-*

FORM check_success.

  DATA: n TYPE i, m TYPE i, fila_tmp TYPE i, columna_tmp TYPE i,
        fila_tmp_float TYPE f, fila_char3(3), columna_char3(3),
        new_icon LIKE icons-l2.
  FIELD-SYMBOLS: <fs5>, <fs6>.
  n = filas * columnas.

* Para mostrar un NUEVO listado con las detecciones incorrectas.

  ADD 1 TO sy-lsind.

* ———————————————————————*
* FILA_TMP y COLUMNA_TMP guardan las coordenadas de la celda a partir de
* su posicion absoluta (SY-INDEX)
* Por ejemplo, el campo 19 esta en la segunda fila, novena columna; para
* un total de 10 filas y 10 columnas.
*———————————————————————-*

  DO n TIMES.
    fila_tmp_float = sy-index / columnas.
    fila_tmp = ceil( fila_tmp_float ).
    columna_tmp = sy-index – ( columnas * fila_tmp ) + columnas.
    MOVE: fila_tmp TO fila_char3, columna_tmp TO columna_char3.
    CONCATENATE ‘ITAB-‘ fila_char3 ‘-‘ columna_char3
    INTO str_tmp.
    CONDENSE str_tmp NO-GAPS.
    ASSIGN (str_tmp) TO <fs6>.

*   Se comprueba que la celda que se ha se¤alado como ‘D’ – Detectada,
*   realmente contiene una bomba.

    IF <fs6> = detect.
      CONCATENATE ‘BOMB_ITAB-‘ fila_char3 ‘-‘ columna_char3
      INTO str_tmp.
      CONDENSE str_tmp NO-GAPS.
      ASSIGN (str_tmp) TO <fs5>.
      IF <fs5> NE bomba.

*       Mensaje(s) de celda(s) incorrecta(s) en nuevo listado.

        CONCATENATE ‘La celda ‘ fila_char3 ‘-‘ columna_char3
        ‘ no contenia ninguna bomba’ INTO str_tmp.
        WRITE str_tmp.
        msg_1 = ‘Has perdido! Juega otra vez’.
        MODIFY LINE line_msg FIELD VALUE msg_1
                             FIELD FORMAT msg_1 COLOR col_negative
                                                INVERSE.
        game_over = ‘X’.” —————————— Partida acabada
      ENDIF.
    ENDIF.
  ENDDO.

* Se pone el boton a ON (para no permitir detectar mas bombas tanto si
* la partida se ha ganado como si se ha perdido)

  new_icon = icon_on.
  icon_prepare_for_modify new_icon.
  boton = boton_on.
  CLEAR sy-lisel.
  MODIFY LINE line_boton FIELD VALUE  icon_boton FROM new_icon
                                      boton
                         FIELD FORMAT boton COLOR = 3.

* Mensaje de partida ganada (no se erro ninguna celda detectada)

  CHECK game_over IS INITIAL.
  msg_1 = ‘Enhorabuena! Ganaste la partida’.
  MODIFY LINE line_msg FIELD VALUE  msg_1
                       FIELD FORMAT msg_1 COLOR col_positive INVERSE.
  game_over = ‘X’.” ——————————- Partida acabada
ENDFORM.                    ” CHECK_SUCCESS

 

Enno Wulff