Shoot, I had some great example code from a few years ago but it was part of a uni assignment so the university made us take it off github.
Here's a fairly in-depth analysis of the approach: http://www.haskellforall.com/2013/05/program-imperatively-us...
The gist of it is that StateT gives you nice syntactic sugar around state-update functions of type `state -> (state, result)`.
Let's say your state type is like
data Boss = Boss {
health :: Int,
...
}
data Player = ...
data State = State {
boss :: Boss,
player :: Player,
...
}
Using Lenses, you can construct StateT operations like this:
boss.health -= 1
player.stats.score += 5
Where each of those updates the state as you imagine. So your code just looks like a bunch of stateful OO-style updates, but it's actually pure. This also lets you do a bunch of cool stuff that you can't do with real imperative code in this fashion, like re-interpret the semantics of assignment or modification, but that's for another time.