intricacy-0.3: BoardColouring.hs
-- This file is part of Intricacy
-- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of version 3 of the GNU General Public License as
-- published by the Free Software Foundation.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see http://www.gnu.org/licenses/.
module BoardColouring where
import Control.Applicative hiding ((<*>))
import Control.Monad
import Data.Function (on)
import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Vector as Vector
import Data.List
import Data.Maybe
import Hex
import GameState
import GameStateTypes
import Util
import GraphColouring
type PieceColouring = Map PieceIdx Int
colouredPieces :: Bool -> GameState -> [PieceIdx]
colouredPieces colourFixed st = [ idx |
(idx, PlacedPiece _ p) <- enumVec $ placedPieces st
, isPivot p ||
and [ isBlock p, idx > 0
, or [ colourFixed, not $ null $ springsEndAtIdx st idx ] ] ]
pieceTypeColouring :: GameState -> [PieceIdx] -> PieceColouring
pieceTypeColouring st coloured = Map.fromList
[ (idx, col) | (idx, PlacedPiece _ p) <- enumVec $ placedPieces st
, idx `elem` coloured
, let col = if isBlock p then 1+((connGraphHeight st idx - 1) `mod` 5) else 0 ]
boardColouring :: GameState -> [PieceIdx] -> PieceColouring -> PieceColouring
boardColouring st coloured lastCol =
fiveColour graph lastCol
where
board = stateBoard st
graph = Map.fromList [ (idx, nub $ neighbours idx)
| idx <- coloured ]
neighbours idx =
neighbours' idx (perim idx) []
perim :: PieceIdx -> Set (HexPos,HexDir)
perim idx =
Set.fromList $ nubBy ((==)`on`fst) [ (pos', neg dir)
| dir <- hexDirs
, pos <- fullFootprint st idx
, let pos' = dir <+> pos
, Just True /= do
(idx',_) <- Map.lookup pos' board
return $ idx == idx'
]
neighbours' :: PieceIdx -> Set (HexPos,HexDir) -> [PieceIdx] -> [PieceIdx]
neighbours' idx as ns
| Set.null as = ns
| otherwise =
let a = head $ Set.elems as
(path, ns') = march idx (fst a) a True
in neighbours' idx
(Set.filter (\(pos,_) -> pos `notElem` path) as)
(ns++ns')
-- |march around the piece's boundary, returning positions visited and
-- neighbouring pieces met (in order)
march idx startPos (pos,basedir) init
| not init && pos == startPos = ([],[])
| otherwise =
let n = do
(idx',_) <- Map.lookup pos board
guard $ idx' `elem` coloured
return idx'
mNext = listToMaybe
[ (pos', rotate (h-2) basedir)
| h <- [1..5]
, let pos' = (rotate h basedir)<+>pos
, (fst <$> Map.lookup pos' board) /= Just idx
]
(path,ns) = case mNext of
Nothing -> ([],[])
Just next -> march idx startPos next False
in (pos:path, (maybeToList n)++ns)