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.
- data/.gitignore +9 -0
- data/README.md +19 -0
- data/bin/rpiet +30 -0
- data/images/cowsay.gif +0 -0
- data/images/helloworld-piet.gif +0 -0
- data/images/helloworld-pietbig.gif +0 -0
- data/images/hi.gif +0 -0
- data/images/loop2to0.gif +0 -0
- data/images/nfib.png +0 -0
- data/lib/rpiet.rb +2 -0
- data/lib/rpiet/codel_chooser.rb +29 -0
- data/lib/rpiet/color.rb +54 -0
- data/lib/rpiet/cycle.rb +59 -0
- data/lib/rpiet/direction_pointer.rb +39 -0
- data/lib/rpiet/event_handler.rb +66 -0
- data/lib/rpiet/group.rb +107 -0
- data/lib/rpiet/image/ascii_image.rb +55 -0
- data/lib/rpiet/image/image.rb +43 -0
- data/lib/rpiet/image/url_image.rb +28 -0
- data/lib/rpiet/interpreter.rb +112 -0
- data/lib/rpiet/machine.rb +111 -0
- data/lib/rpiet/version.rb +3 -0
- data/spec/cycle_spec.rb +56 -0
- data/spec/direction_pointer_spec.rb +17 -0
- data/spec/group_spec.rb +57 -0
- data/spec/interpreter_spec.rb +14 -0
- data/spec/machine_spec.rb +23 -0
- metadata +80 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -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
|
data/bin/rpiet
ADDED
@@ -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
|
data/images/cowsay.gif
ADDED
Binary file
|
Binary file
|
Binary file
|
data/images/hi.gif
ADDED
Binary file
|
data/images/loop2to0.gif
ADDED
Binary file
|
data/images/nfib.png
ADDED
Binary file
|
data/lib/rpiet.rb
ADDED
@@ -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
|
+
|
data/lib/rpiet/color.rb
ADDED
@@ -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
|
data/lib/rpiet/cycle.rb
ADDED
@@ -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
|
data/lib/rpiet/group.rb
ADDED
@@ -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
|
data/spec/cycle_spec.rb
ADDED
@@ -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
|
data/spec/group_spec.rb
ADDED
@@ -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
|