Skip to content
This repository was archived by the owner on May 20, 2024. It is now read-only.

Commit

Permalink
first version, written at an airport
Browse files Browse the repository at this point in the history
  • Loading branch information
mikehelmick committed Apr 14, 2023
1 parent fcd5f14 commit 7b2d2ae
Show file tree
Hide file tree
Showing 11 changed files with 513 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mikehelmick
37 changes: 37 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Go

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:

build:
name: Build
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.20
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v2

- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
# digits-helper
# digits-helper

A solve for the NYTimes Digits game!
https://www.nytimes.com/games/digits

To run from source

1. Clone the repo
2. Run with go run

This does not try to find the 'best' solution or list all solutions.
It just lists the first one that we find.

## Examples

```
❯ go run . 59 2 3 5 11 15 25
*** SOLUTION ***
25 + 15
40 + 11
51 + 5
56 + 3
```

```
❯ go run . 133 4 5 8 11 15 20
*** SOLUTION ***
20 + 15
35 - 11
24 + 8
32 * 4
128 + 5
```
Binary file added digits-helper
Binary file not shown.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module github.com/mikehelmick/digits-helper

go 1.20

require github.com/google/go-cmp v0.5.9

require golang.org/x/exp v0.0.0-20220516143420-24438e51023a
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/exp v0.0.0-20220516143420-24438e51023a h1:tiLLxEjKNE6Hrah/Dp/cyHvsyjDLcMFSocOHO5XDmOM=
golang.org/x/exp v0.0.0-20220516143420-24438e51023a/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
40 changes: 40 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"flag"
"fmt"
"log"
"strconv"
"strings"

"github.com/mikehelmick/digits-helper/pkg/digits"
)

func main() {
flag.Parse()
args := flag.Args()

if len(args) != 7 {
log.Fatalf("7 arguments are required `target` and the 6 digits")
}

intArgs := make([]int, len(args))
for i, v := range args {
converted, err := strconv.ParseInt(v, 10, 32)
if err != nil {
log.Fatalf("Unable to convert argument: %v reason: %v", v, err)
}
intArgs[i] = int(converted)
}

s, err := digits.New(intArgs[0], intArgs[1:])
if err != nil {
log.Fatalf("unable to initialize solver: %v", err)
}

solution, err := s.Solve()
if err != nil {
log.Fatalf("unable to solve: %v", err)
}
fmt.Printf("*** SOLUTION ***\n%v\n", strings.Join(solution, "\n"))
}
76 changes: 76 additions & 0 deletions pkg/digits/operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package digits

import "fmt"

type Operation interface {
Can(a, b int) bool
Op(a, b int) int
String(a, b int) string
}

var (
AllOperations = []Operation{
&Addition{},
&Subtraction{},
&Multiplication{},
&Division{},
}

OpAddition = &Addition{}
OpSubtraction = &Subtraction{}
OpMultiplication = &Multiplication{}
OpDivision = &Division{}
)

type Addition struct{}

func (op *Addition) Can(a, b int) bool { return true }

func (op *Addition) Op(a, b int) int {
return a + b
}

func (op *Addition) String(a, b int) string {
return fmt.Sprintf("%v + %v", a, b)
}

type Subtraction struct{}

func (op *Subtraction) Can(a, b int) bool { return true }

func (op *Subtraction) Op(a, b int) int {
return a - b
}

func (op *Subtraction) String(a, b int) string {
return fmt.Sprintf("%v - %v", a, b)
}

type Multiplication struct{}

func (op *Multiplication) Can(a, b int) bool { return true }

func (op *Multiplication) Op(a, b int) int {
return a * b
}

func (op *Multiplication) String(a, b int) string {
return fmt.Sprintf("%v * %v", a, b)
}

type Division struct{}

func (op *Division) Can(a, b int) bool {
if b == 0 {
return false
}
return a%b == 0
}

func (op *Division) Op(a, b int) int {
return a / b
}

func (op *Division) String(a, b int) string {
return fmt.Sprintf("%v / %v", a, b)
}
55 changes: 55 additions & 0 deletions pkg/digits/operations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package digits

import "testing"

func TestAddition(t *testing.T) {
op := &Addition{}
if !op.Can(1, 2) {
t.Fatalf("addition always can...")
}

res := op.Op(1, 2)
if res != 3 {
t.Fatalf("addition got wrong result 1+2 = %v", res)
}
}

func TestSubtraction(t *testing.T) {
op := &Subtraction{}
if !op.Can(10, 6) {
t.Fatalf("subtraction always can...")
}

res := op.Op(10, 6)
if res != 4 {
t.Fatalf("subtraction got wrong result 10-6 = %v", res)
}
}

func TestMultiplication(t *testing.T) {
op := &Multiplication{}
if !op.Can(10, 6) {
t.Fatalf("multiplication always can...")
}

res := op.Op(10, 6)
if res != 60 {
t.Fatalf("subtraction got wrong result 10*6 = %v", res)
}
}

func TestDivision(t *testing.T) {
op := &Division{}
if op.Can(10, 3) {
t.Fatalf("division shouldn't be able to 10/3")
}

if !op.Can(9, 3) {
t.Fatalf("division should be able to 9/3")
}

res := op.Op(9, 3)
if res != 3 {
t.Fatalf("division got wrong result 9/3 = %v", res)
}
}
114 changes: 114 additions & 0 deletions pkg/digits/solver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package digits

import (
"errors"
"fmt"

"golang.org/x/exp/slices"
)

type Solver struct {
target int
digits []int
operations []string
solved bool
}

var (
ErrorInvalidOp = errors.New("invalid operation")
)

func New(target int, digits []int) (*Solver, error) {
if target <= 0 || target > 2000 {
return nil, fmt.Errorf("target must be >0 and <= 2000")
}
if l := len(digits); l != 6 {
return nil, fmt.Errorf("6 digits are required to start a new puzzle, got: %v", l)
}
for _, d := range digits {
if d <= 0 || d >= 200 {
return nil, fmt.Errorf("starting digits must be > 0 and <= 200, got: %v", d)
}
}

digs := make([]int, len(digits))
copy(digs, digits)

slices.SortFunc(digs, func(i, j int) bool {
return i > j
})

return &Solver{
target: target,
digits: digs,
operations: make([]string, 0),
}, nil
}

func (s *Solver) next(a, b int, op Operation) (*Solver, error) {
if len(s.digits) < 2 {
return nil, fmt.Errorf("not enough digits to perform operation")
}
if !op.Can(a, b) {
return nil, ErrorInvalidOp
}

foundA, foundB := false, false
newD := make([]int, 0, len(s.digits)-2)
for _, d := range s.digits {
if d == a && !foundA {
foundA = true
continue
}
if d == b && !foundB {
foundB = true
continue
}
newD = append(newD, d)
}
ops := make([]string, len(s.operations))
copy(ops, s.operations)

// Do the operation
res := op.Op(a, b)
newD = append(newD, res)
slices.SortFunc(newD, func(i, j int) bool {
return i > j
})

return &Solver{
target: s.target,
digits: newD,
operations: append(ops, op.String(a, b)),
solved: s.target == res,
}, nil
}

func (s *Solver) Solve() ([]string, error) {

for i := 0; i < len(s.digits)-1; i++ {
for j := i + 1; j < len(s.digits); j++ {
for _, op := range AllOperations {
a := s.digits[i]
b := s.digits[j]

n, err := s.next(a, b, op)
if err != nil {
continue
}

if n.solved {
return n.operations, nil
}
// else try to solve from there
res, err := n.Solve()
if res == nil {
continue
}
return res, nil
}
}
}

return nil, fmt.Errorf("no solutions found")
}
Loading

0 comments on commit 7b2d2ae

Please sign in to comment.