Skip to content

Commit

Permalink
Merge pull request #88 from JordanMartinez/development
Browse files Browse the repository at this point in the history
Make minor release: ps-0.12.x-v0.8.1
  • Loading branch information
JordanMartinez committed Oct 4, 2018
2 parents 67dd328 + 047b53a commit 299e5f1
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 172 deletions.
9 changes: 7 additions & 2 deletions 21-Hello-World/09-Games/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Let's tie all of these ideas together. This folder is a project for a simple working game (guess a random number). As such, it will include:
- side-effects (e.g. state manipulation, random number generation, etc.)
- testing (currently cannot implement this yet due to needing monadic effects in QuickCheck)
- testing
- benchmarking (pretty sure I can't implement this yet either)
- data validation
- user input via a terminal
Expand All @@ -18,10 +18,12 @@ Start with `src` and then look at `test`.
- Write our program in a few different styles that show their pros/cons:
- Free-based approach
- Run-based approach
- Explain our thinking process for how to write a QuickCheck test for our Run-based approach
- Write an example QuickCheck test for our program

## Compilation Instructions

To run the programs in this folder, copy and paste this into your terminal:
To run the programs/test in this folder, copy and paste this into your terminal:
```bash
# The Node Readline & Aff folder
pulp --psc-package run -m ConsoleLessons.ReadLine.Effect
Expand All @@ -34,4 +36,7 @@ pulp --psc-package run -m Games.RandomNumber.Run.Infrastructure
# Changes in Run folder
pulp --psc-package run -m Games.RandomNumber.Run.ChangeImplementation
pulp --psc-package run -m Games.RandomNumber.Run.AddDomainTerm

# Run-based Test
pulp --psc-package test -m Test.Games.RandomNumber.Run.Infrastructure
```
5 changes: 1 addition & 4 deletions 21-Hello-World/09-Games/psc-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
"run",
"variant",
"free",
"strings",
"debug",
"psci-support",
"console",
"effect",
"prelude",
"transformers",
"refs",
"st"
"transformers"
]
}
1 change: 0 additions & 1 deletion 21-Hello-World/09-Games/src/02-Random-Number/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ This folder will show how to build a random number game using the Onion Architec
- Shared Code: the main types, data structures, and their related functions that is used across all the other folders
- Free: the program written using `Free`. Thus, it cannot be changed/improved easily
- Run: the program written using `Run`. Once the `Run` boilerplate code is written, we'll show that how easy it is to change/improve the code.
- Version 1: the base version that looks just like the `Free` counterpart
- (not yet started...) MTL: the program written using `MTL`-style of code.

## Control Flow
Expand Down
124 changes: 124 additions & 0 deletions 21-Hello-World/09-Games/test/02-Random-Number/01-Generators.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
module Test.Games.RandomNumber.Generators
( TestData(..), TestDataRecord
, genTestData
) where

import Prelude

import Control.Monad.Gen.Common (genMaybe)
import Data.Array (snoc, uncons)
import Data.Either (fromRight)
import Data.Maybe (Maybe(..), fromJust)
import Data.NonEmpty ((:|))
import Data.Tuple (Tuple(..))
import Games.RandomNumber.Core (Bounds, RemainingGuesses, RandomInt, GameResult(..), mkBounds, mkRemainingGuesses, mkRandomInt, decrement)
import Partial.Unsafe (unsafePartial)
import Test.QuickCheck.Arbitrary (class Arbitrary)
import Test.QuickCheck.Gen (Gen, chooseInt, oneOf, suchThat, vectorOf, randomSample)

type MockedBounds = Tuple Int Int

type TestDataRecord =
-- these two are the required "inputs" for interpreting
-- the API language
{ random :: Int -- the random number for our game
, userInputs :: Array String -- all the user's console inputs
-- (i.e. bounds, total guesses, guesses)

-- this value is the expected "output" for our program's "inputs"
, result :: GameResult -- the expected game result
}

newtype TestData = TestData TestDataRecord
instance arb :: Arbitrary TestData where
arbitrary = genTestData

-- Main Generator

genTestData :: Gen TestData
genTestData = do
bounds <- genBounds
random <- genIntWithinBounds bounds
totalGuesses <- genPositiveInt 100
winOrLoss <- genGameResult totalGuesses

case winOrLoss of
Just takesXGuesses -> do
incorrectGuesses <- genIncorrectGuesses (takesXGuesses - 1) bounds random
let guesses = snoc incorrectGuesses random
let userInputs = mkUserInputs bounds totalGuesses guesses
let _Remaining = mkRemainingGuesses_ (totalGuesses - takesXGuesses)
pure $ TestData
{ random: random, userInputs: userInputs, result: PlayerWins _Remaining }
Nothing -> do
guesses <- genIncorrectGuesses totalGuesses bounds random
let userInputs = mkUserInputs bounds totalGuesses guesses
let _Random = mkRandomInt_ (mkBounds_ bounds) random
pure $ TestData
{ random: random, userInputs: userInputs, result: PlayerLoses _Random }

-- Generators' definitions

genInt :: Gen Int
genInt = chooseInt bottom top -- from Bounded Int

genBounds :: Gen MockedBounds
genBounds = do
a <- genInt
b <- genInt `suchThat` (\b -> b /= a)
let x = if a < b then Tuple a b else Tuple b a
pure x

genIntWithinBounds :: MockedBounds -> Gen Int
genIntWithinBounds (Tuple lower upper) = chooseInt lower upper

-- If we make this `chooseInt 1 top`, it may generate a very large
-- number of guesses. Thus, we'll limit it to the max bound
genPositiveInt :: Int -> Gen Int
genPositiveInt maxBound = chooseInt 1 maxBound

-- | Just (guess correct answer)
-- | Nothing (never guesses correct answer)
genGameResult :: Int -> Gen (Maybe Int)
genGameResult totalGueses = genMaybe $ chooseInt 1 totalGueses

genIncorrectGuesses :: Int -> MockedBounds -> Int -> Gen (Array Int)
genIncorrectGuesses size (Tuple lower upper) random =
vectorOf size (genIncorrectGuess random)

where
beforeRandom = max lower (random - 1)
afterRandom = min upper (random + 1)
genIncorrectGuess r | r == lower = chooseInt afterRandom upper
| r == upper = chooseInt lower beforeRandom
| otherwise =
oneOf (
chooseInt lower beforeRandom :|
[chooseInt afterRandom upper]
)

-- Other Helper Functions

-- | Converts all our inputs into an Array of String, since that's
-- | what is used by API's `GetUserInputF`'s `reply` value
mkUserInputs :: MockedBounds -> Int -> Array Int -> Array String
mkUserInputs (Tuple lower upper) totalGueses guesses = do
[show lower, show upper, show totalGueses] <> (show <$> guesses)

------------------

-- Creates these objects using partial functions because their arguments
-- will be valid

mkBounds_ :: MockedBounds -> Bounds
mkBounds_ (Tuple lower upper) =
unsafePartial $ fromRight $ mkBounds lower upper

-- Since we can't create a RemainingGuesses with a 0 Int value,
-- we'll create it with one and then decrement it.
mkRemainingGuesses_ :: Int -> RemainingGuesses
mkRemainingGuesses_ 0 = decrement $ mkRemainingGuesses_ 1
mkRemainingGuesses_ i = unsafePartial $ fromRight $ mkRemainingGuesses i

mkRandomInt_ :: Bounds -> Int -> RandomInt
mkRandomInt_ b i = unsafePartial $ fromRight $ mkRandomInt b i

0 comments on commit 299e5f1

Please sign in to comment.