SDcheck.pl

#!/usr/local/bin/perl
数独問題の類似度をチェックするプログラム(V3.0)
(c)copyleft 2013-2-2 coded by shun kinoshita / knuhs

my %SDpool;

初期設定
my @datalist = ( 'SDdata.txt' , 'SDdata2.txt' );  # 問題集ファイル(適当に書き替える)
my $Debug = 1;        # =1 なら内部情報を出力する。=0 なら出力しない

print "** 類似の数独問題が存在するかどうか検査します。\n";

問題データの読込み
my $total = 0;        # 問題の総数を記録する
foreach my $fname ( @datalist )
{
  print "\* $fname start..... ";
  open( SuUDOKU, $fname ) || die "開けません。$!";
  my $SDcounter = 0;  # 各ファイルに含まれる問題の数を記録する
  while( <SuUDOKU> )
  {
    chomp;            # 改行コードを削除
    next unless $_ ;  # 空白行は読み飛ばす
    $SDcounter++;
    ( my $SDname, my $SDdata ) = ( m/^(.*?)\t(.*)$/ ); # 問題名と初期値データを取り出す

初期値データから、各ブロックに属する初期値列を求め“キー”として用いる
    $SDdata =~ m/
(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})(\d{3})/;
    my @data = (
 $1.$4.$7, $2.$5.$8, $3.$6.$9, $10.$13.$16, $11.$14.$17, $12.$15.$18, $19.$22.$25, $20.$23.$26, $21.$24.$27 );
    @data = map { join '', sort split '', $_ } @data;
    $_ = join '', sort map { s/0(?!$)//g; $_ } @data;  # キーをソートし標準形式にする
    $i = ++$SDpool{$_}[0];           # キーを登録し出現回数を記録する
    $SDpool{$_}[$i][0] = $SDname;    # キーの基に問題名と
    $SDpool{$_}[$i][1] = $SDdata;    #   初期値データを保存する
  }
  print "($SDcounter個)\n";          # 各ファイル毎の問題の数を表示
  $total += $SDcounter;              # 問題の総数を記録する
  close( SuUDOKU );
}

結果の判定
print "** 検査結果 ** (計 $total個の問題を検査しました)\n";
my $none = 1;                        # 類似問題がない(初期設定)
foreach my $sdkey( keys %SDpool )    # キーを取り出し
{
  my $count = $SDpool{$sdkey}[0];    # 出現回数から類似問題がないか調べる
  next if $count==1;                 # 重複なし(=1)ならば次へ
  print "\n* 類似の問題が $count個見つかりました。\n";
  for( my $index=1; $index<=$count; $index++ )
  {
    my $initVal = $SDpool{$sdkey}[$index][1];             # 初期設定値を取り出す
    print " ($index)$SDpool{$sdkey}[$index][0]\n";        # 問題名を出力
    print " [0]キー値=\"$sdkey\"\n [1]初期値=\"$initVal\"\n" if $Debug;
                                                          # キー値と初期値を出力
初期値データから各行の値を求める
    $initVal =~ m/(\d{9})(\d{9})(\d{9})(\d{9})(\d{9})(\d{9})(\d{9})(\d{9})(\d{9})/;
    my @data = ( $1, $2, $3, $4, $5, $6, $7, $8, $9 );    # 行毎の初期値から行情報を作る
    @data = map { join '', sort split '', $_ } @data;     # 行情報を標準形式にする
    my $Col = join '', sort map { s/0(?!$)//g; $_ } @data;
    $SDpool{$sdkey}[$index][2] = $Col;                    # 行情報の保存

初期値データから各列の値を求める
    @data = split '', $initVal;
my  @RC;
    for ( my $n = 0; $n <= 8; $n++ )                      # 行と列を入れ替える
    {  $RC[$n] = join '', @data[$n, $n+9, $n+18, $n+27, $n+36, $n+45, $n+54, $n+63, $n+72];  }

    @data = map { join '', sort split '', $_ } @RC;       # 列情報を標準形式にする
    my $Row = join '', sort map { s/0(?!$)//g; $_ } @data;
    $SDpool{$sdkey}[$index][3] = $Row;                    # 列情報の保存
    print " [2]行情報=\"$Col\"\n [3]列情報=\"$Row\"\n\n" if $Debug;
                                                          # 行情報と列情報を出力
   }
   $none = 0;      # 類似問題あり

最終判定
  for( my $i = 1; $i <= $count; $i++ )
  {  for( my $j = 1; $j <= $count; $j++ )
     {  next if $i<= $j;
        if ( $SDpool{$sdkey}[$i][1] eq $SDpool{$sdkey}[$j][1] )
        {  print " ・($j)と($i)は、まったく同じ問題です。\n"; }
        elsif ( $SDpool{$sdkey}[$i][2] eq $SDpool{$sdkey}[$j][2]
             || $SDpool{$sdkey}[$i][3] eq $SDpool{$sdkey}[$j][3] )
        {  print " ・($j)と($i)は、ブロックまたは行を入れ替えただけの同じ問題です。\n"; }
        elsif ( $SDpool{$sdkey}[$i][3] eq $SDpool{$sdkey}[$j][2]
             || $SDpool{$sdkey}[$i][2] eq $SDpool{$sdkey}[$j][3] )
        {  print " ・($j)と($i)は、行と列を入れ替えただけの同じ問題です。\n"; }
        else{  print " ・($j)と($i)は、似ていますが別物です。\n";  }
     }
  }
}
print "\n 類似の問題はありません。" if $none;
print "\n*** チェック終了 ***";