lfeutre / lmud

An Erlang and LFE MUD/MUSH Server.
Other
23 stars 9 forks source link

Define graph models for game data #43

Closed oubiwann closed 2 years ago

oubiwann commented 2 years ago

Part of:

Tasks:

oubiwann commented 2 years ago

Data modelling ...

So edges offer some interesting possibilities:

The efficacy of this needs to be tested: how easy is it to treat digraph data as an in-memory database?

Given that digraph is backed by ETS, I see no problem storing all of a MUD's game data there, as long as it gets backed up to disk regularly.

oubiwann commented 2 years ago

Here are a set of experimental uses that demonstrate the possibility of this approach:

(set g (digraph:new))

(set `#(ok ,rm1) (mudstore:load "rooms" "room1"))
(set `#(ok ,rm2) (mudstore:load "rooms" "room2"))
(set `#(ok ,obj1) (mudstore:load "objects" "sword"))
(set `#(ok ,obj2) (mudstore:load "objects" "painting"))
(set `#(ok ,per1) (mudstore:load "characters" "eve"))

(digraph:add_vertex g 'r1 rm1)
(digraph:add_vertex g 'r2 rm2)
(digraph:add_edge g 'r1 'r2 #m(type transit direction east size normal))
(digraph:add_edge g 'r2 'r1 #m(type transit direction west size normal))

(digraph:add_vertex g 'o1 obj1)
(digraph:add_vertex g 'o2 obj2)
(digraph:add_vertex g 'p1 per1)
(digraph:add_edge g 'o1 'r1 #m(type contains))
(digraph:add_edge g 'o2 'r1 #m(type contains))
(digraph:add_edge g 'p1 'r1 #m(type contains))

(defun in-edges (g v)
  (list-comp
    ((<- e (digraph:in_edges g v)))
    (digraph:edge g e)))

(defun out-edges (g v)
  (list-comp
    ((<- e (digraph:out_edges g v)))
    (digraph:edge g e)))

(defun contents (g v)
  (list-comp
    ((<- `#(,_ ,obj ,_ #m(type contains)) (in-edges g v)))
    (let ((`#(,_ ,data) (digraph:vertex g obj)))
      data)))

(defun entrances (g v)
  (list-comp
    ((<- `#(,_ ,obj ,_ #m(type transit)) (in-edges g v)))
    (let ((`#(,_ ,data) (digraph:vertex g obj)))
      data)))

(defun exits (g v)
  (list-comp
    ((<- `#(,_ ,_ ,obj #m(type transit)) (out-edges g v)))
    (let ((`#(,_ ,data) (digraph:vertex g obj)))
      data)))

Example use:

(list-comp
  ((<- v (contents g 'r1)))
  (proplists:get_value 'desc v))

output:

("It's an old, rusty sword that probably isn't very useful anymore."
 "It depicts a dramatic landscape."
 "eve looks fairly ordinary; maybe they should update their description?")
oubiwann commented 2 years ago

We could also use digraph:in_nighbours to get vertices directly:

(defun in-neighbours (g v)
  (list-comp
    ((<- in (digraph:in_neighbours g v)))
    (let ((`#(,_ ,data) (digraph:vertex g in)))
      data)))

However, we'd need to add object type metadata to object files so we could filter by object type ...

Though there is more work using the edge-based approach, the use of edge directions and labels allows one to define nuanced relationships between vertices.

oubiwann commented 2 years ago

As for IDs, I'm tempted just to use uuids ...

oubiwann commented 2 years ago

Same thing as above, but I added uuids to the save files:

(set g (digraph:new))

(set `#(ok ,rm1) (mudstore:load "rooms" "room1"))
(set `#(ok ,rm2) (mudstore:load "rooms" "room2"))
(set `#(ok ,obj1) (mudstore:load "objects" "sword"))
(set `#(ok ,obj2) (mudstore:load "objects" "painting"))
(set `#(ok ,per1) (mudstore:load "characters" "eve"))

(set rm1-id (proplists:get_value 'id rm1))
(set rm2-id (proplists:get_value 'id rm2))
(set obj1-id (proplists:get_value 'id obj1))
(set obj2-id (proplists:get_value 'id obj2))
(set per1-id (proplists:get_value 'id per1))

(digraph:add_vertex g rm1-id rm1)
(digraph:add_vertex g rm2-id rm2)
(digraph:add_edge g rm1-id rm2-id #m(type transit direction east size normal))
(digraph:add_edge g rm2-id rm1-id #m(type transit direction west size normal))

(digraph:add_vertex g obj1-id obj1)
(digraph:add_vertex g obj2-id obj2)
(digraph:add_vertex g per1-id per1)
(digraph:add_edge g obj1-id rm1-id #m(type contains))
(digraph:add_edge g obj2-id rm1-id #m(type contains))
(digraph:add_edge g per1-id rm1-id #m(type contains))

Some use:

(list-comp
  ((<- v (contents g rm1-id)))
  `#m(id ,(proplists:get_value 'id v)
      desc ,(proplists:get_value 'desc v)))

Output:

(#M(desc
      "It's an old, rusty sword that probably isn't very useful anymore."
    id "fc4ae75a-0efb-11ed-abdd-e7e631866491")
 #M(desc "It depicts a dramatic landscape."
    id "f53ca7d2-0efb-11ed-8fc3-8b038c9bb3bb")
 #M(desc
      "eve looks fairly ordinary; maybe they should update their description?"
    id "e10e0102-0efb-11ed-b73a-d77a60b099a2"))
oubiwann commented 2 years ago

With 100s or 1000s of vertices, the use of string identifiers would save the atom table ...

oubiwann commented 2 years ago

I think this one's pretty much done :-)

oubiwann commented 2 years ago

Oh, we could do something interesting for characters ...

oubiwann commented 2 years ago

btw, updating a vertex looks like this:

(set rm2 (proplists:delete 'version rm2))
(set rm2 (lists:append '(#(version 2)) rm2))
(digraph:add_vertex g rm2-id rm2)
ericmanlol commented 2 years ago

i'm still unpacking everything but just wanted to say that this whole approach with graphs is awesome/novel, nice job @oubiwann!

oubiwann commented 2 years ago

@ericmanlol Thanks, man!

(I'm having a blast 😉)