WIP: not working
This commit is contained in:
parent
1c7732cf32
commit
ad900e8767
@ -11,7 +11,7 @@ import (
|
||||
// App holds context for the application
|
||||
type App struct {
|
||||
Target uint64
|
||||
Buckets []bucket.Bucket
|
||||
Buckets bucket.Buckets
|
||||
}
|
||||
|
||||
// New returns an instance of the app
|
||||
@ -27,7 +27,7 @@ func New(args ...string) (*App, error) {
|
||||
|
||||
app := App{
|
||||
Target: target,
|
||||
Buckets: []bucket.Bucket{},
|
||||
Buckets: bucket.Buckets{},
|
||||
}
|
||||
|
||||
for _, arg := range args[1:] {
|
||||
@ -44,8 +44,9 @@ func New(args ...string) (*App, error) {
|
||||
// Run runs the application
|
||||
func (a *App) Run() error {
|
||||
fmt.Printf("target: %d\n", a.Target)
|
||||
for i, bucket := range a.Buckets {
|
||||
fmt.Printf("bucket %d: %s\n", i+1, bucket.String())
|
||||
fmt.Printf("buckets: %s\n", a.Buckets.String())
|
||||
for _, solution := range a.Buckets.FindTarget((a.Target)) {
|
||||
fmt.Println(solution)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -10,6 +10,43 @@ type Bucket struct {
|
||||
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
|
||||
@ -20,6 +57,16 @@ 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
|
||||
|
||||
267
lib/bucket/buckets.go
Normal file
267
lib/bucket/buckets.go
Normal file
@ -0,0 +1,267 @@
|
||||
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
Block a user