Advent of Code, day 16

This December I participated in Advent of Code.

The day I didn’t finish within 24 hours.

Ah yes. Shortest path. Dijkstra. My first program for part 1 brute forced, but when I saw part 2, I rewrote part 1. And good thing too! I needed that later in the month.

Even then part 2 needed a long think. I ended up building a model of the map, where every cell occurred 4 times. The 4 parts of each cell represented the direction, I was headed. Going from “left” to “up” was expensive. On the other hand, going from my “up” to the cell above me, to its “up” part, was rather cheap. Anyway. Once I broke down and coded this the right way, the program ran fine. It took some time, but it ran. But this was still just part 1! The program I had now written 3 times.

Part 2 was replacing “<” with “<=”. Don’t just register a shortest path, register them all. And remember to register where this path was going. Which neighbor visited me, resulting in a shortest path. Because later, I needed to retrace all these shortest paths. Recursively.

Part 1:

<?php
function print_map() {
  global $map, $mapwid, $maphei;
  for($y=0;$y<$maphei;$y++) {
    for($x=0;$x<$mapwid;$x++) {
      if(isset($map[$y][$x])) {
        echo $map[$y][$x];
      } else {
        echo ".";
      }
    }
    echo "\n";
  }
  echo "\n";
}
function find($char) {
  global $map, $mapwid, $maphei;
  for($x=0;$x<$mapwid;$x++) {
    for($y=0;$y<$maphei;$y++) {
      if($map[$y][$x] == $char) {
        return array($x,$y);
      }
    }
  }
}
function find_short() {
  global $unvis, $maphei, $mapwid;
  $dist = 10000000;
  $newx = -1;
  $newy = -1;
  $newdir = "";
  for($x=0;$x<$mapwid;$x++) {
    if(!isset($unvis[$x])) {
      continue;
    }
    for($y=0;$y<$maphei;$y++) {
      if(!isset($unvis[$x][$y])) {
        continue;
      }
      foreach($unvis[$x][$y] as $dir => $val) {
        if($unvis[$x][$y][$dir][1] < $dist) {
          $newx = $x;
          $newy = $y;
          $newdir = $dir;
          $dist = $unvis[$x][$y][$dir][1];
        }
      }
    }
  }
  return array($newx, $newy, $newdir);
}
function isempty($map, $x, $y) {
  if($map[$y][$x] == ".") {
    return 1;
  } else {
    return 0;
  }
}
////////////////////////////////////////////////////////////////
$input = file_get_contents('./d16input2.txt', true);
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  //print("$line\n");
  if(strlen($line)>2) {
    $map[] = str_split($line);
  }
}
$maphei = sizeof($map);
$mapwid = sizeof($map[0]);
// Assign to every node a distance from start value.
// explode map - every cell turns into 4 cells
// $map[$y][$x]
// $dimap[$x][$y][dir][type,dist,neigh]
for($x=0;$x<$mapwid;$x++) {
  for($y=0;$y<$maphei;$y++) {
    $dimap[$x][$y]["^"] = array($map[$y][$x], 10000000, array());
    $dimap[$x][$y][">"] = array($map[$y][$x], 10000000, array());
    $dimap[$x][$y]["v"] = array($map[$y][$x], 10000000, array());
    $dimap[$x][$y]["<"] = array($map[$y][$x], 10000000, array());
  }
}
// neigh: (x, y, type, dist)
// vertical neigh
for($x=0;$x<$mapwid;$x++) {
  for($y=1;$y<$maphei-1;$y++) {
    // neigh above?
    if($dimap[$x][$y-1]["^"][0] != "#") {
      $dimap[$x][$y]["^"][2][] = array($x, $y-1, "^", 1);
    }
    // neigh below?
    if($dimap[$x][$y+1]["v"][0] != "#") {
      $dimap[$x][$y]["v"][2][] = array($x, $y+1, "v", 1);
    }
  }
}
// horizontal neigh
for($x=1;$x<$mapwid-1;$x++) {
  for($y=0;$y<$maphei;$y++) {
    // neigh to the right?
    if($dimap[$x+1][$y][">"][0] != "#") {
      $dimap[$x][$y][">"][2][] = array($x+1, $y, ">", 1);
    }
    // neigh to the left?
    if($dimap[$x-1][$y]["<"][0] != "#") {
      $dimap[$x][$y]["<"][2][] = array($x-1, $y, "<", 1);
    }
  }
}
// internal neigh
for($x=0;$x<$mapwid;$x++) {
  for($y=0;$y<$maphei;$y++) {
    $dimap[$x][$y]["^"][2][] = array($x, $y, ">", 1000);
    $dimap[$x][$y]["^"][2][] = array($x, $y, "<", 1000);
    $dimap[$x][$y][">"][2][] = array($x, $y, "^", 1000);
    $dimap[$x][$y][">"][2][] = array($x, $y, "v", 1000);
    $dimap[$x][$y]["v"][2][] = array($x, $y, "<", 1000);
    $dimap[$x][$y]["v"][2][] = array($x, $y, ">", 1000);
    $dimap[$x][$y]["<"][2][] = array($x, $y, "^", 1000);
    $dimap[$x][$y]["<"][2][] = array($x, $y, "v", 1000);
  }
}
// Create a set of all unvisited nodes: the unvisited set.
$unvis = $dimap;
// Assign to every node a distance from start value: for the starting node, it is zero.
$coords = find("S");
$x = $coords[0];
$y = $coords[1];
$unvis[$x][$y]["^"][1] = 1000;
$unvis[$x][$y][">"][1] = 0;
$unvis[$x][$y]["v"][1] = 1000;
$unvis[$x][$y]["<"][1] = 10000000;
$dist = 0;
// begin loop
//for($i=0;$i<2;$i++) {
while($dist<83500) {
  $coords = find_short();
  $x = $coords[0];
  $y = $coords[1];
  if($x == -1 || $y == -1) {
    break;
  }
  $dir = $coords[2];
  $dist = $unvis[$x][$y][$dir][1];
if($dist % 100 == 0) {
  echo "$dist ";
}
  if(sizeof($unvis[$x][$y][$dir][2]) > 0) {  
    // For the current node, consider all of its unvisited neighbors
    // and update their distances through the current node
    foreach($unvis[$x][$y][$dir][2] as $num => $neigh) {
      $neighx = $neigh[0];
      $neighy = $neigh[1];
      $neighdir = $neigh[2];
      $dist2neigh = $neigh[3];
      if(isset($unvis[$neighx][$neighy][$neighdir])) {
        $neighcurdist = $unvis[$neighx][$neighy][$neighdir][1];
        if($dist + $dist2neigh < $neighcurdist) {
          $unvis[$neighx][$neighy][$neighdir][1] = $dist + $dist2neigh;
        }
      } else {
        unset($unvis[$x][$y][$dir][2][$num]);
      }
    }
  }
  
  // After considering all of the current node's unvisited neighbors,
  // the current node is removed from the unvisited set.
  $dimap[$x][$y][$dir][1] = $dist;
  if($dimap[$x][$y][$dir][0] == "E") {
    break;
  }
  unset($unvis[$x][$y][$dir]);
// end loop
}
$coords = find("E");
$x = $coords[0];
$y = $coords[1];
print_r($dimap[$x][$y]);
?>

Part 2:

<?php
function print_map() {
  global $map, $mapwid, $maphei;
  for($y=0;$y<$maphei;$y++) {
    for($x=0;$x<$mapwid;$x++) {
      if(isset($map[$y][$x])) {
        echo $map[$y][$x];
      } else {
        echo ".";
      }
    }
    echo "\n";
  }
  echo "\n";
}
function find($char) {
  global $map, $mapwid, $maphei;
  for($x=0;$x<$mapwid;$x++) {
    for($y=0;$y<$maphei;$y++) {
      if($map[$y][$x] == $char) {
        return array($x,$y);
      }
    }
  }
}
function find_short() {
  global $unvis, $maphei, $mapwid;
  $dist = 10000000;
  $newx = -1;
  $newy = -1;
  $newdir = "";
  for($x=0;$x<$mapwid;$x++) {
    if(!isset($unvis[$x])) {
      continue;
    }
    for($y=0;$y<$maphei;$y++) {
      if(!isset($unvis[$x][$y])) {
        continue;
      }
      foreach($unvis[$x][$y] as $dir => $val) {
        if($unvis[$x][$y][$dir][1] < $dist) {
          $newx = $x;
          $newy = $y;
          $newdir = $dir;
          $dist = $unvis[$x][$y][$dir][1];
        }
      }
    }
  }
  return array($newx, $newy, $newdir);
}
////////////////////////////////////////////////////////////////
$input = file_get_contents('./d16input2.txt', true);
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  //print("$line\n");
  if(strlen($line)>2) {
    $map[] = str_split($line);
  }
}
$maphei = sizeof($map);
$mapwid = sizeof($map[0]);
// Assign to every node a distance from start value.
// explode map - every cell turns into 4 cells
// $map[$y][$x]
// $dimap[$x][$y][dir][type,dist,prev,neigh]
for($x=0;$x<$mapwid;$x++) {
  for($y=0;$y<$maphei;$y++) {
    $dimap[$x][$y]["^"] = array($map[$y][$x], 10000000, array(), array());
    $dimap[$x][$y][">"] = array($map[$y][$x], 10000000, array(), array());
    $dimap[$x][$y]["v"] = array($map[$y][$x], 10000000, array(), array());
    $dimap[$x][$y]["<"] = array($map[$y][$x], 10000000, array(), array());
  }
}
// neigh: (x, y, type, dist)
// vertical neigh
for($x=0;$x<$mapwid;$x++) {
  for($y=1;$y<$maphei-1;$y++) {
    // neigh above?
    if($dimap[$x][$y-1]["^"][0] != "#") {
      $dimap[$x][$y]["^"][3][] = array($x, $y-1, "^", 1);
    }
    // neigh below?
    if($dimap[$x][$y+1]["v"][0] != "#") {
      $dimap[$x][$y]["v"][3][] = array($x, $y+1, "v", 1);
    }
  }
}
// horizontal neigh
for($x=1;$x<$mapwid-1;$x++) {
  for($y=0;$y<$maphei;$y++) {
    // neigh to the right?
    if($dimap[$x+1][$y][">"][0] != "#") {
      $dimap[$x][$y][">"][3][] = array($x+1, $y, ">", 1);
    }
    // neigh to the left?
    if($dimap[$x-1][$y]["<"][0] != "#") {
      $dimap[$x][$y]["<"][3][] = array($x-1, $y, "<", 1);
    }
  }
}
// internal neigh
for($x=0;$x<$mapwid;$x++) {
  for($y=0;$y<$maphei;$y++) {
    $dimap[$x][$y]["^"][3][] = array($x, $y, ">", 1000);
    $dimap[$x][$y]["^"][3][] = array($x, $y, "<", 1000);
    $dimap[$x][$y][">"][3][] = array($x, $y, "^", 1000);
    $dimap[$x][$y][">"][3][] = array($x, $y, "v", 1000);
    $dimap[$x][$y]["v"][3][] = array($x, $y, "<", 1000);
    $dimap[$x][$y]["v"][3][] = array($x, $y, ">", 1000);
    $dimap[$x][$y]["<"][3][] = array($x, $y, "^", 1000);
    $dimap[$x][$y]["<"][3][] = array($x, $y, "v", 1000);
  }
}
// Create a set of all unvisited nodes: the unvisited set.
$unvis = $dimap;
// Assign to every node a distance from start value: for the starting node, it is zero.
$coords = find("S");
$x = $coords[0];
$y = $coords[1];
$unvis[$x][$y]["^"][1] = 1000;
$unvis[$x][$y][">"][1] = 0;
$unvis[$x][$y]["v"][1] = 1000;
$unvis[$x][$y]["<"][1] = 10000000;
$dist = 0;
// begin loop
while(0 < 1) {
  $coords = find_short();
  $x = $coords[0];
  $y = $coords[1];
  if($x == -1 || $y == -1) {
    break;
  }
  $dir = $coords[2];
  $dist = $unvis[$x][$y][$dir][1];
if($dist % 100 == 0) {
  echo "$dist ";
}
  if(sizeof($unvis[$x][$y][$dir][3]) > 0) {  
    // For the current node, consider all of its unvisited neighbors
    // and update their distances through the current node
    foreach($unvis[$x][$y][$dir][3] as $num => $neigh) {
      $neighx = $neigh[0];
      $neighy = $neigh[1];
      $neighdir = $neigh[2];
      $dist2neigh = $neigh[3];
      if(isset($unvis[$neighx][$neighy][$neighdir])) {
        $neighcurdist = $unvis[$neighx][$neighy][$neighdir][1];
        if($dist + $dist2neigh <= $neighcurdist) {
          $unvis[$neighx][$neighy][$neighdir][1] = $dist + $dist2neigh;
          // remember previous
          $unvis[$neighx][$neighy][$neighdir][2][] = array($x, $y, $dir);
        }
      } else {
        unset($unvis[$x][$y][$dir][3][$num]);
      }
    }
  }
  
  // After considering all of the current node's unvisited neighbors,
  // the current node is removed from the unvisited set.
  $dimap[$x][$y][$dir][1] = $dist;
  $dimap[$x][$y][$dir][2] = $unvis[$x][$y][$dir][2];
  if($dimap[$x][$y][$dir][0] == "E") {
    break;
  }
  unset($unvis[$x][$y][$dir]);
// end loop
}
// output distance
$coords = find("E");
$x = $coords[0];
$y = $coords[1];
$min = 10000000;
foreach($dimap[$x][$y] as $key => $distarr) {
  if($distarr[1] < $min) {
    $min = $distarr[1];
    $dir = $key;
  }
}
echo "$min\n";
// traverse all the shortest paths
$points = array( array($x, $y, $dir));
// begin loop
while(sizeof($points) > 0) {
  // i just need the first
  foreach($points as $key => $point) {
    $x = $point[0];
    $y = $point[1];
    $dir = $point[2];
    $prev = $dimap[$x][$y][$dir][2];
    break;
  }
  unset($points[$key]);
  $map[$y][$x] = "O";
  foreach($prev as $pre) {
    $points[] = array($pre[0], $pre[1], $pre[2]);
  }
// end loop
}
print_map();
$cells = 0;
for($i=0;$i<sizeof($map);$i++) {
  for($j=0;$j<sizeof($map[$i]);$j++) {
    if($map[$i][$j] == "O") {
      $cells++;
    }
  }
}
print("$cells\n");
?>

Scroggs, day 16

A new December and a new bunch of puzzles from mscroggs.co.uk.

Let’s name these numbers:

abc
def
ghi

a * b + c = 46: 5*9+1, 9*5+1, 6*7+4, 7*6+4, 5*8+6, 8*5+6

a, b: 5, 6, 7, 8, 9

c: 1, 4, 6

b + e * h = 12: 3+9*1, 4+8*1, 5+7*1, 7+5*1, 8+4*1, 9+3*1, 1+5*2, 5+1*2, 1+2*4, 2+1*4

New b: 5, 7, 8, 9

e: 1, 3, 4, 5, 7

h: 1, 2

a / d / g = 1: 6/2/3, 6/3/2, 8/2/4, 8/4/2

New a: 6, 8

d, g: 2, 3, 4

Because new a and b: a * b + c = 46: 5*9+1, 9*5+1, 6*7+4, 7*6+4, 5*8+6, 8*5+6

New a: 6, 8

New b: 5, 7

Because new b: b + e * h = 12: 3+9*1, 4+8*1, 5+7*1, 7+5*1, 8+4*1, 9+3*1, 1+5*2, 5+1*2, 1+2*4, 2+1*4

e: 1, 5, 7

h: 1, 2

g – h / i = 1, g = 2, 3, 4, h = 1, 2: 3-1/2, 3-2/1, 4-1/3

New g: 2, 3, 4

i: 1, 2, 3

d, g, h and i can only be among 1, 2, 3, 4, therefore a, b, c, e and f can’t

c = 6

a = 8, b = 5

Because new a: a / d / g = 1: 6/2/3, 6/3/2, 8/2/4, 8/4/2

New d, g: 2, 4

e: 7

h = 1

i = 3

g = 4

d = 2

f = 9

856279413

The product of the red digits is 8*6*7 = 336.

Advent of Code, day 15

This December I participated in Advent of Code.

I don’t remember part 1 being hard. Tedious, but not hard. All that “if this goes left, then…” I might have to code a general “if I am facing left, and looking to my right…” routine some day.

Part 2 on the other hand… Figuring out the correct recursion when going up or down. If I am trying to move up, but there’s a box partially above me, I have to move that first. Only after it’s moved, can I move. This took endless debugging, and trying to get the test case to work correctly.

I am on my way to always having the functions at the top of the code.

Part 1:

<?php

// map in
function print_map() {
  global $map, $mapwid, $maphei;
  for($j=0;$j<$maphei;$j++) {
    for($i=0;$i<$mapwid;$i++) {
      if(isset($map[$j][$j])) {
        echo $map[$j][$i];
      } else {
        echo ".";
      }
    }
    echo "\n";
  }
  echo "\n";
}

$input = file_get_contents('./d15input2.txt', true);

$map = array();
$phase = 1;
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  if(strlen($line) < 3) {
    $phase = 2;
  } else {
    if($phase == 1) {
      $map[] = str_split($line);
    } else {
      $moves[] = str_split($line);
    }
  }
}
$maphei = sizeof($map);
$mapwid = sizeof($map[0]);
$moves = array_merge(...$moves);

// find robot
// find guard
for($i=0;$i<$maphei;$i++) {
  $j = array_search("@", $map[$i]);
  if($j) {
    $robx = $j;
    $roby = $i;
  }
}
print_map();

function movebox($x, $y, $dir) {
  global $map;
  switch ($dir) {
    case "<":
      if($map[$y][$x-1] == "#") {
        return 0;
      }
      if($map[$y][$x-1] == "O") {
        $tmp = movebox($x-1, $y, $dir);
        if($tmp == 1) {
          $map[$y][$x-2] = "O";
          $map[$y][$x-1] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
    case "v":
      if($map[$y+1][$x] == "#") {
        return 0;
      }
      if($map[$y+1][$x] == "O") {
        $tmp = movebox($x, $y+1, $dir);
        if($tmp == 1) {
          $map[$y+2][$x] = "O";
          $map[$y+1][$x] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
    case ">":
      if($map[$y][$x+1] == "#") {
        return 0;
      }
      if($map[$y][$x+1] == "O") {
        $tmp = movebox($x+1, $y, $dir);
        if($tmp == 1) {
          $map[$y][$x+2] = "O";
          $map[$y][$x+1] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
    case "^":
      if($map[$y-1][$x] == "#") {
        return 0;
      }
      if($map[$y-1][$x] == "O") {
        $tmp = movebox($x, $y-1, $dir);
        if($tmp == 1) {
          $map[$y-2][$x] = "O";
          $map[$y-1][$x] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
  }
}

// walk the robot
for($i=0;$i<sizeof($moves);$i++) {
  if(movebox($robx, $roby, $moves[$i])) {
    if($moves[$i] == "^") {
      if($map[$roby-1][$robx] != "#") {
        $map[$roby-1][$robx] = "@";
        $map[$roby][$robx] = ".";
        $roby--;
      }
    }
    if($moves[$i] == ">") {
      if($map[$roby][$robx+1] != "#") {
        $map[$roby][$robx+1] = "@";
        $map[$roby][$robx] = ".";
        $robx++;
      }
    }
    if($moves[$i] == "v") {
      if($map[$roby+1][$robx] != "#") {
        $map[$roby+1][$robx] = "@";
        $map[$roby][$robx] = ".";
        $roby++;
      }
    }
    if($moves[$i] == "<") {
      if($map[$roby][$robx-1] != "#") {
        $map[$roby][$robx-1] = "@";
        $map[$roby][$robx] = ".";
        $robx--;
      }
    }
  }
}

print_map();

// calculate gps
$gps = 0;
for($j=0;$j<$maphei;$j++) {
  for($i=0;$i<$mapwid;$i++) {
    if($map[$j][$i] == "O") {
      $gps += 100 * $j + $i;
    }
  }
}
echo "$gps\n";

?>

Part 2:

<?php
// map in
function print_map() {
  global $map, $mapwid, $maphei;
  for($j=0;$j<$maphei;$j++) {
    for($i=0;$i<$mapwid;$i++) {
      if(isset($map[$j][$j])) {
        echo $map[$j][$i];
      } else {
        echo ".";
      }
    }
    echo "\n";
  }
  echo "\n";
}


$input = file_get_contents('./d15input2.txt', true);

$map = array();
$phase = 1;
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  if(strlen($line) < 3) {
    $phase = 2;
  } else {
    if($phase == 1) {
      $map[] = str_split($line);
    } else {
      $moves[] = str_split($line);
    }
  }
}

// make map twice the width
foreach($map as $row) {
  foreach($row as $cell) {
    switch($cell) {
      case "#":
        $r2[] = $cell;
        $r2[] = $cell;
        break;
      case "@":
        $r2[] = $cell;
        $r2[] = ".";
        break;
      case "O":
        $r2[] = "[";
        $r2[] = "]";
        break;
      case ".":
        $r2[] = $cell;
        $r2[] = $cell;
        break;
    }
  }
  $m2[] = $r2;
  $r2 = array();
}
$map = $m2;

$maphei = sizeof($map);
$mapwid = sizeof($map[0]);
$moves = array_merge(...$moves);

// find robot
for($i=0;$i<$maphei;$i++) {
  $j = array_search("@", $map[$i]);
  if($j) {
    //print("$i $j");
    $robx = $j;
    $roby = $i;
  }
}
print_map();

function moveboxupdown($x, $y, $dir, $mywid) {

  global $map;
  // i am on (x,y) and i am mywid wide, going in dir direction
  // if there is a box in front of me, move it, if possible
  // note, adjacent boxes may be moved partially and then rolled back
  // if i am a box, (x,y) is my left part
  
  // first, check for wall
  if($dir == "^" && $map[$y-1][$x] == "#") {
    // wall, abort
    return 0;
  }
  if($dir == "^" && $mywid == 2 && $map[$y-1][$x+1] == "#") {
    // wall, abort
    return 0;
  }
  if($dir == "v" && $map[$y+1][$x] == "#") {
    // wall, abort
    return 0;
  }
  if($dir == "v" && $mywid == 2 && $map[$y+1][$x+1] == "#") {
    // wall, abort
    return 0;
  }

  // next check for further boxes, and move them!
  if($dir == "^" && $map[$y-1][$x-1] == "[") {
    $tmp = moveboxupdown($x-1, $y-1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  if($dir == "^" && $map[$y-1][$x] == "[") {
    $tmp = moveboxupdown($x, $y-1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  if($dir == "^" && $mywid == 2 && $map[$y-1][$x+1] == "[") {
    $tmp = moveboxupdown($x+1, $y-1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  if($dir == "v" && $map[$y+1][$x-1] == "[") {
    $tmp = moveboxupdown($x-1, $y+1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  if($dir == "v" && $map[$y+1][$x] == "[") {
    $tmp = moveboxupdown($x, $y+1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  if($dir == "v" && $mywid == 2 && $map[$y+1][$x+1] == "[") {
    $tmp = moveboxupdown($x+1, $y+1, $dir, 2);
    if($tmp == 0) {
      return 0;
    }
  }
  
  // if i reached this point, i can move a box
  if($dir == "^" && $mywid == 2) {
    $map[$y-1][$x] = "[";
    $map[$y-1][$x+1] = "]";
    $map[$y][$x] = ".";
    $map[$y][$x+1] = ".";
  }
  if($dir == "v" && $mywid == 2) {
    $map[$y+1][$x] = "[";
    $map[$y+1][$x+1] = "]";
    $map[$y][$x] = ".";
    $map[$y][$x+1] = ".";
  }

  return 1;
}

function moveboxleftright($x, $y, $dir) {
  global $map;
  switch ($dir) {
    case "<":
      if($map[$y][$x-1] == "#") {
        return 0;
      }
      if($map[$y][$x-1] == "]") {
        $tmp = moveboxleftright($x-2, $y, $dir);
        if($tmp == 1) {
          $map[$y][$x-3] = "[";
          $map[$y][$x-2] = "]";
          $map[$y][$x-1] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
    case ">":
      if($map[$y][$x+1] == "#") {
        return 0;
      }
      if($map[$y][$x+1] == "[") {
        $tmp = movebox($x+2, $y, $dir);
        if($tmp == 1) {
          $map[$y][$x+3] = "]";
          $map[$y][$x+2] = "[";
          $map[$y][$x+1] = ".";
        }
        return $tmp;
      } else {
        return 1;
      }
      break;
  }
}

function movebox($x, $y, $dir) {
  global $map;
  $backup = $map;
  if($dir == "^" || $dir == "v") {
    $tmp = moveboxupdown($x, $y, $dir, 1);
  } else {
    $tmp = moveboxleftright($x, $y, $dir);
  }
  if($tmp == 0) {
    $map = $backup;
  }
  return $tmp;
}

// walk the robot
for($i=0;$i<sizeof($moves);$i++) {
  if(movebox($robx, $roby, $moves[$i])) {
    if($moves[$i] == "^") {
      if($map[$roby-1][$robx] != "#") {
        $map[$roby-1][$robx] = "@";
        $map[$roby][$robx] = ".";
        $roby--;
      }
    }
    if($moves[$i] == ">") {
      if($map[$roby][$robx+1] != "#") {
        $map[$roby][$robx+1] = "@";
        $map[$roby][$robx] = ".";
        $robx++;
      }
    }
    if($moves[$i] == "v") {
      if($map[$roby+1][$robx] != "#") {
        $map[$roby+1][$robx] = "@";
        $map[$roby][$robx] = ".";
        $roby++;
      }
    }
    if($moves[$i] == "<") {
      if($map[$roby][$robx-1] != "#") {
        $map[$roby][$robx-1] = "@";
        $map[$roby][$robx] = ".";
        $robx--;
      }
    }
  }
}

print_map();

// calculate gps
$gps = 0;
for($j=0;$j<$maphei;$j++) {
  for($i=0;$i<$mapwid;$i++) {
    if($map[$j][$i] == "[") {
      $gps += 100 * $j + $i;
    }
  }
}
echo "$gps\n";
?>

Scroggs, day 15

A new December and a new bunch of puzzles from mscroggs.co.uk.

We’re looking for a 2 digit number, ab (and the product ab * ba). It can’t be a 1 digit number, a, because a * a wouldn’t have 3 digits. It can’t be a 3 digit number, abc, because abc * cba would have at least 5 digits.

Also, ab is a square. It could be 16, 25, 36, 49, 64, 81. Going through these manually shows 16 * 61 = 976 is the only 3 digit number.

#ThisWeeksFiddler, 20250110

This week the question is: Can You Survive Squid Game (Season 2)?

In Season 2 of Squid Game, contestants play a game of “Mingle” (spoiler alert!). For each round of this game, the contestants all wait in a common area until a number is called. They must then split up into groups of that number. Any contestants not in a group of the correct number after 30 seconds … well, let’s just say bad things happen to them. For now, we’ll refer to contestants who make it through a round in a group of the correct size as having “survived” the round.

Suppose there are initially N contestants.

In the first round, contestants must form groups of 1. Okay, that’s not too hard; everyone survives. In the second round, contestants must form groups of 2. This continues (with groups of k in round k) until the 38th round, when contestants must form groups of 38.

What is the smallest number N such that there is at least one person who survives all 38 rounds?

And for extra credit:

There are N contestants about to play Mingle, where N is less than 500. This time, in each round, the number for group size can be anywhere from 1 to 10, with each number equally likely to be called. Also, the numbers called in the rounds are all independent of each other. It appears this version of Mingle will continue until there are no more survivors.

Before the game starts, one of the contestants, Young-il, does some quick computation. He says to his companion, Gi-hun: “Oh no, it’s too bad we only have N people.” (Of course, Young-il doesn’t actually say “N,” but rather the number that N represents.)

Young-il continues: “If we had started with N+1 people instead, I’d expect the game to last about 10 more rounds, on average.”

What is the value of N?

Highlight to reveal (possibly incorrect) solution:

Program. A little messy.

Part 2:

Program. Spreadsheet/PDF.

I have a suspicion, this is because 360 is such a nice number. Coming from 361, 362, 363 etc., there’s a good chance of landing here. And then only moving on to a lower number, if the random number is 7.

Advent of Code, day 14

This December I participated in Advent of Code.

Part 1 is rather easy. Let the robots walk.

Part 2 proved much more challenging. How does one detect a picture? I developed a strategy of trying to sense, whether the robots were “clumping”, and then printing the map, to get a visual check. This worked, and I found the picture. Then a little while later I discovered, that the apparently random output from part 1 could be used to detect the picture, but I didn’t implement that fully. Still, I really liked that elegant solution. This version of the program was actually built partially after I know the solution.

I’m getting a bit more systematic. Having all the values, that change between test and actual data at the top.

Part 1:

<?php
$input = file_get_contents('./d14input2.txt', true);
$mapwid = 101;
$maphei = 103;
// test: 11 x 7
// actual: 101 x 103
$robots = array();
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
	if(strlen($line)>2) {
		preg_match_all('/-?\d+/', $line, $robot);
		$robots[] = $robot[0];
	}
}
function print_map() {
	global $robots, $mapwid, $maphei;
	$map = array();
	foreach($robots as $robot) {
		$x = $robot[0];
		$y = $robot[1];
		if(isset($map[$x][$y])) {
			$map[$x][$y]++;
		} else {
			$map[$x][$y] = 1;
		}
	}
	for($j=0;$j<$maphei;$j++) {
		for($i=0;$i<$mapwid;$i++) {
			if(isset($map[$i][$j])) {
				echo $map[$i][$j];
			} else {
				echo ".";
			}
		}
		echo "\n";
	}
	echo "\n";
}
for($i=0;$i<100;$i++) {
// print("$i seconds elapsed\n");
  for($j=0;$j<sizeof($robots);$j++) {
    $robots[$j][0] =
      ($robots[$j][0] + $robots[$j][2] + $mapwid) % $mapwid;
    $robots[$j][1] =
      ($robots[$j][1] + $robots[$j][3] + $maphei) % $maphei;
  }
}
$robx0y0 = 0;
$robx0y1 = 0;
$robx1y0 = 0;
$robx1y1 = 0;
for($j=0;$j<sizeof($robots);$j++) {
	if($robots[$j][0] < ($mapwid-1)/2) {
		if($robots[$j][1] < ($maphei-1)/2) {
			$robx0y0++;
		} else {
			if($robots[$j][1] > ($maphei-1)/2) {
				$robx0y1++;
			}
		}
	} else {
		if($robots[$j][0] > ($mapwid-1)/2) {
			if($robots[$j][1] < ($maphei-1)/2) {
				$robx1y0++;
			} else {
				if($robots[$j][1] > ($maphei-1)/2) {
					$robx1y1++;
				}
			}
		}
	}
}
print("$robx0y0 $robx0y1 $robx1y0 $robx1y1\n");
$prod = $robx0y0 * $robx0y1 * $robx1y0 * $robx1y1;
print("$prod\n");
?>

Part 2:

<?php
$input = file_get_contents('./d14input2.txt', true);
$mapwid = 101;
$maphei = 103;
// test: 11 x 7
// actual: 101 x 103
$robots = array();
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  if(strlen($line)>2) {
    preg_match_all('/-?\d+/', $line, $robot);
    $robots[] = $robot[0];
  }
}
//print_r($robots);
function print_map() {
  global $robots, $mapwid, $maphei;
  $map = array();
  foreach($robots as $robot) {
    $x = $robot[0];
    $y = $robot[1];
    if(isset($map[$x][$y])) {
      $map[$x][$y]++;
    } else {
      $map[$x][$y] = 1;
    }
  }
  for($j=0;$j<$maphei;$j++) {
    for($i=0;$i<$mapwid;$i++) {
      if(isset($map[$i][$j])) {
        echo $map[$i][$j];
      } else {
        echo ".";
      }
    }
    echo "\n";
  }
  echo "\n";
}
function xmas_tst() {
  global $robots, $mapwid, $maphei;
  $robx0y0 = 0;
  $robx0y1 = 0;
  $robx1y0 = 0;
  $robx1y1 = 0;
  for($j=0;$j<sizeof($robots);$j++) {
    if($robots[$j][0] < ($mapwid-1)/2) {
      if($robots[$j][1] < ($maphei-1)/2) {
        $robx0y0++;
      } else {
        if($robots[$j][1] > ($maphei-1)/2) {
          $robx0y1++;
        }
      }
    } else {
      if($robots[$j][0] > ($mapwid-1)/2) {
        if($robots[$j][1] < ($maphei-1)/2) {
          $robx1y0++;
        } else {
          if($robots[$j][1] > ($maphei-1)/2) {
            $robx1y1++;
          }
        }
      }
    }
  }
  if(max($robx0y0,$robx0y1,$robx1y0,$robx1y1) > 150) {
    return 1;
  } else {
    return 0;
  }
}
$handle = fopen ("php://stdin","r");
for($i=0;$i<8258;$i++) {
  for($j=0;$j<sizeof($robots);$j++) {
    $robots[$j][0] =
      ($robots[$j][0] + $robots[$j][2] + $mapwid) % $mapwid;
    $robots[$j][1] =
      ($robots[$j][1] + $robots[$j][3] + $maphei) % $maphei;
  }
  if(xmas_tst()) {
    print_map();
    print($i+1 . " seconds elapsed\n");
    $line = fgets($handle);
  }
}
fclose($handle);
?>

Advent of Code, day 13

This December I participated in Advent of Code.

Ah yes. It took me a little while to wrap my head around “2 equations with 2 variables”. Doh. And then it took me a little while to actually implement a standard solution. (Question.) Double doh.

For part 2 I had to change 2 lines, so below is only part 2.

Part 2:

<?php

$input = file_get_contents('./d13input2.txt', true);

$rules = array();
$i = 0;
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
	if(preg_match("/Button A/",$line,$matches)) {
		if(preg_match_all("/(\d+)/",$line,$matches)) {
			$rules[$i]["ax"] = $matches[0][0];
			$rules[$i]["ay"] = $matches[0][1];
		}
	}
	if(preg_match("/Button B/",$line,$matches)) {
		if(preg_match_all("/(\d+)/",$line,$matches)) {
			$rules[$i]["bx"] = $matches[0][0];
			$rules[$i]["by"] = $matches[0][1];
		}
	}
	if(preg_match("/Prize/",$line,$matches)) {
	  if(preg_match_all("/(\d+)/",$line,$matches)) {
	    // part 1 doesn't have the +10000000000000
	    $rules[$i]["px"] = $matches[0][0] + 10000000000000;
	    $rules[$i]["py"] = $matches[0][1] + 10000000000000;
	  }
	}
	if(strlen($line) < 2) {
		$i++;
	}
}

$money = 0;
$hits = 0;
foreach($rules as $rule) {
	// Coefficients of the equations
	$a1 = $rule["ax"]; $b1 = $rule["bx"]; $c1 = $rule["px"];
	$a2 = $rule["ay"]; $b2 = $rule["by"]; $c2 = $rule["py"];

	// Calculate the determinant
	$determinant = $a1 * $b2 - $a2 * $b1;

	if ($determinant == 0) {
	    // The system has no unique solution
	    // (either no solution or infinitely many solutions)
	} else {
	    // Calculate x and y using Cramer's Rule
	    $x = ($c1 * $b2 - $c2 * $b1) / $determinant;
	    $y = ($a1 * $c2 - $a2 * $c1) / $determinant;
	}
	
	$soln = $y;
	$solm = $x;
	if(abs((int)$soln - $soln) < 0.01
	&& abs((int)$solm - $solm) < 0.01) {
		if($solm > 0 && $soln > 0) {
			$money += $solm * 3 + $soln;
			$hits++;
		}
	}
}
print("$hits $money\n");

?>

Scroggs, day 13

A new December and a new bunch of puzzles from mscroggs.co.uk.

Let the square be:

abc
def
ghi

Rules:

  • (1) def = 2 ghi
  • (2) a+d+g = 15
  • (3) b+e+h = 19
  • (4) cfi = 3 ghi

From these we can deduce these further rules:

  • (1a) f is even
  • (1b) d >= 2
  • (4a) g <= 3
  • (4b) c >= 3
  • (4c) i is 0 or 5

Assume i = 0. Then by (1) f = 0. Then by (4) h = 0. Then by (1) e = 0. But by (3), then b = 19, not a digit. So, i != 0.

i = 5. Then by (1) f = 0. Then by (4), c05 = 3 gh5, this might be 405 = 3 * 135 or 705 = 3 * 235. In both cases h = 3. g might be 1 or 2. c might be 4 or 7. Then by (1) e = 7. Then by (3) b = 9.

So far we have:

a9c
d70
g35

Assume g = 1. Then by (1) d = 2. Then by (2) a = 12, not a digit. So, g != 1.

g = 2. Then by (1) d = 4. Then by (2) a = 9. Then by (4) c = 7.

The sought after number, abc, is 997.

Advent of Code, day 12

This December I participated in Advent of Code.

This one had me thinking.

  • In part 1, I need to create program copies of areas defined by their common letter. For this I create a list of the letters represented, make a copy of the map for each letter, and throw away all but one letter from each map.
  • But then I have to test, whether each area is actually connected or there are 2 (or more) areas with the same letter, so my program copy has to be split up. For this I use a little recursion, because a split area may need to be split some more. Also, I think I finally became friends with global variables! And side effects, oh yeah… But, I have a function starting in a random place, marking all the places it can reach in the map, and then it’s easy to ask, was the whole map converted?
  • Next, I have to figure out the size (easy) and periphery (hard) of each map. For the periphery I visit all the parts of the map. If this part of the map is in the area, and a neighboring part isn’t, we’ve found a part of the periphery.
  • For part 2, I additionally test, whether someone before me already registered this part of the periphery.

Part 1:

<?php
$input = file_get_contents('./d12input1.txt', true);
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  if(strlen($line)>2) {
    $map[] = str_split($line);
  }
}
$mapx = sizeof($map);
$mapy = sizeof($map[0]);
// name possible areas
$names = array();
for($i=0;$i<sizeof($map);$i++) {
  for($j=0;$j<sizeof($map[$i]);$j++) {
    $names[$map[$i][$j]] = 1;
  }
}
// convert names
$names2 = array();
foreach($names as $key => $val) {
  $names2[] = $key;
}
$names = $names2;
// thin areas out
$areas = array();
for($k=0;$k<sizeof($names);$k++) {
  $lett = $names[$k];
  $tmp = $map;
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if($tmp[$i][$j] != $lett) {
        unset($tmp[$i][$j]);
      }
    }
  }
  $areas[$k] = $tmp;
}
function subtst($i, $j) {
  global $tmpb;
  //print_r($tmpb);
  if(isset($tmpb[$i-1][$j])) {
    if($tmpb[$i-1][$j] != "*") {
      $tmpb[$i-1][$j] = "*";
      subtst($i-1, $j);
    }
  }
  if(isset($tmpb[$i+1][$j])) {
    if($tmpb[$i+1][$j] != "*") {
      $tmpb[$i+1][$j] = "*";
      subtst($i+1, $j);
    }
  }
  if(isset($tmpb[$i][$j-1])) {
    if($tmpb[$i][$j-1] != "*") {
      $tmpb[$i][$j-1] = "*";
      subtst($i, $j-1);
    }
  }
  if(isset($tmpb[$i][$j+1])) {
    if($tmpb[$i][$j+1] != "*") {
      $tmpb[$i][$j+1] = "*";
      subtst($i, $j+1);
    }
  }
}
function atst() {
  global $tmp, $map, $tmpb, $key, $mapx, $mapy, $areas;
  // grab random beginning point
  $lett = "";
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if(isset($tmp[$i][$j])) {
        $lett = $tmp[$i][$j];
        $x = $i;
        $y = $j;
        break 2;
      }
    }
  }
  // can I reach all points from here?
  $tmpb = $tmp;
  $tmpb[$x][$y] = "*";
  subtst($x, $y);
  if(sizeof(array_count_values(array_merge(...$tmpb))) > 1) {
    $new1 = $tmpb;
    for($i=0;$i<$mapx;$i++) {
      for($j=0;$j<$mapy;$j++) {
        if(isset($new1[$i][$j])) {
          if($new1[$i][$j] == $lett) {
            unset($new1[$i][$j]);
          } else {
            $new1[$i][$j] = $lett;
          }
        }
      }
    }
    $new2 = $tmpb;
    for($i=0;$i<$mapx;$i++) {
      for($j=0;$j<$mapy;$j++) {
        if(isset($new2[$i][$j])) {
          if($new2[$i][$j] != $lett) {
            unset($new2[$i][$j]);
          }
        }
      }
    }
    $areas[$key] = $new1;
    $areas[] = $new2;
  }
}
// if areas aren't actually connected, split up
$areaz = 0;
while($areaz < sizeof($areas)) {
  $areaz = sizeof($areas);
  foreach($areas as $key => $area) {
    $tmp = $area;
    atst();
  }  
}
$sum = 0;
foreach($areas as $key => $area) {
  $sz1 = array_count_values(array_merge(...$area));
  $sz2 = array_pop($sz1);
  $per = 0;
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if(isset($area[$i][$j])) {
        if(!isset($area[$i-1][$j])) {
          $per++;
        }
        if(!isset($area[$i+1][$j])) {
          $per++;
        }
        if(!isset($area[$i][$j-1])) {
          $per++;
        }
        if(!isset($area[$i][$j+1])) {
          $per++;
        }
      }
    }
  }
  $sum += $sz2 * $per;
}  
print("$sum\n");
?>

Part 2:

<?php
$input = file_get_contents('./d12input2.txt', true);
foreach(preg_split("/((\r?\n)|(\r\n?))/", $input) as $line) {
  if(strlen($line)>2) {
    $map[] = str_split($line);
  }
}
$mapx = sizeof($map);
$mapy = sizeof($map[0]);
// name possible areas
$names = array();
for($i=0;$i<sizeof($map);$i++) {
  for($j=0;$j<sizeof($map[$i]);$j++) {
    $names[$map[$i][$j]] = 1;
  }
}
// convert names
$names2 = array();
foreach($names as $key => $val) {
  $names2[] = $key;
}
$names = $names2;
// thin areas out
$areas = array();
for($k=0;$k<sizeof($names);$k++) {
  $lett = $names[$k];
  $tmp = $map;
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if($tmp[$i][$j] != $lett) {
        unset($tmp[$i][$j]);
      }
    }
  }
  $areas[$k] = $tmp;
}
//print_r($areas);
function subtst($i, $j) {
  global $tmpb;
  if(isset($tmpb[$i-1][$j])) {
    if($tmpb[$i-1][$j] != "*") {
      $tmpb[$i-1][$j] = "*";
      subtst($i-1, $j);
    }
  }
  if(isset($tmpb[$i+1][$j])) {
    if($tmpb[$i+1][$j] != "*") {
      $tmpb[$i+1][$j] = "*";
      subtst($i+1, $j);
    }
  }
  if(isset($tmpb[$i][$j-1])) {
    if($tmpb[$i][$j-1] != "*") {
      $tmpb[$i][$j-1] = "*";
      subtst($i, $j-1);
    }
  }
  if(isset($tmpb[$i][$j+1])) {
    if($tmpb[$i][$j+1] != "*") {
      $tmpb[$i][$j+1] = "*";
      subtst($i, $j+1);
    }
  }
}
function atst() {
  global $tmp, $map, $tmpb, $key, $mapx, $mapy, $areas;
  // grab random beginning point
  $lett = "";
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if(isset($tmp[$i][$j])) {
        $lett = $tmp[$i][$j];
        $x = $i;
        $y = $j;
        break 2;
      }
    }
  }
  // can I reach all points from here?
  $tmpb = $tmp;
  $tmpb[$x][$y] = "*";
  subtst($x, $y);
  if(sizeof(array_count_values(array_merge(...$tmpb))) > 1) {
    //print("alert\n");
    $new1 = $tmpb;
    for($i=0;$i<$mapx;$i++) {
      for($j=0;$j<$mapy;$j++) {
        if(isset($new1[$i][$j])) {
          if($new1[$i][$j] == $lett) {
            unset($new1[$i][$j]);
          } else {
            $new1[$i][$j] = $lett;
          }
        }
      }
    }
    $new2 = $tmpb;
    for($i=0;$i<$mapx;$i++) {
      for($j=0;$j<$mapy;$j++) {
        if(isset($new2[$i][$j])) {
          if($new2[$i][$j] != $lett) {
            unset($new2[$i][$j]);
          }
        }
      }
    }
    $areas[$key] = $new1;
    $areas[] = $new2;
  }
}
// if areas aren't actually connected, split up
$areaz = 0;
while($areaz < sizeof($areas)) {
  $areaz = sizeof($areas);
  foreach($areas as $key => $area) {
    $tmp = $area;
    atst();
  }  
}
$sum = 0;
foreach($areas as $key => $area) {
  $sz1 = array_count_values(array_merge(...$area));
  $sz2 = array_pop($sz1);
  $per = 0;
  for($i=0;$i<$mapx;$i++) {
    for($j=0;$j<$mapy;$j++) {
      if(isset($area[$i][$j])) {
        // above
        if(!isset($area[$i-1][$j])) {
          // already counted?
          if(isset($area[$i][$j-1]) && !isset($area[$i-1][$j-1])) {
          } else {
            $per++;
          }
        }
        // below
        if(!isset($area[$i+1][$j])) {
          // already counted?
          if(isset($area[$i][$j-1]) && !isset($area[$i+1][$j-1])) {
          } else {
            $per++;
          }
        }
        // to the left
        if(!isset($area[$i][$j-1])) {
          // already counted?
          if(isset($area[$i-1][$j]) && !isset($area[$i-1][$j-1])) {
          } else {
            $per++;
          }
        }
        // to the right
        if(!isset($area[$i][$j+1])) {
          // already counted?
          if(isset($area[$i-1][$j]) && !isset($area[$i-1][$j+1])) {
          } else {
            $per++;
          }
        }
      }
    }
  }
  $sum += $sz2 * $per;
}  
print("$sum\n");
?>