rpiet 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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