diff --git a/api.go b/api.go
new file mode 100644
index 0000000..da3ab0b
--- /dev/null
+++ b/api.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+)
+
+// CompleteSplit marks a split as finished and unlocks its dependent splits if they have no other dependencies
+func CompleteSplit(session *Session, split *Split) error {
+	fmt.Printf("Splitting split %s (%s)\n", split.Name, split.State)
+	if split.State == Locked {
+		fmt.Println("Death!!")
+		return errors.New("cannot complete a split that is locked")
+	}
+
+	// Set provided split to finished
+	split.State = Finished
+
+	// Iterate over dependencies
+	for _, dep := range split.Dependencies {
+		// Check if dependency has no other dependencies
+		hasOtherDependencies := false
+		for _, otherDep := range session.Graph.Splits {
+			if otherDep == dep || otherDep.State == Finished {
+				continue
+			}
+			for _, otherDep2 := range otherDep.Dependencies {
+				if otherDep2 == dep {
+					hasOtherDependencies = true
+					break
+				}
+			}
+			if hasOtherDependencies {
+				break
+			}
+		}
+
+		// If no other dependencies, set dependency to unlocked
+		if !hasOtherDependencies {
+			dep.State = Unlocked
+		}
+	}
+
+	return nil
+}
diff --git a/graph.go b/graph.go
new file mode 100644
index 0000000..6f4dc40
--- /dev/null
+++ b/graph.go
@@ -0,0 +1,124 @@
+package main
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"math/rand"
+	"os"
+	"strings"
+	"time"
+)
+
+// SplitState represents the state of a split
+type SplitState string
+
+const (
+	Locked   SplitState = "locked"
+	Unlocked SplitState = "unlocked"
+	Started  SplitState = "started"
+	Finished SplitState = "finished"
+)
+
+// Split represents a split in the speedrun graph
+type Split struct {
+	ID           int
+	Name         string
+	Icon         string
+	Dependencies []*Split
+	Timestamp    time.Time
+	State        SplitState
+}
+
+// Graph represents the speedrun graph
+type Graph struct {
+	Splits []*Split
+	Start  *Split
+	Final  *Split
+}
+
+// NewSplit creates a new split
+func NewSplit(name, icon string) *Split {
+	return &Split{
+		ID:        rand.Intn(1000000), // Randomly generating ID
+		Name:      name,
+		Icon:      icon,
+		Timestamp: time.Time{},
+		State:     Locked,
+	}
+}
+
+// AddDependency adds a dependency to a split
+func (s *Split) AddDependency(dep *Split) {
+	s.Dependencies = append(s.Dependencies, dep)
+}
+
+// ReadGraphFromFile reads graph from file representation
+func ReadGraphFromFile(filename string) (*Graph, error) {
+	file, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	scanner := bufio.NewScanner(file)
+	splits := make(map[string]*Split)
+
+	// Read lines from file
+	for scanner.Scan() {
+		line := scanner.Text()
+		parts := strings.Split(line, ",")
+		name := parts[0]
+		icon := parts[1]
+		dependencies := strings.Split(parts[2], "|")
+
+		// Create split
+		split := NewSplit(name, icon)
+		splits[name] = split
+
+		// Create dependencies
+		for _, depName := range dependencies {
+			dep := splits[depName]
+			if dep != nil {
+				split.AddDependency(dep)
+			}
+		}
+	}
+
+	// Set start and final splits
+	var start, final *Split
+	for _, split := range splits {
+		if split.Name == "Start" {
+			start = split
+		} else if len(split.Dependencies) == 0 {
+			if final != nil {
+				fmt.Println(final.Name)
+				return nil, errors.New("more than one final split found, only one split with no dependencies can exist")
+			}
+			final = split
+		}
+	}
+
+	if start == nil {
+		return nil, errors.New("no start split found, there should be a split with name 'Start'")
+	}
+	if final == nil {
+		return nil, errors.New("no final split found, there should be the only split with no dependencies")
+	}
+
+	// Create graph
+	graph := &Graph{
+		Splits: make([]*Split, 0, len(splits)),
+		Start:  start,
+		Final:  final,
+	}
+
+	graph.Start.State = Unlocked
+
+	// Append splits to graph
+	for _, split := range splits {
+		graph.Splits = append(graph.Splits, split)
+	}
+
+	return graph, nil
+}
diff --git a/main.go b/main.go
index 80b134a..1067ca1 100644
--- a/main.go
+++ b/main.go
@@ -1,127 +1,16 @@
 package main
 
 import (
-	"bufio"
-	"errors"
 	"fmt"
-	"math/rand"
-	"os"
-	"strings"
-	"time"
 )
 
-// SplitState represents the state of a split
-type SplitState string
-
-const (
-	Locked   SplitState = "locked"
-	Unlocked SplitState = "unlocked"
-	Started  SplitState = "started"
-	Finished SplitState = "finished"
-)
-
-// Split represents a split in the speedrun graph
-type Split struct {
-	ID           int
-	Name         string
-	Icon         string
-	Dependencies []*Split
-	Timestamp    time.Time
-	State        SplitState
-}
-
-// Graph represents the speedrun graph
-type Graph struct {
-	Splits []*Split
-	Start  *Split
-	Final  *Split
-}
-
-// NewSplit creates a new split
-func NewSplit(name, icon string) *Split {
-	return &Split{
-		ID:        rand.Intn(1000), // Randomly generating ID
-		Name:      name,
-		Icon:      icon,
-		Timestamp: time.Time{},
-		State:     Locked,
-	}
-}
-
-// AddDependency adds a dependency to a split
-func (s *Split) AddDependency(dep *Split) {
-	s.Dependencies = append(s.Dependencies, dep)
-}
-
-// ReadGraphFromFile reads graph from file representation
-func ReadGraphFromFile(filename string) (*Graph, error) {
-	file, err := os.Open(filename)
-	if err != nil {
-		return nil, err
-	}
-	defer file.Close()
-
-	scanner := bufio.NewScanner(file)
-	splits := make(map[string]*Split)
-
-	// Read lines from file
-	for scanner.Scan() {
-		line := scanner.Text()
-		parts := strings.Split(line, ",")
-		name := parts[0]
-		icon := parts[1]
-		dependencies := strings.Split(parts[2], "|")
-
-		// Create split
-		split := NewSplit(name, icon)
-		splits[name] = split
-
-		// Create dependencies
-		for _, depName := range dependencies {
-			dep := splits[depName]
-			if dep != nil {
-				split.AddDependency(dep)
-			}
-		}
-	}
-
-	// Set start and final splits
-	var start, final *Split
-	for _, split := range splits {
-		if split.Name == "Start" {
-			start = split
-		} else if len(split.Dependencies) == 0 {
-			if final != nil {
-				fmt.Println(final.Name)
-				return nil, errors.New("more than one final split found, only one split with no dependencies can exist")
-			}
-			final = split
-		}
-	}
-
-	if start == nil {
-		return nil, errors.New("no start split found, there should be a split with name 'Start'")
-	}
-	if final == nil {
-		return nil, errors.New("no final split found, there should be the only split with no dependencies")
-	}
-
-	// Create graph
-	graph := &Graph{
-		Splits: make([]*Split, 0, len(splits)),
-		Start:  start,
-		Final:  final,
-	}
-
-	// Append splits to graph
-	for _, split := range splits {
-		graph.Splits = append(graph.Splits, split)
-	}
-
-	return graph, nil
-}
-
 func main() {
+	// Create a session with a random ID and read the graph from file
+	session := &Session{
+		ID:    generateRandomID(),
+		Graph: nil,
+	}
+
 	// Read graph from file
 	graph, err := ReadGraphFromFile("graph.txt")
 	if err != nil {
@@ -129,8 +18,17 @@ func main() {
 		return
 	}
 
-	// Example usage
+	// Set the graph for the session
+	session.Graph = graph
+
+	fmt.Println("Session ID:", session.ID)
 	fmt.Println("Graph:")
-	fmt.Println("Start:", graph.Start.Name)
-	fmt.Println("Final:", graph.Final.Name)
+	PrettyPrintGraph(graph)
+
+	CompleteSplit(session, graph.Start)
+	CompleteSplit(session, graph.Splits[1])
+	CompleteSplit(session, graph.Splits[2])
+
+	PrettyPrintGraph(graph)
+	fmt.Println("Done.")
 }
diff --git a/print.go b/print.go
new file mode 100644
index 0000000..b8cc375
--- /dev/null
+++ b/print.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+// PrettyPrintGraph prints the entire graph in a readable format
+func PrettyPrintGraph(graph *Graph) {
+	// Print start split
+	printSplit(graph.Start, 0)
+}
+
+// printSplit prints a split and its dependencies recursively with proper indentation
+func printSplit(split *Split, indent int) {
+	// Print split information
+	fmt.Printf("%s- %s (%s)\n", strings.Repeat("    ", indent), split.Name, split.State)
+
+	// Recursively print dependencies
+	for _, dep := range split.Dependencies {
+		printSplit(dep, indent+1)
+	}
+}
diff --git a/session.go b/session.go
new file mode 100644
index 0000000..9877351
--- /dev/null
+++ b/session.go
@@ -0,0 +1,7 @@
+package main
+
+// Session represents a user session
+type Session struct {
+	ID    string
+	Graph *Graph
+}
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..3bb47fc
--- /dev/null
+++ b/util.go
@@ -0,0 +1,13 @@
+package main
+
+import "math/rand"
+
+// generateRandomID generates a random session ID
+func generateRandomID() string {
+	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	b := make([]byte, 10)
+	for i := range b {
+		b[i] = charset[rand.Intn(len(charset))]
+	}
+	return string(b)
+}