Archive

Posts Tagged ‘haskell’

The beginner’s mind

September 26, 2015 Leave a comment

I recently ran across CodinGame which is similar to CodeWars but is framed as a series of games you’re writing the logic for instead of textural problems to solve. Needless to say this appeals to me. Happily they support Haskell so I dove in and started in on the tutorial. Blammo! No problem. Onwards to problem #1 in the easy category… two hours later, I finally had a solution that worked. Now if this had been C# it would have been a 5 minute problem tops, but the point of this kind of exercise is precisely that I’m not working in something familiar. I’ve done a few of the katas on CodeWars in Haskell and while somewhat challenging, the easy problems were indeed pretty straightforward. This problem however was much more difficult because it was framed as an imperative problem and that has a much higher impedance mismatch with the normal way of doing things in Haskell. I figured this might be a good example to point out a very different way of approaching what should be a very easy to visualize problem.

CodinGame Thor Problem

So in this scenario, you have Thor who must be guided to the light of power. At the beginning of the program you’re provided with Thor’s starting x/y coordinates as well as the x/y of the light. You write to standard out a direction in the form of N, S, E, W, NW, NE, SE, SW over and over until Thor reaches the destination (or the run times out after 100 moves). So this seems pretty straightforward, you could keep track of your x/y, increment or decrement it as you move and each iteration compare where you are to the target and output the correct direction accordingly. Easy peasy.

I did start out somewhat in this vein, I knew I wasn’t going to be mutating a global variable but my idea was to have a function that took the current x/y and target x/y and did the comparison and outputted the correct direction. This is doable in Haskell just like in C# although you would use recursion instead of a while loop, but otherwise it’s pretty similar. Thinking about this problem for a moment though, I realized you actually know right from the start how many moves in each direction you will need. Using that you could construct the entire list of moves to get to the target and then all that’s left would be to iterate over that list printing out the values one at a time.

Here is the code listing (also in the screenshot at the bottom for reference). The structure of the functions and the all the input!!xyz lines were provided at the start of the problem.


import System.IO
import Control.Monad

main :: IO ()
main = do
  hSetBuffering stdout NoBuffering -- DO NOT REMOVE
 
  input_line <- getLine
  let input = words input_line
  let lightX = read (input!!0) :: Int -- the X position of the light of power
  let lightY = read (input!!1) :: Int -- the Y position of the light of power
  let initialTX = read (input!!2) :: Int -- Thor's starting X position
  let initialTY = read (input!!3) :: Int -- Thor's starting Y position
 
  -- my code starts here
  let startToTargetX = lightX - initialTX
  let targetToStartX = initialTX - lightX
  let startToTargetY = lightY - initialTY
  let targetToStartY = initialTY - lightY
 
  let count = max (max startToTargetY targetToStartY) (max startToTargetX targetToStartX)
  let vertical = replicate startToTargetY "S" ++ replicate targetToStartY "N" ++ repeat ""
  let horizontal = replicate startToTargetX "E" ++ replicate targetToStartX "W" ++ repeat ""
  let pairs = zip vertical horizontal
  let directions = map (uncurry (++)) (take count pairs)
 
  loop directions

loop :: [String] -> IO ()
loop [final] = putStrLn final
loop (current:rest) = do
  putStrLn current
  loop rest
loop [] = return ()

Each move is potentially some combination of vertical and horizontal movement and I did not know which direction would contain more moves, so I used Haskell’s lazyness to not have to care. I calculated the total number of moves to get to the target, which is simply the maximum of the horizontal and vertical distances. Then I built up a list of instructions for both directions and appended infinite empty strings to the end. I passed both the horizontal and vertical instruction lists into a zip function that paired them into an infinite list of tuples, and then I took from that list of tuples a number of elements equal to the count I had calculated earlier. Finally for each pair I took the two elements and combined them to build the final string using map. Uncurry for reference, takes a function that normally takes 2 params, and converts it to a function that takes a pair (tuple), that way I can use the standard ++ function with the tuple that got built up by zip.

The main loop function then was just responsible for printing out each value. The first case is matched when there is only one element in the list, which simply outputs that element. The second case is matched when there is at least 2 elements in the list. The final case of

loop [] = return ()

is there just to satisfy the compiler that can’t know that we will never call loop with an empty list, however it will never be executed in this solution.

CodinGame easy Haskell Problem

I’m not claiming this is the best way to do things in Haskell, but it seems vastly more idiomatic than copying an imperative style. Hopefully it’s interesting to see how different a solution can be when you’re working with a different toolbox.

Advertisements