examples/advanced/07_knapsack.cat
#!/usr/bin/env catnip
# Exemple : Knapsack (0/1)

struct Item { name; weight; value; }

items = list(
    Item("compass", 1, 10),
    Item("water", 3, 40),
    Item("sandwich", 2, 30),
    Item("glucose", 2, 25),
    Item("banana", 1, 15),
    Item("cream", 4, 50),
    Item("notebook", 3, 35),
)

capacity = 8
n = len(items)

# Brute force (recursive, 2^n)

print("⇒ Brute force")

solve_bf = (i, w) => {
    if i == 0 or w == 0 { 0 }
    else {
        item = items[i - 1]
        if item.weight > w {
            solve_bf(i - 1, w)
        } else {
            skip = solve_bf(i - 1, w)
            take = item.value + solve_bf(i - 1, w - item.weight)
            if take > skip { take } else { skip }
        }
    }
}

print(f"max value = {solve_bf(n, capacity)}")

# ND-recursion with automatic memoization

print("⇒ ND-memoized")

pragma('nd_memoize', True)

best = ~~(tuple(n, capacity), (state, recur) => {
    i = state[0]
    w = state[1]
    if i == 0 or w == 0 { 0 }
    else {
        item = items[i - 1]
        if item.weight > w { recur(tuple(i - 1, w)) }
        else {
            skip = recur(tuple(i - 1, w))
            take = item.value + recur(tuple(i - 1, w - item.weight))
            if take > skip { take } else { skip }
        }
    }
})

print(f"max value = {best}")

# Backtracking: which items

print("⇒ Selected items")

backtrack = (i, w) => {
    if i == 0 or w == 0 { list() }
    else {
        item = items[i - 1]
        if item.weight > w {
            backtrack(i - 1, w)
        } else {
            skip_val = solve_bf(i - 1, w)
            take_val = item.value + solve_bf(i - 1, w - item.weight)
            if take_val > skip_val {
                backtrack(i - 1, w - item.weight) + list(item)
            } else {
                backtrack(i - 1, w)
            }
        }
    }
}

selected = backtrack(n, capacity)
total_weight = 0
total_value = 0

for item in selected {
    print(f"  {item.name} (w={item.weight}, v={item.value})")
    total_weight = total_weight + item.weight
    total_value = total_value + item.value
}

print(f"total: weight={total_weight}, value={total_value}")