Code: Alles auswählen.
REPORT zknobel_1983_vs_2021_publish.
CLASS lcx DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS lcl_game DEFINITION.
PUBLIC SECTION.
TYPES: mtv_divisor TYPE numc2,
mtv_player TYPE string.
TYPES: BEGIN OF mts_gamestate,
digit1 TYPE char1,
digit2 TYPE char1,
digit3 TYPE char1,
digit4 TYPE char1,
digit5 TYPE char1,
digit6 TYPE char1,
END OF mts_gamestate.
TYPES: BEGIN OF mts_set_digit,
position TYPE i, " 1-6
digit TYPE numc1,
END OF mts_set_digit .
CONSTANTS: mc_player_1 TYPE mtv_player VALUE `Spieler 1 - macht den ersten Zug`,
mc_player_2 TYPE mtv_player VALUE `Spieler 2 - macht den letzten Zug`,
mc_gamestate_not_set TYPE char1 VALUE '?'.
METHODS:
i_want_to_be_player_1 IMPORTING iv_divisor TYPE mtv_divisor
RETURNING VALUE(rv_player) TYPE mtv_player,
set_player IMPORTING iv_player TYPE mtv_player,
set_digit IMPORTING is_gamestate TYPE mts_gamestate
iv_divisor TYPE mtv_divisor
RETURNING VALUE(rs_set_digit) TYPE mts_set_digit
RAISING lcx.
DATA:
mv_player TYPE mtv_player.
ENDCLASS.
CLASS lcl_solution DEFINITION INHERITING FROM lcl_game.
PUBLIC SECTION.
METHODS: i_want_to_be_player_1 REDEFINITION,
set_digit REDEFINITION.
ENDCLASS.
CLASS lcl_opponent DEFINITION INHERITING FROM lcl_game.
PUBLIC SECTION.
METHODS: set_digit REDEFINITION.
ENDCLASS.
CLASS lcl_application DEFINITION FINAL.
PUBLIC SECTION.
CONSTANTS: mc_all_divisors TYPE lcl_game=>mtv_divisor VALUE 99.
METHODS:
initialization,
main IMPORTING iv_divisor TYPE lcl_game=>mtv_divisor.
PRIVATE SECTION.
METHODS:
play_game IMPORTING iv_divisor TYPE lcl_game=>mtv_divisor
CHANGING cv_choice TYPE lcl_game=>mtv_player
cv_won TYPE abap_bool
cv_gamestates TYPE string
RAISING lcx,
advance_game IMPORTING is_set_digit TYPE lcl_game=>mts_set_digit
RETURNING VALUE(rv_move_is_allowed) TYPE abap_bool
RAISING lcx.
DATA:
mo_player1 TYPE REF TO lcl_game,
mo_player2 TYPE REF TO lcl_game,
ms_gamestate TYPE lcl_game=>mts_gamestate.
ENDCLASS.
PARAMETERS: divisor TYPE lcl_game=>mtv_divisor AS LISTBOX VISIBLE LENGTH 40 OBLIGATORY.
INITIALIZATION.
NEW lcl_application( )->initialization( ).
END-OF-SELECTION.
NEW lcl_application( )->main( iv_divisor = divisor ).
CLASS lcl_solution IMPLEMENTATION.
METHOD i_want_to_be_player_1.
rv_player = mc_player_1.
ENDMETHOD.
METHOD set_digit.
*Freie Stelle wählen und dort eine Ziffer eintragen und hoffen, dass das ausreichen ist.
DO 6 TIMES.
ASSIGN COMPONENT sy-index OF STRUCTURE is_gamestate TO FIELD-SYMBOL(<lv_digit>).
IF <lv_digit> = mc_gamestate_not_set.
rs_set_digit = VALUE #( position = sy-index digit = sy-index ).
RETURN.
ENDIF.
ENDDO.
ENDMETHOD.
ENDCLASS.
CLASS lcl_opponent IMPLEMENTATION.
METHOD set_digit.
DATA: lv_count_unset TYPE i,
ls_last LIKE rs_set_digit,
lv_num TYPE char6,
lv_num2 TYPE char6,
lv_offset TYPE i.
*--------------------------------------------------------------------*
* Bei ungünstiger Wahl des Spielers Sonderbehandlung
*--------------------------------------------------------------------*
IF 720 MOD iv_divisor = 0.
IF is_gamestate-digit6 = mc_gamestate_not_set.
rs_set_digit = VALUE #( position = 6 digit = 7 ).
RETURN.
ENDIF.
IF is_gamestate-digit5 = mc_gamestate_not_set.
rs_set_digit = VALUE #( position = 5 digit = 1 ).
RETURN.
ENDIF.
IF is_gamestate-digit4 = mc_gamestate_not_set.
rs_set_digit = VALUE #( position = 4 digit = 3 ).
RETURN.
ENDIF.
ENDIF.
DATA: lt_results TYPE match_result_tab.
FIND ALL OCCURRENCES OF mc_gamestate_not_set IN is_gamestate RESULTS lt_results.
lv_count_unset = lines( lt_results ).
*--------------------------------------------------------------------*
* 1. und 2. Ziffer - irgendwas reinsetzen
*--------------------------------------------------------------------*
DO 6 TIMES.
ASSIGN COMPONENT sy-index OF STRUCTURE is_gamestate TO FIELD-SYMBOL(<lv_digit>).
IF <lv_digit> = mc_gamestate_not_set.
ls_last = rs_set_digit.
rs_set_digit = VALUE #( position = sy-index digit = sy-index ).
IF lv_count_unset > 2.
RETURN.
ENDIF.
ENDIF.
ENDDO.
*--------------------------------------------------------------------*
* das letzte Mal einfach die für den Spieler schlechteste Möglichkeit wählen
*--------------------------------------------------------------------*
IF lv_count_unset = 1.
DO 10 TIMES.
lv_num = is_gamestate.
REPLACE mc_gamestate_not_set IN lv_num WITH |{ sy-index - 1 }|.
IF lv_num MOD iv_divisor <> 0.
rs_set_digit-digit = sy-index - 1.
RETURN.
ENDIF.
ENDDO.
ELSEIF lv_count_unset = 2.
* Einfach 20 * 10 Möglichkeiten durchprobieren und die Schlechteste auswählen
DO 2 TIMES.
IF sy-index = 1.
lv_offset = rs_set_digit-position - 1.
ELSE.
lv_offset = ls_last-position - 1.
ENDIF.
DO 10 TIMES.
lv_num = is_gamestate.
lv_num+lv_offset(1) = |{ sy-index - 1 }|.
DO 10 TIMES.
lv_num2 = lv_num.
REPLACE mc_gamestate_not_set IN lv_num2 WITH |{ sy-index - 1 }|.
IF lv_num2 MOD iv_divisor = 0.
EXIT.
ENDIF.
ENDDO.
IF lv_num2 MOD iv_divisor <> 0.
rs_set_digit = VALUE #( position = lv_offset + 1 digit = lv_num+lv_offset(1) ).
RETURN.
ENDIF.
ENDDO.
ENDDO.
ENDIF.
ENDMETHOD.
ENDCLASS.
CLASS lcl_application IMPLEMENTATION.
METHOD main.
TYPES: BEGIN OF lts_results,
divisor TYPE lcl_game=>mtv_divisor,
choice TYPE lcl_game=>mtv_player,
result TYPE string,
won TYPE abap_bool,
exception TYPE abap_bool,
END OF lts_results.
FIELD-SYMBOLS: <ls_result> TYPE lts_results.
DATA: lt_results TYPE STANDARD TABLE OF lts_results WITH NON-UNIQUE DEFAULT KEY.
*--------------------------------------------------------------------*
* Spiel(e) spielen
*--------------------------------------------------------------------*
IF iv_divisor = mc_all_divisors. " Alle
DO 20 TIMES.
APPEND VALUE #( divisor = sy-index
exception = abap_false
won = abap_false
) TO lt_results ASSIGNING <ls_result>.
TRY.
me->play_game( EXPORTING iv_divisor = <ls_result>-divisor
CHANGING cv_choice = <ls_result>-choice
cv_won = <ls_result>-won
cv_gamestates = <ls_result>-result
).
CATCH lcx.
<ls_result>-exception = abap_true.
ENDTRY.
ENDDO.
ELSE.
TRY.
APPEND VALUE #( divisor = iv_divisor
exception = abap_false
won = abap_false
) TO lt_results ASSIGNING <ls_result>.
me->play_game( EXPORTING iv_divisor = <ls_result>-divisor
CHANGING cv_choice = <ls_result>-choice
cv_won = <ls_result>-won
cv_gamestates = <ls_result>-result
).
CATCH lcx.
<ls_result>-exception = abap_true.
ENDTRY.
ENDIF.
*--------------------------------------------------------------------*
* Ergebnis
*--------------------------------------------------------------------*
TRY.
cl_salv_table=>factory( IMPORTING
r_salv_table = DATA(lo_salv)
CHANGING
t_table = lt_results
).
lo_salv->get_columns( )->set_optimize( ).
LOOP AT lo_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<ls_col>).
<ls_col>-r_column->set_medium_text( CONV #( <ls_col>-columnname ) ).
ENDLOOP.
lo_salv->display( ).
CATCH cx_salv_msg.
ENDTRY.
ENDMETHOD.
METHOD play_game.
DATA: lo_winner TYPE REF TO lcl_game,
lv_gamestate TYPE char6.
*--------------------------------------------------------------------*
* Lösung und Gegner erzeugen
*--------------------------------------------------------------------*
DATA(lo_solution) = NEW lcl_solution( ).
DATA(lo_opponent) = NEW lcl_opponent( ).
*--------------------------------------------------------------------*
* Lösung fragen, ob sie anfange möchte oder nicht und dann
* dementsprechend Spiel initialisieren
*--------------------------------------------------------------------*
IF lo_solution->i_want_to_be_player_1( iv_divisor ) = lcl_game=>mc_player_1.
cv_choice = lcl_game=>mc_player_1.
mo_player1 = lo_solution.
mo_player2 = lo_opponent.
ELSE.
cv_choice = lcl_game=>mc_player_2.
mo_player1 = lo_opponent.
mo_player2 = lo_solution.
ENDIF.
mo_player1->set_player( lcl_game=>mc_player_1 ).
mo_player2->set_player( lcl_game=>mc_player_2 ).
CLEAR me->ms_gamestate WITH lcl_game=>mc_gamestate_not_set.
*--------------------------------------------------------------------*
* Spielen der 6 Züge
*--------------------------------------------------------------------*
DO 3 TIMES.
* Spieler 1 zieht
me->advance_game( mo_player1->set_digit( is_gamestate = me->ms_gamestate iv_divisor = iv_divisor ) ).
lv_gamestate = me->ms_gamestate.
cv_gamestates = |{ cv_gamestates } / { lv_gamestate }|.
* Spieler 2 zieht
me->advance_game( mo_player2->set_digit( is_gamestate = me->ms_gamestate iv_divisor = iv_divisor ) ).
lv_gamestate = me->ms_gamestate.
cv_gamestates = |{ cv_gamestates } / { lv_gamestate }|.
ENDDO.
REPLACE REGEX '^[/\s]*' IN cv_gamestates WITH ``.
* Prüfen ob gewonnen oder nicht
DATA: lv_num TYPE num6.
lv_num = lv_gamestate.
IF lv_num MOD iv_divisor = 0.
cv_won = abap_true.
ELSE.
cv_won = abap_false.
ENDIF.
ENDMETHOD.
METHOD advance_game.
ASSIGN COMPONENT is_set_digit-position OF STRUCTURE me->ms_gamestate TO FIELD-SYMBOL(<lv_digit>).
IF sy-subrc <> 0.
MESSAGE |Position { is_set_digit-position } darf nicht gesetzt werden| TYPE 'I' DISPLAY LIKE 'E'.
RAISE EXCEPTION TYPE lcx.
ENDIF.
IF <lv_digit> <> lcl_game=>mc_gamestate_not_set.
MESSAGE |Position { is_set_digit-position } dist schon belegt| TYPE 'I' DISPLAY LIKE 'E'.
RAISE EXCEPTION TYPE lcx.
ENDIF.
<lv_digit> = is_set_digit-digit.
ENDMETHOD.
METHOD initialization.
DATA lt_values TYPE vrm_values.
DO 20 TIMES.
APPEND VALUE #( key = sy-index ) TO lt_values.
ENDDO.
APPEND VALUE #( key = 99 text = 'Alle Divisoren von 1-20' ) TO lt_values.
CALL FUNCTION 'VRM_SET_VALUES'
EXPORTING
id = 'DIVISOR'
values = lt_values
EXCEPTIONS
id_illegal_name = 1
OTHERS = 2.
divisor = mc_all_divisors.
ENDMETHOD.
ENDCLASS.
CLASS lcl_game IMPLEMENTATION.
METHOD i_want_to_be_player_1.
rv_player = mc_player_1.
ENDMETHOD.
METHOD set_player.
mv_player = iv_player.
ENDMETHOD.
METHOD set_digit.
MESSAGE 'Method SET_DIGIT not redefined' TYPE 'I' DISPLAY LIKE 'E'.
RAISE EXCEPTION TYPE lcx.
ENDMETHOD.
ENDCLASS.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag (Insgesamt 3):
ewx • Thomas R. • DeathAndPain
Code: Alles auswählen.
CONSTANTS: mc_player_1 TYPE mtv_player VALUE `Spieler 1 - macht den ersten Zug`,
mc_player_2 TYPE mtv_player VALUE `Spieler 2 - macht den letzten Zug`,
mc_gamestate_not_set TYPE char1 VALUE '?'.
Code: Alles auswählen.
CLASS lcl_solution DEFINITION INHERITING FROM lcl_game.
PUBLIC SECTION.
TYPES: BEGIN OF mts_decision.
INCLUDE TYPE mts_set_digit AS set_digit.
TYPES:
i_win TYPE abap_bool,
END OF mts_decision.
METHODS:
i_want_to_be_player_1 REDEFINITION,
set_digit REDEFINITION,
can_win IMPORTING is_current_gamestate TYPE mts_gamestate
iv_divisor TYPE mtv_divisor
iv_win_if_divisible TYPE abap_bool
RETURNING VALUE(rs_decision) TYPE mts_decision.
ENDCLASS.
CLASS lcl_solution IMPLEMENTATION.
METHOD i_want_to_be_player_1.
DATA: ls_gamestate TYPE mts_gamestate.
* ls_gamestate = '00????'.
clear ls_gamestate with mc_gamestate_not_set.
DATA(ls_decision) = can_win( is_current_gamestate = ls_gamestate
iv_divisor = iv_divisor
iv_win_if_divisible = abap_true
).
IF ls_decision-i_win = abap_true.
rv_player = mc_player_1.
ELSE.
rv_player = mc_player_2.
ENDIF.
ENDMETHOD.
METHOD set_digit.
DATA(ls_decision) = can_win( is_current_gamestate = is_gamestate
iv_divisor = iv_divisor
iv_win_if_divisible = abap_true
).
rs_set_digit = ls_decision-set_digit.
ENDMETHOD.
METHOD can_win.
DATA: lv_num TYPE num6,
lv_is_divisible TYPE abap_bool,
ls_test_gamestate TYPE mts_gamestate,
ls_decision TYPE mts_decision.
IF count( val = CONV string( is_current_gamestate ) sub = mc_gamestate_not_set ) > 0. " Noch nciht alle Positionen belegt
* Alle Positionen durchprobieren
DO 6 TIMES. " Alle Positionen durchprobieren
data(lv_position) = sy-index.
ls_test_gamestate = is_current_gamestate.
ASSIGN COMPONENT sy-index OF STRUCTURE ls_test_gamestate TO FIELD-SYMBOL(<lv_digit>).
IF <lv_digit> = mc_gamestate_not_set.
DO 10 TIMES.
<lv_digit> = sy-index - 1. " Alle Ziffern von 0 - 9 an dieser Position durchprobieren
* Keine Win-Options --> Rückgabe des letzten Wertes
rs_decision = VALUE #( set_digit = VALUE #( position = lv_position
digit = <lv_digit>
)
i_win = abap_false ).
* Der Gegner gewinnt, wenn ich nicht gewinne --> switch iv_win_if_divisible
IF iv_win_if_divisible = abap_true.
ls_decision = can_win( is_current_gamestate = ls_test_gamestate
iv_divisor = iv_divisor
iv_win_if_divisible = abap_false
).
ELSE.
ls_decision = can_win( is_current_gamestate = ls_test_gamestate
iv_divisor = iv_divisor
iv_win_if_divisible = abap_true
).
ENDIF.
IF ls_decision-i_win = abap_false. " Wenn der Gegner nicht gewinnen kann gewinne ich
rs_decision-i_win = abap_true. " I win this way
RETURN.
ENDIF.
ENDDO.
ENDIF.
ENDDO.
ELSE.
lv_num = is_current_gamestate.
IF lv_num mod iv_divisor = 0.
lv_is_divisible = abap_true.
ELSE.
lv_is_divisible = abap_false.
ENDIF.
* Wenn teilbar Gewinn genau dann, wenn das auch so in Übergabeparameter steht
IF lv_is_divisible = iv_win_if_divisible.
rs_decision-i_win = abap_true.
ELSE.
rs_decision-i_win = abap_false.
ENDIF.
ENDIF.
ENDMETHOD.
ENDCLASS.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
ewx