rubylabs 0.5.5 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/marslab.rb +1147 -0
- data/lib/randomlab.rb +436 -201
- data/lib/rubylabs.rb +15 -14
- data/test/mars_test.rb +519 -0
- data/test/random_test.rb +75 -19
- metadata +4 -4
- data/lib/temps.rb +0 -41
- data/test/temps_test.rb +0 -24
data/lib/rubylabs.rb
CHANGED
@@ -18,6 +18,7 @@ autoload :IterationLab, "iterationlab.rb"
|
|
18
18
|
autoload :RecursionLab, "recursionlab.rb"
|
19
19
|
autoload :HashLab, "hashlab.rb"
|
20
20
|
autoload :BitLab, "bitlab.rb"
|
21
|
+
autoload :MARSLab, "marslab.rb"
|
21
22
|
autoload :RandomLab, "randomlab.rb"
|
22
23
|
autoload :EncryptionLab, "encryptionlab.rb"
|
23
24
|
autoload :ELIZALab, "elizalab.rb"
|
@@ -64,20 +65,20 @@ Log base 2.
|
|
64
65
|
a < b ? a : b
|
65
66
|
end
|
66
67
|
|
67
|
-
=begin rdoc
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
=end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
68
|
+
# =begin rdoc
|
69
|
+
# Return a copy of object x with the elements in a new, scrambled order. The
|
70
|
+
# parameter x can be any object that has an index operator (e.g. strings or
|
71
|
+
# arrays).
|
72
|
+
# =end
|
73
|
+
#
|
74
|
+
# def permutation(x)
|
75
|
+
# res = x.clone
|
76
|
+
# for i in 0..res.length-2
|
77
|
+
# r = rand(res.length-i) + i
|
78
|
+
# res[i], res[r] = res[r], res[i]
|
79
|
+
# end
|
80
|
+
# return res
|
81
|
+
# end
|
81
82
|
|
82
83
|
=begin rdoc
|
83
84
|
|
data/test/mars_test.rb
ADDED
@@ -0,0 +1,519 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
=begin rdoc
|
5
|
+
This file has tests of the code written for the CoreWar project: an
|
6
|
+
assembler for "Redcode" programs, and a "MARS" virtual machine emulator.
|
7
|
+
|
8
|
+
The should probably be a few more tests of individual components, but the
|
9
|
+
tests here just make sure the whole system operates as expected. For example,
|
10
|
+
the instruction tests make arrays of strings containing source instructions,
|
11
|
+
pass them to the assembler, and execute the resulting instructions. The
|
12
|
+
tests don't look too closely at the result, e.g. to make sure all the symbols
|
13
|
+
are in the symbol table, or each instruction has the right components -- the
|
14
|
+
test just makes sure the instructions do what they are expected to do.
|
15
|
+
|
16
|
+
Tests in this file:
|
17
|
+
* an assembler test looks for syntax errors on ill-formed statements
|
18
|
+
* another assembler test looks at the code for a small, valid program
|
19
|
+
* an address mode test makes sure MOV instructions copy a piece of data
|
20
|
+
to the expected location using each of the four addressing modes
|
21
|
+
* individual instruction tests for each machine instruction
|
22
|
+
|
23
|
+
NOTE: to test that instructions fetched from memory have the expected value
|
24
|
+
the assertions compare the result of instr.to_s to a string. Things will
|
25
|
+
break horribly if the to_s method changes the format....
|
26
|
+
|
27
|
+
NOTE: Be careful when making test programs -- there needs to be a label or
|
28
|
+
a space before each opcode, e.g. [" DAT #0, <1", " DAT #0, #1"]
|
29
|
+
|
30
|
+
=end
|
31
|
+
|
32
|
+
include MARSLab
|
33
|
+
|
34
|
+
class TestMARS < Test::Unit::TestCase
|
35
|
+
|
36
|
+
# Assembling these instructions should lead to one error per line, and
|
37
|
+
# no code generated.
|
38
|
+
|
39
|
+
def test_01_syntax_errors
|
40
|
+
source = [
|
41
|
+
"@label mov #0, #1 ; label needs to be alphanumeric",
|
42
|
+
"l@bel mov #0, #1 ; label needs to be alphanumeric",
|
43
|
+
"mov #0, #1 ; unlabeled line must start with space",
|
44
|
+
"label @bogus #0, #1 ; opcode with non-alphanumeric",
|
45
|
+
"label b@gus #0, #1 ; opcode with non-alphanumeric",
|
46
|
+
"label muv #0, #1 ; unknown opcode",
|
47
|
+
" mov #b@gus, #1 ; messed up operand",
|
48
|
+
" mov #0 #1 ; missing comma",
|
49
|
+
" EQU 3 ; missing label on pseudo-op",
|
50
|
+
"x EQU 3x ; arg not an integer",
|
51
|
+
" END foo ; undefined label",
|
52
|
+
]
|
53
|
+
|
54
|
+
name, code, symbols, errors = MARS.assemble(source)
|
55
|
+
|
56
|
+
assert_equal source.length, errors.length # every line generates an error
|
57
|
+
assert_equal 0, code.length # should be no instructions generated
|
58
|
+
end
|
59
|
+
|
60
|
+
# A program from the ICWS archives -- has a wide variety of instructions, each
|
61
|
+
# addressing mode, and a pseudo-op. Should produce no errors, and one machine
|
62
|
+
# instruction for each operation (inclduing DAT).
|
63
|
+
|
64
|
+
def test_02_assembly
|
65
|
+
source = [
|
66
|
+
";redcode",
|
67
|
+
";name Mice",
|
68
|
+
";author Chip Wendell",
|
69
|
+
";strategy 1st place in ICWST'86",
|
70
|
+
";strategy replicator",
|
71
|
+
";assert 1",
|
72
|
+
"PTR DAT #0",
|
73
|
+
"START MOV #12 ,PTR",
|
74
|
+
"LOOP MOV @PTR ,<COPY",
|
75
|
+
" DJN LOOP ,PTR",
|
76
|
+
" SPL @COPY ,0",
|
77
|
+
" ADD #653 ,COPY",
|
78
|
+
" JMZ START ,PTR",
|
79
|
+
"COPY DAT #833",
|
80
|
+
" END START",
|
81
|
+
]
|
82
|
+
|
83
|
+
name, code, symbols, errors = MARS.assemble(source)
|
84
|
+
|
85
|
+
assert_equal "Mice", name
|
86
|
+
assert_equal 0, errors.length
|
87
|
+
assert_equal 8, code.length
|
88
|
+
assert_equal 0, symbols["PTR"]
|
89
|
+
assert_equal 1, symbols["START"]
|
90
|
+
assert_equal 2, symbols["LOOP"]
|
91
|
+
assert_equal 7, symbols["COPY"]
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# Assemble and run a synthetic program that has 4 MOV instructions. Each
|
96
|
+
# instruction uses a different addressing mode to find the data source, and
|
97
|
+
# each copies its data to the same location.
|
98
|
+
|
99
|
+
def test_03_modes
|
100
|
+
source = [
|
101
|
+
"immed mov #1, target ; moves #1",
|
102
|
+
"direct mov dsrc, target ; moves #2",
|
103
|
+
"indir mov @ptr, target ; moves #3",
|
104
|
+
"auto mov <ptr2, target ; moves #4, decrements ptr2",
|
105
|
+
"target dat #0, #0 ; place to store data",
|
106
|
+
"dsrc dat #2, #2 ; source for direct mode",
|
107
|
+
"ptr dat #0, isrc ; a pointer to the indirect source",
|
108
|
+
"isrc dat #3, #3 ; source for indirect mode",
|
109
|
+
"ptr2 dat #0, dummy ; this pointer should be decremented",
|
110
|
+
" dat #4, #4 ; source for auto-decrement",
|
111
|
+
"dummy dat #0, #0 ; not used",
|
112
|
+
]
|
113
|
+
|
114
|
+
s = source.find { |line| line =~ /^target/ }
|
115
|
+
target = source.index(s)
|
116
|
+
|
117
|
+
s = source.find { |line| line =~ /^ptr2/ }
|
118
|
+
ptr2 = source.index(s)
|
119
|
+
|
120
|
+
mem, pc = make_machine(source)
|
121
|
+
|
122
|
+
# value before first MOV:
|
123
|
+
assert_equal "DAT #0 #0", mem.fetch(target).to_s
|
124
|
+
|
125
|
+
# value after MOV with immediate mode -- note only B field is copied
|
126
|
+
instr = mem.fetch(pc.next)
|
127
|
+
instr.execute(pc, mem)
|
128
|
+
assert_equal "DAT #0 #1", mem.fetch(target).to_s
|
129
|
+
|
130
|
+
# value after MOV with direct mode -- note both A and B are moved
|
131
|
+
instr = mem.fetch(pc.next)
|
132
|
+
instr.execute(pc, mem)
|
133
|
+
assert_equal "DAT #2 #2", mem.fetch(target).to_s
|
134
|
+
|
135
|
+
# value after MOV with indirect mode -- both A and B moved
|
136
|
+
instr = mem.fetch(pc.next)
|
137
|
+
instr.execute(pc, mem)
|
138
|
+
assert_equal "DAT #3 #3", mem.fetch(target).to_s
|
139
|
+
|
140
|
+
# value of the pointer before the auto-decrement:
|
141
|
+
assert_equal "DAT #0 2", mem.fetch(ptr2).to_s
|
142
|
+
|
143
|
+
# value after MOV with auto-decrement mode -- both A and B moved
|
144
|
+
instr = mem.fetch(pc.next)
|
145
|
+
instr.execute(pc, mem)
|
146
|
+
assert_equal "DAT #4 #4", mem.fetch(target).to_s
|
147
|
+
|
148
|
+
# value of the pointer after the auto-decrement:
|
149
|
+
assert_equal "DAT #0 1", mem.fetch(ptr2).to_s
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
# A DAT instruction is effectively a halt instruction. Note that the
|
154
|
+
# addressing modes are dereferenced, so the auto-decrement mode should
|
155
|
+
# cause the pointer to decrement.
|
156
|
+
|
157
|
+
def test_04_DAT
|
158
|
+
mem, pc = make_machine([" DAT #0, <1", " DAT #0, #1"])
|
159
|
+
|
160
|
+
# pointer before executing the DAT:
|
161
|
+
assert_equal "DAT #0 #1", mem.fetch(1).to_s
|
162
|
+
|
163
|
+
instr = mem.fetch(pc.next)
|
164
|
+
assert_equal :halt, instr.execute(pc, mem)
|
165
|
+
|
166
|
+
# pointer after executing the DAT -- value changed, mode did not
|
167
|
+
assert_equal "DAT #0 #0", mem.fetch(1).to_s
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
# Simple MOV test just executes the instruction in the IMP program.
|
172
|
+
# A second test verifies a runtime error if the B field is immediate.
|
173
|
+
|
174
|
+
def test_05_MOV
|
175
|
+
mem, pc = make_machine([" MOV 0, 1"], 2)
|
176
|
+
instr = mem.fetch(pc.next)
|
177
|
+
assert_equal :continue, instr.execute(pc, mem)
|
178
|
+
assert_equal mem.fetch(0).to_s, mem.fetch(1).to_s
|
179
|
+
|
180
|
+
mem, pc = make_machine([" MOV 0, #1"])
|
181
|
+
instr = mem.fetch(pc.next)
|
182
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
183
|
+
end
|
184
|
+
|
185
|
+
# ADD tests
|
186
|
+
# "ADD #A, dest" adds the constant A to the B field of dest
|
187
|
+
# "ADD src, dest" adds the A and B fields to src to the A and B of dest
|
188
|
+
# "ADD src, #B" is a runtime error (B can't be immediate)
|
189
|
+
|
190
|
+
def test_06_ADD
|
191
|
+
source = [
|
192
|
+
" ADD #3, sum",
|
193
|
+
" ADD x, y",
|
194
|
+
" ADD x, #0",
|
195
|
+
"sum DAT #1, #2",
|
196
|
+
"x DAT #3, #4",
|
197
|
+
"y DAT #5, #6",
|
198
|
+
]
|
199
|
+
|
200
|
+
mem, pc = make_machine(source)
|
201
|
+
|
202
|
+
instr = mem.fetch(pc.next)
|
203
|
+
assert_equal :continue, instr.execute(pc, mem)
|
204
|
+
assert_equal "DAT #1 #5", mem.fetch(3).to_s
|
205
|
+
|
206
|
+
instr = mem.fetch(pc.next)
|
207
|
+
assert_equal :continue, instr.execute(pc, mem)
|
208
|
+
assert_equal "DAT #8 #10", mem.fetch(5).to_s
|
209
|
+
|
210
|
+
instr = mem.fetch(pc.next)
|
211
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
# SUB tests -- same as ADD, but subtracting A from B
|
216
|
+
|
217
|
+
def test_07_SUB
|
218
|
+
source = [
|
219
|
+
" SUB #3, sum",
|
220
|
+
" SUB x, y",
|
221
|
+
" SUB x, #0",
|
222
|
+
"sum DAT #1, #10",
|
223
|
+
"x DAT #3, #4",
|
224
|
+
"y DAT #5, #6",
|
225
|
+
]
|
226
|
+
|
227
|
+
mem, pc = make_machine(source)
|
228
|
+
|
229
|
+
instr = mem.fetch(pc.next)
|
230
|
+
assert_equal :continue, instr.execute(pc, mem)
|
231
|
+
assert_equal "DAT #1 #7", mem.fetch(3).to_s
|
232
|
+
|
233
|
+
instr = mem.fetch(pc.next)
|
234
|
+
assert_equal :continue, instr.execute(pc, mem)
|
235
|
+
assert_equal "DAT #2 #2", mem.fetch(5).to_s
|
236
|
+
|
237
|
+
instr = mem.fetch(pc.next)
|
238
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
# JMP tests
|
243
|
+
# JMP x should set the program counter to x
|
244
|
+
# JMP #x is an error -- the operand can't be immediate
|
245
|
+
|
246
|
+
def test_08_JMP
|
247
|
+
source = [
|
248
|
+
" JMP x",
|
249
|
+
" JMP #0",
|
250
|
+
"x DAT #0, #0",
|
251
|
+
]
|
252
|
+
|
253
|
+
mem, pc = make_machine(source)
|
254
|
+
|
255
|
+
instr = mem.fetch(pc.next)
|
256
|
+
assert_equal :continue, instr.execute(pc, mem)
|
257
|
+
assert_equal 2, pc.next
|
258
|
+
|
259
|
+
instr = mem.fetch(1)
|
260
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
# JMZ tests
|
265
|
+
# JMZ x y should set the program counter to x if y is 0
|
266
|
+
# JMP #x y is an error -- the operand can't be immediate
|
267
|
+
|
268
|
+
def test_09_JMZ
|
269
|
+
source = [
|
270
|
+
" JMZ x, #1",
|
271
|
+
" JMZ x, #0",
|
272
|
+
" JMZ #0, #0",
|
273
|
+
"x DAT #0, #0",
|
274
|
+
]
|
275
|
+
|
276
|
+
mem, pc = make_machine(source)
|
277
|
+
|
278
|
+
instr = mem.fetch(pc.next)
|
279
|
+
assert_equal :continue, instr.execute(pc, mem)
|
280
|
+
loc = pc.next
|
281
|
+
assert_equal 1, loc # branch not taken
|
282
|
+
|
283
|
+
instr = mem.fetch(loc)
|
284
|
+
assert_equal :continue, instr.execute(pc, mem)
|
285
|
+
loc = pc.next
|
286
|
+
assert_equal 3, loc # branch taken
|
287
|
+
|
288
|
+
instr = mem.fetch(2)
|
289
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
# JMN tests -- same as JMZ, but branch on non-zero
|
294
|
+
|
295
|
+
def test_10_JMN
|
296
|
+
source = [
|
297
|
+
" JMN x, #0",
|
298
|
+
" JMN x, #1",
|
299
|
+
" JMN #0, #0",
|
300
|
+
"x DAT #0, #0",
|
301
|
+
]
|
302
|
+
|
303
|
+
mem, pc = make_machine(source)
|
304
|
+
|
305
|
+
instr = mem.fetch(pc.next)
|
306
|
+
assert_equal :continue, instr.execute(pc, mem)
|
307
|
+
loc = pc.next
|
308
|
+
assert_equal 1, loc # branch not taken
|
309
|
+
|
310
|
+
instr = mem.fetch(loc)
|
311
|
+
assert_equal :continue, instr.execute(pc, mem)
|
312
|
+
loc = pc.next
|
313
|
+
assert_equal 3, loc # branch taken
|
314
|
+
|
315
|
+
instr = mem.fetch(2)
|
316
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
# DJN combines auto-decrement processing with a branch instruction. As with
|
321
|
+
# other branches, immediate mode is not allowed for the A operand.
|
322
|
+
|
323
|
+
def test_11_DJN
|
324
|
+
source = [
|
325
|
+
" DJN y, x", # first won't branch
|
326
|
+
" DJN y, x", # ...but second one will
|
327
|
+
" DJN #0, #0",
|
328
|
+
"x DAT #0, #1",
|
329
|
+
"y MOV 0, 1",
|
330
|
+
]
|
331
|
+
|
332
|
+
mem, pc = make_machine(source)
|
333
|
+
|
334
|
+
instr = mem.fetch(pc.next)
|
335
|
+
assert_equal :continue, instr.execute(pc, mem)
|
336
|
+
loc = pc.next
|
337
|
+
assert_equal 1, loc # branch not taken
|
338
|
+
|
339
|
+
instr = mem.fetch(loc)
|
340
|
+
assert_equal :continue, instr.execute(pc, mem)
|
341
|
+
loc = pc.next
|
342
|
+
assert_equal 4, loc # branch taken
|
343
|
+
|
344
|
+
instr = mem.fetch(2)
|
345
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
346
|
+
|
347
|
+
end
|
348
|
+
|
349
|
+
# CMP fetches the two operands and compares them; if they're the same
|
350
|
+
# the next instruction is skipped.
|
351
|
+
|
352
|
+
def test_12_CMP
|
353
|
+
source = [
|
354
|
+
" CMP #1, x", # don't skip
|
355
|
+
" CMP #0, x", # skip
|
356
|
+
" DAT #0, #0",
|
357
|
+
" CMP x, y", # don't skip
|
358
|
+
" CMP x, x", # skip
|
359
|
+
" DAT #0, #0",
|
360
|
+
" CMP x, #0", # illegal
|
361
|
+
"x DAT #0, #0",
|
362
|
+
"y DAT #0, #1",
|
363
|
+
]
|
364
|
+
|
365
|
+
mem, pc = make_machine(source)
|
366
|
+
|
367
|
+
instr = mem.fetch(pc.next)
|
368
|
+
assert_equal :continue, instr.execute(pc, mem)
|
369
|
+
loc = pc.next
|
370
|
+
assert_equal 1, loc # branch not taken
|
371
|
+
|
372
|
+
instr = mem.fetch(loc)
|
373
|
+
assert_equal :continue, instr.execute(pc, mem)
|
374
|
+
loc = pc.next
|
375
|
+
assert_equal 3, loc # branch taken
|
376
|
+
|
377
|
+
instr = mem.fetch(loc)
|
378
|
+
assert_equal :continue, instr.execute(pc, mem)
|
379
|
+
loc = pc.next
|
380
|
+
assert_equal 4, loc # branch not taken
|
381
|
+
|
382
|
+
instr = mem.fetch(loc)
|
383
|
+
assert_equal :continue, instr.execute(pc, mem)
|
384
|
+
loc = pc.next
|
385
|
+
assert_equal 6, loc # branch taken
|
386
|
+
|
387
|
+
instr = mem.fetch(loc)
|
388
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
389
|
+
|
390
|
+
end
|
391
|
+
|
392
|
+
# In our implementation SLT only works for immediate A operands. Skip the
|
393
|
+
# next instruction if A is less than the A-field of the dereferenced B operand.
|
394
|
+
|
395
|
+
def test_13_SLT
|
396
|
+
source = [
|
397
|
+
" SLT #3, x", # don't skip
|
398
|
+
" SLT #1, x", # skip
|
399
|
+
" SLT #0, #0", # illegal B operand
|
400
|
+
" SLT #0, z", # don't skip when operands equal
|
401
|
+
"x DAT #2", # expect to stop here
|
402
|
+
"z DAT #0",
|
403
|
+
]
|
404
|
+
|
405
|
+
mem, pc = make_machine(source)
|
406
|
+
|
407
|
+
instr = mem.fetch(pc.next)
|
408
|
+
assert_equal :continue, instr.execute(pc, mem)
|
409
|
+
loc = pc.next
|
410
|
+
assert_equal 1, loc # branch not taken
|
411
|
+
|
412
|
+
instr = mem.fetch(loc)
|
413
|
+
assert_equal :continue, instr.execute(pc, mem)
|
414
|
+
loc = pc.next
|
415
|
+
assert_equal 3, loc # branch taken
|
416
|
+
|
417
|
+
instr = mem.fetch(loc)
|
418
|
+
assert_equal :continue, instr.execute(pc, mem)
|
419
|
+
loc = pc.next
|
420
|
+
assert_equal 4, loc # branch not taken
|
421
|
+
|
422
|
+
instr = mem.fetch(2)
|
423
|
+
assert_raise(MARSRuntimeException) { instr.execute(pc, mem) }
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
# SPL forks a new thread that starts execution at the address specified by A.
|
428
|
+
# The pc object maintains a list of locations, with one location per thread.
|
429
|
+
# After the split in this test, the location list should have 2 items, one the
|
430
|
+
# continuation of the original thread and the other the first instruction in
|
431
|
+
# the new thread.
|
432
|
+
|
433
|
+
def test_14_SPL
|
434
|
+
source = [
|
435
|
+
" SPL x",
|
436
|
+
" DAT #0, #0", # space filler
|
437
|
+
" DAT #0, #0",
|
438
|
+
"x MOV 0, 1",
|
439
|
+
]
|
440
|
+
|
441
|
+
mem, pc = make_machine(source)
|
442
|
+
|
443
|
+
instr = mem.fetch(pc.next)
|
444
|
+
assert_equal :continue, instr.execute(pc, mem)
|
445
|
+
assert_equal [1,3], pc.addrs
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
# The program counter object should have a history of memory references for each
|
450
|
+
# thread. pc.history[i] is the history vector for thread i
|
451
|
+
|
452
|
+
def test_15_threads
|
453
|
+
source = [
|
454
|
+
" MOV x, y", # should log source and target references
|
455
|
+
" SPL z", # start new thread
|
456
|
+
" ADD x, y", # first thread logs two memory references
|
457
|
+
" DAT #0", # first thread halts
|
458
|
+
"z SUB x, y", # second thread logs two memory references
|
459
|
+
" DAT #0", # second thread halts
|
460
|
+
"x DAT #1",
|
461
|
+
"y DAT #0",
|
462
|
+
]
|
463
|
+
|
464
|
+
mem, pc = make_machine(source)
|
465
|
+
assert_equal 1, pc.history.length
|
466
|
+
assert_equal [], pc.history[0]
|
467
|
+
|
468
|
+
mem.fetch(pc.next).execute(pc, mem)
|
469
|
+
assert_equal [0,6,7], pc.history[0] # log has instr and both operands
|
470
|
+
|
471
|
+
mem.fetch(pc.next).execute(pc, mem)
|
472
|
+
assert_equal 2, pc.history.length # SPL instruction makes new thread and history vector
|
473
|
+
assert_equal [0,6,7,1], pc.history[0]
|
474
|
+
|
475
|
+
mem.fetch(pc.next).execute(pc, mem)
|
476
|
+
assert_equal [0,6,7,1,2,6,7], pc.history[0] # staying with thread 0, add also logs both operands
|
477
|
+
|
478
|
+
mem.fetch(pc.next).execute(pc, mem)
|
479
|
+
assert_equal [4,6,7], pc.history[1] # next instr comes from thread 1, sub logs both operands
|
480
|
+
|
481
|
+
assert_equal :halt, mem.fetch(pc.next).execute(pc, mem) # back to thread 0, DAT halts the thread
|
482
|
+
assert_equal [0,6,7,1,2,6,7,3], pc.history[0] # DAT is logged
|
483
|
+
assert_equal 1, pc.kill_thread # zap this thread, expect other one still there
|
484
|
+
assert_equal [4,6,7], pc.history[0] # other thread is now thread 0
|
485
|
+
|
486
|
+
assert_equal :halt, mem.fetch(pc.next).execute(pc, mem) # it also executes a DAT
|
487
|
+
assert_equal [4,6,7,5], pc.history[0]
|
488
|
+
assert_equal 0, pc.kill_thread
|
489
|
+
|
490
|
+
assert_equal "DAT #0 #1", mem.fetch(7).to_s # y set to 1, inc to 2 by thread 0, set back to 1 by thread 1
|
491
|
+
end
|
492
|
+
|
493
|
+
# The Warrior class constructor takes a file name or a symbol corresponding to one
|
494
|
+
# of the built-in test programs. The program is assembled, and all the relevant
|
495
|
+
# information saved with the object.
|
496
|
+
|
497
|
+
def test_16_warrior_class
|
498
|
+
w = Warrior.new(:imp)
|
499
|
+
assert_equal "Imp", w.name
|
500
|
+
assert_equal [ Word.new("MOV","0","1") ], w.code
|
501
|
+
assert_equal 0, w.symbols[:start]
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
|
506
|
+
# Set up and initialize a mini-machine that has the assembled instructions
|
507
|
+
# in array a loaded into memory, and a program counter set up to fetch
|
508
|
+
# the first instruction.
|
509
|
+
|
510
|
+
def make_machine(a, n = a.length)
|
511
|
+
mem = Memory.new(n)
|
512
|
+
name, code, symbols, errors = MARS.assemble(a)
|
513
|
+
code.each_with_index do |x, i|
|
514
|
+
mem.store(i, x)
|
515
|
+
end
|
516
|
+
pc = PC.new(name, 0)
|
517
|
+
return mem, pc
|
518
|
+
end
|
519
|
+
|
data/test/random_test.rb
CHANGED
@@ -4,37 +4,86 @@ require 'test_helper'
|
|
4
4
|
include RandomLab
|
5
5
|
|
6
6
|
class TestRandoms < Test::Unit::TestCase
|
7
|
+
|
8
|
+
# The prng_sequence method makes a sequence of numbers using a linear congruential
|
9
|
+
# method with the specified a, c, and m values
|
10
|
+
|
11
|
+
def test_01_prng_sequence
|
12
|
+
sched8 = prng_sequence(1, 8, 12)
|
13
|
+
assert_equal 12, sched8.length
|
14
|
+
assert_equal 3, sched8.uniq.length
|
15
|
+
|
16
|
+
sched7 = prng_sequence(1, 7, 12)
|
17
|
+
assert_equal 12, sched7.length
|
18
|
+
assert_equal 12, sched7.uniq.length
|
19
|
+
|
20
|
+
seq1 = prng_sequence(3, 337, 1000)
|
21
|
+
assert_equal [0, 337, 348, 381], seq1[0..3]
|
22
|
+
assert_equal 100, seq1.uniq.length
|
23
|
+
|
24
|
+
seq2 = prng_sequence(81, 337, 1000)
|
25
|
+
assert_equal [0, 337, 634, 691], seq2[0..3]
|
26
|
+
assert_equal 1000, seq2.uniq.length
|
27
|
+
end
|
28
|
+
|
29
|
+
# A PRNG object generates the same sequence of values as the prng_sequence method,
|
30
|
+
# but makes them on demand. Test the sequence, test resetting the seed, test again.
|
31
|
+
# Also tests the random(n,m) method, which maps a sequence value to n..m
|
32
|
+
|
33
|
+
def test_02_PRNG
|
34
|
+
p = PRNG.new(81, 337, 1000)
|
35
|
+
assert_equal 0, p.state
|
36
|
+
assert_equal 337, p.advance
|
37
|
+
assert_equal 634, p.advance
|
38
|
+
|
39
|
+
p.seed(0)
|
40
|
+
assert_equal 0, p.state
|
41
|
+
assert_equal 337, p.advance
|
42
|
+
assert_equal 634, p.advance
|
43
|
+
|
44
|
+
p.seed(0)
|
45
|
+
assert_equal 2, p.random(1,6) # (337 % 6) + 1
|
46
|
+
assert_equal 5, p.random(1,6) # (634 % 6) + 1
|
47
|
+
end
|
7
48
|
|
8
49
|
# For the Card tests, a call to the Card constructor with an integer argument
|
9
50
|
# makes a specified card, starting with 0 = A ♠ and ending with 51 = 2 ♣.
|
51
|
+
|
52
|
+
def test_03_cards
|
53
|
+
c1 = Card.new(0)
|
54
|
+
assert_equal :ace, c1.rank
|
55
|
+
assert_equal :spades, c1.suit
|
56
|
+
|
57
|
+
c2 = Card.new(51)
|
58
|
+
assert_equal :two, c2.rank
|
59
|
+
assert_equal :clubs, c2.suit
|
60
|
+
end
|
10
61
|
|
11
62
|
# Make a full deck, make sure every card is present. Then shuffle the deck,
|
12
63
|
# check again. Sort the shuffled deck, make sure all cards are there and in
|
13
64
|
# order.
|
14
65
|
|
15
|
-
def
|
16
|
-
|
17
|
-
check_all_cards(
|
18
|
-
|
19
|
-
check_all_cards(
|
20
|
-
|
21
|
-
|
22
|
-
assert_equal deck1, deck3
|
66
|
+
def test_04_deck
|
67
|
+
d = new_deck
|
68
|
+
check_all_cards(d)
|
69
|
+
permute(d)
|
70
|
+
check_all_cards(d)
|
71
|
+
d.sort!
|
72
|
+
assert_equal new_deck, d
|
23
73
|
end
|
24
74
|
|
25
75
|
# Make one of each kind of poker hand.
|
26
76
|
|
27
|
-
def
|
28
|
-
|
29
|
-
assert_equal
|
30
|
-
assert_equal
|
31
|
-
assert_equal
|
32
|
-
assert_equal
|
33
|
-
assert_equal
|
34
|
-
assert_equal
|
35
|
-
assert_equal
|
36
|
-
assert_equal
|
37
|
-
assert_equal poker_hand(deal([0,14,29,44,45])), :nada
|
77
|
+
def test_05_poker_ranks
|
78
|
+
assert_equal poker_rank(deal([0,1,2,3,4])), :straight_flush
|
79
|
+
assert_equal poker_rank(deal([0,13,26,39,1])), :four_of_a_kind
|
80
|
+
assert_equal poker_rank(deal([0,13,26,1,14])), :full_house
|
81
|
+
assert_equal poker_rank(deal([0,2,4,6,8])), :flush
|
82
|
+
assert_equal poker_rank(deal([0,14,2,3,4])), :straight
|
83
|
+
assert_equal poker_rank(deal([0,13,26,1,15])), :three_of_a_kind
|
84
|
+
assert_equal poker_rank(deal([0,13,1,14,15])), :two_pair
|
85
|
+
assert_equal poker_rank(deal([0,13,1,2,3])), :pair
|
86
|
+
assert_equal poker_rank(deal([0,14,29,44,45])), :high_card
|
38
87
|
end
|
39
88
|
|
40
89
|
|
@@ -43,6 +92,9 @@ class TestRandoms < Test::Unit::TestCase
|
|
43
92
|
and initialized with a count of 0.
|
44
93
|
|
45
94
|
+check_all_cards+ makes sure there are 13 of each suit and 4 of each rank.
|
95
|
+
|
96
|
+
+deal(a)+ maps all ints in +a+ into the corresponding Card (used to create
|
97
|
+
a known hand for testing poker ranks)
|
46
98
|
=end
|
47
99
|
|
48
100
|
def bins(a)
|
@@ -60,5 +112,9 @@ class TestRandoms < Test::Unit::TestCase
|
|
60
112
|
rb.each { |x,n| assert_equal n, 4 }
|
61
113
|
sb.each { |x,n| assert_equal n, 13 }
|
62
114
|
end
|
115
|
+
|
116
|
+
def deal(a)
|
117
|
+
return a.map { |x| Card.new(x) }
|
118
|
+
end
|
63
119
|
|
64
120
|
end
|