Hourglass puzzle: Difference between revisions

From Rosetta Code
Content added Content deleted
(added Raku programming solution)
m (→‎{{header|Raku}}: side note)
Line 256: Line 256:
At time t = 14 , flip hourglass 7
At time t = 14 , flip hourglass 7
At time t = 16 , flip hourglass 4 end = 9</pre>
At time t = 16 , flip hourglass 4 end = 9</pre>

If interim-flips are allowed I think the following is shorter, just that I wish I knew how to implement it ..

<lang perl6>t x(4) y(7)
0
4 flip record = 0
7 flip flip record +=3
10 stop flip record +=3
13 stop record +=3</lang>


=={{header|REXX}}==
=={{header|REXX}}==

Revision as of 17:20, 30 December 2020

Hourglass puzzle is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Given two hourglass of 4 minutes and 7 minutes, the task is to measure 9 minutes.

Notes

Implemented as a 1-player game.

Go

Translation of: Julia

<lang go>package main

import (

   "fmt"
   "log"

)

func minimum(a []int) int {

   min := a[0]
   for i := 1; i < len(a); i++ {
       if a[i] < min {
           min = a[i]
       }
   }
   return min

}

func sum(a []int) int {

   s := 0
   for _, i := range a {
       s = s + i
   }
   return s

}

func hourglassFlipper(hourglasses []int, target int) (int, []int) {

   flippers := make([]int, len(hourglasses))
   copy(flippers, hourglasses)
   var series []int
   for iter := 0; iter < 10000; iter++ {
       n := minimum(flippers)
       series = append(series, n)
       for i := 0; i < len(flippers); i++ {
           flippers[i] -= n
       }
       for i, flipper := range flippers {
           if flipper == 0 {
               flippers[i] = hourglasses[i]
           }
       }
       for start := len(series) - 1; start >= 0; start-- {
           if sum(series[start:]) == target {
               return start, series
           }
       }
   }
   log.Fatal("Unable to find an answer within 10,000 iterations.")
   return 0, nil

}

func main() {

   fmt.Print("Flip an hourglass every time it runs out of grains, ")
   fmt.Println("and note the interval in time.")
   hgs := [][]int{{4, 7}, {5, 7, 31}}
   ts := []int{9, 36}
   for i := 0; i < len(hgs); i++ {
       start, series := hourglassFlipper(hgs[i], ts[i])
       end := len(series) - 1
       fmt.Println("\nSeries:", series)
       fmt.Printf("Use hourglasses from indices %d to %d (inclusive) to sum ", start, end)
       fmt.Println(ts[i], "using", hgs[i])
   }

}</lang>

Output:
Flip an hourglass every time it runs out of grains, and note the interval in time.

Series: [4 3 1 4 2 2]
Use hourglasses from indices 2 to 5 (inclusive) to sum 9 using [4 7]

Series: [5 2 3 4 1 5 1 4 3 2 1 4 5 2 3 4 1]
Use hourglasses from indices 4 to 16 (inclusive) to sum 36 using [5 7 31]

Julia

Implemented as a game solver rather than as a game with user input. <lang julia>function euclidean_hourglassflipper(hourglasses, target::Integer)

   gcd(hourglasses) in hourglasses && !(1 in hourglasses) && throw("Hourglasses fail sanity test (not relatively prime enough)")
   flippers, series = deepcopy(hourglasses), Int[]
   for i in 1:typemax(target)
       n = minimum(flippers)
       push!(series, n)
       flippers .-= n
       for (i, n) in enumerate(flippers)
           if n == 0
               flippers[i] = hourglasses[i]
           end
       end
       for startpoint in length(series):-1:1
           if sum(series[startpoint:end]) == target
               println("Series: $series")
               return startpoint, length(series)
           end
       end
   end

end

println("Flip an hourglass every time it runs out of grains, and note the interval in time.") i, j = euclidean_hourglassflipper([4, 7], 9) println("Use hourglasses from step $i to step $j (inclusive) to sum 9 using [4, 7]") i, j = euclidean_hourglassflipper([5, 7, 31], 36) println("Use hourglasses from step $i to step $j (inclusive) to sum 36 using [5, 7, 31]")

</lang>

Output:
Flip an hourglass every time it runs out of grains, and note the interval in time.
Series: [4, 3, 1, 4, 2, 2]
Use hourglasses from step 3 to step 6 (inclusive) to sum 9 using [4, 7]
Series: [5, 2, 3, 4, 1, 5, 1, 4, 3, 2, 1, 4, 5, 2, 3, 4, 1]
Use hourglasses from step 5 to step 17 (inclusive) to sum 36 using [5, 7, 31]

tested with FMSlogo <lang logo> to bb Make "small_capacity 4 Make "big_capacity 7 make "small 0 make "big 0 make "t 0 print "_____________decision_0_game_over print "_________decision_1_start_timing print "_______decision_2_flip_small print "____decision_3_flip_big print "__decision_4_flip_both print "_________any_other_number________________wait do.until [show list list :small :big :t print "your_decision_0_1_2_3_4 human_decision if :my_decision>1 [machine_computes] ] [:my_decision=0] print list :t "minutes_passed end

to human_decision make "my_decision readword if :my_decision=1 [print "reset_timer make "t 0] if :my_decision=2 [print "flip_small make "small :small_capacity-:small] if :my_decision=3 [print "flip_big make "big :big_capacity-:big] if :my_decision=4 [print "flip_both make "small :small_capacity-:small make "big :big_capacity-:big ] if :my_decision>4 [print "wait] end

to machine_computes ifelse :small>:big [make "my_selection :big] [make "my_selection :small] if :small=0 [make "my_selection :big] if :big=0 [make "my_selection :small] make "small :small-:my_selection make "big :big-:my_selection make "t :t+:my_selection if :small<0 [make "small 0] if :big<0 [make "big 0] end

to zzz

A. 7 minutes with 4- and 5-minute timers
B. 15 minutes with 7- and 11-minute timers
C. 14 minutes with 5- and 8-minute timers

ifelse YesNoBox [Welcome] [run / show me the code] [bb] [edall]

A is possible
Turn both the 5 and the 4. When the 4 runs out, flip it over.Now, when the 5 runs out, start timing. The 4 will run for three more minutes, after which, you can flip it over to reach 7.
B is possible
Turn both the 7 and the 11. When the 7 runs out, start timing. The 11 will run for 4 more minutes, after which it can be flipped to reach 15.
C is possible
Turn both the 5 and the 8. When the 5 runs out, flip it. The 8 will then run out after 3 minutes, leaving 2 minutes in the 5. Flip the 8 then. When the 5 runs out, start timing. There are now 6 minutes left in the 8, and flipping the 8 after those 6 minutes gives 6 + 8 = 14 minutes.

end

Make "big 0 Make "big_capacity 5 Make "my_decision " Make "my_selection 4 Make "small 0 Make "small_capacity 4 Make "startup [zzz] Make "t 0


</lang>

Python

There isn't much of a task description as I write this, but, here goes...

<lang python>def hourglass_puzzle():

   t4 = 0
   while t4 < 10_000:
       t7_left = 7 - t4 % 7
       if t7_left == 9 - 4:
           break
       t4 += 4
   else:
       print('Not found')
       return 
   print(f"""

Turn over both hour glasses at the same time and continue flipping them each when they individually run down until the 4 hour glass is flipped {t4//4} times, wherupon the 7 hour glass is immediately placed on its side with {t7_left} hours of sand in it. You can measure 9 hours by flipping the 4 hour glass once, then flipping the remaining sand in the 7 hour glass when the 4 hour glass ends.

""")

hourglass_puzzle()</lang>

Output:
Turn over both hour glasses at the same time and continue flipping them each
when they individually run down until the 4 hour glass is flipped 4 times,
wherupon the 7 hour glass is immediately placed on its side with 5 hours 
of sand in it.
You can measure 9 hours by flipping the 4 hour glass once, then
flipping the remaining sand in the 7 hour glass when the 4 hour glass ends.

Raku

<lang perl6># 20201230 Raku programming solution

my @hourglasses = 4, 7; my $target = 9; my @output = []; my %elapsed = 0 => 1 ; my $done = False ;

for 1 .. ∞ -> $t {

  my $flip-happened = False;
  for @hourglasses -> $hg {
     unless $t % $hg {
        %elapsed{$t} = 1 unless %elapsed{$t};
        with @output[$t] { $_ ~= "\t, flip hourglass $hg " } else {
           $_ = "At time t = $t , flip hourglass $hg" }
        $flip-happened = True
     }
  }
  if $flip-happened {
     for %elapsed.keys.sort -> $t1 {
        if ($t - $t1) == $target {
           @output[$t1] ~= "\tbegin = 0";
           @output[$t]  ~= "\tend   = $target";
           $done = True
        }
        %elapsed{$t} = 1 unless %elapsed{$t} ;
     }
  }
  last if $done

}

.say if .defined for @output</lang>

Output:
At time t = 4 , flip hourglass 4
At time t = 7 , flip hourglass 7        begin = 0
At time t = 8 , flip hourglass 4
At time t = 12 , flip hourglass 4
At time t = 14 , flip hourglass 7
At time t = 16 , flip hourglass 4       end   = 9

If interim-flips are allowed I think the following is shorter, just that I wish I knew how to implement it ..

<lang perl6>t x(4) y(7) 0 4 flip record = 0 7 flip flip record +=3 10 stop flip record +=3 13 stop record +=3</lang>

REXX

This example is incomplete. Output does not show numbers? Please ensure that it meets all task requirements and remove this message.
Translation of: Python

<lang rexx>/*REXX program determines if there is a solution to measure 9 minutes using a */ /*──────────────────────────────────── four and seven minute sandglasses. */ t4= 0 mx= 10000

          do t4=0  by 4  to mx
          t7_left= 7   -   t4 % 7
          if t7_left==9-4  then leave
          end   /*t4*/

say if t4>mx then do

              say 'Not found.'
              exit 4
              end

say "Turn over both sandglasses (at the same time) and continue" say "flipping them each when the sandglasses individually run down" say "until the four-minute glass is flipped {t4//4} times," say "whereupon the seven-minute glass is immediately placed on its" say "side with {t7_left} minutes of sand in it." say say "You can measure 9 minutes by flipping the four-minute glass" say "once, then flipping the remaining sand in the seven-minute" say "glass when the four-minute glass ends." say exit 0</lang>

output   when using the internal default input:
Turn over both sandglasses  (at the same time)  and continue
flipping them each when the sandglasses individually run down
until the four-minute glass is flipped  {t4//4}  times,
whereupon the seven-minute glass is immediately placed on its
side with  {t7_left}  minutes of sand in it.

You can measure 9 minutes by flipping the four-minute glass
once,  then flipping the remaining sand in the seven-minute
glass when the four-minute glass ends.

Wren

Translation of: Julia
Library: Wren-math

<lang ecmascript>import "/math" for Nums

var hourglassFlipper = Fn.new { |hourglasses, target|

   var flippers = hourglasses.toList
   var series = []
   for (iter in 0...10000) {
       var n = Nums.min(flippers)
       series.add(n)
       for (i in 0...flippers.count) flippers[i] = flippers[i] - n
       var i = 0
       for (flipper in flippers) {
           if (flipper == 0) flippers[i] = hourglasses[i]
           i = i + 1
       }
       for (start in series.count-1..0) {
           if (Nums.sum(series[start..-1]) == target) return [start, series]
       }
   }
   Fiber.abort("Unable to find an answer within 10,000 iterations.")

}

System.write("Flip an hourglass every time it runs out of grains, ") System.print("and note the interval in time.") var tests = [ [[4, 7], 9], [[5, 7, 31], 36] ] for (test in tests) {

   var hourglasses = test[0]
   var target = test[1]
   var res = hourglassFlipper.call(hourglasses, target)
   var start = res[0]
   var series = res[1]
   var end = series.count - 1
   System.print("\nSeries: %(series)")
   System.write("Use hourglasses from indices %(start) to %(end) (inclusive) to sum ")
   System.print("%(target) using %(hourglasses)")

}</lang>

Output:
Flip an hourglass every time it runs out of grains, and note the interval in time.

Series: [4, 3, 1, 4, 2, 2]
Use hourglasses from indices 2 to 5 (inclusive) to sum 9 using [4, 7]

Series: [5, 2, 3, 4, 1, 5, 1, 4, 3, 2, 1, 4, 5, 2, 3, 4, 1]
Use hourglasses from indices 4 to 16 (inclusive) to sum 36 using [5, 7, 31]