Advent of Code 2021

  • Be sure to checkout “Tips & Tricks”
    Dear Guest Visitor → Once you register and log-in please checkout the “Tips & Tricks” page for some very handy tips!

    /Steve.
  • BootAble – FreeDOS boot testing freeware

    To obtain direct, low-level access to a system's mass storage drives, SpinRite runs under a GRC-customized version of FreeDOS which has been modified to add compatibility with all file systems. In order to run SpinRite it must first be possible to boot FreeDOS.

    GRC's “BootAble” freeware allows anyone to easily create BIOS-bootable media in order to workout and confirm the details of getting a machine to boot FreeDOS through a BIOS. Once the means of doing that has been determined, the media created by SpinRite can be booted and run in the same way.

    The participants here, who have taken the time to share their knowledge and experience, their successes and some frustrations with booting their computers into FreeDOS, have created a valuable knowledgebase which will benefit everyone who follows.

    You may click on the image to the right to obtain your own copy of BootAble. Then use the knowledge and experience documented here to boot your computer(s) into FreeDOS. And please do not hesitate to ask questions – nowhere else can better answers be found.

    (You may permanently close this reminder with the 'X' in the upper right.)

I haven't tried to make code yet so it may be the case with me, but you actually got lucky with part 1 in that no player got to 99 or 100 on their first roll of the dice.

Also, I find it interesting that this day is one in which there's a REALLY small amount of inputs to use for everyone. If I did the math correctly, there's 100 possible inputs for today. 99 if you exclude the sample input.

Edit: While thinking on it some more, it doesn't matter about the 99 and 100, the length of the board makes the dice roll a mod 10 in net movement anyway, so the 99 and 100 rolls really doesn't matter.
 
Last edited:
Day 22 is not possible to brute force in general. You would need a lot of RAM to represent "cube pixels" even as bits. Let's assume the range was from -100K to +100K in each dimension, then you'd need 200k*200K*200K/8 = 1,000,000,000,000,000 bytes which is 1TB of RAM. I suppose you might have that much memory, but then you'd need years of compute to process all the "pixel bits" individually.

Part one you *can* brute force because it's only -50 to +50 in each dimension, which is 100*100*100 = 1M if you use a boolean per "pixel". I did part one by brute forcing it for expediency.

I know how to approach part two, but I am not even slightly interested in actually coding it. (Have I mentioned before that 3D challenges are completely uninteresting to me.) In any case the approach is to take any two intersecting cubiods and break them down into smaller non-intersecting cuboids. In general that means 64 smaller cubiods will result from breaking down an intersection. (You have x1 and x2 for cuboid one and x1 and x2 for cuboid two, meaning you have 4 values for x, 4 for y and 4 for z. 4*4*4 = 64.)

Anyway, for what it's worth, here's my brute force classes CuboidInstruction and Cuboid50. They're practically derivative, so I don't think they offer any spoilers really.

Java:
final class CuboidInstruction
{
  final int x1;
  final int x2;
  final int y1;
  final int y2;
  final int z1;
  final int z2;
  boolean isOn;

  // example: on x=-20..26,y=-36..17,z=-47..7
  private static final Pattern CUBIOD_INSTRUCTION_PATTERN =
      Pattern.compile("(on|off) x=([-]?\\d+)[.][.]([-]?\\d+),y=([-]?\\d+)[.][.]([-]?\\d+),z=([-]?\\d+)[.][.]([-]?\\d+)");
  public CuboidInstruction(final String instruction)
  {
    final Matcher m = CUBIOD_INSTRUCTION_PATTERN.matcher(instruction);
    if (!m.matches()) throw new IllegalStateException("Couldn't parse instruction: " + instruction);
    isOn=m.group(1).equals("on");
    
    final int xt1=Integer.parseInt(m.group(2));
    final int xt2=Integer.parseInt(m.group(3));
    final int yt1=Integer.parseInt(m.group(4));
    final int yt2=Integer.parseInt(m.group(5));
    final int zt1=Integer.parseInt(m.group(6));
    final int zt2=Integer.parseInt(m.group(7));
    
    if (xt1>xt2)
    {
      x1=xt2;
      x2=xt1;
    }
    else
    {
      x1=xt1;
      x2=xt2;
    }

    if (yt1>yt2)
    {
      y1=yt2;
      y2=yt1;
    }
    else
    {
      y1=yt1;
      y2=yt2;
    }

    if (zt1>zt2)
    {
      z1=zt2;
      z2=zt1;
    }
    else
    {
      z1=zt1;
      z2=zt2;
    }
  }
  
  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    if (isOn)
      sb.append("[on  ");
    else
      sb.append("[off ");
    sb.append(String.format("%6d", x1));
    sb.append("..");
    sb.append(String.format("%6d", x2));
    sb.append(", ");
    sb.append(String.format("%6d", y1));
    sb.append("..");
    sb.append(String.format("%6d", y2));
    sb.append(", ");
    sb.append(String.format("%6d", z1));
    sb.append("..");
    sb.append(String.format("%6d", z2));
    sb.append("]");
    return sb.toString();
  }

  public record Range(int v1, int v2) {}

  public Range getXRange()
  {
    return new Range(x1, x2);
  }

  public Range getYRange()
  {
    return new Range(y1, y2);
  }

  public Range getZRange()
  {
    return new Range(z1, z2);
  }
}


Java:
final class Cuboid50
{
  final boolean cuboidRepresentation[][][] = new boolean [101][101][101];

  public void applyInstruction(final CuboidInstruction cuboidInstruction)
  {
    if (cuboidInstruction.x1<-50 || cuboidInstruction.x2>50) return;
    if (cuboidInstruction.y1<-50 || cuboidInstruction.y2>50) return;
    if (cuboidInstruction.z1<-50 || cuboidInstruction.z2>50) return;

    for (int x=cuboidInstruction.x1; x<=cuboidInstruction.x2; x++)
    {
      for (int y=cuboidInstruction.y1; y<=cuboidInstruction.y2; y++)
      {
        for (int z=cuboidInstruction.z1; z<=cuboidInstruction.z2; z++)
        {
          cuboidRepresentation[x+50][y+50][z+50] = cuboidInstruction.isOn;
        }
      }
    }
  }

  public long getNumberOfActive()
  {
    long count = 0;
    for (int x=-50; x<=50; x++)
    {
      for (int y=-50; y<=50; y++)
      {
        for (int z=-50; z<=50; z++)
        {
          if (cuboidRepresentation[x+50][y+50][z+50])  count++;
        }
      }
    }
    return count;
  }
}
 
So I am angry with AoC. Day 23 wasn’t really a programming problem. I mean you could program it, but it was probably just as easy to do by hand… and apparently that is what the folks getting top leaderboard positions did. Accordingly I have no code for day 23, and no desire to even write any. I’m also angry about day 24, but I will make a separate post about it.
 
[This post will be in multiple parts because of the 10K character limit]

I'm angry about Day 24 because it was impossible to solve with code. You cannot brute force a 14 digit number, no matter how good your code is you're not writing any. I did write the parser anyway, because I thought maybe Eric was going to be nice enough to restrict the range so it would be found quickly. He did not and so I wasted an hour on that.

I then thought maybe it was supposed to be a threading exercise, so I added parallel threads to my code. That didn't improve things enough to have bothered. (It MIGHT have finished in a couple of days.)

I then wrote code to convert the input "program" into [Java] code, thinking maybe that would run fast enough if multi-threaded. It did run somewhat faster, but not fast enough to complete in reasonable time.

I then spent time simplifying the generated code, removing stuff that wasn't really doing useful processing. As I was doing that, I started to see a trend in the input program. There are 14 input digits, and there was 14 sections in the program that were mostly identical, but with different constants.

Having then run out of ideas, I peeked at the Reddit thread and saw someone suggest the code was using the z register to implement a stack. Looking over the code, I started to see what they meant, and then manually transcoded the code into a set of 7 rules. I then used those rules to get the first 5 digits, and then let my now fast bruteforcer do the rest. That got me part one. Part two just meant revisiting the working out of the first 5 digits, and then some quick tweaks to my brute forcer and bingo part two done.

To be clear, I didn't need to do any brute forcing, I just thought my code would be marginally faster at getting the results than my working it out by hand.

Although my parser is completely pointless, I will share it here. It also includes the code I used to translate the input program into Java code. I could also include my brute forcer, but it would include my working out of my supplied inputs, and wouldn't really be useful without it, so I don't think I should.

Java:
final class MonadProgram
{
  private final String[] monadProgramInstructions;
  private long w;
  private long x;
  private long y;
  private long z;
  private String[] programInput = new String[0];
  private int inputPointer;
  
  public MonadProgram(String[] monadProgramInstructions)
  {
    this.monadProgramInstructions = monadProgramInstructions;
  }

  public void translateProgramToJavaCode()
  {
    System.out.println("long w=0;");
    System.out.println("long x=0;");
    System.out.println("long y=0;");
    System.out.println("long z=0;");
    int programCounter = 0;
    int inputNumber = 0;
    while (programCounter < monadProgramInstructions.length)
    {
      final String instruction = monadProgramInstructions[programCounter++];
      final String[] instructionParts = instruction.split(" ");
      switch (instructionParts[0])
      {
        case "inp": System.out.println(instructionParts[1] + " = input[" + inputNumber++ + "];"); continue;
        case "add": System.out.println(instructionParts[1] + " += " + instructionParts[2] + ";"); continue;
        case "mul": System.out.println(instructionParts[1] + " *= " + instructionParts[2] + ";"); continue;
        case "div": System.out.println(instructionParts[1] + " /= " + instructionParts[2] + ";"); continue;
        case "mod": System.out.println(instructionParts[1] + " %= " + instructionParts[2] + ";"); continue;
        case "eql": System.out.println(instructionParts[1] + " = (" + instructionParts[1] + "==" + instructionParts[2] + ")?1:0;"); continue;
        default:  throw new IllegalStateException("Unknown instruction: " + instructionParts[0]);
      }
    }
  }

  public void runWithInput(final String[] programInput)
  {
    w=0;
    x=0;
    y=0;
    z=0;
    inputPointer = 0;
    this.programInput = programInput;
    int programCounter = 0;
    while (programCounter < monadProgramInstructions.length)
    {
      executeInstruction(monadProgramInstructions[programCounter++]);
    }
  }

  private void executeInstruction(final String instruction)
  {
    final String[] instructionParts = instruction.split(" ");
    switch (instructionParts[0])
    {
      case "inp": doInp(instructionParts[1]); return;
      case "add": doAdd(instructionParts[1], instructionParts[2]); return;
      case "mul": doMul(instructionParts[1], instructionParts[2]); return;
      case "div": doDiv(instructionParts[1], instructionParts[2]); return;
      case "mod": doMod(instructionParts[1], instructionParts[2]); return;
      case "eql": doEql(instructionParts[1], instructionParts[2]); return;
      default:  throw new IllegalStateException("Unknown instruction: " + instructionParts[0]);
    }
  }

  private void doInp(final String register)
  {
    if (inputPointer >= programInput.length)  throw new IllegalStateException("Out of input");
    final String input = programInput[inputPointer++];
    final long val = Long.parseLong(input);
    switch (register)
    {
      case "w": w = val; return;
      case "x": x = val; return;
      case "y": y = val; return;
      case "z": z = val; return;
      default:  throw new IllegalStateException("Invalid register: " + register);
    }
  }

  private void doAdd(final String register1, final String register2)
  {
    switch (register1)
    {
      case "w": w += switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "x": x += switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "y": y += switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "z": z += switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      default:  throw new IllegalStateException("Invalid register: " + register1);
    }
  }

  private void doMul(final String register1, final String register2)
  {
    switch (register1)
    {
      case "w": w *= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "x": x *= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "y": y *= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "z": z *= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      default:  throw new IllegalStateException("Invalid register: " + register1);
    }
  }

  private void doDiv(final String register1, final String register2)
  {
    switch (register1)
    {
      case "w": w /= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "x": x /= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "y": y /= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "z": z /= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      default:  throw new IllegalStateException("Invalid register: " + register1);
    }
  }

  private void doMod(final String register1, final String register2)
  {
    switch (register1)
    {
      case "w": w %= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "x": x %= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "y": y %= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      case "z": z %= switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);}; return;
      default:  throw new IllegalStateException("Invalid register: " + register1);
    }
  }

  private void doEql(final String register1, final String register2)
  {
    final long compareTo = switch(register2) {case "w" -> w; case "x" -> x; case "y" -> y; case "z" ->z; default -> Long.parseLong(register2);};
    switch (register1)
    {
      case "w": w = (w == compareTo)?1:0; return;
      case "x": x = (x == compareTo)?1:0; return;
      case "y": y = (y == compareTo)?1:0; return;
      case "z": z = (z == compareTo)?1:0; return;
      default:  throw new IllegalStateException("Invalid register: " + register1);
    }
  }

  long getW()
  {
    return w;
  }

  long getX()
  {
    return x;
  }

  long getY()
  {
    return y;
  }

  long getZ()
  {
    return z;
  }
}
 
[ Part two of the previous post ]

This is the threading runner I put in front of the above class to allow me to run parallel copies:
Java:
final class MonadProgramRunner implements Runnable
{
  private final long start;
  private final long end;
  private long result = -1;
  final MonadProgram mp;

  MonadProgramRunner(final long start, final long end, final String[] inputProgram)
  {
    this.start = start;
    this.end   = end;
    mp = new MonadProgram(inputProgram);
  }

  @Override
  public void run()
  {
    result = -1;
    for (long possibleSN = end; possibleSN>start; possibleSN--)
    {
      final String[] programInput = convertLongToProgramInput(possibleSN);
      if (programInput.length == 0) continue;
      if (possibleSN % 444_444 == 0) System.out.println(possibleSN);  // provide some infrequent progress indication
      mp.runWithInput(programInput);
      if (mp.getZ() == 0)
      {
        result = possibleSN;
        System.out.println("Found result: " + result);
        break;
      }
    }
    if (result == -1)  System.out.println("Thread ends without result");
  }

  private static String[] convertLongToProgramInput(final long numberToConvert)
  {
    final String possibleSNStr = String.format("%d", numberToConvert);
    final String[] result = new String[14];
    for(int i=0; i<14; i++)
    {
      final char c = possibleSNStr.charAt(i);
      if (c == '0') return new String[0];
      result[i] = "" + c;
    }
    return result;
  }


  boolean isResult()
  {
    return result != -1;
  }
  
  long getResult()
  {
    return result;
  }
}

This is the brute forcing code that scheduled the above class. There is commented code and other messiness because I never actually used this code the way I normally would. You can see the code commented out that I used to produce the Java code translation, for example. And you can see the code is currently in the state to brute force part two with some part one code commented out:

Java:
  public static long getPart1Answer(final String[] day24InputLines)
  {
    //final MonadProgram mp = new MonadProgram(day24InputLines);
    //mp.translateProgramToJavaCode();
    return findLargestValidSN();
  }
  
  public static long getPart1AnswerSlowBruteForce(final String[] day24InputLines)
  {
    final ExecutorService executorService = Executors.newFixedThreadPool(30);
    final List<Future<MonadProgramRunner>> futures = new ArrayList<>();
    for (long possibleSN = 99999999999999L; possibleSN>11111111111111L; possibleSN-=10_000_000_000L)
    {
      final long start = possibleSN-10_000_000_000L;
      final long end   = possibleSN;
      final MonadProgramRunner mpr = new MonadProgramRunner(start, end, day24InputLines);
      final Future<MonadProgramRunner> future = executorService.submit(mpr);
      futures.add(future);
    }

    long bestResult = -1;
    for (final Future<MonadProgramRunner> future: futures)
    {
      final MonadProgramRunner mpr;
      try
      {
        mpr = future.get();
      }
      catch (final InterruptedException | ExecutionException e)
      {
        throw new IllegalStateException("Couldn't get future: " + e);
      }

      if (mpr.isResult())
      {
        final long result = mpr.getResult();
        if (result > bestResult)
        {
          System.out.println("Updating bestResult: " + result);
          bestResult = result;
        }
      }
    }
    executorService.shutdown();
    return bestResult;
  }

  private static String[] convertLongToProgramInput(final long numberToConvert)
  {
    final String possibleSNStr = String.format("%d", numberToConvert);
    final String[] result = new String[14];
    for(int i=0; i<14; i++)
    {
      final char c = possibleSNStr.charAt(i);
      if (c == '0') return new String[0];
      result[i] = "" + c;
    }
    return result;
  }

  static long findLargestValidSN()
  {
    long result = -1;
    final int[] input = new int[14];
    //for (int i=0; i<14; i++) input[i] =9;
    for (int i=0; i<14; i++) input[i] =1;
    input[0]=5;  // This would actually be the first five digits of the answer, I changed them all to 5 here so as to not
    input[1]=5;  // give anything away about my input
    input[2]=5;
    input[3]=5;
    input[4]=5;
    
    while (result < 0)
    {
      if (isValidSN(input)) result = Long.parseLong(String.format("%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
          input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7],
          input[8], input[9], input[10], input[11], input[12], input[13]));
      /*
      input[13]--;
      if (input[13]==0)
      {
        input[12]--;
        input[13]=9;
      }
      if (input[12]==0)
      {
        input[11]--;
        input[12]=9;
      }
      if (input[11]==0)
      {
        input[10]--;
        input[11]=9;
      }
      if (input[10]==0)
      {
        input[9]--;
        input[10]=9;
      }
      if (input[9]==0)
      {
        input[8]--;
        input[9]=9;
      }
      if (input[8]==0)
      {
        input[7]--;
        input[8]=9;
      }
      if (input[7]==0)
      {
        input[6]--;
        input[7]=9;
      }
      if (input[6]==0)
      {
        input[5]--;
        input[6]=9;
      }
      if (input[5]==0)
      {
        input[4]--;
        input[5]=9;
        System.out.println("digit 4 wrap");
      }
      if (input[4]==0)
      {
        input[3]--;
        input[4]=9;
        System.out.println("digit 3 wrap");
      }
      if (input[3]==0)
      {
        input[2]--;
        input[3]=9;
        System.out.println("digit 2 wrap");
      }
      if (input[2]==0)
      {
        input[1]--;
        input[2]=9;
        System.out.println("digit 1 wrap");
      }
      if (input[1]==0)
      {
        input[0]--;
        input[1]=9;
        System.out.println("digit 0 wrap");
      }
      if (input[0]==0)  throw new IllegalStateException("Ran out of digits");
      */
      
      input[13]++;
      if (input[13]==10)
      {
        input[12]++;
        input[13]=1;
      }
      if (input[12]==10)
      {
        input[11]++;
        input[12]=1;
      }
      if (input[11]==10)
      {
        input[10]++;
        input[11]=1;
      }
      if (input[10]==10)
      {
        input[9]++;
        input[10]=1;
      }
      if (input[9]==10)
      {
        input[8]++;
        input[9]=1;
      }
      if (input[8]==10)
      {
        input[7]++;
        input[8]=1;
      }
      if (input[7]==10)
      {
        input[6]++;
        input[7]=1;
      }
      if (input[6]==10)
      {
        input[5]++;
        input[6]=1;
      }
      if (input[5]==10)
      {
        input[4]++;
        input[5]=1;
        System.out.println("digit 4 wrap");
      }
      if (input[4]==10)
      {
        input[3]++;
        input[4]=1;
        System.out.println("digit 3 wrap");
      }
      if (input[3]==10)
      {
        input[2]++;
        input[3]=1;
        System.out.println("digit 2 wrap");
      }
      if (input[2]==10)
      {
        input[1]++;
        input[2]=1;
        System.out.println("digit 1 wrap");
      }
      if (input[1]==10)
      {
        input[0]++;
        input[1]=1;
        System.out.println("digit 0 wrap");
      }
      if (input[0]==10)  throw new IllegalStateException("Ran out of digits");

    }
    return result;
  }

This is the reduced generated Java code that I commented as I worked through it... This should give you a massive hint how it was done, so I will spoiler blur it:

Java:
  static boolean isValidSN(final int[] input)
  {
    long w=0;
    long x=0;
    long y=0;
    long z=0;

    z += input[0]+7;  // push input0+7

    z *= 26;
    z += input[1]+8;  // push input1+8

    z *= 26;
    z += input[2]+10;  // push input2+10

    w = input[3];
    x = z % 26 - 2;  //  does input3 == input2+8
    z /= 26;         // pop
    x = (x==w)?1:0;
    x = (x==0)?1:0;
    y = 25 * x + 1;
    z *= y;
    y = (w+4)*x;     
    z += y;           

    w = input[4];
    x = z % 26 - 10;  // does input4 == input1-2
    z /= 26;          // pop
    x = (x==w)?1:0;
    x = (x==0)?1:0;
    y = 25 * x + 1;
    z *= y;
    y = (w+4)*x;
    z += y;           

    z *= 26;
    z += input[5]+6;  // push input5+6

   \\ rest of the code resulting from my personal input is elided
 
I finished day 25, and thus the whole AoC 2021. The last day is a sort of modified game of life simulation. It's not really very tricky, except that you do need to process each round in two stages.

Here's my SeaCucumberSimulation class:

Java:
final class SeaCucumberSimulation
{
  private final int width;
  private final int height;
  private char[][] seaCucumbers;
 
  public SeaCucumberSimulation(final String[] initialConfiguration)
  {
    width = initialConfiguration[0].length();
    height = initialConfiguration.length;
    // System.out.format("Width=%d Height=%d%n", width, height);
    seaCucumbers = new char[width][height];
   
    for (int y=0; y<height; y++)
    {
      for (int x=0; x<width; x++)
      {
        seaCucumbers[x][y] = initialConfiguration[y].charAt(x);
      }
    }
  }

  public long runUntilNoneMove()
  {
    int numberRounds = 1;
    boolean someMoved = true;
    while (someMoved)
    {
      someMoved = oneRound();
      if (someMoved)
      {
        numberRounds++;
      // System.out.println("after round " + numberRounds);
      // outputOceanFloor();
      }
    }

    return numberRounds;
  }

  boolean oneRound()
  {
    char[][] newSeaCucumbers = new char[width][height];
    boolean someMoved = false;
   
    for (int y=0; y<height; y++)
    {
      for (int x=0; x<width; x++)
      {
        newSeaCucumbers[x][y] = '.';
      }
    }

    for (int y=0; y<height; y++)
    {
      for (int x=0; x<width; x++)
      {
        final int nextX;
        if (x == width-1)
          nextX=0;
        else
          nextX=x+1;
        if (seaCucumbers[x][y] == 'v')
          newSeaCucumbers[x][y] = 'v';

        if (seaCucumbers[x][y] == '>')
        {
          if (seaCucumbers[nextX][y] == '.')
          {
            someMoved = true;
            newSeaCucumbers[nextX][y] = '>';
          }
          else
          {
            newSeaCucumbers[x][y]   = '>';
          }
        }
      }
    }

    seaCucumbers = newSeaCucumbers;
    newSeaCucumbers = new char[width][height];
    for (int y=0; y<height; y++)
    {
      for (int x=0; x<width; x++)
      {
        newSeaCucumbers[x][y] = '.';
      }
    }

    boolean someMoved2 = false;
    for (int x=0; x<width; x++)
    {
      for (int y=0; y<height; y++)
      {
        final int nextY;
        if (y == height-1)
          nextY=0;
        else
          nextY=y+1;

        if (seaCucumbers[x][y] == '>')
          newSeaCucumbers[x][y] = '>';

        if (seaCucumbers[x][y] == 'v')
        {
          if (seaCucumbers[x][nextY] == '.')
          {
            someMoved2 = true;
            newSeaCucumbers[x][nextY] = 'v';
          }
          else
          {
            newSeaCucumbers[x][y]   = 'v';
          }
        }
      }
    }

    seaCucumbers = newSeaCucumbers;

    return someMoved || someMoved2;
  }

  void outputOceanFloor()
  {
    System.out.println(this.toString());
  }

  @Override
  public String toString()
  {
    final StringBuilder sb = new StringBuilder();
    for (int y=0; y<height; y++)
    {
      for (int x=0; x<width; x++)
      {
        sb.append(seaCucumbers[x][y]);
      }
      sb.append('\n');
    }
    return sb.toString();
  }
}

Which then gets exercised by
Java:
public static long getPart1Answer(final String day25Input, final String[] day25InputLines)
{
  final SeaCucumberSimulation scs = new SeaCucumberSimulation(day25InputLines);
  return scs.runUntilNoneMove();
}

As appears to be the tradition, there is no part two for day 25... it's just a matter of having all 49 stars to that point to complete it.
 
Hi everyone.

I haven't been here before but I thought someone might have problems with one of the problems of last years challenge or perhaps you just want to watch me solve them :) I understand if this is not allowed, then just tell me and remove this post and I will not push any of my content again.


Best regards
Daniel