Sunteți pe pagina 1din 33

EVOLUTION OF THE ABAP PROGRAMMING

LANGUAGE
DEV262
Exercises / Solutions
Karsten Bohlmann, Holger Janz / ABAP Language Team
These exercises familiarize you with the modern ABAP features that are introduced and
explained during the presentation part of this workshop.
A few programs which have been developed without the new ABAP constructs (7.0 style)
serve as a starting point. Each program implements a popular mini game.
You are asked to employ modern ABAP in a few places so as to make the source code of
these mini games more concise and expressive.
For all exercises the following logon data are used:
System: TDI [local]
Client: 001
User: DEV262
Password: welcome
The icon for the SAP GUI Logon can be found on the desktop. The ADT (ABAP
Development Tools, aka ABAP in Eclipse) can be found in the start menu with the following
path:
SAP SAP NetWeaver ABAP Development Tools ABAP in Eclipse DEV262

DEV262

EXERCISE 1: SWEEP THESE MINES


In this exercise we work on an implementation of the popular Minesweeper game. Open program
ZDEV262_EXERCISE1_MINESWEEPER . Start the game and try it out. Then have a look at the source
code (it is also listed in APPENDIX A of this document).

Constructor Method
First lets have a look at the constructor of class game. It builds the initial playing field. Mines are
placed using the random number generator cl_abap_random_int.
1. Shorten the source code using inline declaration instead of classical declaration for the
following local variables: size, level, r, x, y. Similarly, inline-declare local field
symbols <cell>, <cell1>, <cells>, <cells1>.
2. Use the CONV operator to eliminate the need for local variable i in the creation of the
cl_abap_random_int instance.
3. Eliminate the IF statements which check and set the local variables n (between 2 and 32)
and level (between 1 and 5). Hint: Use the COND operator or built-in functions nmax /
nmin.

Display Method
Use expressions to rewrite the call of method cl_abap_brwoser=>show_html within the display
method of class game.
1. Use the CONV operator directly within the method call such that local variable title is not
needed anymore.
2. Use the COND operator directly within the method call such that local variable size is not
needed for passing the parameter.

Detect Method
Use inline declaration for the local variables u, d, l, r.

Starting the Game


At START-OF-SELECTION, class game is instantiated and started by calling method display. Use
operator NEW and method call chaining to write this in one statement without the use of variable
my_game. Remove the declaration of this variable.
Check if the game still works!
This completes the first exercise. If you still have time and more ideas how to use the new features,
you may proceed with the optional part.

Solution: APPENDIX B

Optional (if there is time)


Find further possibilities to rewrite the code of ZDEV262_EXERCISE1_MINESWEEPER.Try to shorten it
as much as possible. One possible solution can be found in APPENDIX C.

DEV262

EXERCISE 1: GET READY FOR 2048


2048 is a mini puzzle game: Slide numbered tiles on a grid to combine them (two tiles with the same
value merge into a new tile with doubled value); you win when you reach a tile with number 2048.
Program ZDEV262_EXERCISE2_2048 is an ABAP implementation of this game. The task is to
improve the implementation using modern ABAP. Start the game and try it out. Then have a look at
the source code (also listed in APPENDIX D of this document).

Constructor Method
The constructor of class game builds the initial playing field. After comment build field, there are two
nested DO loops. Rewrite this part using nested condition-driven table comprehensions
VALUE #( FOR UNTIL ).
Hint: A statement like CONCATENATE a b INTO c. can be rewritten using a string template:
c = |{ a }{ b }|.

Lets Build a Line


In method build_line, a sequence of two READ statements is used to retrieve an entry of table
field. Use the table selection operator [ ] and REF #( ) for an equivalent expression
which goes directly into the APPEND statement.

Create New Tiles


In method new_tiles, there is another sequence of two READs waiting for your optimization. Use
statement ASSIGN with table selection [ ] to rewrite this code in one statement. Local variables
x and y should no longer be used to derive the coordinates from pos and n.
Check if the game still works!
This completes the second exercise. If you still have time and more ideas how to use the new
features, you may proceed with the optional part.

Solution: APPENDIX E

Optional (if there is time)


Find further possibilities to rewrite the code of ZDEV262_EXERCISE2_2048.Try to shorten it as much
as possible. (Hint: Methods display and build_line can each be reduced to a single statement)
One possible solution can be found in APPENDIX F.

DEV262

APPENDIX A: MINESWEEPER WITHOUT EXPRESSIONS


PROGRAM ZDEV262_EXERCISE1_MINESWEEPER.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
bomb TYPE abap_bool, " cell contains bomb y/n
bombs TYPE i,
" # of neighboring bombs
state TYPE char1,
" [h]idden, [v]isible, [f]lagged
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH DEFAULT KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH DEFAULT KEY.
DATA:
field TYPE t_field,
n
TYPE i,
" dimension of field
bombs TYPE i,
" # of existing bombs
hidden TYPE i,
" # of hidden cells
flags TYPE i,
" # of flagged cells
moves TYPE i,
" # of moves
over
TYPE char1,
" game over: [w]in, [d]ead
header TYPE string.
" HTML header string
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
detect IMPORTING VALUE(x) TYPE i VALUE(y) TYPE i.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
DATA: i TYPE i, j TYPE i, threshold TYPE i.
DATA: cells TYPE t_cells, cell TYPE t_cell.
DATA: r TYPE REF TO cl_abap_random_int.
CONCATENATE ##NO_TEXT
`<html><head><style type="text/css">`
`a{text-decoration:none;width:13px;height:9px}`
`.hid{@#404080} .flg{@red} .bmb{@black}`
`.b0{@#e0e0e0} .b1{@lightblue} .b2{@lightgreen} .b3{@orange}`
`</style><script>function setloc(e){window.location=e;}`
`</script></head><body scroll="no"><table border=0>` INTO header.
REPLACE ALL OCCURRENCES OF `@` IN header WITH
`background-color:` ##NO_TEXT.
DATA size TYPE i VALUE 10.
DATA level TYPE i VALUE 3.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>request(
CHANGING field = level ).
IF
size < 2.
n = 2.
ELSEIF size > 32.
n = 32.
ELSE.
n = size.
ENDIF. " size: 2..32
IF
level < 1.
level = 1.
ELSEIF level > 5.
4

DEV262
level = 5.
ENDIF. " level: 1..5
" place hidden bombs randomly
i = sy-uzeit.
r = cl_abap_random_int=>create( seed = i min = 0 max = 99 ).
threshold = 100 - level * 7.
cell-state = 'h'. " initially all cells are hidden
DO n TIMES.
CLEAR cells.
DO n TIMES.
CLEAR cell-bomb.
i = r->get_next( ).
IF i > threshold.
cell-bomb = 'X'.
ENDIF.
APPEND cell TO cells.
ENDDO.
APPEND cells TO field.
ENDDO.
" compute neighboring-bombs count for
DATA: x TYPE i, y TYPE i.
FIELD-SYMBOLS: <cells> TYPE t_cells,
<cells1> TYPE t_cells,
LOOP AT field ASSIGNING <cells>.
y = sy-tabix.
LOOP AT <cells> ASSIGNING <cell>.
IF <cell>-bomb = 'X'.
bombs = bombs + 1.
ELSE.
x = sy-tabix.
DO 3 TIMES.
i = y - 2 + sy-index.
CHECK i >= 1 AND i <= n.
DO 3 TIMES.
j = x - 2 + sy-index.
CHECK j >= 1 AND j <= n.
READ TABLE field
INDEX i
READ TABLE <cells1> INDEX j
IF <cell1>-bomb = 'X'.
ADD 1 TO <cell>-bombs.
ENDIF.
ENDDO.
ENDDO.
ENDIF.
ENDLOOP.
ENDLOOP.
hidden = n * n.
SET HANDLER at_click.
ENDMETHOD.

each cell, and overall count


<cell> TYPE t_cell,
<cell1> TYPE t_cell.

ASSIGNING <cells1>.
ASSIGNING <cell1>.

METHOD display.
DATA: html TYPE string, style TYPE string.
DATA: title TYPE cl_abap_browser=>title.
DATA: x TYPE i, y TYPE i.
DATA: xn TYPE n LENGTH 2, yn TYPE n LENGTH 2.
DATA: b
TYPE char1, b_lim TYPE char1.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
html = header.
5

DEV262

LOOP AT field ASSIGNING <cells>.


y = sy-tabix. yn = y.
CONCATENATE html '<tr' INTO html.
IF over <> ''.
CONCATENATE html ##NO_TEXT
` onclick="setloc('sapevent:ovr');"` INTO html.
ENDIF.
CONCATENATE html '>' INTO html.
LOOP AT <cells> ASSIGNING <cell>.
x = sy-tabix. xn = x.
WRITE <cell>-bombs TO b.
b_lim = b.
IF b_lim > '3'. b_lim = '3'. ENDIF.
" determine CSS style (hid,flg,b0,...,b3) of cell
IF over <> '' AND <cell>-bomb = 'X'.
style = `bmb`.
"cell with bomb (game over)
ELSEIF <cell>-state = 'f'.
style = `flg`.
"cell flagged by player
ELSEIF <cell>-state = 'v'.
CONCATENATE 'b' b_lim INTO style. "visible cell
ELSEIF over <> ''.
style = `b0`.
"empty cell (game over)
ELSE.
style = `hid`.
"hidden cell
ENDIF.
CONCATENATE html '<td class=' style INTO html.
IF <cell>-state = 'v'.
CONCATENATE html '><a>' b '</a>' INTO html. " bombs value
ELSE.
" HTML events on cell (left: try; right: flag)
CONCATENATE html ##NO_TEXT
` oncontextmenu="setloc('sapevent:flg` 'x' xn 'y' yn `');`
`return false;"><a href="sapevent:try` 'x' xn 'y' yn `"/>`
INTO html.
ENDIF.
CONCATENATE html `</td>` INTO html.
ENDLOOP.
CONCATENATE html `</tr>` INTO html.
ENDLOOP.
CONCATENATE html `</table><br>` INTO html.
IF over = 'd'.
CONCATENATE html `*** Bomb - Game over ***` INTO html.
ELSEIF over = 'w'.
DATA m TYPE string. m = moves. CONDENSE m.
CONCATENATE html `Finished in ` m ` moves!` INTO html ##NO_TEXT.
ENDIF.
CONCATENATE html `</body></html>` INTO html.
DATA size TYPE string.
IF n < 20.
size = cl_abap_browser=>small.
ELSE.
size = cl_abap_browser=>medium.
ENDIF.
title = sy-title.
cl_abap_browser=>show_html(
title
= title
size
= size
format
= cl_abap_browser=>portrait
6

DEV262
context_menu = 'X'
html_string = html ).
ENDMETHOD.
METHOD at_click.
DATA: x TYPE i, y TYPE i.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
IF over <> ''. " game is over, final click
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
moves = moves + 1.
x = action+4(2).
y = action+7(2).
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x ASSIGNING <cell>.
IF action(3) = 'try'.
IF <cell>-bomb = 'X'.
over = 'd'. " hit bomb -> game over
ELSE.
detect( x = x y = y ).
ENDIF.
ELSE. " action(3) = 'flg'
IF <cell>-state = 'h'.
<cell>-state = 'f'. flags = flags + 1. hidden = hidden - 1.
ELSE.
<cell>-state = 'h'. flags = flags - 1. hidden = hidden + 1.
ENDIF.
ENDIF.
IF hidden = 0 AND flags = bombs.
over = 'w'. " all cells opened, all bombs found -> win
ENDIF.
display( ).
ENDMETHOD.
METHOD detect.
DATA: u TYPE i, d TYPE i, l TYPE i, r TYPE i.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
CHECK x >= 1 AND x <= n AND y >= 1 AND y <= n.
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x ASSIGNING <cell>.
CASE <cell>-state.
WHEN 'v'. RETURN.
WHEN 'h'. hidden = hidden - 1.
WHEN 'f'. flags = flags - 1.
ENDCASE.
<cell>-state = 'v'.
CHECK <cell>-bombs = 0.
u = y - 1. d = y + 1. l = x - 1. r = x + 1.
detect( y = u x = l ).
detect( y = u x = x ).
detect( y = u x = r ).
detect( y = y x = l ).
detect( y = y x = r ).
detect( y = d x = l ).
detect( y = d x = x ).
7

DEV262
detect( y = d x = r ).
ENDMETHOD.
ENDCLASS.
DATA:
my_game TYPE REF TO game.
START-OF-SELECTION.
CREATE OBJECT my_game.
my_game->display( ).

DEV262

APPENDIX B: MINESWEEPER WITH SOME EXPRESSIONS


SOLUTION EXERCISE 1
PROGRAM ZDEV262_EXERCISE1_SOLUTION.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
bomb TYPE abap_bool, " cell contains bomb y/n
bombs TYPE i,
" # of neighboring bombs
state TYPE char1,
" [h]idden, [v]isible, [f]lagged
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH DEFAULT KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH DEFAULT KEY.
DATA:
field TYPE t_field,
n
TYPE i,
" dimension of field
bombs TYPE i,
" # of existing bombs
hidden TYPE i,
" # of hidden cells
flags TYPE i,
" # of flagged cells
moves TYPE i,
" # of moves
over
TYPE char1,
" game over: [w]in, [d]ead
header TYPE string.
" HTML header string
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
detect IMPORTING VALUE(x) TYPE i VALUE(y) TYPE i.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
DATA: i TYPE i, j TYPE i, threshold TYPE i.
DATA: cells TYPE t_cells, cell TYPE t_cell.
CONCATENATE ##NO_TEXT
`<html><head><style type="text/css">`
`a{text-decoration:none;width:13px;height:9px}`
`.hid{@#404080} .flg{@red} .bmb{@black}`
`.b0{@#e0e0e0} .b1{@lightblue} .b2{@lightgreen} .b3{@orange}`
`</style><script>function setloc(e){window.location=e;}`
`</script></head><body scroll="no"><table border=0>` INTO header.
REPLACE ALL OCCURRENCES OF `@` IN header WITH
`background-color:` ##NO_TEXT.
DATA(size) = 10.
DATA(level) = 3.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>request(
CHANGING field = level ).
" size: 2..32
n
= nmax( val1 = 2 val2 = nmin( val1 = size val2 = 32 ) ).
" level: 1..5
level = nmax( val1 = 1 val2 = nmin( val1 = level val2 = 5 ) ).
" place hidden bombs randomly
DATA(r) = cl_abap_random_int=>create( seed = CONV i( sy-uzeit )
min = 0 max = 99 ).
threshold = 100 - level * 7.
cell-state = 'h'. " initially all cells are hidden
9

DEV262
DO n TIMES.
CLEAR cells.
DO n TIMES.
CLEAR cell-bomb.
i = r->get_next( ).
IF i > threshold.
cell-bomb = 'X'.
ENDIF.
APPEND cell TO cells.
ENDDO.
APPEND cells TO field.
ENDDO.
" compute neighboring-bombs count for each cell, and overall count
LOOP AT field ASSIGNING FIELD-SYMBOL(<cells>).
DATA(y) = sy-tabix.
LOOP AT <cells> ASSIGNING FIELD-SYMBOL(<cell>).
IF <cell>-bomb = 'X'.
bombs = bombs + 1.
ELSE.
DATA(x) = sy-tabix.
DO 3 TIMES.
i = y - 2 + sy-index.
CHECK i >= 1 AND i <= n.
DO 3 TIMES.
j = x - 2 + sy-index.
CHECK j >= 1 AND j <= n.
READ TABLE field
INDEX i ASSIGNING FIELD-SYMBOL(<cells1>).
READ TABLE <cells1> INDEX j ASSIGNING FIELD-SYMBOL(<cell1>).
IF <cell1>-bomb = 'X'.
ADD 1 TO <cell>-bombs.
ENDIF.
ENDDO.
ENDDO.
ENDIF.
ENDLOOP.
ENDLOOP.
hidden = n * n.
SET HANDLER at_click.
ENDMETHOD.
METHOD display.
DATA: html TYPE string, style TYPE string.
DATA: x TYPE i, y TYPE i.
DATA: xn TYPE n LENGTH 2, yn TYPE n LENGTH 2.
DATA: b
TYPE char1, b_lim TYPE char1.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
html = header.
LOOP AT field ASSIGNING <cells>.
y = sy-tabix. yn = y.
CONCATENATE html '<tr' INTO html.
IF over <> ''.
CONCATENATE html ##NO_TEXT
` onclick="setloc('sapevent:ovr');"` INTO html.
ENDIF.
CONCATENATE html '>' INTO html.
LOOP AT <cells> ASSIGNING <cell>.
x = sy-tabix. xn = x.
WRITE <cell>-bombs TO b.
10

DEV262
b_lim = b.
IF b_lim > '3'. b_lim = '3'. ENDIF.
" determine CSS style (hid,flg,b0,...,b3) of cell
IF over <> '' AND <cell>-bomb = 'X'.
style = `bmb`.
"cell with bomb (game over)
ELSEIF <cell>-state = 'f'.
style = `flg`.
"cell flagged by player
ELSEIF <cell>-state = 'v'.
CONCATENATE 'b' b_lim INTO style. "visible cell
ELSEIF over <> ''.
style = `b0`.
"empty cell (game over)
ELSE.
style = `hid`.
"hidden cell
ENDIF.
CONCATENATE html '<td class=' style INTO html.
IF <cell>-state = 'v'.
CONCATENATE html '><a>' b '</a>' INTO html. " bombs value
ELSE.
" HTML events on cell (left: try; right: flag)
CONCATENATE html ##NO_TEXT
` oncontextmenu="setloc('sapevent:flg` 'x' xn 'y' yn `');`
`return false;"><a href="sapevent:try` 'x' xn 'y' yn `"/>`
INTO html.
ENDIF.
CONCATENATE html `</td>` INTO html.
ENDLOOP.
CONCATENATE html `</tr>` INTO html.
ENDLOOP.
CONCATENATE html `</table><br>` INTO html.
IF over = 'd'.
CONCATENATE html `*** Bomb - Game over ***` INTO html.
ELSEIF over = 'w'.
DATA m TYPE string. m = moves. CONDENSE m.
CONCATENATE html `Finished in ` m ` moves!` INTO html ##NO_TEXT.
ENDIF.
CONCATENATE html `</body></html>` INTO html.
cl_abap_browser=>show_html(
title
= CONV cl_abap_browser=>title( sy-title )
size
= COND #( WHEN n < 20 THEN cl_abap_browser=>small
ELSE cl_abap_browser=>medium )
format
= cl_abap_browser=>portrait
context_menu = 'X'
html_string = html ).
ENDMETHOD.
METHOD at_click.
DATA: x TYPE i, y TYPE i.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
IF over <> ''. " game is over, final click
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
moves = moves + 1.
x = action+4(2).
y = action+7(2).
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x ASSIGNING <cell>.
11

DEV262

IF action(3) = 'try'.
IF <cell>-bomb = 'X'.
over = 'd'. " hit bomb -> game over
ELSE.
detect( x = x y = y ).
ENDIF.
ELSE. " action(3) = 'flg'
IF <cell>-state = 'h'.
<cell>-state = 'f'. flags = flags + 1. hidden = hidden - 1.
ELSE.
<cell>-state = 'h'. flags = flags - 1. hidden = hidden + 1.
ENDIF.
ENDIF.
IF hidden = 0 AND flags = bombs.
over = 'w'. " all cells opened, all bombs found -> win
ENDIF.
display( ).
ENDMETHOD.
METHOD detect.
FIELD-SYMBOLS:
<cells> TYPE t_cells, <cell> TYPE t_cell.
CHECK x >= 1 AND x <= n AND y >= 1 AND y <= n.
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x ASSIGNING <cell>.
CASE <cell>-state.
WHEN 'v'. RETURN.
WHEN 'h'. hidden = hidden - 1.
WHEN 'f'. flags = flags - 1.
ENDCASE.
<cell>-state = 'v'.
CHECK <cell>-bombs = 0.
DATA(u) = y - 1.
DATA(d) = y + 1.
DATA(l) = x - 1.
DATA(r) = x + 1.
detect( y = u x = l ).
detect( y = u x = x ).
detect( y = u x = r ).
detect( y = y x = l ).
detect( y = y x = r ).
detect( y = d x = l ).
detect( y = d x = x ).
detect( y = d x = r ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
NEW game( )->display( ).

12

DEV262

APPENDIX C: MINESWEEPER WITH EXPRESSIONS


SOLUTION EXERCISE 1 EXTREME
PROGRAM ZDEV262_EXERCISE1_XTREM.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
bomb TYPE abap_bool, " cell contains bomb y/n
bombs TYPE i,
" # of neighboring bombs
state TYPE char1,
" [h]idden, [v]isible, [f]lagged
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH EMPTY KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH EMPTY KEY.
DATA:
field TYPE t_field,
n
TYPE i,
" dimension of field
bombs TYPE i,
" # of existing bombs
hidden TYPE i,
" # of hidden cells
flags TYPE i,
" # of flagged cells
moves TYPE i,
" # of moves
over
TYPE char1,
" game over: [w]in, [d]ead
header TYPE string.
" HTML header string
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
detect IMPORTING VALUE(x) TYPE i VALUE(y) TYPE i.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
header = replace(
occ = 0 sub = `@` with = `background-color:` val =
`<html><head><style type="text/css">` &&
`a{text-decoration:none;width:13px;height:9px}` &&
`.hid{@#404080} .flg{@red} .bmb{@black}` &&
`.b0{@#e0e0e0} .b1{@lightblue} .b2{@lightgreen} .b3{@orange}` &&
`</style><script>function setloc(e){window.location=e;}` &&
`</script></head><body scroll="no"><table border=0>` ) ##NO_TEXT.
DATA(size) = 10.
DATA(level) = 3.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>request(
CHANGING field = level ).
" size: 2..32
n
= nmax( val1 = 2 val2 = nmin( val1 = size val2 = 32 ) ).
" level: 1..5
level = nmax( val1 = 1 val2 = nmin( val1 = level val2 = 5 ) ).
DATA(threshold) = 100 - level * 7.
" place hidden bombs randomly
field = VALUE #(
LET r = cl_abap_random_int=>create( seed = CONV i( sy-uzeit )
min = 0 max = 99 ) IN
FOR i = 1 UNTIL i > n
( VALUE #( FOR j = 1 UNTIL j > n
( state = 'h'
bomb = COND #( WHEN r->get_next( ) > threshold
13

DEV262
THEN 'X' ) ) ) ) ).
" compute neighboring-bombs count for each cell, and overall count
LOOP AT field ASSIGNING FIELD-SYMBOL(<cells>).
DATA(y) = sy-tabix.
LOOP AT <cells> ASSIGNING FIELD-SYMBOL(<cell>).
IF <cell>-bomb = 'X'.
bombs = bombs + 1.
ELSE.
DATA(x) = sy-tabix.
<cell>-bombs = REDUCE i(
INIT b = 0
FOR
i = nmax( val1 = 1 val2 = y - 1 )
WHILE i <= nmin( val1 = y + 1 val2 = n )
FOR
j = nmax( val1 = 1 val2 = x - 1 )
WHILE j <= nmin( val1 = x + 1 val2 = n )
NEXT b = COND #( WHEN field[ i ][ j ]-bomb = 'X'
THEN b + 1 ELSE b ) ).
ENDIF.
ENDLOOP.
ENDLOOP.
hidden = n * n.
SET HANDLER at_click.
ENDMETHOD.
METHOD display.
cl_abap_browser=>show_html(
title
= CONV cl_abap_browser=>title( sy-title )
size
= COND #( WHEN n < 20 THEN cl_abap_browser=>small
ELSE cl_abap_browser=>medium )
format
= cl_abap_browser=>portrait
context_menu = 'X'
html_string =
REDUCE string(
INIT h = header
FOR
y = 1 UNTIL y > n
NEXT h = h && |<tr{ COND #( WHEN over <> '' THEN
` onclick="setloc('sapevent:ovr');"` ) }>| &&
REDUCE string(
INIT k = ``
FOR x = 1 UNTIL x > n
LET c = field[ y ][ x ]
" CSS style (hid,flg,bmb,b0,...,b3) of cell
style = COND string(
WHEN over <> '' AND c-bomb = 'X'
THEN `bmb`
" bomb
WHEN c-state = 'f'
THEN `flg`
" flagged
WHEN c-state = 'v' " visible
THEN |b{ nmin( val1 = c-bombs val2 = 3 ) }|
WHEN over <> ''
" empty
THEN `b0`
ELSE `hid` )
" hidden
pos = |x{ x WIDTH = 2 ALIGN = RIGHT PAD = '0' }| &
|y{ y WIDTH = 2 ALIGN = RIGHT PAD = '0' }|
IN
NEXT k = |{ k }<td class={ style }| &&
COND #( WHEN c-state = 'v'
THEN |><a>{ c-bombs }</a>| " bombs value
ELSE " HTML events on cell (left: try; right: flag)
| oncontextmenu="setloc('sapevent:flg{ pos }');| &
|return false;"><a href="sapevent:try{ pos }"/>| )
14

DEV262
&& `</td>` )
&& `</tr>` )
&& `</table><br>`
&& COND #(
WHEN over = 'd' THEN `*** Bomb - Game over ***`
WHEN over = 'w' THEN |Finished in { moves } moves!| )
&& `</body></html>` ).
ENDMETHOD.
METHOD at_click.
IF over <> ''. " game is over, final click
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
moves = moves + 1.
DATA(x) = CONV i( action+4(2) ).
DATA(y) = CONV i( action+7(2) ).
ASSIGN field[ y ][ x ] TO FIELD-SYMBOL(<cell>).
IF action(3) = 'try'.
IF <cell>-bomb = 'X'.
over = 'd'. " hit bomb -> game over
ELSE.
detect( x = x y = y ).
ENDIF.
ELSE. " action(3) = 'flg'
IF <cell>-state = 'h'.
<cell>-state = 'f'. flags = flags + 1. hidden = hidden - 1.
ELSE.
<cell>-state = 'h'. flags = flags - 1. hidden = hidden + 1.
ENDIF.
ENDIF.
IF hidden = 0 AND flags = bombs .
over = 'w'. " all cells opened, all bombs found -> win
ENDIF.
display( ).
ENDMETHOD.
METHOD detect.
CHECK x >= 1 AND x <= n AND y >= 1 AND y <= n.
ASSIGN field[ y ][ x ] TO FIELD-SYMBOL(<cell>).
CASE <cell>-state.
WHEN 'v'. RETURN.
WHEN 'h'. hidden = hidden - 1.
WHEN 'f'. flags = flags - 1.
ENDCASE.
<cell>-state = 'v'.
CHECK <cell>-bombs = 0.
DATA(u) = y - 1.
DATA(d) = y + 1.
DATA(l) = x - 1.
DATA(r) = x + 1.
detect( y = u x = l ).
detect( y = u x = x ).
detect( y = u x = r ).
detect( y = y x = l ).
detect( y = y x = r ).
detect( y = d x = l ).
detect( y = d x = x ).
detect( y = d x = r ).
ENDMETHOD.
15

DEV262
ENDCLASS.
START-OF-SELECTION.
NEW game( )->display( ).

16

DEV262

APPENDIX D: 2048 WITHOUT EXPRESSIONS


PROGRAM ZDEV262_EXERCISE2_2048.
TYPE-POOLS abap.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
value TYPE i,
id
TYPE string,
born TYPE i,
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH DEFAULT KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH DEFAULT KEY,
t_ref
TYPE REF TO t_cell,
t_refs
TYPE STANDARD TABLE OF t_ref WITH DEFAULT KEY.
CONSTANTS tile_colors TYPE string VALUE
`eee4da ede0c8 f2b179 f59563 f67c5f f65e3b ` &
`edcf72 f67c5f edc850 edc53f edc22e` ##NO_TEXT.
DATA:
rnd
TYPE REF TO cl_abap_random_int,
field
TYPE t_field,
n
TYPE i,
" dimension of field
nsq
TYPE i,
" n * n
target TYPE i VALUE 2048, " target value
score
TYPE i,
" current score
moves
TYPE i,
" # of moves
header TYPE string,
" HTML header string
over
TYPE string,
" game-over message
line
TYPE t_refs.
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
build_line IMPORTING VALUE(direction) TYPE char1 VALUE(i) TYPE i,
move_lines IMPORTING VALUE(direction) TYPE char1
RETURNING VALUE(valid) TYPE abap_bool,
move_line RETURNING VALUE(valid) TYPE abap_bool,
new_tiles IMPORTING VALUE(factor) TYPE i DEFAULT 1,
check_game.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
DATA: size TYPE i VALUE 4,
seed TYPE i, limit TYPE i.
DATA: x TYPE string, y TYPE string, s TYPE string.
DATA: cell TYPE t_cell.
FIELD-SYMBOLS <cells> TYPE t_cells.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>add_field( CHANGING field = target ).
cl_demo_input=>request( ).
n = size.
IF
n < 3. n = 3.
ELSEIF n > 8. n = 8.
17

DEV262
ENDIF. " size: 3..8
nsq = n * n.
" target tile value must be a power of 2, at least 8
IF target < 8. target = 8. ENDIF.
target = 2 ** ceil( log( target ) / log( 2 ) ).
seed = sy-uzeit. limit = nsq - 1.
rnd = cl_abap_random_int=>create( seed = seed
min = 0
max = limit ).
" build field
cell-born = -1.
DO n TIMES.
y = sy-index. CONDENSE y.
APPEND INITIAL LINE to field ASSIGNING <cells>.
DO n TIMES.
x = sy-index. CONDENSE x.
CONCATENATE y x INTO cell-id.
APPEND cell TO <cells>.
ENDDO.
ENDDO.
" place initial tiles in field
new_tiles( 2 ).
" build HTML header with styles for tiles;
" cell/font sizes depend on n
DATA w TYPE string. w = trunc( 360 / n ). CONDENSE w.
DATA w2 TYPE string. w2 = trunc( w / 2 ).
CONDENSE w2.
DATA w3 TYPE string. w3 = trunc( w / 3 ).
CONDENSE w3.
DATA w4 TYPE string. w4 = trunc( w / 4 ).
CONDENSE w4.
header =
`<html><head><style type="text/css">` &
`.t0{background-color:#cbbdb0}` &
`td{border:1px solid bbada0;color:#776e65` &
`;text-align:center;vertical-align:center` &
`;font-family:'Century Gothic',sans-serif;font-weight:bold`.
CONCATENATE header `;width:` w `px;height:` w `px` INTO header.
CONCATENATE header `;font-size:` w2 `px}` INTO header.
DATA: pow TYPE i VALUE 2, off TYPE i.
DO 11 TIMES.
s = pow. CONDENSE s.
CONCATENATE header `.t` s `{background-color:#`
tile_colors+off(6) INTO header.
pow = pow * 2. off = off + 7.
IF sy-index >= 3.
CONCATENATE header `;color:#f9f6f2` INTO header.
ENDIF.
IF sy-index >= 10.
CONCATENATE header `;font-size:` w4 `px` INTO header.
ELSEIF sy-index >= 7.
CONCATENATE header `;font-size:` w3 `px` INTO header.
ENDIF.
CONCATENATE header `}` INTO header.
ENDDO.
CONCATENATE header ##NO_TEXT
`div{text-align:center}</style><script type="text/javascript">` &
`function okd(e)` &
`{c=window.event.keyCode;window.location='sapevent:'+c;}` &
`document.onkeydown = okd;</script></head> <body scroll=no ` &
`style="background-color:#bbada0;color:white;font-size:20pt">`
18

DEV262
INTO header.
SET HANDLER at_click.
ENDMETHOD.
METHOD display.
DATA: title TYPE cl_abap_browser=>title.
DATA: html TYPE string, s TYPE string.
FIELD-SYMBOLS: <cells> TYPE t_cells,
<cell> TYPE t_cell.
s = target.
CONCATENATE `Join the numbers and get to the `
s `tile` INTO title ##NO_TEXT.
s = score.
CONCATENATE header `<p align=right>Score ` s `</p>` &
`<table align=center>` INTO html.
LOOP AT field ASSIGNING <cells>.
CONCATENATE html `<tr>` INTO html.
LOOP AT <cells> ASSIGNING <cell>.
s = <cell>-value. CONDENSE s.
CONCATENATE html `<td class=t` s INTO html.
IF <cell>-born = moves.
CONCATENATE html ` style="border-color:red"` INTO html.
ENDIF.
IF <cell>-value = 0.
s = `&nbsp;`.
ENDIF.
CONCATENATE html `>` s `</td>` INTO html.
ENDLOOP.
CONCATENATE html `</tr>` INTO html.
ENDLOOP.
IF over IS INITIAL.
CONCATENATE html `</table><div>` &
`Use arrow keys to join tiles</div></body></html>` INTO html.
ELSE.
CONCATENATE html `</table><br>` over `</body></html>` INTO html.
ENDIF.
cl_abap_browser=>show_html(
title
= title
size
= cl_abap_browser=>small
format
= cl_abap_browser=>portrait
context_menu = 'X'
html_string = html ).
ENDMETHOD.
METHOD at_click.
DATA direction TYPE char1.
CASE action.
WHEN 37. direction = 'L'.
WHEN 38. direction = 'U'.
WHEN 39. direction = 'R'.
WHEN 40. direction = 'D'.
ENDCASE.
IF over <> ``. " game is over; leave game with any non-arrow key
IF direction IS INITIAL.
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
RETURN.
ENDIF.
IF move_lines( direction ) = abap_true.
moves = moves + 1.
19

DEV262
new_tiles( ).
check_game( ).
display( ).
ENDIF.
ENDMETHOD.
METHOD check_game.
DATA: max_value TYPE i, off TYPE i, idx TYPE i,
empty_cell TYPE abap_bool.
CONSTANTS dirs TYPE char4 VALUE 'LRUD'.
" find highest tile value and check if an empty cell exists
FIELD-SYMBOLS: <cells> TYPE t_cells,
<cell> TYPE t_cell,
<curr> TYPE t_ref,
<prev> TYPE t_ref.
LOOP AT field ASSIGNING <cells>.
LOOP AT <cells> ASSIGNING <cell>.
IF <cell>-value > max_value.
max_value = <cell>-value.
ENDIF.
IF <cell>-value = 0.
empty_cell = abap_true.
ENDIF.
ENDLOOP.
ENDLOOP.
" game is won if target value is reached
IF max_value >= target.
over = moves.
CONCATENATE `<div>Finished in ` over `moves!</div>`
INTO over ##NO_TEXT.
ELSEIF empty_cell IS INITIAL.
" no empty cell -> check if stuck: try move in all 4 directions
DO 4 TIMES.
off = sy-index - 1.
DO n TIMES.
build_line( direction = dirs+off(1) i = sy-index ).
LOOP AT line FROM 2 ASSIGNING <curr>.
idx = sy-tabix - 1.
READ TABLE line INDEX idx ASSIGNING <prev>.
IF <curr>->value = <prev>->value.
RETURN. " found a possible move; game not over
ENDIF.
ENDLOOP.
ENDDO.
ENDDO.
over = `<div>*** Stuck - Game over ***</div>`.
ENDIF.
ENDMETHOD.
METHOD move_lines.
CHECK direction IS NOT INITIAL.
DO n TIMES.
build_line( direction = direction i = sy-index ).
IF move_line( ) = abap_true.
valid = abap_true.
ENDIF.
ENDDO.
CLEAR line.
ENDMETHOD.
METHOD build_line.
20

DEV262
DATA: x TYPE i, y TYPE i, j TYPE i.
DATA: r_cell TYPE t_ref.
FIELD-SYMBOLS: <cells> TYPE t_cells.
" build cell references to line i, starting at end (wrt direction)
CLEAR line.
j = n + 1.
DO n TIMES.
j = j - 1.
CASE direction.
WHEN 'L'.
y = i.
x = n + 1 - j.
WHEN 'R'.
y = i.
x = j.
WHEN 'U'.
y = n + 1 - j.
x = i.
WHEN 'D'.
y = j.
x = i.
ENDCASE.
" append reference of cell to line
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x REFERENCE INTO r_cell.
APPEND r_cell TO line.
ENDDO.
ENDMETHOD.
METHOD move_line.
DATA: idx TYPE i VALUE 1, next_idx TYPE i VALUE 2.
DATA: curr TYPE t_ref, next TYPE t_ref.
READ TABLE line INTO curr INDEX 1.
WHILE next_idx <= n.
READ TABLE line INTO next INDEX next_idx.
IF curr->value > 0.
IF curr->value = next->value.
curr->value = curr->value * 2. " join tiles
next->value = 0.
valid = abap_true.
score = score + curr->value.
idx = idx + 1. " proceed
READ TABLE line INTO curr INDEX idx.
ELSEIF next->value > 0 OR next_idx = n.
idx = idx + 1. " proceed
READ TABLE line INTO curr INDEX idx.
next_idx = idx.
ENDIF.
ELSEIF next->value <> 0.
curr->value = next->value. " shift tile to empty cell
next->value = 0.
valid = abap_true.
ENDIF.
next_idx = next_idx + 1.
ENDWHILE.
ENDMETHOD.
METHOD new_tiles.
" place 1 or more (for n>4) new tiles (* 2 at start of game)
DATA: num TYPE i, pos TYPE i, attempts TYPE i, threshold TYPE i.
DATA: x type i, y TYPE i.
21

DEV262
FIELD-SYMBOLS: <cells> TYPE t_cells,
<cell> TYPE t_cell.
num = n - 3.
IF num < 1. num = 1. ENDIF.
num = num * factor.
threshold = nsq / 4.
DO num TIMES.
pos = rnd->get_next( ).
attempts = nsq.
DO. " try to place new tile at this or next free position
y = 1 + pos DIV n.
x = 1 + pos MOD n.
READ TABLE field
INDEX y ASSIGNING <cells>.
READ TABLE <cells> INDEX x ASSIGNING <cell>.
IF <cell>-value = 0.
" free position found - tile value: 2 (likelihood 75%) or 4
IF rnd->get_next( ) > threshold.
<cell>-value = 2.
ELSE.
<cell>-value = 4.
ENDIF.
" remember when tile was born, to display it as 'new'
<cell>-born = -1.
IF moves > 0.
<cell>-born = moves.
ENDIF.
EXIT.
ENDIF.
pos = ( pos + 1 ) MOD nsq.
attempts = attempts - 1.
IF attempts = 0.
RETURN.
ENDIF.
ENDDO.
ENDDO.
ENDMETHOD.
ENDCLASS.
DATA
my_game TYPE REF TO game.
START-OF-SELECTION.
CREATE OBJECT my_game.
my_game->display( ).

22

DEV262

APPENDIX E: 2048 WITH SOME EXPRESSIONS


SOLUTION EXERCISE 2
PROGRAM ZDEV262_EXERCISE2_SOLUTION.
TYPE-POOLS abap.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
value TYPE i,
id
TYPE string,
born TYPE i,
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH DEFAULT KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH DEFAULT KEY,
t_ref
TYPE REF TO t_cell,
t_refs
TYPE STANDARD TABLE OF t_ref WITH DEFAULT KEY.
CONSTANTS tile_colors TYPE string VALUE
`eee4da ede0c8 f2b179 f59563 f67c5f f65e3b ` &
`edcf72 f67c5f edc850 edc53f edc22e` ##NO_TEXT.
DATA:
rnd
TYPE REF TO cl_abap_random_int,
field
TYPE t_field,
n
TYPE i,
" dimension of field
nsq
TYPE i,
" n * n
target TYPE i VALUE 2048, " target value
score
TYPE i,
" current score
moves
TYPE i,
" # of moves
header TYPE string,
" HTML header string
over
TYPE string,
" game-over message
line
TYPE t_refs.
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
build_line IMPORTING VALUE(direction) TYPE char1 VALUE(i) TYPE i,
move_lines IMPORTING VALUE(direction) TYPE char1
RETURNING VALUE(valid) TYPE abap_bool,
move_line RETURNING VALUE(valid) TYPE abap_bool,
new_tiles IMPORTING VALUE(factor) TYPE i DEFAULT 1,
check_game.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
DATA: size TYPE i VALUE 4,
seed TYPE i, limit TYPE i.
DATA: s TYPE string.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>add_field( CHANGING field = target ).
cl_demo_input=>request( ).
n = size.
IF
n < 3. n = 3.
ELSEIF n > 8. n = 8.
ENDIF. " size: 3..8
23

DEV262
nsq = n * n.
" target tile value must be a power of 2, at least 8
IF target < 8. target = 8. ENDIF.
target = 2 ** ceil( log( target ) / log( 2 ) ).
seed = sy-uzeit. limit = nsq - 1.
rnd = cl_abap_random_int=>create( seed = seed
min = 0
max = limit ).
" build field
field = VALUE #( FOR i = 1 UNTIL i > n
( VALUE #( FOR j = 1 UNTIL j > n
( id = |{ i }{ j }| born = -1 ) ) ) ).
" place initial tiles in field
new_tiles( 2 ).
" build HTML header with styles for tiles;
" cell/font sizes depend on n
DATA w TYPE string. w = trunc( 360 / n ). CONDENSE w.
DATA w2 TYPE string. w2 = trunc( w / 2 ).
CONDENSE w2.
DATA w3 TYPE string. w3 = trunc( w / 3 ).
CONDENSE w3.
DATA w4 TYPE string. w4 = trunc( w / 4 ).
CONDENSE w4.
header =
`<html><head><style type="text/css">` &
`.t0{background-color:#cbbdb0}` &
`td{border:1px solid bbada0;color:#776e65` &
`;text-align:center;vertical-align:center` &
`;font-family:'Century Gothic',sans-serif;font-weight:bold`.
CONCATENATE header `;width:` w `px;height:` w `px` INTO header.
CONCATENATE header `;font-size:` w2 `px}` INTO header.
DATA: pow TYPE i VALUE 2, off TYPE i.
DO 11 TIMES.
s = pow. CONDENSE s.
CONCATENATE header `.t` s `{background-color:#`
tile_colors+off(6) INTO header.
pow = pow * 2. off = off + 7.
IF sy-index >= 3.
CONCATENATE header `;color:#f9f6f2` INTO header.
ENDIF.
IF sy-index >= 10.
CONCATENATE header `;font-size:` w4 `px` INTO header.
ELSEIF sy-index >= 7.
CONCATENATE header `;font-size:` w3 `px` INTO header.
ENDIF.
CONCATENATE header `}` INTO header.
ENDDO.
CONCATENATE header ##NO_TEXT
`div{text-align:center}</style><script type="text/javascript">` &
`function okd(e)` &
`{c=window.event.keyCode;window.location='sapevent:'+c;}` &
`document.onkeydown = okd;</script></head> <body scroll=no ` &
`style="background-color:#bbada0;color:white;font-size:20pt">`
INTO header.
SET HANDLER at_click.
ENDMETHOD.
METHOD display.
DATA: title TYPE cl_abap_browser=>title.
DATA: html TYPE string, s TYPE string.
FIELD-SYMBOLS: <cells> TYPE t_cells,
24

DEV262
<cell> TYPE t_cell.
s = target.
CONCATENATE `Join the numbers and get to the `
s `tile` INTO title ##NO_TEXT.
s = score.
CONCATENATE header `<p align=right>Score ` s `</p>` &
`<table align=center>` INTO html.
LOOP AT field ASSIGNING <cells>.
CONCATENATE html `<tr>` INTO html.
LOOP AT <cells> ASSIGNING <cell>.
s = <cell>-value. CONDENSE s.
CONCATENATE html `<td class=t` s INTO html.
IF <cell>-born = moves.
CONCATENATE html ` style="border-color:red"` INTO html.
ENDIF.
IF <cell>-value = 0.
s = `&nbsp;`.
ENDIF.
CONCATENATE html `>` s `</td>` INTO html.
ENDLOOP.
CONCATENATE html `</tr>` INTO html.
ENDLOOP.
IF over IS INITIAL.
CONCATENATE html `</table><div>` &
`Use arrow keys to join tiles</div></body></html>` INTO html.
ELSE.
CONCATENATE html `</table><br>` over `</body></html>` INTO html.
ENDIF.
cl_abap_browser=>show_html(
title
= title
size
= cl_abap_browser=>small
format
= cl_abap_browser=>portrait
context_menu = 'X'
html_string = html ).
ENDMETHOD.
METHOD at_click.
DATA direction TYPE char1.
CASE action.
WHEN 37. direction = 'L'.
WHEN 38. direction = 'U'.
WHEN 39. direction = 'R'.
WHEN 40. direction = 'D'.
ENDCASE.
IF over <> ``. " game is over; leave game with any non-arrow key
IF direction IS INITIAL.
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
RETURN.
ENDIF.
IF move_lines( direction ) = abap_true.
moves = moves + 1.
new_tiles( ).
check_game( ).
display( ).
ENDIF.
ENDMETHOD.
METHOD check_game.
DATA: max_value TYPE i, off TYPE i, idx TYPE i,
25

DEV262
empty_cell TYPE abap_bool.
CONSTANTS dirs TYPE char4 VALUE 'LRUD'.
" find highest tile value and check if an empty cell exists
FIELD-SYMBOLS: <cells> TYPE t_cells,
<cell> TYPE t_cell,
<curr> TYPE t_ref,
<prev> TYPE t_ref.
LOOP AT field ASSIGNING <cells>.
LOOP AT <cells> ASSIGNING <cell>.
IF <cell>-value > max_value.
max_value = <cell>-value.
ENDIF.
IF <cell>-value = 0.
empty_cell = abap_true.
ENDIF.
ENDLOOP.
ENDLOOP.
" game is won if target value is reached
IF max_value >= target.
over = moves.
CONCATENATE `<div>Finished in ` over `moves!</div>`
INTO over ##NO_TEXT.
ELSEIF empty_cell IS INITIAL.
" no empty cell -> check if stuck: try move in all 4 directions
DO 4 TIMES.
off = sy-index - 1.
DO n TIMES.
build_line( direction = dirs+off(1) i = sy-index ).
LOOP AT line FROM 2 ASSIGNING <curr>.
idx = sy-tabix - 1.
READ TABLE line INDEX idx ASSIGNING <prev>.
IF <curr>->value = <prev>->value.
RETURN. " found a possible move; game not over
ENDIF.
ENDLOOP.
ENDDO.
ENDDO.
over = `<div>*** Stuck - Game over ***</div>`.
ENDIF.
ENDMETHOD.
METHOD move_lines.
CHECK direction IS NOT INITIAL.
DO n TIMES.
build_line( direction = direction i = sy-index ).
IF move_line( ) = abap_true.
valid = abap_true.
ENDIF.
ENDDO.
CLEAR line.
ENDMETHOD.
METHOD build_line.
DATA: x TYPE i, y TYPE i, j TYPE i.
" build cell references to line i, starting at end (wrt direction)
CLEAR line.
j = n + 1.
DO n TIMES.
j = j - 1.
CASE direction.
WHEN 'L'.
26

DEV262
y = i.
x = n + 1 - j.
WHEN 'R'.
y = i.
x = j.
WHEN 'U'.
y = n + 1 - j.
x = i.
WHEN 'D'.
y = j.
x = i.
ENDCASE.
" append reference of cell to line
APPEND ref #( field[ y ][ x ] ) TO line.
ENDDO.
ENDMETHOD.
METHOD move_line.
DATA: idx TYPE i VALUE 1, next_idx TYPE i VALUE 2.
DATA: curr TYPE t_ref, next TYPE t_ref.
READ TABLE line INTO curr INDEX 1.
WHILE next_idx <= n.
READ TABLE line INTO next INDEX next_idx.
IF curr->value > 0.
IF curr->value = next->value.
curr->value = curr->value * 2. " join tiles
next->value = 0.
valid = abap_true.
score = score + curr->value.
idx = idx + 1. " proceed
READ TABLE line INTO curr INDEX idx.
ELSEIF next->value > 0 OR next_idx = n.
idx = idx + 1. " proceed
READ TABLE line INTO curr INDEX idx.
next_idx = idx.
ENDIF.
ELSEIF next->value <> 0.
curr->value = next->value. " shift tile to empty cell
next->value = 0.
valid = abap_true.
ENDIF.
next_idx = next_idx + 1.
ENDWHILE.
ENDMETHOD.
METHOD new_tiles.
" place 1 or more (for n>4) new tiles (* 2 at start of game)
DATA: num TYPE i, pos TYPE i, attempts TYPE i, threshold TYPE i.
FIELD-SYMBOLS <cell> TYPE t_cell.
num = n - 3.
IF num < 1. num = 1. ENDIF.
num = num * factor.
threshold = nsq / 4.
DO num TIMES.
pos = rnd->get_next( ).
attempts = nsq.
DO. " try to place new tile at this or next free position
ASSIGN field[ 1 + pos DIV n ][ 1 + pos MOD n ] TO <cell>.
IF <cell>-value = 0.
" free position found - tile value: 2 (likelihood 75%) or 4
IF rnd->get_next( ) > threshold.
27

DEV262
<cell>-value = 2.
ELSE.
<cell>-value = 4.
ENDIF.
" remember when tile was born, to display it as 'new'
<cell>-born = -1.
IF moves > 0.
<cell>-born = moves.
ENDIF.
EXIT.
ENDIF.
pos = ( pos + 1 ) MOD nsq.
attempts = attempts - 1.
IF attempts = 0.
RETURN.
ENDIF.
ENDDO.
ENDDO.
ENDMETHOD.
ENDCLASS.
DATA
my_game TYPE REF TO game.
START-OF-SELECTION.
CREATE OBJECT my_game.
my_game->display( ).

28

DEV262

APPENDIX F: 2048 WITH EXPRESSIONS


SOLUTION EXERCISE 2 EXTREME
PROGRAM ZDEV262_EXERCISE2_XTREM.
CLASS game DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
display.
PRIVATE SECTION.
TYPES:
BEGIN OF t_cell,
value TYPE i,
id
TYPE string,
born TYPE i,
END OF t_cell,
t_cells TYPE STANDARD TABLE OF t_cell WITH EMPTY KEY,
t_field TYPE STANDARD TABLE OF t_cells WITH EMPTY KEY,
t_ref
TYPE REF TO t_cell,
t_refs
TYPE STANDARD TABLE OF t_ref WITH EMPTY KEY.
CONSTANTS tile_colors TYPE string VALUE
`eee4da ede0c8 f2b179 f59563 f67c5f f65e3b ` &
`edcf72 f67c5f edc850 edc53f edc22e` ##NO_TEXT.
DATA:
rnd
TYPE REF TO cl_abap_random_int,
field
TYPE t_field,
n
TYPE i,
" dimension of field
nsq
TYPE i,
" n * n
target TYPE i VALUE 2048, " target value
score
TYPE i,
" current score
moves
TYPE i,
" # of moves
header TYPE string,
" HTML header string
over
TYPE string,
" game-over message
line
TYPE t_refs.
METHODS:
at_click FOR EVENT sapevent OF cl_abap_browser IMPORTING action,
build_line IMPORTING VALUE(direction) TYPE char1 VALUE(i) TYPE i,
move_lines IMPORTING VALUE(direction) TYPE char1
RETURNING VALUE(valid) TYPE abap_bool,
move_line RETURNING VALUE(valid) TYPE abap_bool,
new_tiles IMPORTING VALUE(factor) TYPE i DEFAULT 1,
check_game.
ENDCLASS.
CLASS game IMPLEMENTATION.
METHOD constructor.
DATA(size) = 4.
cl_demo_input=>add_field( CHANGING field = size ).
cl_demo_input=>add_field( CHANGING field = target ).
cl_demo_input=>request( ).
n
= nmax( val1 = 3 val2 = nmin( val1 = size val2 = 8 ) ).
nsq
= n * n.
" target tile value must be a power of 2, at least 8
target = nmax( val1 = 8 val2 = target ).
target = ipow( base = 2 exp = ceil( log( target ) / log( 2 ) ) ).
rnd = cl_abap_random_int=>create( seed = CONV i( sy-uzeit )
min = 0 max = nsq - 1 ).
field = VALUE #( FOR i = 1 UNTIL i > n
29

DEV262

new_tiles( 2 ).

( VALUE #( FOR j = 1 UNTIL j > n


( id = |{ i }{ j }| born = -1 ) ) ) ).
" place initial tiles in field

" build header with styles for tiles; cell/font sizes depend on n
header = REDUCE string( ##NO_TEXT
LET w = 360 / n IN
INIT h =
`<html><head><style type="text/css">` &
`.t0{background-color:#cbbdb0}` &
`td{border:1px solid bbada0;color:#776e65` &
`;text-align:center;vertical-align:center` &
`;font-family:'Century Gothic',sans-serif;font-weight:bold` &&
|;width:{ w }px;height:{ w }px;font-size:{ w / 2 }px\}|
FOR i = 1 UNTIL i > 11
NEXT h =
|{ h }.t{ ipow( base = 2 exp = i ) }\{| &
|background-color:#{ segment( val
= tile_colors
index = i ) }| &
|{ COND #( WHEN i >= 3 THEN `;color:#f9f6f2` ) }| &
|{ COND #( WHEN i >= 10 THEN |;font-size:{ w / 4 }px|
WHEN i >= 7 THEN |;font-size:{ w / 3 }px| ) }\}| ).
header = header && ##NO_TEXT
`div{text-align:center}</style><script type="text/javascript">` &
`function okd(e)` &
`{c=window.event.keyCode;window.location='sapevent:'+c;}` &
`document.onkeydown = okd;</script></head> <body scroll=no ` &
`style="background-color:#bbada0;color:white;font-size:20pt">`.
SET HANDLER at_click.
ENDMETHOD.
METHOD display.
cl_abap_browser=>show_html(
title
= |Join the numbers and get to the { target } tile!|
size
= cl_abap_browser=>small
format
= cl_abap_browser=>portrait
context_menu = 'X'
html_string =
REDUCE string(
INIT h = |{ header }<p align=right>Score { score }</p>| &
|<table align=center>|
FOR <cells> IN field
NEXT h = h && |<tr>| &&
REDUCE string(
INIT k = ``
FOR <c> IN <cells>
NEXT k = |{ k }<td class=t{ <c>-value }| &
|{ COND #( WHEN <c>-born = moves
THEN ` style="border-color:red"` ) }| &
|>{ COND #( WHEN <c>-value = 0 THEN `&nbsp;`
ELSE <c>-value ) }</td>| )
&& `</tr>` )
&& `</table>` &&
COND #( WHEN over = ``
THEN `<div>Use arrow keys to join tiles</div>`
ELSE |<br>{ over }| )
&& `</body></html>` ).
ENDMETHOD.
METHOD at_click.
DATA(direction) = SWITCH #( action

WHEN 37 THEN 'L'


30

DEV262
WHEN 38 THEN 'U'
WHEN 39 THEN 'R'
WHEN 40 THEN 'D' ).
IF over <> ``. " game is over; leave game with any non-arrow key
IF direction IS INITIAL.
cl_abap_browser=>close_browser( ).
LEAVE PROGRAM.
ENDIF.
RETURN.
ENDIF.
IF move_lines( direction ).
moves = moves + 1.
new_tiles( ).
check_game( ).
display( ).
ENDIF.
ENDMETHOD.
METHOD check_game.
CONSTANTS dirs TYPE char4 VALUE 'LRUD'.
" find highest tile value and check if an empty cell exists
DATA(fill) = REDUCE t_cell(
INIT m = VALUE #( )
FOR <cells> IN field
FOR <c>
IN <cells>
NEXT m-value = nmax( val1 = m-value val2 = <c>-value )
m-id
= COND #( WHEN <c>-value = 0 THEN `!` ELSE m-id ) ).
" game is won if target value is reached
IF fill-value >= target.
over = |<div>Finished in { moves } moves!</div>|.
ELSEIF fill-id <> `!`.
" no empty cell -> check if stuck: try move in all 4 directions
DO 4 TIMES.
DATA(off) = sy-index - 1.
DO n TIMES.
build_line( direction = dirs+off(1) i = sy-index ).
LOOP AT line FROM 2 ASSIGNING FIELD-SYMBOL(<curr>).
ASSIGN line[ sy-tabix - 1 ] TO FIELD-SYMBOL(<prev>).
IF <curr>->value = <prev>->value.
RETURN. " found a possible move; game not over
ENDIF.
ENDLOOP.
ENDDO.
ENDDO.
over = `<div>*** Stuck - Game over ***</div>`.
ENDIF.
ENDMETHOD.
METHOD move_lines.
CHECK direction IS NOT INITIAL.
DO n TIMES.
build_line( direction = direction i = sy-index ).
valid = cmax( val1 = move_line( ) val2 = valid ).
ENDDO.
CLEAR line.
ENDMETHOD.
METHOD build_line.
" build cell references to line i, starting at end (wrt direction)
line = VALUE #(
FOR j = n THEN j - 1 UNTIL j = 0 " j = n...1
31

DEV262
LET y = SWITCH i( direction
WHEN 'L' OR 'R' THEN i
WHEN 'D'
THEN j
WHEN 'U'
THEN n + 1 - j )
x = SWITCH i( direction
WHEN 'U' OR 'D' THEN i
WHEN 'R'
THEN j
WHEN 'L'
THEN n + 1 - j )
IN ( REF #( field[ y ][ x ] ) ) ).
ENDMETHOD.
METHOD move_line.
DATA: idx TYPE i VALUE 1, next_idx TYPE i VALUE 2.
DATA(curr) = line[ 1 ].
WHILE next_idx <= n.
DATA(next) = line[ next_idx ].
IF curr->value > 0.
IF curr->value = next->value.
curr->value = curr->value * 2. " join tiles
next->value = 0.
valid = abap_true.
score = score + curr->value.
idx = idx + 1. curr = line[ idx ]. " proceed
ELSEIF next->value > 0 OR next_idx = n.
idx = idx + 1. curr = line[ idx ]. " proceed
next_idx = idx.
ENDIF.
ELSEIF next->value <> 0.
curr->value = next->value. " shift tile to empty cell
next->value = 0.
valid = abap_true.
ENDIF.
next_idx = next_idx + 1.
ENDWHILE.
ENDMETHOD.
METHOD new_tiles.
" place 1 or more (for n>4) new tiles (* 2 at start of game)
DO nmax( val1 = n - 3 val2 = 1 ) * factor TIMES.
DATA(pos) = rnd->get_next( ).
DATA(attempts) = nsq.
DO. " try to place new tile at this or next free position
ASSIGN field[ 1 + pos DIV n ][ 1 + pos MOD n ]
TO FIELD-SYMBOL(<cell>).
IF <cell>-value = 0.
" free position found - tile value: 2 (likelihood 75%) or 4
<cell>-value = COND #( WHEN rnd->get_next( ) > nsq / 4
THEN 2 ELSE 4 ).
" remember when tile was born, to display it as 'new'
<cell>-born = COND #( WHEN moves > 0 THEN moves ELSE -1 ).
EXIT.
ENDIF.
pos = ( pos + 1 ) MOD nsq.
attempts = attempts - 1.
IF attempts = 0.
RETURN.
ENDIF.
ENDDO.
ENDDO.
ENDMETHOD.
32

DEV262
ENDCLASS.
START-OF-SELECTION.
NEW game( )->display( ).

33

S-ar putea să vă placă și