Pyxidatol-C / calculator_the_game_solver.hs

Brute-forcing with style in Haskell

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

# calculator_the_game_solver.hs

A haskell solver for [*Calculator The Game*](https://apps.apple.com/us/app/calculator-the-game/id1243055750)

- [calculator_the_game_solver.hs](#calculator_the_game_solverhs)
  - [Gallery](#gallery)
    - [LEVEL: 42](#level-42)
    - [LEVEL: 155](#level-155)
    - [LEVEL: 188](#level-188)
    - [LEVEL: 194](#level-194)
  - [API](#api)
    - [Parameters](#parameters)
    - [`Op`erations](#operations)
    - [Notes](#notes)
  - [Brief Explanation](#brief-explanation)

## Gallery

### LEVEL: 42

<img
    alt="initial value: 0; goal: -13; moves: 4; buttons: +3, -7, +/-]"
    src="gallery_assets/lvl_42.PNG"
    width="256"
/>

```hs
import           CalculatorTheG

main :: IO ()
main =
    let v0      = 0
        goal    = -13
        nbMoves = 4
        ops     = [Add 3, Sub 7, Neg]
        f       = id
    in  putStrLn $ showSolution v0 goal nbMoves ops f
```

```console
$ ghc solver && ./solver
+3 -> +3 -> +/- -> -7
```

### LEVEL: 155

<img
    alt="initiial value: 9; goal: 3001, moves: 9; buttons: 39 => 93, /3, Store, 31 => 00"
    src="gallery_assets/lvl_155.PNG"
    width="256"
/>

```hs
import           CalculatorTheG

main :: IO ()
main =
    let
        v0      = 9
        goal    = 3001
        nbMoves = 9
        ops =
            [Replace "39" "93", Div 3, StoreNew, StoreInsert, Replace "31" "00"]
        f = id
    in
        putStrLn $ showSolution v0 goal nbMoves ops f
```

```console
$ ghc solver && ./solver
Store(new) -> /3 -> Store(Ins) -> 39=>93 -> Store(new) -> Store(Ins) -> 39=>93 -> /3 -> 31=>00
```

### LEVEL: 188

<img
    alt="initial value: 25; goal: 822; moves: 6; portal piping the 1st digit counting from the left to the 3rd digit; buttons: Mirror, 5, Store, <<"
    src="gallery_assets/lvl_188.PNG"
    width="256"
/>

```hs
import           CalculatorTheG

main :: IO ()
main =
    let v0      = 25
        goal    = 822
        nbMoves = 6
        ops     = [Mirror, Ins "5", StoreNew, StoreInsert, Delete]
        f       = portal 3 1
    in  putStrLn $ showSolution v0 goal nbMoves ops f
```

```console
$ ghc solver && ./solver
5 -> Mirror -> Store(new) -> Store(Ins)
```

### LEVEL: 194

<img
    alt="initial value: 333; goal: 123; moves: 4; buttons: 1, 3, /2, [+]1]"
    src="gallery_assets/lvl_194.PNG"
    width="256"
/>

```hs
import           CalculatorTheG

main :: IO ()
main =
    let v0      = 333
        goal    = 123
        nbMoves = 4
        ops     = [Ins "1", Ins "3", Div 2, IncrementButtonValues 1]
        f       = portal 3 0
    in  putStrLn $ showSolution v0 goal nbMoves ops f
```

```console
$ ghc solver && ./solver
3 -> [+]1 -> /3 -> 2
```

## API

To compute the (shortest) sequence of operations that beats a level, fill in the `???`s below in `solver.hs`:

```hs
import           CalculatorTheG

main :: IO ()
main =
    let v0      = ???
        goal    = ???
        nbMoves = ???
        ops     = ???
        f       = ???
    in  putStrLn $ showSolution v0 goal nbMoves ops f

```

### Parameters

| Parameter | Type         | Description                                                               | Example value(s)                                                                                                                   |
| --------- | ------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `v0`      | `Int`        | Initial value                                                             | `-42`                                                                                                                              |
| `goal`    | `Int`        | Target value                                                              | `520`                                                                                                                              |
| `nbMoves` | `Int`        | Maximum number of moves allowed                                           | `4`                                                                                                                                |
| `ops`     | `[Op]`       | List of possible operations represented by the buttons                    | `[Add 1, Sum, Mirror]` - see the complete list of `Op` values below                                                                |
| `f`       | `Int -> Int` | Function applied after an operation is completed. Used to handle portals. | `id` - no portals; `portal 3 0` - pipes the 4th digit (index 3) from the right to the rightmost digit (index 0), e.g., 1234 -> 235 |

### `Op`erations

| Constructor                 | Example                   | Corresponding button label                     | Effect of said button                                                                                                                                 |
| --------------------------- | ------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Add Int`                   | `Add 1`                   | `+ 1`                                          | 6 -> 7                                                                                                                                                |
| `Sub Int`                   | `Sub 1`                   | `- 1`                                          | 44 -> 43                                                                                                                                              |
| `Mul Int`                   | `Mul 2`                   | `× 2`                                          | 32 -> 64                                                                                                                                              |
| `Div Int`                   | `Div 2`                   | `/ 2`                                          | 20 -> 10 (only if the division yields an integer)                                                                                                     |
| `Square`                    | `Square`                  | `x^2`                                          | -2 -> 4                                                                                                                                               |
| `Cube`                      | `Cube`                    | `x^3`                                          | -3 -> -81                                                                                                                                             |
| `Ins String`                | `Ins "1"`                 | `1`                                            | 7 -> 71                                                                                                                                               |
| `Neg`                       | `Neg`                     | `Neg`                                          | 1 -> -1                                                                                                                                               |
| `Sum`                       | `Sum`                     | `Sum`                                          | 123 -> 6                                                                                                                                              |
| `Reverse`                   | `Reverse`                 | `Reverse`                                      | 69 -> 96                                                                                                                                              |
| `Replace String String`     | `Replace "31" "00"`       | `31 => 00`                                     | 3311 -> 3001                                                                                                                                          |
| `Delete`                    | `Delete`                  | `<<`                                           | 31 -> 3                                                                                                                                               |
| `ShiftR`                    | `ShiftR`                  | `Shift >`                                      | 125 -> 512                                                                                                                                            |
| `ShiftL`                    | `ShiftL`                  | `Shift <`                                      | 125 -> 251                                                                                                                                            |
| `Inv10`                     | `Inv10`                   | `Inv10`                                        | 12590 -> 98510                                                                                                                                        |
| `Mirror`                    | `Mirror`                  | `Mirror`                                       | 123 -> 123321                                                                                                                                         |
| `StoreNew`                  | `StoreNew`                | `Store` (long press)                           | 99 -> 99 and the number 99 is stored, allowing you to insert the number 99 by pressing the store button (only if the number to be stored is positive) |
| `StoreInsert`               | `StoreInsert`             | 99 (after storing this number with `StoreNew`) | 6 -> 699                                                                                                                                              |
| `IncrementButtonValues Int` | `IncrementButtonValues 1` | `[+] 1`                                        | 16 -> 16, but the values of other buttons are incremented by 1; e.g., `Add 1` -> `Add 2`                                                              |

### Notes

- `[+] 1` interacts with `Add`, `Sub`, `Mul`, `Div`, `Ins`(, `StoreInsert` and `Replace`).

- `Insert` and `Replace` only take string representations of non-negative integers as argument(s).

## Brief Explanation

How does the code find a solution?

Well, using brute force:
we attempt to apply each operation for each of the current values.
That is, unless doing so gives us back a value that we have already encountered, in which case we will stop to avoid turning in circles.
We then repeat this step with the freshly generated values, until we find the solution or the maximum number of steps allowed is reached.
In the latter situation, an exception will be thrown.

Further optimisations are possible, of course, but somewhat unnecessary, as the current program is capable of finding the solutions to the problems in the original game in a split second.

About

Brute-forcing with style in Haskell


Languages

Language:Haskell 100.0%