2008年02月12日(火) 0:30 [Perl

バレーボール関係で埋まりかけた連休の合間を見て、数独を解くPerlスクリプトを書いたので、ネタにしようと思う。(自宅サーバネタはまた間に合わなかった)

Wikipedia:数独

上記URLに画像が紹介されている一番ポピュラーな縦9マス横9マスの問題の場合、各列には1~9の数字が1つだけ入り、各行にも1~9の数字が1つだけ入る。更に3×3の正方形内にも1~9の数字が独立して入る。この3つのルールに違反しない数字を書くマスに埋めていくのが数独だ。間違った数字を入れてしまうとどこかで矛盾が生じてしまい、全マスに数字を収められなくなってしまう。逆を取れば、全マスに数字を収められればそれが正解となる。

今回作成したスクリプトは、まず最初のマスに適当に数字を入れ、次のマスへ移動する。そこでも適当に数字を入れる。入れられる数字が無かった場合、1つ前のマスに戻り別の数字を入れ、また次のマスへ移動し、入れられる数字があれば入れていく。これを全マスに数字を収められるまで繰り返していく。いわゆるバックトラックという手法になる。(芸が無いと言えばそれまでだが)

スクリプトは以下のように使用する。

perl sudoku.pl question.txt

「question.txt」(ファイル名は任意)には、空きマスは「0」として空白区切りで下記のように問題を記述する。

0 0 3 5 1 2 0 0 0
0 1 0 0 4 0 3 2 0
6 0 0 0 8 0 0 4 0
3 0 0 0 0 4 0 0 6
1 4 9 0 0 0 7 8 5
5 0 0 9 0 0 0 0 4
0 3 0 0 9 0 0 0 2
0 5 8 0 3 0 0 1 0
0 0 0 1 2 5 8 0 0

これを実行し問題を解くことが出来た場合、空きマスの「0」が数字に書き換えられた「question-answer.txt」が生成される。

4 7 3 5 1 2 9 6 8
8 1 5 6 4 9 3 2 7
6 9 2 3 8 7 5 4 1
3 2 7 8 5 4 1 9 6
1 4 9 2 6 3 7 8 5
5 8 6 9 7 1 2 3 4
7 3 1 4 9 8 6 5 2
2 5 8 7 3 6 4 1 9
9 6 4 1 2 5 8 7 3

例では9×9の問題を使用しているが、もっと大きな問題でも解くことができる(はず)。問題に無理がある場合はもちろん失敗してしまう。問題があっているのに失敗することは無いが、あったらごめんなさい。

ここをクリックでコードの表示・非表示を切り替える。

需要は無いだろうけど、ダウンロードは下記からどうぞ。

ダウンロード

バグがあった場合は、ぜひご連絡を。

今度はイラストロジックを解くスクリプトにでも挑戦してみようかなと思う。

2008年02月05日(火) 23:00 [Perl

PerlのNet::DNSモジュールを使用して、DNSに問い合わせを行ってみる。

Net::DNSモジュールのインストール

標準ではNet::DNSがインストールされていないため、cpanにてインストールを行う。

$ su -
Password: **********
# perl -MCPAN -e shell
cpan>install Net::DNS

これでインストールは完了。
cpanを最初に利用する場合は設定などを聞かれるが、私の場合、ほぼ全てを初期値のままで設定した。「Asia」、「Japan」とダウンロード先のサーバを指定した程度だ。

Windowsの場合はPPMなどを利用すると良いだろう。その辺りのことについては別の機会に投稿しようと思う。

正引き(ホスト名→IPアドレス)

ホスト名からIPアドレスを取得する。

use strict;
use Net::DNS;

my $host = 'spring.sakurasaita.net';
my $res = Net::DNS::Resolver->new;

#ホスト名のIPアドレスを取得(正引き)
if(my $query = $res->search($host, 'A')){
    # Aレコードのみを取得し、IPアドレスの一覧を印字
    print map {$_->address."\n"} grep($_->type eq 'A', $query->answer);
}else{
    print $res->errorstring, "\n";
}

answerの結果でtypeがAレコードの場合のみaddressを取得している。これには訳があり、取得しようとするホスト名がCNAMEレコードだった場合、addressが使用できず、「$_->address」でエラーが発生してしまうためだ。

レコードの種類については、「@IT:主なDNSレコードの種類」が参考になる。

上記のスクリプトを実行すると以下のような結果になる。

# $hostが「spring.sakurasaita.net」の場合
$ perl dns.pl
210.250.97.3
# $hostが「www.google.com」の場合
$ perl dns.pl
66.249.89.104
66.249.89.147
66.249.89.99

逆引き(IPアドレス→ホスト名)

IPアドレスからホスト名を取得する。IPアドレスは任意のものに変更した方が確実に動作するかもしれない。

use strict;
use Net::DNS;

my $ip = '66.249.89.99';
my $res = Net::DNS::Resolver->new;

# IPアドレスのホスト名を取得(逆引き)
if(my $query = $res->search($ip, 'PTR')){
    # PTRレコードのみを取得し、ホスト名の一覧を印字
    print map {$_->ptrdname."\n"} grep($_->type eq 'PTR', $query->answer);
}else{
    print $res->errorstring;
}

answerの結果でtypeがPTRレコードの場合のみホスト名を取得している。これを実行すると以下のような結果になる。

$ perl dns.pl
jp-in-f99.google.com

任意のDNSサーバに問い合わせる  正引き(ホスト名→IPアドレス)

DNSサーバのIPアドレスを更新した場合、周囲のDNSサーバに変更が反映されるまでには多少の時間が必要になる。例えば、ダイナミックDNSサービスを使用していて、IPアドレスの更新を行い、更新が正常に行われているか確認するとする(VALUE-DOMAINなどたまに更新されていないことがある)。その際、プロバイダのDNSサーバに問い合わせたのではタイムラグの問題で変更が反映されていない可能性がある。よって、ドメインを管理している大元のDNSサーバに問い合わせるのが最も確実な方法となる。

下記のスクリプトは、ドメインを管理しているDNSサーバを取得し、取得したDNSサーバに問い合わせを行っている。

use strict;
use Net::DNS;

my $domain = 'sakurasaita.net';
my $host = 'spring.sakurasaita.net';
my $res = Net::DNS::Resolver->new;
my @ns;

# ドメインを管理しているDNSサーバの一覧を取得
if(my $query = $res->query($domain, 'NS')){
    # NSレコードのみを取得し、DNSサーバの一覧を取得
    @ns = map {$_->nsdname} grep($_->type eq 'NS', $query->answer);
    # 取得したDNSサーバの一覧を印字
    print "DNSサーバ:\n";
    print map {"$_\n"} @ns;
}else{
    print $res->errorstring, "\n";
    exit;
}

# 問い合わせるDNSサーバを設定する
$res->nameservers(@ns) if(@ns > 0);

# ホスト名のIPアドレスを取得(正引き)
if(my $query = $res->search($host, 'A')){
    # Aレコードのみを取得し、IPアドレスの一覧を印字
    print "IPアドレス:\n";
    print map {$_->address."\n"} grep($_->type eq 'A', $query->answer);
}else{
    print $res->errorstring, "\n";
}

上記のスクリプトを実行すると以下のような結果になる。

$ perl dns.pl
DNSサーバ:
ns3.value-domain.com
ns1.value-domain.com
ns2.value-domain.com
IPアドレス:
210.250.97.3

「spring.sakurasaita.net」のIPアドレスを問い合わせているのに、DNSサーバの一覧を取得する部分では「sakurasaita.net」を指定している。これは、「spring.sakurasaita.net」にNSレコード(ドメインを管理するDNSサーバを定義するレコード)を指定していないからであって、必ずしも上位レベルのドメインを指定しなければいけないというわけではない。

私の場合、一部のサブドメインにNSレコードを設定しているが、そのサブドメインはDNSサーバの一覧取得にも使用できる。

# 上記スクリプトの下記変数の値のみ変更
my $domain = 'flower.sakurasaita.net';
my $host = 'flower.sakurasaita.net';

上記の変更を行った後に実行すると以下のような結果になる。

$ perl dns.pl
DNSサーバ:
ns1.dns.ne.jp
ns2.dns.ne.jp
IPアドレス:
219.94.128.32

ダイナミックDNSのIPアドレスを更新した後、問い合わせるDNSサーバを指定した場合としなかった場合とで結果を比べてみれば違いが分かると思う。

まとめ

CGIを作成するのであればあまり必要の無い知識だが、スクリプトで手軽にDNS問い合わせを行えるのはおもしろい。このモジュールを利用してダイナミックDNSサービスのIPアドレスを更新するスクリプトを作成してみようと思う(というより、既に作成してあるのでまとめて投稿しようと思う)。

Copyright 2008 As You Like It All rights reserved.
Powered by Wordpress, Base template by WEB MAGIC, Photo by Encyclorecorder