rpiet 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ .DS_Store
2
+ manifest.mf
3
+ *~
4
+ *.orig
5
+ *.rej
6
+ /target/
7
+ dist
8
+ build
9
+ nbproject
@@ -0,0 +1,19 @@
1
+ # RPiet - Ruby implementation of Piet programming language
2
+
3
+ Check it out: http://www.dangermouse.net/esoteric/piet.html
4
+
5
+ This is a pretty naive implementation, but it can run things like fib:
6
+
7
+ [[/tree/master/images/helloworld-pietbig.gif|align=center]]
8
+
9
+ ## Running
10
+
11
+ rpiet --debug images/nfib.png
12
+
13
+ Or an image from the net (notice codel size arg):
14
+
15
+ rpiet -c 8 http://www.retas.de/thomas/computer/programs/useless/secunet_contest/entry_3/s2_8.gif
16
+
17
+ Or for more fun:
18
+
19
+ rpiet http://www.dangermouse.net/esoteric/piet/pietquest.png
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'rpiet'
5
+ require 'rpiet/image/url_image'
6
+
7
+ codel_size = 1
8
+
9
+ log = RPiet::Logger::NoOutput
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: #{__FILE__} -d image_file"
12
+
13
+ opts.on("-d", "--debug style", "turn on debugging") do |style|
14
+ log = case style
15
+ when 'simple'
16
+ RPiet::Logger::SimpleAsciiOutput
17
+ when 'complex'
18
+ RPiet::Logger::ComplexAsciiOutput
19
+ end
20
+ end
21
+ opts.on("-c", "--codel-size w", "size of codel") { |w| codel_size = w.to_i }
22
+ end
23
+
24
+ opts.parse!(ARGV)
25
+ opts.usage("You need to supply a source image filename.") unless ARGV.first
26
+
27
+ filename = ARGV.first
28
+ filename = 'file:' + ARGV.first if File.exist? filename
29
+ image = RPiet::Image::URLImage.new(filename, codel_size)
30
+ RPiet::Interpreter.new(image, log.new).run
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ require 'rpiet/version'
2
+ require 'rpiet/interpreter'
@@ -0,0 +1,29 @@
1
+ require 'rpiet/direction_pointer'
2
+
3
+ module RPiet
4
+ class CodelChooser
5
+ LEFT, RIGHT = -1, 1
6
+ attr_reader :direction
7
+ def initialize; @direction = LEFT; end
8
+ def switch!(amount = 1); @direction *= -1.**(amount % 2); end
9
+
10
+ def ascii(dp)
11
+ case dp.direction
12
+ when RPiet::Direction::RIGHT
13
+ @direction == LEFT ? "^" : "v"
14
+ when RPiet::Direction::UP
15
+ @direction == LEFT ? "<" : ">"
16
+ when RPiet::Direction::LEFT
17
+ @direction == LEFT ? "v" : "^"
18
+ when RPiet::Direction::DOWN
19
+ @direction == LEFT ? ">" : "<"
20
+ end
21
+ end
22
+
23
+ def inspect
24
+ (@direction == LEFT ? "left(0)" : "right(1)")
25
+ end
26
+ alias :to_s :inspect
27
+ end
28
+ end
29
+
@@ -0,0 +1,54 @@
1
+ require 'rpiet/cycle'
2
+
3
+ module RPiet
4
+ class Color
5
+ extend RPiet::CycleMethod
6
+
7
+ cycle :LIGHTNESS, :LIGHT, :NORMAL, :DARK
8
+ cycle :HUE, :RED, :YELLOW, :GREEN, :CYAN, :BLUE, :MAGENTA
9
+
10
+ attr_reader :hue, :lightness
11
+
12
+ def initialize(lightness, hue)
13
+ @lightness, @hue = lightness, hue
14
+ end
15
+
16
+ def to_initial
17
+ @lightness.to_initial + @hue.to_initial
18
+ end
19
+
20
+ def to_s
21
+ (@lightness == LIGHTNESS::NORMAL ? "" : @lightness.to_s + ' ') + @hue.to_s
22
+ end
23
+ alias :inspect :to_s
24
+
25
+ RGB_WHITE, WHITE = '0xffffff', Color.new('white', '')
26
+ RGB_BLACK, BLACK = '0x000000', Color.new('black', '')
27
+
28
+ class << WHITE; def to_initial; '..'; end; end
29
+ class << BLACK; def to_initial; '++'; end; end
30
+
31
+ RGB = {
32
+ '0xffc0c0' => Color.new(LIGHTNESS::LIGHT, HUE::RED),
33
+ '0xffffc0' => Color.new(LIGHTNESS::LIGHT, HUE::YELLOW),
34
+ '0xc0ffc0' => Color.new(LIGHTNESS::LIGHT, HUE::GREEN),
35
+ '0xc0ffff' => Color.new(LIGHTNESS::LIGHT, HUE::CYAN),
36
+ '0xc0c0ff' => Color.new(LIGHTNESS::LIGHT, HUE::BLUE),
37
+ '0xffc0ff' => Color.new(LIGHTNESS::LIGHT, HUE::MAGENTA),
38
+ '0xff0000' => Color.new(LIGHTNESS::NORMAL, HUE::RED),
39
+ '0xffff00' => Color.new(LIGHTNESS::NORMAL, HUE::YELLOW),
40
+ '0x00ff00' => Color.new(LIGHTNESS::NORMAL, HUE::GREEN),
41
+ '0x00ffff' => Color.new(LIGHTNESS::NORMAL, HUE::CYAN),
42
+ '0x0000ff' => Color.new(LIGHTNESS::NORMAL, HUE::BLUE),
43
+ '0xff00ff' => Color.new(LIGHTNESS::NORMAL, HUE::MAGENTA),
44
+ '0xc00000' => Color.new(LIGHTNESS::DARK, HUE::RED),
45
+ '0xc0c000' => Color.new(LIGHTNESS::DARK, HUE::YELLOW),
46
+ '0x00c000' => Color.new(LIGHTNESS::DARK, HUE::GREEN),
47
+ '0x00c0c0' => Color.new(LIGHTNESS::DARK, HUE::CYAN),
48
+ '0x0000c0' => Color.new(LIGHTNESS::DARK, HUE::BLUE),
49
+ '0xc000c0' => Color.new(LIGHTNESS::DARK, HUE::MAGENTA),
50
+ RGB_WHITE => WHITE,
51
+ RGB_BLACK => BLACK
52
+ }
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ module RPiet
2
+ class Cycle
3
+ attr_reader :name, :value
4
+
5
+ def initialize(value, name, list)
6
+ @value, @name, @list = value, name.to_s.downcase, list
7
+ end
8
+
9
+ def delta(other)
10
+ (@value - other.value) % @list.length
11
+ end
12
+
13
+ def -(other)
14
+ @list[(@value - other.value) % @list.length]
15
+ end
16
+
17
+ def +(other)
18
+ @list[(@value + other.value) % @list.length]
19
+ end
20
+
21
+ def incr(amount = 1)
22
+ @list[(@value + amount) % @list.length]
23
+ end
24
+
25
+ def decr(amount = 1)
26
+ @list[(@value - amount) % @list.length]
27
+ end
28
+
29
+ def to_initial
30
+ @name[0]
31
+ end
32
+
33
+ def to_s
34
+ "#{@name}(#{@value})"
35
+ end
36
+ alias :inspect :to_s
37
+ end
38
+
39
+ module CycleMethod
40
+ ##
41
+ # Define a constant in self and then populate a series of constants
42
+ # within that with cycled values from 0-n.
43
+ def cycle(const_name, *names)
44
+
45
+ list = []
46
+ if const_name.is_a? Symbol
47
+ holder_module = Module.new
48
+ const_set const_name, holder_module
49
+ else
50
+ holder_module = const_name
51
+ end
52
+ names.each_with_index do |name, i|
53
+ list[i] = Cycle.new(i, name, list)
54
+ holder_module.const_set name, list[i]
55
+ end
56
+ end
57
+ module_function :cycle
58
+ end
59
+ end
@@ -0,0 +1,39 @@
1
+ require 'rpiet/cycle'
2
+ require 'rpiet/codel_chooser'
3
+
4
+ module RPiet
5
+ extend RPiet::CycleMethod
6
+ cycle :Direction, :RIGHT, :DOWN, :LEFT, :UP
7
+
8
+ class << Direction::RIGHT; def deltas; [1, 0]; end; end
9
+ class << Direction::DOWN; def deltas; [0, 1]; end; end
10
+ class << Direction::LEFT; def deltas; [-1, 0]; end; end
11
+ class << Direction::UP; def deltas; [0, -1]; end; end
12
+
13
+ class DirectionPointer
14
+ attr_reader :direction
15
+
16
+ def initialize
17
+ @direction = RPiet::Direction::RIGHT
18
+ end
19
+
20
+ def rotate!(amount = 1)
21
+ @direction = @direction.incr amount
22
+ end
23
+
24
+ def next_possible(x, y)
25
+ dx, dy = @direction.deltas
26
+ [x + dx, y + dy]
27
+ end
28
+
29
+ ASCII_ARROWS = ['>', 'v', '<', '^']
30
+ def ascii
31
+ ASCII_ARROWS[@direction.value]
32
+ end
33
+
34
+ def inspect
35
+ @direction.inspect
36
+ end
37
+ alias :to_s :inspect
38
+ end
39
+ end
@@ -0,0 +1,66 @@
1
+ module RPiet
2
+ module EventHandler
3
+ def dmesg(message)
4
+ $stderr.puts message
5
+ end
6
+
7
+ def initialized(runtime)
8
+ end
9
+
10
+ def step_begin(runtime)
11
+ end
12
+
13
+ def trying_again(runtime, ex, ey)
14
+ end
15
+
16
+ def seen_white(runtime)
17
+ end
18
+
19
+ def execution_completed(runtime)
20
+ end
21
+
22
+ def operation(runtime, operation)
23
+ end
24
+ end
25
+
26
+ module Logger
27
+ class NoOutput
28
+ include RPiet::EventHandler
29
+ end
30
+
31
+ class SimpleAsciiOutput
32
+ include RPiet::EventHandler
33
+
34
+ def initialized(runtime)
35
+ dmesg "codel_size: #{runtime.source.codel_size}"
36
+ end
37
+
38
+ def step_begin(runtime)
39
+ dmesg "step \##{runtime.step} -- #{runtime.pvm}"
40
+ end
41
+
42
+ def trying_again(runtime, ex, ey)
43
+ dmesg "Trying again at #{ex}, #{ey}. #{runtime.pvm}"
44
+ end
45
+
46
+ def seen_white(runtime)
47
+ dmesg "Entering white; sliding thru"
48
+ end
49
+
50
+ def execution_completed(runtime)
51
+ dmesg "Execution trapped, program terminates"
52
+ end
53
+
54
+ def operation(runtime, operation)
55
+ dmesg "exec: #{operation} -- #{runtime.pvm}"
56
+ end
57
+ end
58
+
59
+ class ComplexAsciiOutput < SimpleAsciiOutput
60
+ def step_begin(runtime)
61
+ super
62
+ dmesg runtime.source.ascii(runtime.groups[runtime.x][runtime.y])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,107 @@
1
+ require 'rpiet/direction_pointer'
2
+ require 'rpiet/codel_chooser'
3
+
4
+ module RPiet
5
+ class Group
6
+ include RPiet::Direction
7
+ attr_reader :rgb, :points
8
+
9
+ def initialize(rgb, point)
10
+ @rgb, @points = rgb, []
11
+ @max = { RIGHT => [], LEFT => [], UP => [], DOWN => [] }
12
+
13
+ self << point
14
+ end
15
+
16
+ def point_for(pvm)
17
+ case pvm.dp.direction
18
+ when RIGHT then
19
+ pvm.cc.direction == RPiet::CodelChooser::LEFT ? @rl : @rr
20
+ when LEFT then
21
+ pvm.cc.direction == RPiet::CodelChooser::LEFT ? @ll : @lr
22
+ when DOWN then
23
+ pvm.cc.direction == RPiet::CodelChooser::LEFT ? @dl : @dr
24
+ when UP then
25
+ pvm.cc.direction == RPiet::CodelChooser::LEFT ? @ul : @ur
26
+ end
27
+ end
28
+
29
+ def <<(point)
30
+ update_up point
31
+ update_down point
32
+ update_left point
33
+ update_right point
34
+
35
+ @points << point
36
+ end
37
+
38
+ def include?(point)
39
+ @points.include? point
40
+ end
41
+
42
+ def finish
43
+ @rl, @rr = ends(@max[RIGHT].sort { |a, b| a[1] <=> b[1] })
44
+ @lr, @ll = ends(@max[LEFT].sort { |a, b| a[1] <=> b[1] })
45
+ @ul, @ur = ends(@max[UP].sort { |a, b| a[0] <=> b[0] })
46
+ @dr, @dl = ends(@max[DOWN].sort { |a, b| a[0] <=> b[0] })
47
+ end
48
+
49
+ def ends(list)
50
+ [list[0], list[-1]]
51
+ end
52
+ private :ends
53
+
54
+ def update_up(point)
55
+ arr = @max[UP]
56
+ if arr.empty? || point[1] < arr[0][1]
57
+ @max[UP] = [point]
58
+ elsif point[1] == arr[0][1]
59
+ arr << point
60
+ end
61
+ end
62
+
63
+ def update_down(point)
64
+ arr = @max[DOWN]
65
+ if arr.empty? || point[1] > arr[0][1]
66
+ @max[DOWN] = [point]
67
+ elsif point[1] == arr[0][1]
68
+ arr << point
69
+ end
70
+ end
71
+
72
+ def update_left(point)
73
+ arr = @max[LEFT]
74
+ if arr.empty? || point[0] < arr[0][0]
75
+ @max[LEFT] = [point]
76
+ elsif point[0] == arr[0][0]
77
+ arr << point
78
+ end
79
+ end
80
+
81
+ def update_right(point)
82
+ arr = @max[RIGHT]
83
+ if arr.empty? || point[0] > arr[0][0]
84
+ @max[RIGHT] = [point]
85
+ elsif point[0] == arr[0][0]
86
+ arr << point
87
+ end
88
+ end
89
+
90
+ def merge(groups, group)
91
+ group.points.each do |point|
92
+ self << point
93
+ groups[point[0]][point[1]] = self
94
+ end
95
+ end
96
+
97
+ def size
98
+ @points.size
99
+ end
100
+
101
+ def inspect
102
+ "Group #{rgb}: Size: #{size} Points: #{@points.inspect}\n" +
103
+ "rr #@rr rl #@rl lr #@lr ll #@ll ur #@ur ul #@ul dr #@dr dl #@dl"
104
+ end
105
+ alias :to_s :inspect
106
+ end
107
+ end
@@ -0,0 +1,55 @@
1
+ require 'rpiet/image/image'
2
+
3
+ module RPiet
4
+ module Image
5
+ class AsciiImage < RPiet::Image::Image
6
+ def initialize(string, codel_size=1)
7
+ @codel_size = codel_size
8
+ lines = string.split("\n")
9
+ @data = []
10
+ lines.each do |line|
11
+ @data << line.split(/\s+/).map { |e| str_to_rgb(e) }
12
+ end
13
+ end
14
+
15
+ def raw_pixel(x, y)
16
+ @data[x][y]
17
+ end
18
+
19
+ def raw_height
20
+ @data.length
21
+ end
22
+
23
+ def raw_width
24
+ @data[0].length
25
+ end
26
+
27
+ STR2RGB = {
28
+ 'lr' => [0xff, 0xc0, 0xc0],
29
+ 'ly' => [0xff, 0xff, 0xc0],
30
+ 'lg' => [0xc0, 0xff, 0xc0],
31
+ 'lc' => [0xc0, 0xff, 0xff],
32
+ 'lb' => [0xc0, 0xc0, 0xff],
33
+ 'lm' => [0xff, 0xc0, 0xff],
34
+ 'nr' => [0xff, 0x00, 0x00],
35
+ 'ny' => [0xff, 0xff, 0x00],
36
+ 'ng' => [0xc0, 0xff, 0x00],
37
+ 'nc' => [0x00, 0xff, 0xff],
38
+ 'nb' => [0x00, 0x00, 0xff],
39
+ 'nm' => [0xff, 0x00, 0xff],
40
+ 'dr' => [0xc0, 0x00, 0x00],
41
+ 'dy' => [0xc0, 0xc0, 0x00],
42
+ 'dg' => [0x00, 0xc0, 0x00],
43
+ 'dc' => [0x00, 0xc0, 0xc0],
44
+ 'db' => [0x00, 0x00, 0xc0],
45
+ 'dm' => [0xc0, 0x00, 0xc0],
46
+ '..' => [0xff, 0xff, 0xff],
47
+ '++' => [0x00, 0x00, 0x00]
48
+ }
49
+
50
+ def str_to_rgb(str)
51
+ STR2RGB[str]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ require 'rpiet/color'
2
+
3
+ module RPiet
4
+ module Image
5
+ ##
6
+ # Abstract base class for all image providers. Image providers only need to
7
+ # implement raw_pixel, raw_width, and raw_height.
8
+ #
9
+ class Image
10
+ attr_reader :codel_size
11
+
12
+ def initialize(codel_size)
13
+ @codel_size = codel_size
14
+ end
15
+
16
+ def pixel(x, y)
17
+ r,g,b = raw_pixel(x * @codel_size, y * @codel_size)
18
+ color_for(format "0x%02x%02x%02x" % [r,g,b])
19
+ end
20
+
21
+ def color_for(rgb_hex)
22
+ RPiet::Color::RGB[rgb_hex]
23
+ end
24
+
25
+ def ascii(group = [])
26
+ s = ''
27
+ w, h = size
28
+ h.times do |j|
29
+ w.times do |i|
30
+ value = pixel(i, j).to_initial
31
+ s << (group.include?([i, j]) ? value.upcase : value) << ' '
32
+ end
33
+ s << "\n"
34
+ end
35
+ s
36
+ end
37
+
38
+ def size
39
+ [raw_width/@codel_size, raw_height/@codel_size]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ require 'java'
2
+ require 'rpiet/image/image'
3
+
4
+ # Raw in Java is BufferendImage. In MRI it just needs to define:
5
+ # width, height, getRGB(x, y)
6
+ module RPiet
7
+ module Image
8
+ class URLImage < RPiet::Image::Image
9
+ def initialize(file, codel_size=1)
10
+ super(codel_size)
11
+ @raw = javax.imageio.ImageIO.read(java.net.URL.new(file))
12
+ end
13
+
14
+ def raw_pixel(x, y)
15
+ rgb = @raw.getRGB(x, y)
16
+ [(rgb >> 16 ) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff]
17
+ end
18
+
19
+ def raw_width
20
+ @raw.width
21
+ end
22
+
23
+ def raw_height
24
+ @raw.height
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,112 @@
1
+ require 'rpiet/color'
2
+ require 'rpiet/machine'
3
+ require 'rpiet/group'
4
+ require 'rpiet/event_handler'
5
+
6
+ module RPiet
7
+ class Interpreter
8
+ attr_reader :pvm, :source, :groups, :x, :y, :step
9
+
10
+ def initialize(source, event_handler=RPiet::Logger::NoOutput.new)
11
+ @x, @y, @pvm, @step = 0, 0, RPiet::Machine.new, 1
12
+ @source, @event_handler = source, event_handler
13
+ @rows, @cols = @source.size
14
+ @pixels = alloc_matrix { |i, j| @source.pixel(i, j)}
15
+ @groups = calculate_groups(alloc_matrix { |i, j| 0 })
16
+ @event_handler.initialized(self)
17
+ end
18
+
19
+ def run
20
+ while(next_step) do
21
+ end
22
+ end
23
+
24
+ ##
25
+ # Is this point on the image and not black?
26
+ def valid?(x, y)
27
+ x >= 0 && x < @rows && y >= 0 && y < @cols &&
28
+ @pixels[x][y] != RPiet::Color::BLACK
29
+ end
30
+
31
+ def next_step
32
+ @pvm.block_value = @groups[@x][@y].size
33
+ i = 0
34
+ seen_white = false
35
+ @event_handler.step_begin(self)
36
+ ex, ey = @groups[@x][@y].point_for(@pvm)
37
+ while i < 8 do
38
+ nx, ny = @pvm.next_possible(ex, ey)
39
+
40
+ if !valid?(nx, ny)
41
+ i += 1
42
+ @pvm.orient_elsewhere(i)
43
+
44
+ ex, ey = @groups[@x][@y].point_for(@pvm) if !seen_white
45
+ @event_handler.trying_again(self, ex, ey)
46
+ next
47
+ elsif @pixels[nx][ny] == RPiet::Color::WHITE
48
+ if !seen_white
49
+ seen_white = true
50
+ i = 0
51
+ @event_handler.seen_white(self)
52
+ end
53
+ ex, ey = nx, ny
54
+ else
55
+ if !seen_white
56
+ operation = @pvm.execute(@pixels[nx][ny], @pixels[@x][@y])
57
+ else
58
+ operation = 'noop'
59
+ end
60
+ @event_handler.operation(self, operation)
61
+ @x, @y = nx, ny
62
+ @step += 1
63
+ return true
64
+ end
65
+ end
66
+ @event_handler.execution_completed(self)
67
+ false
68
+ end
69
+
70
+ # always look up, left, or make new group
71
+ def calculate_groups(groups)
72
+ all_groups = []
73
+ walk_matrix(groups) do |i, j|
74
+ rgb = @pixels[i][j]
75
+ up = j-1 >= 0 ? groups[i][j-1] : nil
76
+ left = i-1 >= 0 ? groups[i-1][j] : nil
77
+ if up && up.rgb == rgb
78
+ up << [i, j]
79
+ groups[i][j] = up
80
+ # disjoint groups to merge
81
+ up.merge(groups, left) if left && left != up && left.rgb == rgb
82
+ end
83
+
84
+ if groups[i][j] == 0 && left && left.rgb == rgb
85
+ left << [i, j]
86
+ groups[i][j] = left
87
+ end
88
+
89
+ if groups[i][j] == 0
90
+ groups[i][j] = RPiet::Group.new(rgb, [i, j])
91
+ all_groups << groups[i][j]
92
+ end
93
+ end
94
+ all_groups.each { |group| group.finish }
95
+ groups
96
+ end
97
+
98
+ def alloc_matrix
99
+ Array.new(@rows) { Array.new(@cols) {nil} }.tap do |matrix|
100
+ walk_matrix(matrix) { |i, j| matrix[i][j] = yield i, j }
101
+ end
102
+ end
103
+
104
+ def walk_matrix(matrix)
105
+ 0.upto(@rows-1) do |i|
106
+ 0.upto(@cols-1) do |j|
107
+ yield i, j
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,111 @@
1
+ require 'rpiet/direction_pointer'
2
+ require 'rpiet/codel_chooser'
3
+
4
+ module RPiet
5
+ ##
6
+ # This is a simple piet runtime to be controled by interp.
7
+ #
8
+ # dp - Direction Pointer (right, down, left, up)
9
+ # cc - Codel Chooser (left, right)
10
+ class Machine
11
+ attr_reader :dp, :cc, :stack
12
+ attr_accessor :block_value
13
+
14
+ def initialize
15
+ @stack, @dp, @cc = [], DirectionPointer.new, CodelChooser.new
16
+ @block_value = 1
17
+ end
18
+
19
+ ##
20
+ # Return the next possible location based on direction pointers facing
21
+ def next_possible(x, y)
22
+ @dp.next_possible(x, y)
23
+ end
24
+
25
+ ##
26
+ # Change either codel chooser or direction pointer to try and look
27
+ # at a different codel.
28
+ def orient_elsewhere(i)
29
+ if i.even?
30
+ dp.rotate!
31
+ else
32
+ cc.switch!
33
+ end
34
+ end
35
+
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
55
+ def roll
56
+ bin_op do |depth, num|
57
+ num %= depth
58
+ return if depth <= 0 || num == 0
59
+ if num > 0
60
+ @stack[-depth..-1] = @stack[-num..-1] + @stack[-depth...-num]
61
+ elsif num < 0
62
+ @stack[-depth..-1] = @stack[-depth...-num] + @stack[-num..-1]
63
+ end
64
+ end
65
+ end
66
+
67
+ def inspect
68
+ "dp: #{@dp.ascii}, cc: #{@cc.ascii(@dp)}, bv: #{@block_value}, st: #{@stack}"
69
+ end
70
+ alias :to_s :inspect
71
+
72
+ ##
73
+ # Execute the operation represented by the two colors
74
+ def execute(color1, color2)
75
+ dh = color1.hue.delta color2.hue
76
+ dd = color1.lightness.delta color2.lightness
77
+ operation = OPERATION[dh][dd]
78
+ __send__(operation)
79
+ end
80
+
81
+ private
82
+ def bin_op(&block)
83
+ return unless @stack.length >= 2
84
+ block.call *@stack.pop(2)
85
+ end
86
+
87
+ def unary_op(&block)
88
+ return unless @stack.length >= 1
89
+ block.call(@stack.pop)
90
+ end
91
+
92
+ def math_op(op); bin_op { |a, b| @stack << a.send(op, b) }; end
93
+
94
+ ##
95
+ # Lightness change
96
+ #Hue change None 1 Darker 2 Darker
97
+ # None push pop
98
+ # 1 Step add subtract multiply
99
+ # 2 Steps divide mod not
100
+ # 3 Steps greater pointer switch
101
+ # 4 Steps duplicate roll in(number)
102
+ # 5 Steps in(char) out(number) out(char)
103
+ #
104
+ OPERATION = [[:noop, :push, :pop],
105
+ [:add, :sub, :mult],
106
+ [:div, :mod, :not],
107
+ [:gtr, :pntr, :swch],
108
+ [:dup, :roll, :n_in],
109
+ [:c_in, :nout, :cout]]
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module RPiet
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,56 @@
1
+ require 'rpiet/cycle'
2
+
3
+ module A
4
+ extend RPiet::CycleMethod
5
+ cycle :Letter, :A, :B, :C
6
+ end
7
+
8
+ describe "RPiet::Cycle" do
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
19
+ end
20
+
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
31
+ end
32
+
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
37
+ end
38
+
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
43
+ end
44
+
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
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ require 'rpiet/direction_pointer'
2
+
3
+ describe "DirectionPointer" do
4
+ it "can rotate" do
5
+ dp = RPiet::DirectionPointer.new
6
+
7
+ dp.direction.should == RPiet::Direction::RIGHT
8
+ dp.rotate!
9
+ dp.direction.should == RPiet::Direction::DOWN
10
+ dp.rotate!
11
+ dp.direction.should == RPiet::Direction::LEFT
12
+ dp.rotate!
13
+ dp.direction.should == RPiet::Direction::UP
14
+ dp.rotate!(2)
15
+ dp.direction.should == RPiet::Direction::DOWN
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ require 'rpiet/group'
2
+ require 'rpiet/machine'
3
+
4
+ describe "Group" do
5
+ before do
6
+ @group = RPiet::Group.new('0x0000c0', [0, 3])
7
+ [[0, 4], [0, 5], [0, 6], [0, 7], [1, 3], [1, 4], [1, 5],
8
+ [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 3], [3, 4], [3, 5], [3, 6],
9
+ [4, 3], [4, 4], [4, 5], [4, 6], [5, 4], [6, 4], [6, 5], [6, 6], [7, 4],
10
+ [7, 5], [7, 6], [8, 5], [8, 6]].each do |point|
11
+ @group << point
12
+ end
13
+ @group.finish
14
+
15
+ @group2 = RPiet::Group.new('0xff0000', [7, 0])
16
+
17
+ [[7, 1], [6, 1], [6, 2], [5, 2], [6, 3], [7, 2],
18
+ [7, 3], [8, 0], [8, 1]].each do |point|
19
+ @group2 << point
20
+ end
21
+ @group2.finish
22
+
23
+ @pvm = RPiet::Machine.new
24
+ end
25
+
26
+ it "knows its size" do
27
+ @group.size.should == 30
28
+ end
29
+
30
+ it "can pick the right points" do
31
+ @group.point_for(@pvm).should == [8, 5] # dp: RIGHT cc: LEFT -> UR
32
+ @pvm.cc.switch! # LEFT -> RIGHT
33
+ @group.point_for(@pvm).should == [8, 6] # dp: RIGHT cc: RIGHT -> LR
34
+ @pvm.dp.rotate! # dp: RIGHT -> DOWN
35
+ @pvm.cc.switch! # cc: RIGHT -> LEFT
36
+ @group.point_for(@pvm).should == [0, 7] # dp: DOWN cc: LEFT -> LR
37
+ @pvm.cc.switch! # cc: LEFT -> RIGHT
38
+ @group.point_for(@pvm).should == [0, 7] # dp: DOWN cc: RIGHT -> LL
39
+ @pvm.dp.rotate! # dp: DOWN -> LEFT
40
+ @pvm.cc.switch! # cc: RIGHT -> LEFT
41
+ @group.point_for(@pvm).should == [0, 7] # dp: LEFT cc: LEFT -> LL
42
+ @pvm.cc.switch! # cc: LEFT -> RIGHT
43
+ @group.point_for(@pvm).should == [0, 3] # dp: LEFT cc: RIGHT -> UL
44
+ @pvm.dp.rotate! # dp: LEFT -> UP
45
+ @pvm.cc.switch!
46
+ @group.point_for(@pvm).should == [0, 3] # dp: UP cc: LEFT -> UL
47
+ @pvm.cc.switch! # cc: LEFT -> RIGHT
48
+ @group.point_for(@pvm).should == [4, 3] # dp: UP cc: RIGHT -> UR
49
+
50
+ # Since last group only has single wide bottom let's try another
51
+ @pvm.cc.switch! # cc: RIGHT -> LEFT
52
+ @pvm.dp.rotate! 2 # dp: UP -> DOWN
53
+ @group2.point_for(@pvm).should == [7, 3] # dp: DOWN cc: LEFT -> LR
54
+ @pvm.cc.switch! # cc: RIGHT -> LEFT
55
+ @group2.point_for(@pvm).should == [6, 3] # dp: DOWN cc: RIGHT -> LL
56
+ end
57
+ end
@@ -0,0 +1,14 @@
1
+ require 'rpiet/interpreter'
2
+ require 'rpiet/image/ascii_image'
3
+
4
+ describe "RPiet::Interpreter" do
5
+ it "Can push" do
6
+ source = RPiet::Image::AsciiImage.new "nb db ++\nnb ++ ++\n++ ++ ++\n"
7
+ interpreter = RPiet::Interpreter.new(source)
8
+
9
+ interpreter.next_step
10
+ interpreter.pvm.stack.should == [2]
11
+ interpreter.next_step
12
+ interpreter.pvm.stack.should == []
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'rpiet/machine'
2
+
3
+ describe "RPiet::Machine" do
4
+ it "can roll" do
5
+ machine = RPiet::Machine.new
6
+
7
+ [115, 100, 101, 2, 1].each do |value|
8
+ machine.block_value = value
9
+ machine.push
10
+ end
11
+
12
+ machine.roll
13
+ machine.stack.should == [115, 101, 100]
14
+
15
+ [3, 1].each do |value|
16
+ machine.block_value = value
17
+ machine.push
18
+ end
19
+
20
+ machine.roll
21
+ machine.stack.should == [100, 115, 101]
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rpiet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: '0.1'
6
+ platform: ruby
7
+ authors:
8
+ - Thomas E. Enebo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-24 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: An implementation of the esoteric programming language Piet
15
+ email: tom.enebo@gmail.com
16
+ executables:
17
+ - rpiet
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - README.md
23
+ - bin/rpiet
24
+ - images/.DS_Store
25
+ - images/cowsay.gif
26
+ - images/helloworld-piet.gif
27
+ - images/helloworld-pietbig.gif
28
+ - images/hi.gif
29
+ - images/loop2to0.gif
30
+ - images/nfib.png
31
+ - lib/rpiet.rb
32
+ - lib/rpiet/codel_chooser.rb
33
+ - lib/rpiet/color.rb
34
+ - lib/rpiet/cycle.rb
35
+ - lib/rpiet/direction_pointer.rb
36
+ - lib/rpiet/event_handler.rb
37
+ - lib/rpiet/group.rb
38
+ - lib/rpiet/image/ascii_image.rb
39
+ - lib/rpiet/image/image.rb
40
+ - lib/rpiet/image/url_image.rb
41
+ - lib/rpiet/interpreter.rb
42
+ - lib/rpiet/machine.rb
43
+ - lib/rpiet/version.rb
44
+ - spec/cycle_spec.rb
45
+ - spec/direction_pointer_spec.rb
46
+ - spec/group_spec.rb
47
+ - spec/interpreter_spec.rb
48
+ - spec/machine_spec.rb
49
+ homepage: http://github.com/enebo/rpiet
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: !binary |-
60
+ MA==
61
+ none: false
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: !binary |-
67
+ MA==
68
+ none: false
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.24
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: A Piet runtime
75
+ test_files:
76
+ - spec/cycle_spec.rb
77
+ - spec/direction_pointer_spec.rb
78
+ - spec/group_spec.rb
79
+ - spec/interpreter_spec.rb
80
+ - spec/machine_spec.rb