chrislo-sourceclassifier 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (326) hide show
  1. data/Manifest +325 -0
  2. data/README.textile +51 -0
  3. data/Rakefile +64 -0
  4. data/examples/example.rb +22 -0
  5. data/lib/sourceclassifier.rb +22 -0
  6. data/lib/trainer.rb +51 -0
  7. data/sourceclassifier.gemspec +35 -0
  8. data/sources/gcc/ackermann.gcc-2.gcc +93 -0
  9. data/sources/gcc/ackermann.gcc-3.gcc +20 -0
  10. data/sources/gcc/ary.gcc +40 -0
  11. data/sources/gcc/binarytrees.gcc +136 -0
  12. data/sources/gcc/binarytrees.gcc-2.gcc +140 -0
  13. data/sources/gcc/binarytrees.gcc-3.gcc +134 -0
  14. data/sources/gcc/chameneos.gcc +117 -0
  15. data/sources/gcc/chameneos.gcc-2.gcc +134 -0
  16. data/sources/gcc/chameneos.gcc-3.gcc +120 -0
  17. data/sources/gcc/chameneosredux.gcc +306 -0
  18. data/sources/gcc/echo.gcc +144 -0
  19. data/sources/gcc/except.gcc +52 -0
  20. data/sources/gcc/fannkuch.gcc +105 -0
  21. data/sources/gcc/fannkuch.gcc-2.gcc +94 -0
  22. data/sources/gcc/fasta.gcc +158 -0
  23. data/sources/gcc/fasta.gcc-2.gcc +132 -0
  24. data/sources/gcc/fasta.gcc-3.gcc +145 -0
  25. data/sources/gcc/fasta.gcc-4.gcc +141 -0
  26. data/sources/gcc/fibo.gcc +20 -0
  27. data/sources/gcc/harmonic.gcc +22 -0
  28. data/sources/gcc/hash.gcc +31 -0
  29. data/sources/gcc/hash2.gcc +38 -0
  30. data/sources/gcc/heapsort.gcc +72 -0
  31. data/sources/gcc/hello.gcc-2.gcc +12 -0
  32. data/sources/gcc/hello.gcc-3.gcc +20 -0
  33. data/sources/gcc/knucleotide.gcc +173 -0
  34. data/sources/gcc/knucleotide.gcc-2.gcc +168 -0
  35. data/sources/gcc/lists.gcc +226 -0
  36. data/sources/gcc/magicsquares.gcc +378 -0
  37. data/sources/gcc/mandelbrot.gcc +84 -0
  38. data/sources/gcc/mandelbrot.gcc-2.gcc +63 -0
  39. data/sources/gcc/mandelbrot.gcc-3.gcc +91 -0
  40. data/sources/gcc/mandelbrot.gcc-4.gcc +78 -0
  41. data/sources/gcc/matrix.gcc +66 -0
  42. data/sources/gcc/message.gcc-2.gcc +72 -0
  43. data/sources/gcc/message.gcc-3.gcc +64 -0
  44. data/sources/gcc/methcall.gcc +89 -0
  45. data/sources/gcc/moments.gcc +120 -0
  46. data/sources/gcc/nbody.gcc +142 -0
  47. data/sources/gcc/nbody.gcc-2.gcc +143 -0
  48. data/sources/gcc/nbody.gcc-3.gcc +142 -0
  49. data/sources/gcc/nestedloop.gcc +25 -0
  50. data/sources/gcc/nsieve.gcc +35 -0
  51. data/sources/gcc/nsievebits.gcc-2.gcc +37 -0
  52. data/sources/gcc/nsievebits.gcc-3.gcc +53 -0
  53. data/sources/gcc/objinst.gcc +95 -0
  54. data/sources/gcc/partialsums.gcc +86 -0
  55. data/sources/gcc/partialsums.gcc-3.gcc +81 -0
  56. data/sources/gcc/partialsums.gcc-4.gcc +68 -0
  57. data/sources/gcc/pidigits.gcc +105 -0
  58. data/sources/gcc/primes.gcc +75 -0
  59. data/sources/gcc/prodcons.gcc +86 -0
  60. data/sources/gcc/random.gcc +29 -0
  61. data/sources/gcc/raytracer.gcc +125 -0
  62. data/sources/gcc/raytracer.gcc-2.gcc +181 -0
  63. data/sources/gcc/recursive.gcc +68 -0
  64. data/sources/gcc/recursive.gcc-2.gcc +55 -0
  65. data/sources/gcc/regexdna.gcc-2.gcc +126 -0
  66. data/sources/gcc/regexmatch.gcc +136 -0
  67. data/sources/gcc/revcomp.gcc +85 -0
  68. data/sources/gcc/revcomp.gcc-2.gcc +88 -0
  69. data/sources/gcc/revcomp.gcc-4.gcc +71 -0
  70. data/sources/gcc/reversefile.gcc +103 -0
  71. data/sources/gcc/reversefile.gcc-2.gcc +56 -0
  72. data/sources/gcc/sieve.gcc +34 -0
  73. data/sources/gcc/spectralnorm.gcc +54 -0
  74. data/sources/gcc/spellcheck.gcc +72 -0
  75. data/sources/gcc/spellcheck.gcc-2.gcc +61 -0
  76. data/sources/gcc/strcat.gcc +38 -0
  77. data/sources/gcc/sumcol.gcc-2.gcc +98 -0
  78. data/sources/gcc/sumcol.gcc-3.gcc +22 -0
  79. data/sources/gcc/sumcol.gcc-4.gcc +18 -0
  80. data/sources/gcc/sumcol.gcc-5.gcc +32 -0
  81. data/sources/gcc/takfp.gcc +23 -0
  82. data/sources/gcc/tcp-stream.gcc +122 -0
  83. data/sources/gcc/tcpecho.gcc +122 -0
  84. data/sources/gcc/tcpecho.gcc-2.gcc +136 -0
  85. data/sources/gcc/tcprequest.gcc +122 -0
  86. data/sources/gcc/threadring.gcc +73 -0
  87. data/sources/gcc/wc.gcc +52 -0
  88. data/sources/gcc/wc.gcc-2.gcc +40 -0
  89. data/sources/gcc/wc.gcc-3.gcc +46 -0
  90. data/sources/gcc/wordfreq.gcc-2.gcc +85 -0
  91. data/sources/perl/ackermann.perl +28 -0
  92. data/sources/perl/ackermann.perl-2.perl +25 -0
  93. data/sources/perl/ackermann.perl-3.perl +20 -0
  94. data/sources/perl/ackermann.perl-4.perl +18 -0
  95. data/sources/perl/ary.perl +25 -0
  96. data/sources/perl/ary.perl-2.perl +23 -0
  97. data/sources/perl/binarytrees.perl +66 -0
  98. data/sources/perl/binarytrees.perl-2.perl +71 -0
  99. data/sources/perl/chameneos.perl +67 -0
  100. data/sources/perl/echo.perl +81 -0
  101. data/sources/perl/except.perl +73 -0
  102. data/sources/perl/fannkuch.perl +44 -0
  103. data/sources/perl/fannkuch.perl-2.perl +38 -0
  104. data/sources/perl/fasta.perl +112 -0
  105. data/sources/perl/fasta.perl-2.perl +135 -0
  106. data/sources/perl/fasta.perl-4.perl +122 -0
  107. data/sources/perl/fibo.perl +15 -0
  108. data/sources/perl/fibo.perl-2.perl +0 -0
  109. data/sources/perl/fibo.perl-3.perl +0 -0
  110. data/sources/perl/harmonic.perl +7 -0
  111. data/sources/perl/hash.perl +23 -0
  112. data/sources/perl/hash.perl-2.perl +17 -0
  113. data/sources/perl/hash.perl-3.perl +24 -0
  114. data/sources/perl/hash2.perl +16 -0
  115. data/sources/perl/heapsort.perl +65 -0
  116. data/sources/perl/heapsort.perl-2.perl +0 -0
  117. data/sources/perl/hello.perl +5 -0
  118. data/sources/perl/knucleotide.perl-2.perl +30 -0
  119. data/sources/perl/lists.perl +48 -0
  120. data/sources/perl/mandelbrot.perl-2.perl +32 -0
  121. data/sources/perl/matrix.perl +59 -0
  122. data/sources/perl/matrix.perl-2.perl +0 -0
  123. data/sources/perl/matrix.perl-3.perl +0 -0
  124. data/sources/perl/message.perl +27 -0
  125. data/sources/perl/methcall.perl +66 -0
  126. data/sources/perl/moments.perl +44 -0
  127. data/sources/perl/nbody.perl +108 -0
  128. data/sources/perl/nestedloop.perl +28 -0
  129. data/sources/perl/nsieve.perl-2.perl +41 -0
  130. data/sources/perl/nsieve.perl-4.perl +43 -0
  131. data/sources/perl/nsievebits.perl +37 -0
  132. data/sources/perl/objinst.perl +73 -0
  133. data/sources/perl/partialsums.perl-3.perl +31 -0
  134. data/sources/perl/pidigits.perl +52 -0
  135. data/sources/perl/pidigits.perl-2.perl +47 -0
  136. data/sources/perl/process.perl +50 -0
  137. data/sources/perl/prodcons.perl +47 -0
  138. data/sources/perl/random.perl-4.perl +17 -0
  139. data/sources/perl/recursive.perl-2.perl +57 -0
  140. data/sources/perl/regexdna.perl +48 -0
  141. data/sources/perl/regexdna.perl-2.perl +43 -0
  142. data/sources/perl/regexdna.perl-3.perl +50 -0
  143. data/sources/perl/regexdna.perl-4.perl +49 -0
  144. data/sources/perl/regexdna.perl-5.perl +42 -0
  145. data/sources/perl/regexdna.perl-6.perl +43 -0
  146. data/sources/perl/regexmatch.perl +35 -0
  147. data/sources/perl/revcomp.perl-2.perl +34 -0
  148. data/sources/perl/reversefile.perl +8 -0
  149. data/sources/perl/reversefile.perl-2.perl +0 -0
  150. data/sources/perl/reversefile.perl-3.perl +0 -0
  151. data/sources/perl/sieve.perl +23 -0
  152. data/sources/perl/spectralnorm.perl-2.perl +54 -0
  153. data/sources/perl/spellcheck.perl +24 -0
  154. data/sources/perl/strcat.perl +13 -0
  155. data/sources/perl/strcat.perl-2.perl +0 -0
  156. data/sources/perl/sumcol.perl +8 -0
  157. data/sources/perl/takfp.perl +23 -0
  158. data/sources/perl/takfp.perl-3.perl +20 -0
  159. data/sources/perl/tcpecho.perl +61 -0
  160. data/sources/perl/tcprequest.perl +61 -0
  161. data/sources/perl/tcpstream.perl +61 -0
  162. data/sources/perl/threadring.perl +55 -0
  163. data/sources/perl/threadring.perl-2.perl +43 -0
  164. data/sources/perl/wc.perl +20 -0
  165. data/sources/perl/wc.perl-2.perl +14 -0
  166. data/sources/perl/wordfreq.perl +22 -0
  167. data/sources/perl/wordfreq.perl-3.perl +0 -0
  168. data/sources/perl/wordfreq.perl3.perl +0 -0
  169. data/sources/python/ackermann.python +21 -0
  170. data/sources/python/ary.python +19 -0
  171. data/sources/python/binarytrees.python +39 -0
  172. data/sources/python/binarytrees.python-3.python +44 -0
  173. data/sources/python/chameneos.python-6.python +73 -0
  174. data/sources/python/chameneosredux.python +126 -0
  175. data/sources/python/chameneosredux.python-2.python +122 -0
  176. data/sources/python/dispatch.python +176 -0
  177. data/sources/python/dispatch.python-2.python +136 -0
  178. data/sources/python/echo.python +64 -0
  179. data/sources/python/except.python +62 -0
  180. data/sources/python/fannkuch.python +50 -0
  181. data/sources/python/fannkuch.python-2.python +54 -0
  182. data/sources/python/fasta.python-2.python +79 -0
  183. data/sources/python/fibo.python +17 -0
  184. data/sources/python/fibo.python-2.python +0 -0
  185. data/sources/python/fibo.python-3.python +0 -0
  186. data/sources/python/harmonic.python-2.python +9 -0
  187. data/sources/python/hash.python +21 -0
  188. data/sources/python/hash.python-2.python +0 -0
  189. data/sources/python/hash2.python +30 -0
  190. data/sources/python/heapsort.python-3.python +66 -0
  191. data/sources/python/hello.python +5 -0
  192. data/sources/python/implicitode.python +231 -0
  193. data/sources/python/knucleotide.python +55 -0
  194. data/sources/python/lists.python +44 -0
  195. data/sources/python/magicsquares.python +145 -0
  196. data/sources/python/mandelbrot.python +44 -0
  197. data/sources/python/mandelbrot.python-2.python +35 -0
  198. data/sources/python/mandelbrot.python-3.python +46 -0
  199. data/sources/python/matrix.python +34 -0
  200. data/sources/python/matrix.python-2.python +23 -0
  201. data/sources/python/message.python +24 -0
  202. data/sources/python/message.python-2.python +20 -0
  203. data/sources/python/message.python-3.python +19 -0
  204. data/sources/python/meteor.python +210 -0
  205. data/sources/python/meteor.python-2.python +192 -0
  206. data/sources/python/methcall.python +51 -0
  207. data/sources/python/moments.python +65 -0
  208. data/sources/python/nbody.python +123 -0
  209. data/sources/python/nbody.python-2.python +120 -0
  210. data/sources/python/nestedloop.python +24 -0
  211. data/sources/python/nsieve.python +27 -0
  212. data/sources/python/nsieve.python-2.python +23 -0
  213. data/sources/python/nsieve.python-4.python +25 -0
  214. data/sources/python/nsievebits.python +27 -0
  215. data/sources/python/nsievebits.python-2.python +43 -0
  216. data/sources/python/objinst.python +53 -0
  217. data/sources/python/partialsums.python +37 -0
  218. data/sources/python/partialsums.python-2.python +35 -0
  219. data/sources/python/partialsums.python-3.python +48 -0
  220. data/sources/python/pidigits.python +38 -0
  221. data/sources/python/pidigits.python-3.python +63 -0
  222. data/sources/python/pidigits.python-4.python +24 -0
  223. data/sources/python/process.python +51 -0
  224. data/sources/python/process.python-2.python +133 -0
  225. data/sources/python/prodcons.python +51 -0
  226. data/sources/python/prodcons.python-2.python +0 -0
  227. data/sources/python/random.python +27 -0
  228. data/sources/python/raytracer.python +203 -0
  229. data/sources/python/recursive.python +35 -0
  230. data/sources/python/regexdna.python +39 -0
  231. data/sources/python/regexdna.python-2.python +34 -0
  232. data/sources/python/regexmatch.python +36 -0
  233. data/sources/python/revcomp.python-3.python +31 -0
  234. data/sources/python/reversefile.python +13 -0
  235. data/sources/python/reversefile.python-2.python +0 -0
  236. data/sources/python/reversefile.python-3.python +0 -0
  237. data/sources/python/sieve.python +50 -0
  238. data/sources/python/spectralnorm.python-2.python +36 -0
  239. data/sources/python/spellcheck.python +17 -0
  240. data/sources/python/strcat.python +35 -0
  241. data/sources/python/strcat.python-2.python +0 -0
  242. data/sources/python/sumcol.python-2.python +0 -0
  243. data/sources/python/sumcol.python-3.python +0 -0
  244. data/sources/python/takfp.python +19 -0
  245. data/sources/python/tcpecho.python +67 -0
  246. data/sources/python/tcprequest.python +67 -0
  247. data/sources/python/tcpstream.python +67 -0
  248. data/sources/python/threadring.python +47 -0
  249. data/sources/python/threadring.python-2.python +40 -0
  250. data/sources/python/threadring.python-3.python +34 -0
  251. data/sources/python/wc.python-2.python +19 -0
  252. data/sources/python/wordfreq.python +43 -0
  253. data/sources/python/wordfreq.python-2.python +0 -0
  254. data/sources/python/wordfreq.python-3.python +28 -0
  255. data/sources/python/wordfreq.python-4.python +38 -0
  256. data/sources/python/wordfreq.python-5.python +39 -0
  257. data/sources/ruby/ackermann.ruby +17 -0
  258. data/sources/ruby/ackermann.ruby-5.ruby +153 -0
  259. data/sources/ruby/ary.ruby +22 -0
  260. data/sources/ruby/binarytrees.ruby-2.ruby +55 -0
  261. data/sources/ruby/chameneos.ruby-2.ruby +71 -0
  262. data/sources/ruby/dispatch.ruby +114 -0
  263. data/sources/ruby/echo.ruby +41 -0
  264. data/sources/ruby/except.ruby +61 -0
  265. data/sources/ruby/except.ruby-2.ruby +61 -0
  266. data/sources/ruby/fannkuch.ruby +42 -0
  267. data/sources/ruby/fasta.ruby +81 -0
  268. data/sources/ruby/fibo.ruby +15 -0
  269. data/sources/ruby/harmonic.ruby-2.ruby +15 -0
  270. data/sources/ruby/hash.ruby +19 -0
  271. data/sources/ruby/hash2.ruby +23 -0
  272. data/sources/ruby/heapsort.ruby +55 -0
  273. data/sources/ruby/hello.ruby +6 -0
  274. data/sources/ruby/knucleotide.ruby-2.ruby +44 -0
  275. data/sources/ruby/lists.ruby +46 -0
  276. data/sources/ruby/mandelbrot.ruby-3.ruby +63 -0
  277. data/sources/ruby/matrix.ruby +40 -0
  278. data/sources/ruby/matrix.ruby-2.ruby +30 -0
  279. data/sources/ruby/message.ruby +29 -0
  280. data/sources/ruby/message.ruby-2.ruby +24 -0
  281. data/sources/ruby/meteor.ruby +386 -0
  282. data/sources/ruby/meteor.ruby-2.ruby +561 -0
  283. data/sources/ruby/methcall.ruby +58 -0
  284. data/sources/ruby/methcall.ruby-2.ruby +54 -0
  285. data/sources/ruby/moments.ruby +64 -0
  286. data/sources/ruby/nbody.ruby-2.ruby +145 -0
  287. data/sources/ruby/nestedloop.ruby +22 -0
  288. data/sources/ruby/nsieve.ruby +36 -0
  289. data/sources/ruby/nsieve.ruby-2.ruby +25 -0
  290. data/sources/ruby/nsievebits.ruby-2.ruby +42 -0
  291. data/sources/ruby/objinst.ruby +58 -0
  292. data/sources/ruby/partialsums.ruby +39 -0
  293. data/sources/ruby/pidigits.ruby +92 -0
  294. data/sources/ruby/pidigits.ruby-2.ruby +109 -0
  295. data/sources/ruby/prodcons.ruby +41 -0
  296. data/sources/ruby/random.ruby +17 -0
  297. data/sources/ruby/recursive.ruby-2.ruby +53 -0
  298. data/sources/ruby/regexdna.ruby +32 -0
  299. data/sources/ruby/regexdna.ruby-2.ruby +38 -0
  300. data/sources/ruby/regexmatch.ruby +33 -0
  301. data/sources/ruby/revcomp.ruby +28 -0
  302. data/sources/ruby/reversefile.ruby +7 -0
  303. data/sources/ruby/sieve.ruby +30 -0
  304. data/sources/ruby/spectralnorm.ruby +48 -0
  305. data/sources/ruby/spellcheck.ruby +18 -0
  306. data/sources/ruby/spellcheck.ruby-2.ruby +0 -0
  307. data/sources/ruby/strcat.ruby +12 -0
  308. data/sources/ruby/strcat.ruby-2.ruby +12 -0
  309. data/sources/ruby/sumcol.ruby +12 -0
  310. data/sources/ruby/sumcol.ruby-2.ruby +5 -0
  311. data/sources/ruby/takfp.ruby +15 -0
  312. data/sources/ruby/tcpecho.ruby +45 -0
  313. data/sources/ruby/tcprequest.ruby +45 -0
  314. data/sources/ruby/tcpstream.ruby +45 -0
  315. data/sources/ruby/threadring.ruby +61 -0
  316. data/sources/ruby/threadring.ruby-2.ruby +33 -0
  317. data/sources/ruby/wc.ruby +15 -0
  318. data/sources/ruby/wordfreq.ruby +17 -0
  319. data/sources/ruby/wordfreq.ruby2.ruby +0 -0
  320. data/test/fixtures/sources/gcc/ackermann.gcc-2.gcc +93 -0
  321. data/test/fixtures/sources/python/ackermann.python +21 -0
  322. data/test/fixtures/sources/ruby/ackermann.ruby +17 -0
  323. data/test/test_source_classifier.rb +40 -0
  324. data/test/test_trainer.rb +34 -0
  325. data/trainer.bin +1193 -0
  326. metadata +393 -0
@@ -0,0 +1,29 @@
1
+ # The Computer Language Shootout
2
+ # http://shootout.alioth.debian.org/
3
+ #
4
+ # Contributed by Jesse Millikan
5
+
6
+ require 'thread'
7
+
8
+ N = ARGV[0].to_i
9
+ next_q = last_q = SizedQueue.new(1)
10
+
11
+ 500.times {
12
+ q = SizedQueue.new(1)
13
+ q2 = next_q
14
+ Thread.new{
15
+ i = N
16
+ while i > 0
17
+ q2.push(q.pop+1)
18
+ i -= 1
19
+ end
20
+ }
21
+ next_q = q
22
+ }
23
+
24
+ Thread.new{N.times{next_q.push(0)}}
25
+
26
+ t = 0
27
+ N.times{t+=last_q.pop}
28
+ puts t
29
+
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Gonzalo Garramuno -- Dec.31 2006
4
+ #
5
+
6
+ def coroutine(n)
7
+ if n > 1
8
+ coroutine(n-1) { |x| yield x + 1 }
9
+ else
10
+ yield 1 while true
11
+ end
12
+ end
13
+
14
+ iter = 0
15
+ last = ARGV[0].to_i
16
+ count = 0
17
+
18
+ coroutine( 500 ) { |x|
19
+ break if iter >= last
20
+ count += x
21
+ iter += 1
22
+ }
23
+
24
+ puts count
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The Computer Language Shootout
4
+ # http://shootout.alioth.debian.org
5
+ # contributed by Kevin Barnes (Ruby novice)
6
+
7
+ def blank_board
8
+ 0b111111100000100000100000100000100000100000100000100000100000100000
9
+ end
10
+
11
+ def is_even( location)
12
+ (location % 12) < 6
13
+ end
14
+
15
+ def create_collector_support
16
+ odd_map = [0b11, 0b110, 0b1100, 0b11000, 0b10000]
17
+ even_map = [0b1, 0b11, 0b110, 0b1100, 0b11000]
18
+
19
+ all_odds = Array.new(0b100000)
20
+ all_evens = Array.new(0b100000)
21
+ bit_counts = Array.new(0b100000)
22
+ new_regions = Array.new(0b100000)
23
+ 0.upto(0b11111) do | i |
24
+ bit_count = odd = even = 0
25
+ 0.upto(4) do | bit |
26
+ if (i[bit] == 1) then
27
+ bit_count += 1
28
+ odd |= odd_map[bit]
29
+ even |= even_map[bit]
30
+ end
31
+ end
32
+ all_odds[i] = odd
33
+ all_evens[i] = even
34
+ bit_counts[i] = bit_count
35
+ new_regions[i] = create_regions( i)
36
+ end
37
+
38
+ @@converter = []
39
+ 10.times { | row | @@converter.push((row % 2 == 0) ? all_evens : all_odds) }
40
+ @@bit_counts = bit_counts
41
+ @@regions = new_regions.collect { | set | set.collect { | value | [ value, bit_counts[value], value] } }
42
+
43
+ end
44
+
45
+ def prunable( board, location, slotting = false)
46
+ collectors = []
47
+ (location / 6).to_i.upto(9) do | row_on |
48
+ regions = @@regions[(board >> (row_on * 6)) & 0b11111 ^ 0b11111]
49
+ converter = @@converter[row_on]
50
+ initial_collector_count = collectors.length
51
+ regions.each do | region |
52
+ collector_found = nil
53
+ region_mask = region[0]
54
+ initial_collector_count.times do | collector_num |
55
+ collector = collectors[collector_num]
56
+ if (collector) then
57
+ collector_mask = collector[0]
58
+ if (collector_mask & region_mask != 0) then
59
+ if (collector_found) then
60
+ collector_found[0] |= collector_mask
61
+ collector_found[1] += collector[1]
62
+ collector_found[2] |= collector[2]
63
+ collectors[collector_num] = nil
64
+ else
65
+ collector_found = collector
66
+ collector[1] += region[1]
67
+ collector[2] |= region_mask
68
+ end
69
+ end
70
+ end
71
+ end
72
+ if (collector_found == nil) then
73
+ collectors.push(Array.new(region))
74
+ end
75
+ end
76
+ collectors.length.times do | collector_num |
77
+ collector = collectors[collector_num]
78
+ if (collector) then
79
+ if (collector[2] == 0) then
80
+ return true if (collector[1] % 5 != 0)
81
+ collectors[collector_num] = nil
82
+ else
83
+ return false if (collector[2] == 0b11111 && !slotting)
84
+ collector[0] = converter[collector[2]]
85
+ collector[2] = 0
86
+ end
87
+ end
88
+ end
89
+ collectors.compact!
90
+ end
91
+ return false if (collectors.length <= 1)
92
+ collectors.any? { | collector | (collector[1] % 5) != 0 }
93
+ end
94
+
95
+ def as_binary( value)
96
+ rtn = ""
97
+ 5.times do | i |
98
+ rtn += "#{value[i]}"
99
+ end
100
+ rtn
101
+ end
102
+
103
+ def create_regions( value )
104
+ regions = []
105
+ cur_region = 0
106
+ 5.times do | bit |
107
+ if (value[bit] == 1) then
108
+ cur_region |= 1 << bit
109
+ else
110
+ if (cur_region !=0 ) then
111
+ regions.push( cur_region)
112
+ cur_region = 0;
113
+ end
114
+ end
115
+ end
116
+ regions.push(cur_region) if (cur_region != 0)
117
+ regions
118
+ end
119
+
120
+ def print_board( board, padding = "", rows = 10, row_offset = 0)
121
+ rows.times do | row |
122
+ rtn = padding
123
+ rtn = "#{rtn} " if ((row + row_offset) % 2) == 1
124
+ 6.times do | col |
125
+ rtn = "#{rtn}#{board[row*6+col]} "
126
+ end
127
+ print "#{rtn}\n"
128
+ end
129
+ end
130
+
131
+ class Rotation
132
+ attr_reader :start_masks
133
+
134
+ @@rotation_even_adder = { :west => -1, :east => 1, :nw => -7, :ne => -6, :sw => 5, :se => 6 }
135
+ @@rotation_odd_adder = { :west => -1, :east => 1, :nw => -6, :ne => -5, :sw => 6, :se => 7 }
136
+
137
+ def initialize( directions )
138
+ values, min = get_values( directions )
139
+ @even_offsets, @odd_offsets = normalize_offsets( values, min)
140
+
141
+ @even_mask = mask_for_offsets( @even_offsets)
142
+ @odd_mask = mask_for_offsets( @odd_offsets)
143
+
144
+ @start_masks = Array.new(60)
145
+
146
+ 0.upto(59) do | offset |
147
+ mask = is_even(offset) ? (@even_mask << offset) : (@odd_mask << offset)
148
+ if (blank_board & mask == 0 && !prunable(blank_board | mask, 0, true)) then
149
+ @start_masks[offset] = mask
150
+ else
151
+ @start_masks[offset] = false
152
+ end
153
+ end
154
+ end
155
+
156
+ def offsets( location)
157
+ if is_even( location) then
158
+ @even_offsets.collect { | value | value + location }
159
+ else
160
+ @odd_offsets.collect { | value | value + location }
161
+ end
162
+ end
163
+
164
+ def normalize_offsets( values, min)
165
+ even_min = is_even(min)
166
+ other_min = even_min ? min + 6 : min + 7
167
+ other_values = values.collect do | value |
168
+ if is_even(value) then
169
+ value + 6 - other_min
170
+ else
171
+ value + 7 - other_min
172
+ end
173
+ end
174
+ values.collect! { | value | value - min }
175
+
176
+ if even_min then
177
+ [values, other_values]
178
+ else
179
+ [other_values, values]
180
+ end
181
+ end
182
+
183
+ def mask_for_offsets( offsets )
184
+ mask = 0
185
+ offsets.each { | value | mask = mask + ( 1 << value ) }
186
+ mask
187
+ end
188
+
189
+ def start_adjust( directions )
190
+ south = east = 0;
191
+ directions.each do | direction |
192
+ east += 1 if ( direction == :sw || direction == :nw || direction == :west )
193
+ south += 1 if ( direction == :nw || direction == :ne )
194
+ end
195
+ [south, east]
196
+ end
197
+
198
+ def get_values ( directions )
199
+ south, east = start_adjust(directions)
200
+ min = start = south * 6 + east
201
+ values = [ start ]
202
+ directions.each do | direction |
203
+ if (start % 12 >= 6) then
204
+ start += @@rotation_odd_adder[direction]
205
+ else
206
+ start += @@rotation_even_adder[direction]
207
+ end
208
+ min = start if (start < min)
209
+ values += [ start ]
210
+ end
211
+
212
+ if (values.length != 5)
213
+ values.uniq!
214
+ end
215
+
216
+ [ values, min ]
217
+ end
218
+ end
219
+
220
+ class Piece
221
+ attr_reader :rotations, :type, :masks
222
+ attr_accessor :placed
223
+
224
+ @@flip_converter = { :west => :west, :east => :east, :nw => :sw, :ne => :se, :sw => :nw, :se => :ne }
225
+ @@rotate_converter = { :west => :nw, :east => :se, :nw => :ne, :ne => :east, :sw => :west, :se => :sw }
226
+
227
+ def initialize( directions, type )
228
+ @type = type
229
+ @rotations = Array.new();
230
+ @map = {}
231
+ generate_rotations( directions )
232
+ directions.collect! { | value | @@flip_converter[value] }
233
+ generate_rotations( directions )
234
+
235
+ @masks = Array.new();
236
+ 0.upto(59) do | i |
237
+ @masks[i] = @rotations.collect do | rotation |
238
+ mask = rotation.start_masks[i]
239
+ @map[mask] = [ i, rotation ] if (mask)
240
+ mask || nil
241
+ end
242
+ @masks[i].compact!
243
+ end
244
+ end
245
+
246
+ def generate_rotations( directions )
247
+ 6.times do
248
+ rotations.push( Rotation.new(directions))
249
+ directions.collect! { | value | @@rotate_converter[value] }
250
+ end
251
+ end
252
+
253
+ def fill_array( board_array)
254
+ location, rotation = @map[@placed]
255
+ rotation.offsets(location).each do | offset |
256
+ row, col = offset.divmod(6)
257
+ board_array[ row*5 + col ] = @type.to_s
258
+ end
259
+ end
260
+ end
261
+
262
+ class Processor
263
+ attr :pieces, :board
264
+
265
+ def initialize()
266
+ create_collector_support
267
+ @pieces = [
268
+ Piece.new( [ :east, :east, :east, :se ], 0),
269
+ Piece.new( [ :ne, :east, :ne, :nw ], 1),
270
+ Piece.new( [ :nw, :ne, :east, :east ], 2),
271
+ Piece.new( [ :east, :east, :sw, :se ], 3),
272
+ Piece.new( [ :ne, :nw, :se, :east, :se ], 4),
273
+ Piece.new( [ :east, :ne, :se, :ne ], 5),
274
+ Piece.new( [ :east, :sw, :sw, :se ], 6),
275
+ Piece.new( [ :ne, :se, :east, :ne ], 7),
276
+ Piece.new( [ :se, :se, :east, :se ], 8),
277
+ Piece.new( [ :se, :se, :se, :west ], 9) ];
278
+
279
+ @all_pieces = Array.new( @pieces)
280
+
281
+ @min_board = "99999999999999999999999999999999999999999999999999"
282
+ @max_board = "00000000000000000000000000000000000000000000000000"
283
+ @stop_count = ARGV[0].to_i || 2089
284
+ @all_boards = {}
285
+ @boards_found = 0
286
+ end
287
+
288
+ def find_all
289
+ find_top( 0)
290
+ find_top( 1)
291
+ print_results
292
+ end
293
+
294
+ def print_results
295
+ print "#{@boards_found} solutions found\n\n"
296
+ print_full_board( @min_board)
297
+ print "\n"
298
+ print_full_board( @max_board)
299
+ print "\n"
300
+ end
301
+
302
+ def find_top( rotation_skip)
303
+ board = blank_board
304
+ @pieces.length.times do
305
+ piece = @pieces.shift
306
+ piece.masks[0].each do | mask |
307
+ if ((rotation_skip += 1) % 2 == 0) then
308
+ piece.placed = mask
309
+ find( 1, 1, board | mask)
310
+ end
311
+ end
312
+ @pieces.push(piece)
313
+ end
314
+ end
315
+
316
+ def find( start_location, placed, board)
317
+ while board[start_location] == 1
318
+ start_location += 1
319
+ end
320
+
321
+ return if (start_location < 28 && prunable( board, start_location))
322
+
323
+ @pieces.length.times do
324
+ piece = @pieces.shift
325
+ piece.masks[start_location].each do | mask |
326
+ if (mask & board == 0) then
327
+ piece.placed = mask
328
+ if (placed == 9) then
329
+ add_board
330
+ else
331
+ find( start_location + 1, placed + 1, board | mask)
332
+ end
333
+ end
334
+ end
335
+ @pieces.push(piece)
336
+ end
337
+ end
338
+
339
+ def print_full_board( board_string)
340
+ 10.times do | row |
341
+ print " " if (row % 2 == 1)
342
+ 5.times do | col |
343
+ print "#{board_string[row*5 + col,1]} "
344
+ end
345
+ print "\n"
346
+ end
347
+ end
348
+
349
+ def add_board
350
+ board_array = Array.new(50)
351
+ @all_pieces.each do | piece |
352
+ piece.fill_array( board_array )
353
+ end
354
+ start_board = board_string = board_array.join("")
355
+ save( board_string)
356
+ board_string = flip( board_string)
357
+ save( board_string)
358
+ end
359
+
360
+ def flip( board_string)
361
+ new_string = ""
362
+ 50.times do | i |
363
+ row, col = i.divmod(5)
364
+ new_string += board_string[((9 - row) * 5) + (4 - col), 1]
365
+ end
366
+ new_string
367
+ end
368
+
369
+ def save( board_string)
370
+ if (@all_boards[board_string] == nil) then
371
+ @min_board = board_string if (board_string < @min_board)
372
+ @max_board = board_string if (board_string > @max_board)
373
+ @all_boards.store(board_string,true)
374
+ @boards_found += 1
375
+
376
+ if (@boards_found == @stop_count) then
377
+ print_results
378
+ exit(0)
379
+ end
380
+ end
381
+ end
382
+
383
+ end
384
+
385
+ proc = Processor.new.find_all
386
+
@@ -0,0 +1,561 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The Computer Language Shootout
4
+ # http://shootout.alioth.debian.org
5
+ # contributed by Kevin Barnes (Ruby novice)
6
+
7
+ # PROGRAM: the main body is at the bottom.
8
+ # 1) read about the problem here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
9
+ # 2) see how I represent a board as a bitmask by reading the blank_board comments
10
+ # 3) read as your mental paths take you
11
+
12
+ # class to represent all information about a particular rotation of a particular piece
13
+ class Rotation
14
+ # an array (by location) containing a bit mask for how the piece maps at the given location.
15
+ # if the rotation is illegal at that location the mask will contain false
16
+ attr_reader :start_masks
17
+
18
+ # maps a direction to a relative location. these differ depending on whether it is an even or
19
+ # odd row being mapped from
20
+ @@rotation_even_adder = { :west => -1, :east => 1, :nw => -7, :ne => -6, :sw => 5, :se => 6 }
21
+ @@rotation_odd_adder = { :west => -1, :east => 1, :nw => -6, :ne => -5, :sw => 6, :se => 7 }
22
+
23
+ def initialize( directions )
24
+ @even_offsets, @odd_offsets = normalize_offsets( get_values( directions ))
25
+
26
+ @even_mask = mask_for_offsets( @even_offsets)
27
+ @odd_mask = mask_for_offsets( @odd_offsets)
28
+
29
+ @start_masks = Array.new(60)
30
+
31
+ # create the rotational masks by placing the base mask at the location and seeing if
32
+ # 1) it overlaps the boundries and 2) it produces a prunable board. if either of these
33
+ # is true the piece cannot be placed
34
+ 0.upto(59) do | offset |
35
+ mask = is_even(offset) ? (@even_mask << offset) : (@odd_mask << offset)
36
+ if (blank_board & mask == 0 && !prunable(blank_board | mask, 0, true)) then
37
+ imask = compute_required( mask, offset)
38
+ @start_masks[offset] = [ mask, imask, imask | mask ]
39
+ else
40
+ @start_masks[offset] = false
41
+ end
42
+ end
43
+ end
44
+
45
+ def compute_required( mask, offset )
46
+ board = blank_board
47
+ 0.upto(offset) { | i | board |= 1 << i }
48
+ board |= mask
49
+ return 0 if (!prunable(board | mask, offset))
50
+ board = flood_fill(board,58)
51
+ count = 0
52
+ imask = 0
53
+ 0.upto(59) do | i |
54
+ if (board[i] == 0) then
55
+ imask |= (1 << i)
56
+ count += 1
57
+ end
58
+ end
59
+ (count > 0 && count < 5) ? imask : 0
60
+ end
61
+
62
+ def flood_fill( board, location)
63
+ return board if (board[location] == 1)
64
+ board |= 1 << location
65
+ row, col = location.divmod(6)
66
+ board = flood_fill( board, location - 1) if (col > 0)
67
+ board = flood_fill( board, location + 1) if (col < 4)
68
+ if (row % 2 == 0) then
69
+ board = flood_fill( board, location - 7) if (col > 0 && row > 0)
70
+ board = flood_fill( board, location - 6) if (row > 0)
71
+ board = flood_fill( board, location + 6) if (row < 9)
72
+ board = flood_fill( board, location + 5) if (col > 0 && row < 9)
73
+ else
74
+ board = flood_fill( board, location - 5) if (col < 4 && row > 0)
75
+ board = flood_fill( board, location - 6) if (row > 0)
76
+ board = flood_fill( board, location + 6) if (row < 9)
77
+ board = flood_fill( board, location + 7) if (col < 4 && row < 9)
78
+ end
79
+ board
80
+ end
81
+
82
+ # given a location, produces a list of relative locations covered by the piece at this rotation
83
+ def offsets( location)
84
+ if is_even( location) then
85
+ @even_offsets.collect { | value | value + location }
86
+ else
87
+ @odd_offsets.collect { | value | value + location }
88
+ end
89
+ end
90
+
91
+ # returns a set of offsets relative to the top-left most piece of the rotation (by even or odd rows)
92
+ # this is hard to explain. imagine we have this partial board:
93
+ # 0 0 0 0 0 x [positions 0-5]
94
+ # 0 0 1 1 0 x [positions 6-11]
95
+ # 0 0 1 0 0 x [positions 12-17]
96
+ # 0 1 0 0 0 x [positions 18-23]
97
+ # 0 1 0 0 0 x [positions 24-29]
98
+ # 0 0 0 0 0 x [positions 30-35]
99
+ # ...
100
+ # The top-left of the piece is at position 8, the
101
+ # board would be passed as a set of positions (values array) containing [8,9,14,19,25] not necessarily in that
102
+ # sorted order. Since that array starts on an odd row, the offsets for an odd row are: [0,1,6,11,17] obtained
103
+ # by subtracting 8 from everything. Now imagine the piece shifted up and to the right so it's on an even row:
104
+ # 0 0 0 1 1 x [positions 0-5]
105
+ # 0 0 1 0 0 x [positions 6-11]
106
+ # 0 0 1 0 0 x [positions 12-17]
107
+ # 0 1 0 0 0 x [positions 18-23]
108
+ # 0 0 0 0 0 x [positions 24-29]
109
+ # 0 0 0 0 0 x [positions 30-35]
110
+ # ...
111
+ # Now the positions are [3,4,8,14,19] which after subtracting the lowest value (3) gives [0,1,5,11,16] thus, the
112
+ # offsets for this particular piece are (in even, odd order) [0,1,5,11,16],[0,1,6,11,17] which is what
113
+ # this function would return
114
+ def normalize_offsets( values)
115
+ min = values.min
116
+ even_min = is_even(min)
117
+ other_min = even_min ? min + 6 : min + 7
118
+ other_values = values.collect do | value |
119
+ if is_even(value) then
120
+ value + 6 - other_min
121
+ else
122
+ value + 7 - other_min
123
+ end
124
+ end
125
+ values.collect! { | value | value - min }
126
+
127
+ if even_min then
128
+ [values, other_values]
129
+ else
130
+ [other_values, values]
131
+ end
132
+ end
133
+
134
+ # produce a bitmask representation of an array of offset locations
135
+ def mask_for_offsets( offsets )
136
+ mask = 0
137
+ offsets.each { | value | mask = mask + ( 1 << value ) }
138
+ mask
139
+ end
140
+
141
+ # finds a "safe" position that a position as described by a list of directions can be placed
142
+ # without falling off any edge of the board. the values returned a location to place the first piece
143
+ # at so it will fit after making the described moves
144
+ def start_adjust( directions )
145
+ south = east = 0;
146
+ directions.each do | direction |
147
+ east += 1 if ( direction == :sw || direction == :nw || direction == :west )
148
+ south += 1 if ( direction == :nw || direction == :ne )
149
+ end
150
+ south * 6 + east
151
+ end
152
+
153
+ # given a set of directions places the piece (as defined by a set of directions) on the board at
154
+ # a location that will not take it off the edge
155
+ def get_values ( directions )
156
+ start = start_adjust(directions)
157
+ values = [ start ]
158
+ directions.each do | direction |
159
+ if (start % 12 >= 6) then
160
+ start += @@rotation_odd_adder[direction]
161
+ else
162
+ start += @@rotation_even_adder[direction]
163
+ end
164
+ values += [ start ]
165
+ end
166
+
167
+ # some moves take you back to an existing location, we'll strip duplicates
168
+ values.uniq
169
+ end
170
+ end
171
+
172
+ # describes a piece and caches information about its rotations to as to be efficient for iteration
173
+ # ATTRIBUTES:
174
+ # rotations -- all the rotations of the piece
175
+ # type -- a numeic "name" of the piece
176
+ # masks -- an array by location of all legal rotational masks (a n inner array) for that location
177
+ # placed -- the mask that this piece was last placed at (not a location, but the actual mask used)
178
+ class Piece
179
+ attr_reader :rotations, :type, :masks
180
+ attr_accessor :placed
181
+
182
+ # transform hashes that change one direction into another when you either flip or rotate a set of directions
183
+ @@flip_converter = { :west => :west, :east => :east, :nw => :sw, :ne => :se, :sw => :nw, :se => :ne }
184
+ @@rotate_converter = { :west => :nw, :east => :se, :nw => :ne, :ne => :east, :sw => :west, :se => :sw }
185
+
186
+ def initialize( directions, type )
187
+ @type = type
188
+ @rotations = Array.new();
189
+ @map = {}
190
+
191
+ generate_rotations( directions )
192
+ directions.collect! { | value | @@flip_converter[value] }
193
+ generate_rotations( directions )
194
+
195
+ # creates the masks AND a map that returns [location, rotation] for any given mask
196
+ # this is used when a board is found and we want to draw it, otherwise the map is unused
197
+ @masks = Array.new();
198
+ 0.upto(59) do | i |
199
+ even = true
200
+ @masks[i] = @rotations.collect do | rotation |
201
+ mask = rotation.start_masks[i]
202
+ @map[mask[0]] = [ i, rotation ] if (mask)
203
+ mask || nil
204
+ end
205
+ @masks[i].compact!
206
+ end
207
+ end
208
+
209
+ # rotates a set of directions through all six angles and adds a Rotation to the list for each one
210
+ def generate_rotations( directions )
211
+ 6.times do
212
+ rotations.push( Rotation.new(directions))
213
+ directions.collect! { | value | @@rotate_converter[value] }
214
+ end
215
+ end
216
+
217
+ # given a board string, adds this piece to the board at whatever location/rotation
218
+ # important: the outbound board string is 5 wide, the normal location notation is six wide (padded)
219
+ def fill_string( board_string)
220
+ location, rotation = @map[@placed]
221
+ rotation.offsets(location).each do | offset |
222
+ row, col = offset.divmod(6)
223
+ board_string[ row*5 + col, 1 ] = @type.to_s
224
+ end
225
+ end
226
+ end
227
+
228
+ # a blank bit board having this form:
229
+ #
230
+ # 0 0 0 0 0 1
231
+ # 0 0 0 0 0 1
232
+ # 0 0 0 0 0 1
233
+ # 0 0 0 0 0 1
234
+ # 0 0 0 0 0 1
235
+ # 0 0 0 0 0 1
236
+ # 0 0 0 0 0 1
237
+ # 0 0 0 0 0 1
238
+ # 0 0 0 0 0 1
239
+ # 0 0 0 0 0 1
240
+ # 1 1 1 1 1 1
241
+ #
242
+ # where left lest significant bit is the top left and the most significant is the lower right
243
+ # the actual board only consists of the 0 places, the 1 places are blockers to keep things from running
244
+ # off the edges or bottom
245
+ def blank_board
246
+ 0b111111100000100000100000100000100000100000100000100000100000100000
247
+ end
248
+
249
+ def full_board
250
+ 0b111111111111111111111111111111111111111111111111111111111111111111
251
+ end
252
+
253
+ # determines if a location (bit position) is in an even row
254
+ def is_even( location)
255
+ (location % 12) < 6
256
+ end
257
+
258
+ # support function that create three utility maps:
259
+ # @@converter -- for each row an array that maps a five bit row (via array mapping)
260
+ # to the a a five bit representation of the bits below it
261
+ # @@bit_count -- maps a five bit row (via array mapping) to the number of 1s in the row
262
+ # @@new_regions -- maps a five bit row (via array mapping) to an array of "region" arrays
263
+ # a region array has three values the first is a mask of bits in the region,
264
+ # the second is the count of those bits and the third is identical to the first
265
+ # examples:
266
+ # 0b10010 => [ 0b01100, 2, 0b01100 ], [ 0b00001, 1, 0b00001]
267
+ # 0b01010 => [ 0b10000, 1, 0b10000 ], [ 0b00100, 1, 0b00100 ], [ 0b00001, 1, 0b00001]
268
+ # 0b10001 => [ 0b01110, 3, 0b01110 ]
269
+ def create_collector_support
270
+ odd_map = [0b11, 0b110, 0b1100, 0b11000, 0b10000]
271
+ even_map = [0b1, 0b11, 0b110, 0b1100, 0b11000]
272
+
273
+ all_odds = Array.new(0b100000)
274
+ all_evens = Array.new(0b100000)
275
+ bit_counts = Array.new(0b100000)
276
+ new_regions = Array.new(0b100000)
277
+ 0.upto(0b11111) do | i |
278
+ bit_count = odd = even = 0
279
+ 0.upto(4) do | bit |
280
+ if (i[bit] == 1) then
281
+ bit_count += 1
282
+ odd |= odd_map[bit]
283
+ even |= even_map[bit]
284
+ end
285
+ end
286
+ all_odds[i] = odd
287
+ all_evens[i] = even
288
+ bit_counts[i] = bit_count
289
+ new_regions[i] = create_regions( i)
290
+ end
291
+
292
+ @@converter = []
293
+ 10.times { | row | @@converter.push((row % 2 == 0) ? all_evens : all_odds) }
294
+ @@bit_counts = bit_counts
295
+ @@regions = new_regions.collect { | set | set.collect { | value | [ value, bit_counts[value], value] } }
296
+ end
297
+
298
+ # determines if a board is punable, meaning that there is no possibility that it
299
+ # can be filled up with pieces. A board is prunable if there is a grouping of unfilled spaces
300
+ # that are not a multiple of five. The following board is an example of a prunable board:
301
+ # 0 0 1 0 0
302
+ # 0 1 0 0 0
303
+ # 1 1 0 0 0
304
+ # 0 1 0 0 0
305
+ # 0 0 0 0 0
306
+ # ...
307
+ #
308
+ # This board is prunable because the top left corner is only 3 bits in area, no piece will ever fit it
309
+ # parameters:
310
+ # board -- an initial bit board (6 bit padded rows, see blank_board for format)
311
+ # location -- starting location, everything above and to the left is already full
312
+ # slotting -- set to true only when testing initial pieces, when filling normally
313
+ # additional assumptions are possible
314
+ #
315
+ # Algorithm:
316
+ # The algorithm starts at the top row (as determined by location) and iterates a row at a time
317
+ # maintainng counts of active open areas (kept in the collector array) each collector contains
318
+ # three values at the start of an iteration:
319
+ # 0: mask of bits that would be adjacent to the collector in this row
320
+ # 1: the number of bits collected so far
321
+ # 2: a scratch space starting as zero, but used during the computation to represent
322
+ # the empty bits in the new row that are adjacent (position 0)
323
+ # The exact procedure is described in-code
324
+ def prunable( board, location, slotting = false)
325
+ collectors = []
326
+ # loop accross the rows
327
+ (location / 6).to_i.upto(9) do | row_on |
328
+ # obtain a set of regions representing the bits of the curent row.
329
+ regions = @@regions[(board >> (row_on * 6)) & 0b11111]
330
+ converter = @@converter[row_on]
331
+
332
+ # track the number of collectors at the start of the cycle so that
333
+ # we don't compute against newly created collectors, only existing collectors
334
+ initial_collector_count = collectors.length
335
+
336
+ # loop against the regions. For each region of the row
337
+ # we will see if it connects to one or more existing collectors.
338
+ # if it connects to 1 collector, the bits from the region are added to the
339
+ # bits of the collector and the mask is placed in collector[2]
340
+ # If the region overlaps more than one collector then all the collectors
341
+ # it overlaps with are merged into the first one (the others are set to nil in the array)
342
+ # if NO collectors are found then the region is copied as a new collector
343
+ regions.each do | region |
344
+ collector_found = nil
345
+ region_mask = region[2]
346
+ initial_collector_count.times do | collector_num |
347
+ collector = collectors[collector_num]
348
+ if (collector) then
349
+ collector_mask = collector[0]
350
+ if (collector_mask & region_mask != 0) then
351
+ if (collector_found) then
352
+ collector_found[0] |= collector_mask
353
+ collector_found[1] += collector[1]
354
+ collector_found[2] |= collector[2]
355
+ collectors[collector_num] = nil
356
+ else
357
+ collector_found = collector
358
+ collector[1] += region[1]
359
+ collector[2] |= region_mask
360
+ end
361
+ end
362
+ end
363
+ end
364
+ if (collector_found == nil) then
365
+ collectors.push(Array.new(region))
366
+ end
367
+ end
368
+
369
+ # check the existing collectors, if any collector overlapped no bits in the region its [2] value will
370
+ # be zero. The size of any such reaason is tested if it is not a muliple of five true is returned since
371
+ # the board is prunable. if it is a multiple of five it is removed.
372
+ # Collector that are still active have a new adjacent value [0] set based n the matched bits
373
+ # and have [2] cleared out for the next cycle.
374
+ collectors.length.times do | collector_num |
375
+ collector = collectors[collector_num]
376
+ if (collector) then
377
+ if (collector[2] == 0) then
378
+ return true if (collector[1] % 5 != 0)
379
+ collectors[collector_num] = nil
380
+ else
381
+ # if a collector matches all bits in the row then we can return unprunable early for the
382
+ # follwing reasons:
383
+ # 1) there can be no more unavailable bits bince we fill from the top left downward
384
+ # 2) all previous regions have been closed or joined so only this region can fail
385
+ # 3) this region must be good since there can never be only 1 region that is nuot
386
+ # a multiple of five
387
+ # this rule only applies when filling normally, so we ignore the rule if we are "slotting"
388
+ # in pieces to see what configurations work for them (the only other time this algorithm is used).
389
+ return false if (collector[2] == 0b11111 && !slotting)
390
+ collector[0] = converter[collector[2]]
391
+ collector[2] = 0
392
+ end
393
+ end
394
+ end
395
+
396
+ # get rid of all the empty converters for the next round
397
+ collectors.compact!
398
+ end
399
+ return false if (collectors.length <= 1) # 1 collector or less and the region is fine
400
+ collectors.any? { | collector | (collector[1] % 5) != 0 } # more than 1 and we test them all for bad size
401
+ end
402
+
403
+ # creates a region given a row mask. see prunable for what a "region" is
404
+ def create_regions( value )
405
+ regions = []
406
+ cur_region = 0
407
+ 5.times do | bit |
408
+ if (value[bit] == 0) then
409
+ cur_region |= 1 << bit
410
+ else
411
+ if (cur_region != 0 ) then
412
+ regions.push( cur_region)
413
+ cur_region = 0;
414
+ end
415
+ end
416
+ end
417
+ regions.push(cur_region) if (cur_region != 0)
418
+ regions
419
+ end
420
+
421
+ # find up to the counted number of solutions (or all solutions) and prints the final result
422
+ def find_all
423
+ find_top( 1)
424
+ find_top( 0)
425
+ print_results
426
+ end
427
+
428
+ # show the board
429
+ def print_results
430
+ print "#{@boards_found} solutions found\n\n"
431
+ print_full_board( @min_board)
432
+ print "\n"
433
+ print_full_board( @max_board)
434
+ print "\n"
435
+ end
436
+
437
+ # finds solutions. This special version of the main function is only used for the top level
438
+ # the reason for it is basically to force a particular ordering on how the rotations are tested for
439
+ # the first piece. It is called twice, first looking for placements of the odd rotations and then
440
+ # looking for placements of the even locations.
441
+ #
442
+ # WHY?
443
+ # Since any found solution has an inverse we want to maximize finding solutions that are not already found
444
+ # as an inverse. The inverse will ALWAYS be 3 one of the piece configurations that is exactly 3 rotations away
445
+ # (an odd number). Checking even vs odd then produces a higher probability of finding more pieces earlier
446
+ # in the cycle. We still need to keep checking all the permutations, but our probability of finding one will
447
+ # diminsh over time. Since we are TOLD how many to search for this lets us exit before checking all pieces
448
+ # this bennifit is very great when seeking small numbers of solutions and is 0 when looking for more than the
449
+ # maximum number
450
+ def find_top( rotation_skip)
451
+ board = blank_board
452
+ (@pieces.length-1).times do
453
+ piece = @pieces.shift
454
+ piece.masks[0].each do | mask, imask, cmask |
455
+ if ((rotation_skip += 1) % 2 == 0) then
456
+ piece.placed = mask
457
+ find( 1, 1, board | mask)
458
+ end
459
+ end
460
+ @pieces.push(piece)
461
+ end
462
+ piece = @pieces.shift
463
+ @pieces.push(piece)
464
+ end
465
+
466
+ # the normail find routine, iterates through the available pieces, checks all rotations at the current location
467
+ # and adds any boards found. depth is acheived via recursion. the overall approach is described
468
+ # here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/
469
+ # parameters:
470
+ # start_location -- where to start looking for place for the next piece at
471
+ # placed -- number of pieces placed
472
+ # board -- current state of the board
473
+ #
474
+ # see in-code comments
475
+ def find( start_location, placed, board)
476
+ # find the next location to place a piece by looking for an empty bit
477
+ while board[start_location] == 1
478
+ start_location += 1
479
+ end
480
+
481
+ @pieces.length.times do
482
+ piece = @pieces.shift
483
+ piece.masks[start_location].each do | mask, imask, cmask |
484
+ if ( board & cmask == imask) then
485
+ piece.placed = mask
486
+ if (placed == 9) then
487
+ add_board
488
+ else
489
+ find( start_location + 1, placed + 1, board | mask)
490
+ end
491
+ end
492
+ end
493
+ @pieces.push(piece)
494
+ end
495
+ end
496
+
497
+ # print the board
498
+ def print_full_board( board_string)
499
+ 10.times do | row |
500
+ print " " if (row % 2 == 1)
501
+ 5.times do | col |
502
+ print "#{board_string[row*5 + col,1]} "
503
+ end
504
+ print "\n"
505
+ end
506
+ end
507
+
508
+ # when a board is found we "draw it" into a string and then flip that string, adding both to
509
+ # the list (hash) of solutions if they are unique.
510
+ def add_board
511
+ board_string = "99999999999999999999999999999999999999999999999999"
512
+ @all_pieces.each { | piece | piece.fill_string( board_string ) }
513
+ save( board_string)
514
+ save( board_string.reverse)
515
+ end
516
+
517
+ # adds a board string to the list (if new) and updates the current best/worst board
518
+ def save( board_string)
519
+ if (@all_boards[board_string] == nil) then
520
+ @min_board = board_string if (board_string < @min_board)
521
+ @max_board = board_string if (board_string > @max_board)
522
+ @all_boards.store(board_string,true)
523
+ @boards_found += 1
524
+
525
+ # the exit motif is a time saver. Ideally the function should return, but those tests
526
+ # take noticable time (performance).
527
+ if (@boards_found == @stop_count) then
528
+ print_results
529
+ exit(0)
530
+ end
531
+ end
532
+ end
533
+
534
+
535
+ ##
536
+ ## MAIN BODY :)
537
+ ##
538
+ create_collector_support
539
+ @pieces = [
540
+ Piece.new( [ :nw, :ne, :east, :east ], 2),
541
+ Piece.new( [ :ne, :se, :east, :ne ], 7),
542
+ Piece.new( [ :ne, :east, :ne, :nw ], 1),
543
+ Piece.new( [ :east, :sw, :sw, :se ], 6),
544
+ Piece.new( [ :east, :ne, :se, :ne ], 5),
545
+ Piece.new( [ :east, :east, :east, :se ], 0),
546
+ Piece.new( [ :ne, :nw, :se, :east, :se ], 4),
547
+ Piece.new( [ :se, :se, :se, :west ], 9),
548
+ Piece.new( [ :se, :se, :east, :se ], 8),
549
+ Piece.new( [ :east, :east, :sw, :se ], 3)
550
+ ];
551
+
552
+ @all_pieces = Array.new( @pieces)
553
+
554
+ @min_board = "99999999999999999999999999999999999999999999999999"
555
+ @max_board = "00000000000000000000000000000000000000000000000000"
556
+ @stop_count = ARGV[0].to_i || 2089
557
+ @all_boards = {}
558
+ @boards_found = 0
559
+
560
+ find_all ######## DO IT!!!
561
+