fork download
  1. # frozen_string_literal: true
  2.  
  3. class Slide
  4. attr_accessor :commands
  5.  
  6. def initialize(field, commands)
  7. commands = optimise(commands.gsub(/[LR]+(?<m>[LR])|[UD]+(?<m>[UD])/, '\k<m>'))
  8. @commands = {
  9. initial: commands[0].to_s,
  10. direction: ('LDRUL'.include?(commands[0, 2]) ? :+ : :-),
  11. size: commands.size
  12. }
  13. @angle = ('ULDR'.index(@commands[:initial]) - (clockwise? ? 1 : 0) ) % 4
  14. @field = normalize(field)
  15. @width = field.index(/\n|\z/)
  16. @height = @field.size
  17. @initial_state = Marshal.load(Marshal.dump(@field)).freeze
  18. end
  19.  
  20. def to_s
  21. return @field.map(&:join).join($/) if @commands[:size] < 2
  22. ary = @field.map { |s| s.ljust(@width, ?.).split('') }
  23. upright(ary)
  24. ary.map(&:join).join($/)
  25. end
  26.  
  27. def upright(ary)
  28. return if @angle == 0
  29. ary.reverse! if @angle <= 2
  30. ary.each(&:reverse!) if @angle >= 2
  31. end
  32.  
  33. def clockwise?
  34. @commands[:direction] == :-
  35. end
  36.  
  37. def optimise(dirs)
  38. nil while dirs.gsub!(/(?<m>.).\k<m>(?=.)|(?<m>(?<n>.).)\k<n>\z/, '\k<m>')
  39. dirs
  40. end
  41.  
  42. def normalize(field)
  43. ary = field.split.map { |s| s.split('') }
  44. f = ->(a) { a.select { |c| c != ?. }.join }
  45. return ary if @commands[:size] == 0
  46. incline_ary(ary, @commands[:initial])
  47. return ary if @commands[:size] == 1
  48. rotate_ary(ary, clockwise? ? :+ : :-)
  49. incline_ary(ary, @commands[:initial])
  50. rotate_ary(ary, @commands[:direction])
  51. upright(ary)
  52. ary.map(&f)
  53. end
  54.  
  55. def incline_ary(ary, direction)
  56. rotate_ary(ary, :+) if %w[U D].include?(direction)
  57. f = case direction
  58. when ?R, ?D then ->c{c == ?.}
  59. when ?L, ?U then ->c{c != ?.}
  60. end
  61. ary.replace(ary.map { |a| a.partition(&f).flatten })
  62. %w[U D].include?(direction) ? rotate_ary(ary, :-) : ary
  63. end
  64.  
  65. def rotate_ary(ary, dir = :+)
  66. a = ary.transpose
  67. case dir
  68. when :+ then ary.replace(a.reverse)
  69. when :- then ary.replace(a.map(&:reverse))
  70. end
  71. end
  72.  
  73. def incline_all(count = @commands[:size] - 2)
  74. f = %w[L R].include?(@commands[:initial]) ? ->(a) { a.even? } : ->(a) { a.odd? }
  75. angle = clockwise? ? -1 : 1
  76. count.times do |i|
  77. f.call(i) ? inversion_h : inversion_v
  78. @angle = (@angle + angle) % 4
  79. break(incline_all((count - i - 1) % (i + 1))) if @field == @initial_state
  80. end
  81. end
  82.  
  83. def inversion_h
  84. @field.map(&:reverse!)
  85. end
  86.  
  87. def inversion_v
  88. verticals = @width.times.map { |i| @field.map { |s| s[i] }.compact }
  89. @field = @height.times.map { |i| verticals.map { |s| s[~i]}.join }
  90. end
  91. end
  92.  
  93. samples = [[<<~EOT, ''], [<<~EOT, 'U'], [<<~EOT, 'UR'], [<<~EOT, 'LRRLLUDRRLRLRRURRRRLLLRULDUDULDLLRDULURULUDLDLUDDL'], [<<~EOT, 'LURD'*10**6]]
  94. ...
  95. .a.
  96. ...
  97. EOT
  98. ...
  99. ...
  100. .a.
  101. EOT
  102. ...
  103. .a.
  104. ...
  105. EOT
  106. .....
  107. .d.i.
  108. kmegk
  109. ..tu.
  110. EOT
  111. .....
  112. .d.i.
  113. kmegk
  114. ..tu.
  115. EOT
  116.  
  117. samples.each do |field, commands|
  118. pre = Time.now
  119. s = Slide.new(field, commands)
  120. s.incline_all
  121. singular = commands.size == 1 ? ['', 'is'] : ['s', 'are']
  122. puts "%s\n%d command%s (%s%s) %s reduced to %d\n%s=>\n%s\n# %f sec" % [
  123. ?-*70, commands.size, singular.first, commands[0, 20],
  124. commands.size > 20 ? '...' : '', singular.last,
  125. s.commands[:size], field, s, Time.now - pre
  126. ]
  127. end
  128.  
Success #stdin #stdout 0.7s 13780KB
stdin
Standard input is empty
stdout
----------------------------------------------------------------------
0 commands () are reduced to 0
...
.a.
...
=>
...
.a.
...
# 0.000189 sec
----------------------------------------------------------------------
1 command (U) is reduced to 1
...
...
.a.
=>
.a.
...
...
# 0.000105 sec
----------------------------------------------------------------------
2 commands (UR) are reduced to 2
...
.a.
...
=>
..a
...
...
# 0.000104 sec
----------------------------------------------------------------------
50 commands (LRRLLUDRRLRLRRURRRRL...) are reduced to 10
.....
.d.i.
kmegk
..tu.
=>
.....
gk...
km...
dietu
# 0.000321 sec
----------------------------------------------------------------------
4000000 commands (LURDLURDLURDLURDLURD...) are reduced to 4000000
.....
.d.i.
kmegk
..tu.
=>
.....
...gk
...km
dietu
# 0.683618 sec