diff --git a/main.go b/main.go index e1b7f42..e629fcd 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "flag" + // "fmt" "log" - // "github.com/goccy/go-graphviz" + + "github.com/goccy/go-graphviz" _ "github.com/mattn/go-sqlite3" ) @@ -22,14 +24,39 @@ func main() { } // log.Printf("databse opened: %#+v\n", am) - exits, err := am.Exits() + gv := graphviz.New() + if err != nil { + log.Fatalf("error opening graph: %s\n", err) + } + g, err := gv.Graph() if err != nil { - log.Fatalf("error fetching Rooms: %s\n", err) + log.Fatalf("error retrieving cgraph: %s\n", err) } - for _, v := range exits { + + /* + x, err := am.Room("673") + if err != nil { + log.Fatalf("error querying room: %s", err) + } + log.Printf("%#+v\n", x) + return + */ + rooms, err := am.Rooms() + log.Printf("rooms found: %d\n", len(rooms)) + for _, room := range rooms[:50] { + log.Printf("main() %s\n", room) // log.Printf("Area: %#+v\n", v) - log.Printf("RoomExit found: %s\n", v) - break + // log.Printf("RoomExit found: %s\n", v) + err := am.Walk(room.Uid.String, g) + if err != nil { + log.Fatalf("error adding room to graph: %s\n", err) + } + } + + log.Printf("exit count: %d\n", g.NumberEdges()) + log.Printf("room count: %d\n", g.NumberNodes()) + if err := gv.RenderFilename(g, graphviz.SVG, "/var/www/renders.ndumas.com/aardmaps/final.svg"); err != nil { + log.Fatal(err) } // now that I have areas and rooms, I can start building a map diff --git a/mapper.go b/mapper.go index 094bafb..922b5bd 100644 --- a/mapper.go +++ b/mapper.go @@ -3,8 +3,9 @@ package main import ( "database/sql" "fmt" - // "log" + "log" + "github.com/goccy/go-graphviz/cgraph" _ "github.com/mattn/go-sqlite3" ) @@ -21,7 +22,9 @@ func NewMapper(fn string) (AardMapper, error) { } type AardMapper struct { - DB *sql.DB + DB *sql.DB + RoomCache map[string]Room + AreaCache map[string]Area } func (am AardMapper) Areas() (map[string]Area, error) { @@ -44,9 +47,69 @@ func (am AardMapper) Areas() (map[string]Area, error) { return areas, nil } -func (am AardMapper) Exits() ([]RoomExit, error) { +func (am AardMapper) Area(uid string) (Area, error) { + a, ok := am.AreaCache[uid] + if ok { + return a, nil + } + a = Area{} + row := am.DB.QueryRow("select uid, name, flags, color, texture from areas where uid = :uid") + + if err := row.Scan(&a.Uid, &a.Name, &a.Flags, &a.Color, &a.Texture); err != nil { + if err == sql.ErrNoRows { + return a, fmt.Errorf("no area found: %w", err) + } + } + + return a, nil +} + +func (am AardMapper) Rooms() ([]Room, error) { + var uidCount int + uidCountRow := am.DB.QueryRow("select count(uid) from rooms;") + uidCountRow.Scan(&uidCount) + log.Println("uidCount:", uidCount) + rows, err := am.DB.Query("select uid from rooms;") + if err != nil { + return nil, fmt.Errorf("error querying room UIDs: %w", err) + } + var uids = make([]string, uidCount) + i := 0 + for rows.Next() { + var uid string + if err := rows.Scan(&uid); err != nil { + return nil, fmt.Errorf("error querying room %q from uid: %w", uid, err) + } + uids[i] = uid + } + rooms := make([]Room, uidCount) + for idx, uid := range uids { + room, err := am.Room(uid) + if err != nil { + return rooms, fmt.Errorf("error fetching room: %s", err) + } + rooms[idx] = room + } + return rooms, nil +} + +func (am AardMapper) Room(uid string) (Room, error) { + r := Room{} + row := am.DB.QueryRow("select uid,name,building,info,notes,flags,area,norecall,noportal from rooms where uid = ?", uid) + + if err := row.Scan(&r.Uid, &r.Name, &r.Building, &r.Info, &r.Notes, &r.Flags, &r.AreaName, &r.Norecall, &r.Noportal); err != nil { + if err == sql.ErrNoRows { + return r, fmt.Errorf("no room found: %w", err) + } + log.Println("room found:", r.Uid.String) + } + + return r, nil +} + +func (am AardMapper) Exits(uid string) ([]RoomExit, error) { exits := make([]RoomExit, 0) - rows, err := am.DB.Query("select r.uid,r.name,r.building,r.info,r.notes,r.flags,r.area, r.norecall,r.noportal,e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid;") + rows, err := am.DB.Query("select r.uid,r.name,r.building,r.info,r.notes,r.flags,r.area, r.norecall,r.noportal,e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid where uid=:uid;", uid) // rows, err := am.DB.Query("select e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid;") defer rows.Close() @@ -65,3 +128,45 @@ func (am AardMapper) Exits() ([]RoomExit, error) { } return exits, nil } + +func (am AardMapper) Walk(uid string, g *cgraph.Graph) error { + originRoom, err := am.Room(uid) + if err != nil { + return fmt.Errorf("error querying origin room for walk %q: %w", uid, err) + } + area, err := am.Area(originRoom.AreaName.String) + if err != nil { + return fmt.Errorf("error querying origin area for walk %q: %w", uid, err) + } + + sg := g.SubGraph("cluster_"+area.Name.String, 1) + sg.SetLabel(area.Name.String) + + originNode, err := sg.CreateNode(originRoom.Uid.String) + if err != nil { + return fmt.Errorf("error creating origin node for room %s: %w", originRoom.Name.String, err) + } + sg.SetLabel(originRoom.Name.String) + + exits, err := am.Exits(originRoom.Uid.String) + if err != nil { + return fmt.Errorf("error fetching exits for origin room %s: %w", originRoom.Name.String, err) + } + + for _, exit := range exits { + destRoom, err := am.Room(exit.Touid.String) + if err != nil { + return fmt.Errorf("error querying destination room %s: %w", uid, err) + } + destNode, err := sg.CreateNode(destRoom.Uid.String) + destNode.SetLabel(destRoom.Name.String) + if err != nil { + return fmt.Errorf("error creating destination node for room %s: %w", originRoom.Name.String, err) + } + // maybe i'll save recursion for later + // am.Walk(exit.Touid.String, g) // gotta prevent cycles. check if edge already exists and return? + sg.CreateEdge(originRoom.Uid.String+">"+originRoom.Uid.String, originNode, destNode) + } + + return nil +} diff --git a/models.go b/models.go index f5afff4..3f442c0 100644 --- a/models.go +++ b/models.go @@ -17,6 +17,10 @@ type Room struct { Exits []Exit } +func (r Room) String() string { + return fmt.Sprintf("{Room:%q[%q]}", r.Name.String, r.Uid.String) +} + type Exit struct { Dir sql.NullString Fromuid, Touid sql.NullString diff --git a/walk.go b/walk.go index 629bd70..46c3c75 100644 --- a/walk.go +++ b/walk.go @@ -1,14 +1,27 @@ package main import ( + "fmt" + // "github.com/goccy/go-graphviz" "github.com/goccy/go-graphviz/cgraph" ) -func walk(r Room, parent *cgraph.Node, g *cgraph.Graph) error { - _, err := g.CreateNode(r.Uid.String) +func walk(re RoomExit, g *cgraph.Graph) error { + sg := g.SubGraph("cluster_"+re.Name.String, 1) + sg.SetLabel(re.Name.String) + origin, err := sg.CreateNode(re.Fromuid.String) + origin.SetLabel(re.Name.String) + if err != nil { + return fmt.Errorf("error creating origin node: %w", err) + } + dest, err := sg.CreateNode(re.Touid.String) + if err != nil { + return fmt.Errorf("error creating destination node: %w", err) + } + _, err = g.CreateEdge(re.String(), origin, dest) if err != nil { - return err + return fmt.Errorf("error creating edge between origin and destination: %w", err) } return nil