remove Go code
parent
ad900e8767
commit
06d4ed30c3
@ -1,25 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.buddy.wtf/challenges/buckets-of-fun/lib/application"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if err := run(); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() error {
|
|
||||||
flag.Parse()
|
|
||||||
app, err := application.New(flag.Args()...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return app.Run()
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
module git.buddy.wtf/challenges/buckets-of-fun
|
|
||||||
|
|
||||||
go 1.13
|
|
@ -1,52 +0,0 @@
|
|||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"git.buddy.wtf/challenges/buckets-of-fun/lib/bucket"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App holds context for the application
|
|
||||||
type App struct {
|
|
||||||
Target uint64
|
|
||||||
Buckets bucket.Buckets
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns an instance of the app
|
|
||||||
func New(args ...string) (*App, error) {
|
|
||||||
if len(args) < 2 {
|
|
||||||
msg := fmt.Sprintf("usage error: requires at least 2 arguments, received %d", len(args))
|
|
||||||
return nil, errors.New(msg)
|
|
||||||
}
|
|
||||||
target, err := strconv.ParseUint(args[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("target must be a positive integer: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
app := App{
|
|
||||||
Target: target,
|
|
||||||
Buckets: bucket.Buckets{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range args[1:] {
|
|
||||||
capacity, err := strconv.ParseUint(arg, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("bucket capacity must be a positive integer: " + err.Error())
|
|
||||||
}
|
|
||||||
app.Buckets = append(app.Buckets, bucket.Bucket{Capacity: capacity})
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
fmt.Printf("target: %d\n", a.Target)
|
|
||||||
fmt.Printf("buckets: %s\n", a.Buckets.String())
|
|
||||||
for _, solution := range a.Buckets.FindTarget((a.Target)) {
|
|
||||||
fmt.Println(solution)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package bucket
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Bucket to store volume
|
|
||||||
type Bucket struct {
|
|
||||||
// Capacity amount the bucket can store
|
|
||||||
Capacity uint64
|
|
||||||
// Volume is the current volume of the bucket
|
|
||||||
Volume uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill returns a copy Bucket filled
|
|
||||||
func Fill(b Bucket) Bucket {
|
|
||||||
return Bucket{
|
|
||||||
Volume: b.Capacity,
|
|
||||||
Capacity: b.Capacity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty returns a copy Bucket set to empty
|
|
||||||
func Empty(b Bucket) Bucket {
|
|
||||||
return Bucket{
|
|
||||||
Volume: 0,
|
|
||||||
Capacity: b.Capacity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pour fills the target bucket to the top
|
|
||||||
func Pour(src, target Bucket) (Bucket, Bucket) {
|
|
||||||
s := Bucket{
|
|
||||||
Capacity: src.Capacity,
|
|
||||||
Volume: src.Volume,
|
|
||||||
}
|
|
||||||
t := Bucket{
|
|
||||||
Capacity: target.Capacity,
|
|
||||||
Volume: target.Volume,
|
|
||||||
}
|
|
||||||
availableVolume := t.Capacity - t.Volume
|
|
||||||
if availableVolume > s.Volume {
|
|
||||||
t.Volume += s.Volume
|
|
||||||
s.Volume = 0
|
|
||||||
} else {
|
|
||||||
s.Volume -= availableVolume
|
|
||||||
t.Volume += availableVolume
|
|
||||||
}
|
|
||||||
return s, t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill sets the volume to the capacity to fill bucket
|
|
||||||
func (b *Bucket) Fill() {
|
|
||||||
b.Volume = b.Capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty sets the volume to 0
|
|
||||||
func (b *Bucket) Empty() {
|
|
||||||
b.Volume = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty is true no volume
|
|
||||||
func (b *Bucket) IsEmpty() bool {
|
|
||||||
return b.Volume == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFull is true if bucket is at capacity
|
|
||||||
func (b *Bucket) IsFull() bool {
|
|
||||||
return b.Volume == b.Capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pour fills the target bucket to the top
|
|
||||||
func (b *Bucket) Pour(target *Bucket) {
|
|
||||||
availableVolume := target.Capacity - target.Volume
|
|
||||||
if availableVolume > b.Volume {
|
|
||||||
target.Volume += b.Volume
|
|
||||||
b.Volume = 0
|
|
||||||
} else {
|
|
||||||
b.Volume -= availableVolume
|
|
||||||
target.Volume += availableVolume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String gets string representation
|
|
||||||
func (b *Bucket) String() string {
|
|
||||||
return fmt.Sprintf("%dvol/%dcap", b.Volume, b.Capacity)
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package bucket
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestPour(t *testing.T) {
|
|
||||||
var testcases = []struct {
|
|
||||||
source *Bucket
|
|
||||||
target *Bucket
|
|
||||||
expectedSrcVol uint64
|
|
||||||
expectedTargetVol uint64
|
|
||||||
}{
|
|
||||||
{&Bucket{Capacity: 5, Volume: 2}, &Bucket{Capacity: 3, Volume: 2}, 1, 3},
|
|
||||||
{&Bucket{Capacity: 5, Volume: 1}, &Bucket{Capacity: 3, Volume: 1}, 0, 2},
|
|
||||||
{&Bucket{Capacity: 5, Volume: 0}, &Bucket{Capacity: 3, Volume: 0}, 0, 0},
|
|
||||||
{&Bucket{Capacity: 5, Volume: 5}, &Bucket{Capacity: 3, Volume: 3}, 5, 3},
|
|
||||||
{&Bucket{Capacity: 5, Volume: 4}, &Bucket{Capacity: 3, Volume: 2}, 3, 3},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testcases {
|
|
||||||
tc.source.Pour(tc.target)
|
|
||||||
if tc.source.Volume != tc.expectedSrcVol {
|
|
||||||
t.Errorf("expected source volume %d, got = %d", tc.expectedSrcVol, tc.source.Volume)
|
|
||||||
}
|
|
||||||
if tc.target.Volume != tc.expectedTargetVol {
|
|
||||||
t.Errorf("expected target volume %d, got = %d", tc.expectedTargetVol, tc.target.Volume)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
package bucket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Buckets manages bucket objects
|
|
||||||
type Buckets []Bucket
|
|
||||||
|
|
||||||
// // action name
|
|
||||||
type actionType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
actionTypeUnknown actionType = iota
|
|
||||||
actionTypeEmpty
|
|
||||||
actionTypeFill
|
|
||||||
actionTypePour
|
|
||||||
)
|
|
||||||
|
|
||||||
type action struct {
|
|
||||||
kind actionType
|
|
||||||
src int
|
|
||||||
target int
|
|
||||||
}
|
|
||||||
|
|
||||||
type actions []action
|
|
||||||
|
|
||||||
func (a actions) String() string {
|
|
||||||
acts := []string{}
|
|
||||||
for _, act := range a {
|
|
||||||
acts = append(acts, act.String())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%s]", strings.Join(acts, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a action) String() string {
|
|
||||||
switch a.kind {
|
|
||||||
case actionTypeEmpty:
|
|
||||||
return fmt.Sprintf("empty %d ", a.target)
|
|
||||||
case actionTypeFill:
|
|
||||||
return fmt.Sprintf("fill %d ", a.target)
|
|
||||||
case actionTypePour:
|
|
||||||
return fmt.Sprintf("pour %d → %d", a.src, a.target)
|
|
||||||
case actionTypeUnknown:
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("unknown (src: %d, target: %d)", a.src, a.target)
|
|
||||||
}
|
|
||||||
|
|
||||||
type result struct {
|
|
||||||
mapping map[string][]actions
|
|
||||||
matched map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) get(key string) ([]actions, bool) {
|
|
||||||
acts, ok := r.mapping[key]
|
|
||||||
return acts, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) keys() []string {
|
|
||||||
keys := []string{}
|
|
||||||
for key := range r.mapping {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) matchedKeys() []string {
|
|
||||||
keys := []string{}
|
|
||||||
for key := range r.matched {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) bestMatches() ([]string, bool) {
|
|
||||||
var best []string
|
|
||||||
found := false
|
|
||||||
numActions := 0
|
|
||||||
|
|
||||||
matched := r.matchedKeys()
|
|
||||||
for _, key := range matched {
|
|
||||||
acts, ok := r.get(key)
|
|
||||||
if found == false && ok && len(acts) > 0 {
|
|
||||||
found = true
|
|
||||||
numActions = len(acts[0])
|
|
||||||
best = []string{key}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if numActions > len(acts[0]) {
|
|
||||||
numActions = len(acts[0])
|
|
||||||
best = []string{key}
|
|
||||||
} else if numActions == len(acts[0]) {
|
|
||||||
best = append(best, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return best, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) addMatch(key string) {
|
|
||||||
r.matched[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *result) addActions(key string, acts actions) bool {
|
|
||||||
currentActions, ok := r.mapping[key]
|
|
||||||
if !ok || len(currentActions) == 0 {
|
|
||||||
currentActions = []actions{acts}
|
|
||||||
r.mapping[key] = currentActions
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(acts) > len(currentActions[0]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(acts) == len(currentActions[0]) {
|
|
||||||
r.mapping[key] = append(currentActions, acts)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
r.mapping[key] = []actions{acts}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) empty(index int) {
|
|
||||||
buk := &b[index]
|
|
||||||
buk.Empty()
|
|
||||||
b[index] = *buk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) fill(index int) {
|
|
||||||
buk := &b[index]
|
|
||||||
buk.Fill()
|
|
||||||
b[index] = *buk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) pour(i, j int) {
|
|
||||||
src := &b[i]
|
|
||||||
dest := &b[j]
|
|
||||||
src.Pour(dest)
|
|
||||||
b[i] = *src
|
|
||||||
b[j] = *dest
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasTarget returns true
|
|
||||||
func (b Buckets) HasTarget(target uint64) bool {
|
|
||||||
for _, bucket := range b {
|
|
||||||
if bucket.Volume == target {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) String() string {
|
|
||||||
bucketStrs := []string{}
|
|
||||||
for _, bucket := range b {
|
|
||||||
bucketStrs = append(bucketStrs, bucket.String())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%s]", strings.Join(bucketStrs, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindTarget returns actions to find target
|
|
||||||
func (b Buckets) FindTarget(target uint64) []string {
|
|
||||||
r := &result{
|
|
||||||
mapping: map[string][]actions{},
|
|
||||||
matched: map[string]interface{}{},
|
|
||||||
}
|
|
||||||
b.findTarget(target, r, actions{})
|
|
||||||
|
|
||||||
keys, found := r.bestMatches()
|
|
||||||
if !found {
|
|
||||||
return []string{"No solution"}
|
|
||||||
}
|
|
||||||
fmt.Printf("%d solutions\n", len(keys))
|
|
||||||
for i, key := range keys {
|
|
||||||
allacts, ok := r.get(key)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newState := make(Buckets, len(b))
|
|
||||||
copy(newState, b)
|
|
||||||
fmt.Printf("solution %d\n", i+1)
|
|
||||||
for _, acts := range allacts {
|
|
||||||
if len(acts) == 0 {
|
|
||||||
fmt.Println("no actions required")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, act := range acts {
|
|
||||||
newState = newState.performAction(act)
|
|
||||||
fmt.Printf("%s\t%s\n", act.String(), newState.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret := []string{}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) performAction(act action) Buckets {
|
|
||||||
newState := make(Buckets, len(b))
|
|
||||||
copy(newState, b)
|
|
||||||
|
|
||||||
switch act.kind {
|
|
||||||
case actionTypeEmpty:
|
|
||||||
newState.empty(act.target)
|
|
||||||
case actionTypeFill:
|
|
||||||
newState.fill(act.target)
|
|
||||||
case actionTypePour:
|
|
||||||
newState.pour(act.src, act.target)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) findTarget(
|
|
||||||
target uint64,
|
|
||||||
states *result,
|
|
||||||
prevActions actions,
|
|
||||||
) {
|
|
||||||
key := b.String()
|
|
||||||
|
|
||||||
added := states.addActions(key, prevActions)
|
|
||||||
|
|
||||||
if b.HasTarget(target) {
|
|
||||||
states.addMatch(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !added {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
possibleActions := b.possibleActions()
|
|
||||||
for _, act := range possibleActions {
|
|
||||||
newActions := append(prevActions, act)
|
|
||||||
newState := b.performAction(act)
|
|
||||||
newState.findTarget(target, states, newActions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Buckets) possibleActions() actions {
|
|
||||||
ret := actions{}
|
|
||||||
for i, bucket := range b {
|
|
||||||
if !bucket.IsFull() {
|
|
||||||
ret = append(ret, action{kind: actionTypeFill, target: i})
|
|
||||||
}
|
|
||||||
if !bucket.IsEmpty() {
|
|
||||||
// ret = append(ret, action{kind: actionTypeEmpty, fn: b.empty(i), target: i})
|
|
||||||
ret = append(ret, action{kind: actionTypeEmpty, target: i})
|
|
||||||
for j, target := range b {
|
|
||||||
if !target.IsFull() && i != j {
|
|
||||||
// ret = append(ret, action{kind: actionTypePour, fn: b.pour(i, j), src: i, target: j})
|
|
||||||
ret = append(ret, action{kind: actionTypePour, src: i, target: j})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
Loading…
Reference in New Issue