-- Game.hs -- Tom Moertel -- CVS $Id: Game.hs,v 1.5 2002/09/06 04:03:51 thor Exp $ -- | The Game module provides types and functions for reading, -- representing, and analyzing games. module Game ( Game(..), GameTurn(..), TurnHistory , readProxyLog , mergeGames , gameHistory ) where import Data.List (sort, union) import BasicTypes import Board import Commands -- | Game represents a complete game -- its entire history, as -- recorded from a player robot(s)'s point of view. Values of type -- Game are represented in a way that conveniently allows different -- robots' views of the a game to be merged to allow for a more -- complete understanding of what happened during the game. data Game = Game { -- | Handshake the recording robot sent to -- the server to announce itself. If this -- is a merged Game, there wil be one -- handshake per robot that contributed -- a recording. gamePlayerHandshakes :: [( RobotID, String )] -- | Game board that the server sent , gameBoard :: Board -- | Robot configuration that server sent -- (one for each robot that contributed -- a recording) , gameRobotConfigs :: [( RobotID, RobotConfiguration )] -- | Initial reponse from server , gameInitialResponse :: [RobotCommandList] -- | All the turns of the game , gameTurns :: [GameTurn] -- | Any final information provided -- by the server after a robot -- died or the game ended (one entry -- per robot that contributed a -- recording). The 'RobotID' -- identifies the robot that -- received the epilogue -- messages. The @Int@ gives the -- turn on which the robot died or -- otherwise finished the game. The -- messages themselves are given in -- the @[String]@ field. , gameEpilogues :: [( RobotID , Int , [String] )] } deriving (Show, Read, Eq) -- | Represents a turn of game play. data GameTurn = GameTurn { -- | Server's list of packages seen -- by robot player(s) this turn gtPackageInfo :: [( RobotID , [PackageInfo] )] -- | Commands sent by robot(s) to server , gtPlayerCommands :: [( RobotID, String )] -- | Response from server , gtServerResponse :: [ RobotCommandList ] } deriving (Show, Read, Eq, Ord) -- |Gets all the server information updates, which together represent a history -- of what happended during the game. type TurnHistory = ([(RobotID, [PackageInfo])], [RobotCommandList]) gameHistory :: Game -> [TurnHistory] gameHistory g = ([], gameInitialResponse g) : zip (map gtPackageInfo (gameTurns g)) (map gtServerResponse (gameTurns g)) -- |Merges two Game values into a composite Game. Useful for -- combining Games that were recorded from the perspectives of -- different robots into a single multi-perspective Game. mergeGames :: Game -> Game -> Game mergeGames gA gB | differentBoards = error "Game boards are not the same." | incompatibleTurns = error "Game histories are not from the same game." | otherwise = mergedAB where differentBoards = (gameBoard gA) /= (gameBoard gB) incompatibleTurns = or (zipWith (/=) (turnSummary gA) (turnSummary gB)) turnSummary = map snd . gameHistory mergedAB = Game { gamePlayerHandshakes = handshakesAB , gameBoard = gameBoard gA , gameRobotConfigs = configsAB , gameInitialResponse = gameInitialResponse gA , gameTurns = turnsAB , gameEpilogues = epilogueAB } where handshakesAB = mergePart gamePlayerHandshakes configsAB = mergePart gameRobotConfigs epilogueAB = mergePart gameEpilogues mergePart part = sort $ (part gA) `union` (part gB) -- merging turns is a special case because the lists of turns -- from game A and game B may be of different lengths (some -- robots may have died earlier than others), so we merge into -- the longer of the two turnsAB = zipWith mergeTurn long short ++ drop (length short) long (gtA, gtB) = (gameTurns gA, gameTurns gB) (lenA, lenB) = (length gtA, length gtB) (long, short) = if lenA > lenB then (gtA,gtB) else (gtB,gtA) mergeTurn ta tb = ta { gtPackageInfo = mergeTPart gtPackageInfo , gtPlayerCommands = mergeTPart gtPlayerCommands } where mergeTPart part = sort $ (part ta) `union` (part tb) -- |Reads a game log recorded by RobotProxy and converts it into a -- Game. readProxyLog :: String -> Game readProxyLog = readProxyLogLines . map (tail . snd . break (==' ')) -- strip labels . lines where readProxyLogLines (hdshake:board:config:initRCL:turnsAndEpilogue) = Game { gamePlayerHandshakes = addID $ read hdshake , gameBoard = read board , gameRobotConfigs = addID $ robotConfig , gameInitialResponse = read initRCL , gameTurns = turns , gameEpilogues = [( robotID , length turns , epilogue )] } where robotConfig = read config robotID = rcRbtID robotConfig addID x = [(robotID, x)] (turns, epilogue) = splitTurns turnsAndEpilogue splitTurns (pkgInfo:playerCmd:serverResponse:rest) = case serverResponse of '[':_ -> let (turns', epl) = splitTurns rest in ( GameTurn (addID (read pkgInfo)) (addID (read playerCmd)) (read serverResponse) : turns' , epl ) _ -> ( [ GameTurn (addID (read pkgInfo)) (addID (read playerCmd)) (read "[]") ] , map read (serverResponse:rest) ) splitTurns _ = ([],[]) -- ================================================================= -- -- Copyright (C) 2002 Thomas Moertel. -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- as published by the Free Software Foundation; either version 2 -- of the License, or (at your option) any later version. -- -- The text of the GNU GPL may be found in the LICENSE file, -- included with this software, or online at the following URL: -- -- http://www.gnu.org/copyleft/gpl.html -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- Except as provided for under the terms of the GNU GPL, all rights -- are reserved worldwide. -- -- =================================================================