Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env perl6
- use v6.c;
- class Node
- {
- has Int $.x;
- has Int $.y;
- has Int $.size;
- has Int $.used;
- has Int $.avail;
- method Str { "[$!x,$!y:$!size/$!used/$!avail]" }
- method gist { self.Str }
- method infix:<eq>(Node $a, Node $b) { $a.x == $b.x && $a.y == $b.y }
- method WHICH { "Node|$!x|$!y" }
- method is-empty { $!used == 0 }
- # Note that IRL, it may or may not fit if self.avail == $n.used (in TB)
- method fits(Node $n) { $!avail >= $n.used }
- method blocks(Node $n) { $!used > $n.avail }
- method adjacent-to(Node $n) { abs($!x-$n.x) + abs($!y-$n.y) == 1 }
- method add-data(Int $data)
- {
- die "No space on {self} for $data of data!" if $data > $!avail;
- $!used += $data;
- $!avail -= $data;
- }
- method move-data-to(Node $n)
- {
- die "Nodes {self} and $n are not adjacent!" unless self.adjacent-to($n);
- die "Data on {self} doesn't fit on $n" if self.blocks($n);
- my $ret = "Move data from {self} to $n";
- $n.add-data($!used);
- $!avail += $!used;
- $!used = 0;
- $ret ~= " ({self}; $n)";
- return $ret;
- }
- }
- sub MAIN(IO() $inputfile where *.f, Bool :v(:$verbose) = False)
- {
- # Read nodes
- my @nodes;
- for $inputfile.lines -> $line {
- next unless $line ~~ / ^ '/dev/' /;
- my ($node, $size, $used, $avail, $use) = $line.words;
- my ($x, $y) = $node.comb(/\d+/)».Int;
- for $size, $used, $avail -> $s is rw {
- # Remove 'T' postfix. Assumes all sizes are in TB.
- $s = $s.subst('T','').Int;
- }
- @nodes[$x;$y] = Node.new(:$x, :$y, :$size, :$used, :$avail);
- }
- # Part 1
- my $viable = 0;
- for @nodes[*;*] -> $n {
- next if $n.is-empty;
- $viable += @nodes[*;*].grep({ $_ ne $n && $_.fits($n) });
- }
- say "There are $viable viable pairs of nodes.";
- # Part 2
- # This algorithm does NOT work in all cases, but IF it gives an answer, it SHOULD be the shortest path,
- # if the following assumptions are true:
- # - There is exactly one empty node
- # - The empty node is involved in all moves, i.e. no data can be moved onto a node already in use
- # - There are no blocking nodes (that can't be moved onto the empty node) in rows 0 and 1
- # - All blocking nodes are in a single contiguous horizontal "wall" from x0,y to x1,y
- # - All non-blocking nodes are of similar sizes, and can hold each others data
- my $grid-width = +@nodes[*;0];
- my $grid-height = +@nodes[0;*];
- # Find the empty node
- my @empty-nodes = @nodes[*;*].grep(*.is-empty);
- if (!@empty-nodes) {
- die "Unable to find an empty node!";
- }
- elsif (@empty-nodes > 1) {
- warn "Warning: more than one empty node found, using the first one";
- }
- my $empty = @empty-nodes[0];
- # Find all nodes that block the empty node - i.e. can't be moved into the empty node
- my @blocking-nodes = @nodes[*;*].grep(*.blocks($empty));
- # Assumption: this is a solid horizontal wall that we need to pass.
- my ($wall-x0, $wall-x1) = (@blocking-nodes».x.min, @blocking-nodes».x.max);
- my ($wall-y0, $wall-y1) = (@blocking-nodes».y.min, @blocking-nodes».y.max);
- warn "Blocking nodes not in a horizontal wall" if $wall-y1 > $wall-y0;
- warn "Blocking nodes not in an unbroken horizontal wall"
- if any(($wall-x0..$wall-x1).map({ @nodes[$_;$wall-y0] })) ∉ @blocking-nodes;
- # We need to pass left or right of the wall, take the shortest route
- my $target-x = $empty.x;
- if $wall-y0 < $empty.y {
- if $wall-x0 > $empty.x || $wall-x1 < $empty.x {
- $target-x = $empty.x;
- }
- elsif $wall-x1 < $grid-width-1 {
- $target-x = $wall-x1+1;
- }
- elsif $wall-x0 > 0 {
- $target-x = $wall-x0-1;
- }
- else {
- die "Unable to pass blocking wall at y = $wall-y0, x = $wall-x0 - $wall-x1";
- }
- }
- my @moves;
- # Move empty cell to x == target-x
- for $empty.x ... $target-x -> $x {
- next if $x == $empty.x;
- @moves.push: @nodes[$x;$empty.y].move-data-to($empty);
- $empty = @nodes[$x;$empty.y];
- }
- # Now move it to y == 0
- for $empty.y ... 0 -> $y {
- next if $y == $empty.y;
- @moves.push: @nodes[$empty.x;$y].move-data-to($empty);
- $empty = @nodes[$empty.x;$y];
- # If we're at the extreme right border, we need to take a step left when y == 1
- if $empty.x == $grid-width-1 && $empty.y == 1 {
- @moves.push: @nodes[$empty.x-1;1].move-data-to($empty);
- $empty = @nodes[$empty.x-1;1]
- }
- }
- # And move it to x == grid-width-2
- for $empty.x ... $grid-width-2 -> $x {
- next if $x == $empty.x;
- @moves.push: @nodes[$x;$empty.y].move-data-to($empty);
- $empty = @nodes[$x;$empty.y];
- }
- # Now we're in position to move the data from the upper right to the upper left corner.
- # - Move data into empty cell
- # - Move empty cell around data cell
- # - Repeat until x == 0
- my $data = @nodes[$grid-width-1;0];
- while $data.x > 0 {
- @moves.push: $data.move-data-to($empty);
- ($data, $empty) = ($empty, $data);
- last if $data.x == 0;
- @moves.push: @nodes[$empty.x;1].move-data-to($empty);
- $empty = @nodes[$empty.x;1];
- @moves.push: @nodes[$empty.x-1;1].move-data-to($empty);
- $empty = @nodes[$empty.x-1;1];
- @moves.push: @nodes[$empty.x-1;1].move-data-to($empty);
- $empty = @nodes[$empty.x-1;1];
- @moves.push: @nodes[$empty.x;0].move-data-to($empty);
- $empty = @nodes[$empty.x;0];
- }
- say '';
- @moves».say if $verbose;
- say "It takes @moves.elems() moves to gain access to the required data.";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement