Changeset 252

Show
Ignore:
Timestamp:
01/27/06 15:07:23 (3 years ago)
Author:
ged
Message:

- Hooked up some of the volition stuff in Spirit.
- Added beginnings of the description-generation system.

Location:
trunk
Files:
4 added
11 modified

Legend:

Unmodified
Added
Removed
  • trunk/lib/fm/commandparser.rb

    r219 r252  
    5656 
    5757 
     58    ### Return the set of FaerieMUD::Verb objects this parser knows about 
     59    def verbs 
     60        return @verbset.values.uniq 
     61    end 
     62 
     63 
    5864    ### Update the parser's verb set from the given +verbs+. 
    5965    def updateVerbSet( *verbs ) 
     
    8086 
    8187 
     88     
     89 
    8290end # class FaerieMUD::CommandParser 
    8391 
  • trunk/lib/fm/entity.rb

    r251 r252  
    130130 
    131131 
     132    # 
     133    #   Linguistics support 
     134    # 
     135 
     136    ### Returns +true+ if the entity should be considered plural for the 
     137    ### purposes of generating english text. 
     138    def plural? 
     139        return false 
     140    end 
     141 
     142 
    132143 
    133144    ######### 
  • trunk/lib/fm/gameobject.rb

    r250 r252  
    154154 
    155155 
     156    ### Create an example instance of the receiving class. This is used to ease 
     157    ### the complexity of creating example objects for experimentation. This 
     158    ### should be overridden by object classes which require instantiation 
     159    ### arguments. 
     160    def self::example( args={} ) 
     161        self::new( args ) 
     162    end 
     163 
     164 
    156165    ############################################################# 
    157166    ### I N S T A N C E   M E T H O D S 
  • trunk/lib/fm/linguistics.rb

    r251 r252  
    9797        include FaerieMUD::Linguistics 
    9898 
    99         ### Create the sentence with the given +subject+, +verb+, and 
    100         ### optional +direct_object+. 
    101         def initialize( subject, verb, direct_object=nil, *prep_phrases ) 
    102             @subject = subject 
    103             @verb = verb 
    104             @direct_object = direct_object 
    105             @prep_phrases = prep_phrases 
     99        ### Create the sentence. Can be constructed either with an explicit 
     100        ### subject, verb, and optional direct object, or with a 
     101        ### FaerieMUD::Event that will provide these parts. 
     102        def initialize( *args ) 
     103            if args.length == 1 
     104                init_from_event( args.first ) 
     105            else 
     106                init_from_parts( *args ) 
     107            end 
    106108        end 
    107109 
    108         attr_reader :subject, :verb, :direct_object 
     110 
     111        # The subject of the sentence (a FaerieMUD::GameObject object) 
     112        attr_reader :subject 
     113 
     114        # The FaerieMUD;;Verb instance associated with the sentence 
     115        attr_reader :verb 
     116 
     117        # The FaerieMUD::GameObject which is the recipient of the action in the 
     118        # sentence, if any. 
     119        attr_reader :direct_object 
    109120 
    110121 
     
    113124            parts = [ 
    114125                makeNounPhrase( @subject ), 
    115                 conjugateVerb( @verb, @subject ) 
     126                @verb.inflect_for( @subject ) 
    116127            ] 
    117128 
    118129            parts << makeNounPhrase( @direct_object ) if @direct_object 
    119             parts.push( *(@prep_phrases.flatten.compact) ) 
    120130 
    121131            return parts.join( " " ) + "." 
    122132        end 
     133 
     134 
     135        ######### 
     136        protected 
     137        ######### 
     138 
     139        ### Set the members of the sentence from the parts of the given +event+ 
     140        ### (a FaerieMUD::Event object). 
     141        def init_from_event( event ) 
     142            init_from_parts( event.instigator, event.verb, event.target ) 
     143        end 
     144 
     145        ### Set the members of the sentence from the given +subject+, +verb+, 
     146        ### and +direct_object+. 
     147        def init_from_parts( subject, verb, direct_object=nil ) 
     148            @subject = subject 
     149            @verb = verb 
     150            @direct_object = direct_object 
     151        end 
     152 
     153 
    123154    end 
    124155 
  • trunk/lib/fm/perception.rb

    r251 r252  
    4949 
    5050    ### Create a new Perception for the given +perceiver+, which will be the 
    51     ### deleated describer of any PerceptualEvents which are handled. 
     51    ### delegated describer of any PerceptualEvents which are handled. 
    5252    def initialize( perceiver, args={} ) 
    5353        @perceiver = perceiver 
  • trunk/lib/fm/spirit.rb

    r250 r252  
    120120    ### Create a new FaerieMUD::Spirit object. 
    121121    def initialize( args={} ) 
    122         @animatedObjects = [] 
     122        @animatedObject = nil 
    123123        @parser = FaerieMUD::CommandParser::new 
    124124 
     
    134134        } 
    135135 
     136        @inputThread = nil 
     137        @running = false 
     138 
    136139        super 
    137140    end 
     
    145148    attr_locked_reader :parser 
    146149 
    147     # The AnimatedObjects controlled by this Spirit. 
    148     attr_locked_reader :animatedObjects 
     150    # The AnimatedObject controlled by this Spirit. 
     151    attr_locked_reader :animatedObject 
    149152 
    150153    # IO Callbacks 
     
    173176        self.writelocked do 
    174177            object.controller = self 
    175             @animatedObjects += [ object ] 
    176         end 
    177  
    178         self.updateParser 
     178            @animatedObject = object 
     179            self.updateParser 
     180        end 
    179181    end 
    180182 
     
    182184    ### Remove the specified +object+ (FaerieMUD::AnimatedObject) from the 
    183185    ### list being controlled by the Spirit. 
    184     def deanimate( object ) 
     186    def deanimate 
     187        return if @animatedObject.nil? 
     188 
    185189        self.writelocked do 
    186             object.controller = nil 
    187             @animatedObjects -= [ object ] 
     190            @animatedObject.controller = nil 
     191            @animatedObject = nil 
    188192        end 
    189193    end 
     
    194198    ### currently-animated objects. 
    195199    def updateParser 
    196         verbs = @animatedObjects.collect {|aobj| aobj.verbs}.flatten 
     200        verbs = @animatedObject.verbs 
    197201        @parser.updateVerbSet( *verbs ) 
    198202    end 
     
    204208    end 
    205209 
     210 
     211    ######### 
     212    protected 
     213    ######### 
     214 
     215    ### Start the input loop of the spirit to fetch use input. 
     216    def start 
     217        @inputThread = Thread::new( &inputLoop ) 
     218    end 
     219 
     220 
     221    ### Wait for input from the Player and pass it to the CommandParser for 
     222    ### action. 
     223    def inputLoop 
     224        Thread.current.abort_on_exception = true 
     225 
     226        @running = true 
     227        while @running 
     228            rawInput = @ioHandlers[:input].call or next 
     229 
     230             
     231        end 
     232 
     233    end 
     234 
     235 
     236 
    206237end # class FaerieMUD::Spirit 
    207238 
  • trunk/lib/fm/verb.rb

    r217 r252  
    11#!/usr/bin/ruby 
    22#  
    3 # This file contains the FaerieMUD::Verb class, a derivative of 
    4 # FaerieMUD::GameObject. Verbs are objects which are used to generate game 
    5 # events. They react to stimulus in the form of Events, timers, or other action, 
    6 # generating events which should be propagated by the instigating object. 
     3# This file contains the FaerieMUD::Verb class. Verbs are objects which are used 
     4# to generate game events. They react to stimulus in the form of Events, 
     5# Activity timers, or other actions, generating events which should be propagated 
     6# by the instigating object. 
    77#  
    88# == Subversion ID 
     
    2828 
    2929 
    30 ### Game verb class  
     30### Game verb class. 
    3131class FaerieMUD::Verb < FaerieMUD::GameObject 
    3232    contributors :ged 
     
    5656 
    5757    ### Set up the verb's internal structure 
    58     def initialize # :notnew: 
     58    def initialize( args={} ) # :notnew: 
    5959        super 
    60         @words = self.class.generateWords() 
     60        @words ||= self.class.generateWords() 
    6161    end 
    6262 
     
    6666    ###### 
    6767 
    68     ### CommandParser interface: query the verb for the word/s which cause it to 
    69     ### be invoked. By default this returns the name of the verb class with the 
    70     ### 'verb' part removed. 
     68    ### CommandParser interface: query the verb for the word/s which cause 
     69    ### it to be invoked. By default this returns the name of the verb class 
     70    ### with the 'verb' part removed. 
    7171    attr_reader :words 
    7272 
    7373 
     74    ### Return the primary English word for this noun. 
     75    def word 
     76        @words.first 
     77    end 
     78 
     79 
    7480    ### Event interface: "run" the verb, generating the event/s necessary to 
    75     ### express the action in the game world. The given +instigator+, +origin+, 
    76     ### and +target+ will be used when building event/s. See 
     81    ### express the action in the game world. The given +instigator+, 
     82    ### +origin+, and +target+ will be used when building event/s. See 
    7783    ### FaerieMUD::Event::new for definitions 
    78     def invoke( instigator, origin, target=nil ) 
     84    def invoke( instigator, origin=nil, target=nil ) 
    7985        raise NotImplementedError, 
    8086            "Required method #invoke not implemented in %s" % 
    81             [self.class.name] 
     87                [self.class.name] 
     88    end 
     89 
     90 
     91    ### Return the Verb's word inflected for the given +subject+, which should 
     92    ### be an object that responds to #plural?. 
     93    def inflect_for( subject ) 
     94        word = self.words.first 
     95        return "#{self.word}s" unless subject.plural? 
     96        return word 
    8297    end 
    8398 
  • trunk/tests/commandparser.tests.rb

    r212 r252  
    44# $Id$ 
    55# 
    6 # Copyright (c) 2003, 2004, 2005 The FaerieMUD Consortium. 
     6# Copyright (c) 2003-2006 The FaerieMUD Consortium. 
    77#  
    88 
     
    1919 
    2020require 'fm/commandparser' 
     21require 'fm/verbs/all' 
    2122 
    2223### This test suite tests the CommandParser of FaerieMUD. 
     
    3031 
    3132 
     33    def setup 
     34        @parser = FaerieMUD::CommandParser::new 
     35    end 
     36 
    3237 
    3338    ################################################################# 
     
    3540    ################################################################# 
    3641 
    37     ### Instance test 
    38     def test_Instance 
    39         printTestHeader "CommandParser: Instantiation" 
    40         rval = nil 
    41  
    42         assert_instance_of Class, FaerieMUD::CommandParser 
    43         assert_nothing_raised { 
    44             rval = FaerieMUD::CommandParser::new 
    45         } 
    46         assert_instance_of FaerieMUD::CommandParser, rval 
    47     end 
    48  
    49  
    5042    ### Parser should accept a list of verbs from which to derive its command 
    5143    ### table. 
    52     def test_UpdateVerbsShouldUpdateVerbset 
    53         printTestHeader "CommandParser: Updating the parser's verb list should " + 
    54             "update its verbset" 
    55         parser = FaerieMUD::CommandParser::new 
     44    def test_parser_should_update_its_command_table_from_given_verbs 
    5645        verb = MockVerb::new 
    5746 
     
    6251 
    6352        # Now add the verb to the parser 
    64         assert parser.verbset.empty?, "Parser's verbset should be empty initially" 
     53        assert @parser.verbset.empty?, "Parser's verbset should be empty initially" 
    6554        assert_nothing_raised do 
    66             parser.updateVerbSet( verb ) 
     55            @parser.updateVerbSet( verb ) 
    6756        end 
    6857 
    6958        # Verify that everything went as planned. 
    7059        verb.verify 
    71         assert !parser.verbset.empty?, 
    72             "Parser's verbset should not be empty after updating" 
    73         assert parser.verbset.key?("test"), 
     60        assert @parser.verbset.key?("test"), 
    7461            "Parser should have the 'test' verb after updating" 
    75         assert parser.verbset.key?("try"), 
     62        assert @parser.verbset.key?("try"), 
    7663            "Parser should have the 'try' verb after updating" 
    7764    end 
     
    8067    ### Parser should raise an exception if two verbs try to register the same 
    8168    ### command. 
    82     def test_UpdateVerbsShouldRaiseOnCollision 
    83         printTestHeader "CommandParser: Updating the parser's verb list should " + 
    84             "raise an exception for command collisions" 
    85         parser = FaerieMUD::CommandParser::new 
     69    def test_parser_should_raise_an_exception_if_two_verbs_are_registered_with_the_same_command 
    8670         
    8771        # Make two mock verbs with colliding wordsets 
    88         verb1 = MockVerb::new 
    89         verb1.setReturnValues( :words => [%w[test try]], :eql? => false ) 
    90         verb2 = MockVerb::new 
    91         verb2.setReturnValues( :words => [%w[trim try]], :eql? => false ) 
     72        verb1 = Class::new( FaerieMUD::SimpleVerb ) do 
     73            set_words %w[test try] 
     74        end.instance 
     75        verb2 = Class::new( FaerieMUD::SimpleVerb ) do 
     76            set_words %w[trim try] 
     77        end.instance 
    9278 
    9379        assert_raises( FaerieMUD::CommandCollisionError ) do 
    94             parser.updateVerbSet( verb1, verb2 ) 
     80            @parser.updateVerbSet( verb1, verb2 ) 
    9581        end 
    9682    end 
    9783 
    9884 
    99     ### Parser should ignore verbs it already has 
    100     def test_UpdateVerbsShouldIgnoreDuplicates 
    101         printTestHeader "CommandParser: Updating the parser's verb list should " + 
    102             "ignore words it already has" 
    103  
    104         parser = FaerieMUD::CommandParser::new 
    105          
    106         verb1 = MockVerb::new 
    107         verb1.setReturnValues( :words => [%w[test try]] ) 
     85    ### Parser should ignore verbs it already knows about 
     86    def test_parser_should_ignore_verbs_it_already_knows_about_when_updating_its_verb_list 
     87        verb = Class::new( FaerieMUD::SimpleVerb ) do 
     88            set_words %w[test try] 
     89        end.instance 
    10890 
    10991        # Add it once 
    11092        assert_nothing_raised do 
    111             parser.updateVerbSet( verb1 ) 
     93            @parser.updateVerbSet( verb ) 
    11294        end 
     95 
     96        # The parser's verbset should now contain the added verb 
     97        assert_equal 1, @parser.verbs.length 
    11398 
    11499        # Add it again, which shouldn't fail 
    115100        assert_nothing_raised do 
    116             parser.updateVerbSet( verb1 ) 
     101            @parser.updateVerbSet( verb ) 
    117102        end 
    118          
     103 
     104        # The parser's verbset shouldn't contain more than one verb still 
     105        assert_equal 1, @parser.verbs.length, 
     106            "Verbset expected to only contain one verb (%p)" % [@parser.verbset] 
    119107    end 
    120108end 
  • trunk/tests/linguistics.tests.rb

    r250 r252  
    44# $Id$ 
    55# 
    6 # Copyright (c) 2005 The FaerieMUD Consortium. 
     6# Copyright (c) 2005, 2006 The FaerieMUD Consortium. 
    77#  
    88 
     
    1919 
    2020require 'fm/linguistics' 
     21require 'fm/verbs/simple' 
    2122 
    2223 
     
    2425class FaerieMUD::LinguisticsTestCase < FaerieMUD::TestCase 
    2526 
     27    # For testing mixin functions 
    2628    class ThingerieThing 
    2729        include FaerieMUD::Linguistics 
    2830    end 
    2931 
     32    # For testing sentence construction 
    3033    class TestingEntity < FaerieMUD::Entity; end 
     34    class DriveEvent < FaerieMUD::Event; end 
     35    class DriveVerb < FaerieMUD::SimpleVerb 
     36        set_event_class DriveEvent 
     37    end 
    3138 
    3239 
     40    ### Setup/teardown 
    3341    def setup 
    3442        @obj = ThingerieThing.new 
    3543        @entity = TestingEntity.new 
     44        @verb = DriveVerb.instance 
    3645    end 
    3746 
     
    8089 
    8190 
     91    def test_sentence_created_with_subjectverb_event_creates_readable_text 
     92        rval = nil 
     93        event = @verb.invoke( @entity ) 
     94        sentence = FaerieMUD::Linguistics::Sentence.new( event ) 
     95 
     96        assert_nothing_raised do 
     97            rval = sentence.to_s 
     98        end 
     99 
     100        assert_equal "a testing entity drives.", rval 
     101    end 
     102 
    82103end 
    83104 
  • trunk/tests/spirit.tests.rb

    r235 r252  
    44# $Id$ 
    55# 
    6 # Copyright (c) 2003, 2004, 2005 The FaerieMUD Consortium. 
     6# Copyright (c) 2003-2006 The FaerieMUD Consortium. 
    77#  
    88 
     
    1818end 
    1919 
     20require 'flexmock' 
    2021require 'fm/spirit' 
    2122require 'fm/animatedobject' 
     
    2425class FaerieMUD::SpiritTestCase < FaerieMUD::TestCase 
    2526 
    26     # Mock the classes that interact with the Spirit so it can be tested inside- 
    27     # out. 
    28     class MockAnimatedObject < Test::Unit::MockObject( FaerieMUD::AnimatedObject ); end 
    29  
    3027 
    3128    ### Setup methods (aliased for older versions of Test::Unit) 
    3229    def setup  
    33         @testAnimObj = MockAnimatedObject::new 
     30        @spirit = FaerieMUD::Spirit.new 
    3431        super 
    3532    end 
    36     alias_method :set_up, :setup 
    37  
    38     ### Teardown methods (aliased for older versions of Test::Unit) 
    39     def teardown  
    40         @testAnimObj = nil 
    41         super 
    42     end 
    43     alias_method :tear_down, :teardown 
    4433 
    4534 
     
    4837    ################################################################# 
    4938 
    50     ### Instance test 
    51     def test_00_Instance 
     39    def test_spirit_objects_should_have_aspects_of_immortality 
    5240        rval = nil 
    53         printTestHeader "Spirit: Instantiation" 
    54  
    55         assert_instance_of Class, FaerieMUD::Spirit 
    56  
    57         # Test with a valid argument list 
    58         assert_nothing_raised { 
    59             rval = FaerieMUD::Spirit::new 
    60         } 
    61         assert_instance_of FaerieMUD::Spirit, rval 
    62  
    63         # Make Spirit objects for each further test 
    64         self.class.addSetupBlock { 
    65             @testSpirit = FaerieMUD::Spirit::new 
    66         } 
    67         self.class.addTeardownBlock { 
    68             @testSpirit = nil 
    69         } 
    70     end 
    71  
    72  
    73     ### Test the aspects of immortality (proxies for constituents of animated 
    74     ### objects) 
    75     def test_10_AspectsOfImmortality 
    76         rval = nil 
    77         printTestHeader "Spirit: Aspects of Immortality" 
    7841 
    7942        # Aspect accessors (ComposedObject::Constituents) 
    8043        %w{physical mental creative}.each do |atype| 
    8144            meth = atype 
    82             assert_respond_to @testSpirit, meth 
    83             assert_nothing_raised { rval = @testSpirit.send(meth) } 
     45            assert_respond_to @spirit, meth 
     46            assert_nothing_raised { rval = @spirit.send(meth) } 
    8447            assert_kind_of FaerieMUD::ComposedObject::Constituent, rval 
    8548        end 
     
    8750        # Statistic accessors -- developmental 
    8851        %w{incarnativity kenning divinity}.each do |aspect| 
    89             assert_respond_to @testSpirit, aspect 
    90             assert_nothing_raised { rval = @testSpirit.send(aspect) } 
     52            assert_respond_to @spirit, aspect 
     53            assert_nothing_raised { rval = @spirit.send(aspect) } 
    9154            assert_instance_of FaerieMUD::DevelopmentalObject, rval 
    9255        end  
     
    9457        # Statistic accessors -- linear 
    9558        %w{presence cognizance influence}.each do |aspect| 
    96             assert_respond_to @testSpirit, aspect.intern 
    97             assert_nothing_raised { rval = @testSpirit.send(aspect) } 
     59            assert_respond_to @spirit, aspect.intern 
     60            assert_nothing_raised { rval = @spirit.send(aspect) } 
    9861            assert_instance_of Fixnum, rval 
    9962        end  
    10063    end 
     64 
     65    def test_animating_should_set_the_animated_objects_controller 
     66        FlexMock.use( "animated object" ) do |mockAnimObj| 
     67            mockAnimObj = FlexMock.new 
     68            mockAnimObj.should_receive( :controller= ).with( @spirit ) 
     69            mockAnimObj.should_receive( :verbs ).returns( [] ) 
     70 
     71            assert_nothing_raised do 
     72                @spirit.animate( mockAnimObj ) 
     73            end 
     74        end 
     75    end 
     76 
     77    def test_should_unset_previous_animated_objects_controller_when_animating_a_new_object 
     78        FlexMock.use( "original animated object", "second animated object" ) do |mockAnimObj, mockAnimObj2| 
     79            mockAnimObj = FlexMock.new 
     80            mockAnimObj.should_receive( :controller= ).with( @spirit ) 
     81            mockAnimObj.should_receive( :verbs ).returns( [] ) 
     82            mockAnimObj.should_receive( :controller= ).with( nil ) 
     83 
     84            mockAnimObj2 = FlexMock.new 
     85            mockAnimObj2.should_receive( :controller= ).with( @spirit ) 
     86            mockAnimObj2.should_receive( :verbs ).returns( [] ) 
     87 
     88            assert_nothing_raised do 
     89                @spirit.animate( mockAnimObj ) 
     90                @spirit.animate( mockAnimObj2 ) 
     91            end 
     92        end 
     93 
     94    end 
     95 
     96 
    10197end 
    10298 
  • trunk/tests/verb.tests.rb