How to select next best block from next k blocks to save white space?

I tried to implement the following using AI but gave up after ~100 iterations…
Here is the algorithm:

  • the page has 2 columns, all blocks are breakable and come from a database, I can create an array of blocks and want to render them at width: 100%.
  • I want to render the blocks one by another using this logic:
  • if the current block C fits completely on current page at full height, use this one.
  • otherwise: search in the next k (for example k=4) blocks and find the block with the highest height that fits completely on the current page. Use this one instead. If no one from this k blocks fit, then we use C. If one from the k blocks fitted: keep in mind that C is the prefered in the next iteration.
  • repeat this after all blocks are rendered.

This would minimize white space on the page quite good because those blocks have different heights. The constant k is limited for performance reasons but also because the elements are sorted by time, so a newer element should not appear at a very late position.

Is there already a package that implements this?

AI gives me some code like this:

#set page(
  paper: "a4",
  margin: 1.5cm,
  columns: 2,
)
#set par(
  spacing: 0pt,
)


#let page-height = 26.7cm
#let k = 4

#let heights = (
  4.2cm, 8.7cm, 3.4cm, 6.1cm,
  9.3cm, 5.2cm, 7.8cm, 3.8cm,
  6.9cm, 9.8cm, 4.7cm, 7.1cm,
  5.6cm, 8.1cm, 3.2cm, 6.5cm,

  7.4cm, 4.9cm, 8.3cm, 3.7cm,
  6.4cm, 9.1cm, 5.5cm, 7.7cm,
  4.1cm, 8.9cm, 3.5cm, 6.8cm,
  9.6cm, 5.8cm, 7.2cm, 4.6cm,

  8.4cm, 3.9cm, 6.3cm, 9.2cm,
  5.1cm, 7.9cm, 4.4cm, 8.6cm,
  3.6cm, 6.7cm, 9.7cm, 5.4cm,
  7.3cm, 4.8cm, 8.2cm, 3.3cm,
)


#let blocks = heights.enumerate().map(((i, height)) => {
  rect(
    width: 100%,
    height: height - 1pt,
    fill: white,
    stroke: 0.5pt + black,
    inset: 5pt,
  )[
    #align(center + horizon)[
      #text(size: 14pt)[#i]
    ]
  ]
})

#let remaining = blocks
#let remaining-heights = heights
#let used = 0cm

#while remaining.len() > 0 {
  let first-height = remaining-heights.at(0)
  let selected = 0

  if used + first-height > page-height {
    let best-height = 0cm
    let found = false

    for i in range(0, calc.min(k, remaining.len())) {
      let h = remaining-heights.at(i)

      if used + h <= page-height {
        if not found or h > best-height {
          selected = i
          best-height = h
          found = true
        }
      }
    }

    if not found {
      selected = 0
    }
  }

  remaining.at(selected)

  used += remaining-heights.at(selected)

  remaining = (
    remaining.slice(0, selected)
    + remaining.slice(selected + 1)
  )

  remaining-heights = (
    remaining-heights.slice(0, selected)
    + remaining-heights.slice(selected + 1)
  )
}

Does only work for the first page and does not use measure. For my real blocks I do not know the heights.