-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathymbh_aoc_day16.prog.abap
528 lines (424 loc) · 15.1 KB
/
ymbh_aoc_day16.prog.abap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
REPORT ymbh_aoc_day16.
INTERFACE if_ticket.
TYPES: BEGIN OF s_numbers,
number TYPE i,
locations TYPE STANDARD TABLE OF i WITH DEFAULT KEY,
END OF s_numbers.
TYPES t_numbers TYPE STANDARD TABLE OF s_numbers WITH DEFAULT KEY.
METHODS build_numbers
IMPORTING
i_input_values TYPE string.
METHODS get_numbers
RETURNING
VALUE(r_numbers) TYPE t_numbers.
METHODS get_class
RETURNING
VALUE(r_class) TYPE text20.
METHODS valid
RETURNING
VALUE(r_valid) TYPE abap_bool.
METHODS add_invalid_number
IMPORTING
i_number TYPE i.
METHODS get_invalid_sum
RETURNING
VALUE(r_sum) TYPE i.
METHODS set_numbers
IMPORTING
i_numbers TYPE if_ticket=>t_numbers.
METHODS set_is_valid
IMPORTING
i_is_valid TYPE abap_bool.
ENDINTERFACE.
INTERFACE if_fields.
TYPES: BEGIN OF s_range,
field_nr TYPE i,
field_name TYPE text100,
range TYPE RANGE OF i,
END OF s_range.
TYPES t_range TYPE STANDARD TABLE OF s_range WITH DEFAULT KEY.
METHODS number_in_range
IMPORTING
i_value TYPE i
RETURNING
VALUE(r_in_range) TYPE abap_bool.
METHODS get_range
RETURNING
VALUE(r_ranges) TYPE if_fields=>t_range.
METHODS get_valid_fields_nr
IMPORTING
i_number TYPE i
RETURNING
VALUE(r_fields) TYPE if_ticket=>t_numbers.
ENDINTERFACE.
INTERFACE if_ticket_collection.
TYPES: BEGIN OF s_ticket,
ticket TYPE REF TO if_ticket,
END OF s_ticket.
TYPES t_tickets TYPE STANDARD TABLE OF s_ticket WITH DEFAULT KEY.
METHODS get_tickets
RETURNING
VALUE(r_result) TYPE if_ticket_collection=>t_tickets.
ENDINTERFACE.
CLASS fields DEFINITION FINAL.
PUBLIC SECTION.
INTERFACES if_fields.
METHODS constructor
IMPORTING
i_input_values TYPE stringtab.
PRIVATE SECTION.
DATA ranges TYPE if_fields=>t_range.
ENDCLASS.
CLASS fields IMPLEMENTATION.
METHOD constructor.
LOOP AT i_input_values ASSIGNING FIELD-SYMBOL(<val>).
DATA(line_index) = sy-tabix.
IF <val> CS 'your ticket:'.
EXIT.
ENDIF.
CHECK <val> IS NOT INITIAL.
SPLIT <val> AT ':' INTO DATA(name) DATA(full_range).
CONDENSE name.
SPLIT full_range AT 'or' INTO DATA(range_1) DATA(range_2).
SPLIT range_1 AT '-' INTO DATA(low_1) DATA(high_1).
SPLIT range_2 AT '-' INTO DATA(low_2) DATA(high_2).
CONDENSE: low_1, high_1, low_2, high_2.
ranges = VALUE #( BASE ranges ( field_nr = line_index
field_name = name
range = VALUE #( ( sign = 'I' option = 'BT' low = low_1 high = high_1 )
( sign = 'I' option = 'BT' low = low_2 high = high_2 ) ) ) ).
ENDLOOP.
ENDMETHOD.
METHOD if_fields~get_range.
r_ranges = ranges.
ENDMETHOD.
METHOD if_fields~number_in_range.
LOOP AT ranges INTO DATA(line).
IF i_value IN line-range.
r_in_range = abap_true.
RETURN.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD if_fields~get_valid_fields_nr.
DATA locations TYPE STANDARD TABLE OF i WITH DEFAULT KEY.
LOOP AT ranges INTO DATA(line).
locations = VALUE #( BASE locations ( COND #( WHEN i_number IN line-range THEN line-field_nr
ELSE 0 ) ) ).
ENDLOOP.
r_fields = VALUE #( BASE r_fields ( number = i_number
locations = locations ) ).
ENDMETHOD.
ENDCLASS.
CLASS ticket DEFINITION FINAL.
PUBLIC SECTION.
INTERFACES if_ticket.
METHODS constructor
IMPORTING
i_ticket_class TYPE text15.
PRIVATE SECTION.
DATA numbers TYPE if_ticket=>t_numbers.
DATA invalid_numbers TYPE if_ticket=>t_numbers.
DATA ticket_class TYPE text20.
DATA state TYPE char10.
DATA is_valid TYPE abap_bool.
ENDCLASS.
CLASS ticket IMPLEMENTATION.
METHOD constructor.
ticket_class = i_ticket_class.
is_valid = abap_true.
ENDMETHOD.
METHOD if_ticket~get_numbers.
r_numbers = numbers.
ENDMETHOD.
METHOD if_ticket~valid.
r_valid = is_valid.
ENDMETHOD.
METHOD if_ticket~build_numbers.
SPLIT i_input_values AT ',' INTO TABLE DATA(raw_numbers).
numbers = VALUE #( FOR raw_number IN raw_numbers
( number = CONV i( raw_number ) ) ) .
ENDMETHOD.
METHOD if_ticket~get_class.
r_class = ticket_class.
ENDMETHOD.
METHOD if_ticket~add_invalid_number.
invalid_numbers = VALUE #( BASE invalid_numbers ( number = i_number ) ).
ENDMETHOD.
METHOD if_ticket~get_invalid_sum.
r_sum = REDUCE #( INIT invalids = 0
FOR line IN invalid_numbers
NEXT invalids = invalids + line-number ).
ENDMETHOD.
METHOD if_ticket~set_numbers.
me->numbers = i_numbers.
ENDMETHOD.
METHOD if_ticket~set_is_valid.
me->is_valid = i_is_valid.
ENDMETHOD.
ENDCLASS.
CLASS ticket_collection DEFINITION FINAL.
PUBLIC SECTION.
INTERFACES if_ticket_collection.
METHODS constructor
IMPORTING
i_input_values TYPE stringtab.
METHODS check_ticket_validity.
METHODS get_invalid_ticket_sum
RETURNING
VALUE(r_result) TYPE i.
METHODS get_multiply_fields
RETURNING
VALUE(r_result) TYPE i.
PRIVATE SECTION.
DATA tickets TYPE if_ticket_collection=>t_tickets.
DATA state TYPE text15.
DATA fields TYPE REF TO if_fields.
ENDCLASS.
CLASS ticket_collection IMPLEMENTATION.
METHOD constructor.
fields = NEW fields( i_input_values ).
DATA ticket TYPE REF TO if_ticket.
LOOP AT i_input_values INTO DATA(value).
IF value CS 'your ticket'.
state = 'Personal ticket'.
CONTINUE.
ELSEIF value CS 'nearby ticket'.
state = 'Nearby tickets'.
CONTINUE.
ENDIF.
IF value IS INITIAL AND state IS NOT INITIAL.
CLEAR state.
CONTINUE.
ENDIF.
IF state IS NOT INITIAL.
ticket = NEW ticket( state ).
ticket->build_numbers( value ).
tickets = VALUE #( BASE tickets ( ticket = ticket ) ).
FREE ticket.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD if_ticket_collection~get_tickets.
r_result = me->tickets.
ENDMETHOD.
METHOD check_ticket_validity.
LOOP AT tickets INTO DATA(ticket).
IF ticket-ticket->get_class( ) = 'Personal ticket'.
CONTINUE.
ENDIF.
DATA(numbers) = ticket-ticket->get_numbers( ).
LOOP AT numbers INTO DATA(number).
IF fields->number_in_range( number-number ) = abap_false.
ticket-ticket->add_invalid_number( number-number ).
ELSE.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
METHOD get_invalid_ticket_sum.
r_result = REDUCE #( INIT invalids = 0
FOR ticket IN tickets
NEXT invalids = invalids + ticket-ticket->get_invalid_sum( ) ).
ENDMETHOD.
METHOD get_multiply_fields.
LOOP AT tickets INTO DATA(ticket).
DATA(numbers) = ticket-ticket->get_numbers( ).
LOOP AT numbers ASSIGNING FIELD-SYMBOL(<number>).
DATA(valid_fields) = fields->get_valid_fields_nr( <number>-number ).
<number>-locations = valid_fields[ number = <number>-number ]-locations.
IF <number>-locations IS INITIAL.
ticket-ticket->set_is_valid( abap_false ).
ENDIF.
ENDLOOP.
ticket-ticket->set_numbers( numbers ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
CLASS ltc_fields DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA cut TYPE REF TO fields.
DATA input_values TYPE stringtab.
METHODS setup.
METHODS build_fields_ranges FOR TESTING.
METHODS get_valid_value_from_range FOR TESTING.
METHODS get_invalid_value_of_range FOR TESTING.
METHODS get_names_of_valid_fields FOR TESTING.
ENDCLASS.
CLASS ltc_fields IMPLEMENTATION.
METHOD setup.
input_values = VALUE #(
( |departure location: 45-535 or 550-961| )
( |departure station: 45-278 or 294-974| )
( |departure platform: 46-121 or 138-965| )
( |departure track: 38-149 or 173-949| )
( |departure date: 34-223 or 248-957| )
( |departure time: 32-64 or 79-952| )
( |arrival location: 49-879 or 905-968| )
( |arrival station: 47-306 or 323-973| )
( |arrival platform: 46-823 or 834-971| )
( |arrival track: 30-464 or 486-963| )
( |class: 40-350 or 372-965| )
( |duration: 47-414 or 423-950| )
( |price: 45-507 or 526-956| )
( |route: 42-779 or 799-970| )
( |row: 26-865 or 872-955| )
( |seat: 43-724 or 739-970| )
( |train: 25-914 or 926-958| )
( |type: 33-205 or 218-965| )
( |wagon: 43-101 or 118-951| )
( |zone: 45-844 or 858-970| )
( || )
( |your ticket:| )
( |173,191,61,199,101,179,257,79,193,223,139,97,83,197,251,53,89,149,181,59| )
( || )
( |nearby tickets:| )
( |949,764,551,379,767,144,556,835,638,591,653,872,198,825,690,527,260,396,873,333| )
( |438,627,99,622,408,671,695,561,695,121,706,144,55,985,566,706,255,595,680,407| )
( |879,876,665,928,874,436,766,328,620,267,995,54,430,503,86,936,489,305,64,688| )
( |613,812,756,258,341,765,91,551,859,379,447,842,148,501,293,766,93,532,939,406| )
( |349,340,670,248,813,557,249,949,506,656,100,19,204,409,944,659,777,843,712,801 | ) ).
cut = NEW #( input_values ).
ENDMETHOD.
METHOD build_fields_ranges.
cl_abap_unit_assert=>assert_equals(
exp = 20
act = lines( cut->if_fields~get_range( ) )
msg = |The range table shoudl have 20 lines.| ).
ENDMETHOD.
METHOD get_valid_value_from_range.
cl_abap_unit_assert=>assert_true(
act = cut->if_fields~number_in_range( 123 ) ).
ENDMETHOD.
METHOD get_invalid_value_of_range.
cl_abap_unit_assert=>assert_false(
act = cut->if_fields~number_in_range( 975 ) ).
ENDMETHOD.
METHOD get_names_of_valid_fields.
input_values = VALUE #( ( |class: 0-1 or 4-19| )
( |row: 0-5 or 8-19| )
( |seat: 0-13 or 16-19| )
( || )
( |your ticket:| )
( |11,12,13| )
( || )
( |nearby tickets:| )
( |3,9,18| )
( |15,1,5| )
( |5,14,9| ) ).
cut = NEW #( input_values ).
DATA(valid_fields) = cut->if_fields~get_valid_fields_nr( 15 ).
cl_abap_unit_assert=>assert_equals(
exp = 2
act = lines( valid_fields[ 1 ]-locations )
msg = |The number 15 should be valid in 2 fields.| ).
ENDMETHOD.
ENDCLASS.
CLASS ltc_ticket DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA cut TYPE REF TO ticket.
DATA input_values TYPE string.
METHODS setup.
METHODS build_ticket_object_w_entries FOR TESTING.
METHODS check_number_is_valid FOR TESTING.
METHODS get_correct_invalid_numbers FOR TESTING.
ENDCLASS.
CLASS ltc_ticket IMPLEMENTATION.
METHOD setup.
input_values = |173,191,61,199,101,179,257,79,193,223,139,97,83,197,251,53,89,149,181,59|.
cut = NEW #( 'your ticket' ).
cut->if_ticket~build_numbers( input_values ).
ENDMETHOD.
METHOD build_ticket_object_w_entries.
cl_abap_unit_assert=>assert_equals(
exp = 20
act = lines( cut->if_ticket~get_numbers( ) )
msg = |There should be 20 numbers in the ticket.| ).
ENDMETHOD.
METHOD check_number_is_valid.
cl_abap_unit_assert=>assert_false(
act = cut->if_ticket~valid( )
msg = |Object should be bound.| ).
ENDMETHOD.
METHOD get_correct_invalid_numbers.
cut->if_ticket~add_invalid_number( 20 ).
cut->if_ticket~add_invalid_number( 10 ).
cut->if_ticket~add_invalid_number( 23 ).
cl_abap_unit_assert=>assert_equals(
exp = 53
act = cut->if_ticket~get_invalid_sum( )
msg = |The sum of invalid numbers should be 53.| ).
ENDMETHOD.
ENDCLASS.
CLASS ltc_ticket_collection DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA cut TYPE REF TO ticket_collection.
DATA input_values TYPE stringtab.
METHODS setup.
METHODS build_tickets FOR TESTING.
METHODS sum_upinvalid_tickets_count FOR TESTING.
METHODS collect_locations_for_numbers FOR TESTING.
ENDCLASS.
CLASS ltc_ticket_collection IMPLEMENTATION.
METHOD setup.
input_values = VALUE #( ( |class: 1-3 or 5-7| )
( |row: 6-11 or 33-44| )
( |seat: 13-40 or 45-50| )
( || )
( |your ticket:| )
( |7,1,14| )
( || )
( |nearby tickets:| )
( |7,3,47| )
( |40,4,50| )
( |55,2,20| )
( |38,6,12| ) ).
cut = NEW #( input_values ).
ENDMETHOD.
METHOD build_tickets.
cl_abap_unit_assert=>assert_equals(
exp = 5
act = lines( cut->if_ticket_collection~get_tickets( ) )
msg = 'msg' ).
ENDMETHOD.
METHOD sum_upinvalid_tickets_count.
cut->check_ticket_validity( ).
cl_abap_unit_assert=>assert_equals(
exp = 71
act = cut->get_invalid_ticket_sum( )
msg = |The invalid ticket count should be 71.| ).
ENDMETHOD.
METHOD collect_locations_for_numbers.
input_values = VALUE #( ( |class: 0-1 or 4-19| )
( |row: 0-5 or 8-19| )
( |seat: 0-13 or 16-19| )
( || )
( |your ticket:| )
( |11,12,13| )
( || )
( |nearby tickets:| )
( |3,9,18| )
( |15,1,5| )
( |5,14,9| )
( |23,45,33| ) ).
cut = NEW #( input_values ).
cut->get_multiply_fields( ).
cl_abap_unit_assert=>assert_equals(
exp = 1716
act = cut->get_multiply_fields( )
msg = 'msg' ).
ENDMETHOD.
ENDCLASS.
DATA input TYPE text1024.
SELECT-OPTIONS: so_input FOR input NO INTERVALS.
START-OF-SELECTION.
DATA(input_values) = VALUE stringtab( FOR <line> IN so_input ( CONV #( <line>-low ) ) ).
DATA(ticket_collection) = NEW ticket_collection( input_values ).
ticket_collection->check_ticket_validity( ).
WRITE / |The result for part 1 is: { ticket_collection->get_invalid_ticket_sum( ) }|.