packages feed

Allure-0.4.2: src/Loc.hs

module Loc
  ( Loc, toLoc, fromLoc, trLoc, zeroLoc, distance, adjacent, surroundings )
  where

import Geometry
import Utils.Assert

-- Loc is a positivie integer for speed and to enforce the use of wrappers
-- (we don't want newtype to avoid the trouble with using EnumMap
-- in place of IntMap, etc.). We represent the screen as a linear framebuffer,
-- when Loc is an Int offset counted from the first cell.
-- We do bounds check for the X size ASAP and each subsequent
-- array access performs another check, effectively for Y size.
-- After dungeon is generated (using (X, Y) points, not Loc), Locs are used
-- mainly as keys and not constructed often, so the performance will improve
-- due to smaller save files, IntMaps and cheaper array indexing,
-- including cheaper bounds checks.
type Loc = Int

toLoc :: X -> (X, Y) -> Loc
toLoc lxsize (x, y) =
  assert (lxsize > x && x >= 0 && y >= 0 `blame` (lxsize, x, y)) $
  x + y * lxsize

fromLoc :: X -> Loc -> (X, Y)
fromLoc lxsize loc =
  assert (loc >= 0 `blame` (lxsize, loc)) $
  (loc `rem` lxsize, loc `quot` lxsize)

-- | Translation by a vector, where dx and dy can be negative.
trLoc :: X -> Loc -> (X, Y) -> Loc
trLoc lxsize loc (dx, dy) =
  assert (loc >= 0 && res >= 0 `blame` (lxsize, loc, (dx, dy))) $
  res
   where res = loc + dx + dy * lxsize

zeroLoc :: Loc
zeroLoc = 0

-- | The distance between two points in the metric with diagonal moves.
distance :: X -> Loc -> Loc -> Int
distance lxsize loc0 loc1
  | (x0, y0) <- fromLoc lxsize loc0, (x1, y1) <- fromLoc lxsize loc1 =
  lenXY (x1 - x0, y1 - y0)

-- | Return whether two locations are adjacent on the map
-- (horizontally, vertically or diagonally).
-- A position is also considered adjacent to itself.
adjacent :: X -> Loc -> Loc -> Bool
adjacent lxsize s t = distance lxsize s t <= 1

-- | Return the 8, or less, surrounding locations of a given location.
surroundings :: X -> Y -> Loc -> [Loc]
surroundings lxsize lysize loc | (x, y) <- fromLoc lxsize loc =
  [ toLoc lxsize (x + dx, y + dy)
  | (dx, dy) <- movesXY,
    x + dx >= 0 && x + dx < lxsize &&
    y + dy >= 0 && y + dy < lysize ]