packages feed

algebra-driven-design-0.1.0.0: src/ADD/Games/Correct.hs

{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE DerivingStrategies    #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}

module ADD.Games.Correct where

import Data.Foldable
import qualified Data.Set as S
import Data.Set (Set)
import Data.Data
import Data.Word
import GHC.Generics
import Test.QuickCheck hiding (Result)
import Control.Monad.Writer
import Data.Tuple (swap)
import Data.List
import QuickSpec

data Event = Event Word8
  deriving stock (Eq, Ord, Show, Data, Generic)

-- # ArbitraryEvent
instance Arbitrary Event where
  arbitrary = Event <$> arbitrary
  shrink    = genericShrink


data EventFilter
  = Always
  | Never
  | Exactly Word8  -- ! 1
  deriving stock (Eq, Ord, Show, Data, Generic)

-- # ArbitraryEventFilter
instance Arbitrary EventFilter where
  arbitrary = frequency
    [ (3, pure Always)
    , (1, pure Never)
    , (5, Exactly <$> arbitrary)
    ]
  shrink = genericShrink

always :: EventFilter
always = Always

never :: EventFilter
never = Never

sig_filters :: Sig
sig_filters = signature
  [ con "always" always
  , con  "never" never
  ]


data Reward = Reward Word8
  deriving stock (Eq, Ord, Show, Data, Generic)

-- # ArbitraryReward
instance Arbitrary Reward where
  arbitrary = Reward <$> arbitrary
  shrink    = genericShrink


data Result
  = Victory
  | Defeat
  deriving stock (Eq, Ord, Show, Data, Generic)

-- # ArbitraryResult
instance Arbitrary Result where
  arbitrary = elements [ victory, defeat ]
  shrink    = genericShrink

victory :: Result
victory = Victory

defeat :: Result
defeat = Defeat

sig_results :: Sig
sig_results = signature
  [ con "victory" victory
  , con "defeat"  defeat
  ]


------------------------------------------------------------------------------
--                         constructors
------------------------------------------------------------------------------

data Game
  = Win
  | Lose
  | RewardThen Reward Game
  | Subgame Game Game Game
  | EitherW Game Game
  | Both Game Game
  | Race Game Game
  | Multigate [(EventFilter, Game)]
  deriving stock (Eq, Ord, Show, Data, Generic)

-- # ArbitraryGame
instance Arbitrary Game where
  arbitrary = sized $ \n ->
    case n <= 1 of
      True -> elements [win, lose]
      False -> frequency
        [ (3, pure win)
        , (3, pure lose)
        , (3, reward  <$> arbitrary)
        , (5, rewardThen <$> arbitrary
                         <*> decayArbitrary 2)
        , (5, andThen <$> decayArbitrary 2
                      <*> decayArbitrary 2)
        , (5, subgame <$> decayArbitrary 3
                      <*> decayArbitrary 3
                      <*> decayArbitrary 3)
        , (5, both <$> decayArbitrary 2
                   <*> decayArbitrary 2)
        , (5, eitherG <$> decayArbitrary 2
                      <*> decayArbitrary 2)
        , (5, race <$> decayArbitrary 2
                   <*> decayArbitrary 2)
        , (5, multigate <$> decayArbitrary 5)
        , (2, comeback  <$> arbitrary)
        , (1, pure bottom)
        , (5, gate <$> arbitrary <*> arbitrary)
        ]
  shrink = genericShrink

-- # ObserveGame
instance
    Observe [Event] (Set Reward, Maybe Result) Game
    where
  observe = runGame

decayArbitrary :: Arbitrary a => Int -> Gen a
decayArbitrary n = scale (`div` n) arbitrary

reward :: Reward -> Game
reward r = rewardThen r win

rewardThen :: Reward -> Game -> Game
rewardThen = RewardThen

win :: Game
win = Win

lose :: Game
lose = Lose

andThen :: Game -> Game -> Game
andThen g1 g2 = subgame g1 g2 lose

subgame :: Game -> Game -> Game -> Game
subgame (RewardThen r g) g1 g2 =
  rewardThen r (subgame g g1 g2)
subgame Win  g1 _  = g1
subgame Lose _  g2 = g2
subgame g    g1 g2 = Subgame g g1 g2

eitherG :: Game -> Game -> Game
eitherG (RewardThen r g1) g2 =
  rewardThen r (eitherG g1 g2)
eitherG g1 (RewardThen r g2) =
  rewardThen r (eitherG g1 g2)
eitherG Lose Lose = lose
eitherG Win  _    = win
eitherG _    Win  = win
eitherG a    b    = EitherW a b

both :: Game -> Game -> Game
both (RewardThen r g1) g2 = rewardThen r (both g1 g2)
both g1 (RewardThen r g2) = rewardThen r (both g1 g2)
both Win  Win  = win
both Lose _    = lose
both _    Lose = lose
both a    b    = Both a b

race :: Game -> Game -> Game
race (RewardThen r g1) g2 = rewardThen r (race g1 g2)
race g1 (RewardThen r g2) = rewardThen r (race g1 g2)
race Win  _ = win
race Lose _ = lose
race _ Win  = win
race _ Lose = lose
race a b    = Race a b

multigate :: [(EventFilter, Game)] -> Game
multigate cs = Multigate cs

sig_games_core :: Sig
sig_games_core = signature
  [ con        "win" win
  , con       "lose" lose
  , con    "subgame" subgame
  , con    "eitherG" eitherG
  , con       "both" both
  , con       "race" race
  , con  "multigate" multigate
  , con "rewardThen" rewardThen
  , con     "gate" gate
  ]

------------------------------------------------------------------------------
--                         extensions
------------------------------------------------------------------------------

comeback :: Game -> Game
comeback g = subgame g lose win

bottom :: Game
bottom = multigate []

gate :: EventFilter -> Game -> Game
gate ef g = multigate [(ef, g)]

sig_games_ext :: Sig
sig_games_ext = signature
  [ con "comeback" comeback
  , con   "bottom" bottom
  , con  "andThen" andThen
  , con   "reward" reward
  ]


bingo :: [[Game]] -> Reward -> Game
bingo squares r
  = let subgames = squares
                ++ transpose squares  -- ! 1
        allOf :: [Game] -> Game
        allOf = foldr both    win
        anyOf :: [Game] -> Game
        anyOf = foldr eitherG lose
     in subgame (anyOf (fmap allOf subgames)) (reward r) lose

------------------------------------------------------------------------------
--                           tests
------------------------------------------------------------------------------

bingo_game :: Game
bingo_game = flip bingo (Reward 100) $ do
  x <- [0..2]
  pure $ do
    y <- [0..2]
    pure $ gate (Exactly $ x * 10 + y) win


foo :: Property
foo = property $ \g g2 -> race g g2 =~= race g2 g

------------------------------------------------------------------------------
--                         observations
------------------------------------------------------------------------------

runGame :: [Event] -> Game -> (Set Reward, Maybe Result)
runGame evs g =
  swap $ runWriter $ fmap _toResult $ _runGame g evs

_toResult :: Game -> Maybe Result
_toResult Win  = Just Victory
_toResult Lose = Just Defeat
_toResult _    = Nothing

_runGame :: Game -> [Event] -> Writer (Set Reward) Game
_runGame g (e : es) = do
  g' <- _stepGame g (Just e)
  _runGame g' es
_runGame g [] = do
  g' <- _stepGame g Nothing
  case g == g' of  -- ! 1
    True  -> pure g'
    False -> _runGame g' []

_stepGame :: Game -> Maybe Event -> Writer (Set Reward) Game
_stepGame Win  _ = pure win
_stepGame Lose _ = pure lose

-- # _stepGameRewardThen
_stepGame (RewardThen r g) e =
  tell (S.singleton r) >> _stepGame g e

_stepGame (Subgame g g1 g2) e =  -- ! 1
  subgame <$> _stepGame g e      -- ! 2
          <*> pure g1
          <*> pure g2
_stepGame (EitherW g1 g2) e =
  eitherG <$> _stepGame g1 e
          <*> _stepGame g2 e
_stepGame (Both g1 g2) e =
  both <$> _stepGame g1 e
       <*> _stepGame g2 e
_stepGame (Race g1 g2) e =
  race <$> _stepGame g1 e
       <*> _stepGame g2 e
_stepGame (Multigate cs) (Just e)
  | Just (_, g) <- find (\(ef, _) -> matches ef e) cs
  = pure g
_stepGame x@Multigate{} _ = pure x


matches :: EventFilter -> Event -> Bool
matches Never  _ = False
matches Always _ = True
matches (Exactly e) (Event ev) = e == ev

------------------------------------------------------------------------------
--                         specifications
------------------------------------------------------------------------------

sig_types :: Sig
sig_types = signature
  [ monoType        $ Proxy @Event
  , monoType        $ Proxy @EventFilter
  , monoType        $ Proxy @Reward
  , monoType        $ Proxy @Result
  , monoTypeObserve $ Proxy @Game
  , vars ["e"]      $ Proxy @Event
  , vars ["ef"]     $ Proxy @EventFilter
  , vars ["r"]      $ Proxy @Reward
  , vars ["res"]    $ Proxy @Result
  , vars ["g"]      $ Proxy @Game
  ]

sig_options :: Sig
sig_options = signature
  [ withMaxTermSize 5
  ]




quickspec_laws' :: [(String, Property)]
quickspec_laws' =
  [ ( "comeback bottom = bottom"
    , property $ comeback bottom =~= bottom)
  , ( "win = comeback lose"
    , property $ win =~= comeback lose)
  , ( "lose = comeback win"
    , property $ lose =~= comeback win)
  , ( "both g g2 = both g2 g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            both g g2 =~= both g2 g)
  , ( "both g g = g"
    , property $ \ (g :: Game) -> both g g =~= g)
  , ( "eitherG g g2 = eitherG g2 g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            eitherG g g2 =~= eitherG g2 g)
  , ( "eitherG g g = g"
    , property $ \ (g :: Game) -> eitherG g g =~= g)
  , ( "race g g = g"
    , property $ \ (g :: Game) -> race g g =~= g)
  , ( "andThen g win = g"
    , property $ \ (g :: Game) -> andThen g win =~= g)
  , ( "andThen bottom g = bottom"
    , property $
        \ (g :: Game) -> andThen bottom g =~= bottom)
  , ( "andThen lose g = lose"
    , property $
        \ (g :: Game) -> andThen lose g =~= lose)
  , ( "andThen win g = g"
    , property $ \ (g :: Game) -> andThen win g =~= g)
  , ( "both g bottom = andThen g bottom"
    , property $
        \ (g :: Game) -> both g bottom =~= andThen g bottom)
  , ( "both g win = g"
    , property $ \ (g :: Game) -> both g win =~= g)
  , ( "eitherG g lose = g"
    , property $ \ (g :: Game) -> eitherG g lose =~= g)
  , ( "race g bottom = g"
    , property $ \ (g :: Game) -> race g bottom =~= g)
  , ( "race bottom g = g"
    , property $ \ (g :: Game) -> race bottom g =~= g)
  , ( "race lose g = both g lose"
    , property $
        \ (g :: Game) -> race lose g =~= both g lose)
  , ( "race win g = eitherG g win"
    , property $
        \ (g :: Game) -> race win g =~= eitherG g win)
  , ( "gate ef bottom = bottom"
    , property $
        \ (ef :: EventFilter) -> gate ef bottom =~= bottom)
  , ( "reward r = rewardThen r win"
    , property $
        \ (r :: Reward) -> reward r =~= rewardThen r win)
  , ( "comeback (comeback g) = g"
    , property $
        \ (g :: Game) -> comeback (comeback g) =~= g)
  , ( "comeback (reward r) = rewardThen r lose"
    , property $
        \ (r :: Reward) ->
            comeback (reward r) =~= rewardThen r lose)
  , ( "andThen g g2 = subgame g g2 lose"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen g g2 =~= subgame g g2 lose)
  , ( "subgame bottom g g2 = bottom"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            subgame bottom g g2 =~= bottom)
  , ( "subgame lose g g2 = g2"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            subgame lose g g2 =~= g2)
  , ( "subgame win g g2 = g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            subgame win g g2 =~= g)
  , ( "comeback g = subgame g lose win"
    , property $
        \ (g :: Game) -> comeback g =~= subgame g lose win)
  , ( "subgame g win bottom = eitherG g bottom"
    , property $
        \ (g :: Game) ->
            subgame g win bottom =~= eitherG g bottom)
  , ( "andThen (comeback g) g2 = subgame g lose g2"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen (comeback g) g2 =~= subgame g lose g2)
  , ( "rewardThen r g = andThen (reward r) g"
    , property $
        \ (g :: Game) (r :: Reward) ->
            rewardThen r g =~= andThen (reward r) g)
  , ( "both g (comeback g) = andThen g lose"
    , property $
        \ (g :: Game) ->
            both g (comeback g) =~= andThen g lose)
  , ( "rewardThen r g = both g (reward r)"
    , property $
        \ (g :: Game) (r :: Reward) ->
            rewardThen r g =~= both g (reward r))
  , ( "eitherG g (comeback g) = subgame g win win"
    , property $
        \ (g :: Game) ->
            eitherG g (comeback g) =~= subgame g win win)
  , ( "race g (comeback g) = g"
    , property $
        \ (g :: Game) -> race g (comeback g) =~= g)
  , ( "race (reward r) g = eitherG g (reward r)"
    , property $
        \ (g :: Game) (r :: Reward) ->
            race (reward r) g =~= eitherG g (reward r))
  , ( "gate ef (comeback g) = comeback (gate ef g)"
    , property $
        \ (ef :: EventFilter) (g :: Game) ->
            gate ef (comeback g) =~= comeback (gate ef g))
  , ( "rewardThen r (comeback g) = comeback (rewardThen r g)"
    , property $
        \ (g :: Game) (r :: Reward) ->
            rewardThen r (comeback g) =~= comeback (rewardThen r g))
  , ( "comeback (andThen g bottom) = subgame g bottom win"
    , property $
        \ (g :: Game) ->
            comeback (andThen g bottom) =~= subgame g bottom win)
  , ( "comeback (andThen g lose) = subgame g win win"
    , property $
        \ (g :: Game) ->
            comeback (andThen g lose) =~= subgame g win win)
  , ( "comeback (both g lose) = eitherG g win"
    , property $
        \ (g :: Game) ->
            comeback (both g lose) =~= eitherG g win)
  , ( "comeback (eitherG g bottom) = subgame g lose bottom"
    , property $
        \ (g :: Game) ->
            comeback (eitherG g bottom) =~= subgame g lose bottom)
  , ( "both lose (comeback g) = both g lose"
    , property $
        \ (g :: Game) ->
            both lose (comeback g) =~= both g lose)
  , ( "both lose (multigate xs) = lose"
    , property $
        \ (xs :: [(EventFilter, Game)]) ->
            both lose (multigate xs) =~= lose)
  , ( "race (comeback g) lose = comeback (race g win)"
    , property $
        \ (g :: Game) ->
            race (comeback g) lose =~= comeback (race g win))
  , ( "race (multigate xs) lose = lose"
    , property $
        \ (xs :: [(EventFilter, Game)]) ->
            race (multigate xs) lose =~= lose)
  , ( "race (multigate xs) win = win"
    , property $
        \ (xs :: [(EventFilter, Game)]) ->
            race (multigate xs) win =~= win)
  , ( "andThen (andThen g g2) g3 = andThen g (andThen g2 g3)"
    , property $
        \ (g :: Game) (g2 :: Game) (g3 :: Game) ->
            andThen (andThen g g2) g3 =~= andThen g (andThen g2 g3))
  , ( "both (both g g2) g3 = both g (both g2 g3)"
    , property $
        \ (g :: Game) (g2 :: Game) (g3 :: Game) ->
            both (both g g2) g3 =~= both g (both g2 g3))
  , ( "eitherG g (andThen g g) = g"
    , property $
        \ (g :: Game) -> eitherG g (andThen g g) =~= g)
  , ( "eitherG g (both g g2) = both g (eitherG g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            eitherG g (both g g2) =~= both g (eitherG g g2))
  , ( "eitherG (eitherG g g2) g3 = eitherG g (eitherG g2 g3)"
    , property $
        \ (g :: Game) (g2 :: Game) (g3 :: Game) ->
            eitherG (eitherG g g2) g3 =~= eitherG g (eitherG g2 g3))
  , ( "eitherG g (rewardThen r g2) = eitherG g2 (rewardThen r g)"
    , property $
        \ (g :: Game) (g2 :: Game) (r :: Reward) ->
            eitherG g (rewardThen r g2) =~= eitherG g2 (rewardThen r g))
  , ( "race g (andThen g g2) = eitherG g (andThen g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (andThen g g2) =~= eitherG g (andThen g g2))
  , ( "race g (both g g2) = both g (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (both g g2) =~= both g (race g g2))
  , ( "race g (eitherG g g2) = eitherG g (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (eitherG g g2) =~= eitherG g (race g g2))
  , ( "race g (race g g2) = race g g2"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (race g g2) =~= race g g2)
  , ( "race g (race g2 g) = race g g2"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (race g2 g) =~= race g g2)
  , ( "race g (rewardThen r g) = rewardThen r g"
    , property $
        \ (g :: Game) (r :: Reward) ->
            race g (rewardThen r g) =~= rewardThen r g)
  , ( "race (both g g2) g = both g (race g2 g)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race (both g g2) g =~= both g (race g2 g))
  , ( "race (eitherG g g2) g = eitherG g (race g2 g)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race (eitherG g g2) g =~= eitherG g (race g2 g))
  , ( "race (race g g2) g3 = race g (race g2 g3)"
    , property $
        \ (g :: Game) (g2 :: Game) (g3 :: Game) ->
            race (race g g2) g3 =~= race g (race g2 g3))
  , ( "race (rewardThen r g) g2 = race g (rewardThen r g2)"
    , property $
        \ (g :: Game) (g2 :: Game) (r :: Reward) ->
            race (rewardThen r g) g2 =~= race g (rewardThen r g2))
  , ( "gate ef (andThen g g2) = andThen (gate ef g) g2"
    , property $
        \ (ef :: EventFilter) (g :: Game) (g2 :: Game) ->
            gate ef (andThen g g2) =~= andThen (gate ef g) g2)
  , ( "subgame (comeback g) g2 g3 = subgame g g3 g2"
    , property $
        \ (g :: Game) (g2 :: Game) (g3 :: Game) ->
            subgame (comeback g) g2 g3 =~= subgame g g3 g2)
  , ( "subgame (reward r) g g2 = rewardThen r g"
    , property $
        \ (g :: Game) (g2 :: Game) (r :: Reward) ->
            subgame (reward r) g g2 =~= rewardThen r g)
  , ( "comeback (subgame g g2 win) = andThen g (comeback g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            comeback (subgame g g2 win) =~= andThen g (comeback g2))
  , ( "andThen g (both g lose) = andThen g lose"
    , property $
        \ (g :: Game) ->
            andThen g (both g lose) =~= andThen g lose)
  , ( "andThen g (eitherG g2 win) = eitherG g (andThen g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen g (eitherG g2 win) =~= eitherG g (andThen g g2))
  , ( "andThen g (race g2 win) = race (andThen g g2) g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen g (race g2 win) =~= race (andThen g g2) g)
  , ( "andThen (eitherG g bottom) g2 = subgame g g2 bottom"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen (eitherG g bottom) g2 =~= subgame g g2 bottom)
  , ( "andThen (eitherG g win) g = g"
    , property $
        \ (g :: Game) -> andThen (eitherG g win) g =~= g)
  , ( "andThen (race g g2) lose = andThen (race g2 g) lose"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            andThen (race g g2) lose =~= andThen (race g2 g) lose)
  , ( "andThen (race g lose) g = race g lose"
    , property $
        \ (g :: Game) ->
            andThen (race g lose) g =~= race g lose)
  , ( "andThen (race g win) g = g"
    , property $
        \ (g :: Game) -> andThen (race g win) g =~= g)
  , ( "both g (eitherG g2 win) = andThen (eitherG g2 win) g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            both g (eitherG g2 win) =~= andThen (eitherG g2 win) g)
  , ( "both lose (eitherG g g2) = both g (both g2 lose)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            both lose (eitherG g g2) =~= both g (both g2 lose))
  , ( "both lose (race g g2) = both g (both g2 lose)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            both lose (race g g2) =~= both g (both g2 lose))
  , ( "both lose (gate ef g) = lose"
    , property $
        \ (ef :: EventFilter) (g :: Game) ->
            both lose (gate ef g) =~= lose)
  , ( "both (comeback g) (comeback g2) = comeback (eitherG g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            both (comeback g) (comeback g2) =~= comeback (eitherG g g2))
  , ( "eitherG g (both g2 lose) = andThen (eitherG g2 win) g"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            eitherG g (both g2 lose) =~= andThen (eitherG g2 win) g)
  , ( "race g (andThen g2 bottom) = both g (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (andThen g2 bottom) =~= both g (race g g2))
  , ( "race g (eitherG g2 bottom) = eitherG g (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race g (eitherG g2 bottom) =~= eitherG g (race g g2))
  , ( "race (comeback g) (comeback g2) = comeback (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race (comeback g) (comeback g2) =~= comeback (race g g2))
  , ( "race (andThen g g) lose = race g lose"
    , property $
        \ (g :: Game) ->
            race (andThen g g) lose =~= race g lose)
  , ( "race (andThen g g) win = race g win"
    , property $
        \ (g :: Game) ->
            race (andThen g g) win =~= race g win)
  , ( "race (andThen g bottom) g2 = both g2 (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race (andThen g bottom) g2 =~= both g2 (race g g2))
  , ( "race (eitherG g bottom) g2 = eitherG g2 (race g g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            race (eitherG g bottom) g2 =~= eitherG g2 (race g g2))
  , ( "race (gate ef g) lose = lose"
    , property $
        \ (ef :: EventFilter) (g :: Game) ->
            race (gate ef g) lose =~= lose)
  , ( "race (gate ef g) win = win"
    , property $
        \ (ef :: EventFilter) (g :: Game) ->
            race (gate ef g) win =~= win)
  , ( "gate ef (eitherG g bottom) = eitherG bottom (gate ef g)"
    , property $
        \ (ef :: EventFilter) (g :: Game) ->
            gate ef (eitherG g bottom) =~= eitherG bottom (gate ef g))
  , ( "subgame g bottom (comeback g2) = comeback (subgame g bottom g2)"
    , property $
        \ (g :: Game) (g2 :: Game) ->
            subgame g bottom (comeback g2) =~= comeback (subgame g bottom g2))
  , ( "eitherG bottom (andThen g lose) = subgame g bottom bottom"
    , property $
        \ (g :: Game) ->
            eitherG bottom (andThen g lose) =~= subgame g bottom bottom)
  ]