packages feed

foo-1.0: Foo.hs

-- ==================================
-- Module name: Foo
-- Project: Foo
-- Copyright (C) 2007  Bartosz Wójcik
-- Created on: 01.10.2007
-- Last update: 07.04.2008
-- Version: %

{-  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 3 of the License, or
    (at your option) any later version.

    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.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-}
-- ==================================
module Foo

where
import Data.List
import Data.Bits
import FooField
import FooMove

-- FooState  has been developed as a brilliant idea, that unfortunatelly hasn't worked like expected.
-- import FooState

-- The program is supposed to play Pencil Soccer against human.
-- The fileld is defined as a graph. The graph has edges everywhere there where there is a possibility to do a move.
-- Once move is done, this affects directly the field - appropriate edge is removed
-- (the idea is taken from Einstein - the space is not constant but changes affected by object and their moves).

-- AlphaBeta extention helps to prune lot of moves without validatig them. Is useless in case moves are pruned in the way it has been finaly done here.
-- #define AlphaBeta


-- Playing machine constists of:
-- playing algorithm
-- Bool - direction, whether played towards goal (_,0)
-- space of possible moves

data PlayingMachine = Play PlayingAlgorithm Bool Move
                    | MinValue                                             -- Min value to use function max
                    | MaxValue                                             -- Max value to use function min

-- List of algorithm can be completed. To do this you have to do 3 actions:
-- 1. Add algorithm name to the typedefinition
-- 2. Complete instance Ord for new algorithm (define how moves will be examined)
-- 3. Describe algorithm in the documentation.
data PlayingAlgorithm = GoAhead
                      | GoBackWatchOpponent
                      | GoAheadWatchOpponent
                      | PreventingOpponent40
                      | PreventingOpponent50
                      | Watch2Ahead
                      | Watch3Ahead
                      deriving (Eq,Ord,Enum,Read,Show)


moveOfMachine :: PlayingMachine -> Move
moveOfMachine (Play _ _ m) = m

-- Selects the best move out of given list of moves.
bestMove :: [PlayingMachine] -> Move
bestMove m = moveOfMachine $ foldr max MinValue m

-- Selects the best move out of given list of moves.
bestMoveWrapped :: [PlayingMachine] -> PlayingMachine
bestMoveWrapped m = foldr max MinValue m

-- Selects the worst move of the give tree of moves. This is in order to examine possible opponent moves.
worstMove :: [PlayingMachine] -> PlayingMachine
worstMove m = foldr min MaxValue m

-- =============
#ifdef AlphaBeta
-- =============

bestMoveAlphaBeta :: PlayingMachine -> [PlayingMachine] -> PlayingMachine
bestMoveAlphaBeta alpha m = foldr (maxOrBetterThan alpha) MinValue m

worstMoveAlphaBeta :: PlayingMachine -> [PlayingMachine] -> PlayingMachine
worstMoveAlphaBeta alpha m = foldr (minOrWorseThan alpha) MaxValue m

minOrWorseThan :: (Ord a) => a -> a -> a -> a
minOrWorseThan alpha x y | alpha > y = y
                         | otherwise = min x y

maxOrBetterThan :: (Ord a) => a -> a -> a -> a
maxOrBetterThan alpha x y | alpha < y = y
                          | otherwise = max x y
-- ======
#endif
-- ======


instance Eq (PlayingMachine) where
   MinValue == MinValue = True
   MaxValue == MaxValue = True
   _ == _ = False


-- =================================================
-- Instance of compare function is playing engine.
-- Max over all defined moves gives the chosen move.
-- =================================================
-- ================================
instance Ord (PlayingMachine) where
-- ================================
   -- There are compared moves of same generations (depths,plies). Generation doesn't access information about own predescor.
   -- If algorithm requires this information this has to be done in tricky way, not directly.

   -- Full description of MinValue and MaxValue constructors. --
   -- =============================================================================================== --
   compare MinValue _         = LT
   compare _        MinValue  = GT
   compare MaxValue _         = GT
   compare _        MaxValue  = LT
   -- =============================================================================================== --

   -- Full description of UnfinishedMove constructor. --
   -- It has been so described that such a move will never be selected.
   -- =============================================================================================== --
   compare (Play _ _ (UnfinishedMove _ _))              (Play _ _ (UnfinishedMove _ _))         = EQ
   compare (Play _ _ (UnfinishedMove _ i))              (Play _ _ _)  | odd i                   = GT
                                                                      | otherwise               = LT
   compare (Play _ _ _)                             (Play _ _ (UnfinishedMove _ i)) | odd i     = LT
                                                                                    | otherwise = GT
   -- =============================================================================================== --


   -- Full description of Goal constructor. --
   -- =============================================================================================== --
   compare (Play _ _ (Goal _))                          (Play _ _ (Goal _))                     = EQ
   compare (Play _ _ (Goal _))                          (Play _ _ _)                            = GT
   compare (Play _ _ _)                                 (Play _ _ (Goal _))                     = LT
   -- =============================================================================================== --

   -- Full description of HalfGoal constructor. --
   -- =============================================================================================== --
   compare (Play _ _ (HalfGoal _ _))                    (Play _ _ (HalfGoal _ _))               = EQ
   compare (Play _ _ (HalfGoal _ _))                    (Play _ _ _)                            = GT
   compare (Play _ _ _)                                 (Play _ _ (HalfGoal _ _))               = LT
   -- =============================================================================================== --

   -- Full description of LostGoal constructor. --
   -- =============================================================================================== --
   compare (Play _ d (LostGoal ((x,y):vs)))             (Play _ _ (LostGoal ((x',y'):vs')))     = EQ
   compare (Play _ _ _)                                 (Play _ _ (LostGoal _))                 = GT
   compare (Play _ _ (LostGoal _))                      (Play _ _ _)                            = LT
   -- =============================================================================================== --


   -- Specialization for GoAheadWatchOpponent algorithm.--
   -- =============================================================================================== --
   compare p@(Play GoAheadWatchOpponent _ (LastPass _ _ _ _ _ 0 _)) p' = compareWatchingOpponent p p'
   compare p p'@(Play GoAheadWatchOpponent _ (LastPass _ _ _ _ _ 0 _)) = compareWatchingOpponent p p'
   -- =============================================================================================== --
   -- =============================================================================================== --
   compare p@(Play GoBackWatchOpponent _ (LastPass _ _ _ _ _ 0 _)) p' = compareWatchingOpponent p p'
   compare p p'@(Play GoBackWatchOpponent _ (LastPass _ _ _ _ _ 0 _)) = compareWatchingOpponent p p'
   -- =============================================================================================== -- 


   -- Specialization for PreventingOpponent40 algorithm.--
   -- =============================================================================================== --
   compare (Play PreventingOpponent40 d m@(LastPass v g l w n i _))
           p@(Play PreventingOpponent40 _ (LostHalfGoal _ _))
#ifdef AlphaBeta
           | i == 0    = compare (worstMoveAlphaBeta p $ mapMove (Play PreventingOpponent40 d ) $ nextMove d (head v) g l w 1) p
#else
           | i == 0    = compare (worstMove $ mapSelectedMove (Play PreventingOpponent40 d ) PreventingOpponent40 $ nextMove d (head v) g l w 1) p
#endif
           | otherwise = GT
           -- ================== --
   compare p@(Play PreventingOpponent40 _ (LostHalfGoal _ _))
           (Play PreventingOpponent40 d m@(LastPass v g l w n i _))
           | i == 0    = compare p (worstMove $ mapSelectedMove (Play PreventingOpponent40 d ) PreventingOpponent40 $ nextMove d (head v) g l w 1)
           | otherwise = LT
           -- ================== --
   compare (Play PreventingOpponent40 d (LastPass ((x,y):vs)    g  l w n  i  _))
           (Play PreventingOpponent40 _ (LastPass ((x',y'):vs') g' _ _ n' i' _))
#ifdef AlphaBeta
           | i == 0                            = compare (worstMoveAlphaBeta alpha $ mapMove (Play PreventingOpponent40 d ) $ nextMove d (x,y)   g  l w 1)
                                                         (alpha)
#else
           | i == 0                            = compare (worstMove $ mapSelectedMove (Play PreventingOpponent40 d ) PreventingOpponent40 $ nextMove d (x,y)   g  l w 1)
                                                         (worstMove $ mapSelectedMove (Play PreventingOpponent40 d ) PreventingOpponent40 $ nextMove d (x',y') g' l w 1)
#endif

           | y > y' && not d                   = GT
           | y > y' &&  d                      = LT
           | y < y' && not d                   = LT
           | y < y' &&  d                      = GT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | otherwise                         = EQ
           where cTM  | yY < l' / 2 && d
                      || yY >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x
                      | yY >= l' / 2 && d
                      || yY < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x)
                 cTM' | yY' < l' / 2 && d
                      || yY' >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x'
                      | yY' >= l' / 2 && d
                      || yY' < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x')
                 l' = fromIntegral l
                 yY' = fromIntegral y'
                 yY = fromIntegral y
#ifdef AlphaBeta
                 alpha = worstMove $ mapMove (Play PreventingOpponent40 d ) $ nextMove d (x',y') g' l w 1
#endif
   -- =============================================================================================== --

   -- Specialization for PreventingOpponent50 algorithm.--
   -- =============================================================================================== --
   compare (Play PreventingOpponent50 d m@(LastPass v g l w n i _))
           p@(Play PreventingOpponent50 _ (LostHalfGoal _ _))
#ifndef AlphaBeta
           | i == 0    = compare (worstMove $ mapSelectedMove (Play PreventingOpponent50 d ) PreventingOpponent50 $ nextMove d (head v) g l w 1) p
#endif
           | otherwise = GT
           -- ================== --
   compare p@(Play PreventingOpponent50 _ (LostHalfGoal _ _))
           (Play PreventingOpponent50 d m@(LastPass v g l w n i _))
           | i == 0    = compare p (worstMove $ mapSelectedMove (Play PreventingOpponent50 d ) PreventingOpponent50 $ nextMove d (head v) g l w 1)
           | otherwise = LT
           -- ================== --
   compare (Play PreventingOpponent50 d (LastPass ((x,y):vs)    g  l w n  i  _))
           (Play PreventingOpponent50 _ (LastPass ((x',y'):vs') g' _ _ n' i' _))
#ifndef AlphaBeta
           | i == 0                            = compare (worstMove $ mapSelectedMove (Play PreventingOpponent50 d ) PreventingOpponent50 $ nextMove d (x,y)   g  l w 1)
                                                         (worstMove $ mapSelectedMove (Play PreventingOpponent50 d ) PreventingOpponent50 $ nextMove d (x',y') g' l w 1)
#endif

           | y > y' && not d                   = GT
           | y > y' &&  d                      = LT
           | y < y' && not d                   = LT
           | y < y' &&  d                      = GT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | otherwise                         = EQ
           where cTM  | yY < l' / 2 && d
                      || yY >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x
                      | yY >= l' / 2 && d
                      || yY < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x)
                 cTM' | yY' < l' / 2 && d
                      || yY' >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x'
                      | yY' >= l' / 2 && d
                      || yY' < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x')
                 l' = fromIntegral l
                 yY' = fromIntegral y'
                 yY = fromIntegral y
   -- =============================================================================================== --

{-   -- Specialization for GoBackLookForward algorithm.--
   -- =============================================================================================== --
   compare (Play GoBackLookForward d m@(LastPass v g l w n i _))
           p@(Play GoBackLookForward _ (LostHalfGoal _ _))
           | i == 0    = compare (worstMove $ mapMove (Play GoBackLookForward d ) $ nextMove d (head v) g l w 1) p
           | otherwise = GT
           -- ================== --
   compare p@(Play GoBackLookForward _ (LostHalfGoal _ _))
           (Play GoBackLookForward d m@(LastPass v g l w n i _))
           | i == 0    = compare p (worstMove $ mapMove (Play GoBackLookForward d ) $ nextMove d (head v) g l w 1)
           | otherwise = LT
           -- ================== --
   compare (Play GoBackLookForward d (LastPass ((x,y):vs)    g  l w n  i  _))
           (Play GoBackLookForward _ (LastPass ((x',y'):vs') g' _ _ n' i' _))
           | i == 0                            = compare (worstMove $ mapMove (Play GoBackLookForward d ) $ nextMove d (x,y)   g  l w 1)
                                                         (worstMove $ mapMove (Play GoBackLookForward d ) $ nextMove d (x',y') g' l w 1)
           | y > y' && not d                   = LT
           | y > y' &&  d                      = GT
           | y < y' && not d                   = GT
           | y < y' &&  d                      = LT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | otherwise                         = EQ
           where cTM  | yY < l' / 2 && d
                      || yY >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x
                      | yY >= l' / 2 && d
                      || yY < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x)
                 cTM' | yY' < l' / 2 && d
                      || yY' >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x'
                      | yY' >= l' / 2 && d
                      || yY' < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x')
                 l' = fromIntegral l
                 yY' = fromIntegral y'
                 yY = fromIntegral y
   -- =============================================================================================== -- -}


   -- Specialization for Watch2Ahead algorithm.--
   -- =============================================================================================== --
   compare (Play Watch2Ahead d m@(LastPass v g l w n i _))
           p@(Play Watch2Ahead _ (LostHalfGoal _ _))
#ifdef AlphaBeta
           | i == 0    = compare (worstMoveAlphaBeta p $ mapMove (Play Watch2Ahead d ) $ nextMove d (head v) g l w 1) p
           | i == 1    = compare (bestMoveAlphaBeta p $ mapMove (Play Watch2Ahead d ) $ nextMove d (head v) g l w 2) p
#else
           | i == 0    = compare (worstMove $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (head v) g l w 1) p
           | i == 1    = compare (bestMoveWrapped $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (head v) g l w 2) p
#endif
           | otherwise = GT
           -- ================== --
   compare p@(Play Watch2Ahead _ (LostHalfGoal _ _))
           (Play Watch2Ahead d m@(LastPass v g l w n i _))
           | i == 0    = compare p (worstMove $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (head v) g l w 1)
           | i == 1    = compare p (bestMoveWrapped $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (head v) g l w 2)
           | otherwise = LT
           -- ================== --
   compare (Play Watch2Ahead d (LastPass ((x,y):vs)    g  l w n  i  _))
           (Play Watch2Ahead _ (LastPass ((x',y'):vs') g' _ _ n' i' _))
#ifdef AlphaBeta
           | i == 0                            = compare (worstMoveAlphaBeta alpha $ mapMove (Play Watch2Ahead d ) $ nextMove d (x,y)   g  l w 1)
                                                         (alpha)
           | i == 1                            = compare (bestMoveAlphaBeta beta $ mapMove (Play Watch2Ahead d ) $ nextMove d (x,y)   g  l w 2)
                                                         (beta)
#else
           | i == 0                            = compare (worstMove $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (x,y)   g  l w 1)
                                                         (worstMove $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (x',y') g' l w 1)
           | i == 1                            = compare (bestMoveWrapped $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (x,y)   g  l w 2)
                                                         (bestMoveWrapped $ mapSelectedMove (Play Watch2Ahead d ) Watch2Ahead $ nextMove d (x',y') g' l w 2)
#endif
           | y > y' && not d                   = GT
           | y > y' &&  d                      = LT
           | y < y' && not d                   = LT
           | y < y' &&  d                      = GT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | otherwise                         = EQ
           where cTM  | yY < l' / 2 && d
                      || yY >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x
                      | yY >= l' / 2 && d
                      || yY < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x)
                 cTM' | yY' < l' / 2 && d
                      || yY' >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x'
                      | yY' >= l' / 2 && d
                      || yY' < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x')
                 l' = fromIntegral l
                 yY' = fromIntegral y'
                 yY = fromIntegral y
#ifdef AlphaBeta
                 alpha = worstMove $ mapMove (Play Watch2Ahead d ) $ nextMove d (x',y') g' l w 1
                 beta  = bestMoveWrapped $ mapMove (Play Watch2Ahead d ) $ nextMove d (x',y') g' l w 2
#endif
   -- =============================================================================================== --

   -- Specialization for Watch3Ahead algorithm.--
   -- =============================================================================================== --
   compare (Play Watch3Ahead d m@(LastPass v g l w n i _))
           p@(Play Watch3Ahead _ (LostHalfGoal _ _))
#ifndef AlphaBeta
           | i == 0    = compare (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 1) p
           | i == 1    = compare (bestMoveWrapped $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 2) p
           | i == 2    = compare (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 3) p
#endif
           | otherwise = GT
           -- ================== --
   compare p@(Play Watch3Ahead _ (LostHalfGoal _ _))
           (Play Watch3Ahead d m@(LastPass v g l w n i _))
           | i == 0    = compare p (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 1)
           | i == 1    = compare p (bestMoveWrapped $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 2)
           | i == 2    = compare p (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (head v) g l w 3)
           | otherwise = LT
           -- ================== --
   compare (Play Watch3Ahead d (LastPass ((x,y):vs)    g  l w n  i  _))
           (Play Watch3Ahead _ (LastPass ((x',y'):vs') g' _ _ n' i' _))
#ifndef AlphaBeta
           | i == 0                            = compare (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x,y)   g  l w 1)
                                                         (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x',y') g' l w 1)
           | i == 1                            = compare (bestMoveWrapped $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x,y)   g  l w 2)
                                                         (bestMoveWrapped $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x',y') g' l w 2)
           | i == 2                            = compare (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x,y)   g  l w 3)
                                                         (worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x',y') g' l w 3)
#endif
           | y > y' && not d                   = GT
           | y > y' &&  d                      = LT
           | y < y' && not d                   = LT
           | y < y' &&  d                      = GT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | otherwise                         = EQ
           where cTM  | yY < l' / 2 && d
                      || yY >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x
                      | yY >= l' / 2 && d
                      || yY < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x)
                 cTM' | yY' < l' / 2 && d
                      || yY' >= l' / 2 && not d = abs $ fromIntegral w / 2 - fromIntegral x'
                      | yY' >= l' / 2 && d
                      || yY' < l' / 2 && not d = fromIntegral w / 2 - abs (fromIntegral w / 2 - fromIntegral x')
                 l' = fromIntegral l
                 yY' = fromIntegral y'
                 yY = fromIntegral y
#ifdef AlphaBeta
                 alpha = worstMove $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x',y') g' l w 1
                 beta  = bestMoveWrapped $ mapSelectedMove (Play Watch3Ahead d ) Watch3Ahead $ nextMove d (x',y') g' l w 2
#endif
   -- =============================================================================================== --

   -- Basic description of LostHalfGoal constructor. --
   -- =============================================================================================== --
   compare (Play _ _ (LostHalfGoal _ _))         (Play _ _ (LostHalfGoal _ _))        = EQ
   compare (Play _ _ (LastPass _ _ _ _ _ i _))   (Play _ _ (LostHalfGoal _ _))        = GT
   compare (Play _ _ (LostHalfGoal _ _))         (Play _ _ (LastPass _ _ _ _ _ i' _)) = LT
   -- =============================================================================================== --

   compare (Play a  d  (LastPass ((x,y):vs)    g  l  w  n  i  m))
           (Play a' d' (LastPass ((x',y'):vs') g' l' w' n' i' m'))
           | y > y' && not d                   = GT
           | y > y' &&  d                      = LT
           | y < y' && not d                   = LT
           | y < y' &&  d                      = GT
           | y == y' && cTM < cTM'             = GT
           | y == y' && cTM > cTM'             = LT
           | y == y' && cTM == cTM' && n > n'  = GT
           | y == y' && cTM == cTM' && n < n'  = LT
           | y == y' && cTM == cTM' && n == n' = EQ
           where cTM  = abs $ fromIntegral w / 2 - fromIntegral x
                 cTM' = abs $ fromIntegral w / 2 - fromIntegral x'

   compare _ _ = EQ       -- just in case
-- ====================================
-- end of instance Ord (PlayingMachine)
-- ====================================
-- ----------------------------------------------------------------------

-- Idea is following.
-- If opponent can score a goal after current move this move is value 0.
-- If move is LostHalfGoal its value is 1.
-- If move is Goal, its value is VERY BIG.
-- Otherwise value of the move is algorithm dependent; higher, closer to opponent goal move finishes.
-- Then compare moves.
compareWatchingOpponent :: PlayingMachine -> PlayingMachine -> Ordering
compareWatchingOpponent p1 p2 | p1' == 0   = LT
                              | p2' == 0   = GT
                              | otherwise  = compare (valueOfMove p1) (valueOfMove p2)
                              where p1' = valueOfMove $ nextWorstMove p1
                                    p2' = valueOfMove $ nextWorstMove p2

-- Select worst (for us) opponent answer.
nextWorstMove :: PlayingMachine -> PlayingMachine
nextWorstMove (Play a d (LastPass ((x,y):vs) g l w _ i _)) = worstMove $ mapMove (Play a d ) $ nextMove d (x,y) g l w (i + 1)
nextWorstMove p = p

-- Give value of the move.
valueOfMove :: PlayingMachine -> Int
-- Different algorithms use different ways of scoring.
valueOfMove (Play GoBackWatchOpponent d (LastPass ((x,y):vs) _ l w n _ _)) | d         = y*l + w - (round $ abs $ fromIntegral w / 2 - fromIntegral x) + n
                                                                           | otherwise = (l-y)*l + w - (round $ abs $ fromIntegral w / 2 - fromIntegral x) + n
valueOfMove (Play _ d (LastPass ((x,y):vs) _ l w n _ _)) | d         = (l-y)*l + w - (round $ abs $ fromIntegral w / 2 - fromIntegral x) + n
                                                         | otherwise = y*l + w - (round $ abs $ fromIntegral w / 2 - fromIntegral x) + n
valueOfMove (Play _ _ (LostGoal _)) = 0
valueOfMove (Play _ _ (LostHalfGoal _ _)) = 1
valueOfMove (Play _ _ (HalfGoal _ _)) = 1000
valueOfMove _ = 0



-- Maps given function on selected leaves of tree of moves.
-- It select moves that finish match and given number of the others.
-- This function has very important meaning for the efficiency and speed of playing machines. 
-- It is a main hash function. It bases on observations, that:
--    Longer moves diminish space of moves, so selecting longer moves leads to faster playing machines
--    When many moves finish in the same vetrex, longer ones are usually not worse than shorter. 
--    Short moves shoudn't be pruned if they are Goal, LostGoal, HalfGoal nad LostHalfGoal. Otherwise they might be omitted by plaiyng machine.
-- Balance between speed when not much moves are selected to next ply and risk that some important moves will be omitted has been selected on experimental way.
-- It is not ensured that all possible end vertices are represented by at least 1 move. This would be strong improvement of this function
-- although would lead to additional costs that then would need to be checed if they are acceptable.   

mapSelectedMove f  PreventingOpponent40       m = takeLastMove 40 0 $ mapMove f m
mapSelectedMove f  PreventingOpponent50       m = takeLastMove 50 0 $ mapMove f m
mapSelectedMove f  Watch2Ahead                m = takeLastMove 30 0 $ mapMove f m
mapSelectedMove f  Watch3Ahead                m = takeLastMove 18 0 $ mapMove f m
mapSelectedMove f  _                          m = takeLastMove 1000 0 $ mapMove f m

-- Takes from the list of moves (wrapped into PlayingMachine) one move each type that finishes match (assuming they are always firsts elements of the list)
-- and 'n' next elements (they are supposed to be LastPass)
takeLastMove :: Int -> Int -> [PlayingMachine] -> [PlayingMachine]
takeLastMove n bits lp@((Play _ _ (LastPass _ _ _ _ _ _ _)):ls)                 = take n lp
takeLastMove n bits (p@(Play _ _ (Goal _)):lp)                 | testBit bits 0 = takeLastMove n bits lp
                                                               | otherwise      = p : takeLastMove n (setBit bits 0) lp
takeLastMove n bits (p@(Play _ _ (HalfGoal _ _)):lp)           | testBit bits 1 = takeLastMove n bits lp
                                                               | otherwise      = p : takeLastMove n (setBit bits 1) lp
takeLastMove n bits (p@(Play _ _ (LostGoal _)):lp)             | testBit bits 2 = takeLastMove n bits lp
                                                               | otherwise      = p : takeLastMove n (setBit bits 2) lp
takeLastMove n bits (p@(Play _ _ (LostHalfGoal _ _)):lp)       | testBit bits 3 = takeLastMove n bits lp
                                                               | otherwise      = p : takeLastMove n (setBit bits 3) lp
takeLastMove n bits (p:lp)                                                      = p : takeLastMove n bits lp
takeLastMove n _ []                                                             = []