Finding "Score Triplets" I'm a highschool mathematics teacher and I've been playing the following game with students
Three one digit numbers are written up on the board. Then the students are required to construct the integers from $1$ to $20$ with the following rules

*

*Each number in the set can be used a maximum of once

*No other numbers can be used

*Only the symbols $\times , \div, +,-,!$ ,^,$\sqrt{}$, ^$\sqrt{}$, and brackets can be used. Concatenation is not valid.

*To avoid trivial solutions all three numbers in the triplet must be used to construct their respective totals

Notes:

*

*^ means numbers can be used as powers, and ^$\sqrt{}$ means you can use numbers as a part of a surd. So if the set is $\{2,3,8\}$ then $2^3$ and $\sqrt[3]8$ are both valid.

*To keep with the spirit of not complicating the operations, factorials can be used and nested, but multifactorials like $5!!=5\times3\times1$ are not valid.

*Rule 4 means that if your triplet is $\{2,3,7\}$ the construction for $7$ can't be $7=7$, but instead could be $7=7^{3-2}$. This is only the case for the three numbers in the triplet, all other totals are not bound by this rule.

A triplet is considered a "score triplet" if every number from $1$ to $20$ is constructible
An example of a score triplet is $\{2,3,8\}$
$1=3-2$
$2=\sqrt{2\times\sqrt[3]8} $
$3=8-2-3$
$4=8 \div 2$
$5=2+3$
$6=2\times 3$
$7=8+2-3$
$8=8\times(3-2)$
$9=3^2$
$10=8+2$
$11=8+3$
$12=8+3!-2$
$13=8\times2-3$
$14=8+3!$
$15=(2+3)!\div8$
$16=8\times2$
$17=3^2+8$
$18=(8\div2)!-3!$
$19=8\times2+3$
$20=3!\times2+8$
However other triplets I have not been able to prove are score triplets, I'm unable to make $19$ from the set $\{1,7,9\}$ for example.
Of the $84$ unique sets of non-repeating triplets (e.g, excluding $\{2,2,5\}$) I've been able to show $33$ are score triplets with a number of others one or two numbers short. I have no idea how to formalize this further and am unsure if some sets just aren't score triplets or if through some monstrous combination of nested factorials, surds, and powers all sets are in fact score triplets.
Ultimately the question becomes;
Is there a way of proving a set of three numbers is not a score triplet?
I know this problem is a little parochial, but I'd love to get some thoughts on this since I can't seem to find information on it anywhere else.
 A: I've written a computer program to compute these. It is still missing doubled factorials so it doesn't get everything.
Run it online here
Full code:
package main

import (
    "fmt"
    "math"
    "os"
)

type binop func(a, b int) (d int, ok bool)

type fac [5]bool

func add(a, b int) (int, bool) {
    return a + b, true
}

func sub(a, b int) (int, bool) {
    return a - b, true
}

func revSub(a, b int) (int, bool) {
    return b - a, true
}

const small = 0.000001

func surd(a, b int) (int, bool) {
    if a == 1 || b == 1 {
        return -1, false
    }
    root := math.Pow(float64(a), 1/float64(b))
    if math.Abs(root-math.Round(root)) < small {
        return int(math.Round(root)), true
    }
    return -1, false
}

func pow(a, b int) (p int, q bool) {
    p = 1
    for i := 0; i < b; i++ {
        p *= a
    }
    return p, true
}

func mul(a, b int) (int, bool) {
    return a * b, true
}

func div(a, b int) (int, bool) {
    if b == 0 || a%b != 0 {
        return 0, false
    }
    return a / b, true
}

func revDiv(a, b int) (int, bool) {
    return div(b, a)
}

var ops = []binop{
    add,
    sub,
    revSub,
    surd,
    pow,
    mul,
    div,
    revDiv,
}

var names = []string{
    "+",
    "-",
    "<->",
    "root",
    "^",
    "x",
    "/",
    "</>",
}

func factorial(a int) (f int) {
    if a > 10 {
        return a
    }
    f = 1
    for a > 1 {
        f *= a
        a--
    }
    return f
}

func checkabc(s *scoTrip, a, b, c int) {
    oa, ob, oc := a, b, c
    if s.f[0] {
        a = factorial(a)
    }
    if s.f[1] {
        b = factorial(b)
    }
    if s.f[2] {
        c = factorial(c)
    }
    for i, oi := range ops {
        ab, ok := oi(a, b)
        if !ok {
            continue
        }
        if s.f[3] {
            ab = factorial(ab)
        }
        var abr int
        abr, s.abt = rootable(ab)
        if s.abt {
            addFound(s, oa, ob, 0, i, 0, abr)
        }
        addFound(s, oa, ob, 0, i, 0, ab)
        for j, oj := range ops {
            abc, ok := oj(ab, c)
            if !ok {
                continue
            }
            if s.f[4] {
                abc = factorial(abc)
            }
            addFound(s, oa, ob, oc, i, j, abc)
            if s.abt {
                abc, ok := oj(abr, c)
                if !ok {
                    continue
                }
                if s.f[4] {
                    abc = factorial(abc)
                }
                addFound(s, oa, ob, oc, i, j, abc)
            }
        }
    }
}

func rootable(a int) (int, bool) {
    if a == 1 {
        return -1, false
    }
    if a == 4 {
        return 2, true
    }
    if a == 9 {
        return 3, true
    }
    z := int(math.Round(math.Sqrt(float64(a))))
    if z*z == a {
        return z, true
    }
    return -1, false
}

func checkabcroot(s *scoTrip, a, b, c int) {
    s.at = false
    s.bt = false
    s.ct = false
    checkabc(s, a, b, c)
    var ar, br, cr int // square roots of a,b,c
    ar, s.at = rootable(a)
    br, s.bt = rootable(b)
    cr, s.ct = rootable(c)
    // Cannot have all three since a==4, b==9 then c=/=4,9
    if s.at && s.bt && s.ct {
        panic("a,b,c=4,9,?")
    }
    if !s.at && !s.bt && !s.ct {
        return
    }
    if s.at {
        checkabc(s, ar, b, c)
        if s.bt {
            checkabc(s, ar, br, c)
        }
        if s.ct {
            checkabc(s, ar, b, cr)
        }
    }
    if s.bt {
        checkabc(s, a, br, c)
        if s.ct {
            checkabc(s, a, br, cr)
        }
    }
    if s.ct {
        checkabc(s, a, b, cr)
    }
}

func facs(fi int) (f fac) {
    for i := 0; i < 5; i++ {
        f[i] = (fi%2 == 1)
        fi /= 2
    }
    return f
}

func plainChecks(s *scoTrip, a, b, c int) {
    checkabcroot(s, a, b, c)
    checkabcroot(s, b, a, c)
    checkabcroot(s, c, a, b)
    checkabcroot(s, c, b, a)
    checkabcroot(s, b, c, a)
    checkabcroot(s, a, c, b)
}

type scoTrip struct {
    found      []bool
    f          fac
    at, bt, ct bool
    abt        bool
}

func check(a, b, c int) bool {
    fmt.Printf("Checking %d, %d, %d:\n", a, b, c)
    var s scoTrip
    s.found = make([]bool, 21)
    for fi := 0; fi < 32; fi++ {
        s.f = facs(fi)
        plainChecks(&s, a, b, c)
    }
    all := true
    missing := make([]int, 0)
    for i, f := range s.found {
        if i == 0 {
            continue
        }
        if !f {
            all = false
            missing = append(missing, i)
        }
    }
    if all {
        if verbose {
            fmt.Printf("They were all found.\n")
        }
        return true
    } else {
        fmt.Printf("Missing %v\n", missing)
        return false
    }
}

func pf(f bool) string {
    if f {
        return "!"
    }
    return ""
}

func root(f bool) string {
    if f {
        return "√"
    }
    return ""
}

func printA(s *scoTrip, a int) (p string) {
    if s.at {
        p += fmt.Sprintf("√%d", a*a)
    } else {
        p += fmt.Sprintf("%d", a)
    }
    p += pf(s.f[0])
    return p
}

func printB(s *scoTrip, b int) (p string) {
    if s.bt {
        p += fmt.Sprintf("√%d", b*b)
    } else {
        p += fmt.Sprintf("%d", b)
    }
    p += pf(s.f[1])
    return p
}

func printC(s *scoTrip, c int) (p string) {
    if s.ct {
        p += fmt.Sprintf("√%d", c*c)
    } else {
        p += fmt.Sprintf("%d", c)
    }
    p += pf(s.f[2])
    return p
}

func addFound(s *scoTrip, a, b, c, i, j, d int) {
    if d < 1 || d > 20 {
        return
    }
    if s.found[d] {
        return
    }
    abroot := ""
    if s.abt {
        abroot = "√"
    }
    if verbose {
        if c == 0 {
            fmt.Printf("Found %d as %s%s %s %s\n", d,
                abroot,
                printA(s, a),
                names[i],
                printB(s, b))
        } else {
            fmt.Printf("Found %d as %s(%s %s %s) %s %s %s %s\n", d,
                abroot,
                printA(s, a), names[i],
                printB(s, b), names[j],
                pf(s.f[3]),
                printC(s, c), pf(s.f[4]))
        }
    }
    s.found[d] = true
}

var verbose = true

func main() {
    var lexi = []int{129, 134, 135, 139, 149, 159, 169, 235, 237, 238, 245, 247, 249, 256, 258, 259, 279, 289, 345, 347, 348, 349, 359, 456, 457, 458, 459, 469, 479, 489, 569, 579, 589}
    verbose = false
    if true {
        check(1, 3, 5)
        os.Exit(0)
    }
    ok := 0
    total := 0
    for i := 1; i <= 7; i++ {
        for j := i + 1; j <= 8; j++ {
            for k := j + 1; k <= 9; k++ {
                isOk := false
                if check(i, j, k) {
                    ok++
                    isOk = true
                }
                l := i*100 + j*10 + k
                found := false
                for _, le := range lexi {
                    if l == le {
                        found = true
                    }
                }
                if found {
                    if isOk {
                        fmt.Printf("Agree\n")
                    } else {
                        fmt.Printf("Failed with %d\n", l)
                    }
                }
                total++
            }
        }
    }
    fmt.Printf("Found %d of total of %d\n", ok, total)
}

```

