Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

[PureScript] New Concept Exercise: booleans #1260

Merged
merged 17 commits into from
Apr 16, 2020
9 changes: 8 additions & 1 deletion languages/purescript/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
"indent_size": 2
},
"exercises": {
"concept": [],
"concept": [
{
"slug": "booleans",
"uuid": "f1eb2cab-d6bc-447a-bce3-6780c44e4b22",
"concepts": ["booleans"],
"prerequisites": []
}
],
"practice": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PureScript uses `true` and `false` to represent the two truth values of logic.

In PureScript, for each of the three logical operations (AND, OR and NOT) there is a corresponding operator: `&&`, `||` and `not`. In general, there are rules regarding the order of the operations, and in this case `not` (negation) is applied first, and then `&&` (conjunction) and then `||` (disjunction).

You can always 'escape' these rules by using an operator with an even higher precedence, namely, `( )` named the 'Grouping operator' or simply called 'parentheses'.
11 changes: 11 additions & 0 deletions languages/purescript/exercises/concept/booleans/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### 1. Check if the 'Fast Attack' action is possible

- The logical NOT operator (`not`) can be placed before an expression to negate its value.

### 2. Check if the 'Approach and Spy' action is possible

- Logical expressions are evaluated from left to right and are tested for possible 'short-circuits'.

### 3. Check if the 'Signal Prisoner' action is possible

- Logical operators in the order of their precedence (from highest to lowest): `not`, `&&`, `||`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
In this exercise, you'll be implementing the quest logic for a new RPG game a friend is developing. The game's main character is Annalyn, a brave girl with a fierce and loyal pet dog. Unfortunately, disaster strikes, as her best friend was kidnapped while searching for berries in the forest. Annalyn will try to find and free her best friend, optionally taking her dog with her on this quest.

After some time spent following her best friend's trail, she finds the camp in which her best friend is imprisoned. It turns out there are two kidnappers: a mighty knight and a cunning archer.

Having found the kidnappers, Annalyn considers which of the following actions she can engage in:

- Fast attack: a fast attack can be made if the knight is sleeping, as it takes time for him to get his armor on, so he will be vulnerable.
- Spy: the group can be spied upon if at least one of them is awake. Otherwise, spying is a waste of time.
- Signal prisoner: the prisoner can be signalled using bird sounds if the prisoner is awake and the archer is sleeping, as archers are trained in bird signaling so they could intercept the message.
- Free prisoner: if the prisoner is awake and the other two characters are sleeping, a sneaky entry into the camp can free the prisoner. This won't work if the prisoner is sleeping, as the prisoner will be startled by the sudden appearance of her friend and the knight and archer will be awoken. The prisoner can also be freed if the archer is sleeping and Annalyn has her pet dog with her, as the knight will be scared by the dog and will withdraw, and the archer can't equip his bow fast enough to prevent the prisoner from being freed.

You have four tasks: to implement the logic for determining if the above actions are available based on the state of the three characters found in the forest and whether Annalyn's pet dog is present or not.

## Tasks

### 1. Check if the 'Fast Attack' action is possible

Implement a function named `canExecuteFastAttack` that takes a boolean value which indicates if the knight is awake. This function returns `true` if the 'Fast Attack' action is available based on the state of the character. Otherwise, returns `false`:

```purescript
knightIsAwake :: Boolean
knightIsAwake = true

canExecuteFastAttack knightIsAwake -- => false
```

### 2. Check if the 'Spy' action is possible

Implement a function named `canSpy` that takes three boolean values, indicating if the knight, archer and the prisoner, respectively, are awake. The function returns `true` if the 'Spy' action is available based on the state of the characters. Otherwise, returns `false`:

```purescript
knightIsAwake :: Boolean
knightIsAwake = true

archerIsAwake :: Boolean
archerIsAwake = true

prisonerIsAwake :: Boolean
prisonerIsAwake = true

canSpy knightIsAwake archerIsAwake prisonerIsAwake -- => true
```

### 3. Check if the 'Signal Prisoner' action is possible

Implement a function named `canSignalPrisoner` that takes two boolean values, indicating if the archer and the prisoner, respectively, are awake. The function returns `true` if the 'Signal Prisoner' action is available based on the state of the characters. Otherwise, returns `false`:

```purescript
archerIsAwake :: Boolean
archerIsAwake = false

prisonerIsAwake :: Boolean
prisonerIsAwake = true

canSignalPrisoner archerIsAwake prisonerIsAwake -- => true
```

### 4. Check if the 'Free Prisoner' action is possible

Implement a function named `canFreePrisoner` that takes four boolean values. The first three parameters indicate if the knight, archer and the prisoner, respectively, are awake. The last parameter indicates if Annalyn's pet dog is present. The function returns `true` if the 'Free Prisoner' action is available based on the state of the characters and Annalyn's pet dog presence. Otherwise, it returns `false`:

```purescript
knightIsAwake :: Boolean
knightIsAwake = true

archerIsAwake :: Boolean
archerIsAwake = false

prisonerIsAwake :: Boolean
prisonerIsAwake = true

petDogIsPresent :: Boolean
petDogIsPresent = true

canFreePrisoner knightIsAwake archerIsAwake prisonerIsAwake petDogIsPresent -- => false
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A boolean represents one of two values: `true` or `false`. Logical operators (`not`, `&&`, `||`) are typically used with boolean values and they return a boolean value.
10 changes: 10 additions & 0 deletions languages/purescript/exercises/concept/booleans/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc-package/
/.psc*
/.purs*
/.psa*
/.spago
15 changes: 15 additions & 0 deletions languages/purescript/exercises/concept/booleans/.meta/Example.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Booleans where

import Prelude

canExecuteFastAttack :: Boolean -> Boolean
canExecuteFastAttack knightIsAwake = not knightIsAwake

canSpy :: Boolean -> Boolean -> Boolean -> Boolean
canSpy knightIsAwake archerIsAwake prisonerIsAwake = knightIsAwake || archerIsAwake || prisonerIsAwake

canSignalPrisoner :: Boolean -> Boolean -> Boolean
canSignalPrisoner archerIsAwake prisonerIsAwake = not archerIsAwake && prisonerIsAwake

canFreePrisoner :: Boolean -> Boolean -> Boolean -> Boolean -> Boolean
canFreePrisoner knightIsAwake archerIsAwake prisonerIsAwake petDogIsPresent = not knightIsAwake && not archerIsAwake && prisonerIsAwake || petDogIsPresent && not archerIsAwake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"authors": [
{
"github_username": "aimorris",
"exercism_username": "Adam-Morris"
}
],
"forked_from": ["javascript/booleans"]
}
128 changes: 128 additions & 0 deletions languages/purescript/exercises/concept/booleans/packages.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
{-
Welcome to your new Dhall package-set!

Below are instructions for how to edit this file for most use
cases, so that you don't need to know Dhall to use it.

## Warning: Don't Move This Top-Level Comment!

Due to how `dhall format` currently works, this comment's
instructions cannot appear near corresponding sections below
because `dhall format` will delete the comment. However,
it will not delete a top-level comment like this one.

## Use Cases

Most will want to do one or both of these options:
1. Override/Patch a package's dependency
2. Add a package not already in the default package set

This file will continue to work whether you use one or both options.
Instructions for each option are explained below.

### Overriding/Patching a package

Purpose:
- Change a package's dependency to a newer/older release than the
default package set's release
- Use your own modified version of some dependency that may
include new API, changed API, removed API by
using your custom git repo of the library rather than
the package set's repo

Syntax:
Replace the overrides' "{=}" (an empty record) with the following idea
The "//" or "⫽" means "merge these two records and
when they have the same value, use the one on the right:"
-------------------------------
let overrides =
{ packageName =
upstream.packageName // { updateEntity1 = "new value", updateEntity2 = "new value" }
, packageName =
upstream.packageName // { version = "v4.0.0" }
, packageName =
upstream.packageName // { repo = "https://www.example.com/path/to/new/repo.git" }
}
-------------------------------

Example:
-------------------------------
let overrides =
{ halogen =
upstream.halogen // { version = "master" }
, halogen-vdom =
upstream.halogen-vdom // { version = "v4.0.0" }
}
-------------------------------

### Additions

Purpose:
- Add packages that aren't already included in the default package set

Syntax:
Replace the additions' "{=}" (an empty record) with the following idea:
-------------------------------
let additions =
{ package-name =
{ dependencies =
[ "dependency1"
, "dependency2"
]
, repo =
"https://example.com/path/to/git/repo.git"
, version =
"tag ('v4.0.0') or branch ('master')"
}
, package-name =
{ dependencies =
[ "dependency1"
, "dependency2"
]
, repo =
"https://example.com/path/to/git/repo.git"
, version =
"tag ('v4.0.0') or branch ('master')"
}
, etc.
}
-------------------------------

Example:
-------------------------------
let additions =
{ benchotron =
{ dependencies =
[ "arrays"
, "exists"
, "profunctor"
, "strings"
, "quickcheck"
, "lcg"
, "transformers"
, "foldable-traversable"
, "exceptions"
, "node-fs"
, "node-buffer"
, "node-readline"
, "datetime"
, "now"
]
, repo =
"https://github.com/hdgarrood/purescript-benchotron.git"
, version =
"v7.0.0"
}
}
-------------------------------
-}


let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.13.6-20200404/packages.dhall sha256:f239f2e215d0cbd5c203307701748581938f74c4c78f4aeffa32c11c131ef7b6

let overrides = {=}

let additions = {=}

in upstream // overrides // additions
9 changes: 9 additions & 0 deletions languages/purescript/exercises/concept/booleans/spago.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{-
Welcome to a Spago project!
You can edit this file as you like.
-}
{ name = "booleans"
, dependencies = [ "spec" ]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs", "test/**/*.purs" ]
}
15 changes: 15 additions & 0 deletions languages/purescript/exercises/concept/booleans/src/Booleans.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Booleans where

import Prelude

canExecuteFastAttack :: Boolean -> Boolean
canExecuteFastAttack knightIsAwake = -- You need to implement this function!

canSpy :: Boolean -> Boolean -> Boolean -> Boolean
canSpy knightIsAwake archerIsAwake prisonerIsAwake = -- You need to implement this function!

canSignalPrisoner :: Boolean -> Boolean -> Boolean
canSignalPrisoner archerIsAwake prisonerIsAwake = -- You need to implement this function!

canFreePrisoner :: Boolean -> Boolean -> Boolean -> Boolean -> Boolean
canFreePrisoner knightIsAwake archerIsAwake prisonerIsAwake petDogIsPresent = -- You need to implement this function!
47 changes: 47 additions & 0 deletions languages/purescript/exercises/concept/booleans/test/Main.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Test.Main where

import Prelude

import Booleans (canExecuteFastAttack, canSpy, canSignalPrisoner, canFreePrisoner)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Test.Spec (describe, it)
import Test.Spec.Assertions (shouldEqual)
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner (runSpec)

main :: Effect Unit
main = launchAff_ $ runSpec [consoleReporter] do
describe "Can fast attack" do
it "Knight is awake" $ canExecuteFastAttack true `shouldEqual` false
it "Knight is asleep" $ canExecuteFastAttack false `shouldEqual` true
describe "Can spy" do
it "Knight is asleep, archer is asleep, prisoner is awake" $ canSpy false false true `shouldEqual` true
it "Knight is asleep, archer is awake, prisoner is asleep" $ canSpy false true false `shouldEqual` true
it "Knight is awake, archer is asleep, prisoner is asleep" $ canSpy true false false `shouldEqual` true
it "Knight is awake, archer is asleep, prisoner is awake" $ canSpy true false true `shouldEqual` true
it "Knight is awake, archer is awake, prisoner is asleep" $ canSpy true true false `shouldEqual` true
it "Knight is awake, archer is awake, prisoner is awake" $ canSpy true true true `shouldEqual` true
it "Knight is asleep, archer is asleep, prisoner is asleep" $ canSpy false false false `shouldEqual` false
describe "Can signal prisoner" do
it "Archer is asleep, prisoner is asleep" $ canSignalPrisoner false false `shouldEqual` false
it "Archer is asleep, prisoner is awake" $ canSignalPrisoner false true `shouldEqual` true
it "Archer is awake, prisoner is asleep" $ canSignalPrisoner true false `shouldEqual` false
it "Archer is awake, prisoner is awake" $ canSignalPrisoner true true `shouldEqual` false
describe "Can free prisoner" do
it "Knight is asleep, archer is asleep, prisoner is asleep, pet dog is not present" $ canFreePrisoner false false false false `shouldEqual` false
it "Knight is asleep, archer is asleep, prisoner is asleep, pet dog is present" $ canFreePrisoner false false false true `shouldEqual` true
it "Knight is asleep, archer is asleep, prisoner is awake, pet dog is not present" $ canFreePrisoner false false true false `shouldEqual` true
it "Knight is asleep, archer is asleep, prisoner is awake, pet dog is present" $ canFreePrisoner false false true true `shouldEqual` true
it "Knight is asleep, archer is awake, prisoner is asleep, pet dog is not present" $ canFreePrisoner false true false false `shouldEqual` false
it "Knight is asleep, archer is awake, prisoner is asleep, pet dog is present" $ canFreePrisoner false true false true `shouldEqual` false
it "Knight is asleep, archer is awake, prisoner is awake, pet dog is not present" $ canFreePrisoner false true true false `shouldEqual` false
it "Knight is asleep, archer is awake, prisoner is awake, pet dog is present" $ canFreePrisoner false true true true `shouldEqual` false
it "Knight is awake, archer is asleep, prisoner is asleep, pet dog is not present" $ canFreePrisoner true false false false `shouldEqual` false
it "Knight is awake, archer is asleep, prisoner is asleep, pet dog is present" $ canFreePrisoner true false false true `shouldEqual` true
it "Knight is awake, archer is asleep, prisoner is awake, pet dog is not present" $ canFreePrisoner true false true false `shouldEqual` false
it "Knight is awake, archer is asleep, prisoner is awake, pet dog is present" $ canFreePrisoner true false true true `shouldEqual` true
it "Knight is awake, archer is awake, prisoner is asleep, pet dog is not present" $ canFreePrisoner true true false false `shouldEqual` false
it "Knight is awake, archer is awake, prisoner is asleep, pet dog is present" $ canFreePrisoner true true false true `shouldEqual` false
it "Knight is awake, archer is awake, prisoner is awake, pet dog is not present" $ canFreePrisoner true true true false `shouldEqual` false
it "Knight is awake, archer is awake, prisoner is awake, pet dog is present" $ canFreePrisoner true true true true `shouldEqual` false
15 changes: 14 additions & 1 deletion reference/concepts/boolean_logic.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,17 @@ This exercise looks at various game events and determine if they have occured by
| ----- | ------------------------------- | ---------------------------------- |
| Elixir | [booleans][implementation-elixir] | None |

[implementation-elixir]: ../../languages/elixir/exercises/concept/booleans/.docs/introduction.md
### RPG game logic

This exercise determines whether specific characters are awake (true or false) and checks whether a certain plan will work or not dependent on which characters are sleeping. The implementation teaches:

- boolean values
- boolean operators
- boolean operators precedence

| Track | Exercise | Changes |
| ----- | ------------------------------- | ---------------------------------- |
| PureScript | [booleans][implementation-purescript] | None |

[implementation-elixir]: ../../languages/elixir/exercises/concept/booleans/.docs/introduction.md
[implementation-purescript]: ../../languages/purescript/exercises/concept/booleans/.docs/introduction.md