北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

Re: 不良セクタの隣〜 横へな 2014.3.7 問題

不良セクタの隣 の問題で肩慣らし。

以下はとりあえずテスト通しただけの状態そのままのコード。単体テスト含めてフルスクラッチからで45分くらい。場当たり的にモジュール化したので、無駄が多い。

初め「複数の」を見落としてて、最後に方針転換したけど、適度にモジュール化されてたのでなんとかなった。

Cloud9上で解いたので、キーバインドでも苦しんだ。Ctrl-s 押し忘れてセーブされてなかったり Ctrl-k 押してよくわからなくなったり。あと、45分の時間には含んでないけど、 コアモジュールのインストールも必要

package Sector;
use strict;
use warnings;
use Exporter qw(import);

our @EXPORT_OK = qw(solve);

my @all_sectors = (
    [100 .. 107],
    [200 .. 215],
    [300 .. 323],
    [400 .. 431],
);

sub degrees ($$) {
    my ($sectors, $sector) = @_;
    my $deg = 360 / @$sectors;
    my $sec_num = $sector - $sectors->[0];
    if ($sec_num == 0) {
        ([360 - $deg / 2, 360], [0, $deg / 2]);
    } else {
        ([($sec_num - .5) * $deg, ($sec_num + .5) * $deg]);
    }
}

sub sectors_in_degrees ($$$) {
    my ($sectors, $deg1, $deg2) = @_;
    my %result;
    for my $sec (@$sectors) {
        my @ds = degrees($sectors, $sec);
        for (@ds) {
            my ($d1, $d2) = @$_;
            $result{$sec}++ unless ($d1 <= $deg1 && $d2 <= $deg1) || ($deg2 <= $d1 && $deg2 <= $d2);
        }
    }
    return keys %result;
}

sub warning_sectors ($$) {
    my ($all_sectors, $sector) = @_;
    my $ringnum = int($sector / 100) - 1;
    my @degrees = degrees($all_sectors->[$ringnum], $sector);

    my %result;
    
    my $localnum = $sector % 100;
    my $localmax = @{$all_sectors->[$ringnum]};
    $result{$all_sectors->[$ringnum][($localnum + 1) % $localmax]}++;
    $result{$all_sectors->[$ringnum][($localnum + $localmax - 1) % $localmax]}++;

    if (0 < $ringnum) {
        my @secs = map { sectors_in_degrees($all_sectors->[$ringnum - 1], $_->[0], $_->[1]) } @degrees;
        $result{$_}++ for @secs;
    }
    if ($ringnum < $#$all_sectors) {
        my @secs = map { sectors_in_degrees($all_sectors->[$ringnum + 1], $_->[0], $_->[1]) } @degrees;
        $result{$_}++ for @secs;
    }
    
    keys %result;
}

sub _diff ($$) {
    my ($ar1, $ar2) = @_;
    my %ar2 = map { $_ => 1 } @$ar2;
    grep { ! $ar2{$_} } @$ar1;
}

sub _uniq ($) {
    my $ar = shift;
    my %ar = map { $_ => 1} @$ar;
    keys %ar;
}

sub solve ($) {
    my @broken = split /,/, $_[0];
    my %warnings;
    for (map { warning_sectors(\@all_sectors, $_) } @broken) {
        $warnings{$_}++;
    }
    my @many_warnings = grep { $warnings{$_} >= 2} keys %warnings;
    my @pure_warnings = _uniq [_diff(\@many_warnings, \@broken)];
    join ',', sort @pure_warnings or 'none';
}
use strict;
use warnings;
use Test::More import => [qw(done_testing is)];
use Sector qw(solve);

while (<DATA>) {
    tr/\r\n//d;
    my ($num, $in, $out) = split /\s+/, $_;
    is solve($in), $out, "TEST#$num";
}

done_testing;
__END__
0  400,401,302 300,301,402
1  105,100,306,414 none
2  100 none
3  211 none
4  317 none
5  414 none
6  100,106 107
7  205,203 102,204
8  303,305 304
9  407,409 306,408
10 104,103 207
11 204,203 102,305
12 313,314 209,418
13 419,418 314
14 100,102,101 201,203
15 103,206,309 205,207,308,310
16 414,310,309 206,311,413
17 104,102,206,307,102,202 101,103,203,204,205,207,308
18 104,206,308,409,407 103,205,207,306,307,309,408,410
19 313,406,213,301,409,422,412,102,428 none
20 101,300,210,308,423,321,403,408,415 none
21 304,316,307,207,427,402,107,431,412,418,424 none
22 205,408,210,215,425,302,311,400,428,412 none
23 200,311,306,412,403,318,427,105,420 none
24 105,305,407,408,309,208,427 104,209,306,406
25 311,304,322,404,429,305,316 203,303,321,405,406,430
26 210,401,316,425,101 211,315
27 414,403,404,416,428,421 303,415
28 207,300,103,211,428 104,206
29 322,314,310 none
30 427,200,215 100,323
31 311,402,424,307,318,430,323,305,201 200,204,301,302,306,322,423,425,431
32 425,430,408 none
33 202,320,209,426 319,427
34 430,209,302,310,304,431,320 202,303,323
35 208,206,406,424,213,312 207,311,313
36 420,302,313,413,317,402 301,403
37 319,306,309,418,204,411 305,307,308,412
38 400,308,105,430,203,428,209 104,210,429,431
39 200,305,214 215
40 214,408,410,407,317,422 306,316,409,423

毎回そうなんだけど、実装中、部品の単体テストをしてないので、最後にテスト全部通るかは神のみぞ知る。とはいえ、1時間で部品の単体テストまで書くのは、時間配分的に現実的ではないと思う。

追記: 3年前の自分の回答 。今書いたコードより整ってるようには見えるけど、 $D ってなんだっけ・・・。