[I6] Swedish Inform and parse_name

Hey all,

I’ve been following along Gareth Rees’ I6 tutorial. Since I wanted to learn Swedish Inform as well, and challenge myself a bit, I decided to try translating the game to Swedish. It was all fun and games until two kittens with parse_name decided to poke my eye out.

Include "SweAlpha";

Constant Story "ALICE I SPEGELLANDET";
Constant Headline "^En interaktiv genomgång^skriven av Gareth Rees^översatt av Björn Paulsen^";
Constant DEBUG;

Include "Parser";
Include "VerbLib";
Include "SweRout";
Include "SwedishG";

Object  Salong "Salongen"
 has    light
 with   name "snö",
        description "Det mjuka ljudet av snöflingor mot fönsterrutan
            antyder en kyla utomhus, och du gläds åt att vara här inne
            i värmen. Salongen återspeglas i den stora väggfasta spegen
            på spiselkransen, och det är sannerligen ett bekvämt rum, med
            en varm brasa, en mjuk matta, och en länsstol som du kan
            krypa ner och sova i.",
        before [;
          if (player notin spiselkrans) rfalse;
            Exit,Go:
              if (noun == d_obj or out_obj)
                "Så kommer man sig inte ner för en spiselkrans!";
            Examine,Enter,ThrowAt,ThrownAt: ;
            default:
              if (inp1 ~= 1 && noun ~= 0 && Inside(noun,spiselkrans) == 0)
                  "Du når inte ner till ", (the) noun, " från din position på spiselkransen.";
              if (inp2 ~= 1 && second ~= 0 && Inside(second,spiselkrans) == 0)
                  "Du når inte ner till ", (the) second, " från din position på spiselkransen.";
        ];

[ Inside x y;
    do {
  x = parent(x);
    } until (x == 0 or y);
    if (x == 0) rfalse;
];

Object  roed_drottning "röd drottning"
 has    female uter
 with   name 'röd' 'röda' 'drottning' 'drottningen',
        short_name_def "röda drottningen",
        description "Hon är en stursk liten schackpjäs.",
        after [;
        Take:
          if (vit_kattunge.state == QUEEN_STATE)
              vit_kattunge.state = CHAIR_STATE;
          if (vit_kattunge.state == QUEEN_STATE)
              vit_kattunge.state = CHAIR_STATE;
        ];

Object  schackbraede "schackbräde" Salong
 has    supporter
 with   name "schackbräde" "schack" "bräde" "brädet" "schackbrädet" "spel" "spelet" "spelbräde" "spelbrädet" "brädspel" "brädspelet",
        short_name_def "schackbrädet",
        initial "Ett övergivet schackbräde ligger på golvet.",
        description "Det är kvarglömt sedan partiet du spelade för att tag sedan,
            men pjäserna saknas allihopa - kattungarna insisterar om att de måste
            leka med dem.",
        has neuter;

Object  brasa "brasa" Salong
 has    scenery
 with   name "brasa" "brasan" "eld" "elden" "spis" "spisen",
        short_name_def "brasan",
        description "Under det att du betraktar brasan undrar du om de har
            en spis i huset i spegellandet. Det är svårt att avgöra genom
            att se efter, såvida din eld inte ryker, och då stiger röken
            i spegellandsrummet också - men det kanske bara är lurendrejeri,
            bara för att det ska se ut som om de har en eld.";

Object  matta "matta" Salong
 has    concealed static supporter enterable
        ! general if you found the red queen under it
 with   name "matta" "mattan" "brasmatta" "brasmattan" "spismatta" "spismattan"
            "arabisk" "arabiska" "vacker" "vackra" "mjuk" "mjuka",
        short_name_def "mattan",
        description "Det är en vacker matta från något avlägset land,
            kanske Indien eller Arabien, varän dessa nu befinner sig.",
        before [;
         Take: "Mattan är alltför stor och tung för att du ska kunna bära den.";
         Push,Pull: "Men meningen med en brasmatta är ju att den ska ligga
            framför brasan!";
         LookUnder:
            if (player in spiselkrans || player in laensstol)
              "Du når inte mattan härifrån.";
            if (player in self)
              "Du försöker lyfta ett av mattans hörn, men misslyckas. Efter att
              tag inser du att detta är för att du står på den. Så märklig världen
              kan vara!";
            if (self hasnt general) {
              give self general;
              move roed_drottning to player;
              "Du lyfter upp ett av mattans hörn för att kika under den, varvid du
              upptäcker den röda drottningen från schackbrädet.";
            }
         ];

Class   Kattunge
 has    animate
 with   state CHAIR_STATE,
        name "katt" "katten" "kattunge" "kattungen" "kisse" "kissen" "misse" "missen",
        short_name_def "kattungen",
        description [;
            "Vilken söt kattunge den ", (the) self, " är. Den är verkligen
             avgjort din favorit av de båda, and mycket sötare än den
             där stygga ",
             (the) self.other_kitten, ".";
        ],
        before [;
         Take:
            if (self.other_kitten in player)
                "Du kan inte bära två kattungar samtidigt!";
            self.state = HELD_STATE;
            move self to player;
           "Du lyfter upp den ", (the) self, ". En sådan vacker liten
            varelse den är!";
        ],
        after [;
         Drop:
            self.state = CHAIR_STATE;
            move self to Salong;
            print_ret "Den ", (the) self, " kravlar sig ut ur din famn och strosar bort.";
         Transfer,PutOn,Insert:
            self.state = CHAIR_STATE;
            print (The) self, " hoppar ner från ", (the) parent(self);
            move self to Salong;
            " och landar lätt på golvet innan den strosar bort.";
        ],
        parse_name [ w ok n;
            do {
                ok = 0;
                w = NextWord();
                if (w == 'kattungarna' or 'kattungar' or 'katter' or 'katterna' or 'kissar' or 'kissarna' or 'missar' or 'missarna') {
                    ok = 1; n++; parser_action=##PluralFound;
                }
                if (w == 'kattunge' or 'kattungen' or 'katt' or 'katten' or 'kisse' or 'kissen' or 'misse' or 'missen' ||
                    w == ((self.&name)-->0)) {
                    ok = 1; n++;
                }
            } until (ok == 0);
            return n;
        ],
        describe [ i;
            switch (self.state) {
             QUEEN_STATE:
                "^En ", (name) self, " leker med den röda drottningen.";
             WOOL_STATE:
                "^En ", (name) self, " leker med bollen av kamgarn.";
             CHAIR_STATE:
                if (self has general) rtrue;
                if ((self.other_kitten).state == CHAIR_STATE) {
                    i = self.other_kitten;
                    give i general;
                    "^Två kattungar, en vit och en svart, leker tillsammans
                    invid länsstolen.";
                }
                "^En ", (name) self, " leker invid länsstolen.";
             default: rtrue;
            }
        ],
        daemon [;
            give self ~general;
        ];

Kattunge vit_kattunge "vit kattunge" Salong
 with   name "vit" "vita",
        short_name_def "vita kattungen",
        this_kittens_turn false,
        other_kitten svart_kattunge;

Kattunge svart_kattunge "svart kattunge" Salong
 with   name "svart" "svarta",
        short_name_def "svarta kattungen",
        this_kittens_turn true,
        other_kitten vit_kattunge;

Object   laensstol "länsstol" Salong
 has     static concealed supporter enterable
         ! general om den är vid spiselkransen
 with    name "länsstol" "länsstolen" "stol" "stolen" "fåtölj" "fåtöljen",
         description "Länsstolen är väldig, en idealisk plats för en kattunge
              eller en liten flicka att kura ihop sig för en tupplur.",
         short_name_def "länsstolen",
         before [ i;
          Climb,Enter:
            if (player in laensstol)
              "Du är redan i länsstolen!";
            print "Du hoppar ";
            if (player notin spiselkrans)
              print "upp";
            else
              print "ned";
            move player to laensstol;
            " i den varma och bekväma länsstolen.";
          Push,Pull:
            if (vit_kattunge in player || svart_kattunge in player)
                "Inte med en kattunge i famnen!";
            if (vit_kattunge.state == CHAIR_STATE) i = vit_kattunge;
            else if (svart_kattunge.state == CHAIR_STATE) i = svart_kattunge;
            if (i ~= 0)
                "Du är just i färd med att börja flytta stolen när du upptäcker
                 att ", (the) i, " är i dess väg. Tur att du upptäckte det, annars
                 hade du mosat den lilla krabaten rakt av.";
            if (player notin Salong)
                "Du måste stiga ur ", (the) parent(player),
                    " först.";
            if (self has general) {
               give self ~general;
               "Du knuffar bort länsstolen från spisen.";
            }
            give self general;
            "Du knuffar fram länsstolen intill spisen.";
         ];

Object   spiselkrans "spiselkrans" Salong
 has     static concealed supporter enterable
 with    name "spiselkrans" "spiselkransen" "hylla" "hyllan" "spishylla" "spishyllan",
         description "Den är högre upp från golvet än ditt huvud, men ser bred
              och stadig nog ut att hålla din vikt.",
         short_name_def "spiselkransen",
         before [;
           Enter,Climb:
             if (player notin laensstol)
                "Spiselkransen är alltför högt upp för dig att klättra.";
             if (laensstol hasnt general)
                "Du kan inte nå spiselkransen härifrån.";
             if (children(player) > 0)
                "Dina händer är alltför fulla.";
             move player to spiselkrans;
             "Du klättrar upp på spiselkransen.";
           PutOn,LetGo:
             if (player notin self && (player notin laensstol || laensstol hasnt general))
               "Spiselkransen är så högt upp att du inte når.";
         ];

Object   spegel "spegel" Salong
 has     static concealed
 with    name "spegel" "spegeln",
         description [;
            if (player in spiselkrans)
              "Märkligt nog börjar glaset smälta undan, precis some en ljus silverne dimma.";
            if (player in laensstol)
              "Inuti spegeln kan du se salongen i spegellandets hus. Det du ser är
              i mångt och mycket detsamma som den här salongen, men allt är omvänt,
              från vänster till höger. Men du är säker på att i glasets djupaste
              vinkel, dit du inte kan se, är spegellandets värld mycket annorlunda
              från din egen.";
            "I spegeln kan du se taket till salongen i spegellandets hus. Det ser
            i mångt och mycket ut som taket till din egen salong.";
         ],
         short_name_def "spegeln",
         before [;
           if (action ~= ##Examine && player notin spiselkrans)
              "Du når inte spegeln från där du står.";
           Touch,Pull,Push:
             "Din hand passerar rakt genom den silverne dimman!";
           ThrownAt:
             "Du vill väl inte drabbas av sju års olycka?";
           Enter:
             ! Really, move Alice to the looking-glass house.
             deadflag = 2;
             "Din hand går rakt genom den silverne dimman, och i nästa ögonblick följer
             resten av dig efter, och du är genom glaset.";
         ];

Verb "nysta" "rulla" "räta"
    * noun -> Untangle
    * noun "ut" -> Untangle
    * noun "upp" -> Untangle;

[ UntangleSub; "Så underliga påfund du har!"; ];

Object   kamgarn "boll av kamgarn" Salong
         ! general if it's in a tangle
 with    name "boll" "bollen" "av" "kamgarn" "kamgarnet" "garn" "garnet" "tråd" "tråden" "fin" "fint" 
"blå" "blått" "ull" "ullen" "ylle" "yllen" "nystan" "nystanet" "ylletråd" "ulltråd" "garnnystan" "garnnystanet",
         short_name_def "bollen av kamgarn",
         describe [;
            if (svart_kattunge.state ~= WOOL_STATE &&
                svart_kattunge.state ~= WOOL_STATE)
                "^En kvarglömd rulle kamgarn ligger på golvet här.";
            rtrue;
         ],
         description [;
           if (self has general)
              "Den är fruktansvärt ihoptrasslad. All den där tiden du
              tillbringade med att rulla ihop den, och se nu på den!";
           "Det är en boll av fin blå ylletråd, hoprullad i förberedelse
           för lite broderi.";
         ],
         before [;
           Untangle:
              give self ~general;
              "Du är kvick som ögat på att rulla ihop garnnystan, om
              du får säga det själv! Snart är den ordnad och prydlig igen.";
         ];

Constant HELD_STATE = 0;     ! Being held
Constant QUEEN_STATE = 1;    ! Playing with the Red Queen
Constant WOOL_STATE = 2;     ! Playing with the worsted
Constant CHAIR_STATE = 3;    ! In the way of the chair

[Initialise ;
  location=Salong;
  "^^^^^Utomhus råder en kall vinterdag, men i spegellandets hus
        är det sommar. Allt du behöver göra är låtsas att det skulle finnas
        ett sätt att på något vis ta dig in genom det...^^";
];

We can deduce two things: one, I won’t be winning any translation contests any time soon (it’s a draft, honest), and two, the parse_name routine used to let the player refer to the kittens as a plural invalidates the definite form of either kitten (“vita” viz “svarta”).

So that’s a problem for me. How should I write a parse_name routine that would allow the game to parse either correctly, so that the player can write “x den vita kattungen” as expected?

Is this just a word length thing? Your singular and plural forms both start with “kattun” and by default only the first six letters are applicable.

Not really, since merely typing “x den vita” also stops working on adding the parse_name routine.

Ah, I see. The Alice example was hard-coded to test only the first token in the parse_name routine from each instance of the class. Since the English version simply used “black” or “white”, that was fine. But a Swedish game usually needs two versions of the word, those being the indefinite and definite form.

Replacing it with a WordInProperty call works, although I guess it could be a bit less efficient if it loops over both the tokens for the class and the object.