#!/usr/bin/env stack
{- stack
--install-ghc
exec ghci
--package lens
-}
module Main where
import Control.Lens
import Data.List.Lens
import Debug.Trace
import Text.Printf
import Prelude hiding (Left, Right)
data Direction = Up | Left | Down | Right
data Location = Location Int Int
type Grid = [[Int]]
main = printClockwise 25
printClockwise :: Int -> IO ()
printClockwise n = putStrLn $ concatMap printRow grid
where
grid = walk directions center 1 emptyGrid
emptyGrid = replicate sq (take sq (repeat 0))
printRow row = concatMap (printf "%4s" . show) row ++ "\n"
-- Ex. for a 5x5 spiral the start location is (2,2)
center = Location half half
half = floor $ (fromIntegral sq) / 2
-- This will return all directions we should walk through the spiral in.
-- Something like [Right, Down, Left, Left, Up, Up, Right, Right, Right]
directions = concatMap (\(direction, steps) -> take steps $ repeat direction) (zip directionOrder steps)
directionOrder = cycle [Right, Down, Left, Up]
-- Ex. for a spiral up to 25, the steps are 1, 1, 2, 2, 3, 3, 4, 4, 5
steps = concatMap (take 2 . repeat) [1 .. sq - 1] <> [sq]
-- Assume the input is a perfect square of an odd number
sq = ceiling $ sqrt (fromIntegral n)
walk :: [Direction] -> Location -> Int -> Grid -> Grid
walk (dir : dirs) loc@(Location x y) current grid = do
let grid' = grid & (ix y . ix x) .~ current
let loc' = moveLocation dir loc
walk dirs loc' (current + 1) grid'
walk [] _ _ numbers = numbers
moveLocation Down (Location x y) = Location (x) (y + 1)
moveLocation Right (Location x y) = Location (x + 1) (y)
moveLocation Left (Location x y) = Location (x -1) (y)
moveLocation Up (Location x y) = Location (x) (y - 1)
I came across this problem on reddit and thought it'd be fun to try my hand at it in Haskell. The idea is to generate the grid before printing, and the insight to generate that grid was that the movement follows a predictable pattern, of right, down, left, up, and the # of steps taken in a given direction is 1, 1, 2, 2, 3, 3, 4, 4... , n - 1, n -1, n , where n is the square of the input number. So with that you can walk through the grid and fill in the numbers.
The function has no input checking, it assumes the input is a perfect square (of an odd number).