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