Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nudoku for openSUSE:Factory checked in at 2026-01-22 15:13:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nudoku (Old) and /work/SRC/openSUSE:Factory/.nudoku.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nudoku" Thu Jan 22 15:13:30 2026 rev:12 rq:1328240 version:7.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/nudoku/nudoku.changes 2025-08-20 13:28:57.745521713 +0200 +++ /work/SRC/openSUSE:Factory/.nudoku.new.1928/nudoku.changes 2026-01-22 15:13:33.272676389 +0100 @@ -1,0 +2,6 @@ +Tue Jan 20 10:19:59 UTC 2026 - Michael Vetter <[email protected]> + +- Update to 7.0.0: + * Implement better sudoku solver (pr #78) + +------------------------------------------------------------------- Old: ---- nudoku-6.0.0.tar.gz New: ---- nudoku-7.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nudoku.spec ++++++ --- /var/tmp/diff_new_pack.ZQcMlu/_old 2026-01-22 15:13:34.104711062 +0100 +++ /var/tmp/diff_new_pack.ZQcMlu/_new 2026-01-22 15:13:34.104711062 +0100 @@ -1,7 +1,7 @@ # # spec file for package nudoku # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: nudoku -Version: 6.0.0 +Version: 7.0.0 Release: 0 Summary: Ncurses based sudoku game License: GPL-3.0-only ++++++ nudoku-6.0.0.tar.gz -> nudoku-7.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nudoku-6.0.0/ChangeLog new/nudoku-7.0.0/ChangeLog --- old/nudoku-6.0.0/ChangeLog 2025-08-20 11:59:01.000000000 +0200 +++ new/nudoku-7.0.0/ChangeLog 2026-01-20 11:15:55.000000000 +0100 @@ -1,3 +1,6 @@ +6.0.0 to 7.0.0 (2026-01-20) + * Implement better sudoku solver (pr #78) + 5.0.0 to 6.0.0 (2025-08-20) * Wrap around board edges when navigating with arrow keys (pr #68) * Update Turkish translation (pr #69) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nudoku-6.0.0/configure.ac new/nudoku-7.0.0/configure.ac --- old/nudoku-6.0.0/configure.ac 2025-08-20 11:59:01.000000000 +0200 +++ new/nudoku-7.0.0/configure.ac 2026-01-20 11:15:55.000000000 +0100 @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([nudoku], [6.0.0], [[email protected]]) +AC_INIT([nudoku], [7.0.0], [[email protected]]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_MACRO_DIRS([m4]) AM_INIT_AUTOMAKE([foreign]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nudoku-6.0.0/man/nudoku.6 new/nudoku-7.0.0/man/nudoku.6 --- old/nudoku-6.0.0/man/nudoku.6 2025-08-20 11:59:01.000000000 +0200 +++ new/nudoku-7.0.0/man/nudoku.6 2026-01-20 11:15:55.000000000 +0100 @@ -1,5 +1,5 @@ .\" Manpage for nudoku. -.TH man 6 "20 August 2025" "6.0.0" "nudoku man page" +.TH man 6 "20 January 2026" "7.0.0" "nudoku man page" .SH NAME nudoku \- ncurses based sudoku game .SH SYNOPSIS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nudoku-6.0.0/src/sudoku.c new/nudoku-7.0.0/src/sudoku.c --- old/nudoku-6.0.0/src/sudoku.c 2025-08-20 11:59:01.000000000 +0200 +++ new/nudoku-7.0.0/src/sudoku.c 2026-01-20 11:15:55.000000000 +0100 @@ -77,61 +77,91 @@ return true; } -/* solver code is influenced by sb0rg: https://codereview.stackexchange.com/questions/37430/sudoku-solver-in-c */ -static bool is_available(char puzzle[STREAM_LENGTH], int row, int col, int num) -{ - int i; - int rowStart = (row/3) * 3; - int colStart = (col/3) * 3; - - num += 48; - - for (i=0; i<9; i++) - { - if (puzzle[row * 9 + i] == num) - return false; - if (puzzle[i * 9 + col] == num) - return false; - if (puzzle[(rowStart + (i % 3)) * 9 + (colStart + (i / 3))] == num) - return false; - } - return true; +static int get_candidates(char puzzle[STREAM_LENGTH], int pos) { + int row = pos / 9, col = pos % 9; + int box_row = (row / 3) * 3, box_col = (col / 3) * 3; + int mask = 0; + + for (int i = 0; i < 9; i++) { + if (puzzle[row * 9 + i] != '.') mask |= 1 << (puzzle[row * 9 + i] - '1'); + if (puzzle[i * 9 + col] != '.') mask |= 1 << (puzzle[i * 9 + col] - '1'); + if (puzzle[(box_row + i / 3) * 9 + box_col + i % 3] != '.') + mask |= 1 << (puzzle[(box_row + i / 3) * 9 + box_col + i % 3] - '1'); + } + return 0x1FF & ~mask; +} + +static int find_best_cell(char puzzle[STREAM_LENGTH], int *cand_out) { + int best_pos = -1, best_count = 10; + + for (int pos = 0; pos < 81; pos++) { + if (puzzle[pos] != '.') continue; + + int candidates = get_candidates(puzzle, pos); + int n = __builtin_popcount(candidates); // single CPU instruction + + if (n == 0) { *cand_out = 0; return -2; } // dead end + if (n < best_count) { + best_count = n; + best_pos = pos; + *cand_out = candidates; + if (n == 1) break; + } + } + return best_pos; // -1 if solved +} + +// counts solutions (always backtracks) +static int count_solutions(char puzzle[STREAM_LENGTH], int count) { + int candidates; + int pos = find_best_cell(puzzle, &candidates); + + if (pos == -2) return count; // dead end + if (pos == -1) return count + 1; // solved + + for (int num = 0; num < 9 && count < 2; num++) { + if (candidates & (1 << num)) { + puzzle[pos] = '1' + num; + count = count_solutions(puzzle, count); + puzzle[pos] = '.'; // always reset + } + } + return count; } -/* solve_recursively function influenced by CMPS: https://stackoverflow.com/questions/24343214/determine-whether-a-sudoku-has-a-unique-solution */ -static int solve_recursively(char puzzle[STREAM_LENGTH], int row, int col, int count) -{ - int i; - if (row == 9) - { - row = 0; - if (++col == 9) - return 1+count; - } - if (puzzle[row * 9 + col] != '.') // skip filled cells - return solve_recursively(puzzle, row + 1, col, count); - for (i = 0; i < 9 && count < 2; ++i) - { - if (is_available(puzzle, row, col, i + 1)) - { - puzzle[row * 9 + col] = i + 1 + 48; - count = solve_recursively(puzzle, row + 1, col, count); - } - else - puzzle[row * 9 + col] = '.'; // reset on backtrack - } - return count; -} - -int solve(char puzzle[STREAM_LENGTH]) -{ - int count = 0; - if (is_valid_puzzle(puzzle)) - return solve_recursively(puzzle, 0, 0, count); - else - return 0; +// Fills puzzle with first solution (keeps values) +static bool fill_puzzle(char puzzle[STREAM_LENGTH]) { + int candidates; + int pos = find_best_cell(puzzle, &candidates); + + if (pos == -2) return false; // dead end + if (pos == -1) return true; // solved + + for (int num = 0; num < 9; num++) { + if (candidates & (1 << num)) { + puzzle[pos] = '1' + num; + if (fill_puzzle(puzzle)) return true; // keep solution + puzzle[pos] = '.'; + } + } + return false; } +int solve(char puzzle[STREAM_LENGTH]) { + if (!is_valid_puzzle(puzzle)) + return 0; + + // count solutions on a copy + char copy[STREAM_LENGTH]; + strncpy(copy, puzzle, STREAM_LENGTH); + int count = count_solutions(copy, 0); + + // fill original if solvable + if (count >= 1) + fill_puzzle(puzzle); + + return count; +} /* GENERATOR */ /* Generator code is influenced by: http://rubyquiz.strd6.com/quizzes/182-sudoku-generator */ static int rand_int(int n)
