our
@cells
;
our
@rows
;
use
version;
our
$VERSION
= qv(
'0.0.2'
);
my
$mw
;
my
$display_mode_restrict
;
sub
_config_disp_mode_restrict {
my
$restrict_lb
=
shift
;
$mw
=
$restrict_lb
->toplevel;
$restrict_lb
->configure(
-textvariable
=> \
$display_mode_restrict
);
return
;
}
{
sub
enter_unitname {
$mw
or _config_disp_mode_restrict(
shift
);
my
$pause_restrict
;
_kill_tracewindow();
my
$db
=
$mw
->DialogBox(
-title
=>
'Restrict pause mode'
,
-buttons
=> [
qw/Ok Cancel/
],
-default_button
=>
'Ok'
,
-cancel_button
=>
'Cancel'
);
$db
->Label(
-text
=>
"Restrict pause mode 'Value found'"
.
" to a unit\n(e. g. b5 for the center block)"
)->
pack
();
my
$en
=
$db
->add(
'Entry'
,
-width
=> 2,
-textvariable
=> \
$pause_restrict
,
-validate
=>
'key'
)->
pack
();
$en
->configure(
-validatecommand
=> [ \
&_validate_unit
,
$en
] );
$en
->selectionRange( 0,
'end'
);
$db
->configure(
-focus
=>
$en
);
my
$answer
=
$db
->Show();
if
(
$answer
eq
'Cancel'
or
$pause_restrict
!~ /^(r|c|b)[1-9]$/ ) {
Games::Sudoku::Trainer::GUI::set_pause_mode(
'default'
);
return
;
}
my
$unit
= Games::Sudoku::Trainer::Unit->by_name(
$pause_restrict
);
my
$count
=
$unit
->active_Members;
if
(
$unit
->active_Members <= 1 ) {
Games::Sudoku::Trainer::Run::user_err(
"All values of $pause_restrict already found"
);
Games::Sudoku::Trainer::GUI::set_pause_mode(
'default'
);
}
$display_mode_restrict
=
$pause_restrict
;
Games::Sudoku::Trainer::Pause->setMode_restriction(
$pause_restrict
);
return
;
}
sub
_validate_unit {
my
$en
=
shift
;
my
(
$unit_name
,
undef
,
undef
,
undef
,
$action
) =
@_
;
if
(
$action
> 1 ) {
$action
-= 7 }
;
if
(
$action
!= 1 ) {
return
1 }
;
if
(
$unit_name
=~ /^(r|c|b)$/ ) {
return
1;
}
if
(
$unit_name
!~ /^(r|c|b)[1-9]$/ ) {
Games::Sudoku::Trainer::Run::user_err(
"Invalid unit name $unit_name,"
.
" format is 'unit type - unit number'"
,
"\n(unit type: 'r' for row, 'c' for column, 'b' for block)"
);
$en
->focusForce();
return
0;
}
return
1;
}
}
{
sub
enter_cellname {
$mw
or _config_disp_mode_restrict(
shift
);
my
$pause_restrict
;
my
$db
=
$mw
->DialogBox(
-title
=>
'Enter cell name'
,
-buttons
=> [
qw/Ok Cancel/
],
-default_button
=>
'Ok'
,
-cancel_button
=>
'Cancel'
);
$db
->Label(
-text
=>
"Enter the cell name\n(e. g. r1c9 for the upper right cell)"
)
->
pack
();
my
$en
=
$db
->add(
'Entry'
,
-width
=> 4,
-textvariable
=> \
$pause_restrict
,
-validate
=>
'key'
)->
pack
();
$en
->configure(
-validatecommand
=> [ \
&_validate_cell
,
$en
] );
$en
->selectionRange( 0,
'end'
);
$db
->configure(
-focus
=>
$en
);
my
$answer
=
$db
->Show();
$pause_restrict
=
lc
(
$pause_restrict
);
if
(
$answer
eq
'Cancel'
or
$pause_restrict
!~ /^r[1-9]c[1-9]$/ ) {
Games::Sudoku::Trainer::GUI::set_pause_mode(
'default'
);
return
;
}
my
$cell
= Games::Sudoku::Trainer::Cell->by_name(
$pause_restrict
);
my
$candcount
=
$cell
->cands_count;
my
$pause_info_ref
= Games::Sudoku::Trainer::Pause->Info_ref;
if
(
$candcount
> 0
and
defined
$pause_info_ref
and
$pause_info_ref
->[1] eq
'insert'
and
$pause_info_ref
->[3]->[1] ==
$cell
)
{
$candcount
= 0;
}
if
(
$candcount
== 0 ) {
Games::Sudoku::Trainer::Run::user_err(
"Value of $pause_restrict already found"
);
Games::Sudoku::Trainer::GUI::set_pause_mode(
'default'
);
return
;
}
$display_mode_restrict
=
$pause_restrict
;
Games::Sudoku::Trainer::Pause->setMode_restriction(
$pause_restrict
);
return
;
}
sub
_validate_cell {
my
$en
=
shift
;
my
(
$cell_name
,
undef
,
undef
,
undef
,
$action
) =
@_
;
if
(
$action
> 1 ) {
$action
-= 7 };
if
(
$action
!= 1 ) {
return
1 };
if
(
$cell_name
=~ /^r[1-9]?c?[1-9]?$/i ) {
return
1;
}
else
{
Games::Sudoku::Trainer::Run::user_err(
"Invalid cell name $cell_name, format is\n"
,
'"r" - row number - "c" - column number'
);
$en
->focusForce();
return
0;
}
}
}
sub
norestrict_pause {
my
$menubar
=
shift
;
my
$pause_restriction
=
$display_mode_restrict
;
$pause_restriction
or
return
;
length
(
$pause_restriction
) == 4 and _kill_tracewindow();
$pause_restriction
or
return
;
my
$pausemode_menu
=
$menubar
->entrycget(
'Pause Mode'
, -menu );
my
$valfound
=
$pausemode_menu
->entrycget(
'Value found'
, -menu );
my
$anywhere
=
$valfound
->entrycget(
'anywhere'
, -variable );
$$anywhere
=
'anywhere'
;
$display_mode_restrict
=
''
;
Games::Sudoku::Trainer::Pause->setMode_restriction(
''
);
return
;
}
{
my
$pause_restriction
;
my
$tracewindow
;
my
$canv
;
my
$tracestatus
;
sub
build_tracewindow {
my
(
$candsize
,
$cellsize
) =
@_
;
$pause_restriction
=
$display_mode_restrict
;
$pause_restriction
or
return
;
if
( Exists(
$tracewindow
) ) {
$tracewindow
->destroy;
}
$tracewindow
=
$mw
->Toplevel();
$tracewindow
->overrideredirect(1);
$tracewindow
->Frame(
-height
=> 1,
-bg
=>
'black'
)
->
pack
(
-pady
=> 4,
-expand
=> 1,
-fill
=>
'x'
);
$tracewindow
->Label(
-text
=>
"Trace of cell $pause_restriction"
)
->
pack
(
-pady
=> 4 );
$canv
=
$tracewindow
->Canvas(
-borderwidth
=> 2,
-relief
=>
'groove'
)
->
pack
(
-anchor
=>
'w'
);
$tracewindow
->Label(
-textvariable
=> \
$tracestatus
)->
pack
();
Games::Sudoku::Trainer::GUI::build_candsquares(
$canv
,
Games::Sudoku::Trainer::Cell->by_name(
$pause_restriction
) );
$canv
->move(
'new'
, 2, 2 );
$canv
->itemconfigure(
'black'
,
-fill
=>
'black'
,
-outline
=>
'black'
,
-state
=>
'normal'
);
$canv
->itemconfigure(
'red'
,
-fill
=>
'red'
,
-outline
=>
'red'
,
-state
=>
'normal'
);
$canv
->createRectangle(
0, 0,
$cellsize
+ 1,
$cellsize
+ 1,
-tags
=>
'new'
);
$canv
->scale(
'new'
, 0, 0, 1.5, 1.5 );
$canv
->dtag(
'new'
);
my
$pointfac
= 1 /
$canv
->fpixels(
'1p'
);
my
$legendx
=
$cellsize
+ 30;
my
$legendy
= 3;
my
%legend_values
=
(
qw/black Active red Excluded/
,
'orange'
=>
'Just excluded'
);
foreach
my
$key
(
keys
%legend_values
) {
$canv
->createRectangle(
$legendx
,
$legendy
,
$legendx
+
$candsize
- 1,
$legendy
+
$candsize
- 1,
-fill
=>
$key
,
-outline
=>
$key
,
);
$canv
->createText(
$legendx
+ 10,
$legendy
+
$candsize
/ 2 - 1,
-anchor
=>
'w'
,
-text
=>
$legend_values
{
$key
}
);
$legendy
+= 20 *
$pointfac
;
}
$canv
->configure(
-scrollregion
=> [
$canv
->bbox(
'all'
) ] );
my
(
$x_ul
,
$y_ul
,
$x_lr
,
$y_lr
) =
$canv
->bbox(
'all'
);
$canv
->configure(
-height
=>
$y_lr
-
$y_ul
- 1,
-width
=>
$x_lr
-
$x_ul
- 1
);
$tracewindow
->resizable( 0, 1 );
$mw
->
bind
(
'<Configure>'
=>
sub
{
$mw
->
after
(
200
=>
sub
{
$tracewindow
or
return
;
$tracewindow
->state ne
'normal'
and
return
;
$tracewindow
->withdraw;
$tracewindow
->Popup(
-overanchor
=>
'sw'
,
-popanchor
=>
'nw'
);
}
);
}
);
my
$pause_info_ref
= Games::Sudoku::Trainer::Pause->Info_ref;
$pause_info_ref
and update_tracewindow(
$pause_info_ref
);
$tracewindow
->Popup(
-popover
=>
$mw
,
-overanchor
=>
'sw'
,
-popanchor
=>
'nw'
);
return
;
}
sub
update_tracewindow {
my
$found_info_ref
=
shift
;
$tracestatus
=
''
;
if
(
$found_info_ref
->[1] eq
'insert'
) {
my
$cell
=
$found_info_ref
->[3]->[1];
if
(
$cell
->Name eq
$pause_restriction
) {
$tracestatus
=
"Value found for the trace cell\nby strategy "
. Games::Sudoku::Trainer::Pause->Strat
.
".\nEnd of trace mode"
;
Games::Sudoku::Trainer::GUI::set_pause_mode(
'default'
);
Games::Sudoku::Trainer::Check_pause::pause();
_kill_tracewindow()
if
( Games::Sudoku::Trainer::Pause->Mode ne
'Trace a cell'
);
return
;
}
else
{
my
@trace_units
=
Games::Sudoku::Trainer::Cell->by_name(
$pause_restriction
)
->get_Containers();
my
@cell_units
=
$cell
->get_Containers();
foreach
( 0 .. 2 ) {
next
if
(
$cell_units
[
$_
] !=
$trace_units
[
$_
] );
_change_color(
$found_info_ref
->[3]->[0] );
}
}
}
else
{
my
$exclude_info_ref
=
$found_info_ref
->[3];
my
@trace_this
=
map
{
$_
=~ /(\d+)-(\d)/;
my
$cellname
=
$cells
[$1]->Name;
my
$cand
= $2;
$cellname
=~ /
$pause_restriction
/ ? (
$cand
) : ()
}
@$exclude_info_ref
;
_change_color(
@trace_this
);
}
return
;
}
sub
_change_color {
my
@trace_this
=
@_
;
my
$fresh
;
foreach
my
$digit
(
@trace_this
) {
my
(
$square
) =
$canv
->find(
withtag
=>
$pause_restriction
.
"&&d$digit"
);
next
unless
$square
;
grep
(
$_
=~
'black'
,
$canv
->gettags(
$square
) ) or
next
;
$canv
->dtag(
$square
,
'black'
);
$canv
->addtag(
'fresh'
,
withtag
=>
$pause_restriction
.
"&&d$digit"
);
$fresh
++;
}
$fresh
or
return
;
$canv
->itemconfigure(
'fresh'
,
-fill
=>
'orange'
,
-outline
=>
'orange'
);
$tracestatus
=
"$fresh candidates excluded by strategy "
. Games::Sudoku::Trainer::Pause->Strat .
".\n"
;
$canv
->ismapped or
return
;
Games::Sudoku::Trainer::Check_pause::pause();
if
( !Tk::Exists(
$canv
) ) {
return
;
}
$canv
->itemconfigure(
'fresh'
,
-fill
=>
'red'
,
-outline
=>
'red'
);
my
@fresh
=
$canv
->find(
withtag
=>
'fresh'
);
$canv
->addtag(
'red'
,
withtag
=>
'fresh'
);
$canv
->dtag(
'fresh'
);
$tracewindow
->update();
return
;
}
sub
_kill_tracewindow {
$tracewindow
or
return
;
$tracewindow
->destroy;
$tracewindow
=
undef
;
$display_mode_restrict
=
''
;
Games::Sudoku::Trainer::Pause->setMode_restriction(
''
);
return
;
}
}
1;