import Jam
import Data.Char
import Data.List
import Data.List.Split
ns = words "zero one two three four five six seven eight nine"
ms = "":"":"double":"triple":((++"uple") <$>
words "quadr quint sext sept oct non dec")
speak s@(c:_)
| m == 1 || m > 10 = unwords $ replicate m d
| otherwise = ms!!m ++ " " ++ d
where d = ns!!digitToInt c
m = length s
main = jam $ do
(n, f) <- (\[n, f] -> (n, map read $ splitOn "-" f)) . words <$> gets
pure $ unwords $ map (unwords . map speak . group) $ splitPlaces f n
Read Phone Number
The splitPlaces and group functions make short work of this problem.
Rational Number Tree
We solve question 1 by considering the binary expansion of n. The bits
tell us whether to take the left or right path at each level.
For question 2, if the numerator exceeds the denominator, then we have just taken the right path, otherwise we have taken the left. We work backwards using this rule and build the index one bit at a time.
import Jam
main = jam $ do
(id:p:q) <- getintegers
pure $ case id of
1 -> (\(p, q) -> show p ++ " " ++ show q) $ f p
2 -> show $ g p $ head q
f 1 = (1, 1)
f n | r == 1 = (a + b, b)
| otherwise = (a, a + b)
where (q, r) = divMod n 2
(a, b) = f q
g p q | p > q = 1 + 2 * g (p - q) q
| p < q = 2 * g p (q - p)
| otherwise = 1
Sorting
We use partition to separate odd and even numbers, sort them independently,
then recursively put them back together, using the original list as a guide.
import Jam import Data.List import Data.Ord main = jam $ do gets ns <- getints let (as, bs) = partition odd ns pure $ unwords $ map show $ f [] (sort as) (sortBy (flip compare) bs) ns f acc [] [] [] = reverse acc f acc as bs (n:ns) | odd n = f (head as:acc) (tail as) bs ns | otherwise = f (head bs:acc) as (tail bs) ns
Cross the maze
Tedious but straightforward.
If we start in the first row, facing east guarantees there will be a wall on our left, and similarly for facing west if we start in the last row.
If we start completely surrounded by walls, we immediately return Nothing,
which represents failure. Otherwise we proceed with the left-hand rule; if we
reach the exit within 10000 steps then we return Just the number of steps
taken, otherwise we return Nothing.
import Jam
import Data.Array
import Data.Maybe
main = jam $ do
[n] <- getints
as <- getsn n
[sx, sy, ex, ey] <- getints
let
maze = listArray ((1, 1), (n, n)) $ concat as
(dx, dy) | sx == 1 = (0, 1)
| otherwise = (0, -1)
f acc (x, y) (dx, dy)
| (x, y) == (ex, ey) = Just $ reverse acc
| length acc == 10000 = Nothing
| not $ isWall (x + hx, y + hy) = go (hx, hy)
| not $ isWall (x + dx, y + dy) = go (dx, dy)
| otherwise = f acc (x, y) $ turnR (dx, dy)
where
(hx, hy) = turnL (dx, dy)
go (dx, dy) = f (compass (dx, dy):acc) (x + dx, y + dy) (dx, dy)
isWall (x, y)
| x == 0 || y == 0 || x > n || y > n = True
| otherwise = maze!(x, y) == '#'
pure $ maybe "Edison ran out of energy." out $
if all (isWall . (\(dx, dy) -> (sx + dx, sy + dy)))
[(0, 1), (1, 0), (0, -1), (-1, 0)]
then Nothing
else f "" (sx, sy) (dx, dy)
compass ( 1, 0) = 'S'
compass (-1, 0) = 'N'
compass ( 0, 1) = 'E'
compass ( 0, -1) = 'W'
turnL (x, y) = (-y, x)
turnR (x, y) = (y, -x)
out s = show (length s) ++ "\n" ++ s
Spaceship Defence
The inputs are obfuscated graphs. Each node is a colour, and each turbolift is an edge. When converting to a graph, we remove self-edges and only keep the edges of minimum weight.
Then we apply Dijkstra’s algorithm on each soldier to find the shortest paths.
import Jam
import Data.Array
import Data.List
import Data.Ord
import qualified Data.Map as M
import qualified Data.Set as S
main = jamLnCat $ do
[n] <- getints
cs <- getsn n
[m] <- getints
ts <- getintsn m
[s] <- getints
pqs <- getintsn s
let
aa = listArray (1, n) cs
keepBest [x@(k, v)] ds = case lookup k ds of
Nothing -> x:ds
Just v' -> if v < v' then x:delete (k, v') ds else ds
mm = M.fromListWith keepBest $ [(ca, [(cb, t)]) | [a, b, t] <- ts,
let ca = aa!a, let cb = aa!b, ca /= cb]
nbrs x = if x `M.notMember` mm then [] else mm M.! x
shortest p q
| p == q = 0
| otherwise = f S.empty (S.singleton p) q $ M.singleton p 0
f done todo q ds
| null todo = -1
| x == q = c
| otherwise = f (S.insert x done) (foldl' (flip S.insert) (S.delete x todo) (map fst u)) q $
foldl' (\ds (n,m) -> M.insertWith min n m ds) ds [(n, c + m) | (n, m) <- u]
where x = minimumBy (comparing (ds M.!)) todo
c = ds M.! x
u = filter ((`S.notMember` done) . fst) $ nbrs x
pure $ unlines $ map show $ [shortest (aa!p) (aa!q) | [p, q] <- pqs]