chrislo-sourceclassifier 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +0 -312
- data/Rakefile +2 -2
- data/sourceclassifier.gemspec +6 -6
- metadata +4 -316
- data/sources/gcc/ackermann.gcc-2.gcc +0 -93
- data/sources/gcc/ackermann.gcc-3.gcc +0 -20
- data/sources/gcc/ary.gcc +0 -40
- data/sources/gcc/binarytrees.gcc +0 -136
- data/sources/gcc/binarytrees.gcc-2.gcc +0 -140
- data/sources/gcc/binarytrees.gcc-3.gcc +0 -134
- data/sources/gcc/chameneos.gcc +0 -117
- data/sources/gcc/chameneos.gcc-2.gcc +0 -134
- data/sources/gcc/chameneos.gcc-3.gcc +0 -120
- data/sources/gcc/chameneosredux.gcc +0 -306
- data/sources/gcc/echo.gcc +0 -144
- data/sources/gcc/except.gcc +0 -52
- data/sources/gcc/fannkuch.gcc +0 -105
- data/sources/gcc/fannkuch.gcc-2.gcc +0 -94
- data/sources/gcc/fasta.gcc +0 -158
- data/sources/gcc/fasta.gcc-2.gcc +0 -132
- data/sources/gcc/fasta.gcc-3.gcc +0 -145
- data/sources/gcc/fasta.gcc-4.gcc +0 -141
- data/sources/gcc/fibo.gcc +0 -20
- data/sources/gcc/harmonic.gcc +0 -22
- data/sources/gcc/hash.gcc +0 -31
- data/sources/gcc/hash2.gcc +0 -38
- data/sources/gcc/heapsort.gcc +0 -72
- data/sources/gcc/hello.gcc-2.gcc +0 -12
- data/sources/gcc/hello.gcc-3.gcc +0 -20
- data/sources/gcc/knucleotide.gcc +0 -173
- data/sources/gcc/knucleotide.gcc-2.gcc +0 -168
- data/sources/gcc/lists.gcc +0 -226
- data/sources/gcc/magicsquares.gcc +0 -378
- data/sources/gcc/mandelbrot.gcc +0 -84
- data/sources/gcc/mandelbrot.gcc-2.gcc +0 -63
- data/sources/gcc/mandelbrot.gcc-3.gcc +0 -91
- data/sources/gcc/mandelbrot.gcc-4.gcc +0 -78
- data/sources/gcc/matrix.gcc +0 -66
- data/sources/gcc/message.gcc-2.gcc +0 -72
- data/sources/gcc/message.gcc-3.gcc +0 -64
- data/sources/gcc/methcall.gcc +0 -89
- data/sources/gcc/moments.gcc +0 -120
- data/sources/gcc/nbody.gcc +0 -142
- data/sources/gcc/nbody.gcc-2.gcc +0 -143
- data/sources/gcc/nbody.gcc-3.gcc +0 -142
- data/sources/gcc/nestedloop.gcc +0 -25
- data/sources/gcc/nsieve.gcc +0 -35
- data/sources/gcc/nsievebits.gcc-2.gcc +0 -37
- data/sources/gcc/nsievebits.gcc-3.gcc +0 -53
- data/sources/gcc/objinst.gcc +0 -95
- data/sources/gcc/partialsums.gcc +0 -86
- data/sources/gcc/partialsums.gcc-3.gcc +0 -81
- data/sources/gcc/partialsums.gcc-4.gcc +0 -68
- data/sources/gcc/pidigits.gcc +0 -105
- data/sources/gcc/primes.gcc +0 -75
- data/sources/gcc/prodcons.gcc +0 -86
- data/sources/gcc/random.gcc +0 -29
- data/sources/gcc/raytracer.gcc +0 -125
- data/sources/gcc/raytracer.gcc-2.gcc +0 -181
- data/sources/gcc/recursive.gcc +0 -68
- data/sources/gcc/recursive.gcc-2.gcc +0 -55
- data/sources/gcc/regexdna.gcc-2.gcc +0 -126
- data/sources/gcc/regexmatch.gcc +0 -136
- data/sources/gcc/revcomp.gcc +0 -85
- data/sources/gcc/revcomp.gcc-2.gcc +0 -88
- data/sources/gcc/revcomp.gcc-4.gcc +0 -71
- data/sources/gcc/reversefile.gcc +0 -103
- data/sources/gcc/reversefile.gcc-2.gcc +0 -56
- data/sources/gcc/sieve.gcc +0 -34
- data/sources/gcc/spectralnorm.gcc +0 -54
- data/sources/gcc/spellcheck.gcc +0 -72
- data/sources/gcc/spellcheck.gcc-2.gcc +0 -61
- data/sources/gcc/strcat.gcc +0 -38
- data/sources/gcc/sumcol.gcc-2.gcc +0 -98
- data/sources/gcc/sumcol.gcc-3.gcc +0 -22
- data/sources/gcc/sumcol.gcc-4.gcc +0 -18
- data/sources/gcc/sumcol.gcc-5.gcc +0 -32
- data/sources/gcc/takfp.gcc +0 -23
- data/sources/gcc/tcp-stream.gcc +0 -122
- data/sources/gcc/tcpecho.gcc +0 -122
- data/sources/gcc/tcpecho.gcc-2.gcc +0 -136
- data/sources/gcc/tcprequest.gcc +0 -122
- data/sources/gcc/threadring.gcc +0 -73
- data/sources/gcc/wc.gcc +0 -52
- data/sources/gcc/wc.gcc-2.gcc +0 -40
- data/sources/gcc/wc.gcc-3.gcc +0 -46
- data/sources/gcc/wordfreq.gcc-2.gcc +0 -85
- data/sources/perl/ackermann.perl +0 -28
- data/sources/perl/ackermann.perl-2.perl +0 -25
- data/sources/perl/ackermann.perl-3.perl +0 -20
- data/sources/perl/ackermann.perl-4.perl +0 -18
- data/sources/perl/ary.perl +0 -25
- data/sources/perl/ary.perl-2.perl +0 -23
- data/sources/perl/binarytrees.perl +0 -66
- data/sources/perl/binarytrees.perl-2.perl +0 -71
- data/sources/perl/chameneos.perl +0 -67
- data/sources/perl/echo.perl +0 -81
- data/sources/perl/except.perl +0 -73
- data/sources/perl/fannkuch.perl +0 -44
- data/sources/perl/fannkuch.perl-2.perl +0 -38
- data/sources/perl/fasta.perl +0 -112
- data/sources/perl/fasta.perl-2.perl +0 -135
- data/sources/perl/fasta.perl-4.perl +0 -122
- data/sources/perl/fibo.perl +0 -15
- data/sources/perl/fibo.perl-2.perl +0 -0
- data/sources/perl/fibo.perl-3.perl +0 -0
- data/sources/perl/harmonic.perl +0 -7
- data/sources/perl/hash.perl +0 -23
- data/sources/perl/hash.perl-2.perl +0 -17
- data/sources/perl/hash.perl-3.perl +0 -24
- data/sources/perl/hash2.perl +0 -16
- data/sources/perl/heapsort.perl +0 -65
- data/sources/perl/heapsort.perl-2.perl +0 -0
- data/sources/perl/hello.perl +0 -5
- data/sources/perl/knucleotide.perl-2.perl +0 -30
- data/sources/perl/lists.perl +0 -48
- data/sources/perl/mandelbrot.perl-2.perl +0 -32
- data/sources/perl/matrix.perl +0 -59
- data/sources/perl/matrix.perl-2.perl +0 -0
- data/sources/perl/matrix.perl-3.perl +0 -0
- data/sources/perl/message.perl +0 -27
- data/sources/perl/methcall.perl +0 -66
- data/sources/perl/moments.perl +0 -44
- data/sources/perl/nbody.perl +0 -108
- data/sources/perl/nestedloop.perl +0 -28
- data/sources/perl/nsieve.perl-2.perl +0 -41
- data/sources/perl/nsieve.perl-4.perl +0 -43
- data/sources/perl/nsievebits.perl +0 -37
- data/sources/perl/objinst.perl +0 -73
- data/sources/perl/partialsums.perl-3.perl +0 -31
- data/sources/perl/pidigits.perl +0 -52
- data/sources/perl/pidigits.perl-2.perl +0 -47
- data/sources/perl/process.perl +0 -50
- data/sources/perl/prodcons.perl +0 -47
- data/sources/perl/random.perl-4.perl +0 -17
- data/sources/perl/recursive.perl-2.perl +0 -57
- data/sources/perl/regexdna.perl +0 -48
- data/sources/perl/regexdna.perl-2.perl +0 -43
- data/sources/perl/regexdna.perl-3.perl +0 -50
- data/sources/perl/regexdna.perl-4.perl +0 -49
- data/sources/perl/regexdna.perl-5.perl +0 -42
- data/sources/perl/regexdna.perl-6.perl +0 -43
- data/sources/perl/regexmatch.perl +0 -35
- data/sources/perl/revcomp.perl-2.perl +0 -34
- data/sources/perl/reversefile.perl +0 -8
- data/sources/perl/reversefile.perl-2.perl +0 -0
- data/sources/perl/reversefile.perl-3.perl +0 -0
- data/sources/perl/sieve.perl +0 -23
- data/sources/perl/spectralnorm.perl-2.perl +0 -54
- data/sources/perl/spellcheck.perl +0 -24
- data/sources/perl/strcat.perl +0 -13
- data/sources/perl/strcat.perl-2.perl +0 -0
- data/sources/perl/sumcol.perl +0 -8
- data/sources/perl/takfp.perl +0 -23
- data/sources/perl/takfp.perl-3.perl +0 -20
- data/sources/perl/tcpecho.perl +0 -61
- data/sources/perl/tcprequest.perl +0 -61
- data/sources/perl/tcpstream.perl +0 -61
- data/sources/perl/threadring.perl +0 -55
- data/sources/perl/threadring.perl-2.perl +0 -43
- data/sources/perl/wc.perl +0 -20
- data/sources/perl/wc.perl-2.perl +0 -14
- data/sources/perl/wordfreq.perl +0 -22
- data/sources/perl/wordfreq.perl-3.perl +0 -0
- data/sources/perl/wordfreq.perl3.perl +0 -0
- data/sources/python/ackermann.python +0 -21
- data/sources/python/ary.python +0 -19
- data/sources/python/binarytrees.python +0 -39
- data/sources/python/binarytrees.python-3.python +0 -44
- data/sources/python/chameneos.python-6.python +0 -73
- data/sources/python/chameneosredux.python +0 -126
- data/sources/python/chameneosredux.python-2.python +0 -122
- data/sources/python/dispatch.python +0 -176
- data/sources/python/dispatch.python-2.python +0 -136
- data/sources/python/echo.python +0 -64
- data/sources/python/except.python +0 -62
- data/sources/python/fannkuch.python +0 -50
- data/sources/python/fannkuch.python-2.python +0 -54
- data/sources/python/fasta.python-2.python +0 -79
- data/sources/python/fibo.python +0 -17
- data/sources/python/fibo.python-2.python +0 -0
- data/sources/python/fibo.python-3.python +0 -0
- data/sources/python/harmonic.python-2.python +0 -9
- data/sources/python/hash.python +0 -21
- data/sources/python/hash.python-2.python +0 -0
- data/sources/python/hash2.python +0 -30
- data/sources/python/heapsort.python-3.python +0 -66
- data/sources/python/hello.python +0 -5
- data/sources/python/implicitode.python +0 -231
- data/sources/python/knucleotide.python +0 -55
- data/sources/python/lists.python +0 -44
- data/sources/python/magicsquares.python +0 -145
- data/sources/python/mandelbrot.python +0 -44
- data/sources/python/mandelbrot.python-2.python +0 -35
- data/sources/python/mandelbrot.python-3.python +0 -46
- data/sources/python/matrix.python +0 -34
- data/sources/python/matrix.python-2.python +0 -23
- data/sources/python/message.python +0 -24
- data/sources/python/message.python-2.python +0 -20
- data/sources/python/message.python-3.python +0 -19
- data/sources/python/meteor.python +0 -210
- data/sources/python/meteor.python-2.python +0 -192
- data/sources/python/methcall.python +0 -51
- data/sources/python/moments.python +0 -65
- data/sources/python/nbody.python +0 -123
- data/sources/python/nbody.python-2.python +0 -120
- data/sources/python/nestedloop.python +0 -24
- data/sources/python/nsieve.python +0 -27
- data/sources/python/nsieve.python-2.python +0 -23
- data/sources/python/nsieve.python-4.python +0 -25
- data/sources/python/nsievebits.python +0 -27
- data/sources/python/nsievebits.python-2.python +0 -43
- data/sources/python/objinst.python +0 -53
- data/sources/python/partialsums.python +0 -37
- data/sources/python/partialsums.python-2.python +0 -35
- data/sources/python/partialsums.python-3.python +0 -48
- data/sources/python/pidigits.python +0 -38
- data/sources/python/pidigits.python-3.python +0 -63
- data/sources/python/pidigits.python-4.python +0 -24
- data/sources/python/process.python +0 -51
- data/sources/python/process.python-2.python +0 -133
- data/sources/python/prodcons.python +0 -51
- data/sources/python/prodcons.python-2.python +0 -0
- data/sources/python/random.python +0 -27
- data/sources/python/raytracer.python +0 -203
- data/sources/python/recursive.python +0 -35
- data/sources/python/regexdna.python +0 -39
- data/sources/python/regexdna.python-2.python +0 -34
- data/sources/python/regexmatch.python +0 -36
- data/sources/python/revcomp.python-3.python +0 -31
- data/sources/python/reversefile.python +0 -13
- data/sources/python/reversefile.python-2.python +0 -0
- data/sources/python/reversefile.python-3.python +0 -0
- data/sources/python/sieve.python +0 -50
- data/sources/python/spectralnorm.python-2.python +0 -36
- data/sources/python/spellcheck.python +0 -17
- data/sources/python/strcat.python +0 -35
- data/sources/python/strcat.python-2.python +0 -0
- data/sources/python/sumcol.python-2.python +0 -0
- data/sources/python/sumcol.python-3.python +0 -0
- data/sources/python/takfp.python +0 -19
- data/sources/python/tcpecho.python +0 -67
- data/sources/python/tcprequest.python +0 -67
- data/sources/python/tcpstream.python +0 -67
- data/sources/python/threadring.python +0 -47
- data/sources/python/threadring.python-2.python +0 -40
- data/sources/python/threadring.python-3.python +0 -34
- data/sources/python/wc.python-2.python +0 -19
- data/sources/python/wordfreq.python +0 -43
- data/sources/python/wordfreq.python-2.python +0 -0
- data/sources/python/wordfreq.python-3.python +0 -28
- data/sources/python/wordfreq.python-4.python +0 -38
- data/sources/python/wordfreq.python-5.python +0 -39
- data/sources/ruby/ackermann.ruby +0 -17
- data/sources/ruby/ackermann.ruby-5.ruby +0 -153
- data/sources/ruby/ary.ruby +0 -22
- data/sources/ruby/binarytrees.ruby-2.ruby +0 -55
- data/sources/ruby/chameneos.ruby-2.ruby +0 -71
- data/sources/ruby/dispatch.ruby +0 -114
- data/sources/ruby/echo.ruby +0 -41
- data/sources/ruby/except.ruby +0 -61
- data/sources/ruby/except.ruby-2.ruby +0 -61
- data/sources/ruby/fannkuch.ruby +0 -42
- data/sources/ruby/fasta.ruby +0 -81
- data/sources/ruby/fibo.ruby +0 -15
- data/sources/ruby/harmonic.ruby-2.ruby +0 -15
- data/sources/ruby/hash.ruby +0 -19
- data/sources/ruby/hash2.ruby +0 -23
- data/sources/ruby/heapsort.ruby +0 -55
- data/sources/ruby/hello.ruby +0 -6
- data/sources/ruby/knucleotide.ruby-2.ruby +0 -44
- data/sources/ruby/lists.ruby +0 -46
- data/sources/ruby/mandelbrot.ruby-3.ruby +0 -63
- data/sources/ruby/matrix.ruby +0 -40
- data/sources/ruby/matrix.ruby-2.ruby +0 -30
- data/sources/ruby/message.ruby +0 -29
- data/sources/ruby/message.ruby-2.ruby +0 -24
- data/sources/ruby/meteor.ruby +0 -386
- data/sources/ruby/meteor.ruby-2.ruby +0 -561
- data/sources/ruby/methcall.ruby +0 -58
- data/sources/ruby/methcall.ruby-2.ruby +0 -54
- data/sources/ruby/moments.ruby +0 -64
- data/sources/ruby/nbody.ruby-2.ruby +0 -145
- data/sources/ruby/nestedloop.ruby +0 -22
- data/sources/ruby/nsieve.ruby +0 -36
- data/sources/ruby/nsieve.ruby-2.ruby +0 -25
- data/sources/ruby/nsievebits.ruby-2.ruby +0 -42
- data/sources/ruby/objinst.ruby +0 -58
- data/sources/ruby/partialsums.ruby +0 -39
- data/sources/ruby/pidigits.ruby +0 -92
- data/sources/ruby/pidigits.ruby-2.ruby +0 -109
- data/sources/ruby/prodcons.ruby +0 -41
- data/sources/ruby/random.ruby +0 -17
- data/sources/ruby/recursive.ruby-2.ruby +0 -53
- data/sources/ruby/regexdna.ruby +0 -32
- data/sources/ruby/regexdna.ruby-2.ruby +0 -38
- data/sources/ruby/regexmatch.ruby +0 -33
- data/sources/ruby/revcomp.ruby +0 -28
- data/sources/ruby/reversefile.ruby +0 -7
- data/sources/ruby/sieve.ruby +0 -30
- data/sources/ruby/spectralnorm.ruby +0 -48
- data/sources/ruby/spellcheck.ruby +0 -18
- data/sources/ruby/spellcheck.ruby-2.ruby +0 -0
- data/sources/ruby/strcat.ruby +0 -12
- data/sources/ruby/strcat.ruby-2.ruby +0 -12
- data/sources/ruby/sumcol.ruby +0 -12
- data/sources/ruby/sumcol.ruby-2.ruby +0 -5
- data/sources/ruby/takfp.ruby +0 -15
- data/sources/ruby/tcpecho.ruby +0 -45
- data/sources/ruby/tcprequest.ruby +0 -45
- data/sources/ruby/tcpstream.ruby +0 -45
- data/sources/ruby/threadring.ruby +0 -61
- data/sources/ruby/threadring.ruby-2.ruby +0 -33
- data/sources/ruby/wc.ruby +0 -15
- data/sources/ruby/wordfreq.ruby +0 -17
- data/sources/ruby/wordfreq.ruby2.ruby +0 -0
data/sources/ruby/message.ruby
DELETED
@@ -1,29 +0,0 @@
|
|
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
|
-
|
@@ -1,24 +0,0 @@
|
|
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
|
data/sources/ruby/meteor.ruby
DELETED
@@ -1,386 +0,0 @@
|
|
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
|
-
|
@@ -1,561 +0,0 @@
|
|
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
|
-
|