packages feed

scroll-1.20150313: Invert.hs

module Invert where

import Types

-- Inverting back to front.
class Invertable a where
	invert :: a -> a

instance Invertable Direction where
	invert DLeft = DRight
	invert DRight = DLeft
	invert DUp = DDown
	invert DDown = DUp
	invert DDive =DDive

instance Invertable Segment where
	invert s = s { segmentDirection = invert (segmentDirection s) }

-- Head moves to the position of the last tail segment, which moves to the
-- position of the head. The body is reversed.
--
-- If the final tail segment is inside the scroll, avoid putting the head
-- there. In this case, the worm technically gets 1 segment shorter,
-- but it's one that cannot be seen.
instance Invertable Player where
	invert p = go $ reverse $ map invert $ playerBody p
	  where
		go [] = p
		go (t:bs)
			| segmentSide t == InSide = go bs
			| otherwise = repoint $ p
				{ playerHead = getPos t
				, playerBody = bs ++ [t { segmentPos = playerHead p }]
				}

-- Ensures that each segment after the last consistently points to
-- the next segment (assuming the next segment is actually touching it;
-- segments may somehow be off elsewhere in the map. The first segment
-- is repointed at the head.
repoint :: Player -> Player
repoint p = p { playerBody = map go (zip (playerBody p) (playerHead p : map getPos (playerBody p))) }
  where
	go (s, nextpos) = case finddir (getPos s) nextpos of
		Nothing -> s
		Just d -> s { segmentDirection = d }

	finddir from to = case filter (\d -> offsetPos (directionOffset d) from == to) [minBound..maxBound] of
		[] -> Nothing
		(d:_) -> Just d