Nonoblock: Difference between revisions

From Rosetta Code
Content added Content deleted
m (=={{header|Racket}}== stub added)
(→‎{{header|Racket}}: actual implementation added)
Line 350: Line 350:


=={{header|Racket}}==
=={{header|Racket}}==

This implementation does not "error" on the impossible case.

Knowing that there are no solutions (empty result list) is good enough.

Also, the blocks are not identified. I suppose they could be easily enough, but in the nonogram task, these patterns are converted to bit-fields shortly after the nonoblock generation, and bits have no names (sad, but true).

<lang racket>#lang racket
(require racket/trace)

(define add1-to-car (match-lambda [(cons (app add1 p1) t) (cons p1 t)]))

;; inputs:
;; cells -- available cells
;; blocks -- list of block widths
;; output:
;; gap-block+gaps
;; where gap-block+gaps is:
;; (list gap) -- a single gap
;; (list gap block-width gap-block+gaps) -- padding to left, a block, right hand side
(define (nonoblock cells blocks)
(match* ((- cells (apply + (length blocks) -1 blocks)) #| padding available on both sides |# blocks)
[(_ (list)) (list (list cells))] ; generates an empty list of padding
[((? negative?) _) null] ; impossible to satisfy
[((and avp
;; use add1 with in-range because we actually want from 0 to available-padding
;; without add1, in-range iterates from 0 to (available-padding - 1)
(app add1 avp+1))
(list block))
(for/list ((l-pad (in-range 0 avp+1)))
(define r-pad (- avp l-pad)) ; what remains goes to right
(list l-pad block r-pad))]
[((app add1 avp+1) (list block more-blocks ...))
(for*/list ((l-pad (in-range 0 avp+1))
(cells-- (in-value (- cells block l-pad 1)))
(r-blocks (in-value (nonoblock cells-- more-blocks)))
(r-block (in-list r-blocks)))
(list* l-pad block (add1-to-car r-block)))])) ; put a single space pad on left of r-block

(define (neat rslt)
(define dots (curryr make-string #\.))
(define Xes (curryr make-string #\X))
(define inr
(match-lambda
[(list 0 (app Xes b) t ...)
(string-append b (inr t))]
[(list (app dots p) (app Xes b) t ...)
(string-append p b (inr t))]
[(list (app dots p)) p]))
(define (neat-row r)
(string-append "|" (inr r) "|"))
(string-join (map neat-row rslt) "\n"))

(define (tst c b)
(define rslt (nonoblock c b))
(define rslt-l (length rslt))
(printf "~a cells, ~a blocks => ~a~%~a~%" c b
(match rslt-l
[0 "impossible"]
[1 "1 solution"]
[(app (curry format "~a solutions") r) r])
(neat rslt)))

(module+ test
(tst 5 '[2 1])
(tst 5 '[])
(tst 10 '[8])
(tst 15 '[2 3 2 3])
(tst 5 '[2 3]))</lang>

{{out}}
<pre>5 cells, (2 1) blocks => 3 solutions
|XX.X.|
|XX..X|
|.XX.X|
5 cells, () blocks => 1 solution
|.....|
10 cells, (8) blocks => 3 solutions
|XXXXXXXX..|
|.XXXXXXXX.|
|..XXXXXXXX|
15 cells, (2 3 2 3) blocks => 15 solutions
|XX.XXX.XX.XXX..|
|XX.XXX.XX..XXX.|
|XX.XXX.XX...XXX|
|XX.XXX..XX.XXX.|
|XX.XXX..XX..XXX|
|XX.XXX...XX.XXX|
|XX..XXX.XX.XXX.|
|XX..XXX.XX..XXX|
|XX..XXX..XX.XXX|
|XX...XXX.XX.XXX|
|.XX.XXX.XX.XXX.|
|.XX.XXX.XX..XXX|
|.XX.XXX..XX.XXX|
|.XX..XXX.XX.XXX|
|..XX.XXX.XX.XXX|
5 cells, (2 3) blocks => impossible
</pre>


=={{header|Ruby}}==
=={{header|Ruby}}==

Revision as of 08:08, 13 April 2015

Nonoblock is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Nonoblock is a chip off the old Nonogram puzzle.

Given
  • The number of cells in a row.
  • The size of each, (space separated), connected block of cells to fit in the row, in left-to right order.

The task is to

  • show all possible positions
  • and the number of positions

of the blocks for the following cases within the row. On this page. Using a "neat" diagram of the block positions.

Enumerate the following configurations
  1. 5 cells and [2, 1] blocks
  2. 5 cells and [] blocks (no blocks)
  3. 10 cells and [8] blocks
  4. 15 cells and [2, 3, 2, 3] blocks
  5. 5 cells and [2, 3] blocks (Should give some indication of this not being possible).
Example

Given a row of five cells and a block of two cells followed by a block of 1 cell - in that order, the example could be shown as:

   |_|_|_|_|_| # 5 cells and [2, 1] blocks

And would expand to the following 3 possible rows of block positions:

   |A|A|_|B|_|
   |A|A|_|_|B|
   |_|A|A|_|B|


Note how the sets of blocks are always separated by a space.

Note also that it is not necessary for each block to have a separate letter. Output approximating

This:

   |#|#|_|#|_|
   |#|#|_|_|#|
   |_|#|#|_|#|

Or even this:

   ##.#.
   ##..#
   .##.#

Would also work.

An algorithm
  • Find the minimum space to the right that is needed to legally hold all but the leftmost block of cells (with a space between blocks remember).
  • The leftmost cell can legitimately be placed in all positions from the LHS up to a RH position that allows enough room for the rest of the blocks.
  • for each position of the LH block recursively compute the position of the rest of the blocks in the remaining space to the right of the current placement of the LH block.

(This is the algorithm used in the Nonoblock#Python solution).

Reference

D

Translation of: python

<lang d>import std.stdio, std.array, std.algorithm, std.exception, std.conv,

      std.concurrency, std.range;

struct Solution { uint pos, len; }

Generator!(Solution[]) nonoBlocks(in uint[] blocks, in uint cells) {

   return new typeof(return)({
       if (blocks.empty || blocks[0] == 0) {
           yield([Solution(0, 0)]);
       } else {
           enforce(blocks.sum + blocks.length - 1 <= cells,
                   "Those blocks cannot fit in those cells.");
           immutable firstBl = blocks[0];
           const restBl = blocks.dropOne;
           // The other blocks need space.
           immutable minS = restBl.map!(b => b + 1).sum;
           // Slide the start position from left to max RH
           // index allowing for other blocks.
           foreach (immutable bPos; 0 .. cells - minS - firstBl + 1) {
               if (restBl.empty) {
                   // No other blocks to the right so just yield
                   // this one.
                   yield([Solution(bPos, firstBl)]);
               } else {
                   // More blocks to the right so create a sub-problem
                   // of placing the restBl blocks in the cells one
                   // space to the right of the RHS of this block.
                   immutable offset = bPos + firstBl + 1;
                   immutable newCells = cells - offset;
                   // Recursive call to nonoBlocks yields multiple
                   // sub-positions.
                   foreach (const subPos; nonoBlocks(restBl, newCells)) {
                       // Remove the offset from sub block positions.
                       auto rest = subPos.map!(sol => Solution(offset + sol.pos, sol.len));
                       // Yield this block plus sub blocks positions.
                       yield(Solution(bPos, firstBl) ~ rest.array);
                   }
               }
           }
       }
   });

}


/// Pretty prints each run of blocks with a /// different letter for each block of filled cells. string show(in Solution[] vec, in uint nCells) pure {

   auto result = ['_'].replicate(nCells);
   foreach (immutable i, immutable sol; vec)
       foreach (immutable j; sol.pos .. sol.pos + sol.len)
           result[j] = (result[j] == '_') ? to!char('A' + i) : '?';
   return '[' ~ result ~ ']';

}

void main() {

   static struct Problem { uint[] blocks; uint nCells; }
   immutable Problem[] problems = [{[2, 1], 5},
                                   {[], 5},
                                   {[8], 10},
                                   {[2, 3, 2, 3], 15},
                                   {[4, 3], 10},
                                   {[2, 1], 5},
                                   {[3, 1], 10},
                                   {[2, 3], 5}];
   foreach (immutable prob; problems) {
       writefln("Configuration (%d cells and %s blocks):",
                prob.nCells, prob.blocks);
       show([], prob.nCells).writeln;
       "Possibilities:".writeln;
       auto nConfigs = 0;
       foreach (const sol; nonoBlocks(prob.tupleof)) {
           show(sol, prob.nCells).writeln;
           nConfigs++;
       }
       writefln("A total of %d possible configurations.", nConfigs);
       writeln;
   }

}</lang>

Output:
Configuration (5 cells and [2, 1] blocks):
[_____]
Possibilities:
[AA_B_]
[AA__B]
[_AA_B]
A total of 3 possible configurations.

Configuration (5 cells and [] blocks):
[_____]
Possibilities:
[_____]
A total of 1 possible configurations.

Configuration (10 cells and [8] blocks):
[__________]
Possibilities:
[AAAAAAAA__]
[_AAAAAAAA_]
[__AAAAAAAA]
A total of 3 possible configurations.

Configuration (15 cells and [2, 3, 2, 3] blocks):
[_______________]
Possibilities:
[AA_BBB_CC_DDD__]
[AA_BBB_CC__DDD_]
[AA_BBB_CC___DDD]
[AA_BBB__CC_DDD_]
[AA_BBB__CC__DDD]
[AA_BBB___CC_DDD]
[AA__BBB_CC_DDD_]
[AA__BBB_CC__DDD]
[AA__BBB__CC_DDD]
[AA___BBB_CC_DDD]
[_AA_BBB_CC_DDD_]
[_AA_BBB_CC__DDD]
[_AA_BBB__CC_DDD]
[_AA__BBB_CC_DDD]
[__AA_BBB_CC_DDD]
A total of 15 possible configurations.

Configuration (10 cells and [4, 3] blocks):
[__________]
Possibilities:
[AAAA_BBB__]
[AAAA__BBB_]
[AAAA___BBB]
[_AAAA_BBB_]
[_AAAA__BBB]
[__AAAA_BBB]
A total of 6 possible configurations.

Configuration (5 cells and [2, 1] blocks):
[_____]
Possibilities:
[AA_B_]
[AA__B]
[_AA_B]
A total of 3 possible configurations.

Configuration (10 cells and [3, 1] blocks):
[__________]
Possibilities:
[AAA_B_____]
[AAA__B____]
[AAA___B___]
[AAA____B__]
[AAA_____B_]
[AAA______B]
[_AAA_B____]
[_AAA__B___]
[_AAA___B__]
[_AAA____B_]
[_AAA_____B]
[__AAA_B___]
[__AAA__B__]
[__AAA___B_]
[__AAA____B]
[___AAA_B__]
[___AAA__B_]
[___AAA___B]
[____AAA_B_]
[____AAA__B]
[_____AAA_B]
A total of 21 possible configurations.

Configuration (5 cells and [2, 3] blocks):
[_____]
Possibilities:
object.Exception @nonoblock.d(17): Those blocks cannot fit in those cells.
----------------
0x0040AC17 in pure @safe void std.exception.bailOut(immutable(char)[], uint, const(char[]))
...

Python

<lang python>def nonoblocks(blocks, cells):

   if not blocks or blocks[0] == 0:
       yield [(0, 0)]
   else:
       assert sum(blocks) + len(blocks)-1 <= cells, \
           'Those blocks will not fit in those cells'
       blength, brest = blocks[0], blocks[1:]      # Deal with the first block of length
       minspace4rest = sum(1+b for b in brest)     # The other blocks need space
       # Slide the start position from left to max RH index allowing for other blocks.
       for bpos in range(0, cells - minspace4rest - blength + 1):
           if not brest:
               # No other blocks to the right so just yield this one.
               yield [(bpos, blength)]
           else:
               # More blocks to the right so create a *sub-problem* of placing
               # the brest blocks in the cells one space to the right of the RHS of 
               # this block.
               offset = bpos + blength +1
               nonoargs = (brest, cells - offset)  # Pre-compute arguments to nonoargs
               # recursive call to nonoblocks yields multiple sub-positions
               for subpos in nonoblocks(*nonoargs):
                   # Remove the offset from sub block positions
                   rest = [(offset + bp, bl) for bp, bl in subpos]
                   # Yield this block plus sub blocks positions
                   vec = [(bpos, blength)] + rest
                   yield vec

def pblock(vec, cells):

   'Prettyprints each run of blocks with a different letter A.. for each block of filled cells'
   vector = ['_'] * cells
   for ch, (bp, bl) in enumerate(vec, ord('A')):
       for i in range(bp, bp + bl):
           vector[i] = chr(ch) if vector[i] == '_' else'?'
   return '|' + '|'.join(vector) + '|'


if __name__ == '__main__':

   for blocks, cells in (
           ([2, 1], 5),
           ([], 5),
           ([8], 10),
           ([2, 3, 2, 3], 15),
          # ([4, 3], 10),
          # ([2, 1], 5),
          # ([3, 1], 10),
           ([2, 3], 5),
           ):
       print('\nConfiguration:\n    %s # %i cells and %r blocks' % (pblock([], cells), cells, blocks))        
       print('  Possibilities:')
       for i, vector in enumerate(nonoblocks(blocks, cells)):
           print('   ', pblock(vector, cells))
       print('  A total of %i Possible configurations.' % (i+1))</lang>
Output:
Configuration:
    |_|_|_|_|_| # 5 cells and [2, 1] blocks
  Possibilities:
    |A|A|_|B|_|
    |A|A|_|_|B|
    |_|A|A|_|B|
  A total of 3 Possible configurations.

Configuration:
    |_|_|_|_|_| # 5 cells and [] blocks
  Possibilities:
    |_|_|_|_|_|
  A total of 1 Possible configurations.

Configuration:
    |_|_|_|_|_|_|_|_|_|_| # 10 cells and [8] blocks
  Possibilities:
    |A|A|A|A|A|A|A|A|_|_|
    |_|A|A|A|A|A|A|A|A|_|
    |_|_|A|A|A|A|A|A|A|A|
  A total of 3 Possible configurations.

Configuration:
    |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| # 15 cells and [2, 3, 2, 3] blocks
  Possibilities:
    |A|A|_|B|B|B|_|C|C|_|D|D|D|_|_|
    |A|A|_|B|B|B|_|C|C|_|_|D|D|D|_|
    |A|A|_|B|B|B|_|C|C|_|_|_|D|D|D|
    |A|A|_|B|B|B|_|_|C|C|_|D|D|D|_|
    |A|A|_|B|B|B|_|_|C|C|_|_|D|D|D|
    |A|A|_|B|B|B|_|_|_|C|C|_|D|D|D|
    |A|A|_|_|B|B|B|_|C|C|_|D|D|D|_|
    |A|A|_|_|B|B|B|_|C|C|_|_|D|D|D|
    |A|A|_|_|B|B|B|_|_|C|C|_|D|D|D|
    |A|A|_|_|_|B|B|B|_|C|C|_|D|D|D|
    |_|A|A|_|B|B|B|_|C|C|_|D|D|D|_|
    |_|A|A|_|B|B|B|_|C|C|_|_|D|D|D|
    |_|A|A|_|B|B|B|_|_|C|C|_|D|D|D|
    |_|A|A|_|_|B|B|B|_|C|C|_|D|D|D|
    |_|_|A|A|_|B|B|B|_|C|C|_|D|D|D|
  A total of 15 Possible configurations.

Configuration:
    |_|_|_|_|_| # 5 cells and [2, 3] blocks
  Possibilities:
Traceback (most recent call last):
  File "C:/Users/Paddy/Google Drive/Code/nonoblocks.py", line 104, in <module>
    for i, vector in enumerate(nonoblocks(blocks, cells)):
  File "C:/Users/Paddy/Google Drive/Code/nonoblocks.py", line 60, in nonoblocks
    'Those blocks will not fit in those cells'
AssertionError: Those blocks will not fit in those cells

Racket

This implementation does not "error" on the impossible case.

Knowing that there are no solutions (empty result list) is good enough.

Also, the blocks are not identified. I suppose they could be easily enough, but in the nonogram task, these patterns are converted to bit-fields shortly after the nonoblock generation, and bits have no names (sad, but true).

<lang racket>#lang racket (require racket/trace)

(define add1-to-car (match-lambda [(cons (app add1 p1) t) (cons p1 t)]))

inputs
cells -- available cells
blocks -- list of block widths
output
gap-block+gaps
where gap-block+gaps is
(list gap) -- a single gap
(list gap block-width gap-block+gaps) -- padding to left, a block, right hand side

(define (nonoblock cells blocks)

 (match* ((- cells (apply + (length blocks) -1 blocks)) #| padding available on both sides |# blocks)
   [(_ (list)) (list (list cells))] ; generates an empty list of padding
   
   [((? negative?) _) null] ; impossible to satisfy
   
   [((and avp
          ;; use add1 with in-range because we actually want from 0 to available-padding
          ;; without add1, in-range iterates from 0 to (available-padding - 1)
          (app add1 avp+1))
     (list block))
    (for/list ((l-pad (in-range 0 avp+1)))
      (define r-pad (- avp l-pad)) ; what remains goes to right
      (list l-pad block r-pad))]
   
   [((app add1 avp+1) (list block more-blocks ...))
    (for*/list ((l-pad (in-range 0 avp+1))
                (cells-- (in-value (- cells block l-pad 1)))
                (r-blocks (in-value (nonoblock cells-- more-blocks)))
                (r-block (in-list r-blocks)))
      (list* l-pad block (add1-to-car r-block)))])) ; put a single space pad on left of r-block

(define (neat rslt)

 (define dots (curryr make-string #\.))
 (define Xes (curryr make-string #\X))
 (define inr
   (match-lambda
     [(list 0 (app Xes b) t ...)
      (string-append b (inr t))]
     [(list (app dots p) (app Xes b) t ...)
      (string-append p b (inr t))]
     [(list (app dots p)) p]))
 (define (neat-row r)
   (string-append "|" (inr r) "|"))
 (string-join (map neat-row rslt) "\n"))

(define (tst c b)

 (define rslt (nonoblock c b))
 (define rslt-l (length rslt))
 (printf "~a cells, ~a blocks => ~a~%~a~%" c b
         (match rslt-l
           [0 "impossible"]
           [1 "1 solution"]
           [(app (curry format "~a solutions") r) r])
         (neat rslt)))

(module+ test

 (tst  5 '[2 1])
 (tst  5 '[])
 (tst 10 '[8])
 (tst 15 '[2 3 2 3])
 (tst  5 '[2 3]))</lang>
Output:
5 cells, (2 1) blocks => 3 solutions
|XX.X.|
|XX..X|
|.XX.X|
5 cells, () blocks => 1 solution
|.....|
10 cells, (8) blocks => 3 solutions
|XXXXXXXX..|
|.XXXXXXXX.|
|..XXXXXXXX|
15 cells, (2 3 2 3) blocks => 15 solutions
|XX.XXX.XX.XXX..|
|XX.XXX.XX..XXX.|
|XX.XXX.XX...XXX|
|XX.XXX..XX.XXX.|
|XX.XXX..XX..XXX|
|XX.XXX...XX.XXX|
|XX..XXX.XX.XXX.|
|XX..XXX.XX..XXX|
|XX..XXX..XX.XXX|
|XX...XXX.XX.XXX|
|.XX.XXX.XX.XXX.|
|.XX.XXX.XX..XXX|
|.XX.XXX..XX.XXX|
|.XX..XXX.XX.XXX|
|..XX.XXX.XX.XXX|
5 cells, (2 3) blocks => impossible

Ruby

Simple version: <lang ruby>def nonoblocks(cell, blocks)

 raise 'Those blocks will not fit in those cells' if cell < blocks.inject(0,:+) + blocks.size - 1
 nblock(cell, blocks, , [])

end

def nblock(cell, blocks, position, result)

 if cell <= 0
   result << position[0..cell-1]
 elsif blocks.empty? or blocks[0].zero?
   result << position + '.' * cell
 else
   rest = cell - blocks.inject(:+) - blocks.size + 2
   bl, brest = blocks[0], blocks.drop(1)
   rest.times.inject(result) do |res, i|
     nblock(cell-i-bl-1, brest, position + '.'*i + '#'*bl + '.', res)
   end
 end

end

conf = [[ 5, [2, 1]],

       [ 5, []],
       [10, [8]],
       [15, [2, 3, 2, 3]],
       [ 5, [2, 3]],      ]

conf.each do |cell, blocks|

 begin
   puts "#{cell} cells and #{blocks} blocks"
   result = nonoblocks(cell, blocks)
   puts result, result.size, ""
 rescue => e
   p e
 end

end</lang>

Output:
5 cells and [2, 1] blocks
##.#.
##..#
.##.#
3

5 cells and [] blocks
.....
1

10 cells and [8] blocks
########..
.########.
..########
3

15 cells and [2, 3, 2, 3] blocks
##.###.##.###..
##.###.##..###.
##.###.##...###
##.###..##.###.
##.###..##..###
##.###...##.###
##..###.##.###.
##..###.##..###
##..###..##.###
##...###.##.###
.##.###.##.###.
.##.###.##..###
.##.###..##.###
.##..###.##.###
..##.###.##.###
15

5 cells and [2, 3] blocks
#<RuntimeError: Those blocks will not fit in those cells>

Class version

The output form consulted the one of the python. <lang ruby>class NonoBlock

 def initialize(cell, blocks)
   raise 'Those blocks will not fit in those cells' if cell < blocks.inject(0,:+) + blocks.size - 1
   @result = []
   nonoblocks(cell, blocks, )
 end
 
 def result(correct=true)
   correct ? @result.map(&:nonocell) : @result
 end
 
 private
 def nonoblocks(cell, blocks, position)
   if cell <= 0
     @result << position[0..cell-1]
   elsif blocks.empty? or blocks[0].zero?
     @result << position + '.' * cell
   else
     rest = cell - blocks.inject(0,:+) - blocks.size + 2
     bl, brest = blocks[0], blocks.drop(1)
     rest.times do |i|
       nonoblocks(cell-i-bl-1, brest, position + '.'*i + '#'*bl + '.')
     end
   end
 end

end

class String

 def nonocell                  # "##.###..##" -> "|A|A|_|B|B|B|_|_|C|C|"
   chr = ('A'..'Z').each
   s = tr('.','_').gsub(/#+/){|sharp| chr.next * sharp.size}
   "|#{s.chars.join('|')}|"
 end

end

if __FILE__ == $0

 conf = [[ 5, [2, 1]],
         [ 5, []],
         [10, [8]],
         [15, [2, 3, 2, 3]],
         [ 5, [2, 3]]       ]
 conf.each do |cell, blocks|
   begin
     puts "Configuration:",
          "#{('.'*cell).nonocell} # #{cell} cells and #{blocks} blocks",
          "Possibilities:"
     result = NonoBlock.new(cell, blocks).result
     puts result,
          "A total of #{result.size} Possible configurations.", ""
   rescue => e
     p e
   end
 end

end</lang>

Output:
Configuration:
|_|_|_|_|_| # 5 cells and [2, 1] blocks
Possibilities:
|A|A|_|B|_|
|A|A|_|_|B|
|_|A|A|_|B|
A total of 3 Possible configurations.

Configuration:
|_|_|_|_|_| # 5 cells and [] blocks
Possibilities:
|_|_|_|_|_|
A total of 1 Possible configurations.

Configuration:
|_|_|_|_|_|_|_|_|_|_| # 10 cells and [8] blocks
Possibilities:
|A|A|A|A|A|A|A|A|_|_|
|_|A|A|A|A|A|A|A|A|_|
|_|_|A|A|A|A|A|A|A|A|
A total of 3 Possible configurations.

Configuration:
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| # 15 cells and [2, 3, 2, 3] blocks
Possibilities:
|A|A|_|B|B|B|_|C|C|_|D|D|D|_|_|
|A|A|_|B|B|B|_|C|C|_|_|D|D|D|_|
|A|A|_|B|B|B|_|C|C|_|_|_|D|D|D|
|A|A|_|B|B|B|_|_|C|C|_|D|D|D|_|
|A|A|_|B|B|B|_|_|C|C|_|_|D|D|D|
|A|A|_|B|B|B|_|_|_|C|C|_|D|D|D|
|A|A|_|_|B|B|B|_|C|C|_|D|D|D|_|
|A|A|_|_|B|B|B|_|C|C|_|_|D|D|D|
|A|A|_|_|B|B|B|_|_|C|C|_|D|D|D|
|A|A|_|_|_|B|B|B|_|C|C|_|D|D|D|
|_|A|A|_|B|B|B|_|C|C|_|D|D|D|_|
|_|A|A|_|B|B|B|_|C|C|_|_|D|D|D|
|_|A|A|_|B|B|B|_|_|C|C|_|D|D|D|
|_|A|A|_|_|B|B|B|_|C|C|_|D|D|D|
|_|_|A|A|_|B|B|B|_|C|C|_|D|D|D|
A total of 15 Possible configurations.

Configuration:
|_|_|_|_|_| # 5 cells and [2, 3] blocks
Possibilities:
#<RuntimeError: Those blocks will not fit in those cells>

Tcl

Works with: Tcl version 8.6
Library: Tcllib (Package: generator)
Translation of: Python

<lang tcl>package require Tcl 8.6 package require generator

generator define nonoblocks {blocks cells} {

   set sum [tcl::mathop::+ {*}$blocks]
   if {$sum == 0 || [lindex $blocks 0] == 0} {

generator yield Template:0 0 return

   } elseif {$sum + [llength $blocks] - 1 > $cells} {

error "those blocks will not fit in those cells"

   }
   set brest [lassign $blocks blen]
   for {set bpos 0} {$bpos <= $cells - $sum - [llength $brest]} {incr bpos} {

if {![llength $brest]} { generator yield [list [list $bpos $blen]] return } set offset [expr {$bpos + $blen + 1}] generator foreach subpos [nonoblocks $brest [expr {$cells - $offset}]] { generator yield [linsert [lmap b $subpos { lset b 0 [expr {[lindex $b 0] + $offset}] }] 0 [list $bpos $blen]] }

   }

}

if {[info script] eq $::argv0} {

   proc pblock {cells {vec {}}} {

set vector [lrepeat $cells "_"] set ch 64 foreach b $vec { incr ch lassign $b bp bl for {set i $bp} {$i < $bp + $bl} {incr i} { lset vector $i [format %c $ch] } } return |[join $vector "|"]|

   }
   proc flist {items} {

return [format "\[%s\]" [join $items ", "]]

   }
   foreach {blocks cells} {

{2 1} 5 {} 5 {8} 10 {2 3 2 3} 15 {2 3} 5

   } {

puts "\nConfiguration:" puts [format "%s # %d cells and %s blocks" \ [pblock $cells] $cells [flist $blocks]] puts " Possibilities:" set i 0 try { generator foreach vector [nonoblocks $blocks $cells] { puts " [pblock $cells $vector]" incr i } puts " A total of $i possible configurations" } on error msg { puts " --> ERROR: $msg" }

   }

}

package provide nonoblock 1</lang>

Output:

Configuration:
|_|_|_|_|_| # 5 cells and [2, 1] blocks
  Possibilities:
    |A|A|_|B|_|
    |A|A|_|_|B|
    |_|A|A|_|B|
  A total of 3 possible configurations

Configuration:
|_|_|_|_|_| # 5 cells and [] blocks
  Possibilities:
    |_|_|_|_|_|
  A total of 1 possible configurations

Configuration:
|_|_|_|_|_|_|_|_|_|_| # 10 cells and [8] blocks
  Possibilities:
    |A|A|A|A|A|A|A|A|_|_|
    |_|A|A|A|A|A|A|A|A|_|
    |_|_|A|A|A|A|A|A|A|A|
  A total of 3 possible configurations

Configuration:
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| # 15 cells and [2, 3, 2, 3] blocks
  Possibilities:
    |A|A|_|B|B|B|_|C|C|_|D|D|D|_|_|
    |A|A|_|B|B|B|_|C|C|_|_|D|D|D|_|
    |A|A|_|B|B|B|_|C|C|_|_|_|D|D|D|
    |A|A|_|B|B|B|_|_|C|C|_|D|D|D|_|
    |A|A|_|B|B|B|_|_|C|C|_|_|D|D|D|
    |A|A|_|B|B|B|_|_|_|C|C|_|D|D|D|
    |A|A|_|_|B|B|B|_|C|C|_|D|D|D|_|
    |A|A|_|_|B|B|B|_|C|C|_|_|D|D|D|
    |A|A|_|_|B|B|B|_|_|C|C|_|D|D|D|
    |A|A|_|_|_|B|B|B|_|C|C|_|D|D|D|
    |_|A|A|_|B|B|B|_|C|C|_|D|D|D|_|
    |_|A|A|_|B|B|B|_|C|C|_|_|D|D|D|
    |_|A|A|_|B|B|B|_|_|C|C|_|D|D|D|
    |_|A|A|_|_|B|B|B|_|C|C|_|D|D|D|
    |_|_|A|A|_|B|B|B|_|C|C|_|D|D|D|
  A total of 15 possible configurations

Configuration:
|_|_|_|_|_| # 5 cells and [2, 3] blocks
  Possibilities:
    --> ERROR: those blocks will not fit in those cells