Skip to content

Instantly share code, notes, and snippets.

@Xetera
Last active April 1, 2019 07:59
Show Gist options
  • Save Xetera/03837ce02614f0502f59a26c8851031c to your computer and use it in GitHub Desktop.
Save Xetera/03837ce02614f0502f59a26c8851031c to your computer and use it in GitHub Desktop.
A haskell simulation implementing Optimal Stopping Theory for the math game, game of googol.
{-
Using Optimal Stopping for finding the highest number in a list
1. Generate an integer list of N size
2. Reveal the numbers of the first N/e elements
3. Keep revealing numbers until you find one bigger than the biggest revealed number
4. The chances of the first matching number being the highest number in the list will consistently be 1/e
-}
-- Games played: 1000
-- Games Won: 358
-- Games Lost: 642
-- Win Ratio: 35.8%
{-# LANGUAGE NumericUnderscores #-}
module Lib
( someFunc
) where
import System.Random (randomRIO)
import Text.Read (readMaybe)
import Data.List
import Data.Maybe
import System.Environment
import Control.Monad
import Data.Bifunctor (bimap)
import Control.Concurrent.Async
e :: Float
e = 2.7182818284590452353602874713527
genRand :: (Int, Int) -> IO Int
genRand = randomRIO
randomNums :: Int -> Int -> IO [Int]
randomNums arrLength maxNum =
let randomNum = genRand (1, maxNum)
in traverse (const randomNum) [1..arrLength]
ratio :: Int -> Int
ratio nums = round $ fromIntegral (nums) / e
grabE :: [Int] -> [Int]
grabE nums =
let amount = ratio (length nums)
in take amount nums
-- Plays a game and returns the winning outcome
game :: IO Bool
game = do
total <-randomNums 1000 1000
let eNums = grabE total
let maxNum = maximum eNums
let totalMax = maximum total
let iterNums = drop (ratio (length total)) total
let nextBiggest = find (>maxNum) iterNums
let out = fromMaybe (-1) nextBiggest
return $ out == totalMax
mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple = join bimap
getStats :: Int -> Int -> Float
getStats first second = (fromIntegral first / fromIntegral (first + second)) * 100
extractCount :: [String] -> Either String Int
extractCount [] = Left "Must enter the number of simulations!"
extractCount (count:_) =
case readMaybe count of
Just num -> Right num
Nothing -> Left $ "invalid input: " <> count
getResults :: (Int, Int) -> String
getResults (passing, nonPassing) =
"Games played: " <> show (passing + nonPassing) <> "\n"
<> "Games Won: " <> show passing <> "\n"
<> "Games Lost: " <> show nonPassing <> "\n"
<> "Win Ratio: " <> show (getStats passing nonPassing) <> "%"
someFunc :: IO ()
someFunc = getArgs
>>= either return process . extractCount
>>= putStrLn
where
process count = getResults . mapTuple length . partition (==True) <$> replicateM count game
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment