From 2dc039dff1730aae933c4cd7a4de384e2a7689e0 Mon Sep 17 00:00:00 2001 From: Rubydragon Date: Thu, 18 Apr 2024 01:34:38 +0200 Subject: [PATCH] Add session and Complete Split --- api.go | 45 +++++++++++++++++ graph.go | 124 +++++++++++++++++++++++++++++++++++++++++++++++ main.go | 138 +++++++---------------------------------------------- print.go | 23 +++++++++ session.go | 7 +++ util.go | 13 +++++ 6 files changed, 230 insertions(+), 120 deletions(-) create mode 100644 api.go create mode 100644 graph.go create mode 100644 print.go create mode 100644 session.go create mode 100644 util.go 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) +}