AoC Day 22: Sporifica Virus Part 1, a Solution in Beginner's Haskell

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
2
down vote
favorite
Until recently, I was very much only devoted to imperative languages (mainly C++ and C, to be precise), when I decided to venture into unknown waters by picking up a new, completely different language, which happened to be Haskell, a decision which happened to be influenced by the fact that I owned a copy of "Learn You a Haskell for Great Good!", a book which I very much enjoyed learning from.
Some days ago, I finished said book, and, wanting to apply my newly acquired knowledge, ventured our to find some programming exercises. I quickly remembered Advent of Code, which offers a whole pre-Christmas period's worth of easy to mildly difficult programming problems, a few of which I had already solved during the holidays.
I skimmed through the exercises, looking for one simple enough to be conquerable with my still very inadequate and shaky Haskell skills, and finally chose the task of Day 22.
Problem Description
Diagnostics indicate that the local grid computing cluster has been
contaminated with the Sporifica Virus. The grid computing cluster is a
seemingly-infinite two-dimensional grid of compute nodes. Each node is
either clean or infected by the virus.
To prevent overloading the nodes (which would render them useless to
the virus) or detection by system administrators, exactly one virus
carrier moves through the network, infecting or cleaning nodes as it
moves. The virus carrier is always located on a single node in the
network (the current node) and keeps track of the direction it is
facing.
To avoid detection, the virus carrier works in bursts; in each burst,
it wakes up, does some work, and goes back to sleep. The following
steps are all executed in order one time each burst:
- If the current node is infected, it turns to its right. Otherwise, it
turns to its left. (Turning is done in-place; the current node does
not change.)
- If the current node is clean, it becomes infected.
Otherwise, it becomes cleaned. (This is done after the node is
considered for the purposes of changing direction.)
- The virus carrier
moves forward one node in the direction it is facing. Diagnostics have
also provided a map of the node infection status (your puzzle input).
Clean nodes are shown as .; infected nodes are shown as #. This map
only shows the center of the grid; there are many more nodes beyond
those shown, but none of them are currently infected.
The virus carrier begins in the middle of the map facing up.
(The full puzzle description, including examples, is available on the official AoC website.)
The mentioned puzzle input consists of a file containing a grid of . and #.
My Solution
import Data.List.Index
import qualified Data.Set as Set
import System.Environment
import System.IO
import qualified System.IO.Strict as IOS
type Position = (Int, Int)
type Dimensions = (Int, Int)
type NodeMap = Set.Set Position
data Rotation = Clockwise | Counterclockwise deriving (Eq, Show)
data Direction = North | East | South | West deriving (Eq, Show)
nextDirection :: Rotation -> Direction -> Direction
nextDirection rot dir
| rot == Clockwise && dir == West = North
| rot == Clockwise && dir == East = South
| rot == Clockwise && dir == North = East
| rot == Clockwise && dir == South = West
| rot == Counterclockwise && dir == West = South
| rot == Counterclockwise && dir == East = North
| rot == Counterclockwise && dir == North = West
| rot == Counterclockwise && dir == South = East
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head . lines $ s, length . lines $ s),
foldl (set (index, char) ->
if char == '#'
then Set.insert index set
else set)
Set.empty
. ifoldl (ls index line ->
ls ++ zipWith (ix (i, char) ->
((i, ix), char))
(repeat index) (indexed line))
. lines $ s)
simulateNBurstsImpl :: Int -> (Int, Position, Direction, NodeMap)
-> (Int, Position, Direction, NodeMap)
simulateNBurstsImpl 0 x = x
simulateNBurstsImpl i (count, pos, dir, map) = simulateNBurstsImpl
(i - 1) transitionFunction
where
step (a, b) dir
| dir == West = (a - 1, b)
| dir == East = (a + 1, b)
| dir == North = (a, b - 1)
| dir == South = (a, b + 1)
transitionFunction
| Set.member pos map == True = let
nextDir = nextDirection Clockwise dir
in (count, step pos nextDir, nextDir, Set.delete pos map)
| Set.member pos map == False = let
nextDir = nextDirection Counterclockwise dir
in (count + 1, step pos nextDir, nextDir, Set.insert pos map)
simulateNBursts :: Int -> Dimensions -> NodeMap -> Int
simulateNBursts i (width, height) map =
first (simulateNBurstsImpl i (0, startingPos, North, map))
where
startingPos = (width `quot` 2, height `quot` 2)
first (a, _, _, _) = a
main = getArgs >>= (filename : _) ->
withFile filename ReadMode IOS.hGetContents >>= content ->
let parsed = parseInput content in
print . simulateNBursts 10000 (fst parsed) . snd $ parsed
Notes:
- The program takes a single argument on the commandline; the path to the file containing the starting grid.
- I decided on using a set as the underlying data structure as it makes it easy to work with growing grids. My first attempt used a two-dimensional sequence, but extending the grid turned out to be too much of a hassle for my taste.
- The code assumes that all inputs are valid, including the fact that a commandline parameter was passed and points to a valid file.
Review Requests
Please feel free to review anything and everything that comes to mind! That said, I do have a few concrete questions:
- Having
simulateNBurstsas a beautified interface tosimulateNBurstsImplseems kind of ugly to me. Is there a way to clean this up and join the two functions? Or is this a common pattern? - How readable is this code? As its author, I find it hard to judge how (un-)pleasant this code is to the eye of a third person, especially since I have next to no experience in reading and writing Haskell code. What can I do to improve readability?
nextDirectionseems very verbose to me. Is there a more concise way to implement it?
beginner haskell
add a comment |Â
up vote
2
down vote
favorite
Until recently, I was very much only devoted to imperative languages (mainly C++ and C, to be precise), when I decided to venture into unknown waters by picking up a new, completely different language, which happened to be Haskell, a decision which happened to be influenced by the fact that I owned a copy of "Learn You a Haskell for Great Good!", a book which I very much enjoyed learning from.
Some days ago, I finished said book, and, wanting to apply my newly acquired knowledge, ventured our to find some programming exercises. I quickly remembered Advent of Code, which offers a whole pre-Christmas period's worth of easy to mildly difficult programming problems, a few of which I had already solved during the holidays.
I skimmed through the exercises, looking for one simple enough to be conquerable with my still very inadequate and shaky Haskell skills, and finally chose the task of Day 22.
Problem Description
Diagnostics indicate that the local grid computing cluster has been
contaminated with the Sporifica Virus. The grid computing cluster is a
seemingly-infinite two-dimensional grid of compute nodes. Each node is
either clean or infected by the virus.
To prevent overloading the nodes (which would render them useless to
the virus) or detection by system administrators, exactly one virus
carrier moves through the network, infecting or cleaning nodes as it
moves. The virus carrier is always located on a single node in the
network (the current node) and keeps track of the direction it is
facing.
To avoid detection, the virus carrier works in bursts; in each burst,
it wakes up, does some work, and goes back to sleep. The following
steps are all executed in order one time each burst:
- If the current node is infected, it turns to its right. Otherwise, it
turns to its left. (Turning is done in-place; the current node does
not change.)
- If the current node is clean, it becomes infected.
Otherwise, it becomes cleaned. (This is done after the node is
considered for the purposes of changing direction.)
- The virus carrier
moves forward one node in the direction it is facing. Diagnostics have
also provided a map of the node infection status (your puzzle input).
Clean nodes are shown as .; infected nodes are shown as #. This map
only shows the center of the grid; there are many more nodes beyond
those shown, but none of them are currently infected.
The virus carrier begins in the middle of the map facing up.
(The full puzzle description, including examples, is available on the official AoC website.)
The mentioned puzzle input consists of a file containing a grid of . and #.
My Solution
import Data.List.Index
import qualified Data.Set as Set
import System.Environment
import System.IO
import qualified System.IO.Strict as IOS
type Position = (Int, Int)
type Dimensions = (Int, Int)
type NodeMap = Set.Set Position
data Rotation = Clockwise | Counterclockwise deriving (Eq, Show)
data Direction = North | East | South | West deriving (Eq, Show)
nextDirection :: Rotation -> Direction -> Direction
nextDirection rot dir
| rot == Clockwise && dir == West = North
| rot == Clockwise && dir == East = South
| rot == Clockwise && dir == North = East
| rot == Clockwise && dir == South = West
| rot == Counterclockwise && dir == West = South
| rot == Counterclockwise && dir == East = North
| rot == Counterclockwise && dir == North = West
| rot == Counterclockwise && dir == South = East
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head . lines $ s, length . lines $ s),
foldl (set (index, char) ->
if char == '#'
then Set.insert index set
else set)
Set.empty
. ifoldl (ls index line ->
ls ++ zipWith (ix (i, char) ->
((i, ix), char))
(repeat index) (indexed line))
. lines $ s)
simulateNBurstsImpl :: Int -> (Int, Position, Direction, NodeMap)
-> (Int, Position, Direction, NodeMap)
simulateNBurstsImpl 0 x = x
simulateNBurstsImpl i (count, pos, dir, map) = simulateNBurstsImpl
(i - 1) transitionFunction
where
step (a, b) dir
| dir == West = (a - 1, b)
| dir == East = (a + 1, b)
| dir == North = (a, b - 1)
| dir == South = (a, b + 1)
transitionFunction
| Set.member pos map == True = let
nextDir = nextDirection Clockwise dir
in (count, step pos nextDir, nextDir, Set.delete pos map)
| Set.member pos map == False = let
nextDir = nextDirection Counterclockwise dir
in (count + 1, step pos nextDir, nextDir, Set.insert pos map)
simulateNBursts :: Int -> Dimensions -> NodeMap -> Int
simulateNBursts i (width, height) map =
first (simulateNBurstsImpl i (0, startingPos, North, map))
where
startingPos = (width `quot` 2, height `quot` 2)
first (a, _, _, _) = a
main = getArgs >>= (filename : _) ->
withFile filename ReadMode IOS.hGetContents >>= content ->
let parsed = parseInput content in
print . simulateNBursts 10000 (fst parsed) . snd $ parsed
Notes:
- The program takes a single argument on the commandline; the path to the file containing the starting grid.
- I decided on using a set as the underlying data structure as it makes it easy to work with growing grids. My first attempt used a two-dimensional sequence, but extending the grid turned out to be too much of a hassle for my taste.
- The code assumes that all inputs are valid, including the fact that a commandline parameter was passed and points to a valid file.
Review Requests
Please feel free to review anything and everything that comes to mind! That said, I do have a few concrete questions:
- Having
simulateNBurstsas a beautified interface tosimulateNBurstsImplseems kind of ugly to me. Is there a way to clean this up and join the two functions? Or is this a common pattern? - How readable is this code? As its author, I find it hard to judge how (un-)pleasant this code is to the eye of a third person, especially since I have next to no experience in reading and writing Haskell code. What can I do to improve readability?
nextDirectionseems very verbose to me. Is there a more concise way to implement it?
beginner haskell
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Until recently, I was very much only devoted to imperative languages (mainly C++ and C, to be precise), when I decided to venture into unknown waters by picking up a new, completely different language, which happened to be Haskell, a decision which happened to be influenced by the fact that I owned a copy of "Learn You a Haskell for Great Good!", a book which I very much enjoyed learning from.
Some days ago, I finished said book, and, wanting to apply my newly acquired knowledge, ventured our to find some programming exercises. I quickly remembered Advent of Code, which offers a whole pre-Christmas period's worth of easy to mildly difficult programming problems, a few of which I had already solved during the holidays.
I skimmed through the exercises, looking for one simple enough to be conquerable with my still very inadequate and shaky Haskell skills, and finally chose the task of Day 22.
Problem Description
Diagnostics indicate that the local grid computing cluster has been
contaminated with the Sporifica Virus. The grid computing cluster is a
seemingly-infinite two-dimensional grid of compute nodes. Each node is
either clean or infected by the virus.
To prevent overloading the nodes (which would render them useless to
the virus) or detection by system administrators, exactly one virus
carrier moves through the network, infecting or cleaning nodes as it
moves. The virus carrier is always located on a single node in the
network (the current node) and keeps track of the direction it is
facing.
To avoid detection, the virus carrier works in bursts; in each burst,
it wakes up, does some work, and goes back to sleep. The following
steps are all executed in order one time each burst:
- If the current node is infected, it turns to its right. Otherwise, it
turns to its left. (Turning is done in-place; the current node does
not change.)
- If the current node is clean, it becomes infected.
Otherwise, it becomes cleaned. (This is done after the node is
considered for the purposes of changing direction.)
- The virus carrier
moves forward one node in the direction it is facing. Diagnostics have
also provided a map of the node infection status (your puzzle input).
Clean nodes are shown as .; infected nodes are shown as #. This map
only shows the center of the grid; there are many more nodes beyond
those shown, but none of them are currently infected.
The virus carrier begins in the middle of the map facing up.
(The full puzzle description, including examples, is available on the official AoC website.)
The mentioned puzzle input consists of a file containing a grid of . and #.
My Solution
import Data.List.Index
import qualified Data.Set as Set
import System.Environment
import System.IO
import qualified System.IO.Strict as IOS
type Position = (Int, Int)
type Dimensions = (Int, Int)
type NodeMap = Set.Set Position
data Rotation = Clockwise | Counterclockwise deriving (Eq, Show)
data Direction = North | East | South | West deriving (Eq, Show)
nextDirection :: Rotation -> Direction -> Direction
nextDirection rot dir
| rot == Clockwise && dir == West = North
| rot == Clockwise && dir == East = South
| rot == Clockwise && dir == North = East
| rot == Clockwise && dir == South = West
| rot == Counterclockwise && dir == West = South
| rot == Counterclockwise && dir == East = North
| rot == Counterclockwise && dir == North = West
| rot == Counterclockwise && dir == South = East
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head . lines $ s, length . lines $ s),
foldl (set (index, char) ->
if char == '#'
then Set.insert index set
else set)
Set.empty
. ifoldl (ls index line ->
ls ++ zipWith (ix (i, char) ->
((i, ix), char))
(repeat index) (indexed line))
. lines $ s)
simulateNBurstsImpl :: Int -> (Int, Position, Direction, NodeMap)
-> (Int, Position, Direction, NodeMap)
simulateNBurstsImpl 0 x = x
simulateNBurstsImpl i (count, pos, dir, map) = simulateNBurstsImpl
(i - 1) transitionFunction
where
step (a, b) dir
| dir == West = (a - 1, b)
| dir == East = (a + 1, b)
| dir == North = (a, b - 1)
| dir == South = (a, b + 1)
transitionFunction
| Set.member pos map == True = let
nextDir = nextDirection Clockwise dir
in (count, step pos nextDir, nextDir, Set.delete pos map)
| Set.member pos map == False = let
nextDir = nextDirection Counterclockwise dir
in (count + 1, step pos nextDir, nextDir, Set.insert pos map)
simulateNBursts :: Int -> Dimensions -> NodeMap -> Int
simulateNBursts i (width, height) map =
first (simulateNBurstsImpl i (0, startingPos, North, map))
where
startingPos = (width `quot` 2, height `quot` 2)
first (a, _, _, _) = a
main = getArgs >>= (filename : _) ->
withFile filename ReadMode IOS.hGetContents >>= content ->
let parsed = parseInput content in
print . simulateNBursts 10000 (fst parsed) . snd $ parsed
Notes:
- The program takes a single argument on the commandline; the path to the file containing the starting grid.
- I decided on using a set as the underlying data structure as it makes it easy to work with growing grids. My first attempt used a two-dimensional sequence, but extending the grid turned out to be too much of a hassle for my taste.
- The code assumes that all inputs are valid, including the fact that a commandline parameter was passed and points to a valid file.
Review Requests
Please feel free to review anything and everything that comes to mind! That said, I do have a few concrete questions:
- Having
simulateNBurstsas a beautified interface tosimulateNBurstsImplseems kind of ugly to me. Is there a way to clean this up and join the two functions? Or is this a common pattern? - How readable is this code? As its author, I find it hard to judge how (un-)pleasant this code is to the eye of a third person, especially since I have next to no experience in reading and writing Haskell code. What can I do to improve readability?
nextDirectionseems very verbose to me. Is there a more concise way to implement it?
beginner haskell
Until recently, I was very much only devoted to imperative languages (mainly C++ and C, to be precise), when I decided to venture into unknown waters by picking up a new, completely different language, which happened to be Haskell, a decision which happened to be influenced by the fact that I owned a copy of "Learn You a Haskell for Great Good!", a book which I very much enjoyed learning from.
Some days ago, I finished said book, and, wanting to apply my newly acquired knowledge, ventured our to find some programming exercises. I quickly remembered Advent of Code, which offers a whole pre-Christmas period's worth of easy to mildly difficult programming problems, a few of which I had already solved during the holidays.
I skimmed through the exercises, looking for one simple enough to be conquerable with my still very inadequate and shaky Haskell skills, and finally chose the task of Day 22.
Problem Description
Diagnostics indicate that the local grid computing cluster has been
contaminated with the Sporifica Virus. The grid computing cluster is a
seemingly-infinite two-dimensional grid of compute nodes. Each node is
either clean or infected by the virus.
To prevent overloading the nodes (which would render them useless to
the virus) or detection by system administrators, exactly one virus
carrier moves through the network, infecting or cleaning nodes as it
moves. The virus carrier is always located on a single node in the
network (the current node) and keeps track of the direction it is
facing.
To avoid detection, the virus carrier works in bursts; in each burst,
it wakes up, does some work, and goes back to sleep. The following
steps are all executed in order one time each burst:
- If the current node is infected, it turns to its right. Otherwise, it
turns to its left. (Turning is done in-place; the current node does
not change.)
- If the current node is clean, it becomes infected.
Otherwise, it becomes cleaned. (This is done after the node is
considered for the purposes of changing direction.)
- The virus carrier
moves forward one node in the direction it is facing. Diagnostics have
also provided a map of the node infection status (your puzzle input).
Clean nodes are shown as .; infected nodes are shown as #. This map
only shows the center of the grid; there are many more nodes beyond
those shown, but none of them are currently infected.
The virus carrier begins in the middle of the map facing up.
(The full puzzle description, including examples, is available on the official AoC website.)
The mentioned puzzle input consists of a file containing a grid of . and #.
My Solution
import Data.List.Index
import qualified Data.Set as Set
import System.Environment
import System.IO
import qualified System.IO.Strict as IOS
type Position = (Int, Int)
type Dimensions = (Int, Int)
type NodeMap = Set.Set Position
data Rotation = Clockwise | Counterclockwise deriving (Eq, Show)
data Direction = North | East | South | West deriving (Eq, Show)
nextDirection :: Rotation -> Direction -> Direction
nextDirection rot dir
| rot == Clockwise && dir == West = North
| rot == Clockwise && dir == East = South
| rot == Clockwise && dir == North = East
| rot == Clockwise && dir == South = West
| rot == Counterclockwise && dir == West = South
| rot == Counterclockwise && dir == East = North
| rot == Counterclockwise && dir == North = West
| rot == Counterclockwise && dir == South = East
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head . lines $ s, length . lines $ s),
foldl (set (index, char) ->
if char == '#'
then Set.insert index set
else set)
Set.empty
. ifoldl (ls index line ->
ls ++ zipWith (ix (i, char) ->
((i, ix), char))
(repeat index) (indexed line))
. lines $ s)
simulateNBurstsImpl :: Int -> (Int, Position, Direction, NodeMap)
-> (Int, Position, Direction, NodeMap)
simulateNBurstsImpl 0 x = x
simulateNBurstsImpl i (count, pos, dir, map) = simulateNBurstsImpl
(i - 1) transitionFunction
where
step (a, b) dir
| dir == West = (a - 1, b)
| dir == East = (a + 1, b)
| dir == North = (a, b - 1)
| dir == South = (a, b + 1)
transitionFunction
| Set.member pos map == True = let
nextDir = nextDirection Clockwise dir
in (count, step pos nextDir, nextDir, Set.delete pos map)
| Set.member pos map == False = let
nextDir = nextDirection Counterclockwise dir
in (count + 1, step pos nextDir, nextDir, Set.insert pos map)
simulateNBursts :: Int -> Dimensions -> NodeMap -> Int
simulateNBursts i (width, height) map =
first (simulateNBurstsImpl i (0, startingPos, North, map))
where
startingPos = (width `quot` 2, height `quot` 2)
first (a, _, _, _) = a
main = getArgs >>= (filename : _) ->
withFile filename ReadMode IOS.hGetContents >>= content ->
let parsed = parseInput content in
print . simulateNBursts 10000 (fst parsed) . snd $ parsed
Notes:
- The program takes a single argument on the commandline; the path to the file containing the starting grid.
- I decided on using a set as the underlying data structure as it makes it easy to work with growing grids. My first attempt used a two-dimensional sequence, but extending the grid turned out to be too much of a hassle for my taste.
- The code assumes that all inputs are valid, including the fact that a commandline parameter was passed and points to a valid file.
Review Requests
Please feel free to review anything and everything that comes to mind! That said, I do have a few concrete questions:
- Having
simulateNBurstsas a beautified interface tosimulateNBurstsImplseems kind of ugly to me. Is there a way to clean this up and join the two functions? Or is this a common pattern? - How readable is this code? As its author, I find it hard to judge how (un-)pleasant this code is to the eye of a third person, especially since I have next to no experience in reading and writing Haskell code. What can I do to improve readability?
nextDirectionseems very verbose to me. Is there a more concise way to implement it?
beginner haskell
asked Feb 10 at 22:35
Ben Steffan
4,85011234
4,85011234
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
You can inline simulateNBurstsImpl if you get rid of the recursion:
simulateNBurstsImpl n = foldr (.) id $ replicate n transitionFunction
Combining zipWith with repeat is a fool's errand. (I don't know where you get ifoldl and indexed, so I'll assume they start at 0.)
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head $ lines s, length $ lines s), Set.fromList
[ (x, y)
| (y, line) <- zip [0..] $ lines s
, (x, char) <- zip [0..] line
, char == '#'
])
Direction and Rotation can be directly represented as offsets and functions on offsets.
import Data.NumInstances.Tuple
type Rotation = Direction -> Direction
type Direction = (Int, Int)
transitionFunction (count, pos, (dx,dy), map) = if Set.member pos map
then let nextdir = (-dy,dx)
in (count , pos + nextDir, nextDir, Set.delete pos map)
else let nextdir = (dy,-dx)
in (count + 1, pos + nextDir, nextDir, Set.insert pos map)
Optionally: lens and State specialize in this fiddly sort of stuff.
data S = S
_count :: Int
, _pos :: Position
, _dir :: Int
, _map :: NodeMap
makeLenses ''S
simulateNBursts i (width, height) map =
(`evalState` (0, (width `quot` 2, height `quot` 2), (0,-1), map)) $ do
replicateM_ i $ do
hashtagged <- map . contains pos <<%= negate
nextDir <- dir <%= (x,y) -> if hashtagged then (-y,x) else (y,-x)
pos += nextDir
unless hashtagged $ count += 1
use count
Thank you for the answer.ifoldlandindexedare from theilistpackage (from Data.List.Index and start, as you guessed, from 0.
â Ben Steffan
Feb 11 at 16:23
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
You can inline simulateNBurstsImpl if you get rid of the recursion:
simulateNBurstsImpl n = foldr (.) id $ replicate n transitionFunction
Combining zipWith with repeat is a fool's errand. (I don't know where you get ifoldl and indexed, so I'll assume they start at 0.)
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head $ lines s, length $ lines s), Set.fromList
[ (x, y)
| (y, line) <- zip [0..] $ lines s
, (x, char) <- zip [0..] line
, char == '#'
])
Direction and Rotation can be directly represented as offsets and functions on offsets.
import Data.NumInstances.Tuple
type Rotation = Direction -> Direction
type Direction = (Int, Int)
transitionFunction (count, pos, (dx,dy), map) = if Set.member pos map
then let nextdir = (-dy,dx)
in (count , pos + nextDir, nextDir, Set.delete pos map)
else let nextdir = (dy,-dx)
in (count + 1, pos + nextDir, nextDir, Set.insert pos map)
Optionally: lens and State specialize in this fiddly sort of stuff.
data S = S
_count :: Int
, _pos :: Position
, _dir :: Int
, _map :: NodeMap
makeLenses ''S
simulateNBursts i (width, height) map =
(`evalState` (0, (width `quot` 2, height `quot` 2), (0,-1), map)) $ do
replicateM_ i $ do
hashtagged <- map . contains pos <<%= negate
nextDir <- dir <%= (x,y) -> if hashtagged then (-y,x) else (y,-x)
pos += nextDir
unless hashtagged $ count += 1
use count
Thank you for the answer.ifoldlandindexedare from theilistpackage (from Data.List.Index and start, as you guessed, from 0.
â Ben Steffan
Feb 11 at 16:23
add a comment |Â
up vote
1
down vote
accepted
You can inline simulateNBurstsImpl if you get rid of the recursion:
simulateNBurstsImpl n = foldr (.) id $ replicate n transitionFunction
Combining zipWith with repeat is a fool's errand. (I don't know where you get ifoldl and indexed, so I'll assume they start at 0.)
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head $ lines s, length $ lines s), Set.fromList
[ (x, y)
| (y, line) <- zip [0..] $ lines s
, (x, char) <- zip [0..] line
, char == '#'
])
Direction and Rotation can be directly represented as offsets and functions on offsets.
import Data.NumInstances.Tuple
type Rotation = Direction -> Direction
type Direction = (Int, Int)
transitionFunction (count, pos, (dx,dy), map) = if Set.member pos map
then let nextdir = (-dy,dx)
in (count , pos + nextDir, nextDir, Set.delete pos map)
else let nextdir = (dy,-dx)
in (count + 1, pos + nextDir, nextDir, Set.insert pos map)
Optionally: lens and State specialize in this fiddly sort of stuff.
data S = S
_count :: Int
, _pos :: Position
, _dir :: Int
, _map :: NodeMap
makeLenses ''S
simulateNBursts i (width, height) map =
(`evalState` (0, (width `quot` 2, height `quot` 2), (0,-1), map)) $ do
replicateM_ i $ do
hashtagged <- map . contains pos <<%= negate
nextDir <- dir <%= (x,y) -> if hashtagged then (-y,x) else (y,-x)
pos += nextDir
unless hashtagged $ count += 1
use count
Thank you for the answer.ifoldlandindexedare from theilistpackage (from Data.List.Index and start, as you guessed, from 0.
â Ben Steffan
Feb 11 at 16:23
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
You can inline simulateNBurstsImpl if you get rid of the recursion:
simulateNBurstsImpl n = foldr (.) id $ replicate n transitionFunction
Combining zipWith with repeat is a fool's errand. (I don't know where you get ifoldl and indexed, so I'll assume they start at 0.)
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head $ lines s, length $ lines s), Set.fromList
[ (x, y)
| (y, line) <- zip [0..] $ lines s
, (x, char) <- zip [0..] line
, char == '#'
])
Direction and Rotation can be directly represented as offsets and functions on offsets.
import Data.NumInstances.Tuple
type Rotation = Direction -> Direction
type Direction = (Int, Int)
transitionFunction (count, pos, (dx,dy), map) = if Set.member pos map
then let nextdir = (-dy,dx)
in (count , pos + nextDir, nextDir, Set.delete pos map)
else let nextdir = (dy,-dx)
in (count + 1, pos + nextDir, nextDir, Set.insert pos map)
Optionally: lens and State specialize in this fiddly sort of stuff.
data S = S
_count :: Int
, _pos :: Position
, _dir :: Int
, _map :: NodeMap
makeLenses ''S
simulateNBursts i (width, height) map =
(`evalState` (0, (width `quot` 2, height `quot` 2), (0,-1), map)) $ do
replicateM_ i $ do
hashtagged <- map . contains pos <<%= negate
nextDir <- dir <%= (x,y) -> if hashtagged then (-y,x) else (y,-x)
pos += nextDir
unless hashtagged $ count += 1
use count
You can inline simulateNBurstsImpl if you get rid of the recursion:
simulateNBurstsImpl n = foldr (.) id $ replicate n transitionFunction
Combining zipWith with repeat is a fool's errand. (I don't know where you get ifoldl and indexed, so I'll assume they start at 0.)
parseInput :: String -> (Dimensions, NodeMap)
parseInput s = ((length . head $ lines s, length $ lines s), Set.fromList
[ (x, y)
| (y, line) <- zip [0..] $ lines s
, (x, char) <- zip [0..] line
, char == '#'
])
Direction and Rotation can be directly represented as offsets and functions on offsets.
import Data.NumInstances.Tuple
type Rotation = Direction -> Direction
type Direction = (Int, Int)
transitionFunction (count, pos, (dx,dy), map) = if Set.member pos map
then let nextdir = (-dy,dx)
in (count , pos + nextDir, nextDir, Set.delete pos map)
else let nextdir = (dy,-dx)
in (count + 1, pos + nextDir, nextDir, Set.insert pos map)
Optionally: lens and State specialize in this fiddly sort of stuff.
data S = S
_count :: Int
, _pos :: Position
, _dir :: Int
, _map :: NodeMap
makeLenses ''S
simulateNBursts i (width, height) map =
(`evalState` (0, (width `quot` 2, height `quot` 2), (0,-1), map)) $ do
replicateM_ i $ do
hashtagged <- map . contains pos <<%= negate
nextDir <- dir <%= (x,y) -> if hashtagged then (-y,x) else (y,-x)
pos += nextDir
unless hashtagged $ count += 1
use count
edited Feb 12 at 18:20
answered Feb 11 at 16:16
Gurkenglas
2,658411
2,658411
Thank you for the answer.ifoldlandindexedare from theilistpackage (from Data.List.Index and start, as you guessed, from 0.
â Ben Steffan
Feb 11 at 16:23
add a comment |Â
Thank you for the answer.ifoldlandindexedare from theilistpackage (from Data.List.Index and start, as you guessed, from 0.
â Ben Steffan
Feb 11 at 16:23
Thank you for the answer.
ifoldl and indexed are from the ilist package (from Data.List.Index and start, as you guessed, from 0.â Ben Steffan
Feb 11 at 16:23
Thank you for the answer.
ifoldl and indexed are from the ilist package (from Data.List.Index and start, as you guessed, from 0.â Ben Steffan
Feb 11 at 16:23
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187283%2faoc-day-22-sporifica-virus-part-1-a-solution-in-beginners-haskell%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password