diff --git a/haskell/vindinium/app/Main.hs b/haskell/vindinium/app/Main.hs index 874416a..d669ed3 100644 --- a/haskell/vindinium/app/Main.hs +++ b/haskell/vindinium/app/Main.hs @@ -27,7 +27,7 @@ loop1 pos depth acc in loop1 sim (depth - 1) acc' sim1 :: Pos -> (Int, Pos) -sim1 pos = (\(val, (_,_, pos, _)) -> (val, pos)) (simulate board1 (0, 100, pos, singleton (0,4))) +sim1 pos = (\(val, (_,_, pos, _,_)) -> (val, pos)) (simulate board1 (0, 100, pos, singleton (0,4), empty)) board1 :: Board board1 = fromList $ fmap fromList diff --git a/haskell/vindinium/package.yaml b/haskell/vindinium/package.yaml index 9fd949d..4392b34 100644 --- a/haskell/vindinium/package.yaml +++ b/haskell/vindinium/package.yaml @@ -32,6 +32,7 @@ dependencies: - haskell-src-exts - vector - mtl +- time library: source-dirs: src diff --git a/haskell/vindinium/src/Player.hs b/haskell/vindinium/src/Player.hs index 32bf9e7..0329360 100644 --- a/haskell/vindinium/src/Player.hs +++ b/haskell/vindinium/src/Player.hs @@ -10,6 +10,7 @@ import System.Random import Data.Char (digitToInt) import Data.List as L import qualified Data.Vector as V +import Data.Time.Clock.POSIX import BotRunner import Graph @@ -49,6 +50,7 @@ bot readLine writeLine = do -- game loop forever $ do + t1 <- getPOSIXTime input_line <- getLine let entitycount = read input_line :: Int -- the number of entities @@ -71,6 +73,9 @@ bot readLine writeLine = do let hero = V.head $ V.filter (\e -> case e of EHero id _ _ _ -> id == myId _ -> False) heroes + let enemies = V.filter (\e -> case e of + EHero id _ _ _ -> id /= myId + _ -> False) heroes let mines = V.filter (\e -> case e of EMine oId _ -> oId /= myId _ -> False) entities @@ -81,23 +86,26 @@ bot readLine writeLine = do EMine oId _ -> oId == myId _ -> False) entities - let gs = gameState hero $ fmap posFromEntity myMines + let gs = gameState hero (fmap posFromEntity myMines) (fmap posFromEntity enemies) let oldMines = length $ getMines gs let sim = simulate board gs let newMines = length $ getMines $ snd sim - let cmd = if newMines - oldMines > 0 - then (\(_,(_,_,pos,_)) -> moveToPos pos) sim - else case life hero of - Just lp -> if lp < 30 then moveToPos minTavernPos else moveToEntity minEMine - Nothing -> moveToEntity minEMine + let cmd = (\(_,(_,_,pos,_,_)) -> moveToPos pos) sim - -- hPrint stderr $ newMines - oldMines - -- hPrint stderr minEMine + -- let cmd = if newMines - oldMines > 0 + -- then (\(_,(_,_,pos,_,_)) -> moveToPos pos) sim + -- else case life hero of + -- Just lp -> if lp < 30 then moveToPos minTavernPos else moveToEntity minEMine + -- Nothing -> moveToEntity minEMine + + t2 <- getPOSIXTime + hPrint stderr $ newMines - oldMines + hPrint stderr $ round $ 1000 * (t2 - t1) putStrLn cmd getMines :: GameState -> V.Vector Pos -getMines (_,_,_,m) = m +getMines (_,_,_,m,_) = m moveToEntity :: Entity -> String moveToEntity e = case e of @@ -116,9 +124,9 @@ posFromEntity :: Entity -> (Int, Int) posFromEntity (EHero _ p _ _) = p posFromEntity (EMine _ p) = p -gameState :: Entity -> V.Vector Pos -> GameState -gameState (EHero _ pos l g) mines = (g, l, pos, mines) -gameState (EMine _ pos) mines = (-1, -1, pos, mines) +gameState :: Entity -> V.Vector Pos -> V.Vector Pos -> GameState +gameState (EHero _ pos l g) mines enemies = (g, l, pos, mines, enemies) +gameState (EMine _ pos) mines enemies = (-1, -1, pos, mines, enemies) isTavern :: BoardEntity -> Bool isTavern Tavern = True diff --git a/haskell/vindinium/src/Simulation/Board.hs b/haskell/vindinium/src/Simulation/Board.hs index e924148..0cbfa92 100644 --- a/haskell/vindinium/src/Simulation/Board.hs +++ b/haskell/vindinium/src/Simulation/Board.hs @@ -9,8 +9,9 @@ import Control.Monad.State as S import Control.Monad.State.Class import Data.List as L import Simulation.Data +import Simulation.Lib -searchDepth = 9 +searchDepth = 8 -- fromPlayerBoard :: Board -> BoardInternal -- fromPlayerBoard pBoardInternal = fmap (fmap $ fromEnum) asVector @@ -24,7 +25,7 @@ simulate :: Board -> GameState -> (Int, GameState) simulate board = simulateMove board (-1,-1) searchDepth simulateMove :: Board -> Pos -> Int -> GameState -> (Int, GameState) -simulateMove board prevPos depth state@(_,_,pos,_) +simulateMove board prevPos depth state@(_,_,pos,_,_) | depth == 0 = let state' = evalMove board state in (evalGameState state', state') @@ -34,37 +35,38 @@ simulateMove board prevPos depth state@(_,_,pos,_) -- der Trick: in einem Zug muss die Minenposition zurueckgegeben werden, die Position des Helden -- aendert sich aber nicht. Im naechsten Zug will der Held dann nicht mehr die Mine erobern. pos' = if bPos == Tavern || bPos == Mine then prevPos else pos -- move back out of tavern/mine - -- pos wird nicht benutzt. Pos ist aber der Wert, der zurueckgegeben werden muss, damit sich der Held in die Taverne bewegt - moves = filter (posValid board state) $ possibleMoves pos' + moves = V.filter (posValid board state) $ possibleMoves pos' vals = fmap (\pos'' -> simulateMove board pos' (depth-1) (updatePos pos'' state')) moves valsWithOldPos = if depth == searchDepth then vals -- return position of submove on first level - else zip (fmap fst vals) $ fmap (updatePos pos . snd) vals -- return starting position otherwise -- pos' + else V.zip (fmap fst vals) $ fmap (updatePos pos . snd) vals -- return starting position otherwise -- pos' in L.maximumBy (\(v1, _) (v2, _) -> compare v1 v2) valsWithOldPos updatePos :: Pos -> GameState -> GameState -updatePos pos (gold, life, _, mines) = (gold, life, pos, mines) +updatePos pos (gold, life, _, mines, enemies) = (gold, life, pos, mines, enemies) -- update State according to hero position on board -- executed every move evalMove :: Board -> GameState -> GameState -evalMove board state@(gold, life, pos, mines) = evalDeath evalBuildings +evalMove board state@(gold, life, pos, mines, enemies) = evalDeath $ evalEnemies evalBuildings where evalBuildings - | entity == Air = (gold + length mines, thirst life, pos, mines) - | entity == SpawnPoint = (gold + length mines, thirst life, pos, mines) + | entity == Air = (gold + length mines, thirst life, pos, mines, enemies) + | entity == SpawnPoint = (gold + length mines, thirst life, pos, mines, enemies) | entity == Tavern = if gold >= 2 then ( gold + length mines - 2 , min 100 (life + 50) -- TODO: Check if life is +19 , pos , mines + , enemies ) else ( gold + length mines , thirst life , pos , mines + , enemies ) | entity == Mine = let addMine = pos `V.notElem` mines @@ -74,12 +76,18 @@ evalMove board state@(gold, life, pos, mines) = evalDeath evalBuildings , if addMine then thirst life - 20 else thirst life , pos , mines' + , enemies ) | entity == Wall = state -- should never happen where entity = boardPos board pos - evalDeath state'@(gold', life', pos', mines') - | life' < 5 = (gold', 100, (0,0), V.empty) -- TODO: starting position is not 0,0 but spawnpoint + -- TODO: Gegner verliert auch Leben + evalEnemies :: GameState -> GameState + evalEnemies state'@(gold', life', pos', mines', enemies') + | any (<2) $ fmap (dist pos') enemies' = (gold', life' - 20, pos', mines', enemies') + | otherwise = state' + evalDeath state'@(gold', life', pos', mines', enemies') + | life' < 5 = (gold', 100, (0,0), V.empty, enemies') -- TODO: starting position is not 0,0 but spawnpoint | otherwise = state' thirst life = max 1 (life - 1) @@ -87,21 +95,21 @@ thirst life = max 1 (life - 1) -- retuns the evalutaion of the current move -- executed if maximum depth is reached evalGameState :: GameState -> Int -evalGameState (gold, life, _, mines) = gold + (life `div` 10) + length mines * 2 +evalGameState (gold, life, _, mines, _) = gold + (life `div` 10) + length mines * 2 -- TODO: Warum macht nur mines quatsch? -- get BoardInternalEntity Enum of Pos on BoardInternal boardPos :: Board -> Pos -> BoardEntity boardPos board (x,y) = (board V.! y) V.! x posValid :: Board -> GameState -> Pos -> Bool -posValid board (_, _, _, mines) pos@(x,y) = onBoardInternal && boardPos' /= Wall && pos `notElem` mines +posValid board (_, _, _, mines, _) pos@(x,y) = onBoardInternal && boardPos' /= Wall && pos `notElem` mines where size = length board boardPos' = boardPos board pos onBoardInternal = x >= 0 && x < size && y >= 0 && y < size -possibleMoves :: Pos -> [Pos] -possibleMoves (x,y) = [ (x+1, y), (x, y+1), (x-1, y), (x, y-1) ] +possibleMoves :: Pos -> V.Vector Pos +possibleMoves (x,y) = V.fromList [ (x+1, y), (x, y+1), (x-1, y), (x, y-1) ] data Tree v = Node v (Tree v) | Leaf v \ No newline at end of file diff --git a/haskell/vindinium/src/Simulation/Data.hs b/haskell/vindinium/src/Simulation/Data.hs index ecaf7bb..88bf716 100644 --- a/haskell/vindinium/src/Simulation/Data.hs +++ b/haskell/vindinium/src/Simulation/Data.hs @@ -10,5 +10,5 @@ type IndexedBoard = V.Vector (Pos, BoardEntity) type Pos = (Int, Int) --- (gold, life, hero pos, own mines) -type GameState = (Int, Int, Pos, V.Vector Pos) +-- (gold, life, hero pos, own mines, enemies) +type GameState = (Int, Int, Pos, V.Vector Pos, V.Vector Pos) diff --git a/haskell/vindinium/stackproject.cabal b/haskell/vindinium/stackproject.cabal index d1bc234..c5dd978 100644 --- a/haskell/vindinium/stackproject.cabal +++ b/haskell/vindinium/stackproject.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: a40eac4da8b91ec478d65b8403d0b4c79040e0e5d0ae95d897222ea2c05cb732 +-- hash: 417e6c9b6226b7bcecca4ed2838a83b02bcb813752e3a15911e72c73db470c74 name: stackproject version: 0.1.0.0 @@ -51,6 +51,7 @@ library , haskell-src-exts , mtl , random + , time , vector default-language: Haskell2010 @@ -74,6 +75,7 @@ executable stackproject-exe , mtl , random , stackproject + , time , vector default-language: Haskell2010 @@ -98,5 +100,6 @@ test-suite stackproject-test , mtl , random , stackproject + , time , vector default-language: Haskell2010