git @ Cat's Eye Technologies Nhohnhehr / rel_1_0_2021_0624
Merge pull request #2 from catseye/support-python-3 Develop 1.0 revision 2021.0624 Chris Pressey authored 2 years ago GitHub committed 2 years ago
7 changed file(s) with 508 addition(s) and 370 deletion(s). Raw diff Collapse all Expand all
+0
-2
.hgtags less more
0 c70c2046cb929b3775c0457dfb8c3109631390ff rel_1_0_2011_0510
1 49142375b8a2de1b65fbc3094b87009a4c46c45f rel_1_0_2014_0819
+0
-207
README.markdown less more
0 The Nhohnhehr Programming Language
1 ==================================
2
3 Nhohnhehr is a remotely fungeoid esoteric programming language designed
4 by Chris Pressey between December 4 and December 8, 2010.
5
6 Overview
7 --------
8
9 A Nhohnhehr program consists of a single object called a *room*, which
10 is a 2-dimensional, square grid of cells of finite, and usually small,
11 extent. To emphasize its bounds, the single room in a Nhohnhehr program
12 text must be delimited with an ASCII box, in the manner of the
13 following:
14
15 +----+
16 | |
17 | |
18 | |
19 | |
20 +----+
21
22 Arbitrary text, including comments, may occur outside this bounding box;
23 it will not be considered part of the Nhohnhehr program.
24
25 Once defined, the contents of a room are immutable. Although only a
26 single room may appear in a program text, new rooms may be created
27 dynamically at runtime and adjoined to the edges of existing rooms (see
28 below for details on how this works.)
29
30 Execution of Instructions
31 -------------------------
32
33 In a running Nhohnhehr program there is an instruction pointer. At any
34 given time it has a definite position inside one of the rooms of the
35 program, and is traveling in one of the four cardinal directions. It is
36 also associated with a five-state variable called the *edge mode*. As
37 the instruction pointer passes over non-blank cells, it executes them,
38 heeding the following meanings:
39
40 / causes the pointer to travel north if it was traveling east,
41 south if travelling west.
42 \ causes the pointer to travel north if it was traveling west,
43 south if travelling east.
44 = sets wrap edge mode.
45 & sets copy-room-verbatim edge mode.
46 } sets copy-room-rotate-cw-90 edge mode.
47 { sets copy-room-rotate-ccw-90 edge mode.
48 ! sets copy-room-rotate-180 edge mode.
49 # causes the instruction pointer to skip over the next cell
50 (like # in Befunge-93.)
51 ? inputs a bit. If it is 0, rotate direction of travel 90 degrees
52 counterclockwise; if it is 1, rotate direction of travel 90 degress
53 clockwise; if no more input is available, the direction of travel
54 does not change.
55 0 outputs a 0 bit.
56 1 outputs a 1 bit.
57 @ halts the program.
58 $ only indicates where initial instruction pointer is located;
59 otherwise it has no effect. The initial direction of travel is east.
60
61 Blank cells are NOPs.
62
63 Edge Crossing
64 -------------
65
66 If the instruction pointer reaches an edge of the room and tries to
67 cross it, what happens depends on the current edge mode:
68
69 - In wrap edge mode (this is the initial edge mode), the pointer wraps
70 to the corresponding other edge of the room, as if the room were
71 mapped onto a torus.
72 - In all other modes, if there already exists a room adjoining the
73 current room on that edge, the instruction pointer leaves the
74 current room and enters the adjoining room in the corresponding
75 position. However, if no such adjoining room exists yet, one will be
76 created by making a copy of the current room, transforming it
77 somehow, and adjoining it. The instruction pointer then enters the
78 new room, just as if it had already existed. The details of the
79 transformation depend on the edge mode:
80 - In copy-room-verbatim edge mode, no translation is done.
81 - In copy-room-rotate-cw-90 edge mode, the copy of the current
82 room is rotated clockwise 90 degrees before being adjoined.
83 - In copy-room-rotate-ccw-90 edge mode, the copy of the current
84 room is rotated counterclockwise 90 degrees before being
85 adjoined.
86 - In copy-room-rotate-180 edge mode, the copy of the current room
87 is rotated 180 degrees before being adjoined.
88
89 Examples
90 --------
91
92 The following example reads in a sequence of bits and creates a series
93 of rooms, where 1 bits correspond to unrotated rooms and 0 bits
94 correspond to rooms rotated 90 degrees clockwise (though not precisely
95 one-to-one).
96
97 +------+
98 | /}|
99 |&#/$?@|
100 | / \&|
101 | |
102 | { |
103 |\\ |
104 +------+
105
106 After reading a 0 bit and leaving the right edge, the room is copied,
107 rotated 90 degrees clockwise, and adjoined, so that the rooms of the
108 program are:
109
110 +------+------+
111 | /}|\ & |
112 |&#/$?@|\{ # |
113 | / \&| // |
114 | | $ |
115 | { | \?/|
116 |\\ | &@}|
117 +------+------+
118
119 After leaving the right edge again, the current room is copied, this
120 time rotated 90 degrees counterclockwise, and adjoined, and we get:
121
122 +------+------+------+
123 | /}|\ & | /}|
124 |&#/$?@|\{ # |&#/$?@|
125 | / \&| // | / \&|
126 | | $ | |
127 | { | \?/| { |
128 |\\ | &@}|\\ |
129 +------+------+------+
130
131 Say we were to now read in a 1 bit; we would thus have:
132
133 +------+------+------+------+
134 | /}|\ & | /}| /}|
135 |&#/$?@|\{ # |&#/$?@|&#/$?@|
136 | / \&| // | / \&| / \&|
137 | | $ | | |
138 | { | \?/| { | { |
139 |\\ | &@}|\\ |\\ |
140 +------+------+------+------+
141
142 It should be fairly clear at this point that this program will read all
143 input bits, creating rooms thusly, terminating when there are no more
144 input bits.
145
146 We can write a program that is a variation of the above which, when it
147 encounters the end of input, writes out the bits in the reverse order
148 they were read in, with the following changes:
149
150 * for every `1` in the input, a `1` comes out
151 * for every `0` in the input, `10` comes out
152 * there's an extra `1` at the end of the output
153
154 Here is the program:
155
156 +------------+
157 | /} |
158 |&#/$? \ |
159 | / \& |
160 | |
161 | |
162 | 0 |
163 | ! |
164 | |
165 | |
166 | {1 /# |
167 | { |
168 |\\@ |
169 +------------+
170
171 Computational Class
172 -------------------
173
174 The last example in the previous section was written to demonstrate that
175 Nhohnhehr is at least as powerful as a push-down automaton.
176
177 The author suspects Nhohnhehr to be more powerful still; at least a
178 linear bounded automaton, but possibly even Turing-complete. A strategy
179 for simulating a Turing machine could be developed from the above
180 examples: create new rooms to represent new tape cells, with each
181 possible orientation of the room representing a different tape symbol.
182 The finite control is encoded and embedded in the possible pathways that
183 the instruction pointer can traverse inside each room. Because rooms
184 cannot be changed once created, one might have to resort to creative
185 measures to "change" a tape cell; for instance, each tape cell might
186 have a "stack" of rooms, with a new room appended to the stack each time
187 the cell is to be "changed".
188
189 Source
190 ------
191
192 This document was adapted from [the esolangs.org wiki page for
193 Nhohnhehr](http://www.esolangs.org/wiki/Nhohnhehr), which, like all
194 esowiki articles, has been placed under public domain dedication.
195
196 Implementation
197 --------------
198
199 The Nhohnhehr distribution contains a Nhohnhehr interpreter, written
200 in Python, based on [this implementation of
201 Nhohnhehr](http://esolangs.org/wiki/User:Marinus/Nhohnhehr_interpreter)
202 by [Marinus](http://www.esolangs.org/wiki/User:Marinus). It
203 is effectively the reference interpreter, since it seems to correctly
204 implement the language described here, and there are, to the best of
205 my knowledge, no other implementations of Nhohnhehr in existence.
206 Like all content from the esowiki, it too is in the public domain.
0 The Nhohnhehr Programming Language
1 ==================================
2
3 Version 1.0
4 | _Wiki entry_ [@ esolangs.org](https://esolangs.org/wiki/Nhohnhehr)
5 | _See also:_ [Flobnar](https://github.com/catseye/Flobnar#readme)
6 ∘ [Wunnel](https://github.com/catseye/Wunnel#readme)
7 ∘ [Gemooy](https://github.com/catseye/Gemooy#readme)
8 ∘ [Jolverine](https://github.com/catseye/Jolverine#readme)
9
10 - - - -
11
12 Nhohnhehr is a remotely fungeoid esoteric programming language designed
13 by Chris Pressey between December 4 and December 8, 2010.
14
15 Overview
16 --------
17
18 A Nhohnhehr program consists of a single object called a *room*, which
19 is a 2-dimensional, square grid of cells of finite, and usually small,
20 extent. To emphasize its bounds, the single room in a Nhohnhehr program
21 text must be delimited with an ASCII box, in the manner of the
22 following:
23
24 +----+
25 | |
26 | |
27 | |
28 | |
29 +----+
30
31 Arbitrary text, including comments, may occur outside this bounding box;
32 it will not be considered part of the Nhohnhehr program.
33
34 Once defined, the contents of a room are immutable. Although only a
35 single room may appear in a program text, new rooms may be created
36 dynamically at runtime and adjoined to the edges of existing rooms (see
37 below for details on how this works.)
38
39 Execution of Instructions
40 -------------------------
41
42 In a running Nhohnhehr program there is an instruction pointer. At any
43 given time it has a definite position inside one of the rooms of the
44 program, and is traveling in one of the four cardinal directions. It is
45 also associated with a five-state variable called the *edge mode*. As
46 the instruction pointer passes over non-blank cells, it executes them,
47 heeding the following meanings:
48
49 / causes the pointer to travel north if it was traveling east,
50 south if travelling west.
51 \ causes the pointer to travel north if it was traveling west,
52 south if travelling east.
53 = sets wrap edge mode.
54 & sets copy-room-verbatim edge mode.
55 } sets copy-room-rotate-cw-90 edge mode.
56 { sets copy-room-rotate-ccw-90 edge mode.
57 ! sets copy-room-rotate-180 edge mode.
58 # causes the instruction pointer to skip over the next cell
59 (like # in Befunge-93.)
60 ? inputs a bit. If it is 0, rotate direction of travel 90 degrees
61 counterclockwise; if it is 1, rotate direction of travel 90 degress
62 clockwise; if no more input is available, the direction of travel
63 does not change.
64 0 outputs a 0 bit.
65 1 outputs a 1 bit.
66 @ halts the program.
67 $ only indicates where initial instruction pointer is located;
68 otherwise it has no effect. The initial direction of travel is east.
69
70 Blank cells are NOPs.
71
72 Edge Crossing
73 -------------
74
75 If the instruction pointer reaches an edge of the room and tries to
76 cross it, what happens depends on the current edge mode:
77
78 - In wrap edge mode (this is the initial edge mode), the pointer wraps
79 to the corresponding other edge of the room, as if the room were
80 mapped onto a torus.
81 - In all other modes, if there already exists a room adjoining the
82 current room on that edge, the instruction pointer leaves the
83 current room and enters the adjoining room in the corresponding
84 position. However, if no such adjoining room exists yet, one will be
85 created by making a copy of the current room, transforming it
86 somehow, and adjoining it. The instruction pointer then enters the
87 new room, just as if it had already existed. The details of the
88 transformation depend on the edge mode:
89 - In copy-room-verbatim edge mode, no translation is done.
90 - In copy-room-rotate-cw-90 edge mode, the copy of the current
91 room is rotated clockwise 90 degrees before being adjoined.
92 - In copy-room-rotate-ccw-90 edge mode, the copy of the current
93 room is rotated counterclockwise 90 degrees before being
94 adjoined.
95 - In copy-room-rotate-180 edge mode, the copy of the current room
96 is rotated 180 degrees before being adjoined.
97
98 Examples
99 --------
100
101 The following example reads in a sequence of bits and creates a series
102 of rooms, where 1 bits correspond to unrotated rooms and 0 bits
103 correspond to rooms rotated 90 degrees clockwise (though not precisely
104 one-to-one).
105
106 +------+
107 | /}|
108 |&#/$?@|
109 | / \&|
110 | |
111 | { |
112 |\\ |
113 +------+
114
115 After reading a 0 bit and leaving the right edge, the room is copied,
116 rotated 90 degrees clockwise, and adjoined, so that the rooms of the
117 program are:
118
119 +------+------+
120 | /}|\ & |
121 |&#/$?@|\{ # |
122 | / \&| // |
123 | | $ |
124 | { | \?/|
125 |\\ | &@}|
126 +------+------+
127
128 After leaving the right edge again, the current room is copied, this
129 time rotated 90 degrees counterclockwise, and adjoined, and we get:
130
131 +------+------+------+
132 | /}|\ & | /}|
133 |&#/$?@|\{ # |&#/$?@|
134 | / \&| // | / \&|
135 | | $ | |
136 | { | \?/| { |
137 |\\ | &@}|\\ |
138 +------+------+------+
139
140 Say we were to now read in a 1 bit; we would thus have:
141
142 +------+------+------+------+
143 | /}|\ & | /}| /}|
144 |&#/$?@|\{ # |&#/$?@|&#/$?@|
145 | / \&| // | / \&| / \&|
146 | | $ | | |
147 | { | \?/| { | { |
148 |\\ | &@}|\\ |\\ |
149 +------+------+------+------+
150
151 It should be fairly clear at this point that this program will read all
152 input bits, creating rooms thusly, terminating when there are no more
153 input bits.
154
155 We can write a program that is a variation of the above which, when it
156 encounters the end of input, writes out the bits in the reverse order
157 they were read in, with the following changes:
158
159 * for every `1` in the input, a `1` comes out
160 * for every `0` in the input, `10` comes out
161 * there's an extra `1` at the end of the output
162
163 Here is the program:
164
165 +------------+
166 | /} |
167 |&#/$? \ |
168 | / \& |
169 | |
170 | |
171 | 0 |
172 | ! |
173 | |
174 | |
175 | {1 /# |
176 | { |
177 |\\@ |
178 +------------+
179
180 Computational Class
181 -------------------
182
183 The last example in the previous section was written to demonstrate that
184 Nhohnhehr is at least as powerful as a push-down automaton.
185
186 The author suspects Nhohnhehr to be more powerful still; at least a
187 linear bounded automaton, but possibly even Turing-complete. A strategy
188 for simulating a Turing machine could be developed from the above
189 examples: create new rooms to represent new tape cells, with each
190 possible orientation of the room representing a different tape symbol.
191 The finite control is encoded and embedded in the possible pathways that
192 the instruction pointer can traverse inside each room. Because rooms
193 cannot be changed once created, one might have to resort to creative
194 measures to "change" a tape cell; for instance, each tape cell might
195 have a "stack" of rooms, with a new room appended to the stack each time
196 the cell is to be "changed".
197
198 Source
199 ------
200
201 This document was adapted from [the esolangs.org wiki page for
202 Nhohnhehr](http://www.esolangs.org/wiki/Nhohnhehr), which, like all
203 esowiki articles, has been placed under public domain dedication.
204
205 Implementation
206 --------------
207
208 The Nhohnhehr distribution contains a Nhohnhehr interpreter, written
209 in Python, based on [this implementation of
210 Nhohnhehr](http://esolangs.org/wiki/User:Marinus/Nhohnhehr_interpreter)
211 by [Marinus](http://www.esolangs.org/wiki/User:Marinus). It
212 is effectively the reference interpreter, since it seems to correctly
213 implement the language described here, and there are, to the best of
214 my knowledge, no other implementations of Nhohnhehr in existence.
215 Like all content from the esowiki, it too is in the public domain.
00 #!/usr/bin/env python
11
2 ### Nhohnhehr interpreter ###
2 # ### Nhohnhehr interpreter ###
33
44 # Written by Marinus. The contents of this file are in the public domain.
55 # Adapted from this esowiki page on May 10 2011:
66 # http://www.esolangs.org/wiki/User:Marinus/Nhohnhehr_interpreter
7 # Adapted to run under Python 3, and to conform to PEP8 style,
8 # by Chris Pressey, summer 2021.
79
810 # usage: nhohnhehr.py bits filename (binary I/O)
911 # nhohnhehr.py [bytes] filename (ASCII I/O)
1012
1113 import sys
1214
13 def addvec( (x1,y1), (x2,y2) ): return (x1+x2, y1+y2)
14 def mulvec( (x,y), m ): return (x*m, y*m)
15
16 DEBUG = False
17
18
19 def addvec(v1, v2):
20 (x1, y1) = v1
21 (x2, y2) = v2
22 return (x1 + x2, y1 + y2)
23
24
25 def mulvec(v, m):
26 (x, y) = v
27 return (x * m, y * m)
1528
1629
1730 # room
18 class Room:
31 class Room(object):
1932 data = None
2033 size = 0
21
22 def __getitem__(self, (x, y)):
23 if x>=0 and y>=0 and x<self.size and y<self.size:
34
35 def __getitem__(self, v):
36 (x, y) = v
37 if x >= 0 and y >= 0 and x < self.size and y < self.size:
2438 return self.data[y][x]
2539 else:
2640 raise IndexError("value out of range")
27
41
2842 def __str__(self):
2943 return "\n".join(''.join(str(item) for item in line) for line in self.data)
30
44
3145 # transformations
3246 NONE, CW, CCW, ROT = range(4)
33
47
3448 def transform(self, transformation):
35 if transformation==Room.NONE: return
36 elif transformation==Room.ROT:
49 if transformation == Room.NONE:
50 return
51 elif transformation == Room.ROT:
3752 # rotate 180 degrees: flip lines, and reverse each line
3853 self.data.reverse()
39 for line in self.data: line.reverse()
40 elif transformation==Room.CCW:
54 for line in self.data:
55 line.reverse()
56 elif transformation == Room.CCW:
4157 # clockwise 90 degrees
4258 data = self.data
43 self.data = [[0]*self.size for x in range(self.size)]
59 self.data = [[0] * self.size for x in range(self.size)]
4460 for y in range(self.size):
4561 for x in range(self.size):
46 self.data[self.size-x-1][y] = data[y][x]
47 elif transformation==Room.CW:
62 self.data[self.size - x - 1][y] = data[y][x]
63 elif transformation == Room.CW:
4864 # counterclockwise 90 degrees
4965 data = self.data
50 self.data = [[0]*self.size for x in range(self.size)]
66 self.data = [[0] * self.size for x in range(self.size)]
5167 for y in range(self.size):
5268 for x in range(self.size):
53 self.data[y][x] = data[self.size-x-1][y]
69 self.data[y][x] = data[self.size - x - 1][y]
5470 else:
55 raise ValueError("invalid transformation (%d)"%transformation)
56
71 raise ValueError("invalid transformation (%d)" % transformation)
72
5773 # init room from file or from room+transformation
5874 def __init__(self, file=None, room=None, transform=None):
5975 if (file and room) or (not (file or room)):
6076 raise TypeError("Room needs to be initialized with either a file or a room.")
61
77
6278 # init from file
6379 if file:
6480 self.data = []
65
81
6682 # read file
6783 lines = file.readlines()
68
84
6985 # find possible top-left coordinates for the box
7086 possibleStartCoords = []
7187 for y in range(len(lines)):
7288 for x in range(len(lines[y])):
7389 if lines[y][x] == '+':
7490 try:
75 if lines[y][x+1] == '-' and lines[y+1][x]=='|':
91 if lines[y][x + 1] == '-' and lines[y + 1][x] == '|':
7692 possibleStartCoords.append((x, y))
7793 except IndexError:
7894 # we hit a boundary looking for | or -, so this
7995 # isn't a valid one.
8096 pass
81
97
8298 # check if a box can be found
8399 startCoords = None
84100 roomSize = 0
85101 for (x, y) in possibleStartCoords:
86
102
87103 line = lines[y]
88104 # find next '+'
89 x2 = x+1
90 while x2<len(line) and line[x2]!='+': x2+=1
91
92 if x2==len(line):
105 x2 = x + 1
106 while x2 < len(line) and line[x2] != '+':
107 x2 += 1
108
109 if x2 == len(line):
93110 # no '+' here.
94111 continue
95
112
96113 # found
97 size = x2 - x
114 size = x2 - x
98115 ok = False
99116 # see if it's square
100117 try:
101118 # check horizontal lines
102 if lines[y+size][x:x+size+1] == '+'+'-'*(size-1)+'+':
119 if lines[y + size][x:x + size + 1] == '+' + '-' * (size - 1) + '+':
103120 ok = True
104121 # check vertical lines
105 for y2 in range(y+1, y+size):
106 ok = ok and (lines[y2][x] + lines[y2][x+size] == '||')
107 if not ok: break
108
122 for y2 in range(y + 1, y + size):
123 ok = ok and (lines[y2][x] + lines[y2][x + size] == '||')
124 if not ok:
125 break
126
109127 except IndexError:
110128 # we went outside of the file, so this one isn't valid
111129 ok = False
112
130
113131 if not ok:
114132 # try next pair
115133 continue
117135 # found one!
118136 if startCoords:
119137 # but we already had one...
120 raise ValueError("Multiple valid rooms in one file, first room" +
121 " found at: (%d,%d); second one at: (%d,%d)." \
138 raise ValueError("Multiple valid rooms in one file, first room"
139 " found at: (%d,%d); second one at: (%d,%d)."
122140 % (startCoords[0], startCoords[1], x, y))
123141 else:
124142 # + 1 because that's the start of the data, we don't need the boundary
126144 roomSize = size - 1
127145 # and we have to continue looking in case we find another one,
128146 # in which case the file is invalid.
129
147
130148 # no room in the file
131149 if not startCoords:
132150 raise ValueError("Cannot find a valid room in this file.")
133
151
134152 # we have a room, load it
135153 x, y = startCoords
136154 for lineno in range(roomSize):
137 self.data.append([m for m in lines[lineno+y][x:roomSize+x]])
138
155 self.data.append([m for m in lines[lineno + y][x:roomSize + x]])
156
139157 self.size = roomSize
140158
141159 # init from other room
143161 # this one's easier
144162 self.size = room.size
145163 self.data = [line[:] for line in room.data]
146
164
147165 # transformation needed?
148166 if transform:
149167 self.transform(transform)
150
151
152
153 class Environment:
168
169
170 class Environment(object):
154171 rooms = {}
155172 ip = ()
156173 direction = ()
157174 edgemode = None
158175 roomsize = 0
159176 halt = False
160
177
161178 # states
162 WRAP,COPY,CW,CCW,ROT = range(5)
163
179 WRAP, COPY, CW, CCW, ROT = range(5)
180
164181 # directions
165 LEFT,RIGHT,UP,DOWN = (-1,0), (1,0), (0,-1), (0,1)
166
167 def __getitem__(self, (x, y)):
182 LEFT, RIGHT, UP, DOWN = (-1, 0), (1, 0), (0, -1), (0, 1)
183
184 def __getitem__(self, v):
168185 # get whatever's in that room at that space
186 (x, y) = v
169187 room = self.rooms[self.roomCoords((x, y))]
170 roomX = x%self.roomsize
171 roomY = y%self.roomsize
188 roomX = x % self.roomsize
189 roomY = y % self.roomsize
190 if DEBUG:
191 sys.stdout.write('XY {}: room {}, roomXY {}\n'.format(
192 (x, y), self.roomCoords((x, y)), (roomX, roomY)
193 ))
172194 return room[roomX, roomY]
173
174 def __init__(self, room, (infunc, outfunc)):
175 self.rooms = { (0,0): room }
195
196 def __init__(self, room, io_system):
197 self.rooms = {
198 (0, 0): room
199 }
176200 self.roomsize = room.size
177201 self.dir = Environment.RIGHT
178202 self.edgemode = Environment.WRAP
179 self.infunc, self.outfunc = infunc, outfunc
203 self.io_system = io_system
180204 self.halt = False
181205 # find initial instruction pointer
182
206
183207 self.ip = (-1, -1)
184208 for x in range(self.roomsize):
185209 for y in range(self.roomsize):
186210 if room[x, y] == '$':
187211 self.ip = (x, y)
188212 break
189
213
190214 if self.ip == (-1, -1):
191215 raise ValueError("no $ in room")
192
193 def roomCoords(self, (x,y)):
194 return (int(x/self.roomsize), int(y/self.roomsize))
195
216
217 def infunc(self):
218 return self.io_system.units_in()
219
220 def outfunc(self, unit):
221 return self.io_system.units_out(unit)
222
223 def roomCoords(self, v):
224 (x, y) = v
225 return (x // self.roomsize, y // self.roomsize)
226
196227 def advanceIP(self):
197228 newIP = addvec(self.ip, self.dir)
198
229
199230 if self.roomCoords(self.ip) != self.roomCoords(newIP):
200231 if self.edgemode == Environment.WRAP:
201232 # wrap to edge of last room
204235 # make a new room if none exists yet
205236 if not self.roomCoords(newIP) in self.rooms:
206237 # transformations
207 transform = { Environment.COPY: Room.NONE,
208 Environment.CW: Room.CW,
209 Environment.CCW: Room.CCW,
210 Environment.ROT: Room.ROT } [self.edgemode]
211
212 self.rooms.update( { self.roomCoords(newIP):
213 Room(room=self.rooms[self.roomCoords(self.ip)],
214 transform=transform) } )
238 transform = {
239 Environment.COPY: Room.NONE,
240 Environment.CW: Room.CW,
241 Environment.CCW: Room.CCW,
242 Environment.ROT: Room.ROT
243 }[self.edgemode]
244 self.rooms.update({
245 self.roomCoords(newIP): Room(
246 room=self.rooms[self.roomCoords(self.ip)],
247 transform=transform
248 )
249 })
215250 self.ip = newIP
216
251
217252 def step(self):
218253 command = self[self.ip]
219 ccwrot = {self.LEFT: self.DOWN, self.RIGHT: self.UP,
220 self.UP: self.RIGHT, self.DOWN: self.LEFT}
221 cwrot = {self.LEFT: self.UP, self.RIGHT: self.DOWN,
222 self.UP: self.LEFT, self.DOWN: self.RIGHT}
254 if DEBUG:
255 sys.stdout.write('({}) @ {}\n'.format(command, self.ip))
256 ccwrot = {
257 self.LEFT: self.DOWN,
258 self.RIGHT: self.UP,
259 self.UP: self.RIGHT,
260 self.DOWN: self.LEFT
261 }
262 cwrot = {
263 self.LEFT: self.UP,
264 self.RIGHT: self.DOWN,
265 self.UP: self.LEFT,
266 self.DOWN: self.RIGHT
267 }
223268
224269 if command == '/':
225270 self.dir = ccwrot[self.dir]
226271 elif command == '\\':
227272 self.dir = cwrot[self.dir]
228273 elif command in '=&{}!':
229 self.edgemode = { '=': self.WRAP,
230 '&': self.COPY,
231 '{': self.CCW,
232 '}': self.CW,
233 '!': self.ROT } [command]
274 self.edgemode = {
275 '=': self.WRAP,
276 '&': self.COPY,
277 '{': self.CCW,
278 '}': self.CW,
279 '!': self.ROT
280 }[command]
234281 elif command == '#':
235282 self.advanceIP()
236283 elif command == '?':
237284 try:
238 self.dir = ( self.infunc() and cwrot or ccwrot ) [self.dir]
285 self.dir = (self.infunc() and cwrot or ccwrot)[self.dir]
239286 except IOError:
240287 # no more input available = do nothing
241288 pass
243290 self.outfunc(int(command))
244291 elif command == '@':
245292 self.halt = True
246
293
247294 self.advanceIP()
248
295
249296 def run(self):
250 while not self.halt: self.step()
251
297 while not self.halt:
298 self.step()
299
300
301 class NhohnhehrIO(object):
302 def units_in(self):
303 raise NotImplementedError('implement units_in please')
304
305 def units_out(self, unit):
306 raise NotImplementedError('implement units_out please')
307
308 def close(self):
309 pass
310
311
312 class BitsIO(NhohnhehrIO):
313 def units_in(self):
314 i = None
315 while i not in ('0', '1'):
316 i = sys.stdin.read(1)
317 if i == '':
318 raise IOError() # eof
319 return int(i)
320
321 def units_out(self, bit):
322 sys.stdout.write(('0', '1')[bit])
323 sys.stdout.flush()
324
325 def close(self):
326 sys.stdout.write("\n")
327 sys.stdout.flush()
328
329
330 class BytesIO(NhohnhehrIO):
331 def __init__(self):
332 self.bits = [[]]
333
334 def units_in(self):
335 # get data if necessary
336 if self.bits[0] == []:
337 i = sys.stdin.read(1)
338 if (i == ''):
339 raise IOError() # eof
340 else:
341 self.bits[0] = [int(bool(ord(i) & (1 << b))) for b in range(7, -1, -1)]
342
343 # return data
344 bit = self.bits[0][0]
345 self.bits[0] = self.bits[0][1:]
346 return bit
347
348 def units_out(self, bit):
349 self.bits[0].append(bit)
350
351 # if we have 8 bits, output
352 if len(self.bits[0]) == 8:
353 sys.stdout.write(chr(sum(self.bits[0][7 - b] << b for b in range(7, -1, -1))))
354 sys.stdout.flush()
355 self.bits[0] = []
356
252357
253358 def main(argv):
254 if not len(argv) in (2,3) or (len(argv)==3 and not argv[1] in ('bits','bytes')):
255 print "Usage: [python] %s [bits|bytes] filename" % argv[0]
256 print " bits/bytes: specify i/o mode"
257 print ""
258 print " In bits mode, i/o uses the characters '0' and '1'"
259 print " (and when reading input, everything that's not '0'"
260 print " or 1 is ignored)."
261 print " In bytes mode, i/o is done 8 bits at a time as ASCII."
262 print ""
263 print " If no mode is given, bytes mode is used."
264 print ""
265
359 if len(argv) not in (2, 3) or (len(argv) == 3 and not argv[1] in ('bits', 'bytes')):
360 print("""\
361 Usage: [python] %s [bits|bytes] filename
362 bits/bytes: specify i/o mode
363
364 In bits mode, i/o uses the characters '0' and '1'
365 (and when reading input, everything that's not '0'
366 or 1 is ignored).
367 In bytes mode, i/o is done 8 bits at a time as ASCII.
368
369 If no mode is given, bytes mode is used.
370 """ % argv[0])
266371 sys.exit()
267
268 if len(argv)==2:
372
373 if len(argv) == 2:
269374 mode = 'bytes'
270375 fname = argv[1]
271376 else:
272377 mode = argv[1]
273378 fname = argv[2]
274
275 # i/o functions
276 def bits_in():
277 i = None
278 while not i in ('0','1'):
279 i = sys.stdin.read(1)
280 if i == '':
281 raise IOError() # eof
282 return int(i)
283
284 def bits_out(bit):
285 sys.stdout.write(('0', '1')[bit])
286 sys.stdout.flush()
287
288 def bytes_in(bits=[[]]):
289 # get data if necessary
290 if bits[0]==[]:
291 i = sys.stdin.read(1)
292 if (i==''): raise IOError() # eof
293 else:
294 bits[0] = [ int(bool(ord(i) & (1 << b))) for b in range(7,-1,-1) ]
295
296 # return data
297 bit = bits[0][0]
298 bits[0] = bits[0][1:]
299 return bit
300
301 def bytes_out(bit, bits=[[]]):
302 bits[0].append(bit)
303
304 # if we have 8 bits, output
305 if len(bits[0]) == 8:
306 sys.stdout.write(chr(sum(bits[0][7-b]<<b for b in range(7,-1,-1))))
307 sys.stdout.flush()
308 bits[0] = []
309
310 modes = { 'bits': (bits_in, bits_out),
311 'bytes': (bytes_in, bytes_out) }
379
380 io_system = {
381 'bits': BitsIO(),
382 'bytes': BytesIO(),
383 }[mode]
312384 try:
313 Environment( Room(file=file(fname)), modes[mode] ).run()
314 if mode=='bits': print # newline
315
316 except Exception, e:
317 print "Error: ", e
318
319 if __name__=='__main__': main(sys.argv)
385 with open(fname, 'r') as f:
386 Environment(Room(file=f), io_system).run()
387 io_system.close()
388 except Exception as e:
389 print("Error: {}".format(e))
390
391
392 if __name__ == '__main__':
393 main(sys.argv)
00 #!/bin/sh
11
2 # Just a rudimentary sanity test, better than nothing.
3
4 EXPECTED='110101010101011111111'
5 R=`echo '11111110000001' | src/nhohnhehr.py bits eg/reverse-esque.nho`
6 if [ $R = $EXPECTED ]; then
7 echo 'Sanity test passed.'
8 else
9 echo "Expected $EXPECTED but got $R."
10 exit 1
11 fi
2 APPLIANCES="tests/appliances/nhohnhehr.py.md"
3 falderal $APPLIANCES tests/Nhohnhehr.md || exit 1
0 Test Suite for Nhohnhehr
1 ========================
2
3 This test suite is written in [Falderal][] format.
4
5 [Falderal]: https://catseye.tc/node/Falderal
6
7 -> Tests for functionality "Run Nhohnhehr program, outputting at most 80 bits"
8
9 "Reverse-esque" example.
10
11 | +------------+
12 | | /} |
13 | |&#/$? \ |
14 | | / \& |
15 | | |
16 | | |
17 | | 0 |
18 | | ! |
19 | | |
20 | | |
21 | | {1 /# |
22 | | { |
23 | |\\@ |
24 | +------------+
25 + 11111110000001
26 = 110101010101011111111
27
28 "Read and store" example.
29
30 | +------+
31 | | /}|
32 | |&#/$?@|
33 | | / \&|
34 | | |
35 | | { |
36 | |\\ |
37 | +------+
38 + 11111110000001
39 =
40
41 Truth-machine example.
42
43 | +---+
44 | |@/0|
45 | |$? |
46 | |#\1|
47 | +---+
48 + 0
49 = 0
50
51 | +---+
52 | |@/0|
53 | |$? |
54 | |#\1|
55 | +---+
56 + 1
57 = 11111111111111111111111111111111111111111111111111111111111111111111111111111111
0 -> Functionality "Run Nhohnhehr program, outputting at most 80 bits" is implemented by
1 -> shell command
2 -> "python2 src/nhohnhehr.py bits %(test-body-file) <%(test-input-file) 2>&1 | head -c 80"
3
4 -> Functionality "Run Nhohnhehr program, outputting at most 80 bits" is implemented by
5 -> shell command
6 -> "python3 src/nhohnhehr.py bits %(test-body-file) <%(test-input-file) 2>&1 | head -c 80"