nonogram.Editor

new nonogram.Editor(
  4,
  8,
  'edit1',
  { theme: {
    threshold: 0.2,
    isMeshed: true,
    boldMeshGap: 2 }})
new nonogram.Editor(
  2,
  4,
  'edit2',
  { grid: [[1, 0, 1, 0], [0, 1, 0, 1]] })

nonogram.Game

new nonogram.Game(
  [[1,1], [1,1]],
  [[1], [1], [1], [1]],
  'play1',
  { theme: {
    filledColor: '#0c6',
    correctColor: '#0c6' }})
new nonogram.Game(
  [[1,1], [1,1]],
  [[2], [], [2], []],
  'play2')

nonogram.Solver

new nonogram.Solver(
  [[1,1], [1,1]],
  [[2], [], [2], []],
  'solve1',
  { delay: 200, onError: (error) => console.error(error) }
).solve()

The following nonogram produces a valid QRCode that, once decoded, reveals the text: You are the BEST!

window._row = [
  [7, 2, 2, 7],
  [1, 1, 1, 2, 1, 1],
  [1, 3, 1, 3, 1, 1, 3, 1],
  [1, 3, 1, 2, 1, 1, 3, 1],
  [1, 3, 1, 2, 1, 3, 1],
  [1, 1, 2, 2, 1, 1],
  [7, 1, 1, 1, 7],
  [2],
  [2, 3, 2, 1, 4],
  [1, 1, 3, 3, 2, 1],
  [3, 1, 3, 2, 2],
  [1, 1, 1, 3, 1, 1],
  [1, 5, 1, 1, 1, 1],
  [1, 1, 1, 1, 3, 1],
  [7, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1],
  [1, 3, 1, 1, 1, 2, 2],
  [1, 3, 1, 2, 1, 2, 1, 1],
  [1, 3, 1, 1, 1, 2],
  [1, 1, 2, 1, 1],
  [7, 1, 3, 1],
]
window._column = [
  [7, 1, 2, 7],
  [1, 1, 1, 1, 1, 1],
  [1, 3, 1, 1, 1, 3, 1],
  [1, 3, 1, 1, 1, 1, 3, 1],
  [1, 3, 1, 1, 1, 1, 3, 1],
  [1, 1, 2, 1, 1],
  [7, 1, 1, 1, 7],
  [4],
  [4, 2, 2, 2, 2, 2],
  [1, 2, 1, 1, 1, 2, 3],
  [1, 2, 2, 2],
  [2, 3, 1, 1, 1, 1, 1],
  [3, 3, 2, 3, 1, 1],
  [1, 1, 3, 2],
  [7, 1, 1],
  [1, 1, 1, 1, 1, 1, 1],
  [1, 3, 1, 3, 2, 3],
  [1, 3, 1, 2, 2, 1, 1],
  [1, 3, 1, 1, 1, 1, 1],
  [1, 1, 5, 3],
  [7, 1, 1, 2, 1],
]
new nonogram.Solver(
  window._row,
  window._column,
  'solve2',
  { onError: (error) => console.error(error) }
).solve()

Advanced Usages

Create Your Own Nonogram

Here is an example showing how to use listeners to create nonogram.Game and nonogram.Solver instances automatically.

Save nonogram pattern as image Save nonogram grid & hints as image Save nonogram hints as JSON
function f(grid) {
  var m = +document.getElementById('m').value,
      n = +document.getElementById('n').value;
  if ((grid && grid.length) || window.advEditor1) {
    if (grid && grid.length) {
      m = grid.length;
      n = grid[0].length;
    } else {
      grid = window.advEditor1.grid;
    }
  }
  window.advEditor1 = new nonogram.Editor(
    m, n,
    'adv-edit1',
    { threshold: +document.getElementById('threshold').value,
      grid: grid,
      onHintChange(row, column) {
        new nonogram.Game(row, column, 'adv-play1', { theme: { boldMeshGap: 0 } })
        new nonogram.Solver(row, column, 'adv-solve1', { onError: (error) => console.error(error) }).solve()
      } })
  document.getElementById('pattern-download-link').onclick = function () {
    this.href = gridToBase64Img(window.advEditor1.grid)
  }
  document.getElementById('hints-grid-download-link').onclick = function () {
    this.href = document.getElementById('adv-play1').toDataURL()
  }
  document.getElementById('hints-as-json-link').onclick = function () {
    this.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(window.advEditor1.hints))
  }
}
document.getElementById('m').addEventListener('change', f)
document.getElementById('n').addEventListener('change', f)
document.getElementById('threshold').addEventListener('change', f)
document.getElementById('load-from-file').addEventListener('change', () => gridFromImgFile((grid) => {
  document.getElementById('m').value = grid.length;
  document.getElementById('n').value = grid[0].length;
  f(grid)
}))
f()

Interactive Nonogram Solver

This example shows how to solve the nonogram by given hints, which are separated by newlines and commas.

Put your rows in the first textarea, columns in the second textarea. One row (or column) a line, numbers separated by any non-numerical characters. To represent an empty row (or column), you can use an empty line, or a line including the number 0 as well. However, empty lines at two ends will be dropped, since they are really unnecessary.

If the process end with an error, please check your input carefully.

Delay: 0~500ms. Setting delay=0 disables step-by-step solution, with a much smaller time cost.

Parse JSON-like strings or anything.

function parseArray(text) {
  return text
    .replace(/[^\d\n]+/g, ' ')
    .trim()
    .split('\n')
    .map(row => (row.match(/\d+/g) || [])
      .map(parseFloat)
      .filter(Math.sign))
}

document.getElementById('btn-solve2').addEventListener('click', () => {
  new nonogram.Solver(
    parseArray(document.getElementById('txt-row-hints2').value),
    parseArray(document.getElementById('txt-col-hints2').value),
    'adv-solve2',
    { theme: {
        isMeshed: true,
        isBoldMeshOnly: true,
        isMeshOnTop: true },
      delay: +document.getElementById('delay').value,
      onError: (error) => console.error(error),
      onSuccess(time) {
        document.getElementById('timecost').innerHTML = 'Solved in ' + time + 'ms.'
      } }).solve()
})

Craft nonograms that reveal QRCodes

The qrcode lib is used in this example to try to build a solvable grid in the form of a valid QRCode that, once decoded, reveals some text:

function rebuildQRCodeSolver() {
  var text = document.getElementById('text').value;
  var qrcode = QRCode.create(text, { errorCorrectionLevel: 'L' });
  // Getting the hints for this grid:
  var size = qrcode.modules.size;
  var data = [].slice.call(qrcode.modules.data);
  var grid = [];
  while(data.length) {
    grid.push(data.splice(0, size));
  }
  var hints = nonogram.Editor.getHints(grid);
  // Displaying a Solver that will check if a unique solution exists:
  var downloadPatternLink = document.getElementById('qrcode-pattern-download-link');
  var downloadJsonLink = document.getElementById('qrcode-hints-as-json-link');
  var downloadGridLink = document.getElementById('qrcode-hints-grid-download-link');
  var solver = new nonogram.Solver(hints.row, hints.column, 'edit-qrcode', {
    onSuccess(duration, iterations, solved) {
      downloadPatternLink.style.display = solved ? 'block' : 'none';
      downloadJsonLink.style.display = solved ? 'block' : 'none';
      downloadGridLink.style.display = solved ? 'block' : 'none';
      if (solved) {  // == the grid has a unique solution
        downloadPatternLink.onclick = function () {
          this.href = gridToBase64Img(grid)
        }
        downloadJsonLink.onclick = function () {
          this.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(hints))
        }
        // Using a hidden Game/canvas in order to produce a downloable grid:
        class GameWithoutController extends nonogram.Game {
          printController() {}  // We hide the "controller", i.e. the bottom-right switch button
        }
        new GameWithoutController(hints.row, hints.column, 'hidden-game', { theme: { boldMeshGap: 0 } })
        downloadGridLink.onclick = function () {
          this.href = document.getElementById('hidden-game').toDataURL()
        }
      }
    },
  });
  solver.solve()
}
window.rebuildQRCodeSolver = rebuildQRCodeSolver
rebuildQRCodeSolver()