diff --git a/combinatoric-solver/Cargo.lock b/combinatoric-solver/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..d5d26d1b0e334c49a64c5ea48b05e6f8fa414172
--- /dev/null
+++ b/combinatoric-solver/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "combinatoric-solver"
+version = "0.1.0"
diff --git a/combinatoric-solver/Cargo.toml b/combinatoric-solver/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..cd0e8603bee10ba489005d64ce0a9c7fdf095a14
--- /dev/null
+++ b/combinatoric-solver/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "combinatoric-solver"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/combinatoric-solver/src/collection.rs b/combinatoric-solver/src/collection.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f16919cc65443c9c68774e4d9a82a70ab019517e
--- /dev/null
+++ b/combinatoric-solver/src/collection.rs
@@ -0,0 +1,67 @@
+use crate::score::Score;
+
+#[derive(Clone)]
+pub struct Collection<const MAX_ITEMS: usize, S, ItemVariant>
+where
+    S: Score,
+    ItemVariant: Eq,
+{
+    pub score: S,
+    pub item_quantity: usize,
+    pub item_variant: [ItemVariant; MAX_ITEMS],
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq + Default + Copy>
+    Collection<MAX_ITEMS, S, ItemVariant>
+{
+    pub fn push(&self, item_variant: ItemVariant) -> Self {
+        let mut new = self.clone();
+        new.item_variant[self.item_quantity] = item_variant;
+        new.item_quantity += 1;
+        new
+    }
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq + Default + Copy> Default
+    for Collection<MAX_ITEMS, S, ItemVariant>
+{
+    fn default() -> Self {
+        let score = S::default();
+        let item_quantity = 0;
+        let item_variant = [ItemVariant::default(); MAX_ITEMS];
+        Self {
+            score,
+            item_quantity,
+            item_variant,
+        }
+    }
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq> PartialEq
+    for Collection<MAX_ITEMS, S, ItemVariant>
+{
+    fn eq(&self, other: &Self) -> bool {
+        self.item_variant == other.item_variant
+    }
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq> PartialOrd
+    for Collection<MAX_ITEMS, S, ItemVariant>
+{
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        self.score.partial_cmp(&other.score)
+    }
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq> Eq
+    for Collection<MAX_ITEMS, S, ItemVariant>
+{
+}
+
+impl<const MAX_ITEMS: usize, S: Score, ItemVariant: Eq> Ord
+    for Collection<MAX_ITEMS, S, ItemVariant>
+{
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.score.cmp(&other.score)
+    }
+}
diff --git a/combinatoric-solver/src/item.rs b/combinatoric-solver/src/item.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a37121c21ec510789e597288e4195971d908b067
--- /dev/null
+++ b/combinatoric-solver/src/item.rs
@@ -0,0 +1,6 @@
+pub trait ItemVariant: Eq + Copy + Default {}
+
+pub trait CombinatoricItem {
+    type ItemVariant;
+    fn variants(&self) -> impl IntoIterator<Item = Self::ItemVariant>;
+}
diff --git a/combinatoric-solver/src/lib.rs b/combinatoric-solver/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8d79d2533a131ace47671c564057a957139b9f02
--- /dev/null
+++ b/combinatoric-solver/src/lib.rs
@@ -0,0 +1,6 @@
+pub mod collection;
+pub mod item;
+pub mod score;
+pub mod solver;
+
+pub use crate::solver::Solver;
diff --git a/combinatoric-solver/src/score.rs b/combinatoric-solver/src/score.rs
new file mode 100644
index 0000000000000000000000000000000000000000..426175d2839de0a0eba7cdd3b72fdc98a3cf8fa3
--- /dev/null
+++ b/combinatoric-solver/src/score.rs
@@ -0,0 +1 @@
+pub trait Score: Ord + Default + Clone {}
diff --git a/combinatoric-solver/src/solver.rs b/combinatoric-solver/src/solver.rs
new file mode 100644
index 0000000000000000000000000000000000000000..863a28795b985889b69b3673a064f841f4b723c6
--- /dev/null
+++ b/combinatoric-solver/src/solver.rs
@@ -0,0 +1,56 @@
+use std::{cmp::Reverse, collections::BinaryHeap};
+
+use crate::{
+    collection::Collection,
+    item::{CombinatoricItem, ItemVariant},
+    score::Score,
+};
+
+pub struct Solver<const MAX_ITEMS: usize, S, V, I, Parameters>
+where
+    S: Score,
+    V: ItemVariant,
+    I: CombinatoricItem,
+{
+    top_items: BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>,
+    max_capacity: usize,
+    parameters: Parameters,
+    items: Vec<I>,
+}
+
+impl<'a, const MAX_ITEMS: usize, S, V, I, Parameters> Solver<MAX_ITEMS, S, V, I, Parameters>
+where
+    S: Score,
+    V: ItemVariant,
+    I: CombinatoricItem<ItemVariant = V>,
+{
+    pub fn new(max_capacity: usize, items: Vec<I>, parameters: Parameters) -> Self {
+        let top_items = BinaryHeap::with_capacity(max_capacity);
+        Self {
+            top_items,
+            max_capacity,
+            parameters,
+            items,
+        }
+    }
+    pub fn solve(mut self) -> Vec<I> {
+        let item = &self.items;
+        Self::rec_iter(0, Collection::default(), item, &mut self.top_items);
+        vec![]
+    }
+    fn rec_iter(
+        i: usize,
+        collection: Collection<MAX_ITEMS, S, V>,
+        items: &'a [I],
+        top_items: &mut BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>,
+    ) {
+        if i >= items.len() {
+            return;
+        }
+
+        for variant in items[i].variants() {
+            let collections = collection.push(variant);
+            Self::rec_iter(i + 1, collections, items, top_items);
+        }
+    }
+}