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/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