rubylabs 0.5.5 → 0.6.2

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/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
- Return a copy of object x with the elements in a new, scrambled order. The
69
- parameter x can be any object that has an index operator (e.g. strings or
70
- arrays).
71
- =end
72
-
73
- def permutation(x)
74
- res = x.clone
75
- for i in 0..res.length-2
76
- r = rand(res.length-i) + i
77
- res[i], res[r] = res[r], res[i]
78
- end
79
- return res
80
- end
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 test_01_decks
16
- deck1 = deal((0..51).to_a)
17
- check_all_cards(deck1)
18
- deck2 = shuffle
19
- check_all_cards(deck2)
20
- deck3 = deck2.sort
21
- check_all_cards(deck3)
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 test_02_poker_hands
28
- print "\n poker hands"
29
- assert_equal poker_hand(deal([0,1,2,3,4])), :straight_flush
30
- assert_equal poker_hand(deal([0,13,26,39,1])), :four_of_a_kind
31
- assert_equal poker_hand(deal([0,13,26,1,14])), :full_house
32
- assert_equal poker_hand(deal([0,2,4,6,8])), :flush
33
- assert_equal poker_hand(deal([0,14,2,3,4])), :straight
34
- assert_equal poker_hand(deal([0,13,26,1,15])), :three_of_a_kind
35
- assert_equal poker_hand(deal([0,13,1,14,15])), :two_pair
36
- assert_equal poker_hand(deal([0,13,1,2,3])), :pair
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