Many steps towards handling input (but not there yet...)
authorChristopher Allan Webber <cwebber@dustycloud.org>
Tue, 3 May 2016 14:45:23 +0000 (09:45 -0500)
committerChristopher Allan Webber <cwebber@dustycloud.org>
Tue, 3 May 2016 14:45:23 +0000 (09:45 -0500)
Makefile.am
mudsync.scm
mudsync/command.scm [new file with mode: 0644]
mudsync/gameobj.scm
mudsync/parser.scm
mudsync/player.scm

index a9c186f46a94fe7946015dd2f5bf35c0d6cbf840..bccc8083792add47c7eeb942dd0c115967cc5e21 100644 (file)
@@ -45,6 +45,7 @@ moddir=$(prefix)/share/guile/site/2.0
 godir=$(libdir)/guile/2.0/ccache
 
 SOURCES =  \
 godir=$(libdir)/guile/2.0/ccache
 
 SOURCES =  \
+       mudsync/command.scm \
        mudsync/game-master.scm \
        mudsync/gameobj.scm \
        mudsync/networking.scm \
        mudsync/game-master.scm \
        mudsync/gameobj.scm \
        mudsync/networking.scm \
index 84cb6d0ab40536e4f1e7b60ac92c9a90e4a14712..31caf486acbfd8a470b947bef2df28fe6660ebe4 100644 (file)
@@ -32,7 +32,7 @@
       '(game-master
         gameobj
         networking
       '(game-master
         gameobj
         networking
-        parser
+        command
         player
         room
         run-game))
         player
         room
         run-game))
diff --git a/mudsync/command.scm b/mudsync/command.scm
new file mode 100644 (file)
index 0000000..28092fc
--- /dev/null
@@ -0,0 +1,191 @@
+(define-module (mudsync command)
+  #:use-module (mudsync parser)
+  #:use-module (mudsync gameobj)
+  #:use-module (8sync systems actors)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
+  #:use-module (ice-9 control)
+  #:use-module (ice-9 match)
+
+  #:export (direct-command
+            indir-command
+            empty-command
+            direct-greedy-command
+            greedy-command
+            player-gather-command-handlers
+            find-command-winner))
+
+;;; Commands
+;;; ========
+
+(define %low-priority 0)
+(define %default-priority 1)
+(define %high-priority 2)
+
+(define-record-type <command>
+  (make-command verbs matcher should-handle action priority)
+  command?
+  (verbs command-verbs)
+  (matcher command-matcher)
+  (should-handle command-should-handle)
+  (action command-action)
+  (priority command-priority))
+
+
+(define (direct-command verbs action)
+  (make-command verbs
+                cmatch-direct-obj
+                ;; @@: Should we allow fancier matching than this?
+                ;;   Let the actor itself pass along this whole method?
+                (lambda* (goes-by #:key direct-obj)
+                  (member direct-obj goes-by))
+                action
+                %default-priority))
+
+(define* (indir-command verbs action #:optional prepositions)
+  (make-command verbs
+                cmatch-indir-obj
+                ;; @@: Should we allow fancier matching than this?
+                ;;   Let the actor itself pass along this whole method?
+                (lambda* (goes-by #:key direct-obj indir-obj preposition)
+                  (if prepositions
+                      (and
+                       (member indir-obj goes-by)
+                       (member preposition prepositions))
+                      (member indir-obj goes-by)))
+                action
+                %high-priority))
+
+(define (empty-command verbs action)
+  (make-command verbs
+                cmatch-empty
+                (const #t)
+                action
+                %low-priority))
+
+(define (greedy-command verbs action)
+  (make-command verbs
+                cmatch-greedy
+                (const #t)
+                action
+                %low-priority))
+
+(define (direct-greedy-command verbs action)
+  "greedy commands but which match the direct object"
+  (make-command verbs
+                cmatch-direct-obj-greedy
+                (lambda* (goes-by #:key direct-obj rest)
+                  (member direct-obj goes-by))
+                action
+                %low-priority))
+
+;; @@: We should probably ONLY allow these to go to users!
+(define* (custom-command verbs matcher should-handle action
+                         #:optional (priority %default-priority))
+  "Full-grained customizable command."
+  (make-command verbs matcher should-handle action priority))
+
+
+;;; Command handling
+;;; ================
+
+;; @@: Hard to know whether this should be in player.scm or here...
+;; @@: This could be more efficient as a stream...!?
+(define (player-gather-command-handlers player verb)
+
+  (define player-loc
+    (let ((result (gameobj-loc player)))
+      (if result
+          result
+          (throw 'player-has-no-location
+                 "Player ~a has no location!  How'd that happen?\n"
+                 #:player-id (actor-id player)))))
+
+  ;; Ask the room for its commands
+  (define room-commands
+    ;; TODO: Map room id and sort
+    (message-ref
+     (<-wait player player-loc
+             'get-container-commands
+             #:verb verb)
+     'commands))
+
+  ;; All the co-occupants of the room (not including ourself)
+  (define co-occupants
+    (remove
+     (lambda (x) (equal? x (actor-id player)))
+     (message-ref
+      (<-wait player player-loc 'get-occupants)
+      'occupants)))
+
+  ;; @@: There's a race condition here if someone leaves the room
+  ;;   during this, heh...
+  ;;   I'm not sure it can be solved, but "lag" on the race can be
+  ;;   reduced maybe?
+
+  ;; Get all the co-occupants' commands
+  (define co-occupant-commands
+    ;; TODO: Switch this to a fold.  Ignore a result if it
+    ;;   returns false for in the command response
+    (map
+     (lambda (co-occupant)
+       (let ((result (<-wait player co-occupant 'get-commands
+                             #:verb verb)))
+         (list
+          (message-ref result 'commands)
+          (message-ref result 'goes-by)
+          co-occupant)))
+     co-occupants))
+
+  ;; Append our own command handlers
+  (define our-commands
+    ((@@ (mudsync player) player-self-commands) player))
+
+  ;; TODO: Append our inventory's relevant command handlers
+
+  ;; Now return a big ol sorted list of ((actor-id . command))
+  (append
+   (sort-commands-append-actor room-commands
+                               player-loc '()) ; room doesn't go by anything
+   (sort-commands-multi-actors co-occupant-commands)
+   (sort-commands-append-actor our-commands
+                               (actor-id player) '()))) ; nor does player
+
+(define (sort-commands-append-actor commands actor-id goes-by)
+  (sort-commands-multi-actors
+   (map (lambda (command) (list command actor-id)) commands)))
+
+(define (sort-commands-multi-actors actors-and-commands)
+  (sort
+   actors-and-commands
+   (lambda (x y)
+     (> (command-priority (cdr x))
+        (command-priority (cdr y))))))
+
+
+(define-record-type <command-winner>
+  (make-command-winner action actor-id)
+  command-winner?
+  (action command-winner-action)
+  (actor-id command-winner-action-id))
+
+
+(define (find-command-winner sorted-candidates line)
+  "Find a command winner from a sorted list of candidates"
+  ;; A cache of results from matchers we've already seen
+  (define matcher-cache '())
+  (find
+   (match-lambda
+     ((command actor-id actor-goes-by)
+      
+      (let* ((matcher (command-matcher command))
+             (matched (matcher line)))
+        (if (and matched
+                 ;; Great, it matched, but does it also pass
+                 ;; should-handle?
+                 (apply (command-should-handle command)
+                        actor-goes-by
+                        matched))  ; matched is kwargs if truthy
+            actor-id
+            #f))))
+   sorted-candidates))
index 9651140ec12d4f4f72e308a59f18ff50458d2e9d..2c3970613d0867ae298fc817cafbfc44d83ba861 100644 (file)
            #:init-value '())
 
   ;; Commands we can handle
            #:init-value '())
 
   ;; Commands we can handle
-  (dirobj-commands #:init-value '())
-  (indirobj-commands #:init-value '())
+  (commands #:init-value '())
 
   ;; Commands we can handle by being something's container
 
   ;; Commands we can handle by being something's container
-  (contain-commands #:init-value #f))
+  (contain-commands #:init-value '()))
 
 
 (define (gameobj-simple-name-f gameobj)
 
 
 (define (gameobj-simple-name-f gameobj)
index 3ad5b73fe757905cce8f12de2da968a0c05a68f8..305f43d2ca49e6f7d080f0766a181f1e8de34a04 100644 (file)
 (define-module (mudsync parser)
   #:use-module (rx irregex)
   #:use-module (ice-9 match)
 (define-module (mudsync parser)
   #:use-module (rx irregex)
   #:use-module (ice-9 match)
-  #:use-module (srfi srfi-9))
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
 
 
+  #:export (match-to-kwargs
+            split-verb-and-rest
+            article preposition
+
+            cmatch-indir-obj
+            cmatch-direct-obj
+            cmatch-direct-obj-greedy
+            cmatch-empty
+            cmatch-greedy))
 
 (define (match-to-kwargs irx string)
   (let ((rx-match (irregex-match irx string)))
     (if rx-match
 
 (define (match-to-kwargs irx string)
   (let ((rx-match (irregex-match irx string)))
     (if rx-match
-        (map (match-lambda
-               ((match-part . idx)
-                (cons match-part
-                      (irregex-match-substring
-                       rx-match idx))))
-             (irregex-match-names rx-match))
+        (fold
+         (match-lambda*
+           (((match-part . idx) prev)
+            (cons (symbol->keyword match-part)
+                  (cons (irregex-match-substring
+                         rx-match idx)
+                        prev))))
+         '()
+         (irregex-match-names rx-match))
         #f)))
 
 (define (split-verb-and-rest string)
   (let* ((trimmed (string-trim-both string))
          (first-space (string-index trimmed #\space)))
     (if first-space
         #f)))
 
 (define (split-verb-and-rest string)
   (let* ((trimmed (string-trim-both string))
          (first-space (string-index trimmed #\space)))
     (if first-space
-        `((verb . ,(substring trimmed 0 first-space))
-          (rest . ,(substring trimmed (+ 1 first-space))))
-        `((verb . ,trimmed)
-          (rest . "")))))
+        (cons (substring trimmed 0 first-space)
+              (substring trimmed (+ 1 first-space)))
+        (cons trimmed ""))))
 
 ;; @@: Not currently used
 ;; Borrowed from irregex.scm
 
 ;; @@: Not currently used
 ;; Borrowed from irregex.scm
   (sre->irregex
    `(: (? (: ,preposition (+ space)))  ; possibly a preposition (ignored)
        (? (: ,article (+ space)))      ; possibly an article (ignored)
   (sre->irregex
    `(: (? (: ,preposition (+ space)))  ; possibly a preposition (ignored)
        (? (: ,article (+ space)))      ; possibly an article (ignored)
-       (=> direct-object (* any))      ; direct object (kept)
+       (=> direct-obj (* any))      ; direct object (kept)
        (+ space)
        (=> preposition ,preposition)   ; main preposition (kept)
        (+ space)
        (? (: ,article (+ space)))      ; possibly an article (ignored)
        (+ space)
        (=> preposition ,preposition)   ; main preposition (kept)
        (+ space)
        (? (: ,article (+ space)))      ; possibly an article (ignored)
-       (=> indirect-object (+ any))))) ; indirect object (kept)
+       (=> indir-obj (+ any))))) ; indirect object (kept)
 
 
-(define (indirect-matcher phrase)
+(define (cmatch-indir-obj phrase)
   (match-to-kwargs indirect-irx phrase))
 
 (define direct-irx
   (sre->irregex
    `(: (? (: ,preposition (+ space)))  ; possibly a preposition (ignored)
        (? (: ,article (+ space)))     ; possibly an article (ignored)
   (match-to-kwargs indirect-irx phrase))
 
 (define direct-irx
   (sre->irregex
    `(: (? (: ,preposition (+ space)))  ; possibly a preposition (ignored)
        (? (: ,article (+ space)))     ; possibly an article (ignored)
-       (=> direct-object (* any)))))  ; direct object (kept)
+       (=> direct-obj (* any)))))  ; direct object (kept)
 
 
-(define (direct-matcher phrase)
+(define (cmatch-direct-obj phrase)
   (match-to-kwargs direct-irx phrase))
 
   (match-to-kwargs direct-irx phrase))
 
-(define say-example "say I really need to get going.")
-(define attack-sword-example "hit goblin with sword")
-(define attack-simple-example "hit goblin")
-(define put-book-on-desk "put the book on the desk")
+(define (cmatch-empty phrase)
+  (if (equal? (string-trim phrase) ")")
+      '()
+      #f))
 
 
-(define-record-type <command-handler>
-  (make-command-handler matcher should-handle action)
-  command-handler?
-  (matcher command-handler-matcher)
-  (should-handle command-handler-should-handle?)
-  (action command-handler-action))
+(define (cmatch-direct-obj-greedy phrase)
+  ;; Turns out this uses the same semantics as splitting verb/rest
+  (match (split-verb-and-rest string)
+    ((direct-obj . rest)
+     (list #:direct-obj direct-obj
+           #:rest rest))
+    (#f #f)))
 
 
-(define command-handler make-command-handler)
+(define (cmatch-greedy phrase)
+  `((line . ,phrase)))
 
 
+;; (define say-example "say I really need to get going.")
+;; (define attack-sword-example "hit goblin with sword")
+;; (define attack-simple-example "hit goblin")
+;; (define put-book-on-desk "put the book on the desk")
index 340fa3d7c16a0cfec98a630382ff38fcea20b4fb..fece716dbe415d45f0073dedb8e6cfa70eeaa384 100644 (file)
 ;;; along with Mudsync.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (mudsync player)
 ;;; along with Mudsync.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (mudsync player)
+  #:use-module (mudsync command)
   #:use-module (mudsync gameobj)
   #:use-module (mudsync game-master)
   #:use-module (mudsync gameobj)
   #:use-module (mudsync game-master)
+  #:use-module (mudsync parser)
   #:use-module (8sync systems actors)
   #:use-module (8sync agenda)
   #:use-module (ice-9 format)
   #:use-module (oop goops)
   #:use-module (8sync systems actors)
   #:use-module (8sync agenda)
   #:use-module (ice-9 format)
   #:use-module (oop goops)
-  #:export (<player>))
+  #:use-module (srfi srfi-1)
+  #:export (<player>
+            player-self-commands))
 
 ;;; Players
 ;;; =======
 
 ;;; Players
 ;;; =======
 
 
 (define-mhandler (player-handle-input player message input)
 
 
 (define-mhandler (player-handle-input player message input)
+  (define split-input (split-verb-and-rest input))
+  (define input-verb (pk 'input-verb (car split-input)))
+  (define input-rest (pk 'input-rest (cdr split-input)))
+
+  (define command-candidates
+    (pk 'candidates
+        (player-gather-command-handlers player input-verb)))
+
+  (define winner
+    (pk 'winner (find-command-winner command-candidates input-rest)))
+
   (<- player (gameobj-gm player) 'write-home
       #:text
       (format #f "<~a>: ~s\n"
   (<- player (gameobj-gm player) 'write-home
       #:text
       (format #f "<~a>: ~s\n"
@@ -80,3 +95,6 @@
     (format #f "**~a**\n~a\n" room-name room-desc))
 
   (<- player (gameobj-gm player) 'write-home #:text message-text))
     (format #f "**~a**\n~a\n" room-name room-desc))
 
   (<- player (gameobj-gm player) 'write-home #:text message-text))
+
+
+