rpiet 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +12 -0
  4. data/bin/color_wheel +84 -0
  5. data/bin/image_gen +39 -0
  6. data/bin/rpiet +68 -11
  7. data/images/counter.txt +7 -0
  8. data/lib/rpiet/asg/graph_interpreter.rb +39 -0
  9. data/lib/rpiet/asg/parser.rb +156 -0
  10. data/lib/rpiet/asg/visitor.rb +66 -0
  11. data/lib/rpiet/asg.rb +336 -0
  12. data/lib/rpiet/codel_chooser.rb +32 -4
  13. data/lib/rpiet/color.rb +70 -25
  14. data/lib/rpiet/cycle.rb +18 -7
  15. data/lib/rpiet/debugger/debugger.rb +298 -0
  16. data/lib/rpiet/debugger/stylesheet.css +88 -0
  17. data/lib/rpiet/direction_pointer.rb +49 -18
  18. data/lib/rpiet/event_handler.rb +62 -7
  19. data/lib/rpiet/group.rb +25 -8
  20. data/lib/rpiet/image/ascii_image.rb +8 -20
  21. data/lib/rpiet/image/image.rb +8 -3
  22. data/lib/rpiet/image/url_image.rb +28 -14
  23. data/lib/rpiet/interpreter.rb +72 -72
  24. data/lib/rpiet/ir/assembler.rb +87 -0
  25. data/lib/rpiet/ir/builder.rb +255 -0
  26. data/lib/rpiet/ir/cfg.rb +494 -0
  27. data/lib/rpiet/ir/instructions.rb +536 -0
  28. data/lib/rpiet/ir/ir_cfg_interpreter.rb +23 -0
  29. data/lib/rpiet/ir/ir_interpreter.rb +101 -0
  30. data/lib/rpiet/ir/ir_native_interpreter.rb +77 -0
  31. data/lib/rpiet/ir/jruby_backend.rb +279 -0
  32. data/lib/rpiet/ir/operands.rb +28 -0
  33. data/lib/rpiet/ir/passes/data_flow_problem.rb +32 -0
  34. data/lib/rpiet/ir/passes/flow_graph_node.rb +76 -0
  35. data/lib/rpiet/ir/passes/peephole.rb +214 -0
  36. data/lib/rpiet/ir/passes/push_pop_elimination_pass.rb +112 -0
  37. data/lib/rpiet/live_machine_state.rb +15 -0
  38. data/lib/rpiet/machine.rb +62 -32
  39. data/lib/rpiet/source.rb +83 -0
  40. data/lib/rpiet/version.rb +1 -1
  41. data/lib/rpiet.rb +2 -2
  42. data/rpiet.gemspec +19 -0
  43. data/spec/asg/visitor_spec.rb +41 -0
  44. data/spec/cycle_spec.rb +34 -34
  45. data/spec/direction_pointer_spec.rb +33 -6
  46. data/spec/group_spec.rb +73 -48
  47. data/spec/interpreter_spec.rb +161 -12
  48. data/spec/ir/assembler_spec.rb +122 -0
  49. data/spec/ir/builder_spec.rb +20 -0
  50. data/spec/ir/cfg_spec.rb +151 -0
  51. data/spec/ir/ir_interpreter_spec.rb +102 -0
  52. data/spec/ir/passes/push_pop_elimination_pass_spec.rb +34 -0
  53. data/spec/machine_spec.rb +5 -3
  54. data/spec/source_spec.rb +69 -0
  55. data/spec/spec_helper.rb +78 -0
  56. metadata +54 -16
  57. data/images/nfib.png +0 -0
@@ -0,0 +1,112 @@
1
+ require_relative 'data_flow_problem'
2
+ require_relative 'flow_graph_node'
3
+
4
+ module RPiet
5
+ module IR
6
+ module Passes
7
+ class PushInfo
8
+ attr_reader :bb, :instr
9
+ def initialize(bb, instr)
10
+ @bb, @instr = bb, instr
11
+ end
12
+
13
+ def ==(other)
14
+ other && operand == other.operand
15
+ end
16
+
17
+ def operand = instr&.operand
18
+
19
+ def to_s
20
+ "#{bb.label}:#{instr}"
21
+ end
22
+ alias inspect to_s
23
+ end
24
+
25
+ class PushPopEliminationNode < FlowGraphNode
26
+ attr_reader :ins, :outs
27
+ TOP, BOTTOM = nil, PushInfo.new(nil, nil)
28
+
29
+ def initialize(problem, bb)
30
+ super
31
+ end
32
+
33
+ def replace_instr(instr, new_instr)
34
+ index = bb.instrs.index(instr)
35
+ bb.instrs[index] = new_instr
36
+ end
37
+
38
+ def apply_transfer_function(instr)
39
+ if instr.operation == :push
40
+ @ins ||= []
41
+ @ins << PushInfo.new(bb, instr)
42
+ elsif instr.operation == :pop
43
+ push = @ins.pop # Can ASG create invalid instrs combos of push/pop?
44
+ if push != TOP && push != BOTTOM
45
+ puts "replace pop #{instr} with #{push.instr} in #{push.bb.label}" if debug
46
+ replace_instr(instr, Instructions::CopyInstr.new(instr.result, push.operand))
47
+ @remove_instrs << [push.bb, push.instr]
48
+ end
49
+ end
50
+ end
51
+
52
+ def assign_outs
53
+ puts "assign_outs: #{bb.label}" if debug
54
+ puts "ins: #{@ins}" if debug
55
+ @remove_instrs.each { |bb, instr| bb.instrs.delete(instr) }
56
+ @outs = @ins.dup
57
+ puts "outs: #{@outs}" if debug
58
+ end
59
+
60
+ def compute_meet(other)
61
+ if @ins.nil?
62
+ puts "initial meet: #{self} +++ #{other.outs.dup}" if debug
63
+ @ins = other.outs.dup
64
+ return
65
+ end
66
+
67
+ puts "continued meet: #{self} +++ #{other}" if debug
68
+ oins = other.outs
69
+
70
+ # if one branch has more pushes recorded than another branch then obviously
71
+ # all branches do not have a push to eliminate. We truncate to shortest of n
72
+ # last pushes.
73
+ if @ins.size > oins.size
74
+ @ins = @ins[-oins.size..-1]
75
+ elsif @ins.size < oins.size
76
+ oins = oins[-@ins.size..-1]
77
+ end
78
+
79
+ @ins.zip(oins).each_with_index do |(joined_in, other_in), index|
80
+ if joined_in != other_in
81
+ if other_in != TOP # Any mismatched value lowers to unsolved
82
+ @ins[index] = BOTTOM
83
+ elsif joined_in == TOP # if meet has nothing yet then we can give it incoming value
84
+ @ins[index] = other_in # Note: I doubt this is possible for this pass
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def solution_changed?
91
+ !@remove_instrs.empty?
92
+ end
93
+
94
+ def solution_init
95
+ @remove_instrs = []
96
+ end
97
+
98
+ def to_s
99
+ "#{bb.label}\n#{bb.instrs.map {|i| i.disasm}.join("\n")}\nins: #{@ins}\nouts: #{@outs}"
100
+ end
101
+ alias inspect to_s
102
+
103
+ end
104
+
105
+ class PushPopEliminationProblem < DataFlowProblem
106
+ def initialize(cfg)
107
+ super(cfg, PushPopEliminationNode)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ module RPiet
2
+ module LiveMachineState
3
+ attr_reader :stack, :event_handler
4
+ attr_accessor :dp, :cc, :input, :output
5
+
6
+ def reset_machine
7
+ @stack, @dp, @cc, @input, @output = [], DirectionPointer.new, CodelChooser.new, $stdin, $stdout
8
+ end
9
+
10
+ def inspect
11
+ "dp: #{dp}, cc: #{cc}, st: #{stack}"
12
+ end
13
+ alias :to_s :inspect
14
+ end
15
+ end
data/lib/rpiet/machine.rb CHANGED
@@ -1,18 +1,25 @@
1
- require 'rpiet/direction_pointer'
2
- require 'rpiet/codel_chooser'
1
+ require_relative 'direction_pointer'
2
+ require_relative 'codel_chooser'
3
+ require_relative 'live_machine_state'
3
4
 
4
5
  module RPiet
6
+ # Using the value npiet decided to use if division by zero happens.
7
+ DIV_BY_ZERO_VALUE = 99999999
8
+
5
9
  ##
6
10
  # This is a simple piet runtime to be controled by interp.
7
11
  #
8
12
  # dp - Direction Pointer (right, down, left, up)
9
13
  # cc - Codel Chooser (left, right)
10
14
  class Machine
11
- attr_reader :dp, :cc, :stack
15
+ ##
16
+ # Each group's size represents a block value which can be used by the push operation.
12
17
  attr_accessor :block_value
13
18
 
19
+ include LiveMachineState
20
+
14
21
  def initialize
15
- @stack, @dp, @cc = [], DirectionPointer.new, CodelChooser.new
22
+ reset_machine
16
23
  @block_value = 1
17
24
  end
18
25
 
@@ -25,35 +32,53 @@ module RPiet
25
32
  ##
26
33
  # Change either codel chooser or direction pointer to try and look
27
34
  # at a different codel.
28
- def orient_elsewhere(i)
29
- if i.even?
35
+ def orient_elsewhere(attempt)
36
+ if attempt.even?
30
37
  dp.rotate!
31
38
  else
32
39
  cc.switch!
33
40
  end
34
41
  end
35
42
 
36
- def noop; end
37
- def push; @stack << @block_value; end
38
- def pop; @stack.pop; end
39
-
40
- def add; math_op :+; end
41
- def sub; math_op :-; end
42
- def mult; math_op :*; end
43
- def div; math_op :/; end
44
- def mod; math_op :%; end
45
-
46
- def gtr; bin_op { |a, b| @stack << (a > b ? 1 : 0) }; end
47
- def not; unary_op { |top| @stack << (!top || top == 0 ? 1 : 0) }; end
48
- def dup; @stack << @stack[-1]; end
49
- def nout; unary_op { |top| print top }; end
50
- def cout; unary_op { |top| print top.chr }; end
51
- def pntr; unary_op { |top| @dp.rotate! top }; end
52
- def swch; unary_op { |top| @cc.switch! top }; end
53
- def n_in; puts "Enter an integer: "; @stack << $stdin.gets.to_i; end
54
- def c_in; $stdout.write "> "; c = $stdin.read(1).ord; @stack << c; end
43
+ def noop
44
+ end
45
+
46
+ def push = @stack << @block_value
47
+ def pop = @stack.pop
48
+
49
+ def add = math_op :+
50
+ def sub = math_op :-
51
+ def mult = math_op :*
52
+
53
+ # Note: Following npiet's div by zero value.
54
+ def div = bin_op { |a, b| @stack << (b == 0 ? DIV_BY_ZERO_VALUE : a / b) }
55
+
56
+ def mod = math_op :%
57
+
58
+ def gtr = bin_op { |a, b| @stack << (a > b ? 1 : 0) }
59
+ def not = unary_op { |top| @stack << (!top || top == 0 ? 1 : 0) }
60
+
61
+ def dup
62
+ @stack << @stack[-1] if @stack[-1]
63
+ end
64
+
65
+ def nout = unary_op { |top| print top }
66
+ def cout = unary_op { |top| print top.chr }
67
+ def pntr = unary_op { |top| @dp.rotate! top }
68
+ def swch = unary_op { |top| @cc.switch! top }
69
+
70
+ def nin
71
+ output.puts "Enter an integer: "
72
+ @stack << input.gets.to_i
73
+ end
74
+
75
+ def cin
76
+ output.write "> "
77
+ @stack << input.read(1).ord
78
+ end
79
+
55
80
  def roll
56
- bin_op do |depth, num|
81
+ bin_op do |depth, num|
57
82
  num %= depth
58
83
  return if depth <= 0 || num == 0
59
84
  if num > 0
@@ -65,17 +90,22 @@ module RPiet
65
90
  end
66
91
 
67
92
  def inspect
68
- "dp: #{@dp.ascii}, cc: #{@cc.ascii(@dp)}, bv: #{@block_value}, st: #{@stack}"
93
+ super + ", bv: #@block_value"
69
94
  end
70
95
  alias :to_s :inspect
71
96
 
72
97
  ##
73
98
  # Execute the operation represented by the two colors
74
99
  def execute(color1, color2)
100
+ operation = calculate_operation(color1, color2)
101
+ __send__(operation)
102
+ operation
103
+ end
104
+
105
+ def calculate_operation(color1, color2)
75
106
  dh = color1.hue.delta color2.hue
76
107
  dd = color1.lightness.delta color2.lightness
77
- operation = OPERATION[dh][dd]
78
- __send__(operation)
108
+ OPERATION[dh][dd]
79
109
  end
80
110
 
81
111
  private
@@ -85,7 +115,7 @@ module RPiet
85
115
  end
86
116
 
87
117
  def unary_op(&block)
88
- return unless @stack.length >= 1
118
+ return if @stack.length < 1
89
119
  block.call(@stack.pop)
90
120
  end
91
121
 
@@ -105,7 +135,7 @@ module RPiet
105
135
  [:add, :sub, :mult],
106
136
  [:div, :mod, :not],
107
137
  [:gtr, :pntr, :swch],
108
- [:dup, :roll, :n_in],
109
- [:c_in, :nout, :cout]]
138
+ [:dup, :roll, :nin],
139
+ [:cin, :nout, :cout]]
110
140
  end
111
141
  end
@@ -0,0 +1,83 @@
1
+ require_relative 'color'
2
+ require_relative 'group'
3
+ require_relative 'image/image'
4
+
5
+
6
+ module RPiet
7
+ class Source
8
+ attr_reader :rows, :cols, :groups, :pixels, :codel_size
9
+
10
+ def initialize(image)
11
+ @cols, @rows = image.size.map &:to_i
12
+ @codel_size = image.codel_size
13
+ @pixels = alloc_matrix { |i, j| image.pixel(i, j)}
14
+ @groups_matrix, @groups = calculate_groups
15
+ end
16
+
17
+ def entry_group
18
+ end
19
+
20
+ ##
21
+ # Is this point on the image and not black?
22
+ def valid?(x, y)
23
+ x >= 0 && x < @cols && y >= 0 && y < @rows &&
24
+ @pixels[x][y] != RPiet::Color::BLACK
25
+ end
26
+
27
+ def group_at(x, y)
28
+ @groups_matrix[x][y]
29
+ end
30
+
31
+ ##
32
+ # With grid of pixels start in upper left corner processing each pixel
33
+ # rightwards and downwards. As you encounter a pixel look up and left to
34
+ # see if it is a new color or part of an existing neighboring group.
35
+ def calculate_groups
36
+ groups_matrix = alloc_matrix { |i, j| 0 }
37
+ group = []
38
+ walk_matrix do |i, j|
39
+ color = @pixels[i][j]
40
+ up = j-1 >= 0 ? groups_matrix[i][j-1] : nil
41
+ left = i-1 >= 0 ? groups_matrix[i-1][j] : nil
42
+ if up && up.color == color
43
+ up << [i, j]
44
+ groups_matrix[i][j] = up
45
+ # disjoint groups to merge
46
+ if left && left != up && left.color == color
47
+ up.merge(groups_matrix, left)
48
+ left.points.each do |x, y|
49
+ groups_matrix[x][y] = up
50
+ group.delete left
51
+ end
52
+ end
53
+ end
54
+
55
+ if groups_matrix[i][j] == 0 && left && left.color == color
56
+ left << [i, j]
57
+ groups_matrix[i][j] = left
58
+ end
59
+
60
+ if groups_matrix[i][j] == 0 # Create new group for first codel found
61
+ groups_matrix[i][j] = RPiet::Group.new(color, [i, j])
62
+ group << groups_matrix[i][j]
63
+ end
64
+ end
65
+ group.each { |group| group.calculate_corners }
66
+ return groups_matrix, group
67
+ end
68
+
69
+ private def alloc_matrix
70
+ Array.new(@cols) { Array.new(@rows) {nil} }.tap do |matrix|
71
+ walk_matrix {|i, j| matrix[i][j] = yield i, j }
72
+ end
73
+ end
74
+
75
+ private def walk_matrix
76
+ 0.upto(@cols-1) do |i|
77
+ 0.upto(@rows-1) do |j|
78
+ yield i, j
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/rpiet/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RPiet
2
- VERSION = '0.1'
2
+ VERSION = '0.2'
3
3
  end
data/lib/rpiet.rb CHANGED
@@ -1,2 +1,2 @@
1
- require 'rpiet/version'
2
- require 'rpiet/interpreter'
1
+ require_relative 'rpiet/version'
2
+ require_relative 'rpiet/interpreter'
data/rpiet.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rpiet/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'rpiet'
7
+ s.version = RPiet::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = 'Thomas E. Enebo'
10
+ s.email = 'tom.enebo@gmail.com'
11
+ s.homepage = 'http://github.com/enebo/rpiet'
12
+ s.summary = 'A Piet runtime'
13
+ s.description = 'An implementation of the esoteric programming language Piet'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../../lib/rpiet/asg/parser'
2
+ require_relative '../../lib/rpiet/asg/visitor'
3
+ require_relative '../../lib/rpiet/image/ascii_image'
4
+
5
+ describe "RPiet::Visitor" do
6
+ let(:cycle) do
7
+ RPiet::Image::AsciiImage.new <<-EOS
8
+ nb db nb
9
+ db ++ nb
10
+ db db db
11
+ EOS
12
+ end
13
+
14
+ let(:my_visitor) do
15
+ Class.new(RPiet::Visitor) {
16
+ attr_reader :nodes
17
+
18
+ def initialize
19
+ super
20
+ @nodes = {}
21
+ end
22
+
23
+ def visit_first(node)
24
+ @nodes[node] = 1
25
+ super
26
+ end
27
+ alias :visit_first_swch :visit_first
28
+ alias :visit_first_pntr :visit_first
29
+
30
+ def visit_again(node)
31
+ @nodes[node] += 1
32
+ end
33
+ }.new
34
+ end
35
+
36
+ it "can visit all nodes once plus one extra visit for a cycle" do
37
+ my_visitor.run RPiet::ASG::Parser.new(cycle).run
38
+ expect(my_visitor.nodes.size).to eq(10)
39
+ expect(my_visitor.nodes.values.inject(0) { |s, e| s += e; s}).to eq(11) # 1 node visited twice
40
+ end
41
+ end
data/spec/cycle_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'rpiet/cycle'
1
+ require_relative '../lib/rpiet/cycle'
2
2
 
3
3
  module A
4
4
  extend RPiet::CycleMethod
@@ -7,50 +7,50 @@ end
7
7
 
8
8
  describe "RPiet::Cycle" do
9
9
  it "can do cycle addition" do
10
- (A::Letter::A + A::Letter::A).should == A::Letter::A
11
- (A::Letter::A + A::Letter::B).should == A::Letter::B
12
- (A::Letter::A + A::Letter::C).should == A::Letter::C
13
- (A::Letter::B + A::Letter::A).should == A::Letter::B
14
- (A::Letter::B + A::Letter::B).should == A::Letter::C
15
- (A::Letter::B + A::Letter::C).should == A::Letter::A
16
- (A::Letter::C + A::Letter::A).should == A::Letter::C
17
- (A::Letter::C + A::Letter::B).should == A::Letter::A
18
- (A::Letter::C + A::Letter::C).should == A::Letter::B
10
+ expect(A::Letter::A + A::Letter::A).to eq(A::Letter::A)
11
+ expect(A::Letter::A + A::Letter::B).to eq(A::Letter::B)
12
+ expect(A::Letter::A + A::Letter::C).to eq(A::Letter::C)
13
+ expect(A::Letter::B + A::Letter::A).to eq(A::Letter::B)
14
+ expect(A::Letter::B + A::Letter::B).to eq(A::Letter::C)
15
+ expect(A::Letter::B + A::Letter::C).to eq(A::Letter::A)
16
+ expect(A::Letter::C + A::Letter::A).to eq(A::Letter::C)
17
+ expect(A::Letter::C + A::Letter::B).to eq(A::Letter::A)
18
+ expect(A::Letter::C + A::Letter::C).to eq(A::Letter::B)
19
19
  end
20
20
 
21
21
  it "can do cycle subtraction" do
22
- (A::Letter::A - A::Letter::A).should == A::Letter::A
23
- (A::Letter::A - A::Letter::B).should == A::Letter::C
24
- (A::Letter::A - A::Letter::C).should == A::Letter::B
25
- (A::Letter::B - A::Letter::A).should == A::Letter::B
26
- (A::Letter::B - A::Letter::B).should == A::Letter::A
27
- (A::Letter::B - A::Letter::C).should == A::Letter::C
28
- (A::Letter::C - A::Letter::A).should == A::Letter::C
29
- (A::Letter::C - A::Letter::B).should == A::Letter::B
30
- (A::Letter::C - A::Letter::C).should == A::Letter::A
22
+ expect(A::Letter::A - A::Letter::A).to eq(A::Letter::A)
23
+ expect(A::Letter::A - A::Letter::B).to eq(A::Letter::C)
24
+ expect(A::Letter::A - A::Letter::C).to eq(A::Letter::B)
25
+ expect(A::Letter::B - A::Letter::A).to eq(A::Letter::B)
26
+ expect(A::Letter::B - A::Letter::B).to eq(A::Letter::A)
27
+ expect(A::Letter::B - A::Letter::C).to eq(A::Letter::C)
28
+ expect(A::Letter::C - A::Letter::A).to eq(A::Letter::C)
29
+ expect(A::Letter::C - A::Letter::B).to eq(A::Letter::B)
30
+ expect(A::Letter::C - A::Letter::C).to eq(A::Letter::A)
31
31
  end
32
32
 
33
33
  it "can increment" do
34
- A::Letter::A.incr.should == A::Letter::B
35
- A::Letter::B.incr.should == A::Letter::C
36
- A::Letter::C.incr.should == A::Letter::A
34
+ expect(A::Letter::A.incr).to eq(A::Letter::B)
35
+ expect(A::Letter::B.incr).to eq(A::Letter::C)
36
+ expect(A::Letter::C.incr).to eq(A::Letter::A)
37
37
  end
38
38
 
39
39
  it "can decrement" do
40
- A::Letter::A.decr.should == A::Letter::C
41
- A::Letter::B.decr.should == A::Letter::A
42
- A::Letter::C.decr.should == A::Letter::B
40
+ expect(A::Letter::A.decr).to eq(A::Letter::C)
41
+ expect(A::Letter::B.decr).to eq(A::Letter::A)
42
+ expect(A::Letter::C.decr).to eq(A::Letter::B)
43
43
  end
44
44
 
45
45
  it "can calculate deltas" do
46
- A::Letter::A.delta(A::Letter::A).should == 0
47
- A::Letter::A.delta(A::Letter::B).should == 2
48
- A::Letter::A.delta(A::Letter::C).should == 1
49
- A::Letter::B.delta(A::Letter::A).should == 1
50
- A::Letter::B.delta(A::Letter::B).should == 0
51
- A::Letter::B.delta(A::Letter::C).should == 2
52
- A::Letter::C.delta(A::Letter::A).should == 2
53
- A::Letter::C.delta(A::Letter::B).should == 1
54
- A::Letter::C.delta(A::Letter::C).should == 0
46
+ expect(A::Letter::A.delta(A::Letter::A)).to eq(0)
47
+ expect(A::Letter::A.delta(A::Letter::B)).to eq(2)
48
+ expect(A::Letter::A.delta(A::Letter::C)).to eq(1)
49
+ expect(A::Letter::B.delta(A::Letter::A)).to eq(1)
50
+ expect(A::Letter::B.delta(A::Letter::B)).to eq(0)
51
+ expect(A::Letter::B.delta(A::Letter::C)).to eq(2)
52
+ expect(A::Letter::C.delta(A::Letter::A)).to eq(2)
53
+ expect(A::Letter::C.delta(A::Letter::B)).to eq(1)
54
+ expect(A::Letter::C.delta(A::Letter::C)).to eq(0)
55
55
  end
56
56
  end
@@ -1,17 +1,44 @@
1
- require 'rpiet/direction_pointer'
1
+ require_relative '../lib/rpiet/direction_pointer'
2
2
 
3
3
  describe "DirectionPointer" do
4
4
  it "can rotate" do
5
5
  dp = RPiet::DirectionPointer.new
6
6
 
7
- dp.direction.should == RPiet::Direction::RIGHT
7
+ expect(dp.direction).to eq(RPiet::Direction::RIGHT)
8
8
  dp.rotate!
9
- dp.direction.should == RPiet::Direction::DOWN
9
+ expect(dp.direction).to eq(RPiet::Direction::DOWN)
10
10
  dp.rotate!
11
- dp.direction.should == RPiet::Direction::LEFT
11
+ expect(dp.direction).to eq(RPiet::Direction::LEFT)
12
12
  dp.rotate!
13
- dp.direction.should == RPiet::Direction::UP
13
+ expect(dp.direction).to eq(RPiet::Direction::UP)
14
14
  dp.rotate!(2)
15
- dp.direction.should == RPiet::Direction::DOWN
15
+ expect(dp.direction).to eq(RPiet::Direction::DOWN)
16
+ end
17
+
18
+ it "knows it's ordinal values" do
19
+ dp = RPiet::DirectionPointer.new
20
+ expect(dp.ordinal).to eq(0)
21
+ dp.rotate!
22
+ expect(dp.ordinal).to eq(1)
23
+ dp.rotate!
24
+ expect(dp.ordinal).to eq(2)
25
+ dp.rotate!
26
+ expect(dp.ordinal).to eq(3)
27
+ dp.rotate!
28
+ expect(dp.ordinal).to eq(0)
29
+ end
30
+
31
+ it "can restore from it's ordinal values" do
32
+ dp = RPiet::DirectionPointer.new
33
+ dp.from_ordinal!(0)
34
+ expect(dp.direction).to eq(RPiet::Direction::RIGHT)
35
+ dp.from_ordinal!(1)
36
+ expect(dp.direction).to eq(RPiet::Direction::DOWN)
37
+ dp.from_ordinal!(2)
38
+ expect(dp.direction).to eq(RPiet::Direction::LEFT)
39
+ dp.from_ordinal!(3)
40
+ expect(dp.direction).to eq(RPiet::Direction::UP)
41
+ dp.from_ordinal!(4)
42
+ expect(dp.direction).to eq(RPiet::Direction::RIGHT)
16
43
  end
17
44
  end