Skip to content

Commit

Permalink
Merge ed17497 into 98faaf9
Browse files Browse the repository at this point in the history
  • Loading branch information
pikesley committed Nov 20, 2017
2 parents 98faaf9 + ed17497 commit f02fda4
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 44 deletions.
12 changes: 5 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ install:

solve:
./towers console --discs $(or $(discs),3)


solve-constrained:
./towers console --discs $(or $(discs),3) --constrained

phat:
python webserver.py &
sleep 2
./towers phat

fake-phat:
python dummywebserver.py &
sleep 2
./towers phat
./towers phat --constrained

kill:
kill `ps ax | grep webser | tr -s ' ' ' ' | cut -d ' ' -f 1`
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,29 @@ make phat
to watch this all play out on the pHAT:

<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/Bbrs3diAw1B/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by Sam (@pikesley)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2017-11-19T16:04:50+00:00">Nov 19, 2017 at 8:04am PST</time></p></div></blockquote> <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>

## Constrained version

There is a [constrained variant of the problem](https://www.youtube.com/watch?v=bdMfjfT0lKk), with the restriction that a disc may only move to an adjacent stack. I've also implemented the solution for this (which maps to the rhythms of ternary) - you can run this with

```
make solve-constrained discs=n
```

(this is now the default mode for `make phat`)

## Fake pHAT mode

The Flask webserver attempts to detect if it's running on a Pi, and if it thinks it's not then it will log an ASCII rendition of the pHAT matrix on the console:

```
..... ..... ..... ..... ooo.. .o...
..... ..... ..... ..... o.o.. .o...
..... ..... ..... ..... ooo.. .o...
..... ..... ..... ..... ..... .....
.ooo. ..... ..... ..ooo ..ooo ..ooo
oooo. ..... ..... ..o.o ..o.o ..o.o
ooooo .oo.. ..o.. ..ooo ..ooo ..ooo
```

this may or may not be useful (I'm not sure any of this is useful tbh)
62 changes: 62 additions & 0 deletions lib/ruby/constrained_towers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
class ConstrainedTowers < Towers
@@directions = {}

def initialize discs
super
@ternary = ConstrainedTowers.ternarise @count, @discs
end

def move
flip = Towers.diff ConstrainedTowers.ternarise(@count, @discs), ConstrainedTowers.ternarise(@count += 1, @discs)
source = Towers.find_disc flip, @stacks

@stacks[ConstrainedTowers.find_adjacent_stack flip, source, @stacks].push @stacks[source].pop
end

def binary
ternary
end

def ternary
ConstrainedTowers.ternarise @count, @discs
end

def solved
ternary.chars.all? { |trit| trit.to_i == 2 }
end

def inspect
{
stacks: @stacks,
count: ternary
}
end

def ConstrainedTowers.ternarise value, width
'%0*d' % [width, value.to_s(3)]
end

def ConstrainedTowers.find_adjacent_stack disc, source, stacks
begin
direction = @@directions[disc]
rescue KeyError
@@directions[disc] = :right
direction = right
end

case source
when 0
@@directions[disc] = :right
return 1
when 2
@@directions[disc] = :left
return 1
when 1
if @@directions[disc] == :right
return 2
else
return 0
end
end
end
end
17 changes: 15 additions & 2 deletions lib/ruby/towers.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Towers
attr_reader :count, :stacks, :binary
attr_reader :count, :stacks

def initialize discs
@discs = discs
Expand Down Expand Up @@ -78,16 +78,29 @@ def little_bit value, matrix, offset, side
for i in (row..row + 2) do
matrix[i][column] = 1
end
when 2
for i in (row..row + 2) do
matrix[i][column] = 1
end
matrix[row][column - 1] = 1
matrix[row + 2][column + 1] = 1
end
end

def inspect
{
stacks: @stacks,
count: binary
}
end

def Towers.binarise value, width
'%0*b' % [width, value]
end

def Towers.diff first, second
first.chars.reverse.each_with_index do |bit, index|
if bit < second.chars.reverse[index]
if bit < second.chars.reverse[index]
return index
end
end
Expand Down
65 changes: 65 additions & 0 deletions spec/constrained_towers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
describe ConstrainedTowers do
it 'generates ternary numbers' do
expect(ConstrainedTowers.ternarise 19, 5).to eq '00201'
end

context 'diffing ternary numbers' do
specify 'easy case' do
expect(Towers.diff '001', '002').to eq 0
end

specify 'trickier case' do
expect(Towers.diff '012', '020').to eq 1
end
end
context 'adjacent stacks' do
context 'noddy cases' do
it 'moves from left to centre' do
stacks = [[], [], []]
expect(ConstrainedTowers.find_adjacent_stack 0, 0, stacks).to eq 1
end

it 'moves from right to centre' do
stacks = [[], [], []]
expect(ConstrainedTowers.find_adjacent_stack 0, 2, stacks).to eq 1
end
end
end

it 'follows the rules' do
towers = ConstrainedTowers.new 4

until towers.solved do
towers.move
towers.stacks.each do |stack|
expect(stack.sort.reverse).to eq stack
end
end
end

context 'matrices' do
context 'matrices' do
towers = ConstrainedTowers.new 3
towers.move
towers.move
it 'has the correct second matrix' do
expect(towers.matrix).to eq [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
]
end
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'ruby/towers'
require 'ruby/constrained_towers'
require 'coveralls'

Coveralls.wear!
Expand Down
4 changes: 2 additions & 2 deletions spec/towers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@
context 'locates the next receptive stack' do
it 'solves the easy case' do
stacks = [[2], [0], []]
expect(Towers.find_stack(1, 2, stacks)).to eq 0
expect(Towers.find_stack 1, 2, stacks).to eq 0
end

it 'solves a trickier case' do
stacks = [[], [2], [1]]
expect(Towers.find_stack(0, 2, stacks)).to eq 0
expect(Towers.find_stack 0, 2, stacks).to eq 0
end
end
end
44 changes: 11 additions & 33 deletions towers
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ require 'json'
require 'thor'

require_relative 'lib/ruby/towers'
require_relative 'lib/ruby/constrained_towers'

INTERVAL = 0.3

Expand All @@ -21,28 +22,6 @@ def hit_phat towers
HTTParty.patch(url, body: payload.to_json, headers: headers)
end

def fakephat towers
s = ''
7.times do |i|
45.times do |j|
bit = towers.matrix[i][j]
if (4 - (j % 8)) < 0
s += ' '
else
case bit
when 0
s += '.'
when 1
s += 'o'
end
end
end
s += "\n"
end
s += "\n\n"
print s
end

def display towers
s = ''
towers.stacks.each do |stack|
Expand All @@ -56,9 +35,13 @@ end

class TowersSolver < Thor
desc "phat", "solve the towers against the pHAT's webserver"
option :constrained, type: :boolean
def phat
while true do
towers = Towers.new 5
if options[:constrained]
towers = ConstrainedTowers.new 5
end
until towers.solved do
hit_phat towers
towers.move
Expand All @@ -69,28 +52,23 @@ class TowersSolver < Thor
end
end

desc "fakephat", "simulate the pHAT on the console console"
def fakephat
towers = Towers.new 5
until towers.solved do
fakephat towers
towers.move
sleep INTERVAL
end
fakephat towers
end

desc "console", "solve the towers on the console"
option :discs, default: 3, type: :numeric
option :constrained, type: :boolean
def console
moves = 0
towers = Towers.new options[:discs]
if options[:constrained]
towers = ConstrainedTowers.new options[:discs]
end

until towers.solved do
display towers
towers.move
moves += 1
end
display towers

puts "%s moves to solve %s discs" % [moves, options[:discs]]
end
end
Expand Down

0 comments on commit f02fda4

Please sign in to comment.