The AI code for my little soccer game basically works like this: There is a function that derives facts that describe the current situation on the pitch:
deriveFacts :: GameState -> [Fact]
... where the facts look kind of like this:
data Fact =
FactCanIntercept ObjId ObjId
| FactBestPosition Team Spot Spot
| FactKickOff
| FactBallCarrier ObjId
| FactBestShootingVector Velocity3
| FactBestPassingVector Velocity3
| ...
... and there are rules that look uniformly like this:
rule_shoot facts = do
FactBallCarrier ballCarrier <- checkBallCarrier facts
FactBestShootingVector goalVector <- checkBestShootingVector facts
return [message (ballCarrier, shoot goalVector)]
And then there is a rule engine that runs all the rules and gathers up the resulting messages.
That's fine so far and works quite nicely. What's kind of annoying, though: In this approach, I need an "accessor" function like this for every Fact:
checkBestShootingVector :: [Fact] -> Maybe Fact
checkBestShootingVector facts =
listToMaybe [e | e@(FactBestShootingVector {}) <- facts]
This approach leads to a lot of ugly boilerplate code. My question: Is there an elegant solution that removes the need for creating these accessor functions by hand?