Up to Main Index                           Up to Journal for October, 2022

                     JOURNAL FOR MONDAY 3RD OCTOBER, 2022
______________________________________________________________________________

SUBJECT: The Cottage — Mere edition (terrible idea)
   DATE: Mon  3 Oct 20:22:26 BST 2022

For myself a programming language should meet two criteria. It should allow
programmers to write meaningful programs and it should let the programmer
express themselves clearly — the code should be readable and writable. There
are other important criteria such as being self consistent and efficient, but
these are secondary concerns.

I thought I’d apply my criteria, and test out Mere’s new map implementation,
by trying to write something meaningful in Mere. I decided to write a Mere
version of “The Cottage”[1]. It did not go well…

The program runs and does what it is supposed to do. Here is a brief example:


    >bin/mere theCottage

        Welcome to the cottage!

    For a list of commands type h or help.

    [ Path outside a cottage ]
    You are on a path leading north to a small, white cottage.

    You see exits: north, south and west
    ? help
    Available commands:

        n or north - move north
        s or south - move south
         e or east - move east
         w or west - move west
            sneeze - makes you sneeze
      x or examine - examine an item
         l or look - describe location again
         h or help - display this help
         q or quit - exit the game

    ? west
    [ Front lawn ]
    You are standing on a small, neat lawn outside the cottage.

    You see a green frog here.
    You see a red ball here.
    You see a red frog here.

    You see exits: east and north
    ? examine red
    You examine a red ball.
    This is a small, red child's ball.
    ? examine ball
    You examine a red ball.
    This is a small, red child's ball.
    ? examine red frog
    You examine a red frog.
    This is a small, red frog.
    ? east
    [ Path outside a cottage ]
    You are on a path leading north to a small, white cottage.

    You see exits: north, south and west
    ? north
    [ Front door ]
    You are standing at the open front door of a small, white cottage.

    You see the front door here.
    You see a doormat here.

    You see exits: north and south
    ? quit

    Bye bye!

    >


I guess in a way that is success. However, the code is terrible. Writing the
code is a bit of a nightmare for three reasons: everything is global, there
are no scopes and there are no user defined functions. Having proper for loops
would help make the code cleaner and clearer — but, no scopes. All things I
need to work on implementing.

Ok, maybe I’m being too harsh on myself. Reimplementing “The Cottage” at this
stage in Mere’s development was probably a little too ambitious :(

For the morbid, I’ve added the code to the end of this post. NOTE THAT IT WILL
NOT RUN IN MERE ICE. I have not implemented the input built-in yet — and I’m
still not too sure on how I’m going to do that — and Mere/Mere ICE do not have
the work on maps included yet. Note also that everything is in one file with
the data hard-coded, just like the original implementation.

Astute readers will notice that the ‘Locs’ data structure, in the code below,
extends over multiple lines. Implementing maps while having everything crammed
onto a single line was becoming too painful. The fix was a minor tweak to the
tokenizer. Lines ending with a left parenthesis or a comma now continue to the
next line.

I hope to be updating Mere and Mere ICE with the latest map additions soon.

One more pain point I, I keep trying to gofmt my Mere source code XD

--
Diddymus

  [1] Original “The Cottage”: ../../2019/5/cottage.html




/*

The Cottage - Mere edition

Copyright 2022, Andrew 'Diddymus' Rolfe. All rights reserved.

The cottage is a small, simple, single player, plain text, 'toy' adventure
game. It is intended to be used for learning, experimenting, general hacking
and having fun while doing so.

*/

// Exits lookup map for directions
Exits:[string] = [string] (
  "n" "north",
  "s" "south",
  "e" "east",
  "w" "west",
)

// Locs map for location data.
Locs:[string] = [string](
  "path" [string](
    "title" "Path outside a cottage",
    "text" "You are on a path leading north to a small, white cottage.",
    "exits" [string]("n" "frontdoor", "s" "", "w" "frontlawn"),
    "inv" [string](
      "path" [string](
        "alias" []string("path"),
        "name" "a path",
        "text" "The path is made of small, neat paving stones.",
        "narrative" true,
      ),
      "paving" [string](
        "alias" []string("paving", "stones"),
        "name" "some paving stones",
        "text" "The paving stones are a foot square and grey in colour.",
        "narrative" true,
      ),
      "cottage" [string](
        "alias" []string("cottage"),
        "name" "a white cottage",
        "text" "From here the cottage seems to be very inviting.",
        "narrative" true,
      ),
    ),
  ),
  "frontlawn" [string](
    "title" "Front lawn",
    "text" "You are standing on a small, neat lawn outside the cottage.",
    "exits" [string]("n" "sidepath", "e" "path"),
    "inv" [string](
      "greenfrog" [string](
        "alias" []string("green" "frog"),
        "name" "a green frog",
        "text" "This is a small, green frog.",
      ),
      "redfrog" [string](
        "alias" []string("red" "frog"),
        "name" "a red frog",
        "text" "This is a small, red frog.",
      ),
      "redball" [string](
        "alias" []string("red" "ball"),
        "name" "a red ball",
        "text" "This is a small, red child's ball.",
      ),
    ),
  ),
  "frontdoor" [string](
    "title" "Front door",
    "text" "You are standing at the open front door of a small, white cottage.",
    "exits" [string]("n" "southhall", "s" "path"),
    "inv" [string](
      "door" [string](
        "alias" []string("front" "door"),
        "name" "the front door",
        "text" "This is a sturdy, wooden front door. Someone has painted it black.",
      ),
      "doormat" [string](
        "alias" []string("door" "mat", "doormat"),
        "name" "a doormat",
        "text" "This is a brown, bristly doormat. It has \"welcome\" written on it.",
      ),
    ),
  ),
  "southhall" [string](
    "title" "Hallway",
    "text" "You are in a hallway by the front door.",
    "exits" [string]("n" "northhall", "e" "kitchen", "s" "frontdoor", "w" "study"),
  ),
  "northhall" [string](
    "title" "Hallway",
    "text" "You are in a hallway. Some pictures hang on the wall.",
    "exits" [string]("n" "bathroom", "s" "southhall", "w" "bedroom"),
  ),
  "kitchen" [string](
    "title" "Kitchen",
    "text" "You are standing in a small, very tidy kitchen.",
    "exits" [string]("n" "backlawn", "w" "southhall"),
  ),
  "study" [string](
    "title" "Study",
    "text" "This is a small, cosy study with a desk and many bookcases.",
    "exits" [string]("e" "southhall"),
  ),
  "bedroom" [string](
    "title" "Bedroom",
    "text" "You are in a small bedroom with a bed and a large wardrobe.",
    "exits" [string]("e" "northhall"),
  ),
  "bathroom" [string](
    "title" "Bathroom",
    "text" "You are in a small, very clean and bright bathroom.",
    "exits" [string]("s" "northhall"),
  ),
  "backlawn" [string](
    "title" "Back lawn",
    "text" "You are standing on a small, neat lawn at the back of the cottage.",
    "exits" [string]("s" "kitchen", "w" "sidepath"),
  ),
  "sidepath" [string](
    "title" "Side path",
    "text" "You are on a narrow path running along the side of the cottage.",
    "exits" [string]("n" "shed", "e" "backlawn", "s" "frontlawn"),
  ),
  "shed" [string](
    "title" "Shed",
    "text" "You are standing in a small, dark, dingy garden shed.",
    "exits" [string]("s" "sidepath"),
  ),
)

// Global variables
Cmd:string = "look"           // Starting command
Here:[string] = Locs["path"]  // Starting location
Words:[]string                // Additional words after command
FoundItem:string              // Shared with findItem subroutine

gosub Greeting

// Main game processing loop.
Main:
  handled:bool = false
  if Cmd == "h" || Cmd == "help"; gosub Help
  if Cmd == "q" || Cmd == "quit"; gosub Quit
  if Cmd == "l" || Cmd == "look"; gosub Look
  if Cmd == "n" || Cmd == "north"; gosub Move
  if Cmd == "s" || Cmd == "south"; gosub Move
  if Cmd == "e" || Cmd == "east"; gosub Move
  if Cmd == "w" || Cmd == "west"; gosub Move
  if Cmd == "x" || Cmd == "examine"; gosub Examine
  if               Cmd == "sneeze"; gosub Sneeze
  if !handled && Cmd != ""; println "Eh?"
  print "? "; input Cmd
  gosub Split
goto Main

// Quit exits the game.
Quit:
  println
  println "Bye bye!"
  println
  exit 0

// Sneeze makes the player sneeze. Example of a very simple command.
Sneeze:
  handled = true
  println "Achoo! You sneeze."
return

// Look command to describe the player's current location.
Look:
  handled = true
  println "[ " Here["title"] " ]"
  println Here["text"]
  println

  if !exists Here "inv"; goto noinv

  needNL:bool = false
  invKeys:[]string = keys Here["inv"]
  inv_loop:
    item:[string] = Here["inv"][invKeys[0]]
    if exists(item, "narrative"); goto isNarrative
    println("You see ", item["name"], " here.")
    needNL = true
  isNarrative:
    delete(invKeys, 0)
  if len(invKeys) > 0; goto inv_loop

  if needNL; println

  noinv:

  exitList:string = ""
  dkeys:[]string  = keys Here["exits"]
  dirs_loop:
    exitList += Exits[dkeys[0]]
    if len(dkeys) == 2; exitList += " and "
    if len(dkeys) > 2;  exitList += ", "
    delete dkeys 0
  if len(dkeys) > 0; goto dirs_loop

  println "You see exits: " exitList
return

// Move command to move player between locations.
Move:
  handled = true
  dir:string = Cmd[0]

  if !exists Here["exits"] dir; goto noexit
  if !exists Locs Here["exits"][dir]; goto badexit

  Here = Locs[Here["exits"][dir]]
  gosub Look
return

noexit:  println "You can't go " Exits[dir]  "."; return
badexit: println "Oops, you can't actually go " Exits[dir]  "."; return

// Examine command to examine items.
Examine:
  handled = true

  if len(Words) == 0; goto examineWhat

  gosub findItem
  if FoundItem == ""; goto noExamine

  item:[string] = Here["inv"][FoundItem]
  println "You examine " item["name"] "."
  println item["text"]
return

examineWhat: println "You goto examine... something?"; return
noExamine:
  wordList:string = sprintf("%s", Words)
  wordList = substr(wordList, 1, len(wordList)-2)
  println "You see no '" wordList "' here to examine."
return

// findItem looks for items with aliases matching Words. Returns the index of
// the first match as a string in FoundItem. If there are no matches return an
// empty string.
findItem:
  items:[string] = Here["inv"]
  keep:[]string = keys items
  keepers:[]string = []string()
  matching:[]string = Words
  FoundItem = ""

  midx:int = 0
  matchLoop:
    kidx:int = 0
    keepLoop:
      match:bool = false
      aliases:[]string = items[keep[kidx]]["alias"]
      aidx:int = 0
      aliasLoop:
        if aliases[aidx] == matching[midx]; match = true
      if aidx++ < len(aliases); goto aliasLoop
      if match; keepers = []string keepers keep[kidx]
    if kidx++ < len(keep); goto keepLoop
    keep = keepers
    keepers = []string()
    if len(keep) == 0; return
  if midx++ < len(matching); goto matchLoop

  if len(keep) > 0; FoundItem = keep[0]
return

// Split player command into Words and move first word into Cmd.
Split:
  Words = []string()
  if len(Cmd) == 0; return

  word:string = ""
  Cmd += " "

  pos:int = 0
  split_loop:
    if Cmd[pos] != " "; word += Cmd[pos]
    if Cmd[pos] == " " && word != ""; Words = []string Words word
    if Cmd[pos] == " " && word != ""; word = ""
  if pos++ < len Cmd; goto split_loop

  Cmd = Words[0]
  delete Words 0
return

// Greeting welcomes the player.
Greeting:
  println
  println "    Welcome to the cottage!"
  println
  println "For a list of commands type h or help."
  println
return

// Help displays the available commands.
Help:
  handled = true
  println "Available commands:"
  println
  println "    n or north - move north"
  println "    s or south - move south"
  println "     e or east - move east"
  println "     w or west - move west"
  println "        sneeze - makes you sneeze"
  println "  x or examine - examine an item"
  println "     l or look - describe location again"
  println "     h or help - display this help"
  println "     q or quit - exit the game"
  println
return


  Up to Main Index                           Up to Journal for October, 2022