Home > Uncategorized > Battle Game In Haskell #2 – Fighting using recursion and Either

Battle Game In Haskell #2 – Fighting using recursion and Either

October 28th, 2012 Leave a comment Go to comments

Welcome back! Last time we laid the basics of the battle game by applying damage to a unit. However, we had to specify the amount of damage, while it would be more realistic that the damage results from a hit of a hostile unit.

Shooting a unit

The following modifications include some refactors (record accessor for Firepower, cond utility), and more importantly the shoot function where a unit shoots and damages an other unit. The main function was also updated to demonstrate the changes.

-- made to be a record for easy access
data Firepower = Firepower { runFp :: Int }
     deriving Show

-- helper fun
cond :: Bool -> a -> Maybe a
cond True x = Just a
cond False _ = Nothing

-- changed pattern matching to record matching
-- changed if-then-Just-else-Nothing to cond
damage :: Unit -> Int -> Maybe Unit
damage u @ Unit { remainingHp = HP hp } dmg =
       cond (hp > dmg) $ u { remainingHp = HP $ hp - dmg }

-- unit shoots an other unit, which may die
shoot :: Unit -> Unit -> Maybe Unit
shoot x y = damage y (runFp $ firepower x)

-- rename repeatM to repeatBind

main :: IO ()
main = do
     let unit1 = Unit (HP 100) (Firepower 5)
     let unit2 = Unit (HP 50) (Firepower 12)
     let unit2' = repeatBind 5 (Just unit2) (shoot unit1)
     putStrLn $ show (remainingHp unit2) ++ " --> " ++ show (fmap remainingHp unit2')

Fighting until death

While a single shot is more than nothing, let’s make two units fight until death on an encounter.

-- fight has a bias, the left unit gets to shoot first
fight :: Unit -> Unit -> Either Unit Unit
fight x y = 
  let damagedY = x `shoot` y
  in maybe (Left x) (\y' -> swap $ fight y' x) damagedY

-- utility
swap :: Either a b -> Either b a
swap (Left x)  = Right x 
swap (Right x) = Left x

main :: IO ()
main = do
     let unit1 = Unit (HP 100) (Firepower  5)
     let unit2 = Unit (HP  50) (Firepower 12)
     let winner = unit1 `fight` unit2
     putStrLn $ show unit1 ++ " vs " ++ show unit2 ++ "  --> " ++ show winner 
The fight function might need some explanation: The first (or left) unit shoots the second (or right) one, so the right unit may be dead already. The maybe function is the folding function for the Maybe data type, it converts both Just x and Nothing to a value of a given type. Here we apply it for the damaged right unit, and if it is dead, then the left unit is the winner. Otherwise the roles are swapped, and the right unit gets to shoot the left one. Note that since we deliberately swap the roles in the next fight, the result of that fight needs to be swapped back too. The resulting full Main.hs file is here, which also includes a custom Show instance for Unit so units are printed a bit nicer.

Next time

So now we can get two hostile units together, and observe the final outcome of the battle. However, we have no information on how did the fight reach that outcome. This is especially frustrating if we would like to animate the process of the fight, instead of just making one of the units disappear. Therefore next time we will add logging using theWriter monad (combined with some monad transformers), so the full flow of events will be captured. Happy hacking!

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)
  1. No comments yet.
  1. No trackbacks yet.