{-# LANGUAGE FlexibleInstances #-}
module Helper
where
import Data.Maybe
import Data.List
import Data.Ord
import Data.Function
import GHC.Exts
import GHC.Int
import FRP.Yampa
import FRP.Yampa.Geometry
import Object
import Message
import Command
import States
import Physics
import Global
import BasicTypes
import AL
ratio param winy maxh currh = --8.625
(currh/maxh)*(pPitchLength param/winy)
pointToPitch :: (Integral b, Integral b1) => Param -> Double ->
(t, t1, Double) -> (b, b1) -> (Double, Double) ->
(Double, Double)
pointToPitch param maxHeight currTrans winSize (x,y) =
let (winX, winY) = (fromIntegral . fst $ winSize, fromIntegral . snd $ winSize)
(_,_,currHeight) = currTrans
(pWidth,pLength) = (pPitchWidth param, pPitchLength param)
mPp = ratio param winY maxHeight currHeight
pLeft = pWidth/2 - (winX/2) * mPp
pUpper = pLength/2 - (winY/2) * mPp
in (pLeft+x*mPp, pUpper+y*mPp)
-- *************************************************************************
--
-- Various geometric functions
--
-- *************************************************************************
spotToPoint :: Spot -> Point2 Double
spotToPoint (Spot x y) = Point2 x y
pointToSpot :: Point2 Double -> Spot
pointToSpot (Point2 x y) = Spot x y
distanceToSpot :: Spot -> Point2 Double -> Point2 Double -> Ordering
distanceToSpot (Spot x y) =
comparing (distance (Point2 x y))
spotDistance :: Spot -> Point2 Double -> Double
spotDistance (Spot x1 y1) =
distance (Point2 x1 y1)
pointsForward :: RealFloat a => Vector3 a -> Team -> Bool
pointsForward v team =
(team == Home && vector3Y v < 0) ||
(team == Away && vector3Y v > 0)
tm :: TacticalPlayerMessage -> MessageBody
tm x = PlayerMessage $ TacticalPlayerMessage x
pm :: PhysicalPlayerMessage -> MessageBody
pm x = PlayerMessage $ PhysicalPlayerMessage x
otherTeam :: Team -> Team
otherTeam Home = Away
otherTeam Away = Home
lastPlayer :: (t, BallMsgParam) -> ObjId
lastPlayer ballState =
case ballState of
(_, BPWho oid _) -> oid
(_, BPInit _ oid) -> oid
(_, BPOutOfPlay _ _ _ oid) -> oid
teamMates :: ObjId -> [VisibleState] -> [VisibleState]
teamMates me vss =
let team = vsTeam $ fetchVS vss me
in [p | p@(VSPlayer {}) <- vss, vsTeam p == team, vsObjId p /= me]
teamPlayers :: Team -> [VisibleState] -> [VisibleState]
teamPlayers team vss = [p | p@(VSPlayer {vsTeam = team'}) <- vss, team' == team]
fetchGoalie :: Team -> [VisibleState] -> VisibleState
fetchGoalie team vss =
head $ filter isGoalie $ teamPlayers team vss
isGoalie :: VisibleState -> Bool
isGoalie = (Goalie ==) . piPlayerRole . vsPlayerInfo
playerWithBall :: [VisibleState] -> Maybe ObjId
playerWithBall vss =
case filter hasBall vss of
[player] -> Just $ vsObjId player
[] -> Nothing
pls -> error $ "Helper.hs/playerWithBall: too many players " ++ show (map vsObjId pls)
playerIsFree :: [VisibleState] -> VisibleState -> Bool
playerIsFree vss vs =
let pos = vsPos vs
team = vsTeam vs
otherPlayers = teamPlayers (otherTeam team) vss
in foldl' (\acc vso -> acc &&
distance pos (vsPos vso) > 5) True otherPlayers
homeValue :: Param -> Spot -> Double
homeValue param (Spot x y) =
half x (pPositionFactorX param) (pPitchWidth param) +
(pPitchLength param - y) * pPositionFactorY param
awayValue :: Param -> Spot -> Double
awayValue param (Spot x y) =
homeValue param (Spot x (pPitchLength param - y))
half :: (Fractional a, Ord a) => a -> a -> a -> a
half u factor max_
| u < (max_ / 2) = u * factor
| otherwise = (max_ - u) * factor
bestFreePlayer :: Param -> [VisibleState] -> VisibleState -> Maybe VisibleState
bestFreePlayer param vss ballCarrier =
let team = vsTeam ballCarrier
me = vsObjId ballCarrier
valFun = if team == Home then homeValue else awayValue
freePlayers = filter (playerIsFree vss) $ teamMates me vss
in if null freePlayers then Nothing
else Just $ maximumBy (compare `on` valFun param . pointToSpot . projectP . vsPos) freePlayers
nearestNonAIPlayer :: Team -> [VisibleState] -> Position2 -> ObjId
nearestNonAIPlayer team vss pos =
let players = [p | p@(VSPlayer {}) <- vss, vsTeam p == team, fst (vsPTState p) == TSNonAI ]
nearest = minimumBy (\pl1 pl2 -> closerToPoint pos (projectP $ vsPos pl1) (projectP $ vsPos pl2)) players
in vsObjId nearest
nearestAIPlayer :: Team -> [VisibleState] -> Position2 -> ObjId
nearestAIPlayer team vss pos =
let players = [p | p@(VSPlayer {}) <- vss, vsTeam p == team, fst (vsPTState p) /= TSNonAI ]
nearest = minimumBy (\pl1 pl2 -> closerToPoint pos (projectP $ vsPos pl1) (projectP $ vsPos pl2)) players
in vsObjId nearest
nearestAIFieldPlayer :: Team -> [VisibleState] -> Position2 -> ObjId
nearestAIFieldPlayer team vss pos =
let players = [p | p@(VSPlayer {}) <- vss, vsTeam p == team, fst (vsPTState p) /= TSNonAI,
(piPlayerRole . vsPlayerInfo) p /= Goalie ]
nearest = minimumBy (\pl1 pl2 -> closerToPoint pos (projectP $ vsPos pl1) (projectP $ vsPos pl2)) players
in vsObjId nearest
nearestPlayer :: Team -> [VisibleState] -> Position2 -> ObjId
nearestPlayer team vss pos =
let players = [p | p@(VSPlayer {}) <- vss, vsTeam p == team]
nearest = minimumBy (\pl1 pl2 -> closerToPoint pos (projectP $ vsPos pl1) (projectP $ vsPos pl2)) players
in vsObjId nearest
closerToPoint :: (Ord a, AffineSpace p v a) => p -> p -> p -> Ordering
closerToPoint p p1 p2 =
if distance p p1 < distance p p2 then LT else GT
fetchVS :: [VisibleState] -> ObjId -> VisibleState
fetchVS vss = fromJust . flip getObjVS vss
getObjVS :: ObjId -> [VisibleState] -> Maybe VisibleState
getObjVS _ [] = Nothing
getObjVS oid (vs:vss) =
if vsObjId vs == oid then Just vs else getObjVS oid vss
fetchGameVS :: [VisibleState] -> VisibleState
fetchGameVS [] = error "Helper/fetchGameVS: No game in Visible States"
fetchGameVS (v:vs) =
case v of
VSGame {} -> v
_ -> fetchGameVS vs
fetchBallOOS :: [ObsObjState] -> ObsObjState
fetchBallOOS [] = error "Helper/fetchBallVS: No ball in OOS"
fetchBallOOS (v:vs) =
case v of
OOSBall {} -> v
_ -> fetchBallOOS vs
fetchBallVS :: [VisibleState] -> VisibleState
fetchBallVS [] = error "Helper/fetchBallVS: No ball in Visible States"
fetchBallVS (v:vs) =
case v of
VSBall {} -> v
_ -> fetchBallVS vs
fetchBallCarrier :: [VisibleState] -> Maybe VisibleState
fetchBallCarrier vss =
let (s, sp) = vsBallState ball
ball = fetchBallVS vss
in if s `elem` [BSControlled, BSControlledOOP, BSControlledGoalie]
then Just (fetchVS vss (fromBPWho sp))
else Nothing
ballId :: ALOut -> ObjId
ballId (AL []) = error "Helper.hs/ballId: No Ball in Object Output"
ballId (AL ((xId, xOO):xs)) =
case xOO of
ObjOutput (OOSBall {}) _ _ _ -> xId
_ -> ballId (AL xs)
gameId :: ALOut -> ObjId
gameId (AL []) = error "Helper.hs/gameId: No Game in Object Output"
gameId (AL ((xId, xOO):xs)) =
case xOO of
ObjOutput (OOSGame {}) _ _ _ -> xId
_ -> gameId (AL xs)
-- time possession score1 score2
hasBall :: VisibleState -> Bool
hasBall vs = isPlayer vs && (fst . vsPBState) vs == PBSInPossession
offsiteFrontier :: Param -> ObjId -> [VisibleState] -> Point2 Position
offsiteFrontier param me vss =
-- yield the a position for the player on the offsite frontiert. needs at least 2 players in every team
let myself = fetchVS vss me
myTeam = vsTeam myself
Point3 myXPos _ _ = vsPos myself
others = teamPlayers (otherTeam myTeam) vss
reverser = if myTeam == Home then id else reverse
-- Achtung: Auf-/absteigend sortieren je nach Home / Away!
Point3 _ y _ = vsPos . head . tail . reverser $ sortWith (point3Y . vsPos) others
half_ = pPitchLength param / 2
yAdjust = if myTeam == Home then min y half_ + 1 else max y half_ - 1
in Point2 myXPos yAdjust
adjustForOffsite :: Param -> Point2 Position -> ObjId -> [VisibleState] -> Point2 Position
adjustForOffsite param p@(Point2 _ y) me vss =
let pOff@(Point2 _ yOff) = offsiteFrontier param me vss
myself = fetchVS vss me
myTeam = vsTeam myself
ballCarrier = hasBall myself
in
if not ballCarrier &&
((myTeam == Home && y < yOff) ||
(myTeam == Away && y > yOff))
then pOff else p
basePosition :: Param -> ObjId -> [VisibleState] -> Team -> TacticalStateParam
basePosition param me vss attacker =
-- yield me's optimal position in relation to the ball and his state (attacking or defending)
let pI = vsPlayerInfo $ fetchVS vss me
defensivePos = piBasePosDefense pI
offensivePos = piBasePosOffense pI
myTeam = vsTeam $ fetchVS vss me
ball = fetchBallVS vss
posBall = projectP . vsPos $ ball
adjust = posBall .-. pitchCenter param
adjust' = vector2 (pHorizontalShiftRatio param * vector2X adjust) (pVerticalShiftRatio param * vector2Y adjust)
-- defPos = limitPosition (border, pitchWidth + border, border + lineEnds, pitchLength + border - lineEnds) (defensivePos .+^ adjust')
defPos = limitPosition (0, pPitchWidth param, pLineEnds param, pPitchLength param - pLineEnds param) (defensivePos .+^ adjust')
offPos = adjustForOffsite param offensivePos me vss
in TacticalStateParam (Just $ if myTeam == attacker then offPos else defPos)
Nothing
False
Nothing
Nothing
Nothing
Nothing
-- *************************************************************************
--
-- Various geometric functions
--
-- *************************************************************************
limitPosition :: RealFloat a => (a, a, a, a) -> Point2 a -> Point2 a
limitPosition (xmin, xmax, ymin, ymax) (Point2 px py) =
Point2 (minmax xmin xmax px) (minmax ymin ymax py)
where minmax min_ max_ z = if z < min_ then min_ else if z > max_ then max_ else z
noPoint :: Point2 Double
noPoint = Point2 (-1) (-1)
zeroVel :: Vector3 Double
zeroVel = vector3 0 0 0
sameDirection :: Param -> Velocity2 -> Velocity2 -> Bool
sameDirection param u v =
abs(vector2Rho u) > pEps param &&
abs((vector2Rho u + vector2Rho v) - vector2Rho (u ^+^ v)) < pEps param
inOneSecond :: AffineSpace p v a => p -> v -> p
inOneSecond pos vel = pos .+^ vel
(.+!) :: Point2 Double -> Double -> Point3 Double
(Point2 x y) .+! z = Point3 x y z
(^+!) :: Vector2 Double -> Double -> Vector3 Double
v ^+! z =
let x = vector2X v
y = vector2Y v
in vector3 x y z
project :: Velocity3 -> Velocity2
project v = vector2 x y
where x = vector3X v
y = vector3Y v
projectP :: Position3 -> Position2
projectP v = Point2 x y
where x = point3X v
y = point3Y v
brake :: Param -> Velocity3 -> Double -> Velocity3
brake param v a =
vector3 (-b*x) (-b*y) (pGravity param)
where x = vector3X v
y = vector3Y v
b = a / vector2Rho (vector2 x y)
halfPi :: Double
halfPi = pi / 2
(^-.) :: RealFloat a => Point2 a -> Vector2 a -> Point2 a
(Point2 x y) ^-. v = Point2 (vector2X v - x) (vector2Y v - y)
normTheta :: (Floating a, Ord a) => a -> a
normTheta x
| x < (- pi) = x + 2 * pi
| x > pi = x - 2 * pi
| otherwise = x
lookTo :: AffineSpace p v a => p -> p -> p
lookTo p0 p1 =
p0 .+^ (0.0001 *^ normalize (p1 .-. p0))
towards :: Spot -> Spot -> Vector3 Double
towards curr dest =
let delta = 20 *^ normalize (spotToPoint dest .-. spotToPoint curr)
in delta ^+! 0
turnBy :: Vector2 Double -> Vector2 Double -> Vector2 Double
u `turnBy` v =
fromPolar (vector2Theta u - vector2Theta v) (vector2Rho u)
isLeftFrom :: Vector2 Double -> Vector2 Double -> Bool
u `isLeftFrom` v =
normTheta (vector2Theta (u `turnBy` v)) >= 0
isRigthFrom :: Vector2 Double -> Vector2 Double -> Bool
u `isRigthFrom` v = v `isLeftFrom` u
sameDirAs :: Vector2 Double -> Vector2 Double -> Bool
u `sameDirAs` v =
let theta = normTheta $ vector2Theta (u `turnBy` v)
in
(theta > 0 && theta < pi/2) || (theta < 0 && theta > -(pi/2))
toPolar :: RealFloat t => t -> t -> (t, t)
toPolar x y =
(atan2 y x, sqrt ((x*x) + (y*y)))
getAngle :: RealFloat a => Vector2 a -> a
getAngle v = atan2 (vector2Y v) (vector2X v)
fromPolar :: RealFloat a => a -> a -> Vector2 a
fromPolar theta rho =
vector2 (rho * cos theta) (rho * sin theta)
fromPolar3 :: RealFloat a => a -> a -> a -> Vector3 a
fromPolar3 theta rho =
vector3 (rho * cos theta) (rho * sin theta)
limit :: (Ord a, VectorSpace v a) => a -> v -> v
limit max_ v =
let len = norm v
in
if len > max_ then (max_/len) *^ v else v
mirrorPoint :: RealFloat a => Point2 a -> Point2 a -> Point2 a
mirrorPoint (Point2 x y) (Point2 ax ay) =
Point2 (mirrorAt x ax) (mirrorAt y ay)
mirrorAt :: Num a => a -> a -> a
mirrorAt x axis = 2 * axis - x
sqr :: Num a => a -> a
sqr x = x * x
-- *************************************************************************
--
-- Parameters
--
-- *************************************************************************
awayGoalCenter :: Param -> Point2 Double
awayGoalCenter param = Point2 (pPitchWidth param / 2) 0
homeGoalCenter :: Param -> Point2 Double
homeGoalCenter param = Point2 (pPitchWidth param / 2) (pPitchLength param)
pitchCenter :: Param -> Point2 Double
pitchCenter param = Point2 (pPitchWidth param / 2) (pPitchLength param / 2)
-- *************************************************************************
--
-- Messages and commands
--
-- *************************************************************************
comToMsg :: Command -> [MessageBody]
comToMsg (CmdKickHigh dT) = [pm (PPTLoseMe, BSPRelease dT RTHigh)]
comToMsg (CmdKickLow dT) = [pm (PPTLoseMe, BSPRelease dT RTLow)]
comToMsg (CmdPassHigh dT) = [pm (PPTLoseMe, BSPPass dT RTHigh Nothing)]
comToMsg (CmdPassLow dT) = [pm (PPTLoseMe, BSPPass dT RTLow Nothing)]
comToMsg CmdFlipHigh = [pm (PPTLoseMe, BSPPass 1 RTHigh Nothing)]
comToMsg CmdFlipLow = [pm (PPTLoseMe, BSPPass 1 RTLow Nothing)]
--comToMsg pos CmdMoveForward = [tm (TPTMoveTo, TacticalStateParam (Just $ pos .+^ vector2 0 (-20))
-- Nothing False Nothing
-- Nothing Nothing)]
comToMsg _ = []
-- *************************************************************************
--
-- Various generic list and tupel functions
--
-- *************************************************************************
collect :: (Eq a) => [(a,b)] -> [(a, [b])]
collect [] = []
collect ((a, b) : rest) =
(a, b:fetch a rest) : collect (remove a rest)
fetch :: (Eq a) => a -> [(a,b)] -> [b]
fetch a = map snd . filter (\(a',_) -> a == a')
remove :: Eq a => a -> [(a, t)] -> [(a, t)]
remove a = filter (\(a',_) -> a /= a')
mergeList :: (Eq a) => [(a, [b])] -> [(a, [b])]
mergeList =
map (\(a,bs) -> (a, concat bs)) . collect
fst3 :: (t, t1, t2) -> t
fst3 (a, _, _) = a
snd3 :: (t, t1, t2) -> t1
snd3 (_, b, _) = b
thrd3 :: (t, t1, t2) -> t2
thrd3 (_, _, c) = c
fromRight :: Either t t1 -> t1
fromRight (Right x) = x