That's... profound. Or a contradiction in terms. Or... an answer to a question which has not yet been asked.The site is very simple, but fairly advanced.
Yes, I'll give it a try. Thanks to @PHolder for mentioning the start of a new series. I'm ready to advance to day 2.Is anyone else planning on participating in the Advent of Code?
I've watched no videos, no. I have seen one in the past where the author of Advent of Code talked about his process and methods... I think that talk was given at Google.did you watch the video
Nope. I didn't even know there was one. I completed the first of today's tasks. Actually, I was up after midnight last night and looked at it before I went to sleep. I came up with a design before my brain shut down, remembered and refined it this morning. Now I'm going to practice for my magic class and try not to think about the second half until I am done.
/(?:(?:59|6[0-9]|7[1-6])in|1(?:[5-8][0-9]|9[0-3])cm)/
In Java you can capture groups from regex input using the round brackets. In fact you need to escape it if you don't want it to capture. So my code for that was:RegEx
//hgt (Height) - a number followed by either cm or in:
// If cm, the number must be at least 150 and at most 193.
// If in, the number must be at least 59 and at most 76.
private static final Pattern heightPattern = Pattern.compile("([0-9]+)(cm|in)");
private boolean validateHeight(final String input)
{
final Matcher m = heightPattern.matcher(input);
if (m.matches())
{
if (input.contains("cm"))
{
final int numcm = Integer.parseInt(m.group(1));
if ((numcm < 150) || (numcm > 193)) return false;
return true;
}
if (input.contains("in"))
{
final int numin = Integer.parseInt(m.group(1));
if ((numin < 59) || (numin > 76)) return false;
return true;
}
}
return false;
}
///<summary>
/// Validate height.
/// </summary>
/// <param name="value">The height.</param>
/// <returns><see langword="true"/> if <paramref name="value"/> contains a number followed by either cm or in:
/// If cm, the number must be at least 150 and at most 193.
/// If in, the number must be at least 59 and at most 76;
/// otherwise <see langword="false"/>.</returns>
static bool ValidateHgt(string value)
=> int.TryParse(value[0..^2], out int height)
&& (value[^2..^0] == "cm" || value[^2..^0] == "in")
&& ((value[^2..^0] == "cm"
&& height >= 150
&& height <= 193) || (value[^2..^0] == "in" && height >= 59 && height <= 76));
private static int calculateRow(final String boardingPass)
{
int min = 0;
int max = 127;
for(int i=0; i<6; i++)
{
final char c = boardingPass.charAt(i);
if (c == 'F')
{
max = max-((max-min)/2)-1;
}
if (c == 'B')
{
min = min+((max-min)/2)+1;
}
}
if (boardingPass.charAt(6) == 'F')
return min;
return max;
}
private static int calculateCol(final String boardingPass)
{
int min = 0;
int max = 7;
for(int i=7; i<9; i++)
{
final char c = boardingPass.charAt(i);
if (c == 'L')
{
max = max-((max-min)/2)-1;
}
if (c == 'R')
{
min = min+((max-min)/2)+1;
}
}
if (boardingPass.charAt(9) == 'L')
return min;
return max;
}
final class AnswerGroup
{
private final int numberOfPeople;
private final Set<Character> setOfQuestionsAnswered = new HashSet<>();
private final Map<Character, Integer> numberOfTimesQuestionsAnswered = new HashMap<>();
public AnswerGroup(final String[] lines)
{
numberOfPeople = lines.length;
for(final char c: "abcdefghijklmnopqrstuvwxyz".toCharArray())
{
numberOfTimesQuestionsAnswered.put(c, 0);
}
for (final String line: lines)
{
for(final char c: line.toCharArray())
{
setOfQuestionsAnswered.add(c);
numberOfTimesQuestionsAnswered.put(c, numberOfTimesQuestionsAnswered.get(c)+1);
}
}
}
public int getNumberOfQuestionsAnswered()
{
return setOfQuestionsAnswered.size();
}
public int getNumberOfQuestionsEveryoneAnswered()
{
int result = 0;
for(final char c: setOfQuestionsAnswered)
{
if (numberOfTimesQuestionsAnswered.get(c) == numberOfPeople) result++;
}
return result;
}
}
const data =
with a return, it would be a one-liner.d
is the parameter to the function.d.split('\n\n').map(e => e.replace(/\n/g, '')).map(e => [...new Set(e.split('').sort())]).reduce((p, v) => p + v.length, 0)
sort()
call.final class BagRulesManager
{
final Map<String, BagRule> rulesForWhatABagCanContain = new HashMap<>();
final Map<String, Set<String>> rulesForWhatCanContainABag = new HashMap<>();
public void addNewRule(final BagRule bagRuleToAdd)
{
final String bagName = bagRuleToAdd.getBagName();
rulesForWhatABagCanContain.put(bagName, bagRuleToAdd);
if (bagRuleToAdd.isAllowedContents())
{
final BagContentsItem[] allowedBagContents = bagRuleToAdd.getAllowedContents();
for (int i=0; i<allowedBagContents.length; i++)
{
final String childName = allowedBagContents[i].getNameOfItem();
if (!rulesForWhatCanContainABag.containsKey(childName)) rulesForWhatCanContainABag.put(childName, new HashSet<String>());
final Set<String> bagsThatCanContain = rulesForWhatCanContainABag.get(childName);
bagsThatCanContain.add(bagName);
}
}
}
public int getTotalNumberOfBagsABagCanContain(final String bagName)
{
final int result = _getTotalNumberOfBagsABagCanContain(bagName);
if (result == 0) return 0;
return result - 1;
}
private int _getTotalNumberOfBagsABagCanContain(final String bagName)
{
if (!rulesForWhatABagCanContain.containsKey(bagName)) throw new IllegalStateException("Asked to _getTotalNumberOfBagsABagCanContain() for a bagName that was not found: " + bagName);
final BagRule br = rulesForWhatABagCanContain.get(bagName);
if (!br.isAllowedContents()) return 0;
int result = 1;
final BagContentsItem[] allowedBagContents = br.getAllowedContents();
for (int i=0; i<allowedBagContents.length; i++)
{
final int numSub = _getTotalNumberOfBagsABagCanContain(allowedBagContents[i].getNameOfItem());
if (numSub == 0)
{
result += allowedBagContents[i].getNumberOfItem();
}
else
{
result += numSub * allowedBagContents[i].getNumberOfItem();
}
}
return result;
}
public String[] getBagsThatCanContainABag(final String bagName)
{
if (!rulesForWhatCanContainABag.containsKey(bagName)) return new String[0];
final Set<String> allPossibleContainingBags = new HashSet<>();
final Set<String> parents = rulesForWhatCanContainABag.get(bagName);
for (final String parentNames : parents)
{
allPossibleContainingBags.add(parentNames);
final String[] grandParents = getBagsThatCanContainABag(parentNames);
for (final String grandParentNames : grandParents)
{
allPossibleContainingBags.add(grandParentNames);
}
}
return allPossibleContainingBags.toArray(new String[0]);
}
}
public static void main(final String[] args)
{
final CodeReader cr = new FileCodeReader(FILE);
final Program p = Program.readProgram(cr, 1000);
final ProgramRunner pr = new ProgramRunner(p);
pr.runProgramStopBeforeFirstInstructionRepeat();
final int val = pr.getAccumVal();
System.out.println("Part 1: " + val);
// Iterate over the program instructions one by one, changing NOP->JMP or JMP->NOP then
// test run the program to see if it terminates normally
for (int i=0; i<p.getProgramSize(); i++)
{
final ProgramInstruction pi = p.getInstructionAt(i);
final InstructionType it = pi.getInstructionType();
final InstructionType newIT;
switch (it)
{
case TERMINATE: throw new IllegalStateException("Hit terminate");
case JMP: newIT = InstructionType.NOP; break;
case NOP: newIT = InstructionType.JMP; break;
default: newIT = InstructionType.UNDEFINED_INSTRUCTION; break;
}
if (newIT != InstructionType.UNDEFINED_INSTRUCTION)
{
final Program newP = p.newVersionWithChangedInstruction(i, newIT);
final ProgramRunner newPR = new ProgramRunner(newP);
if (newPR.runProgramStopBeforeFirstInstructionRepeat())
{
final int part2Val = newPR.getAccumVal();
System.out.println("Part 2 (@" + i +"): " + part2Val);
break;
}
}
}
}
// Only instructions run in part 1 could be affected for the answer to part 2
// Iterate over the program instructions one by one, changing NOP->JMP or JMP->NOP then
// test run the program to see if it terminates normally
for(final int i : pr.getInstructionAddressesOfExecutedInstructions())
final NumberReader nr = new NumberReader(FILE);
final NumberFIFO nf = new NumberFIFO(25);
// fill preamble
for (int i=0; i<25; i++) nf.addNumber(nr.getNextNumber());
long soughtNumber = -1; // For Part 2
while (nr.isMoreInput())
{
final long val = nr.getNextNumber();
if (nf.doesExistTwoNumbersThatSumTo(val))
{
nf.addNumber(val);
}
else
{
System.out.println("Part 1: " + val);
soughtNumber = val;
break;
}
}
for(int i=2; i<1000; i++)
{
final NumberReader nri = new NumberReader(FILE);
final NumberFIFO nfi = new NumberFIFO(i);
for (int j=0; j<i; j++) // fill preamble
{
nfi.addNumber(nri.getNextNumber());
}
boolean wasFound = false;
while (nri.isMoreInput())
{
if (nfi.addAll() == soughtNumber)
{
wasFound = true;
break;
}
nfi.addNumber(nri.getNextNumber());
}
if (wasFound)
{
System.out.println("Part 2: " + nfi.findMinAndMaxAndSumThem() + " (found in FIFO of size " + i + ")");
break;
}
}
public void doPart1Calculations()
{
int currentJoltage = 0;
int currentAdapterJoltage = -1;
while (joltageAdapterEnumerator.canGetNextJoltageAdapter())
{
currentAdapterJoltage = joltageAdapterEnumerator.getNextJoltageAdapter();
final int diff = currentAdapterJoltage-currentJoltage;
switch(diff)
{
case 1: num1Diffs++; break;
case 3: num3Diffs++; break;
default: throw new IllegalStateException("Couldn't work with joltage diff:" + diff);
}
currentJoltage = currentAdapterJoltage;
}
num3Diffs++; // For the device
}
private static final int[] tribonacci = {0, 1, 1, 2, 4, 7, 13, 24, 44};
public long doPart2Calculations()
{
joltageAdapterEnumerator.reset();
long result = 1;
int currentJoltage = 0;
int count = 1;
while (joltageAdapterEnumerator.canGetNextJoltageAdapter())
{
final int nextJoltage = joltageAdapterEnumerator.getNextJoltageAdapter();
if (currentJoltage+1 == nextJoltage)
{
count++;
}
else
{
result*=tribonacci[count];
count = 1;
}
currentJoltage = nextJoltage;
}
return result;
}
public long doPart2CalculationsB()
{
final Map<Integer, Long> pathsToPoint = new HashMap<>();
joltageAdapterEnumerator.reset();
pathsToPoint.put(0, 1L);
int adapterJoltage=0;
while (joltageAdapterEnumerator.canGetNextJoltageAdapter())
{
adapterJoltage = joltageAdapterEnumerator.getNextJoltageAdapter();
final long result =
pathsToPoint.getOrDefault(adapterJoltage-1, 0L) +
pathsToPoint.getOrDefault(adapterJoltage-2, 0L) +
pathsToPoint.getOrDefault(adapterJoltage-3, 0L);
pathsToPoint.put(adapterJoltage, result);
}
return pathsToPoint.get(adapterJoltage);
}
Yes, my description was rather brief. I had to mull over the problem for a while before I really understood what we were dealing with, and considered various approaches before I hit upon that one. I was a bit surprised that the gigantic number it produced on its first run was the right answer.@Paul F I didn't understand your solution at first, but I get it now. I implemented the same thing in Java using a HashMap and it runs as fast as my other solution and gives the same results.
I'm about to start part 2 (once again part 1 was trivial). There was a hint that made me think 64-bit ints would be needed (" there must be more than a trillion valid ways to arrange them!").I was a bit surprised that the gigantic number it produced on its first run was the right answer.
final SeatingStateProvider ssp = new FileSeatingStateProvider(FILE);
final SeatingState ss = new SeatingState(ssp.getWidth(), ssp.getHeight());
ss.initialize(ssp);
while (ss.evolve()) ;
System.out.println("Part 1: " + ss.getNumberOfOccupiedSeats());
ss.initialize(ssp);
while (ss.evolve2()) ;
System.out.println("Part 2: " + ss.getNumberOfOccupiedSeats());
Agreed, but the input layout is square (98x98), which interestingly becomes 100x100 if you add a floor border.Day 11 was very straight forward ... (I also made the mistake of assuming the input would be square ... )
Remember that everyone gets a unique input. Mine is:input layout is square (98x98),
Oh! I didn't know that. That's really interesting. I guess that's to stop people from posting just the answers. Each person probably gets input data randomly from a pre-generated set . It explains a message I got for an incorrect answer saying that it matched a different person's answer.Remember that everyone gets a unique input. Mine is:
Width=96, Height=98
final NavigationInstructionReader nir = new FileNavigationInstructionReader(FILE);
final BoatControl bc = new BoatControl1(0, 0, Direction.EAST);
while (nir.isMoreInput())
{
final NavigationInstruction ni = nir.getNextNavigationInstruction();
bc.navigate(ni);
}
System.out.println("Part 1: " + bc.getManhattanDistance());
final NavigationInstructionReader nir2 = new FileNavigationInstructionReader(FILE);
final BoatControl bc2 = new BoatControl2(0, 0, Direction.EAST, 10, -1);
while (nir2.isMoreInput())
{
final NavigationInstruction ni = nir2.getNextNavigationInstruction();
bc2.navigate(ni);
}
System.out.println("Part 2: " + bc2.getManhattanDistance());
// See note above, I copied the supplied input directly into my code
// private static final int timestamp = removed my specific input timestamp;
// private static final int[] buses = {23,41, removed the rest of my specific inputs};
public static void main(final String[] args)
{
int minimumWaitAnyNextBus = 10_000_000;
int busNumber=0;
for(int i=0; i<buses.length; i++)
{
final int tripNumberOfBusYouJustMissed = timestamp / buses[i];
final int tripNumberOfNextBus = tripNumberOfBusYouJustMissed + 1;
final int timestampOfNextBus = tripNumberOfNextBus * buses[i];
final int waitForNextArrivalOfBus = timestampOfNextBus - timestamp;
if (waitForNextArrivalOfBus < minimumWaitAnyNextBus)
{
minimumWaitAnyNextBus=waitForNextArrivalOfBus;
busNumber=buses[i];
}
}
System.out.format("Part 1: %d (You should catch bus %d in %d minutes)%n", (busNumber*minimumWaitAnyNextBus), busNumber, minimumWaitAnyNextBus);
System.out.println("Part 2: " + Part2.doCalc());
}
public static long doCalcWith(final long[] schedule)
{
final List<Long> nVals = new ArrayList<>();
final List<Long> aVals = new ArrayList<>();
for (int i=0; i<schedule.length; i++)
{
if (schedule[i]!=0)
{
nVals.add(schedule[i]);
aVals.add(schedule[i]-i);
}
}
final long[] n = new long[nVals.size()];
final long[] a = new long[nVals.size()];
for(int i=0; i<nVals.size(); i++)
{
n[i]=nVals.get(i);
a[i]=aVals.get(i);
}
return ChineseRemainderTheorem.chineseRemainder(n, a);
}
final InstructionReader ir = new FileInstructionReader(FILE);
final InstructionDecoder id = new InstructionDecoder(ir);
final ComputerEmulator ce = new ComputerEmulator(id);
ce.runProgramToEnd();
final long sum1 = ce.sumAllMemory();
System.out.println("Part 1: " + sum1);
ir.reset();
final InstructionDecoder2 id2 = new InstructionDecoder2(ir);
final ComputerEmulator2 ce2 = new ComputerEmulator2(id2);
ce2.runProgramToEnd();
final long sum2 = ce2.sumAllMemory();
System.out.println("Part 2: " + sum2);
public static ComputerInstruction makeSetMaskInstruction(final String mask)
{
final long andMask = Long.parseLong("0" + mask.replace('1', 'X').replace('X', '1'), 2);
final long orMask = Long.parseLong("0" + mask.replace('0', 'X').replace('X', '0'), 2);
return new ComputerInstruction(ComputerInstructionType.SET_MASK, andMask, orMask, -1, -1);
}
case STORE_TO_MEMORY:
{
lastAddress = ci.getAddress();
lastValue = ci.getValue() & currentAndMask | currentOrMask;
computerMemory.put(lastAddress, lastValue);
}
break;
public static ComputerInstruction2 makeSetMaskInstruction(final String mask)
{
final long addressXMask = Long.parseLong("0" + mask.replace('1', '0').replace('X', '1'), 2);
final long addressOrMask = Long.parseLong("0" + mask.replace('0', 'X').replace('X', '0'), 2);
return new ComputerInstruction2(ComputerInstructionType.SET_MASK, addressXMask, addressOrMask, -1, -1);
}
private void putValueAtAllAddresses()
{
final Set<Long> addressesToAffect = new HashSet<>();
long xPlacesFinder = 1;
boolean first = true;
int nummberOfXBits = 0;
final long oredAddress = lastAddress | currentAddressOrMask;
while (xPlacesFinder < 0b1_0000_0000_0000_0000_0000_0000_0000_0000_0000L)
{
if (xPlacesFinder == (currentAddressXMask & xPlacesFinder))
{
nummberOfXBits++;
if (first)
{
first = false;
addressesToAffect.add(oredAddress & ~xPlacesFinder);
addressesToAffect.add(oredAddress | xPlacesFinder);
}
else
{
final List<Long> newAddresses = new ArrayList<>();
for (final long addr : addressesToAffect)
{
newAddresses.add(addr & ~xPlacesFinder);
newAddresses.add(addr | xPlacesFinder);
}
addressesToAffect.addAll(newAddresses);
}
}
xPlacesFinder<<=1;
}
final int numberOfExpectedAddresses = (int)Math.pow(2, nummberOfXBits);
lastNumberOfAddresses = addressesToAffect.size();
if (lastNumberOfAddresses != numberOfExpectedAddresses)
{
throw new IllegalStateException(String.format("Didn't generate as many addresses(%d) as expected(%d)", lastNumberOfAddresses, numberOfExpectedAddresses));
}
private static final int[] startingInput = {2,0,... more numbers removed as I assume my input was unique};
public static void main(final String[] args)
{
final ElfGame eg = new ElfGame(startingInput);
final int part1 = eg.playGameUntilRound(2020);
System.out.println("Part 1: " + part1);
final int part2 = eg.playGameUntilRound(I don't know if my round number is unique, so eliminated);
System.out.println("Part 2: " + part2);
}
final class ElfGame
{
private int currentRound;
private int lastNumberSpoken;
private Map<Integer,Integer> valueAtRound = new HashMap<>();
private Map<Integer,Integer> numberOfTimesSpoken = new HashMap<>();
private Map<Integer,Integer> previouslySpoken = new HashMap<>();
…
public void playRound()
{
currentRound++;
final int nextNumber;
if (getNumberOfTimesSpoken(lastNumberSpoken) == 1)
{
nextNumber = 0;
}
else
{
int prevRoundSpoken = getPreviousRoundSpoken(lastNumberSpoken);
nextNumber = currentRound - prevRoundSpoken - 1;
}
updatePreviouslySpoken(nextNumber);
valueAtRound.put(nextNumber, currentRound);
numberOfTimesSpoken.put(nextNumber, numberOfTimesSpoken.getOrDefault(nextNumber, 0)+1);
lastNumberSpoken = nextNumber;
}
Those elves could memorize PI to 30, 000, 000 digits.Day 15 is a bizarre sequence of numbers in the form of an Elf game.
final Ticket myTicket = new Ticket(new int[] {127,83,rest of my unique input deleted});
final NumberRulesReader nrr = new FileNumberRulesReader(FILE);
final NearbyTicketReader ntr = new FileNearbyTicketReader(FILE);
final NumberRulesList nrl = new NumberRulesList();
nrl.readRules(nrr);
final ValidTicketList vtl = new ValidTicketList(nrl);
vtl.addTicket(myTicket);
final int sum = vtl.readTicketsAddValidAndSumInvalidFields(ntr);
System.out.println("Part 1: " + sum);
System.out.format("%d valid tickets (including mine)%n", vtl.getSize());
final TicketFieldNames tfn = TicketFieldNames.resolve(nrl, vtl);
final int[] part2Vals = myTicket.getAllFieldsWhosNameStartWith("departure", tfn);
long part2=part2Vals[0];
for(int i=1; i<part2Vals.length; i++) part2*=part2Vals[i];
System.out.println("Part 2: " + part2);
final Map<String, Integer> fieldNameToNumberMapping = new HashMap<>();
final Set<NumberRules> rulesYetMatched = new HashSet<>(nrl.getRules());
while (!rulesYetMatched.isEmpty())
{
for (int i=0; i<nrl.getSize(); i++)
{
final Set<Integer> allNumbersFromAllTickets = getAllNumbersAtIndexFromAllTickets(vtl, i);
final List<NumberRules> matchedRules = findAnyRulesThatWorkWithAllNumbers(rulesYetMatched, allNumbersFromAllTickets);
if (matchedRules.size() == 1)
{
final NumberRules matched = matchedRules.get(0);
fieldNameToNumberMapping.put(matched.getName(), i);
rulesYetMatched.remove(matched);
}
}
}
The site is very simple, but fairly advanced. It generates unique inputs for each user, so you can't submit someone else's results as your own. Also, don't assume these are easy-peasy little challenges... Some of them will be fairly straightforward, and some of them will be anything but. I did some of the challenges from last year (after the fact, because that was when I first learned about it) but stopped when I got completely stumped on a maze walking challenge and wasted a couple of days on it without progress.
I think you can still go back and do past years challenges if you want... Here's the maze one I found stumping https://adventofcode.com/2019/day/18 (warning that if you want to do all them in order, they will form a story. Additionally, some of the challenges from 2019 had you building a special virtual machine IntCode parser and you will probably have to solve those problems in order as they build on each other.)
Here's a post about last years progression live as it happened (and then eventually stopped happening) https://www.twit.community/t/advent-of-code-2019/4362/1
private static final String INITIAL_STATE =
"""
#.#.#.##
// removed the middle of my personalized input
#.##..##
""";
public static void main(final String[] args)
{
final TwoDimensionalPlane initialState = new TwoDimensionalPlane();
initialState.loadFrom(INITIAL_STATE);
ThreeDimensionalSpace cube = new ThreeDimensionalSpace();
cube.loadFrom(initialState);
for (int i=1; i<=6; i++)
{
cube = CubeEvolver.evolve(cube);
}
System.out.println("Part 1: " + cube.getNumberOfOccupiedCoordinates());
FourDimensionalSpace cube4d = new FourDimensionalSpace();
cube4d.loadFrom(initialState);
for (int i=1; i<=6; i++)
{
cube4d = CubeEvolver.evolve(cube4d);
}
System.out.println("Part 2: " + cube4d.getNumberOfOccupiedCoordinates());
}
public static void main(final String[] args)
{
final MathExpressionReader reader = new MathExpressionFileReader(FILE);
long sum1 = 0;
long sum2 = 0;
int numberOfExpressions = 0;
while (reader.isMoreInput())
{
final String mathExpression = reader.getNextMathExpression();
sum1 += MathExpressionComputer.compute(mathExpression);
sum2 += MathExpressionComputer.compute2(mathExpression);
numberOfExpressions++;
}
System.out.println("Number of math expressions: " + numberOfExpressions);
System.out.println("Part 1: " + sum1);
System.out.println("Part 2: " + sum2);
}
// eliminate pesky spaces
public static long compute(final String mathExpression)
{
final String expr = mathExpression.replaceAll(" ", "");
return _compute(expr);
}
private static long _compute(final String expr)
{
long curVal=0;
int curOp='+';
int i=0;
while(i<expr.length())
{
long num = 0;
if (expr.charAt(i)=='(')
{
int bracket=0;
for(int j=i; j<expr.length(); j++)
{
if (expr.charAt(j)=='(') bracket++;
if (expr.charAt(j)==')') bracket--;
if (bracket==0)
{
final String subExpr = expr.substring(i+1, j);
num = _compute(subExpr);
i=j+1;
break;
}
}
}
else
{
num=expr.charAt(i)-'0';
i++;
}
switch(curOp)
{
case '+':
{
curVal=curVal+num;
}
break;
case '*':
{
curVal=curVal*num;
}
break;
}
if (i<expr.length())
{
curOp = expr.charAt(i);
i++;
}
else
{
return curVal;
}
}
return curVal;
}
// This eliminates all expressions in brackets, replacing them with their computed value
private static long _compute2(final String expr)
{
String modExpr = expr;
while (modExpr.contains("("))
{
int b1 = -1;
int b2 = -1;
int bc = 0;
for (int i=0; i<modExpr.length(); i++)
{
final char c = modExpr.charAt(i);
if (c=='(')
{
if (b1<0) b1=i;
bc++;
}
if (c==')')
{
bc--;
if (bc == 0)
{
b2=i;
break;
}
}
}
final String before = modExpr.substring(0,b1);
final String bracedExpr = modExpr.substring(b1+1,b2);
final String after = modExpr.substring(b2+1);
modExpr = before + _compute2(bracedExpr) + after;
}
return _calc2a(modExpr);
}
// This makes sure that multiplication happens after addition because of the strange precedence rules
private static long _calc2a(final String expr)
{
if (expr.contains("*"))
{
int m1 = -1;
for (int i=0; i<expr.length(); i++)
{
final char c = expr.charAt(i);
if (c=='*')
{
m1=i;
break;
}
}
final String before = expr.substring(0,m1);
final String after = expr.substring(m1+1);
return _calc2b(before) * _calc2a(after);
}
return _calc2b(expr);
}
// This is a simplification of part 1 but one that can parse multidigit numbers
private static long _calc2b(final String expr)
{
long curVal=0;
int i=0;
while(i<expr.length())
{
long num=0;
while (i<expr.length() && isdigit(expr.charAt(i)))
{
num=num*10 + (expr.charAt(i)-'0');
i++;
}
curVal=curVal+num;
if (i<expr.length())
{
i++;
}
else
{
return curVal;
}
}
return curVal;
}
Day 18 was a lot of work for me too. It was interesting enough that didn't mind spending a lot of time on it.Day 18 was a struggle for me. It’s all about parsing some math with strange precedence rules.
...
I in no way commend my approach to anyone, but it did work well for me in the end, after spending 5 hours more than I should have banging my head trying wrong approaches.
I thought it would be interesting to try inserting parentheses into the expressions to explicitly give priority to '+' over '*'
My approach is almost one-pass. I scan left to right. When I find a '+' I work backwards to put a '(' then forwards to put a ')'. It's very crude but gets the job done for the puzzle.I tried your approach too ... I was trying to be iterative rather than recursive ... in one pass ...
It was an interesting challenge, which I solved in a very interesting (and potentially unusual) way.That’s the right answer! You are one gold star closer to saving your vacation. You got rank 945 on this star’s leaderboard.
private void _buildRecursive(final StringBuilder sb, final int ruleNumber)
{
if (inRule2Mode)
{
if (ruleNumber == 8)
{
handlePart2Rule8(sb);
return;
}
if (ruleNumber == 11)
{
handlePart2Rule11(sb);
return;
}
}
final RuleWrangler rw = new RuleWrangler();
final RuleReader rr = new FileRuleReader(FILE);
int numberOfRules = 0;
while (rr.isMoreInput())
{
final String rule = rr.getNextRule();
numberOfRules++;
rw.addRule(rule);
}
System.out.println("Number of rules: " + numberOfRules);
final Pattern p1 = rw.getMasterPattern(0);
final MessageReader mr = new FileMessageReader(FILE);
int numberOfMessages = 0;
int numberOfValidMessages = 0;
while (mr.isMoreInput())
{
final String message = mr.getNextMessage();
numberOfMessages++;
final Matcher m = p1.matcher(message);
if (m.matches()) numberOfValidMessages++;
}
System.out.println("Number of messages: " + numberOfMessages);
System.out.println("Part 1: " + numberOfValidMessages);
rw.toggleToPart2Mode();
final Pattern p2 = rw.getMasterPattern(0);
final MessageReader mr2 = new FileMessageReader(FILE);
int numberOfValidMessages2 = 0;
while (mr2.isMoreInput())
{
final String message = mr2.getNextMessage();
final Matcher m = p2.matcher(message);
if (m.matches()) numberOfValidMessages2++;
}
System.out.println("Part 2: " + numberOfValidMessages2);
final TileMatcher tm = new TileMatcher(12,12);
final TileReader tr = new TileFileReader(FILE);
int numberOfTiles = 0;
while (tr.isMoreInput())
{
final Tile t = tr.getNextTile();
tm.addTile(t);
numberOfTiles++;
}
System.out.println("Number of tiles: " + numberOfTiles);
final Set<Integer> corners = tm.findCornerTiles();
long part1 = 1;
for (final int tileNumber : corners)
{
part1 *= tileNumber;
}
System.out.println("Part 1: " + part1);
tm.buildMap();
final MonsterMap mm = new MonsterMap(12*8, 12*8);
tm.populateMonsterMap(mm);
mm.findAndHighlightSeaMonsters();
mm.dumpMap();
final int part2 = mm.countChar('#');
System.out.println("Part 2: " + part2);
somethingWasEliminated=true;
while(somethingWasEliminated)
{
somethingWasEliminated=false;
...
if (condition) somethingWasEliminated=true;
}
public int resolveAllergens()
{
boolean madeAnEntry = true;
// keep making passes until we have eliminated all we can
while (madeAnEntry)
{
madeAnEntry = false;
for (final Allergen a : Allergen.getAllAllergens())
{
// Find all foods that list this allergen
final Set<FoodItem> foodItemsWithAllergen = allergenToFoodItemMapping.get(a);
// find any ingredient(s) in common with all those foods
final Set<Ingredient> ingredientsInCommon = getIngredientsInCommon(foodItemsWithAllergen);
// One of the ingredients in common MUST be the allergen
// See if any of the ingredients are already known to be another allergen
final Set<Ingredient> ingredientsNotEliminated = new HashSet<>();
for (final Ingredient i : ingredientsInCommon)
{
if (!mappingOfIngredientToAllergen.containsKey(i))
{
ingredientsNotEliminated.add(i);
}
}
if (ingredientsNotEliminated.size() == 1)
{
// this must be the allergen
for (final Ingredient i: ingredientsNotEliminated)
{
mappingOfIngredientToAllergen.put(i, a);
}
madeAnEntry = true;
}
}
}
final Set<Ingredient> ingredientsNotEliminated = new HashSet<>();
for (final FoodItem fi: foodItemList)
{
for (final Ingredient i : fi.getIngredientList())
{
if (!mappingOfIngredientToAllergen.containsKey(i))
{
ingredientsNotEliminated.add(i);
}
}
}
final Food food = new Food();
final FoodItemReader fir = new FoodItemReader(FILE);
while (fir.isMoreInput())
{
final FoodItem fi = fir.getNextFoodItem();
food.addItem(fi);
}
System.out.println("Number of food items: " + food.getNumberOfFoodItems());
System.out.println("Number of unique ingredients: " + Ingredient.getNumberOfUniqueIngredients());
System.out.println("Number of unique allergens: " + Allergen.getNumberOfUniqueAllergens());
final int part1 = food.resolveAllergens();
System.out.println("Part 1: " + part1);
final String part2 = food.getDangerousIngredients();
System.out.println("Part 2: " + part2);
public Hand playToEnd()
{
while (hand1.getNumberOfCards()>0 && hand2.getNumberOfCards()>0)
{
final int p1c = hand1.getTopCard();
final int p2c = hand2.getTopCard();
if (p1c > p2c)
{
hand1.putOnBottom(p1c);
hand1.putOnBottom(p2c);
}
else
{
hand2.putOnBottom(p2c);
hand2.putOnBottom(p1c);
}
}
return returnWinner();
}
private static int _recurse(final Hand hand1, final Hand hand2, final Set<String> previousHands)
{
while (hand1.getNumberOfCards()>0 && hand2.getNumberOfCards()>0)
{
if (isPreviousHand(previousHands, hand1, hand2))
{
return 1;
}
final int p1c = hand1.getTopCard();
final int p2c = hand2.getTopCard();
int p1nc = hand1.getNumberOfCards();
int p2nc = hand2.getNumberOfCards();
if ((p1nc>=p1c) && (p2nc>=p2c))
{
final int roundWinner = makeHandAndRecurse(p1c, p2c, hand1, hand2);
doWinner(roundWinner, hand1, hand2, p1c, p2c);
}
else if (p1c > p2c)
{
doWinner(1, hand1, hand2, p1c, p2c);
}
else
{
doWinner(2, hand1, hand2, p1c, p2c);
}
}
if (hand1.getNumberOfCards() == 0) return 2;
return 1;
}
private static int makeHandAndRecurse(final int p1nc, final int p2nc, final Hand hand1in, final Hand hand2in)
{
final Set<String> previousHands = new HashSet<>();
final Hand newHand1 = hand1in.newSubHand(p1nc);
final Hand newHand2 = hand2in.newSubHand(p2nc);
return _recurse(newHand1, newHand2, previousHands);
}
// I chose to just copy the supplied input into String arrays in my main code
final Hand hand1 = new Hand(HAND1);
final Hand hand2 = new Hand(HAND2);
final Game game = new Game(hand1,hand2);
final Hand winningHand = game.playToEnd();
final long part1 = winningHand.calculateHandSum();
System.out.println("Part 1: " + part1);
final Hand hand1b = new Hand(HAND1);
final Hand hand2b = new Hand(HAND2);
final Game game2 = new Game(hand1b,hand2b);
final Hand winningHand2 = game2.recursivePlayToEnd();
final long part2 = winningHand2.calculateHandSum();
winningHand2.dump();
System.out.println("Part 2: " + part2);
public void playRounds(final int numberOfRounds)
{
final int[] pickedup = new int[3];
for (int i=0; i<numberOfRounds; i++)
{
pickedup[0]=cups[(currentCupPos+1)%cups.length];
pickedup[1]=cups[(currentCupPos+2)%cups.length];
pickedup[2]=cups[(currentCupPos+3)%cups.length];
int destination=cups[currentCupPos]-1;
if (destination <1) destination = cups.length;
while(destination==pickedup[0] || destination==pickedup[1] || destination==pickedup[2])
{
destination--;
if (destination <1) destination = cups.length;
}
int destLocation=-1;
int j=(currentCupPos+4)%cups.length;
while (destLocation<0)
{
if (cups[j] == destination)
destLocation=(j+1)%cups.length;
else
j=(j+1)%cups.length;
}
int to=(currentCupPos+1)%cups.length;
int from=(currentCupPos+4)%cups.length;
while (from != destLocation)
{
cups[to]=cups[from];
to=(to+1)%cups.length;
from=(from+1)%cups.length;
}
cups[(to)%cups.length]=pickedup[0];
cups[(to+1)%cups.length]=pickedup[1];
cups[(to+2)%cups.length]=pickedup[2];
currentCupPos=(currentCupPos+1)%cups.length;
}
}
public void playRounds2(final int numberOfRounds)
{
final int[] pickedup = new int[3];
for(int i=0; i<cups.length-1; i++)
{
nextNumber.put(cups[i], cups[i+1]);
}
prevValue = cups[cups.length-1];
nextNumber.put(prevValue, cups[0]);
for (int i=0; i<numberOfRounds; i++)
{
if (i%1_000_000==0)System.out.format("Round #%,d%n",i);
int nextPrev = nextNumber.get(prevValue);
pickedup[0]=nextNumber.get(nextPrev);
pickedup[1]=nextNumber.get(pickedup[0]);
pickedup[2]=nextNumber.get(pickedup[1]);
int destination=nextNumber.get(prevValue)-1;
nextNumber.put(nextNumber.get(prevValue), nextNumber.get(pickedup[2]));
prevValue = nextPrev;
if (destination <1) destination = cups.length;
while(destination==pickedup[0] || destination==pickedup[1] || destination==pickedup[2])
{
destination--;
if (destination <1) destination = cups.length;
}
int hold=nextNumber.get(destination);
nextNumber.put(destination, pickedup[0]);
nextNumber.put(pickedup[2], hold);
}
}
// cups is a hard coded int[] with the supplied input
public static void main(final String[] args)
{
final CupGame game = new CupGame(cups);
game.playRounds(100);
final String part1 = game.collectResult();
System.out.println("Part 1: " + part1);
final int[] newCups = makeNewCups(cups, 1_000_000);
final CupGame game2 = new CupGame(newCups);
game2.playRounds2(10_000_000);
final long part2 = game2.collectPart2Result();
System.out.println("Part 2: " + part2);
}
private static int[] makeNewCups(final int[] firstCups, final int numberOfCups)
{
final int[] newCups = new int[numberOfCups];
for (int i=0; i<firstCups.length; i++)
{
newCups[i]=firstCups[i];
}
for (int i=firstCups.length; i<numberOfCups; i++)
{
newCups[i]=i+1;
}
return newCups;
}
// Using an array named nextNumberArray of cups.length+1 (not using the zeroth element)
public void playRounds3(final int numberOfRounds)
{
final int[] pickedup = new int[3];
for(int i=0; i<cups.length-1; i++)
{
nextNumberArray[cups[i]] = cups[i+1];
}
prevValue = cups[cups.length-1];
nextNumberArray[prevValue] = cups[0];
for (int i=0; i<numberOfRounds; i++)
{
if (i%1_000_000==0)System.out.format("Round #%,d%n",i);
int nextPrev = nextNumberArray[prevValue];
pickedup[0]=nextNumberArray[nextPrev];
pickedup[1]=nextNumberArray[pickedup[0]];
pickedup[2]=nextNumberArray[pickedup[1]];
int destination=nextNumberArray[prevValue]-1;
nextNumberArray[nextNumberArray[prevValue]] = nextNumberArray[pickedup[2]];
prevValue = nextPrev;
if (destination <1) destination = cups.length;
while(destination==pickedup[0] || destination==pickedup[1] || destination==pickedup[2])
{
destination--;
if (destination <1) destination = cups.length;
}
int hold=nextNumberArray[destination];
nextNumberArray[destination]= pickedup[0];
nextNumberArray[pickedup[2]] = hold;
}
}
public static TileAddress convertTilePathToAddress(final String tilePath)
{
int x = 0;
int y = 0;
int i = 0;
while (i < tilePath.length())
{
final char c1 = tilePath.charAt(i++);
if ((c1 == 'n') || (c1 == 's'))
{
final char c2 = tilePath.charAt(i++);
if (c1 == 'n')
{
y--;
if (c2 == 'w')
{
x--;
}
else if (c2 == 'e')
{
x++;
}
else throw new IllegalStateException();
}
else if (c1 == 's')
{
y++;
if (c2 == 'w')
{
x--;
}
else if (c2 == 'e')
{
x++;
}
else throw new IllegalStateException();
}
else throw new IllegalStateException();
}
else
{
if (c1 == 'w')
{
x -= 2;
}
else if (c1 == 'e')
{
x += 2;
}
else throw new IllegalStateException();
}
}
return new TileAddress(x, y);
}
final TilePathReader tpr = new TilePathReader(FILE);
final TileTracker tt = new TileTracker();
while(tpr.isMoreInput())
{
final String tilePath = tpr.getNextTilePath();
final TileAddress ta = TileAddress.convertTilePathToAddress(tilePath);
tt.flipTileAtAddress(ta);
}
final int part1 = tt.getNumberOfBlackTiles();
System.out.println("Part 1: " + part1);
tt.flipTilesForDays(100);
final int part2 = tt.getNumberOfBlackTiles();
System.out.println("Part 2: " + part2);
private static final long v1 = __INPUT#1_not_shown__;
private static final long v2 = __INPUT#2_not_shown__;
public static void main(final String[] args)
{
final int loopSize1 = LoopFinder.findLoop(1, 7, 20201227, v1);
System.out.println(loopSize1);
final int loopSize2 = LoopFinder.findLoop(1, 7, 20201227, v2);
System.out.println(loopSize2);
final long key1 = LoopFinder.findKey(1, v1, loopSize2, 20201227);
System.out.println(key1);
final long key2 = LoopFinder.findKey(1, v2, loopSize1, 20201227);
System.out.println(key2);
}
I did the first 17 parts of 2019, but in early 2020. I got stumped on #18, I didn't know the necessary algorithm, and literally spent 3 days working on it to no end. I think my approach is probably correct, but I have a bug I just couldn't find/eliminate. I got busy, and never bothered to go back, but I may yet one day.I've already started on the 2019 series.