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