# Solution: Microwave Numbers (#118)

**URL:** <https://www.ruby-forum.com/t/solution-microwave-numbers-118/90790>\
**Category:** Ruby\
**Created:** [April 1, 2007, 5:33pm UTC](https://www.ruby-forum.com/t/solution-microwave-numbers-118/90790 "2007-04-01T17:33:00Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![lazukars](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eb9ed0/32.png) [@lazukars](https://www.ruby-forum.com/u/lazukars)\
**Post date:** [April 1, 2007, 5:33pm UTC](https://www.ruby-forum.com/t/solution-microwave-numbers-118/90790/1 "2007-04-01T17:33:00Z")

</div>

Well much like my recent return to posting on the O’Reilly Ruby blog,  
I couldn’t resist getting back into the Ruby Q. fray. I’m pretty  
pleased with my solution. I developed it test first and implemented  
all the “extra credit.” I also handle any possible number of seconds,  
as I’m pretty sure I came up with the right algorithm when the seconds  
are more than 99.

Enjoy the code below (warning I know some of the longer lines at the  
bottom will wrap.)

Ryan

# Ruby Q. #118: Microwave

# Solution by Ryan L.

# I’m glad to be back…

require ‘test/unit’

class Microwave

# Finds the most efficient button combo for the given seconds and

metric  
def self.microwave(seconds, metric = proc {|b| euclidean\_distance(b)})  
generate\_buttons(seconds).sort\_by(&metric).first  
end

# Allows a tolerance when finding the most efficient button combo

def self.microwave\_give\_or\_take(seconds, tolerance,  
metric = proc {|b|  
euclidean\_distance(b)})  
((seconds-tolerance)…(seconds+tolerance)).map do |s|  
generate\_buttons(s)  
end.flatten.sort\_by(&metric).first  
end

# Generates the various button combinations for the given seconds

def self.generate\_buttons(seconds)  
result = []  
if seconds \< 100  
result \<\< seconds  
end  
if seconds \> 59  
min,sec = seconds.divmod(60)  
result \<\< “#{min}#{”%02d" % sec}".to\_i  
if min \> 1 and sec \< 40  
sec += 60  
min -= 1  
result \<\< “#{min}#{”%02d" % sec}".to\_i  
end  
end

```
result.sort

```

end

# The default button layout

@points = {  
‘1’ =\> [0,0],  
‘2’ =\> [0,1],  
‘3’ =\> [0,2],  
‘4’ =\> [1,0],  
‘5’ =\> [1,1],  
‘6’ =\> [1,2],  
‘7’ =\> [2,0],  
‘8’ =\> [2,1],  
‘9’ =\> [2,2],  
‘0’ =\> [3,1],  
‘\*’ =\> [3,2]  
}

# Traverses the buttons calculating Euclidean distance

def self.euclidean\_distance(buttons, points = @points)  
button\_path\_distance(buttons, :calc\_euclidean\_distance, points)  
end

# Traverses the buttons calculating Manhattan distance

def self.manhattan\_distance(buttons, points = @points)  
button\_path\_distance(buttons, :calc\_manhattan\_distance, points)  
end

# Surprise, surprise this gives the number of buttons pressed

def self.number\_of\_buttons(buttons)  
# The +1 adds the \* button  
buttons.to\_s.length + 1  
end

private

# The worker function for getting button distance

def self.button\_path\_distance(buttons, method, points)  
result = 0.0  
# Get each digit as a string and add the \* for the cook button,  
# then convert to the points the buttons represent  
sequence = (buttons.to\_s.scan(/./) \<\< ‘\*’).map { |b| points[b] }  
start = sequence.shift  
sequence.each do |point|  
result += self.send(method, start, point)  
start = point  
end  
result  
end

def self.calc\_euclidean\_distance(start\_point, end\_point)  
Math.sqrt(  
((end\_point[0] - start\_point[0]) \*\* 2) +  
((end\_point[1] - start\_point[1]) \*\* 2))  
end

def self.calc\_manhattan\_distance(start\_point, end\_point)  
(end\_point[0] - start\_point[0]).abs +  
(end\_point[1] - start\_point[1]).abs  
end  
end

# Defining the method requested in the quiz

def microwave(seconds)  
Microwave.microwave(seconds)  
end

# A pretty darn thorough test case…I did this all test first

class MicrowaveTest \< Test::Unit::TestCase  
def test\_microwave  
assert\_equal(1, microwave(1))  
assert\_equal(10, microwave(10))  
assert\_equal(35, microwave(35))  
assert\_equal(45, microwave(45))  
assert\_equal(60, microwave(60))  
assert\_equal(74, microwave(74))  
assert\_equal(99, microwave(99))  
assert\_equal(140, microwave(100))  
assert\_equal(159, microwave(119))  
assert\_equal(200, microwave(120))  
assert\_equal(199, microwave(159))  
assert\_equal(240, microwave(160))  
assert\_equal(780, microwave(500))  
assert\_equal(1700, microwave(1020))  
end

def test\_give\_or\_take  
# I was lazy and only decided to test for the given case 🙂  
assert\_equal(99, Microwave.microwave\_give\_or\_take(95, 5))  
end

def test\_generate\_buttons  
assert\_equal([1], Microwave.generate\_buttons(1))  
assert\_equal([13], Microwave.generate\_buttons(13))  
assert\_equal([27], Microwave.generate\_buttons(27))  
assert\_equal([55], Microwave.generate\_buttons(55))  
assert\_equal([60,100], Microwave.generate\_buttons(60))  
assert\_equal([68,108], Microwave.generate\_buttons(68))  
assert\_equal([75,115], Microwave.generate\_buttons(75))  
assert\_equal([99,139], Microwave.generate\_buttons(99))  
assert\_equal([140], Microwave.generate\_buttons(100))  
assert\_equal([141], Microwave.generate\_buttons(101))  
assert\_equal([159], Microwave.generate\_buttons(119))  
assert\_equal([160,200], Microwave.generate\_buttons(120))  
assert\_equal([188,228], Microwave.generate\_buttons(148))  
assert\_equal([199,239], Microwave.generate\_buttons(159))  
assert\_equal([240], Microwave.generate\_buttons(160))  
assert\_equal([250], Microwave.generate\_buttons(170))  
assert\_equal([259], Microwave.generate\_buttons(179))  
assert\_equal([260,300], Microwave.generate\_buttons(180))  
assert\_equal([780,820], Microwave.generate\_buttons(500))  
assert\_equal([1645], Microwave.generate\_buttons(1005))  
assert\_equal([1660,1700], Microwave.generate\_buttons(1020))  
end

def test\_euclidean\_distance  
assert\_in\_delta(3.61, Microwave.euclidean\_distance(1), 0.01)  
assert\_in\_delta(3.61, Microwave.euclidean\_distance(11), 0.01)  
assert\_in\_delta(3.16, Microwave.euclidean\_distance(2), 0.01)  
assert\_in\_delta(3.16, Microwave.euclidean\_distance(22), 0.01)  
assert\_in\_delta(3.0, Microwave.euclidean\_distance(3), 0.01)  
assert\_in\_delta(3.0, Microwave.euclidean\_distance(33), 0.01)  
assert\_in\_delta(2.83, Microwave.euclidean\_distance(4), 0.01)  
assert\_in\_delta(2.83, Microwave.euclidean\_distance(44), 0.01)  
assert\_in\_delta(2.24, Microwave.euclidean\_distance(5), 0.01)  
assert\_in\_delta(2.24, Microwave.euclidean\_distance(55), 0.01)  
assert\_in\_delta(2.0, Microwave.euclidean\_distance(6), 0.01)  
assert\_in\_delta(2.0, Microwave.euclidean\_distance(66), 0.01)  
assert\_in\_delta(2.24, Microwave.euclidean\_distance(7), 0.01)  
assert\_in\_delta(2.24, Microwave.euclidean\_distance(77), 0.01)  
assert\_in\_delta(1.41, Microwave.euclidean\_distance(8), 0.01)  
assert\_in\_delta(1.41, Microwave.euclidean\_distance(88), 0.01)  
assert\_in\_delta(1.0, Microwave.euclidean\_distance(9), 0.01)  
assert\_in\_delta(1.0, Microwave.euclidean\_distance(99), 0.01)  
assert\_in\_delta(1.0, Microwave.euclidean\_distance(0), 0.01)  
assert\_in\_delta(5.0, Microwave.euclidean\_distance(123), 0.01)  
assert\_in\_delta(4.16, Microwave.euclidean\_distance(100), 0.01)  
assert\_in\_delta(3.83, Microwave.euclidean\_distance(159), 0.01)  
end

def test\_manhattan\_distance  
assert\_equal(5, Microwave.manhattan\_distance(1))  
assert\_equal(5, Microwave.manhattan\_distance(11))  
assert\_equal(4, Microwave.manhattan\_distance(2))  
assert\_equal(4, Microwave.manhattan\_distance(22))  
assert\_equal(3, Microwave.manhattan\_distance(7))  
assert\_equal(3, Microwave.manhattan\_distance(777))  
assert\_equal(5, Microwave.manhattan\_distance(100))  
end

def test\_number\_of\_buttons  
assert\_equal(2, Microwave.number\_of\_buttons(1))  
assert\_equal(3, Microwave.number\_of\_buttons(12))  
assert\_equal(4, Microwave.number\_of\_buttons(567))  
assert\_equal(5, Microwave.number\_of\_buttons(1000))  
end  
end

if $0 == **FILE**  
if ARGV.length \> 0  
if ARGV[0] =~ /^\d\*$/  
seconds = ARGV[0].to\_i  
puts “For #{seconds} seconds the ideal minimum microwave buttons  
are:”  
puts “\t#{microwave(seconds)} using Euclidean distances”  
puts “\t#{Microwave.microwave(seconds, proc{|b|  
Microwave.manhattan\_distance(b)})} using Manhattan distances”  
puts “\t#{Microwave.microwave(seconds, proc{|b|  
Microwave.number\_of\_buttons(b)})} using number of buttons”  
puts “\t#{Microwave.microwave\_give\_or\_take(seconds, 5)} using  
Euclidean distances and a 5 second threshold”  
puts “\t#{Microwave.microwave\_give\_or\_take(seconds, 10)} using  
Euclidean distances and a 10 second threshold”  
wide\_points = {  
‘1’ =\> [0,0],  
‘2’ =\> [0,2],  
‘3’ =\> [0,4],  
‘4’ =\> [1,0],  
‘5’ =\> [1,2],  
‘6’ =\> [1,4],  
‘7’ =\> [2,0],  
‘8’ =\> [2,2],  
‘9’ =\> [2,4],  
‘0’ =\> [3,2],  
‘\*’ =\> [3,4]  
}  
puts “\t#{Microwave.microwave(seconds, proc{|b|  
Microwave.euclidean\_distance(b, wide\_points)})} using Euclidean  
distances and wide buttons”  
else  
puts “Usage: #$0 (or nothing to run the test cases)”  
exit(1)  
end  
# Don’t run the test case  
Test::Unit.run = true  
end  
end

---

<div class="post-metadata">

**Author:** ![lazukars](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eb9ed0/32.png) [@lazukars](https://www.ruby-forum.com/u/lazukars)\
**Post date:** [April 1, 2007, 6:03pm UTC](https://www.ruby-forum.com/t/solution-microwave-numbers-118/90790/2 "2007-04-01T18:03:02Z")

</div>

# My straight-forward solution.

require ‘enumerator’

# How much can the time differ?

FUZZ = 0

POS = { ?1 =\> [0,0], ?2 =\> [1,0], ?3 =\> [2,0],  
?4 =\> [0,1], ?5 =\> [1,1], ?6 =\> [2,1],  
?7 =\> [0,2], ?8 =\> [1,2], ?9 =\> [2,2],  
?0 =\> [1,3], ?\* =\> [2,3] }

def metric(string)  
string.enum\_for(:each\_byte).map { |b| POS[b] }.  
enum\_for(:each\_cons, 2).inject(0) { |sum, ((x1,y1), (x2, y2))|  
# 1-norm  
# sum + (x1-x2).abs + (y1-y2).abs

```
# 2-norm
sum + Math.sqrt((x1-x2)**2 + (y1-y2)**2)

```

}  
end

def entries(time)  
return [] if time \<= 0

min, sec = time.divmod(60)  
entries = []

# seconds only

entries \<\< “%d\*” % [time] if time \< 100

# usual time format

entries \<\< “%d%02d\*” % [min, sec]

# more than 60 seconds

entries \<\< ("%d%02d\*" % [min-1, sec+60]) if min \> 1 && sec \< 40

entries  
end

1.upto(999) { |time|  
entries = (-FUZZ…FUZZ).map { |offset| entries(time + offset)  
}.flatten

# Sort by movement length, then by keypresses.

quickest = entries.sort\_by { |s| [metric(s), s.size] }.first  
puts “%3d (%02d:%02d): %s” % [time, time.divmod(60), quickest].flatten  
}

**END**

---

<div class="post-metadata">

**Author:** ![lazukars](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/eb9ed0/32.png) [@lazukars](https://www.ruby-forum.com/u/lazukars)\
**Post date:** [April 2, 2007, 5:18am UTC](https://www.ruby-forum.com/t/solution-microwave-numbers-118/90790/3 "2007-04-02T05:18:57Z")

</div>

On Apr 1, 2007, at 10:32 AM, Ryan L. wrote:

> # Ruby Q. #118: Microwave
> 
> # Solution by Ryan L.
> 
> # I’m glad to be back…

I, for one, am very glad to have you back.

James Edward G. II
