This one is... complex...
If this were a more weakly-typed language, I would probably just do some introspect and just... do the thing. But it's not.
Looks like I get to play around actual classes this time, instead of just dancing around it with very nested pairs.
class SFNum {
public:
int num = -1;
pair<SFNum, SFNum> *child;
SFNum(){};
SFNum(int i){ num = i; };
SFNum(SFNum a, SFNum b) { num = -1; child = new pair<SFNum, SFNum>(a, b); }
SFNum(string s) {
if (s.length() == 1) {
num = stoi(s);
return;
}
int nested = 0, splitIndex = -1;
for (int i = 0; i < s.length(); i++) {
if (splitIndex >= 0) break;
switch(s[i]) {
case '[': nested++; break;
case ']': nested--; break;
case ',':
if (nested == 1) {
splitIndex = i;
}
break;
}
}
string left = s.substr(1, splitIndex-1);
string right = s.substr(splitIndex+1, string::npos);
right.pop_back();
child = new pair<SFNum, SFNum>(SFNum(left), SFNum(right));
}
};
It took a while to get that just right, mostly because it's been so long since I've written a class in C++ that I forgot that it works a bit differently than I'm used to. But it works, it parses the numbers correctly! Now I have to get it to reduce...
~
Wow that took a WAY longer time than I expected. It was both a complex problem to solve, really difficult to really think about, and admittedly I had to brute-force a few combinations of left and right passing of numbers.
enum NumState { None, Explode, ExplodeLeft, ExplodeRight, Split, NotDone };
NumState reduce(int level = 0) {
passup = -1;
if (num >= 0) return NumState::None;
if (level >= 4) return NumState::Explode;
NumState leftState = child->first.reduce(level+1);
switch(leftState) {
case NumState::Explode:
passup = child->first.child->first.num;
child->second.explode(child->first.child->second.num, Dir::Left);
child->first = SFNum(0);
return NumState::ExplodeLeft;
case NumState::None:
// Handle other states....
}
NumState rightState = child->second.reduce(level+1);
switch(rightState) {
// Same as above, but first/second swapped
}
return NumState::None;
};
NumState split() {
if (num >= 0) {
if (num > 9) {
int carry = num % 2;
int half = num / 2;
num = -1;
child = new pair(SFNum(half), SFNum(half+carry));
return NumState::Split;
}
return NumState::None;
}
NumState leftState = child->first.split();
if (leftState != NumState::None) return leftState;
return child->second.split();
};
I truncated A LOT here, so the full code, as it always has been, can be found at the github link at the bottom of the post.
After a lot of thorough testing, I still need to find the magnitude, but that seems easy...
int magnitude() {
if (num >= 0) return num;
return ( (3 * child->first.magnitude()) + (2 * child->second.magnitude()) );
}
I have a feeling I'm missing some sort of trick to all of these. I may trawl through some other solutions on the subreddit for all of this to see if I really am missing something or not.
But trick or not, I'm done with part one then! In by far the longest time I've spent on this so far.
Okay, this one doesn't seem TOO bad. The hard part is done, it's just a different way of dealing with it all. I guess. I think I'll need a copy constructor though, that's the only thing I'm missing, since messing with these numbers is pretty destructive...
And that was a pretty quick change. No changes to the main logic, just how I'm having these numbers interact.
int maxMagnitude = -1;
for (int i = 0; i < lines.size(); i++)
for (int j = 0; j < lines.size(); j++) {
if (i == j) continue;
SFNum num = SFNum(SFNum(lines[i]), SFNum(lines[j]));
NumState state = NumState::NotDone;
while (state != NumState::None) {
state = num.reduce();
if (state == NumState::None)
state = num.split();
}
maxMagnitude = max(maxMagnitude, num.magnitude());
}
I really don't like these fish now, and they can flunk their math classes for all I care.