Suppose you're trying to make goal 280 from sequence 11 6 16 20. That is, you want to find some operators ⚀, ⚁, and ⚂, so that 11 ⚀ 6 ⚁ 16 ⚂ 20 = 280.
Consider using + for the last operator ⚂ : 11 ⚀ 6 ⚁ 16 + 20 = 280. Since we evaluate operators from left to right, this is equivalent to (11 ⚀ 6 ⚁ 16) + 20 = 280. So we can only replace ⚂ with + if we can make 11 ⚀ 6 ⚁ 16 = 280 - 20 = 260. We should recursively try to make 260 from sequence 11 6 16, and if we succeed, then we can also make 280 from 11 6 16 20.
Note that we can skip the recursion unless 280 > 20, because we don't get negative numbers or zero as input and we cannot use - as an operator.
Also consider using * for the last operator ⚂ : 11 ⚀ 6 ⚁ 16 * 20 = 280. This is equivalent to (11 ⚀ 6 ⚁ 16) * 20 = 280. So we can only replace ⚂ with * if we can make 11 ⚀ 6 ⚁ 16 = 280 / 20 = 14. We should recursively try to make 14 from 11 6 16, and if we succeed, then we can also make 280 from 11 6 16 20.
Note that we can skip the recursion unless 280 is a multiple of 20 because we don't get fractions as input and we cannot use ÷ (division) as an operator.
Turning this into Swift code, we get a function like this:
func canMake(_ goal: Int, from inputs: ArraySlice<Int>) -> Bool {
guard inputs.count > 1 else {
return goal == inputs.first!
}
var inputs = inputs
let last = inputs.removeLast()
if goal > last && canMake(goal - last, from: inputs) {
return true
}
if goal.isMultiple(of: last) && canMake(goal / last, from: inputs) {
return true
}
return false
}
// Example use:
if canMake(280, from: [11, 6, 16, 20]) {
answer += 280
}
For part 2, we get the new operator ||. We can handle this very similarly to how we handled + and *, except now we need to look at the decimal representations of the goal and of the last number in the sequence. If we're trying to make 12963 and the last number in the sequence is 3, then we can make 12963 as 1296 || 3, so we recursively ask whether we can make 1296 using the earlier part of the sequence.
func canMake(_ goal: Int, from inputs: ArraySlice<Int>) -> Bool {
guard inputs.count > 1 else {
return goal == inputs.first!
}
var inputs = inputs
let last = inputs.removeLast()
if goal > last && canMake(goal - last, from: inputs) {
return true
}
if goal.isMultiple(of: last) && canMake(goal / last, from: inputs) {
return true
}
let goalDigits = String(goal)
let lastDigits = String(last)
if goalDigits.count > lastDigits.count && goalDigits.hasSuffix(lastDigits) {
let newGoal = Int(goalDigits.dropLast(lastDigits.count))!
return canMake(newGoal, from: inputs)
}
return false
}