def blocks_world_actions(blocks):
    actions = []
    for a in blocks:
        for b in blocks:
            if a == b:
                continue
            # Stack a on b: a and b both clear, a not on table-locked
            stack_precond = frozenset({
                f"Clear({a})", f"Clear({b})", f"On({a},Table)",
            })
            actions.append(Action(
                name=f"Stack({a},{b})",
                preconditions=stack_precond,
                add_list=frozenset({f"On({a},{b})"}),
                delete_list=frozenset({f"On({a},Table)", f"Clear({b})"}),
            ))
            # Unstack a from b back onto the table
            unstack_precond = frozenset({f"On({a},{b})", f"Clear({a})"})
            actions.append(Action(
                name=f"Unstack({a},{b})",
                preconditions=unstack_precond,
                add_list=frozenset({f"On({a},Table)", f"Clear({b})"}),
                delete_list=frozenset({f"On({a},{b})"}),
            ))
    return actions


if __name__ == "__main__":
    blocks = ["A", "B", "C"]
    initial = frozenset({
        "On(A,Table)", "On(B,Table)", "On(C,Table)",
        "Clear(A)", "Clear(B)", "Clear(C)",
    })
    goal = frozenset({"On(A,B)", "On(B,C)"})

    actions = blocks_world_actions(blocks)
    plan = strips_plan(initial, goal, actions)
    print("Plan:", plan)
