fork download
  1. type InputParser(str: string) =
  2. static member Parse (str:string) =
  3. let inputs =
  4. List.ofArray (str.Split(' '))
  5. |> List.map (fun i ->
  6. match System.Int32.TryParse(i) with
  7. | (true, int) -> int
  8. | (false, int) -> invalidArg "inputs" "Input was not an integer.")
  9. let ticks = inputs.Head
  10. let movements = inputs.Tail
  11. ticks, movements
  12.  
  13. type ICombinationLock =
  14. interface
  15. abstract member Input: string
  16. abstract member Clicks: int
  17. end
  18.  
  19. type CombinationLock1(str: string) =
  20. let ticks, movements = InputParser.Parse(str)
  21.  
  22. let Clockwise dst (acc, src) =
  23. if src > dst then (acc + (ticks + dst - src), dst)
  24. elif src = dst then (acc + ticks, dst)
  25. elif dst > src then (acc + (dst - src), dst)
  26. else (acc, dst)
  27.  
  28. let CounterClockwise dst (acc, src) =
  29. if src > dst then (acc + (src - dst), dst)
  30. elif src = dst then (acc + ticks, dst)
  31. elif dst > src then (acc + (ticks + src - dst), dst)
  32. else (acc, dst)
  33.  
  34. let Spin (acc, src: int) = (acc + ticks, src)
  35.  
  36. let SpinClockwise (acc: int * int) (elem: int * int) =
  37. if snd acc <> snd elem then Spin acc else acc
  38. |> Clockwise (snd elem)
  39.  
  40. let SpinCounterClockwise (acc: int * int) (elem: int * int) =
  41. if snd acc <> snd elem then Spin acc else acc
  42. |> CounterClockwise (snd elem)
  43.  
  44. interface ICombinationLock with
  45. member this.Input = str
  46. member this.Clicks =
  47. let ApplyMovementIndex index elem = (index, elem)
  48. let HasEvenIndex elem = (fst elem % 2 = 0)
  49.  
  50. let indexedMovements = Seq.mapi ApplyMovementIndex ([0] @ movements)
  51.  
  52. let TurnDialAccumulateClicks acc elem =
  53. match elem with
  54. | (index, _) when index = 0 -> Clockwise (snd elem) acc
  55. | (index, _) when index = List.length movements && HasEvenIndex elem -> CounterClockwise (snd elem) acc
  56. | (index, _) when index = List.length movements -> Clockwise (snd elem) acc
  57. | _ when HasEvenIndex elem -> SpinCounterClockwise acc elem
  58. | _ -> SpinClockwise acc elem
  59.  
  60. (* The first movement is executed as goto 0 from 0, or a full spin.
  61.  
  62. Before any intermediate movements are made, a preliminary full spin is executed.
  63. The movement direction is determined by whether or not the movement index is even or odd.
  64. i.e. The 0th movement is even and counter-clockwise, the 1st movement is odd and clockwise.
  65.  
  66. The final movement is executed as a direct spin to the final destination without a preliminary full spin.
  67. The movement direction is determined by whether or not the movement index is even or odd.
  68. *)
  69.  
  70. let clicks = fst (Seq.fold TurnDialAccumulateClicks (0,0) indexedMovements)
  71. clicks
  72.  
  73. type CombinationLock2(str: string) =
  74. let ticks, movements = InputParser.Parse(str)
  75.  
  76. let f n x y z =
  77. let g a b = (n + b - a - 1) % n + 1 // (n + b - a - 1) gets the remainder, like Haskell's mod function appears to do
  78. let clicks = 3 * n + x + g y x + g y z
  79. if x = 0 then clicks - n else clicks // special handling to remove extra turn when the first movement is 0
  80.  
  81.  
  82. interface ICombinationLock with
  83. member this.Input = str
  84. member this.Clicks =
  85. f ticks movements.[0] movements.[1] movements.[2]
  86.  
  87. type CombinationLock3(str: string) =
  88. let ticks, movements = InputParser.Parse(str)
  89.  
  90. let Move src dst = (ticks + dst - src - 1) % ticks + 1
  91. // g a b = (n + b - a - 1) % n + 1
  92. let Spin = Move 0 0
  93. // 3 * n, as above in CombinationLock2, if executed 3 times will return 3 * ticks
  94. let Clockwise src dst = Move src dst
  95. // g y z, as above in CombinationLock2, a clockwise movement
  96. let CounterClockwise src dst = Move dst src
  97. // g y x, as above in CombinationLock2, a counter-clockwise movement
  98. // if ticks was set to 5 then we would find that 21 = spin + spin + cw 0 1 + spin + ccw 1 2 + cw 2 3
  99. let SpinClockwise src dst = if src <> dst then Spin + Clockwise src dst else Clockwise src dst
  100. let SpinCounterClockwise src dst = if src <> dst then Spin + CounterClockwise src dst else CounterClockwise src dst
  101.  
  102. interface ICombinationLock with
  103. member this.Input = str
  104. member this.Clicks =
  105. let ApplyMovementIndex index elem = (index, elem)
  106. let HasEvenIndex elem = (fst elem % 2 = 0)
  107.  
  108. let indexedMovements = List.mapi ApplyMovementIndex ([0] @ movements)
  109.  
  110. let TurnDialAccumulateClicks acc elem =
  111. let src = if fst elem > 0 then snd (indexedMovements.[fst elem - 1]) else 0
  112. let dst = snd elem
  113. let clicks =
  114. match elem with
  115. | (index, _) when index = 0 -> Clockwise src dst
  116. | (index, _) when index = List.length movements && HasEvenIndex elem -> CounterClockwise src dst
  117. | (index, _) when index = List.length movements -> Clockwise src dst
  118. | _ when HasEvenIndex elem -> SpinCounterClockwise src dst
  119. | _ -> SpinClockwise src dst
  120. acc + clicks
  121.  
  122. let clicks = List.fold TurnDialAccumulateClicks 0 indexedMovements
  123. clicks
  124.  
  125.  
  126. [<EntryPoint>]
  127. let main argv =
  128. let PrintLockInfo (lock: ICombinationLock) lockType =
  129. printfn "Number of clicks for input \"%s\" using %s was %d" lock.Input lockType lock.Clicks |> ignore
  130.  
  131. let PrintSuite input =
  132. PrintLockInfo (new CombinationLock1(input)) "CombinationLock1"
  133. PrintLockInfo (new CombinationLock2(input)) "CombinationLock2"
  134. PrintLockInfo (new CombinationLock3(input)) "CombinationLock3"
  135. printfn ""
  136.  
  137. PrintSuite "5 1 2 3"
  138. PrintSuite "5 0 0 0"
  139. PrintSuite "5 1 2 1"
  140. PrintSuite "5 3 2 1"
  141. PrintSuite "100 25 23 1"
  142. PrintLockInfo (new CombinationLock3("60 12 32 48 1 25 39 14 58")) "CombinationLock3"
  143. 0 // return an integer exit code
Success #stdin #stdout 0.12s 11984KB
stdin
Standard input is empty
stdout
Number of clicks for input "5 1 2 3" using CombinationLock1 was 21
Number of clicks for input "5 1 2 3" using CombinationLock2 was 21
Number of clicks for input "5 1 2 3" using CombinationLock3 was 21

Number of clicks for input "5 0 0 0" using CombinationLock1 was 20
Number of clicks for input "5 0 0 0" using CombinationLock2 was 20
Number of clicks for input "5 0 0 0" using CombinationLock3 was 20

Number of clicks for input "5 1 2 1" using CombinationLock1 was 24
Number of clicks for input "5 1 2 1" using CombinationLock2 was 24
Number of clicks for input "5 1 2 1" using CombinationLock3 was 24

Number of clicks for input "5 3 2 1" using CombinationLock1 was 23
Number of clicks for input "5 3 2 1" using CombinationLock2 was 23
Number of clicks for input "5 3 2 1" using CombinationLock3 was 23

Number of clicks for input "100 25 23 1" using CombinationLock1 was 405
Number of clicks for input "100 25 23 1" using CombinationLock2 was 405
Number of clicks for input "100 25 23 1" using CombinationLock3 was 405

Number of clicks for input "60 12 32 48 1 25 39 14 58" using CombinationLock3 was 716