Add ability to restart the game

This commit is contained in:
Andrew Miner
2018-05-28 16:54:20 -06:00
parent a7b7df482a
commit b629982e70
6 changed files with 169 additions and 134 deletions
-1
View File
@@ -1 +0,0 @@
theme: jekyll-theme-hacker
+104 -86
View File
@@ -4,6 +4,10 @@ class GameLog
@content = "" @content = ""
@onChange = onChange @onChange = onChange
clear: ->
@content = ""
@onChange(this)
echoInput: (text)-> echoInput: (text)->
@content += "<br>&gt; " + text + "<br>" @content += "<br>&gt; " + text + "<br>"
@onChange(this) @onChange(this)
@@ -21,22 +25,33 @@ class GameLog
class Inventory class Inventory
constructor: -> constructor: ->
@items = {} @clear()
@length = 0
# Properties ###################################################################################
Object.defineProperties @prototype,
length:
get: -> return @_length
# Public Methods ###############################################################################
add: (item)-> add: (item)->
if not @items[item.name] if not @_items[item.name]
@items[item.name] = item @_items[item.name] = item
@length += 1 @_length += 1
all: -> all: ->
return (item for name, item of @items) return (item for name, item of @_items)
clear: ->
@_items = {}
@_length = 0
describe: (options={})-> describe: (options={})->
options.simple ?= false options.simple ?= false
result = [] result = []
for name, item of @items for name, item of @_items
if options.simple if options.simple
result.push(item.name) result.push(item.name)
else else
@@ -45,16 +60,16 @@ class Inventory
return result.join("\n") return result.join("\n")
eachItem: (withItem=(->))-> eachItem: (withItem=(->))->
for name, item of @items for name, item of @_items
withItem(item) withItem(item)
has: (item)-> has: (item)->
return !! @items[item.name] return !! @_items[item.name]
remove: (itemToRemove)-> remove: (itemToRemove)->
if @items[itemToRemove.name] if @_items[itemToRemove.name]
delete @items[itemToRemove.name] delete @_items[itemToRemove.name]
@length -= 1 @_length -= 1
######################################################################################################################## ########################################################################################################################
@@ -96,6 +111,8 @@ class Location
@transitions = [] @transitions = []
@visited = false @visited = false
# Public Methods ###############################################################################
addTransition: (direction, toLocation, locked=false)-> addTransition: (direction, toLocation, locked=false)->
@transitions[direction] = new Transition(direction, toLocation, locked) @transitions[direction] = new Transition(direction, toLocation, locked)
@@ -111,6 +128,7 @@ class Location
result.push("\n") result.push("\n")
result.push(@description) result.push(@description)
console.log("@inventory.length: #{@inventory.length}")
if @inventory.length > 0 if @inventory.length > 0
result.push("") result.push("")
result.push(@inventory.describe()) result.push(@inventory.describe())
@@ -142,10 +160,7 @@ class ParseError extends Error
class Parser class Parser
constructor: (@story)-> constructor: (@story)->
@aliases = {} @restart()
@directions = []
@verbs = {}
@fillerWords = new Set()
addAliases: (aliasMap)-> addAliases: (aliasMap)->
for alias, meaning of aliasMap for alias, meaning of aliasMap
@@ -178,6 +193,12 @@ class Parser
@_validateSentence(sentence) @_validateSentence(sentence)
return sentence return sentence
restart: ->
@aliases = {}
@directions = []
@fillerWords = new Set()
@verbs = {}
# Private Methods ############################################################################## # Private Methods ##############################################################################
_normalizeSentence: (sentence)-> _normalizeSentence: (sentence)->
@@ -278,9 +299,7 @@ class Player
constructor: (@story)-> constructor: (@story)->
@inventory = new Inventory() @inventory = new Inventory()
@onChange = (->) @onChange = (->)
@verbs = {} @restart()
@_score = 0
# Properties ################################################################################### # Properties ###################################################################################
@@ -326,6 +345,12 @@ class Player
@story.arrive(location) @story.arrive(location)
restart: ->
@verbs = {}
@inventory.clear()
@score = 0
@_configureDefaultVerbs()
take: (item)-> take: (item)->
localItems = @story.currentLocation.inventory localItems = @story.currentLocation.inventory
if not item if not item
@@ -338,13 +363,51 @@ class Player
if not foundTakeableItem then @story.log.writeln("There's nothing here you can take.") if not foundTakeableItem then @story.log.writeln("There's nothing here you can take.")
else if localItems.has(item) else if localItems.has(item)
console.log(item)
if item.take() if item.take()
localItems.remove(item) localItems.remove(item)
@inventory.add(item) @inventory.add(item)
else else
throw new ParseError("There isn't a #{item} here.") throw new ParseError("There isn't a #{item} here.")
# Private Methods ##############################################################################
_configureDefaultVerbs: ->
@addVerb "drop", (sentence)=>
if sentence.has(item: 0)
@drop()
else
for itemToken in sentence.tokens.item
@drop(itemToken.referant)
@addVerb "go", (sentence)=>
if sentence.has(location: 1)
@move(sentence.location)
else
throw new ParseError("I'm not sure where you want to go...")
@addVerb "inventory", =>
if @inventory.length is 0
@story.log.writeln("You're not carrying anything.")
else
@story.log.writeln("You have:")
@story.log.writeln(@inventory.describe(simple: true))
@addVerb "look", (sentence)=>
if sentence.has(item: 1)
@story.log.writeln(sentence.item.describe(verbose: true))
else
@story.log.writeln(@story.currentLocation.describe(verbose: true))
@addVerb "restart", =>
@story.restart()
@addVerb "take", (sentence)=>
if sentence.has(item: 0)
@take()
else
for item in sentence.items
@take(item)
######################################################################################################################## ########################################################################################################################
@@ -377,6 +440,8 @@ class Sentence
Object.defineProperties @prototype, Object.defineProperties @prototype,
"item": "item":
get: -> return @tokens.item[0].referant get: -> return @tokens.item[0].referant
"items":
get: -> (i.referant for i in @tokens.item)
"location": "location":
get: -> return @tokens.location[0].referant get: -> return @tokens.location[0].referant
"verb": "verb":
@@ -387,18 +452,12 @@ class Sentence
class Story class Story
constructor: (@title)-> constructor: (@title, @onRestart=(->))->
@currentLocation = null
@items = []
@locations = []
@log = new GameLog() @log = new GameLog()
@onChange = (->)
@parser = new Parser(this) @parser = new Parser(this)
@player = new Player(this) @player = new Player(this)
@_turns = 0 @onChange = (->)
@_configure()
# Properties ################################################################################### # Properties ###################################################################################
@@ -434,11 +493,6 @@ class Story
location.visited = true location.visited = true
begin: ->
@log.writeln(@title)
@log.writeln()
@arrive(@currentLocation)
interpret: (userInput)-> interpret: (userInput)->
try try
@log.echoInput(userInput) @log.echoInput(userInput)
@@ -453,67 +507,31 @@ class Story
# Private Methods ############################################################################## # Private Methods ##############################################################################
_configure: -> restart: ->
@currentLocation = null
@items = []
@locations = []
@turns = 0
@log.clear()
@parser.restart()
@player.restart()
@parser.addDirections( @parser.addDirections(
"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down" "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down"
) )
@parser.addFillerWords( @parser.addFillerWords(
"a", "a", "all", "an", "around", "at", "everything", "of", "the", "to"
"an",
"around",
"at",
"of",
"the",
"to"
) )
@parser.addAliases({ @parser.addAliases({
"d": "down", "d": "down", "e": "east", "g": "go", "i": "inventory", "l": "look", "n": "north", "ne": "northeast",
"e": "east", "nw": "northwest", "s": "south", "se": "southeast", "sw": "southwest", "u": "up", "w": "west",
"everything": "all",
"g": "go"
"i": "inventory"
"l": "look",
"n": "north",
"ne": "northeast",
"nw": "northwest",
"s": "south",
"se": "southeast",
"sw": "southwest",
"u": "up",
"w": "west",
}) })
@player.addVerb "drop", (sentence)=> @onRestart()
if sentence.has(item: 0) @log.writeln(@title)
@player.drop() @log.writeln()
else @arrive(@currentLocation)
for itemToken in sentence.tokens.item
@player.drop(itemToken.referant)
@player.addVerb "go", (sentence)=>
if sentence.has(location: 1)
@player.move(sentence.location)
else
throw new ParseError("I'm not sure where you want to go...")
@player.addVerb "inventory", =>
if @player.inventory.length is 0
@log.writeln("You're not carrying anything.")
else
@log.writeln("You have:")
@log.writeln(@player.inventory.describe(simple: true))
@player.addVerb "look", (sentence)=>
if sentence.has(item: 1)
@log.writeln(sentence.item.describe(verbose: true))
else
@log.writeln(@currentLocation.describe(verbose: true))
@player.addVerb "take", (sentence)=>
if sentence.has(item: 1)
@player.take(sentence.item)
else if sentence.has(item: 0)
@player.take()
######################################################################################################################## ########################################################################################################################
+2
View File
@@ -2,6 +2,7 @@
<link rel="stylesheet" type="text/css" href="styles.css"/> <link rel="stylesheet" type="text/css" href="styles.css"/>
<body> <body>
<div class="game">
<div class="status"> <div class="status">
<span class="turns">&nbsp;</span> <span class="turns">&nbsp;</span>
<span class="score">&nbsp;</span> <span class="score">&nbsp;</span>
@@ -10,6 +11,7 @@
<p class="log-text"></p> <p class="log-text"></p>
</div> </div>
<input class="entry" type="text"></input> <input class="entry" type="text"></input>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://coffeescript.org/v2/browser-compiler/coffeescript.js"></script> <script src="https://coffeescript.org/v2/browser-compiler/coffeescript.js"></script>
+1 -1
View File
@@ -33,5 +33,5 @@ $(document).ready ->
STORY.onChange(STORY) STORY.onChange(STORY)
# start the story # start the story
STORY.begin() STORY.restart()
$input.focus() $input.focus()
+21 -17
View File
@@ -1,39 +1,43 @@
window.STORY = s = new Story("A Walk Through My House") window.STORY = new Story "A Walk Through My House", ->
# Items ################################################################################################################ # Items ################################################################################################################
boxOfTile = s.addItem("box of tiles") boxOfTile = @addItem "box of tiles"
boxOfTile.description = boxOfTile.description =
"This appears to be a box of flooring tiles. The tiles appear to be a very light color of natural stone. It is "This appears to be a box of flooring tile@ The tiles appear to be a very light color of natural stone. It is
quite heavy." quite heavy."
boxOfTile.take = -> boxOfTile.take = =>
s.log.writeln("Oof! It's too heavy.") @log.writeln "Oof! It's too heavy."
return false return false
hose = s.addItem("garden hose") car = @addItem "your car"
car.description =
"Your car isn't anything special, but it gets you from place to place."
hose = @addItem "garden hose"
hose.description = "It's a pretty ordinary garden hose about 20' long." hose.description = "It's a pretty ordinary garden hose about 20' long."
# Locations ############################################################################################################ # Locations ############################################################################################################
driveway = s.addLocation("Driveway") driveway = @addLocation "Driveway"
driveway.description = driveway.description =
"The driveway extends slightly uphill a short way to the west back to the street. At this part, it's recessed a "The driveway extends slightly uphill a short way to the west back to the street. At this part, it's recessed a
little below the level of the lawn with a stone wall defining the boundary. To the south are three garage doors little below the level of the lawn with a stone wall defining the boundary. To the south are three garage doors
which make up the entire side of the house. Above the doors are a few windows. The back yard is to the east, and which make up the entire side of the house. Above the doors are a few windows. The back yard is to the east, and
the some steps lead up to the front porch to the southwest." the some steps lead up to the front porch to the southwest."
driveway.addItem(hose) driveway.addItem hose
frontPorch = s.addLocation("Front Porch") frontPorch = @addLocation "Front Porch"
frontPorch.description = frontPorch.description =
"You're standing on the front porch. It's a large wooden deck with an overhang above, and a railing all about. To "You're standing on the front porch. It's a large wooden deck with an overhang above, and a railing all about.
the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside the To the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside
house. It has a fancy crystal inset in the windows, so while you can see a light on inside the house, you can't make the house. It has a fancy crystal inset in the windows, so while you can see a light on inside the house, you
out any details." can't make out any details."
frontPorch.addItem(boxOfTile) frontPorch.addItem boxOfTile
# Configure Map ######################################################################################################## # Configure Map ########################################################################################################
driveway.addTransition("southwest", frontPorch) driveway.addTransition "southwest", frontPorch
frontPorch.addTransition("north", driveway) frontPorch.addTransition "north", driveway
s.currentLocation = frontPorch @currentLocation = frontPorch
+21 -9
View File
@@ -3,47 +3,59 @@
} }
body { body {
align-items: center;
display: flex; display: flex;
align-items: stretch;
font-size: 16px; font-size: 16px;
font-family: monospace; font-family: monospace;
flex-direction: column; height: 100%;
justify-content: center; margin: 0;
}
html {
height: 100%;
} }
.entry { .entry {
background: black; background: black;
border: none; border: none;
border-top: 1px solid gray; border-top: 1px solid #444;
color: green; color: green;
flex: 0 0 auto;
font-family: monospace; font-family: monospace;
font-size: 16px; font-size: 16px;
margin: 0; margin: 0;
outline: none; outline: none;
padding: 0.5em 1em; padding: 0.5em 1em;
width: 800px; }
.game {
display: flex;
flex: 1 1 100%;
flex-direction: column;
align-items: stretch;
justify-content: stretch;
height: 100%;
} }
.log { .log {
background: black; background: black;
color: green; color: green;
height: 600px; flex: 1 1 100%;
overflow-y: auto; overflow-y: auto;
padding: 0.25em 1em; padding: 0.25em 1em;
width: 800px;
} }
.status { .status {
background: black; background: black;
border: none; border: none;
border-bottom: 1px solid gray; border-bottom: 1px solid #444;
color: green; color: green;
display: flex; display: flex;
flex: 0 0 auto;
font-family: monospace; font-family: monospace;
font-size: 16px; font-size: 16px;
margin: 0; margin: 0;
padding: 0.5em 1em; padding: 0.5em 1em;
width: 800px;
} }
.score { .score {