3 # The Computer Language Shootout
4 # http://shootout.alioth.debian.org
5 # contributed by Kevin Barnes (Ruby novice)
8 0b111111100000100000100000100000100000100000100000100000100000100000
11 def is_even( location
)
15 def create_collector_support
16 odd_map
= [0b11, 0b110, 0b1100, 0b11000, 0b10000]
17 even_map
= [0b1, 0b11, 0b110, 0b1100, 0b11000]
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
34 bit_counts
[i
] = bit_count
35 new_regions
[i
] = create_regions( i
)
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
] } }
45 def prunable( board
, location
, slotting
= false)
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
|
53 region_mask
= region
[0]
54 initial_collector_count
.times
do | collector_num
|
55 collector
= collectors
[collector_num
]
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
65 collector_found
= collector
66 collector
[1] += region
[1]
67 collector
[2] |= region_mask
72 if (collector_found
== nil) then
73 collectors
.push(Array
.new(region
))
76 collectors
.length
.times
do | collector_num
|
77 collector
= collectors
[collector_num
]
79 if (collector
[2] == 0) then
80 return true if (collector
[1] % 5 != 0)
81 collectors
[collector_num
] = nil
83 return false if (collector
[2] == 0b11111 && !slotting
)
84 collector
[0] = converter
[collector
[2]]
91 return false if (collectors
.length
<= 1)
92 collectors
.any
? { | collector
| (collector
[1] % 5) != 0 }
103 def create_regions( value
)
107 if (value
[bit
] == 1) then
108 cur_region
|= 1 << bit
110 if (cur_region
!=0 ) then
111 regions
.push( cur_region
)
116 regions
.push(cur_region
) if (cur_region
!= 0)
120 def print_board( board
, padding
= "", rows
= 10, row_offset
= 0)
121 rows
.times
do | row
|
123 rtn
= "#{rtn} " if ((row
+ row_offset
) % 2) == 1
125 rtn
= "#{rtn}#{board[row*6+col]} "
132 attr_reader
:start_masks
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 }
137 def initialize( directions
)
138 values
, min
= get_values( directions
)
139 @even_offsets, @odd_offsets = normalize_offsets( values
, min
)
141 @even_mask = mask_for_offsets( @even_offsets)
142 @odd_mask = mask_for_offsets( @odd_offsets)
144 @start_masks = Array
.new(60)
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
151 @start_masks[offset
] = false
156 def offsets( location
)
157 if is_even( location
) then
158 @even_offsets.collect
{ | value
| value
+ location
}
160 @odd_offsets.collect
{ | value
| value
+ location
}
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
171 value
+ 7 - other_min
174 values
.collect
! { | value
| value
- min
}
177 [values
, other_values
]
179 [other_values
, values
]
183 def mask_for_offsets( offsets
)
185 offsets
.each
{ | value
| mask
= mask
+ ( 1 << value
) }
189 def start_adjust( directions
)
191 directions
.each
do | direction
|
192 east
+= 1 if ( direction
== :sw || direction
== :nw || direction
== :west )
193 south
+= 1 if ( direction
== :nw || direction
== :ne )
198 def get_values ( directions
)
199 south
, east
= start_adjust(directions
)
200 min
= start
= south
* 6 + east
202 directions
.each
do | direction
|
203 if (start
% 12 >= 6) then
204 start
+= @
@rotation_odd_adder[direction
]
206 start
+= @
@rotation_even_adder[direction
]
208 min
= start
if (start
< min
)
212 if (values
.length
!= 5)
221 attr_reader
:rotations, :type, :masks
222 attr_accessor
:placed
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 }
227 def initialize( directions
, type
)
229 @rotations = Array
.new();
231 generate_rotations( directions
)
232 directions
.collect
! { | value
| @
@flip_converter[value
] }
233 generate_rotations( directions
)
235 @masks = Array
.new();
237 @masks[i
] = @rotations.collect
do | rotation
|
238 mask
= rotation
.start_masks
[i
]
239 @map[mask
] = [ i
, rotation
] if (mask
)
246 def generate_rotations( directions
)
248 rotations
.push( Rotation
.new(directions
))
249 directions
.collect
! { | value
| @
@rotate_converter[value
] }
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
266 create_collector_support
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) ];
279 @all_pieces = Array
.new( @pieces)
281 @min_board = "99999999999999999999999999999999999999999999999999"
282 @max_board = "00000000000000000000000000000000000000000000000000"
283 @stop_count = ARGV[0].to_i
|| 2089
295 print
"#{@boards_found} solutions found\n\n"
296 print_full_board( @min_board)
298 print_full_board( @max_board)
302 def find_top( rotation_skip
)
304 @pieces.length
.times
do
305 piece
= @pieces.shift
306 piece
.masks
[0].each
do | mask
|
307 if ((rotation_skip
+= 1) % 2 == 0) then
309 find( 1, 1, board
| mask
)
316 def find( start_location
, placed
, board
)
317 while board
[start_location
] == 1
321 return if (start_location
< 28 && prunable( board
, start_location
))
323 @pieces.length
.times
do
324 piece
= @pieces.shift
325 piece
.masks
[start_location
].each
do | mask
|
326 if (mask
& board
== 0) then
328 if (placed
== 9) then
331 find( start_location
+ 1, placed
+ 1, board
| mask
)
339 def print_full_board( board_string
)
341 print
" " if (row
% 2 == 1)
343 print
"#{board_string[row*5 + col,1]} "
350 board_array
= Array
.new(50)
351 @all_pieces.each
do | piece
|
352 piece
.fill_array( board_array
)
354 start_board
= board_string
= board_array
.join("")
356 board_string
= flip( board_string
)
360 def flip( board_string
)
363 row
, col
= i
.divmod(5)
364 new_string
+= board_string
[((9 - row
) * 5) + (4 - col
), 1]
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)
376 if (@boards_found == @stop_count) then
385 proc
= Processor
.new
.find_all