# 0hn0: is this correct deduction?

Starting with:

Text:

.....9..6
....3.2..
6.5....2.
.7.6..R..
...7R....
..R.R.6..
5.....5..
..4..2...
.9...1.1.


We soon get to this:

Text:

.....9..6
....3.2..
6B5....2.
.7.6..R..
.B.7R.B..
.BR.RB7BB
5B...R5BB
.B4.R2BR*
.9..R1R1B


If you look at the empty spot in the lower right corner, you can see that if it is Blue, then the 6 at the top of the column cannot see that spot, so there must be Red between it and the 6. If this is the case this spot is not seen by any Blue that starts with a number.

There is no rule that a Blue will always be seen by a Blue with a number but I have never seen a situation when it is not - it would mean that the spot can be either Blue or Red which would be ambiguous (again, nothing against that in the rules).

There are other ways to proceed from here, this is not a difficult puzzle, but I do feel that I can confidently put Red in the lower right empty spot based on the deduction above. Is this a valid deduction? If we solve the puzzle by usual means, sure enough there will be Red there.

• Reasoning from assumed uniqueness of the solution is 1) a deadly sin 2) a completely valid speedrunning strat.
– Bass
Nov 29, 2022 at 5:37
• Especially when the source code leaves only one option. Nov 29, 2022 at 7:26

This is what's called a "uniqueness deduction": using the fact "the puzzle has exactly one solution" as a logical premise to conclude things from.

As long as that statement is true - that the puzzle-writer didn't make a mistake, or the puzzle-generation algorithm isn't broken - using uniqueness logic will work.

However, the puzzle creator (whether human or machine) could not use that additional premise when making the puzzle. So there must be a feasible way to solve the puzzle without it. Using this premise, then, could skip the intended path through the puzzle.

Because of this, many solvers prefer not to use uniqueness logic - it feels like "cheating". It's not a rule for how the puzzle should be filled: it's a guarantee given from the creator, on a "higher level" than the rules about valid configurations.

So, using uniqueness logic won't break things (again, as long as there wasn't a mistake when creating the puzzle). But it's never strictly necessary, and there are good reasons to avoid using it. It's up to your own personal judgement what you want to do.

• "... many solvers prefer not to use uniqueness logic": do you have a source backing this statement? I came to accept this as a valid and clever way to solve sudokus faster. I did it in the past but in the end it felt silly to deduce the value of a cell and then not write it down. Nov 27, 2022 at 16:22
• The developer never gave the uniqueness constraint so I do not think we can take it for granted, I looked at an earlier version source code at <github.com/Techdojo/0hn0> but was unable to conclude with certainty if uniqueness is guaranteed. Nov 27, 2022 at 20:22

This is not in the rules, but it has to be.

Because if a solution exists where the dot is blue, then the same position but with a red dot is equally valid. That means there would be multiple solutions.

With the assumption that there is a unique solution, which is always the case in these puzzles, you can rule out a blue dot.

@Florian F's point aside, there's an important rule in the site's 0hn0 script source code:

Blue dots can see others in their own row and column

It doesn't say only the numbered dots can see others. There's also an error message to the same effect (it showed up when I intentionally painted an isolated dot blue to test it out):

A blue dot should always see at least one other

A dot completely isolated from all the numbers is technically "safe" to paint either red or blue. If there's a rule to paint such dots always blue, that also provides a unique solution, but that can lead to a dot not seeing any blue dots. If multiple empty blue dots form an isolated "island", there should have been a number or you just paint them all red.

If you have a suspicious dot that's not meant to be surrounded by red dots, its being painted blue would require being isolated with red squares blocking it off, and the last paragraph would be the case anyway.

PS.

https://github.com/Techdojo/0hn0/blob/master/js/tile.js

  function isLockedIn() {
if (!isDot()) return false;
for (var dir in Directions) {
if (self.move(dir) && !self.move(dir).isWall())
return false;
}
return true;
}


All the tiles can fit four possible categories: numbers (numbered blue tiles), dots (blue without numbers), walls (red) or unknowns (grey). LockedIn was the name of the error I experienced. If I left a locked-in tile alone, "this one should be easy" (MustBeWall) shows up instead:

Only dots can be examined for the LockedIn status. Then you move to the adjacent tiles and check them. If it's not surrounded by walls from all directions, the original dot isn't locked in. But what if there's a numberless island of otherwise "safe" tiles surrounded by walls? That's how the MustBeWall code works:

https://github.com/Techdojo/0hn0/blob/master/js/grid.js

  function solve(silent, hintMode) {
var tryAgain = true;
var attempts = 0;
var hintTile = null;
var pool = tiles;

// for stepByStep solving, randomize the pool
if (hintMode) {
pool = tiles.concat();
Utils.shuffle(pool);
}

while (tryAgain && attempts++ < 99) {
//console.log('solving, attempt ', attempts);

tryAgain = false;

if (isDone()) {
if (silent)
clearTimeout(renderTOH);
return true;
}

// first pass collection
for (var i=0; i<pool.length; i++) {
pool[i].info = pool[i].collect();
}

// second pass collection, now we have full info
for (var i=0; i<pool.length; i++) {
var tile = pool[i],
info = tile.collect(tile.info);

// dots with no empty tiles in its paths can be fixed
if (tile.isDot() && !info.unknownsAround && !hintMode) {
tile.number(info.numberCount, true);
tryAgain = HintType.NumberCanBeEntered;
break;
}

// if a number has unknowns around, perhaps we can fill those unknowns
if (tile.isNumber() && info.unknownsAround) {

// if its number is reached, close its paths by walls
if (info.numberReached) {
if (hintMode)
hintTile = tile;
else
tile.close();
tryAgain = HintType.ValueReached;
break;
}

// if a tile has only one direction to go, fill the first unknown there with a dot and retry
if (info.singlePossibleDirection) {
if (hintMode)
hintTile = tile;
else
tile.closeDirection(info.singlePossibleDirection, true, 1);
tryAgain = HintType.OneDirectionLeft;
break;
}
// if its number CAN be reached by filling out exactly the remaining unknowns, then do so!
//else if (info.canBeCompletedWithUnknowns) {
//console.log(tile.x, tile.y)
//tile.close(true);
//tryAgain = true;
//}

// check if a certain direction would be too much
for (var dir in Directions) {
var curDir = info.direction[dir];
if (curDir.wouldBeTooMuch) {
if (hintMode)
hintTile = tile;
else
tile.closeDirection(dir);
tryAgain = HintType.WouldExceed;
break;
}
// if dotting one unknown tile in this direction is at least required no matter what
else if (curDir.unknownCount && curDir.numberWhenDottingFirstUnknown + curDir.maxPossibleCountInOtherDirections <= tile.value) {
if (hintMode)
hintTile = tile;
else
tile.closeDirection(dir, true, 1);
tryAgain = HintType.OneDirectionRequired;
break;
}
}
// break out the outer for loop too
if (tryAgain)
break;
}
// if a number has its required value around, but still an empty tile somewhere, close it
// (this core regards that situation FROM the empty unknown tile, not from the number itself)
// (but only if there are no tiles around that have a number and already reached it)
if (tile.isUnknown() && !info.unknownsAround && !info.completedNumbersAround) {
if (info.numberCount == 0) {
if (hintMode)
hintTile = tile;
else
tile.wall(true);
tryAgain = HintType.MustBeWall;
break;
}
//else if (info.numberCount > 0) {
//tile.number(info.numberCount);
//tryAgain = 7;
//break;
//}
}
}
if (hintMode) {
hint.mark(hintTile, tryAgain);
//console.log(tryAgain, tile)
tryAgain = false;
return false;
}
}

//console.log(attempts + ' attempts. ' + (tryAgain? 'Not done.' : 'Done.'));
render();

if (silent)
clearTimeout(renderTOH);

return false;
}


Notice that the last commented/documented section always leads to a MustBeWall situation (with "safe" or unsafe empty tiles alike). It works with a numberless empty tile surrounded by walls, so it can work with a possible numberless island too. If you can't prove a tile is blue with all the available information without a doubt, it must be red.

• I don't think this is the case here.
– ACB
Nov 27, 2022 at 14:40
• Yes, it is. It boils down to this. Nov 27, 2022 at 15:07
• Thanks for the code. So based on the code, we can conclude that everything that can't see a number must be wall. +1 Nov 29, 2022 at 8:36
• "A blue dot should always see at least one other" - it is in theory possible to have 2 (or more) blue dots that see each other and completely isolated form other dots. In practice for not numbered dots it never happens, it also is not stated anywhere that would be the case. I feel that this answer concentrates too much on the case of one surrounded dot, where the question was not about that case. Dec 2, 2022 at 19:19
• "[I]t is in theory possible to have 2 (or more) blue dots that see each other and completely isolated form other dots." - This answer is supposed to prove that uniqueness logic is actually used by the developer even if there are isolated blue tiles next to each other, making your initial assumption correct. Dec 5, 2022 at 8:09