This commit is contained in:
mike
2026-01-23 01:55:12 +01:00
parent 2295a7d97c
commit dc45ad45c9
13 changed files with 210 additions and 731 deletions

428
package-lock.json generated
View File

@@ -1,428 +0,0 @@
{
"name": "puzzle-generator",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "puzzle-generator",
"version": "1.0.0",
"dependencies": {
"better-sqlite3": "^12.5.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/better-sqlite3": {
"version": "12.5.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz",
"integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
},
"engines": {
"node": "20.x || 22.x || 23.x || 24.x || 25.x"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"engines": {
"node": ">=6"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/napi-build-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="
},
"node_modules/node-abi": {
"version": "3.85.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
"integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^2.0.0",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}

View File

@@ -1,13 +0,0 @@
{
"name": "puzzle-generator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"private": true,
"dependencies": {
"better-sqlite3": "^12.5.0"
}
}

View File

@@ -12,6 +12,8 @@ import puzzle.SwedishGenerator.Dict;
import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.FillResult;
import puzzle.SwedishGenerator.Grid; import puzzle.SwedishGenerator.Grid;
import puzzle.SwedishGenerator.Slotinfo; import puzzle.SwedishGenerator.Slotinfo;
import static precomp.Const9x8.CLUE_DOWN0;
import static precomp.Const9x8.CLUE_RIGHT1;
import static precomp.Const9x8.INIT_GRID_OUTPUT; import static precomp.Const9x8.INIT_GRID_OUTPUT;
import static precomp.Const9x8.INIT_GRID_OUTPUT_ARR; import static precomp.Const9x8.INIT_GRID_OUTPUT_ARR;
import static puzzle.Export.Clue.DOWN0; import static puzzle.Export.Clue.DOWN0;
@@ -34,8 +36,6 @@ import static puzzle.SwedishGenerator.X;
public record Export() { public record Export() {
public static final ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[8]); public static final ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[8]);
static final byte CLUE_DOWN = 0;
static final byte CLUE_RIGHT = 1;
static final byte CLUE_UP = 2; static final byte CLUE_UP = 2;
static final byte CLUE_LEFT = 3; static final byte CLUE_LEFT = 3;
static final byte CLUE_LEFT_TOP = 4; static final byte CLUE_LEFT_TOP = 4;
@@ -48,8 +48,8 @@ public record Export() {
static int INDEX(int r, int cols, int c) { return r * cols + c; } static int INDEX(int r, int cols, int c) { return r * cols + c; }
@AllArgsConstructor @AllArgsConstructor
enum Clue { enum Clue {
DOWN0(CLUE_DOWN, 'B', 'b'), DOWN0(CLUE_DOWN0, 'B', 'b'),
RIGHT1(CLUE_RIGHT, 'A', 'a'), RIGHT1(CLUE_RIGHT1, 'A', 'a'),
UP2(CLUE_UP, 'C', 'c'), UP2(CLUE_UP, 'C', 'c'),
LEFT3(CLUE_LEFT, 'D', 'd'), LEFT3(CLUE_LEFT, 'D', 'd'),
LEFT_TOP4(CLUE_LEFT_TOP, 'E', 'e'), LEFT_TOP4(CLUE_LEFT_TOP, 'E', 'e'),
@@ -89,8 +89,8 @@ public record Export() {
for (var l = c.lo & c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP)); for (var l = c.lo & c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
for (var l = c.lo & c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_RIGHT_TOP)); for (var l = c.lo & c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_RIGHT_TOP));
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT)); for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT1));
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN)); for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN0));
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_UP)); for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT)); for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
for (var h = c.hi & c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP)); for (var h = c.hi & c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
@@ -99,7 +99,7 @@ public record Export() {
return stream.build(); return stream.build();
} }
public Slotinfo[] slots(Dict D) { public Slotinfo[] slots(Dict D) {
return Masker.slots(c, D.index()); return Masker.slots(c, D.index(), D.reversed());
} }
} }
@@ -214,20 +214,32 @@ public record Export() {
public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) { public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) {
private static ShardLem lookup(long w, byte[] bytes) { record ShaLemma(String word, @Delegate ShardLem rec) { }
private static ShaLemma lookup(long w, byte[] bytes) {
try { try {
val rec = Meta.lookupSilent(w); val rec = Meta.lookupSilent(w);
System.out.println("\nQuery: w=" + w + " -> i=" + rec.mmap()); System.out.println("\nQuery: w=" + w + " -> i=" + rec.mmap());
System.out.println(" word=" + Lemma.asWord(w, bytes) + "\n" + " simpel=" + rec.simpel() + "\n" + " clues=" + Arrays.toString(rec.clues())); var word1 = Lemma.asWord(w, bytes);
return rec; System.out.println(" word=" + word1 + "\n" + " simpel=" + rec.simpel() + "\n" + " clues=" + Arrays.toString(rec.clues()));
return new ShaLemma(word1, rec);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
static long reverse(long w) {
int L = Lemma.unpackSize(w) + 1;
long letters = w & Lemma.LETTER_MASK;
long rev = 0;
for (int i = 0; i < L; i++) {
long letter = (letters >>> (5 * i)) & 31;
rev |= (letter << (5 * (L - 1 - i)));
}
return (w & ~Lemma.LETTER_MASK) | rev;
}
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed, byte[] bytes) { public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed, byte[] bytes) {
val meta = lookup(l, bytes); val meta = lookup(isReversed ? reverse(l) : l, bytes);
this(Lemma.asWord(l, bytes), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, this(meta.word, new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed,
meta.simpel(), meta.clues()); meta.simpel(), meta.clues());
} }
} }

View File

@@ -59,7 +59,6 @@ public class Main {
void main(String[] args) { void main(String[] args) {
_main(args); _main(args);
} }
@SneakyThrows
public void _main(String[] args) { public void _main(String[] args) {
var opts = parseArgs(args); var opts = parseArgs(args);
@@ -254,9 +253,9 @@ public class Main {
// Package-private method for testing // Package-private method for testing
PuzzleResult generatePuzzle(Opts opts) { PuzzleResult generatePuzzle(Opts opts) {
var tLoad0 = System.nanoTime(); var tLoad0 = System.nanoTime();
Dict dict = puzzle.dict800.DictData800.DICT800;//loadDict(opts.wordsPath); Dict dict = puzzle.dict800.DictData800.DICT800;//loadDict(opts.wordsPath);
var tLoad1 = System.nanoTime(); var tLoad1 = System.nanoTime();
section("Load"); section("Load");
info(String.format(Locale.ROOT, "words : %,d", dict.length())); info(String.format(Locale.ROOT, "words : %,d", dict.length()));
@@ -370,9 +369,9 @@ public class Main {
//val mask = generateClues(); //val mask = generateClues();
if (mask == null) return null; if (mask == null) return null;
val slotInfo = Masker.slots(mask, dict.index()); val slotInfo = Masker.slots(mask, dict.index(), dict.reversed());
var grid = Slotinfo.grid(slotInfo);// mask.toGrid(); var grid = Slotinfo.grid(slotInfo);// mask.toGrid();
var filled = fillMask(rng, slotInfo, grid); var filled = fillMask(rng, slotInfo, grid.lo, grid.hi, grid.g);
if (!multiThreaded) { if (!multiThreaded) {
System.out.print("\r" + " ".repeat(120 - "".length()) + "\r"); System.out.print("\r" + " ".repeat(120 - "".length()) + "\r");
@@ -405,6 +404,8 @@ public class Main {
//System.out.println(Arrays.stream(new Clued(mask).gridToString().split("\n")).map(s -> "\"" + s + "\\n\" +").collect(Collectors.joining("\n"))); //System.out.println(Arrays.stream(new Clued(mask).gridToString().split("\n")).map(s -> "\"" + s + "\\n\" +").collect(Collectors.joining("\n")));
} }
if (filled.ok()) { if (filled.ok()) {
grid.lo = ~mask.lo;
grid.hi = 0xFFL & ~mask.hi;
return new PuzzleResult(new Signa(mask), new Puzzle(grid, mask), slotInfo, filled); return new PuzzleResult(new Signa(mask), new Puzzle(grid, mask), slotInfo, filled);
} }

View File

@@ -0,0 +1,12 @@
package puzzle;
public interface Mask {
record Masker(long lo, long hi)
implements Mask { }
default Mask or(Mask o) { return new Masker(o.lo() | lo(), o.hi() | hi()); }
default Mask and(Mask o) { return new Masker(o.lo() & lo(), o.hi() & hi()); }
long hi();
long lo();
}

View File

@@ -1,8 +1,6 @@
package puzzle; package puzzle;
import module java.base; import module java.base;
import anno.GenerateNeighbor;
import anno.GenerateNeighbors;
import anno.Shaped; import anno.Shaped;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@@ -24,6 +22,7 @@ public final class Masker {
@Shaped public static final int MIN_LEN = Neighbors9x8.MIN_LEN;//Config.MIN_LEN; @Shaped public static final int MIN_LEN = Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
@Shaped public static final int C = Neighbors9x8.C; @Shaped public static final int C = Neighbors9x8.C;
@Shaped public static final int R = Neighbors9x8.R; @Shaped public static final int R = Neighbors9x8.R;
@Shaped public static final int SIZE = Neighbors9x8.SIZE;
@Shaped public static final double SIZED = Neighbors9x8.SIZED;// ~18 @Shaped public static final double SIZED = Neighbors9x8.SIZED;// ~18
@Shaped private static final long[] NBR_LO = Neighbors9x8.NBR_LO; @Shaped private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
@Shaped private static final long[] NBR_HI = Neighbors9x8.NBR_HI; @Shaped private static final long[] NBR_HI = Neighbors9x8.NBR_HI;
@@ -43,6 +42,7 @@ public final class Masker {
this.stack = stack; this.stack = stack;
this.cache = cache; this.cache = cache;
} }
public static boolean isLo(int n) { return (n & 64) == 0; }
public boolean isValid(Clues c) { public boolean isValid(Clues c) {
return findOffendingClue(c) == -1; return findOffendingClue(c) == -1;
@@ -172,17 +172,18 @@ public final class Masker {
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN) if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
visitor.visit(key, rayLo, rayHi); visitor.visit(key, rayLo, rayHi);
} }
public static Slot[] extractSlots(Clues c, DictEntry[] index) { public static Slot[] extractSlots(Clues c, DictEntry[] index, DictEntry[] rev) {
var slots = new ArrayList<Slot>(c.clueCount()); var slots = new ArrayList<Slot>(c.clueCount());
c.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, index[Slot.length(lo, hi)]))); c.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, Slotinfo.increasing(key) ? index[Slot.length(lo, hi)] : rev[Slot.length(lo, hi)])));
return slots.toArray(Slot[]::new); return slots.toArray(Slot[]::new);
} }
public static Slotinfo[] slots(Clues mask, DictEntry[] index) { public static Slotinfo[] slots(Clues mask, Dict d) { return slots(mask, d.index(), d.reversed()); }
var slots = Masker.extractSlots(mask, index); public static Slotinfo[] slots(Clues mask, DictEntry[] index, DictEntry[] rev) {
var slots = Masker.extractSlots(mask, index, rev);
return Masker.scoreSlots(slots); return Masker.scoreSlots(slots);
} }
public static Slotinfo[] scoreSlots(Slot[] slots) { public static Slotinfo[] scoreSlots(Slot[] slots) {
val count = new byte[SwedishGenerator.SIZE]; val count = new byte[SIZE];
var slotInfo = new Slotinfo[slots.length]; var slotInfo = new Slotinfo[slots.length];
for (var s : slots) { for (var s : slots) {
for (var b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++; for (var b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++;
@@ -714,7 +715,7 @@ public final class Masker {
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED; return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
} }
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); } public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); }
public void forEachSlot(SlotVisitor visitor) { public void forEachSlot(SlotVisitor visitor) {
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1)); for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));

View File

@@ -3,6 +3,8 @@ package puzzle;
import anno.ConstGen; import anno.ConstGen;
import anno.GenerateNeighbor; import anno.GenerateNeighbor;
import anno.GenerateNeighbors; import anno.GenerateNeighbors;
import anno.GenerateShapedCopies;
import anno.Shaped;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.val; import lombok.val;
@@ -33,21 +35,21 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
@GenerateNeighbor(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2), @GenerateNeighbor(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2),
@GenerateNeighbor(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2) @GenerateNeighbor(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2)
}) })
/*@GenerateShapedCopies( /*@GenerateShapedCopies(
className = "SwedishGeneratorX",
shapes = { "precomp.Neighbors9x8", "precomp.Neighbors4x3" } shapes = { "precomp.Neighbors9x8", "precomp.Neighbors4x3" }
)*/ )*/
public record SwedishGenerator() { public record SwedishGenerator() {
public static final long X = 0L; public static final int MAX_TRIES_PER_SLOT = 500;// MAX_TRIES_PER_SLOT;
public static final int SIZE = Neighbors9x8.SIZE; public static final long X = 0L;
public static final int MAX_TRIES_PER_SLOT = 500;// MAX_TRIES_PER_SLOT; @Shaped private static final int SIZE = Neighbors9x8.SIZE;
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE; @Shaped private static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;
public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624; @Shaped private static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;
public static boolean isLo(int n) { return (n & 64) == 0; }
interface Bit1029 { interface Bit1029 {
static long[] bit1029() { return new long[2048]; }
private static int wordIndex(int bitIndex) { return bitIndex >> 6; } private static int wordIndex(int bitIndex) { return bitIndex >> 6; }
static boolean get(long[] bits, int bitIndex) { return (bits[wordIndex(bitIndex)] & 1L << bitIndex) != X; } static boolean get(long[] bits, int bitIndex) { return (bits[wordIndex(bitIndex)] & 1L << bitIndex) != X; }
static void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; } static void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
@@ -55,12 +57,11 @@ public record SwedishGenerator() {
} }
//@formatter:off //@formatter:off
public record Dict(DictEntry[] index, int length) { } public record Dict(DictEntry[] index,DictEntry[] reversed, int length) { public Dict(DictEntry[] index,int length){this(index,index,length);} }
public record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } public record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
@AllArgsConstructor @NoArgsConstructor static final class Assign { long w; } @AllArgsConstructor @NoArgsConstructor static final class Assign { long w; }
public static final class FillStats { public double simplicity; }
@AllArgsConstructor public static final class Grid { public final byte[] g; public long lo, hi; } @AllArgsConstructor public static final class Grid { public final byte[] g; public long lo, hi; }
public record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { } public record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed ) { }
//@formatter:on //@formatter:on
public static final class Rng { public static final class Rng {
@@ -98,15 +99,15 @@ public record SwedishGenerator() {
static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; } static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; }
static long packShiftIn(byte[] b) { static long packShiftIn(byte[] b) {
long w = 0; long w = 0;
for (int i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31); for (var i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31);
return w; return w;
} }
static long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); } static long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111L); } static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111L); }
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); } static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
static String asWord(long word, byte[] bytes) { static String asWord(long word, byte[] bytes) {
int bi = 0; var bi = 0;
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) bytes[bi++] = (byte) ((w & 31) | 64); for (var w = word & LETTER_MASK; w != 0; w >>>= 5) bytes[bi++] = (byte) ((w & 31) | 64);
return new String(bytes, 0, bi, US_ASCII); return new String(bytes, 0, bi, US_ASCII);
} }
static int unpackIndex(long w) { return (int) (w >>> 40); } static int unpackIndex(long w) { return (int) (w >>> 40); }
@@ -132,70 +133,45 @@ public record SwedishGenerator() {
} }
} }
public static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) { public static long patternForSlot(final long glo, final long ghi, final byte[] g, final long lo, final long hi) {
if (((lo & glo) | (hi & ghi)) == X) return X; if (((lo & glo) == X) && (hi & ghi) == X) return X;
long p = 0; long p = 0;
int n = 0, offset, idx; int n = 0, offset, idx;
if (Slotinfo.increasing(key)) { for (var b = lo & glo; b != X; b &= b - 1) {
for (long b = lo & glo; b != X; b &= b - 1) { idx = numberOfTrailingZeros(b);
idx = numberOfTrailingZeros(b); p |= ((long) (bitCount(lo & ((1L << idx) - 1)) * 26 + g[idx])) << (n++ << 3);
p |= ((long) (bitCount(lo & ((1L << idx) - 1)) * 26 + g[idx])) << (n++ << 3); }
} offset = bitCount(lo);
offset = bitCount(lo); for (var b = hi & ghi; b != X; b &= b - 1) {
for (long b = hi & ghi; b != X; b &= b - 1) { idx = numberOfTrailingZeros(b);
idx = numberOfTrailingZeros(b); p |= ((long) ((offset + bitCount(hi & ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
p |= ((long) ((offset + bitCount(hi & ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
}
} else {
offset = bitCount(hi);
for (long b = hi & ghi; b != X; b &= b - 1) {
idx = numberOfTrailingZeros(b);
p |= ((long) (bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
}
for (long b = lo & glo; b != X; b &= b - 1) {
idx = numberOfTrailingZeros(b);
p |= ((long) ((offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))) * 26 + g[idx])) << (n++ << 3);
}
} }
return p; return p;
} }
/// pattern cannot be X /// pattern cannot be X
public static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) { public static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) {
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs); var indices = new int[candidateCountForPattern(res, pattern, posBitsets, numLongs)];
for (long p = pattern >>> 8; p != X; p >>>= 8) { for (int k = 0, ki = 0; k < numLongs; k++) for (var w = res[k]; w != X; w &= w - 1) indices[ki++] = (k << 6) | numberOfTrailingZeros(w);
long[] bs = posBitsets[(int) (p & 0xFF) - 1];
for (int k = 0; k < numLongs; k++) res[k] &= bs[k];
}
int count = 0;
for (int k = 0; k < numLongs; k++) count += bitCount(res[k]);
int[] indices = new int[count];
for (int k = 0, ki = 0; k < numLongs; k++) {
for (long w = res[k]; w != X; w &= w - 1) indices[ki++] = (k << 6) | numberOfTrailingZeros(w);
}
return indices; return indices;
} }
/// pattern cannot be X /// pattern cannot be X
public static int candidateCountForPattern(final long[] res, final long pattern, final long[][] posBitsets, final int numLongs) { public static int candidateCountForPattern(final long[] res, final long pattern, final long[][] pos, final int num) {
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs); System.arraycopy(pos[(int) (pattern & 0xFF) - 1], 0, res, 0, num);
for (long p = pattern >>> 8; p != X; p >>>= 8) { for (var p = pattern >>> 8; p != X; p >>>= 8) {
long[] bs = posBitsets[(int) (p & 0xFF) - 1]; var bs = pos[(int) (p & 0xFF) - 1];
for (int k = 0; k < numLongs; k++) res[k] &= bs[k]; for (var k = 0; k < num; k++) res[k] &= bs[k];
} }
int count = 0; var count = 0;
for (int k = 0; k < numLongs; k++) count += bitCount(res[k]); for (var k = 0; k < num; k++) count += bitCount(res[k]);
return count; return count;
} }
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots, public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
final Grid grid) { final long lo, final long hi, final byte[] g) {
val used = new long[2048]; val used = new long[2048];
val bitset = new long[2500]; val bitset = new long[2500];
val g = grid.g;
val TOTAL = slots.length; val TOTAL = slots.length;
val t0 = System.currentTimeMillis(); val t0 = System.currentTimeMillis();
class Solver { class Solver {
@@ -203,82 +179,64 @@ public record SwedishGenerator() {
static final int PICK_NOT_DONE = -1; static final int PICK_NOT_DONE = -1;
static final int PICK_DONE = 0; static final int PICK_DONE = 0;
long nodes; long nodes;
long backtracks; long tracks;
long glo = grid.lo, ghi = grid.hi; long glo = lo, ghi = hi;
Slotinfo currentSlot; Slotinfo slot;
int[] currentIndices; int[] indices;
boolean placeWordDec(final long lo, final long hi, final long w) { boolean place(final long lo, final long hi, final long w) {
int idx; int idx;
int bcHi = bitCount(hi); for (var b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return true;
for (long b = hi & ghi; b != X; b &= b - 1) var bcLo = bitCount(lo);
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; for (var b = hi & ghi; b != X; b &= b - 1) if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return true;
for (long b = lo & glo; b != X; b &= b - 1)
if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
long maskLo = lo & ~glo, maskHi = hi & ~ghi; long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) { if ((maskLo | maskHi) != X) {
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); for (var b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); for (var b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
glo |= maskLo; glo |= maskLo;
ghi |= maskHi; ghi |= maskHi;
} }
return true; return false;
}
boolean placeWordInc(final long lo, final long hi, final long w) {
int idx;
for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
int bcLo = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1) if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) {
for (long b = maskLo; b != X; b &= b - 1) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
glo |= maskLo;
ghi |= maskHi;
}
return true;
} }
int chooseMRV() { int chooseMRV() {
Slotinfo best = null; Slotinfo best = null;
int count2 = -1, bestScore = -1; int max = -1, bestScore = -1;
for (int i = 0, n = TOTAL; i < n; i++) { for (int i = 0, n = TOTAL; i < n; i++) {
var s = slots[i]; var s = slots[i];
if (s.assign.w != X) continue; if (s.assign.w != X) continue;
var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi); var pattern = patternForSlot(glo, ghi, g, s.lo, s.hi);
var index = s.entry; var index = s.entry;
int count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); var count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
if (count == 0) return PICK_NOT_DONE; if (count == 0) return PICK_NOT_DONE;
if (best == null if (best == null
|| count < count2 || count < max
|| (count == count2 && s.score > bestScore)) { || (count == max && s.score > bestScore)) {
best = s; best = s;
bestScore = s.score; bestScore = s.score;
count2 = count; max = count;
if (count <= 1) break; if (count <= 1) break;
} }
} }
if (best == null) return PICK_DONE; if (best == null) return PICK_DONE;
var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); var pattern = patternForSlot(glo, ghi, g, best.lo, best.hi);
currentSlot = best; slot = best;
var index = best.entry; var index = best.entry;
currentIndices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong); indices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
return 1; return 1;
} }
boolean backtrack(int depth) { boolean backtrack(int depth) {
if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false; if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false;
nodes++; nodes++;
int status = chooseMRV(); var status = chooseMRV();
if (status == PICK_DONE) return true; if (status == PICK_DONE) return true;
if (status == PICK_NOT_DONE) { if (status == PICK_NOT_DONE) {
backtracks++; tracks++;
return false; return false;
} }
val info = currentIndices; val info = indices;
val s = currentSlot; val s = slot;
val inc = Slotinfo.increasing(s.key);
val slo = s.lo; val slo = s.lo;
val shi = s.hi; val shi = s.hi;
val assign = s.assign; val assign = s.assign;
@@ -294,11 +252,7 @@ public record SwedishGenerator() {
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
top = ghi; top = ghi;
if (inc) { if (place(slo, shi, w)) continue;
if (!placeWordInc(slo, shi, w)) continue;
} else {
if (!placeWordDec(slo, shi, w)) continue;
}
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
assign.w = w; assign.w = w;
@@ -308,45 +262,36 @@ public record SwedishGenerator() {
glo = low; glo = low;
ghi = top; ghi = top;
} }
backtracks++; tracks++;
return false; return false;
} }
var N = words.length; var N = words.length;
for (var t = 0; t < s.minL; t++) { for (var t = 0; t < s.minL; t++) {
var w = words[rng.biasedIndexPow3(N - 1)]; var w = words[rng.biasedIndexPow3(N - 1)];
var lemIdx = Lemma.unpackIndex(w); var lem = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lem)) continue;
low = glo; low = glo;
top = ghi; top = ghi;
if (inc) { if (place(slo, shi, w)) continue;
if (!placeWordInc(slo, shi, w)) continue;
} else {
if (!placeWordDec(slo, shi, w)) continue;
}
Bit1029.set(used, lemIdx); Bit1029.set(used, lem);
assign.w = w; assign.w = w;
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
assign.w = X; assign.w = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lem);
glo = low; glo = low;
ghi = top; ghi = top;
} }
backtracks++; tracks++;
return false; return false;
} }
} }
var solver = new Solver(); var solver = new Solver();
var ok = solver.backtrack(0); var ok = solver.backtrack(0);
grid.lo = solver.glo; return new FillResult(ok, solver.nodes, solver.tracks, solver.slot == null ? 0 : solver.slot.entry.length, System.currentTimeMillis() - t0);
grid.hi = solver.ghi;
return new FillResult(ok, solver.nodes, solver.backtracks, solver.currentSlot == null ? 0 : solver.currentSlot.entry.length, System.currentTimeMillis() - t0,
new FillStats());
} }
} }

View File

@@ -5,15 +5,22 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import lombok.val; import lombok.val;
import org.junit.jupiter.api.Test;
import precomp.Neighbors9x8; import precomp.Neighbors9x8;
import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.Dict;
import puzzle.SwedishGenerator.DictEntry; import puzzle.SwedishGenerator.DictEntry;
import puzzle.SwedishGenerator.Lemma; import puzzle.SwedishGenerator.Lemma;
import puzzle.dict950.DictData950;
import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.US_ASCII;
public final class DictJavaGeneratorMulti { public final class DictJavaGeneratorMulti {
@Test
void testReversed() {
val wOrig = DictData950.DICT950.index()[4].words()[4];
val wRev = DictData950.DICT950.reversed()[4].words()[4];
System.out.println(Lemma.asWord(wOrig, new byte[8]) + " " + Lemma.asWord(wRev, new byte[8]));
}
interface Dicts { interface Dicts {
static Dict makeDict(long[] wordz) { static Dict makeDict(long[] wordz) {
@@ -52,7 +59,6 @@ public final class DictJavaGeneratorMulti {
} }
} }
record DictEntryDTO(LongArrayList words, IntListDTO[][] pos) { record DictEntryDTO(LongArrayList words, IntListDTO[][] pos) {
public DictEntryDTO(int L) { public DictEntryDTO(int L) {

View File

@@ -27,8 +27,6 @@ import static puzzle.LemmaData.AZ;
import static puzzle.SwedishGenerator.Lemma; import static puzzle.SwedishGenerator.Lemma;
import static puzzle.SwedishGenerator.Slotinfo; import static puzzle.SwedishGenerator.Slotinfo;
import static puzzle.SwedishGenerator.fillMask; import static puzzle.SwedishGenerator.fillMask;
import static puzzle.SwedishGeneratorTest.LETTER_A;
import static puzzle.SwedishGeneratorTest.LETTER_Z;
@DictGen( @DictGen(
packageName = "puzzle.dict950", packageName = "puzzle.dict950",
className = "DictData950", className = "DictData950",
@@ -101,7 +99,7 @@ public class MainTest {
public void testGridBasics() { public void testGridBasics() {
var clues = Signa.of(r2c1d2); var clues = Signa.of(r2c1d2);
var grid = new Puzzle(clues); var grid = new Puzzle(clues);
r1c1.or(r0c1).lo();
// Test set/get // Test set/get
GridBuilder.placeWord(grid.grid(), grid.grid().g, r2c1d2.slotKey, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ); GridBuilder.placeWord(grid.grid(), grid.grid().g, r2c1d2.slotKey, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
val map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); val map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
@@ -183,12 +181,12 @@ public class MainTest {
); );
var slotInfo = mask.slots(DictData950.DICT950); var slotInfo = mask.slots(DictData950.DICT950);
var grid = Slotinfo.grid(slotInfo); var grid = Slotinfo.grid(slotInfo);
var filled = fillMask(rng, slotInfo, grid); var filled = fillMask(rng, slotInfo, grid.lo,grid.hi, grid.g);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed"); Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
Assertions.assertEquals("BEADEMT", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get())); Assertions.assertEquals("BEADEMT", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get()));
Assertions.assertEquals(-1L, grid.lo); Assertions.assertEquals(74732156493031040L, grid.lo);
Assertions.assertEquals(-1L, grid.hi); Assertions.assertEquals(-63L, grid.hi);
var g = new Puzzle(grid, mask.c()); var g = new Puzzle(grid, mask.c());
g.gridToString(); g.gridToString();
var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(new Rewards(1, 1, 1)); var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(new Rewards(1, 1, 1));

View File

@@ -10,7 +10,6 @@ import puzzle.Export.Rewards;
import puzzle.Masker.Clues; import puzzle.Masker.Clues;
import puzzle.SwedishGenerator.Assign; import puzzle.SwedishGenerator.Assign;
import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.FillResult;
import puzzle.SwedishGenerator.FillStats;
import puzzle.SwedishGenerator.Lemma; import puzzle.SwedishGenerator.Lemma;
import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Rng;
import puzzle.SwedishGenerator.Slotinfo; import puzzle.SwedishGenerator.Slotinfo;
@@ -257,7 +256,7 @@ public class MarkerTest {
@Test @Test
void testCornerDownExtraction() { void testCornerDownExtraction() {
var slots = Masker.slots(Signa.of(r0c0d4).c(), DictData950.DICT950.index()); var slots = Masker.slots(Signa.of(r0c0d4).c(), DictData950.DICT950);
assertEquals(1, slots.length); assertEquals(1, slots.length);
assertEquals(r0c0d4.d, Masker.Slot.dir(slots[0].key())); assertEquals(r0c0d4.d, Masker.Slot.dir(slots[0].key()));
} }
@@ -303,7 +302,7 @@ public class MarkerTest {
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST)); assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST));
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats()); var fillResult = new FillResult(true, 0, 0, 0, 0);
var puzzleResult = new PuzzleResult(clues, grid, new Slotinfo[]{ var puzzleResult = new PuzzleResult(clues, grid, new Slotinfo[]{
new Slotinfo(key, lo, 0L, 0, new Assign(TEST), null, 0) new Slotinfo(key, lo, 0L, 0, new Assign(TEST), null, 0)
}, fillResult); }, fillResult);
@@ -343,9 +342,9 @@ public class MarkerTest {
@Test @Test
void testExportFormatEmpty() { void testExportFormatEmpty() {
var grid = SwedishGeneratorTest.createEmpty(); var grid = SwedishGeneratorTest.createEmpty9x8();
val clues = Clues.createEmpty(); val clues = Clues.createEmpty();
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats()); var fillResult = new FillResult(true, 0, 0, 0, 0);
var puzzleResult = new PuzzleResult(new Signa(clues), new Puzzle(grid, clues), new Slotinfo[0], fillResult); var puzzleResult = new PuzzleResult(new Signa(clues), new Puzzle(grid, clues), new Slotinfo[0], fillResult);
var exported = puzzleResult.exportFormatFromFilled(new Rewards(0, 0, 0)); var exported = puzzleResult.exportFormatFromFilled(new Rewards(0, 0, 0));

View File

@@ -9,6 +9,7 @@ import puzzle.Export.Clue;
import puzzle.Export.Signa; import puzzle.Export.Signa;
import puzzle.Export.Puzzle; import puzzle.Export.Puzzle;
import puzzle.Masker.Clues; import puzzle.Masker.Clues;
import puzzle.SwedishGenerator.Grid;
import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Rng;
import puzzle.SwedishGenerator.Slotinfo; import puzzle.SwedishGenerator.Slotinfo;
@@ -72,8 +73,9 @@ public class PerformanceTest {
var successCount = 0; var successCount = 0;
for (var i = 0; i < iterations; i++) { for (var i = 0; i < iterations; i++) {
val slotInfo = Masker.slots(arr[c], DICT800.index()); val slotInfo = Masker.slots(arr[c], DICT800);
val result = fillMask(rng, slotInfo, Slotinfo.grid(slotInfo)); var grid = Slotinfo.grid(slotInfo);
val result = fillMask(rng, slotInfo,grid.lo,grid.hi, grid.g);
if (result.ok()) successCount++; if (result.ok()) successCount++;
totalNodes += result.nodes(); totalNodes += result.nodes();
totalBacktracks += result.backtracks(); totalBacktracks += result.backtracks();
@@ -100,7 +102,7 @@ public class PerformanceTest {
r6c2d1, r6c2d1,
r7c0d2, r7c1d2, r7c2d1, r7c7d2, r7c8d2 r7c0d2, r7c1d2, r7c2d1, r7c7d2, r7c8d2
); );
val allSlots = Masker.slots(mask.c(), DICT900.index()); val allSlots = Masker.slots(mask.c(), DICT900);
//mask.toGrid() //mask.toGrid()
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---"); System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
System.out.println("[DEBUG_LOG] Full Slot Layout:"); System.out.println("[DEBUG_LOG] Full Slot Layout:");
@@ -121,7 +123,7 @@ public class PerformanceTest {
// A single horizontal slot at (0,0) // A single horizontal slot at (0,0)
val mask = Signa.of(r0c0d1); val mask = Signa.of(r0c0d1);
val slots = Masker.slots(mask.c(), DICT800.index()); val slots = Masker.slots(mask.c(), DICT800);
System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---"); System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");
if (slots.length > 0) { if (slots.length > 0) {
@@ -142,7 +144,8 @@ public class PerformanceTest {
// Reset assignments for each iteration // Reset assignments for each iteration
for (var s : slots) s.assign().w = 0; for (var s : slots) s.assign().w = 0;
val result = fillMask(rng, slots, Slotinfo.grid(slots)); var grid = Slotinfo.grid(slots);
val result = fillMask(rng, slots, grid.lo,grid.hi,grid.g);
if (result.ok()) { if (result.ok()) {
successCount++; successCount++;
} }

View File

@@ -7,6 +7,7 @@ import anno.LemmaGen;
import lombok.val; import lombok.val;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import precomp.Neighbors9x8;
import precomp.Neighbors9x8.rci; import precomp.Neighbors9x8.rci;
import puzzle.DictJavaGeneratorMulti.DictEntryDTO.IntListDTO; import puzzle.DictJavaGeneratorMulti.DictEntryDTO.IntListDTO;
import puzzle.Export.Signa; import puzzle.Export.Signa;
@@ -37,6 +38,9 @@ import static puzzle.LemmaData.INER;
import static puzzle.LemmaData.INEREN; import static puzzle.LemmaData.INEREN;
import static puzzle.LemmaData.INERENA; import static puzzle.LemmaData.INERENA;
import static puzzle.LemmaData.INERENAE; import static puzzle.LemmaData.INERENAE;
import static puzzle.LemmaData.WORD_A;
import static puzzle.LemmaData.WORD_C;
import static puzzle.LemmaData.WORD_X;
import static puzzle.SwedishGenerator.*; import static puzzle.SwedishGenerator.*;
@GenerateNeighbors(@GenerateNeighbor(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2)) @GenerateNeighbors(@GenerateNeighbor(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2))
@LemmaGen( @LemmaGen(
@@ -52,10 +56,7 @@ import static puzzle.SwedishGenerator.*;
) )
public class SwedishGeneratorTest { public class SwedishGeneratorTest {
public static final long WORD_A = Lemma.from("A"); static Grid createEmpty9x8() { return new Grid(new byte[Neighbors9x8.SIZE], X, X); }
public static final long WORD_C = Lemma.from("C");
public static final long WORD_X = Lemma.from("X");
static Grid createEmpty() { return new Grid(new byte[SIZE], X, X); }
record Context(long[] bitset) { record Context(long[] bitset) {
public Context() { this(new long[2500]); } public Context() { this(new long[2500]); }
@@ -86,26 +87,10 @@ public class SwedishGeneratorTest {
INERENA, INERENA,
INERENAE }; INERENAE };
static final byte LETTER_A = ((byte) 'A') & 31;
static final byte LETTER_P = ((byte) 'P') & 31;
static final byte LETTER_L = ((byte) 'L') & 31;
static final byte LETTER_B = ((byte) 'B') & 31;
static final byte LETTER_C = ((byte) 'C') & 31;
static final byte LETTER_E = ((byte) 'E') & 31;
static final byte LETTER_I = ((byte) 'I') & 31;
static final byte LETTER_N = ((byte) 'N') & 31;
static final byte LETTER_X = ((byte) 'X') & 31;
static final byte LETTER_R = ((byte) 'R') & 31;
static final byte LETTER_Z = ((byte) 'Z') & 31;
static final byte CLUE_DOWN = 0;
static final byte CLUE_RIGHT = 1;
static final byte CLUE_UP = 2;
static final byte CLUE_LEFT = 3;
@Test @Test
void testPatternForSlotAllLetters() { void testPatternForSlotAllLetters() {
var grid = new Puzzle(Signa.of(r0c0d1)); var grid = new Puzzle(Signa.of(r0c0d1));
GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC); GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c1.or(r0c2).or(r0c3).lo(), X, ABC);
val map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); val map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
assertEquals(LETTER_A, map.get(OFF_0_1)); assertEquals(LETTER_A, map.get(OFF_0_1));
assertEquals(LETTER_B, map.get(OFF_0_2)); assertEquals(LETTER_B, map.get(OFF_0_2));
@@ -114,60 +99,32 @@ public class SwedishGeneratorTest {
@Test @Test
void testPatternForSlotMixed() { void testPatternForSlotMixed() {
var grid = createEmpty(); var grid = createEmpty9x8();
GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, 1L << OFF_0_0, 0, WORD_A); GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, r0c0.mask, X, WORD_A);
GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, 1L << OFF_2_0, 0, WORD_C); GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, r2c0.mask, X, WORD_C);
var key = Slot.packSlotKey(OFF_1_0, CLUE_RIGHT); var pattern = patternForSlot(grid.lo, grid.hi, grid.g, 7L, X);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L);
assertEquals(14081L, pattern); assertEquals(14081L, pattern);
} }
@Test @Test
void testPatternForSlotAllDashes() { void testPatternForSlotAllDashes() {
var grid = createEmpty(); var grid = createEmpty9x8();
var key = Slot.packSlotKey(1 << Slot.BIT_FOR_DIR, CLUE_RIGHT); var pattern = patternForSlot(grid.lo, grid.hi, grid.g, 7L, X);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L); assertEquals(X, pattern);
assertEquals(0L, pattern);
} }
@Test @Test
void testPatternForSlotSingleLetter() { void testPatternForSlotSingleLetter() {
var grid = createEmpty(); var grid = createEmpty9x8();
//Slot.packSlotKey(0, CLUE_RIGHT) GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, r0c0.mask, X, WORD_A);
GridBuilder.placeWord(grid, grid.g, r0c0d1.slotKey, 1L << OFF_0_0, 0, WORD_A); var pattern = patternForSlot(grid.lo, grid.hi, grid.g, 7L, X);
var key = Slot.packSlotKey(1, CLUE_RIGHT);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L);
assertEquals(1L, pattern); assertEquals(1L, pattern);
} }
@Test
void testRng() {
var rng = new Rng(123);
var val1 = rng.nextU32();
var val2 = rng.nextU32();
assertNotEquals(val1, val2);
var rng2 = new Rng(123);
assertEquals(val1, rng2.nextU32());
for (var i = 0; i < 100; i++) {
var r = rng.randomClueDir();
assertTrue(r >= 0 && r <= 5);
var f = rng.nextFloat();
assertTrue(f >= 0.0 && f <= 1.0);
}
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
}
@Test @Test
void testGrid() { void testGrid() {
var grid = new Puzzle(Clues.createEmpty()); var grid = new Puzzle(Clues.createEmpty());
GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, 1L << OFF_0_0, 0, WORD_A); GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c0d0.mask, X, WORD_A);
val arr = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); val arr = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
assertEquals(1, arr.size()); assertEquals(1, arr.size());
assertEquals(LETTER_A, arr.get(OFF_0_0)); assertEquals(LETTER_A, arr.get(OFF_0_0));
@@ -210,25 +167,10 @@ public class SwedishGeneratorTest {
@Test @Test
void testSlot() { void testSlot() {
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR); assertEquals(OFF_2_3, Slot.clueIndex(r2c3d0.slotKey));
// key = (r << 8) | (c << 4) | d assertEquals(CLUE_DOWN0, Slot.dir(r2c3d0.slotKey));
var offset = OFF_2_3; assertFalse(Slot.horiz(r2c3d0.slotKey));
System.out.println("[DEBUG_LOG] Grid.offset(2, 3) = " + offset); var cells = Puzzle.cellWalk((byte) r2c3d0.slotKey, r2c5.or(r3c5).or(r4c5).lo(), 0L).mapToObj(i -> Masker.IT[i]).toArray(rci[]::new);
var key = Slot.packSlotKey(offset, CLUE_DOWN);
System.out.println("[DEBUG_LOG] key = " + key);
long lo = 0;
// pos 0: (2, 5)
lo |= 1L << OFF_2_5;
// pos 1: (3, 5)
lo |= 1L << OFF_3_5;
// pos 2: (4, 5)
lo |= 1L << OFF_4_5;
System.out.println("[DEBUG_LOG] s.dir() = " + Slot.dir(key));
assertEquals(OFF_2_3, Slot.clueIndex(key));
assertEquals(CLUE_DOWN, Slot.dir(key));
assertFalse(Slot.horiz(key));
var cells = Puzzle.cellWalk((byte) key, lo, 0L).mapToObj(i -> Masker.IT[i]).toArray(rci[]::new);
assertEquals(2, cells[0].r()); assertEquals(2, cells[0].r());
assertEquals(3, cells[1].r()); assertEquals(3, cells[1].r());
assertEquals(4, cells[2].r()); assertEquals(4, cells[2].r());
@@ -236,8 +178,8 @@ public class SwedishGeneratorTest {
assertEquals(5, cells[1].c()); assertEquals(5, cells[1].c());
assertEquals(5, cells[2].c()); assertEquals(5, cells[2].c());
assertTrue(Slot.horiz(CLUE_RIGHT)); // right assertTrue(Slot.horiz(CLUE_RIGHT1)); // right
assertFalse(Slot.horiz(CLUE_DOWN)); // down assertFalse(Slot.horiz(CLUE_DOWN0)); // down
} }
static long packPattern(String s) { static long packPattern(String s) {
@@ -267,13 +209,13 @@ public class SwedishGeneratorTest {
void testForEachSlotAndExtractSlots() { void testForEachSlotAndExtractSlots() {
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2) // This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
var dict = DictJavaGeneratorMulti.Dicts.makeDict(WORDS2); var dict = DictJavaGeneratorMulti.Dicts.makeDict(WORDS2);
var slots = Masker.extractSlots(Signa.of(r0c0d1).c(), dict.index()); var slots = Masker.extractSlots(Signa.of(r0c0d1).c(), dict.index(), dict.reversed());
assertEquals(1, slots.length); assertEquals(1, slots.length);
var s = slots[0]; var s = slots[0];
assertTrue(Slot.length(s.lo(), s.hi()) >= 2); assertTrue(Slot.length(s.lo(), s.hi()) >= 2);
assertEquals(OFF_0_0, Slot.clueIndex(s.key())); assertEquals(OFF_0_0, Slot.clueIndex(s.key()));
assertEquals(CLUE_RIGHT, Slot.dir(s.key())); assertEquals(CLUE_RIGHT1, Slot.dir(s.key()));
} }
@Test @Test
@@ -292,33 +234,20 @@ public class SwedishGeneratorTest {
@Test @Test
void testGeneticAlgorithmComponents() { void testGeneticAlgorithmComponents() {
var rng = new Rng(42); var gen = new Masker(new Rng(42), new int[Masker.STACK_SIZE], Masker.Clues.createEmpty());
var gen = new Masker(rng, new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); var c1 = new Signa(gen.randomMask(18));
var c1 = new Signa(gen.randomMask(18));
assertNotNull(c1); assertNotNull(c1);
var g2 = new Signa(gen.mutate(c1.deepCopyGrid().c())); var g2 = new Signa(gen.mutate(c1.deepCopyGrid().c()));
assertNotNull(g2); assertNotNull(g2);
assertNotSame(c1.c(), g2.c()); assertNotSame(c1.c(), g2.c());
assertNotNull(gen.crossover(c1.c(), g2.c())); assertNotNull(gen.crossover(c1.c(), g2.c()));
assertNotNull(gen.hillclimb(c1.c(), 18, 10));
var g4 = gen.hillclimb(c1.c(), 18, 10);
assertNotNull(g4);
} }
@Test @Test
void testPlaceWord() { void testPlaceWord() {
var grid = new Puzzle(Clues.createEmpty()); var grid = new Puzzle(Clues.createEmpty());
// Slot at OFF_0_0 length 3, horizontal (right) assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c0.or(r0c1).or(r0c2).lo(), X, ABC));
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
val hi = 0L;
var w1 = ABC;
// 1. Successful placement in empty grid
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
var map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); var map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
assertEquals(3, map.size()); assertEquals(3, map.size());
@@ -327,9 +256,9 @@ public class SwedishGeneratorTest {
assertEquals(LETTER_C, map.get(OFF_0_2)); assertEquals(LETTER_C, map.get(OFF_0_2));
// 2. Successful placement with partial overlap (same characters) // 2. Successful placement with partial overlap (same characters)
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1)); assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c0.or(r0c1).or(r0c2).lo(), X, ABC));
// 3. Conflict: place "ABD" where "ABC" is // 3. Conflict: place "ABD" where "ABC" is
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, ABD)); assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c0.or(r0c1).or(r0c2).lo(), X, ABD));
// Verify grid is unchanged (still "ABC") // Verify grid is unchanged (still "ABC")
map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
assertEquals(3, map.size()); assertEquals(3, map.size());
@@ -339,8 +268,8 @@ public class SwedishGeneratorTest {
// 4. Partial placement then conflict (rollback) // 4. Partial placement then conflict (rollback)
grid = new Puzzle(Clues.createEmpty()); grid = new Puzzle(Clues.createEmpty());
GridBuilder.placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, WORD_X); // Conflict at the end GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, 1L << OFF_0_2, 0, WORD_X); // Conflict at the end
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1)); assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c0.or(r0c1).or(r0c2).lo(), X, ABC));
map = grid.stream().collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); map = grid.stream().collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
assertEquals(1, map.size()); assertEquals(1, map.size());
assertEquals(LETTER_X, map.get(OFF_0_2)); assertEquals(LETTER_X, map.get(OFF_0_2));
@@ -350,12 +279,9 @@ public class SwedishGeneratorTest {
void testBacktrackingHelpers() { void testBacktrackingHelpers() {
var grid = new Puzzle(Clues.createEmpty()); var grid = new Puzzle(Clues.createEmpty());
// Slot at 0,1 length 2 // Slot at 0,1 length 2
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
var w = AZ;
val low = grid.grid().lo; val low = grid.grid().lo;
val top = grid.grid().hi; val top = grid.grid().hi;
var placed = GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w); var placed = GridBuilder.placeWord(grid.grid(), grid.grid().g, r0c0d1.slotKey, r0c1.or(r0c2).lo(), X, AZ);
assertTrue(placed); assertTrue(placed);
var map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter)); var map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
@@ -374,16 +300,16 @@ public class SwedishGeneratorTest {
@Test @Test
void testInnerWorkings() { void testInnerWorkings() {
// 1. Test Slot.increasing // 1. Test Slot.increasing
assertFalse(Slotinfo.increasing(CLUE_LEFT)); // Left assertFalse(Slotinfo.increasing(CLUE_LEFT3)); // Left
assertTrue(Slotinfo.increasing(CLUE_RIGHT)); // Right assertTrue(Slotinfo.increasing(CLUE_RIGHT1)); // Right
assertTrue(Slotinfo.increasing(CLUE_DOWN)); // Down assertTrue(Slotinfo.increasing(CLUE_DOWN0)); // Down
assertFalse(Slotinfo.increasing(CLUE_UP)); // Up assertFalse(Slotinfo.increasing(CLUE_UP2)); // Up
assertTrue(Slotinfo.increasing(Slot.packSlotKey(0, CLUE_RIGHT))); assertTrue(Slotinfo.increasing(r0c0d1.slotKey));
assertFalse(Slotinfo.increasing(Slot.packSlotKey(0, CLUE_LEFT))); assertFalse(Slotinfo.increasing(r0c0d3.slotKey));
// 2. Test slotScore // 2. Test slotScore
val counts = new byte[SIZE]; val counts = new byte[Neighbors9x8.SIZE];
counts[1] = 2; counts[1] = 2;
counts[2] = 3; counts[2] = 3;
var dict = DictJavaGeneratorMulti.Dicts.makeDict(WORDS); var dict = DictJavaGeneratorMulti.Dicts.makeDict(WORDS);
@@ -415,4 +341,29 @@ public class SwedishGeneratorTest {
var fitOne = gen.maskFitness(grid, 18); var fitOne = gen.maskFitness(grid, 18);
assertTrue(fitOne < fitEmpty); assertTrue(fitOne < fitEmpty);
} }
@Test
void testRng() {
var rng = new Rng(123);
var val1 = rng.nextU32();
var val2 = rng.nextU32();
assertNotEquals(val1, val2);
var rng2 = new Rng(123);
assertEquals(val1, rng2.nextU32());
for (var i = 0; i < 100; i++) {
var r = rng.randomClueDir();
assertTrue(r >= 0 && r <= 5);
var f = rng.nextFloat();
assertTrue(f >= 0.0 && f <= 1.0);
}
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
}
} }

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true">
<sourceFolder url="file://$MODULE_DIR$/src/main/generated-sources" isTestSource="false" generated="true" />
</content>
</component>
</module>