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()
######################################################################################################################## ########################################################################################################################
+9 -7
View File
@@ -2,14 +2,16 @@
<link rel="stylesheet" type="text/css" href="styles.css"/> <link rel="stylesheet" type="text/css" href="styles.css"/>
<body> <body>
<div class="status"> <div class="game">
<span class="turns">&nbsp;</span> <div class="status">
<span class="score">&nbsp;</span> <span class="turns">&nbsp;</span>
<span class="score">&nbsp;</span>
</div>
<div class="log">
<p class="log-text"></p>
</div>
<input class="entry" type="text"></input>
</div> </div>
<div class="log">
<p class="log-text"></p>
</div>
<input class="entry" type="text"></input>
<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()
+34 -30
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"
hose.description = "It's a pretty ordinary garden hose about 20' long." car.description =
"Your car isn't anything special, but it gets you from place to place."
# Locations ############################################################################################################ hose = @addItem "garden hose"
hose.description = "It's a pretty ordinary garden hose about 20' long."
driveway = s.addLocation("Driveway") # Locations ############################################################################################################
driveway.description =
"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
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."
driveway.addItem(hose)
frontPorch = s.addLocation("Front Porch") driveway = @addLocation "Driveway"
frontPorch.description = driveway.description =
"You're standing on the front porch. It's a large wooden deck with an overhang above, and a railing all about. To "The driveway extends slightly uphill a short way to the west back to the street. At this part, it's recessed a
the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside the little below the level of the lawn with a stone wall defining the boundary. To the south are three garage doors
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 which make up the entire side of the house. Above the doors are a few windows. The back yard is to the east, and
out any details." the some steps lead up to the front porch to the southwest."
frontPorch.addItem(boxOfTile) driveway.addItem hose
# Configure Map ######################################################################################################## frontPorch = @addLocation "Front Porch"
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 the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside
the 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 out any details."
frontPorch.addItem boxOfTile
driveway.addTransition("southwest", frontPorch) # Configure Map ########################################################################################################
frontPorch.addTransition("north", driveway)
s.currentLocation = frontPorch driveway.addTransition "southwest", frontPorch
frontPorch.addTransition "north", driveway
@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 {