nonograms
change
commit 1cc92ca4e0b377c1820e2c8edaf8c5de170b3451
author Alpha Chen <alpha@kejadlen.dev>
date
parent 0184c9ea
diff --git a/sentient/gchq.json b/sentient/gchq.json
new file mode 100644
index 0000000..ed95b13
--- /dev/null
+++ b/sentient/gchq.json
@@ -0,0 +1,81 @@
+{
+  "rowNumbers": [
+    [7, 3, 1, 1, 7],
+    [1, 1, 2, 2, 1, 1],
+    [1, 3, 1, 3, 1, 1, 3, 1],
+    [1, 3, 1, 1, 6, 1, 3, 1],
+    [1, 3, 1, 5, 2, 1, 3, 1],
+    [1, 1, 2, 1, 1],
+    [7, 1, 1, 1, 1, 1, 7],
+    [3, 3],
+    [1, 2, 3, 1, 1, 3, 1, 1, 2],
+    [1, 1, 3, 2, 1, 1],
+    [4, 1, 4, 2, 1, 2],
+    [1, 1, 1, 1, 1, 4, 1, 3],
+    [2, 1, 1, 1, 2, 5],
+    [3, 2, 2, 6, 3, 1],
+    [1, 9, 1, 1, 2, 1],
+    [2, 1, 2, 2, 3, 1],
+    [3, 1, 1, 1, 1, 5, 1],
+    [1, 2, 2, 5],
+    [7, 1, 2, 1, 1, 1, 3],
+    [1, 1, 2, 1, 2, 2, 1],
+    [1, 3, 1, 4, 5, 1],
+    [1, 3, 1, 3, 10, 2],
+    [1, 3, 1, 1, 6, 6],
+    [1, 1, 2, 1, 1, 2],
+    [7, 2, 1, 2, 5]
+  ],
+  "columnNumbers": [
+    [7, 2, 1, 1, 7],
+    [1, 1, 2, 2, 1, 1],
+    [1, 3, 1, 3, 1, 3, 1, 3, 1],
+    [1, 3, 1, 1, 5, 1, 3, 1],
+    [1, 3, 1, 1, 4, 1, 3, 1],
+    [1, 1, 1, 2, 1, 1],
+    [7, 1, 1, 1, 1, 1, 7],
+    [1, 1, 3],
+    [2, 1, 2, 1, 8, 2, 1],
+    [2, 2, 1, 2, 1, 1, 1, 2],
+    [1, 7, 3, 2, 1],
+    [1, 2, 3, 1, 1, 1, 1, 1],
+    [4, 1, 1, 2, 6],
+    [3, 3, 1, 1, 1, 3, 1],
+    [1, 2, 5, 2, 2],
+    [2, 2, 1, 1, 1, 1, 1, 2, 1],
+    [1, 3, 3, 2, 1, 8, 1],
+    [6, 2, 1],
+    [7, 1, 4, 1, 1, 3],
+    [1, 1, 1, 1, 4],
+    [1, 3, 1, 3, 7, 1],
+    [1, 3, 1, 1, 1, 2, 1, 1, 4],
+    [1, 3, 1, 4, 3, 3],
+    [1, 1, 2, 2, 2, 6, 1],
+    [7, 1, 3, 2, 1, 1]
+  ],
+  "grid": {
+    "3": {
+      "3": true, "4": true,
+      "12": true, "13": true,
+      "21": true
+    },
+    "8": {
+      "6": true, "7": true,
+      "10": true,
+      "14": true, "15": true,
+      "18": true
+    },
+    "16": {
+      "6": true,
+      "11": true,
+      "16": true,
+      "20": true
+    },
+    "21": {
+      "3": true, "4": true,
+      "9": true, "10": true,
+      "15": true,
+      "20": true, "21": true
+    }
+  }
+}
diff --git a/sentient/nonogram.json b/sentient/nonogram.json
new file mode 100644
index 0000000..e69de29
diff --git a/sentient/nonogram.snt b/sentient/nonogram.snt
new file mode 100644
index 0000000..0a09a8b
--- /dev/null
+++ b/sentient/nonogram.snt
@@ -0,0 +1,49 @@
+function main () {
+  array25<array25<bool>> grid;
+  array25<array13<int6>> rowNumbers, columnNumbers;
+
+  rowNumbers = removeZeros(rowNumbers);
+  columnNumbers = removeZeros(columnNumbers);
+
+  grid.validate!(rowNumbers);
+  grid.transpose.validate!(columnNumbers);
+
+  expose grid, rowNumbers, columnNumbers;
+};
+
+function removeZeros (numbersArray) {
+  return numbersArray.map(function (numbers) {
+    return numbers.reject(*zero?);
+  });
+};
+
+function validate! (grid, rowNumbers) {
+  rowNumbers.each(function^ (numbers, rowIndex) {
+    cells = grid[rowIndex];
+
+    previous = false;
+    count = 0;
+    index = -1;
+
+    cells.each(function^ (cell) {
+      edge = cell != previous;
+      endOfRun = edge && !cell;
+
+      tickOffNumberIf(endOfRun);
+
+      count = edge ? 1 : count + 1;
+      previous = cell;
+    });
+
+    tickOffNumberIf(previous);
+    invariant index == numbers.length - 1;
+  });
+};
+
+function^ tickOffNumberIf (condition) {
+  index += condition ? 1 : 0;
+  number, inBounds = numbers.get(index);
+  invariant condition ? number == count : true;
+};
+
+main();
\ No newline at end of file
diff --git a/z3/Gemfile b/z3/Gemfile
new file mode 100644
index 0000000..6306eb5
--- /dev/null
+++ b/z3/Gemfile
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+
+gem "z3"
diff --git a/z3/Gemfile.lock b/z3/Gemfile.lock
new file mode 100644
index 0000000..284ef12
--- /dev/null
+++ b/z3/Gemfile.lock
@@ -0,0 +1,15 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    ffi (1.15.0)
+    z3 (0.0.20181229)
+      ffi (~> 1.9)
+
+PLATFORMS
+  x86_64-darwin-17
+
+DEPENDENCIES
+  z3
+
+BUNDLED WITH
+   2.2.16
diff --git a/z3/hello.py b/z3/hello.py
new file mode 100644
index 0000000..f1edaf4
--- /dev/null
+++ b/z3/hello.py
@@ -0,0 +1,5 @@
+from z3 import *
+
+x = Int('x')
+y = Int('y')
+solve(x > 2, y < 10, x + 2*y == 7)
diff --git a/z3/red-vs-blue.py b/z3/red-vs-blue.py
new file mode 100644
index 0000000..c0d5e4c
--- /dev/null
+++ b/z3/red-vs-blue.py
@@ -0,0 +1,155 @@
+from itertools import *
+from z3 import *
+
+row_rules = [
+  [17, 4],
+  [17, 1, 3],
+  [14, 2, 3],
+  [12, 1, 3],
+  [9, 2, 2, 1],
+
+  [2, 7, 1, 4],
+  [4, 4, 3, 1, 1],
+  [1, 3, 2, 1, 3, 1, 1],
+  [3, 2, 3, 2],
+  [3, 1, 2, 3, 1],
+
+  [2, 5, 2, 1, 3, 1],
+  [1, 8, 1, 2, 3],
+  [1, 11, 1, 1],
+  [2, 1, 2, 6, 4, 2],
+  [1, 1, 4, 3, 1],
+
+  [2, 2, 1, 3, 2],
+  [1, 2, 3, 1],
+  [1, 2, 1, 3],
+  [4, 2, 1],
+  [3, 1, 2],
+
+  [3, 2, 1],
+  [3, 1, 1, 2],
+  [3, 2, 1],
+  [3, 2, 2],
+  [3, 2, 1],
+
+  [4, 3, 2],
+  [2, 3, 3, 1],
+  [3, 4, 4],
+  [6, 1, 1],
+  [4],
+]
+
+col_rules = [
+  [3, 2, 2, 1, 1, 11],
+  [3, 3, 2, 1, 2, 11],
+  [3, 4, 2, 3, 6, 2],
+  [3, 4, 3, 6, 1],
+  [3, 7, 1, 6],
+
+  [3, 4, 4, 7],
+  [3, 2, 6, 6],
+  [3, 8, 3, 1],
+  [3, 8, 2, 1],
+  [3, 8, 2, 2],
+
+  [3, 6, 1, 1],
+  [3, 3, 2, 6],
+  [3, 1, 4, 5],
+  [3, 4, 2, 5],
+  [6, 7, 1, 4],
+
+  [6, 9, 2, 4],
+  [6, 1, 9, 1, 3],
+  [6, 4, 9, 2, 3],
+  [4, 6, 9, 1, 2],
+  [2, 1, 3, 9, 2, 2],
+
+  [4, 1, 2, 11, 1],
+  [6, 5, 9, 2],
+  [7, 6, 6, 4],
+  [6, 6, 4, 6],
+  [1, 7, 6, 1, 8],
+
+  [1, 1, 3, 6, 10],
+  [2, 1, 1, 4, 12],
+  [2, 1, 2, 1, 1, 14],
+  [3, 1, 3, 16],
+  [3, 4, 18],
+]
+
+def pairwise(iterable):
+    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
+    a, b = tee(iterable)
+    next(b, None)
+    return zip(a, b)
+
+def cell(x, y):
+  return Bool(f"cell-{x},{y}")
+
+def row(y):
+  return [cell(x, y) for x in range(0, len(col_rules))]
+
+def col(x):
+  return [cell(x, y) for y in range(0, len(row_rules))]
+
+s = Solver()
+
+for x, col_rule in enumerate(col_rules):
+  stripe_name = f"col-{x}"
+  stripe_constraints = col_rule
+  stripe = col(x)
+  group_count = len(stripe_constraints)
+  group_start = [Int(f"{stripe_name}-{i}-start") for i in range(0, group_count)]
+  group_end = [Int(f"{stripe_name}-{i}-end") for i in range(0, group_count)]
+
+  # # Start and end of each group
+  for i in range(0, group_count):
+    s.add(group_start[i] >= 0, group_start[i] < len(stripe))
+    s.add(group_end[i] >= 0, group_end[i] < len(stripe))
+    s.add(group_end[i] - group_start[i] == stripe_constraints[i] - 1)
+
+  # Gap between each group and following group
+  for i, j in pairwise(range(0, group_count)):
+    s.add(group_start[j] >= group_end[i] + 2)
+
+  # Cells
+  for k in range(0, len(stripe)):
+    cell_in_specific_group = [
+        And(k >= group_start[i], k <= group_end[i])
+        for i in range(0, group_count)
+    ]
+    s.add(stripe[k] == Or(*cell_in_specific_group))
+
+for y, row_rule in enumerate(row_rules):
+  stripe_name = f"row-{y}"
+  stripe_constraints = row_rule
+  stripe = row(y)
+  group_count = len(stripe_constraints)
+  group_start = [Int(f"{stripe_name}-{i}-start") for i in range(0, group_count)]
+  group_end = [Int(f"{stripe_name}-{i}-end") for i in range(0, group_count)]
+
+  # # Start and end of each group
+  for i in range(0, group_count):
+    s.add(group_start[i] >= 0, group_start[i] < len(stripe))
+    s.add(group_end[i] >= 0, group_end[i] < len(stripe))
+    s.add(group_end[i] - group_start[i] == stripe_constraints[i] - 1)
+
+  # Gap between each group and following group
+  for i, j in pairwise(range(0, group_count)):
+    s.add(group_start[j] >= group_end[i] + 2)
+
+  # Cells
+  for k in range(0, len(stripe)):
+    cell_in_specific_group = [
+        And(k >= group_start[i], k <= group_end[i])
+        for i in range(0, group_count)
+    ]
+    s.add(stripe[k] == Not(Or(*cell_in_specific_group)))
+
+s.check()
+m = s.model()
+
+for y in range(0, len(row_rules)):
+  row = "".join(["█" if m[cell(x,y)] else " " for x in range(0, len(col_rules))])
+  print(row)
+
diff --git a/z3/red-vs-blue.rb b/z3/red-vs-blue.rb
new file mode 100644
index 0000000..399def2
--- /dev/null
+++ b/z3/red-vs-blue.rb
@@ -0,0 +1,120 @@
+require "z3"
+
+class NonogramSolver
+  def initialize
+    @row_constraints = [
+      [3],
+      [5],
+      [3,1],
+      [2,1],
+      [3,3,4],
+      [2,2,7],
+      [6,1,1],
+      [4,2,2],
+      [1,1],
+      [3,1],
+      [6],
+      [2,7],
+      [6,3,1],
+      [1,2,2,1,1],
+      [4,1,1,3],
+      [4,2,2],
+      [3,3,1],
+      [3,3],
+      [3],
+      [2,1],
+    ]
+    @column_constraints = [
+      [2],
+      [1,2],
+      [2,3],
+      [2,3],
+      [3,1,1],
+      [2,1,1],
+      [1,1,1,2,2],
+      [1,1,3,1,3],
+      [2,6,4],
+      [3,3,9,1],
+      [5,3,2],
+      [3,1,2,2],
+      [2,1,7],
+      [3,3,2],
+      [2,4],
+      [2,1,2],
+      [2,2,1],
+      [2,2],
+      [1],
+      [1],
+    ]
+    @row_count = @row_constraints.size
+    @column_count = @column_constraints.size
+    @solver = Z3::Solver.new
+  end
+
+  def cell(x,y)
+    Z3.Bool("cell#{x},#{y}")
+  end
+
+  def row(y)
+    (0...@column_count).map{|x| cell(x,y) }
+  end
+
+  def column(x)
+    (0...@row_count).map{|y| cell(x,y) }
+  end
+
+  def setup_grid_constraints!
+    (0...@column_count).each do |x|
+      setup_stripe_constraints! "column-#{x}", @column_constraints[x], column(x)
+    end
+
+    (0...@row_count).each do |y|
+      setup_stripe_constraints! "row-#{y}", @row_constraints[y], row(y)
+    end
+  end
+
+  def setup_stripe_constraints!(stripe_name, stripe_constraints, stripe)
+    group_count = stripe_constraints.size
+    group_start = (0...group_count).map{|i| Z3.Int("#{stripe_name}-#{i}-start")}
+    group_end = (0...group_count).map{|i| Z3.Int("#{stripe_name}-#{i}-end")}
+
+    # Start and end of each group
+    (0...group_count).each do |i|
+      @solver.assert (group_start[i] >= 0) & (group_start[i] < stripe.size)
+      @solver.assert (group_end[i] >= 0) & (group_end[i] < stripe.size)
+      @solver.assert group_end[i] - group_start[i] == stripe_constraints[i] - 1
+    end
+    # Gap between each group and following group
+    (0...group_count).each_cons(2) do |i,j|
+      @solver.assert group_start[j] >= group_end[i] + 2
+    end
+    # Cells
+    (0...stripe.size).each do |k|
+      cell_in_specific_group = (0...group_count).map{|i|
+        (k >= group_start[i]) & (k <= group_end[i])
+      }
+      @solver.assert stripe[k] == Z3.Or(*cell_in_specific_group)
+    end
+  end
+
+  def solve!
+    setup_grid_constraints!
+    if @solver.satisfiable?
+      model = @solver.model
+      (0...@row_count).each do |y|
+        (0...@column_count).each do |x|
+          value = model[cell(x,y)].to_s
+          print value == "true" ? "\u25FC" : "\u25FB"
+        end
+        print "\n"
+      end
+    else
+      puts "Nonogram has no solution"
+    end
+  end
+end
+
+if __FILE__ == $0
+  nonogram = NonogramSolver.new
+  nonogram.solve!
+end
diff --git a/z3/shell.nix b/z3/shell.nix
new file mode 100644
index 0000000..c1aa3fe
--- /dev/null
+++ b/z3/shell.nix
@@ -0,0 +1,15 @@
+{ pkgs ? import <nixpkgs> {} }:
+let
+  my-python = pkgs.python3.buildEnv.override {
+    extraLibs = [ pkgs.python3Packages.z3 ];
+  };
+in
+
+pkgs.mkShell {
+  nativeBuildInputs = [
+    pkgs.go
+    my-python
+    pkgs.ruby
+    pkgs.z3
+  ];
+}