Advertisement
Guest User

AoC-2017-10

a guest
Dec 10th, 2017
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 9.02 KB | None | 0 0
  1. #! /usr/bin/ruby
  2.  
  3. # --- Day 10: Knot Hash ---
  4. #
  5. # You come across some programs that are trying to implement a software
  6. # emulation of a hash based on knot-tying. The hash these programs are
  7. # implementing isn't very strong, but you decide to help them anyway. You make
  8. # a mental note to remind the Elves later not to invent their own cryptographic
  9. # functions.
  10. #
  11. # This hash function simulates tying a knot in a circle of string with 256
  12. # marks on it. Based on the input to be hashed, the function repeatedly selects
  13. # a span of string, brings the ends together, and gives the span a half-twist
  14. # to reverse the order of the marks within it. After doing this many times, the
  15. # order of the marks is used to build the resulting hash.
  16. #      4--5   pinch   4  5           4   1
  17. #     /    \  5,0,1  / \/ \  twist  / \ / \
  18. #    3      0  -->  3      0  -->  3   X   0
  19. #     \    /         \ /\ /         \ / \ /
  20. #      2--1           2  1           2   5
  21. #
  22. # To achieve this, begin with a list of numbers from 0 to 255, a current
  23. # position which begins at 0 (the first element in the list), a skip size
  24. # (which starts at 0), and a sequence of lengths (your puzzle input).  Then,
  25. # for each length:
  26. #  - Reverse the order of that length of elements in the list, starting with
  27. #    the element at the current position.
  28. #  - Move the current position forward by that length plus the skip size.
  29. #  - Increase the skip size by one.
  30. #
  31. # The list is circular; if the current position and the length try to reverse
  32. # elements beyond the end of the list, the operation reverses using as many
  33. # extra elements as it needs from the front of the list. If the current
  34. # position moves past the end of the list, it wraps around to the front.
  35. # Lengths larger than the size of the list are invalid.
  36. #
  37. # Here's an example using a smaller list:
  38. #
  39. # Suppose we instead only had a circular list containing five elements, 0, 1,
  40. # 2, 3, 4, and were given input lengths of 3, 4, 1, 5.
  41. #  - The list begins as [0] 1 2 3 4 (where square brackets indicate the
  42. #    current position).
  43. #  - The first length, 3, selects ([0] 1 2) 3 4 (where parentheses indicate
  44. #    the sublist to be reversed).
  45. #  - After reversing that section (0 1 2 into 2 1 0), we get ([2] 1 0) 3 4.
  46. #  - Then, the current position moves forward by the length, 3, plus the skip
  47. #    size, 0: 2 1 0 [3] 4. Finally, the skip size increases to 1.
  48. #  - The second length, 4, selects a section which wraps: 2 1) 0 ([3] 4.
  49. #  - The sublist 3 4 2 1 is reversed to form 1 2 4 3: 4 3) 0 ([1] 2.
  50. #  - The current position moves forward by the length plus the skip size, a
  51. #    total of 5, causing it not to move because it wraps around: 4 3 0 [1] 2.
  52. #    The skip size increases to 2.
  53. #  - The third length, 1, selects a sublist of a single element, and so
  54. #    reversing it has no effect.
  55. #  - The current position moves forward by the length (1) plus the skip size
  56. #    (2): 4 [3] 0 1 2. The skip size increases to 3.
  57. #  - The fourth length, 5, selects every element starting with the second: 4)
  58. #    ([3] 0 1 2. Reversing this sublist (3 0 1 2 4 into 4 2 1 0 3) produces:
  59. #    3) ([4] 2 1 0.
  60. #  - Finally, the current position moves forward by 8: 3 4 2 1 [0]. The skip
  61. #    size increases to 4.
  62. #
  63. # In this example, the first two numbers in the list end up being 3 and 4; to
  64. # check the process, you can multiply them together to produce 12.
  65. #
  66. # However, you should instead use the standard list size of 256 (with values 0
  67. # to 255) and the sequence of lengths in your puzzle input. Once this process
  68. # is complete, what is the result of multiplying the first two numbers in the
  69. # list?
  70. #
  71. # --- Part Two ---
  72. #
  73. # The logic you've constructed forms a single round of the Knot Hash algorithm;
  74. # running the full thing requires many of these rounds. Some input and output
  75. # processing is also required.
  76. #
  77. # First, from now on, your input should be taken not as a list of numbers, but
  78. # as a string of bytes instead. Unless otherwise specified, convert characters
  79. # to bytes using their ASCII codes. This will allow you to handle arbitrary
  80. # ASCII strings, and it also ensures that your input lengths are never larger
  81. # than 255. For example, if you are given 1,2,3, you should convert it to the
  82. # ASCII codes for each character: 49,44,50,44,51.
  83. #
  84. # Once you have determined the sequence of lengths to use, add the following
  85. # lengths to the end of the sequence: 17, 31, 73, 47, 23. For example, if you
  86. # are given 1,2,3, your final sequence of lengths should be
  87. # 49,44,50,44,51,17,31,73,47,23 (the ASCII codes from the input string combined
  88. # with the standard length suffix values).
  89. #
  90. # Second, instead of merely running one round like you did above, run a total
  91. # of 64 rounds, using the same length sequence in each round. The current
  92. # position and skip size should be preserved between rounds. For example, if
  93. # the previous example was your first round, you would start your second round
  94. # with the same length sequence (3, 4, 1, 5, 17, 31, 73, 47, 23, now assuming
  95. # they came from ASCII codes and include the suffix), but start with the
  96. # previous round's current position (4) and skip size (4).
  97. #
  98. # Once the rounds are complete, you will be left with the numbers from 0 to 255
  99. # in some order, called the sparse hash. Your next task is to reduce these to a
  100. # list of only 16 numbers called the dense hash. To do this, use numeric
  101. # bitwise XOR to combine each consecutive block of 16 numbers in the sparse
  102. # hash (there are 16 such blocks in a list of 256 numbers). So, the first
  103. # element in the dense hash is the first sixteen elements of the sparse hash
  104. # XOR'd together, the second element in the dense hash is the second sixteen
  105. # elements of the sparse hash XOR'd together, etc.
  106. #
  107. # For example, if the first sixteen elements of your sparse hash are as shown
  108. # below, and the XOR operator is ^, you would calculate the first output number
  109. # like this:
  110. #    65 ^ 27 ^ 9 ^ 1 ^ 4 ^ 3 ^ 40 ^ 50 ^ 91 ^ 7 ^ 6 ^ 0 ^ 2 ^ 5 ^ 68 ^ 22 = 64
  111. #
  112. # Perform this operation on each of the sixteen blocks of sixteen numbers in
  113. # your sparse hash to determine the sixteen numbers in your dense hash.
  114. #
  115. # Finally, the standard way to represent a Knot Hash is as a single hexadecimal
  116. # string; the final output is the dense hash in hexadecimal notation. Because
  117. # each number in your dense hash will be between 0 and 255 (inclusive), always
  118. # represent each number as two hexadecimal digits (including a leading zero as
  119. # necessary). So, if your first three numbers are 64, 7, 255, they correspond
  120. # to the hexadecimal numbers 40, 07, ff, and so the first six characters of the
  121. # hash would be 4007ff. Because every Knot Hash is sixteen such numbers, the
  122. # hexadecimal representation is always 32 hexadecimal digits (0-f) long.
  123. #
  124. # Here are some example hashes:
  125. #  - The empty string becomes a2582a3a0e66e6e86e3812dcb672a272.
  126. #  - AoC 2017 becomes 33efeb34ea91902bb2f59c9920caa6cd.
  127. #  - 1,2,3 becomes 3efbe78a8d82f29979031a4aa0b16a9d.
  128. #  - 1,2,4 becomes 63960835bcdc130f0b66d7ff4f6a5a8e.
  129. #
  130. # Treating your puzzle input as a string of ASCII characters, what is the Knot
  131. # Hash of your puzzle input? Ignore any leading or trailing whitespace you
  132. # might encounter.
  133.  
  134. require_relative 'Framework'
  135. require 'pp'
  136.  
  137. EXAMPLE_INPUT = {count: 5, data: '3,4,1,5'}
  138.  
  139. INPUT = {
  140.   count: 256,
  141.   data: '46,41,212,83,1,255,157,65,139,52,39,254,2,86,0,204'
  142. }
  143.  
  144. INPUT_SUFFIX = [17, 31, 73, 47, 23]
  145.  
  146. def knot_hash_once(kh_data)
  147.   kh_data[:lengths].each do |l|
  148.     new_values = (kh_data[:list] * 2)[kh_data[:current_position], l].reverse
  149.     (0...l).each do |i|
  150.       kh_data[:list][(kh_data[:current_position] + i) % kh_data[:list].length] = new_values[i]
  151.     end
  152.     kh_data[:current_position] += l + kh_data[:skip_size]
  153.     kh_data[:current_position] %= kh_data[:list].length
  154.     kh_data[:skip_size] = (kh_data[:skip_size] + 1) % kh_data[:list].length
  155.   end
  156. end
  157.  
  158. def simulate_crypto(input)
  159.   data = {
  160.     list: (0...input[:count]).to_a,
  161.     lengths: input[:data].split(',').map { |s| s.to_i },
  162.     current_position: 0,
  163.     skip_size: 0
  164.   }
  165.   knot_hash_once(data)
  166.   return data[:list][0] * data[:list][1]
  167. end
  168.  
  169. def encrypt(input)
  170.   data = {
  171.     list: (0...256).to_a,
  172.     lengths: input.chars.map { |c| c.ord } + INPUT_SUFFIX,
  173.     current_position: 0,
  174.     skip_size: 0
  175.   }
  176.   64.times { knot_hash_once(data) }
  177.   result = data[:list].each_slice(16).map do |group|
  178.     chars = group.reduce(0){ |r, v| r^=v }.to_s(16)
  179.     if chars.length == 1 then
  180.       chars = '0' + chars
  181.     end
  182.     chars
  183.   end.join
  184.   return result
  185. end
  186.  
  187. do_it({ ordinal: 'One',
  188.         function_name: method(:simulate_crypto),
  189.         examples: {EXAMPLE_INPUT => 12},
  190.         input: INPUT })
  191. puts
  192. do_it({ ordinal: 'Two',
  193.         function_name: method(:encrypt),
  194.         examples: { '' => 'a2582a3a0e66e6e86e3812dcb672a272',
  195.                     'AoC 2017' => '33efeb34ea91902bb2f59c9920caa6cd',
  196.                     '1,2,3' => '3efbe78a8d82f29979031a4aa0b16a9d',
  197.                     '1,2,4' => '63960835bcdc130f0b66d7ff4f6a5a8e'},
  198.         input: INPUT[:data] })
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement