2020-09-29 08:59:45 -04:00
( import comparse )
( import srfi-1 )
( import srfi-14 )
( import ( chicken io ) )
( import srfi-13 )
( import matchable )
( import fmt )
( import ansi-escape-sequences )
( import ( chicken file ) )
2020-10-01 07:54:23 -04:00
( define ( lift fn parser )
( bind parser ( compose result fn ) ) )
2020-09-29 08:59:45 -04:00
( define ( is-not x )
( satisfies ( lambda ( y )
( not ( eqv? x y ) ) ) ) )
( define parse-whitespace
( one-or-more ( is #\space ) ) )
( define skip-whitespace
( skip ( zero-or-more ( is #\space ) ) ) )
2020-10-01 07:54:23 -04:00
( define +letter-char-set+
( string->char-set "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" ) )
( define +symbol-char-set+
( char-set-union +letter-char-set+ ( string->char-set "-0123456789" ) ) )
2020-09-29 08:59:45 -04:00
( define parse-symbol
2020-10-01 07:54:23 -04:00
( lift ( compose string->symbol string-downcase list->string ( cut apply append <> ) )
( sequence ( lift list ( in +letter-char-set+ ) ) ( zero-or-more ( in +symbol-char-set+ ) ) ) ) )
2020-09-29 08:59:45 -04:00
( define parse-number
( lift ( compose string->number list->string ) ( one-or-more ( in char-set:digit ) ) ) )
( define parse-string
( lift list->string ( enclosed-by ( is #\" ) ( one-or-more ( is-not #\" ) ) ( is #\" ) ) ) )
( define ( followed-by-consuming parser separator )
( sequence* ( ( value parser ) ( _ separator ) )
( result value ) ) )
( define ( separated-by separator parser )
( one-or-more ( any-of ( followed-by-consuming parser separator ) parser ) ) )
( define parse-symbol-or-number-or-string
( any-of parse-number parse-symbol parse-string ) )
( define ( completely-parse parser )
( followed-by parser end-of-input ) )
( define parse-statement
( all-of skip-whitespace ( separated-by parse-whitespace parse-symbol-or-number-or-string ) ) )
( define ( just fn )
( lambda args
( fn ) ) )
( define ( perhaps fn arg )
( if arg
( fn arg )
arg ) )
( define display-newline
( compose ( just newline ) display ) )
( define ( display-lines ln )
( perhaps ( cut map display-newline <> ) ln ) )
( define ( parse-input )
( parse ( completely-parse parse-statement ) ( read-line ) ) )
( define ( type-of elem )
( cond ( ( pair? elem ) 'pair )
( ( symbol? elem ) 'symbol )
( ( number? elem ) 'number )
( ( char? elem ) 'char )
( ( string? elem ) 'string )
( ( boolean? elem ) 'boolean ) ) )
( define ( show str )
( fmt #t ( dsp ( wrap-lines str ) ) ) )
( define ( prompt str )
( newline )
( display str )
( let ( ( result ( read-line ) ) )
( if ( equal? "" result )
( prompt str )
result ) ) )
( define ( prompt-yn str )
( newline )
( display str )
( let ( ( result ( string-downcase ( read-line ) ) ) )
( cond ( ( equal? "yes" result ) #t )
( ( equal? "no" result ) #t )
( else ( begin
( newline )
( display "Please answer yes or no." )
( prompt-yn str ) ) ) ) ) )
( define +articles-prepositions+
' ( a an the into on to at ) )
( define ( adventure-prompt )
( let ( ( result ( parse ( completely-parse parse-statement ) ( prompt "> " ) ) ) )
( if result
( let ( ( grug-result ( filter ( lambda ( n ) ( not ( member n +articles-prepositions+ ) ) ) result ) ) )
( if ( not ( null? grug-result ) )
grug-result
( begin ( display "I didn't quite understand that." )
( adventure-prompt ) ) ) )
( begin ( display "I didn't quite understand that." )
( adventure-prompt ) ) ) ) )
2020-10-01 07:54:23 -04:00
( define ( compose-symbols . ln )
( let loop ( ( ln ln ) )
( case ( length ln )
( ( 0 ) ' ( ) )
( ( 1 ) ( list ( symbol->string ( car ln ) ) ) )
( else ( string-append ( symbol->string ( car ln ) ) "-" ( loop ( cdr ln ) ) ) ) ) ) )
2020-09-29 08:59:45 -04:00
( define *database* ' ( ) )
( define ( database-set name key value )
( set! *database* ( let loop ( ( kv *database* ) )
( if ( null? kv )
( list ( cons name ( list ( cons key value ) ) ) )
( if ( equal? name ( caar kv ) )
( cons ( cons name ( let loop ( ( kv ( cdar kv ) ) )
( if ( null? kv )
( list ( cons key value ) )
( if ( equal? key ( caar kv ) )
( cons ( cons key value ) ( cdr kv ) )
( cons ( car kv ) ( loop ( cdr kv ) ) ) ) ) ) ) ( cdr kv ) )
( cons ( car kv ) ( loop ( cdr kv ) ) ) ) ) ) ) )
( define ( database-get name key default )
( let loop ( ( kv *database* ) )
( if ( null? kv )
default
( if ( equal? name ( caar kv ) )
( let loop ( ( kv ( cdar kv ) ) )
( if ( null? kv )
default
( if ( equal? key ( caar kv ) )
( cdar kv )
( loop ( cdr kv ) ) ) ) )
( loop ( cdr kv ) ) ) ) ) )
( define ( database-save filename )
( with-output-to-file filename ( cut write *database* ) ) )
( define ( database-load filename )
( with-input-from-file filename ( lambda ( ) ( set! *database* ( car ( read-list ) ) ) ) ) )
( define ( get-all-objects )
( map car *database* ) )
( define ( object-exists? object )
( member object ( get-all-objects ) ) )
( define ( has-property? object property )
( database-get object property #f ) )
( define ( toggle-flag object flag )
( if ( has-property? object flag )
( database-set object flag #f )
( database-set object flag #t ) ) )
( define ( get-location object )
( database-get object 'location #f ) )
( define ( set-name object name )
( database-set object 'name name ) )
( define ( set-description object description )
( database-set object 'description description ) )
( define ( get-name object )
( database-get object 'name ( symbol->string object ) ) )
( define ( get-description object )
( database-get object 'description "You see the swirling void of creation." ) )
( define ( get-container object )
( database-get object 'container #f ) )
( define ( get-contents object )
( database-get object 'contents ' ( ) ) )
( define ( set-destination object destination )
( database-set object 'destination destination ) )
( define ( get-destination object )
( database-get object 'destination #f ) )
( define ( set-enter-message object msg )
( database-set object 'enter-message msg ) )
( define ( get-enter-message object )
( database-get object 'enter-message #f ) )
( define ( get-aliases object )
( database-get object 'aliases ' ( ) ) )
( define ( set-aliases object alias-list )
( database-set object 'aliases alias-list ) )
( define ( add-alias object alias )
( let ( ( aliases ( get-aliases object ) ) )
( if ( not ( member alias aliases ) )
( set-aliases object ( cons alias aliases ) ) ) ) )
( define ( remove-alias object alias )
( let ( ( aliases ( get-aliases object ) ) )
( if ( member alias aliases )
( set-aliases object ( remove ( cut eq? alias <> ) aliases ) ) ) ) )
2020-10-01 07:54:23 -04:00
( define ( get-put-message object )
( database-get object 'put-message "You put the ~a into the ~a." ) )
2020-09-29 08:59:45 -04:00
;; Is development mode enabled?
( define ( devmode-enabled? )
( has-property? 'you 'devmode ) )
( define ( toggle-devmode )
( toggle-flag 'you 'devmode ) )
;; Is an object fixed in place (e.g. cannot be picked up?)
( define ( fixed? object )
( has-property? object 'fixed ) )
( define ( toggle-fixed object )
( toggle-flag object 'fixed ) )
;; Match a tag against a list of objects, checking for its tag and its aliases.
( define ( match-object tag objects )
( let loop ( ( objects objects ) )
( if ( null? objects )
#f
( let ( ( taglist ( cons ( car objects ) ( get-aliases ( car objects ) ) ) ) )
( if ( member tag taglist )
( car objects )
( loop ( cdr objects ) ) ) ) ) ) )
( define ( create-object tag name description )
( set-name tag name )
( set-description tag description ) )
( define ( move-object object container )
( let ( ( prev-container ( get-container object ) ) )
( database-set object 'container container )
( let ( ( contents ( get-contents container ) ) )
( if ( not ( member object contents ) )
( begin
( database-set container 'contents ( cons object contents ) )
( database-set prev-container 'contents ( remove ( cut eq? object <> ) ( get-contents prev-container ) ) ) ) ) ) ) )
;; Determine the objects visible to a source object, zork-style
( define ( visible-objects source )
( let ( ( result ( get-container source ) ) )
( if ( and result ( object-exists? result ) )
( cons ( get-container source ) ( get-contents ( get-container source ) ) )
( error "Tried to determine visible objects for object without a container." ) ) ) )
( define ( do-command-enter tag )
( let ( ( object ( match-object tag ( visible-objects 'you ) ) ) )
( if ( not object )
( show "You cannot go that way." )
( let ( ( destination ( get-destination object ) ) )
( if ( not destination )
( show "You cannot enter that." )
( begin
( move-object 'you destination )
( perhaps show ( get-enter-message object ) )
( print-room-description ( get-container 'you ) ) ) ) ) ) ) )
( define ( print-room-description room )
( newline )
( display ( set-text ' ( bold ) ( get-name room ) ) )
( newline )
( display " " )
( fmt #t ( dsp ( wrap-lines ( get-description room ) ) ) )
( newline )
( display "You see: " )
( map ( lambda ( n ) ( display n ) ( display " " ) ) ( map get-name ( remove ( cut eq? 'you <> ) ( get-contents room ) ) ) )
( newline ) )
( define ( do-command-save )
( let ( ( save-name ( prompt "Enter save name: " ) ) )
( if ( or ( not ( file-exists? save-name ) ) ( prompt-yn "That file already exists. Overwrite? " ) )
( begin
( show "Saving database, please wait..." )
( database-save save-name )
( show "Done." ) ) ) ) )
( define ( do-command-load )
( let ( ( save-name ( prompt "Enter save file name to load: " ) ) )
( if ( not ( file-exists? save-name ) )
( show "That file does not exist." )
( begin
( show "Loading database, please wait..." )
( database-load save-name )
( show "Done." ) ) ) ) )
( define ( do-command-look )
( print-room-description ( get-container 'you ) ) )
( define ( do-command-examine tag )
( let ( ( object ( match-object tag ( if ( devmode-enabled? )
( get-all-objects )
( visible-objects 'you ) ) ) ) )
( if ( not object )
( show "You cannot see that here." )
( show ( get-description object ) ) ) ) )
( define ( do-command-inventory )
( map ( compose show get-name ) ( get-contents 'you ) ) )
( define ( do-command-take tag )
( if ( not ( symbol? tag ) )
( show "I didn't quite understand that." )
( let ( ( object ( match-object tag ( if ( devmode-enabled? )
( get-all-objects )
( visible-objects 'you ) ) ) ) )
( if ( or ( not object ) ( and ( fixed? object ) ( not ( devmode-enabled? ) ) ) )
( if object
( show "That is fixed in place." )
( show "You cannot see that here." ) )
( begin
( show ( string-append "You get " ( get-name object ) "." ) )
( move-object object 'you ) ) ) ) ) )
( define ( do-command-drop tag )
( if ( not ( symbol? tag ) )
( show "I didn't quite understand that." )
( let ( ( object ( match-object tag ( get-contents 'you ) ) ) )
( if ( not object )
( show "You are not carrying that." )
( begin
( show ( string-append "You drop " ( get-name object ) "." ) )
( move-object object ( get-container 'you ) ) ) ) ) ) )
2020-10-01 07:54:23 -04:00
( define ( do-command-put tag destination-tag )
( let ( ( object ( match-object tag ( get-contents 'you ) ) ) )
( if ( not object )
( show "You are not carrying that." )
( let ( ( destination-object ( match-object destination-tag ( visible-objects 'you ) ) ) )
( if ( not destination-object )
( show "You cannot see that here." )
( move-object object ( get-destination destination-object ) ) ) ) ) ) )
2020-09-29 08:59:45 -04:00
( define ( do-command-devmode )
( toggle-devmode )
( if ( devmode-enabled? )
( show "Development mode enabled." )
( show "Development mode disabled." ) ) )
( define ( do-command-create tag name description )
( if ( not ( and ( symbol? tag ) ( string? name ) ( string? description ) ) )
( show "I didn't quite understand that." )
( if ( object-exists? tag )
( show "That object already exists." )
( begin
( create-object tag name description )
( move-object tag ( get-container 'you ) ) ) ) ) )
( define ( do-command-rename tag name )
( if ( not ( and ( symbol? tag ) ( string? name ) ) )
( show "I didn't quite understand that." )
( if ( not ( object-exists? tag ) )
( show "That object doesn't exist." )
( begin
( set-name tag name ) ) ) ) )
2020-10-01 07:54:23 -04:00
+
2020-09-29 08:59:45 -04:00
( define ( do-command-describe tag description )
( if ( not ( and ( symbol? tag ) ( string? description ) ) )
( show "I didn't quite understand that." )
( if ( not ( object-exists? tag ) )
( show "That object doesn't exist." )
( begin
( set-description tag description ) ) ) ) )
;;(define (do-command-dig direction destination)
;; (
( define ( do-command-exit )
( show "Goodbye, see you later..." )
( set! *exit-adventure* #t ) )
( define ( alias-transform input )
( match input
( ( 'quit ) ' ( exit ) )
( ( 'i ) ' ( inventory ) )
( ( 'inv ) ' ( inventory ) )
( ( 'look x ) ` ( examine , x ) )
( ( 'go x ) ` ( enter , x ) )
( ( 'get x ) ` ( take , x ) )
( _ input ) ) )
( define ( dispatch-command input )
( let ( ( success #t ) )
( match input
( ( 'look ) ( do-command-look ) )
( ( 'save ) ( do-command-save ) )
( ( 'load ) ( do-command-load ) )
( ( 'devmode ) ( do-command-devmode ) )
( ( 'exit ) ( do-command-exit ) )
( ( 'enter x ) ( do-command-enter x ) )
( ( 'take x ) ( do-command-take x ) )
( ( 'drop x ) ( do-command-drop x ) )
( ( 'inventory ) ( do-command-inventory ) )
( ( 'examine x ) ( do-command-examine x ) )
( ( 'put x y ) ( do-command-put x y ) )
( _ ( if ( devmode-enabled? )
( match input
( ( 'create x y z ) ( do-command-create x y z ) )
( ( 'rename x y ) ( do-command-rename x y ) )
( ( 'describe x y ) ( do-command-describe x y ) )
( ( 'dig x y ) ( do-command dig x y ) )
( _ ( set! success #f ) ) )
( set! success #f ) ) ) )
success ) )
( create-object 'garden "A Well-Kept Garden" "A french-style garden with topiary in the shape of various animals. A fountain gurgles happily in the middle." )
( create-object 'unicorn "a frolicking unicorn" "A white unicorn, with a long spiral horn." )
( create-object 'forest "A Foreboding Forest" "Tall trees bunch around a winding path." )
( create-object 'trail "a trail" "A winding trail." )
( add-alias 'trail 'winding )
( set-enter-message 'trail "You walk along the winding trail..." )
( move-object 'you 'garden )
( move-object 'trail 'garden )
( toggle-fixed 'trail )
( set-destination 'trail 'forest )
( move-object 'unicorn 'garden )
( define *exit-adventure* #f )
( define ( adventure )
( let ( ( success ( dispatch-command ( alias-transform ( adventure-prompt ) ) ) ) )
( if ( not success )
( begin
( show "I didn't quite understand that." )
( adventure ) )
( if *exit-adventure*
( display *database* )
( adventure ) ) ) ) )
( print-room-description ( get-container 'you ) )
( adventure )