Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Guile Hoot game template repository

A topic by David Thompson created Apr 30, 2024 Views: 3,099 Replies: 37
Viewing posts 1 to 11
HostSubmitted (1 edit) (+4)

If anyone out there is interested in making an HTML5 game using Scheme then Guile Hoot is a great option for you! I work on the Hoot team at the Spritely Institute and we just released version 0.4.1 today. As an added bonus we put together a game jam template Git repo that has everything you need to get started making 2D games.

It includes:

  • Bindings to the necessary web APIs to make an interactive game with HTML5 canvas
  • A Makefile for compiling, running a development web server, and generating a .zip bundle for uploading to itch.io
  • A very simple Breakout-like example game that demonstrates how to put all the pieces together

Hoot is a Scheme to WebAssembly compiler that currently supports most of R7RS-small Scheme (sans eval, basically) and some Guile extensions such as delimited continuations (useful for scripting games via coroutines). I used Hoot in the last jam, when it was in a much rougher state, to make the space shooter Strigoform. While it is still early days, Hoot has become much more featureful and a lot less buggy since the last jam, so I can wholeheartedly recommend it for making web games!

I'll do my best to help anyone that tries Hoot this time around, whether here in this forum, on the official Spritely Institute forum, or on the #spritely channel on the Libera.Chat IRC network. Have fun!  ðŸ¦‰

(+2)

Really awesome, thank you!

Submitted

hey i am looking at this as a likely candidate for my game...

ran into a snag trying to build the test repo

$ make

guild compile-wasm -L modules -o game.wasm game.scm

guild: unknown script "compile-wasm"

Try `guild help' for more information.

make: *** [Makefile:14: game.wasm] Error 1


i have guix, did guix pull to get to the latest version, added the latest guile, and hoot... this is my first time working with this sort of sotware stack, not familiar with all the necesasry working parts, not really sure where to go from here...
HostSubmitted (1 edit)

It looks like your Guile load path is not configured properly. Did you run 'guix shell' from the root of the repo to create your develop environment? I strongly recommend this method for getting all the necessary dependencies and automatically setting up the proper environment variables.

Submitted(+1)

i was able to successfully build by specifying guix shell -m manifest.scm
thank you, looking forward to playing around

HostSubmitted

You're welcome!

(1 edit)

hi this looks really neat! i'm trying this out but am getting this error when running guix shell:

guix shell: loading environment from '/home/.../projects/lisp-game-jam/manifest.scm'...
/home/.../projects/lisp-game-jam/manifest.scm:7:37: error: guile-hoot: unbound variable
hint: Did you forget a `use-modules' form?
HostSubmitted

Hi! Sounds like your Guix might be really out of date. Have you run `guix pull` recently? Run `guix show guile-hoot` to make sure the package is there and also verify that it's version 0.4.1. Hope this helps!

(2 edits)

It might be! I did a guix pull, but am still getting the error. it seemingly can't find the package:

$ guix show guile-hoot
guix show: error: guile-hoot: package not found
HostSubmitted

Strange! Are you using the Guix distro or Guix on top of another distro? My best guess is that the version of Guix that you pulled is not being used somehow. One thing to try is running `hash guix` first to ensure that the guix executable being invoked is the right thing. For example, `which guix` outputs /home/dave/.config/guix/current/bin/guix for me. You should see something similar for your user account.

(1 edit) (+1)

that seems to be the problem, i must be missing a step to set up my path properly


edit: I had the wrong thing in my bash_profile, manually running guix pull from ~/.config/guix/current and then following those instructions got it working! the previous guix pull had given me slightly different instructions. thanks for the help!

I've got it working now, but i'm noticing that the "display" function isn't available, is there a way to log to the console for debugging purposes? apologies if i'm missing something simple, i only have prior experience using racket, not guile.

HostSubmitted

In R7RS-small, the `display` procedure is in the (scheme write) module. I recommend keeping the R7RS-small spec handy for reference. That's what I do. :) https://small.r7rs.org/

(+1)

thanks!

HostSubmitted

Also, you may find yourself needing to manually flush output ports via (flush-output-port (current-output-port)) so if you don't see the expected output try that!

Is it possible at the moment to use hoot with something like Geiser in Emacs?  I can't seem to evaluate the `game.scm` file in emacs, there is trouble importing (hoot ffi) and (math), both complaining about missing (hoot features). 

scheme@(guile-user)> (import (hoot ffi))
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/ffi.scm
;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/cond-expand.scm
;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/cond-expand.scm failed:
;;; no code for module (hoot features)
;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/ffi.scm failed:
;;; no code for module (hoot features)
;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/cond-expand.scm
;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/cond-expand.scm failed:
;;; no code for module (hoot features)
While compiling expression: 
no code for module (hoot features)

I am running through direnv+guix shell, and the correct guile (3.0.9-0.3b76a30) is being used.   I am able to both compile the wasm through the Makefile as well as run the dev server.

HostSubmitted

That's not possible, currently. Hoot is a cross-compiler, so what you're trying to do in your snippet is kind of like running an ARM executable on x86 without a virtual machine. Everything in the (hoot ...) namespace is only usable when compiling to wasm. For example, (hoot ffi) is for calling functions on a wasm host, not native Guile. Likewise, Guile's (system foreign) provides a C FFI and couldn't be used with Hoot. The Guile VM is the host environment that runs the compiler, and the browser is the target that runs the compiled wasm. In the future, we'd like to host the Hoot compiler in wasm, so that you could do the interactive development we're all used to doing with Geiser, but that's a ways off. Hope this helps!

Definitely helps! ty

I've been getting the below error when attempting to compile, despite having correct syntax (as checked by normal Guile) and not using the procedure fill-rect anywhere:

guild compile-wasm -L modules -o game.wasm game.scm
Backtrace:
In ice-9/read.scm:
   231:26 19 (lp #\2)
   231:26 18 (lp #\")
   231:26 17 (lp #\c)
   231:26 16 (lp #\")
   231:26 15 (lp #\a)
   231:26 14 (lp #\")
   231:26 13 (lp #\a)
   231:26 12 (lp #\")
   231:26 11 (lp #\a)
   231:26 10 (lp #\")
   231:26  9 (lp #\a)
   231:26  8 (lp #\")
   231:26  7 (lp #\a)
   231:26  6 (lp #\")
   231:26  5 (lp #\a)
   231:26  4 (lp #\")
   231:26  3 (lp #\a)
   231:26  2 (lp #\")
   734:20  1 (lp #\#)
In unknown file:
           0 (list->typed-array #{c1c"\x29;\xa;;     }# 140 (# # # …))
ERROR: In procedure list->typed-array:
In procedure length: Wrong type argument in position 1: fill-rect
make: *** [Makefile:14: game.wasm] Error 1

Guile is version 3.0.9-0.3b76a30 and was updated yesterday through guix pull on a Debian 6.1.90-1 system.

There actually was a syntax error, but one that Guile and my syntax highlighter failed to report: I was missing a double quote. (A variety of other errors, the result of Hoot not having full list and string support just yet, could not have helped.)

HostSubmitted

Glad you found the error! What list/string support have you found missing?

I had to define filter, count, and delete. A variety of other issues I've since found: 

  • There is no inexact->exact, which means it has to be approximated with FFI when an exact integer is needed (i.e. in list-ref).
  • Division of a float by an integer inexplicably winds up as positive infinity?
  • I'm not sure that this is Hoot-specific, but having type predicates in define-typed-record would be nice, especially given that interactive debugging doesn't yet work.
HostSubmitted

Ah, SRFI-1 stuff. Yeah we don't ship any SRFIs currently. Could borrow it from Guile or any portable implementation, though. We'll be filling in more of this as we go on now that we more-or-less have R7RS-small covered.

Regarding inexact->exact, the equivalent R7RS-small procedure is simply 'exact', available in (scheme base). Likewise, 'inexact' is also available from that module. Hoot 0.5.0 will have inexact->exact, though, as part of the (guile) module. Either way, there's no need to use the FFI for this as R7RS-small has you covered.

For float/integer division, do you happen to have an example of two numbers that produce positive infinity? I can't reproduce on my end with the numbers I've tried.

The define-record-type syntax includes definition of a predicate.  For example: (define-record-type <foo> (make-foo bar) foo? (bar foo-bar)) ...or did I misunderstand?

Thanks for the feedback!

Syntax errors are really hard to catch right now. I've had an issue where I used a-very-long-identifier and a-very-long-identifiers (plural form) and couldn't figure out what the error was. If I could've been alerted to the fact I had not defined that, it'd be easy to figure out; or at least if I was given line/column numbers for the error.

Hi I'm also having compilation errors:

>make

guild compile-wasm -L modules -o game.wasm game.scm

guile: warning: failed to install locale

warning: failed to install locale: Invalid argument

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/scripts/compile-wasm.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0

;;;       or pass the --no-auto-compile argument to disable.

;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/scripts/compile-wasm.scm

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/compile.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/compile.scm

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/library-group.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/inline-wasm.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/inline-wasm.scm

;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/inline-wasm.scm failed:

;;; In procedure resolve-interface: no binding `add-primcall-effect-analyzer!' in module (language tree-il effects)

;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/compile.scm failed:

;;; In procedure resolve-interface: no binding `add-primcall-effect-analyzer!' in module (language tree-il effects)

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/backend.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/backend.scm

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/language/cps/hoot.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; compiling /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/language/cps/hoot.scm

;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/language/cps/hoot.scm failed:

;;; In procedure resolve-interface: no binding `primcall-raw-representations' in module (language cps utils)

;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/hoot/backend.scm failed:

;;; In procedure resolve-interface: no binding `primcall-raw-representations' in module (language cps utils)

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/stdlib.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/wat.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/types.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/dump.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/link.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/lower.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/lower-globals.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/lower-stringrefs.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/resolve.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: compilation of /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/share/guile/site/3.0/scripts/compile-wasm.scm failed:

;;; Syntax error:

;;; unknown location: definition in expression context, where definitions are not allowed, in form (define (maybe-branch-on-arity-mismatch checks) (if (null? checks) (quote ()) (branch-on-arity-mismatch checks)))

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/reflect.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/hoot/config.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/canonical-types.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/parse.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/vm.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/stack.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

;;; WARNING: loading compiled file /gnu/store/s8v5axydz2mvl65grbnszpis53njp7vb-profile/lib/guile/3.0/site-ccache/wasm/assemble.go failed:

;;; In procedure load-thunk-from-memory: incompatible bytecode version

Backtrace:

In ice-9/boot-9.scm:

  1752:10 10 (with-exception-handler _ _ #:unwind? _ # _)

In unknown file:

           9 (apply-smob/0 #<thunk 7fd71ae612e0>)

In ice-9/boot-9.scm:

    724:2  8 (call-with-prompt _ _ #<procedure default-prompt-handle?>)

In ice-9/eval.scm:

    619:8  7 (_ #(#(#<directory (guile-user) 7fd71ae66c80>)))

In /home/shakya/.guix-profile/bin/guild:

    72:17  6 (main _)

In ice-9/eval.scm:

    619:8  5 (_ #(#(#(#(#(#<directory (scripts compile-?> ?)) ?) ?) ?))

   293:34  4 (_ #(#(#(#(#(#<directory (scripts compile-?> ?)) ?) ?) ?))

In ice-9/boot-9.scm:

    152:2  3 (with-fluid* _ _ _)

In ice-9/eval.scm:

   182:19  2 (proc #(#(#<directory (scripts compile-wasm) 7fd719?> ?)))

   142:16  1 (compile-top-call #<directory (scripts compile-wasm) 7?> ?)

In unknown file:

           0 (%resolve-variable (7 . compile-file) #<directory (scri?>)

ERROR: In procedure %resolve-variable:

Unbound variable: compile-file

make: *** [Makefile:14: game.wasm] Error 1

I'm on Ubuntu 22.04LTS on wsl.

I don't know why your error is happening, but it might be WSL.

Don't use WSL... either use a VM or install linux and dual-boot.

HostSubmitted

I don't know what your setup is like but "incompatible bytecode version" errors suggest that you are using the wrong version of Guile to run 'guild compile-wasm'. If I had to guess I'd say it's using the Guile installed via apt but you need to make sure you're using the Guile you built from Git. Hope this helps!

Submitted (1 edit)

Please, I need some help with this,  to get a better guile hoot understanding.  

I'm trying to create a toggle full-screen  command.

(define-foreign full-screen-element
  "document" "fullscreenElement"
  (ref extern) ->   (ref null extern) )

I already have the full-screen and exit full-screen commands. But the command required for testing is giving me some trouble.

The function returns either null or the element, I expected that the null value would be considered as false in scheme but it is not the case.  I'm supposed to cast these values ? test for the string "null" ? or is my usage of define-foreign wrong?

Edited: I found out about the external-null?  and external-non-null? this work for the mentioned example but I'm still confused about the general approach for define-foreign.  Extra care is needed for those functions in scheme code?

HostSubmitted

Yeah, extra care is needed. A null extern ref is like a null pointer in Guile's C FFI, basically.

Submitted

I'm trying to use the record #:parent property to refactor some code. I'm unable to use the parent property of a record, what is the proper usage of this property?

(define-record-type <particle>
  (make-particle)
  particle?
;; omitted)
(define-record-type <prop>   
#:parent <particle>
(make-prop) 
prop?
;; omitted)

The define-record-type procedure is from SRFI-9. The #:parent propery applies to make-record-type which is a lower-level Guile specific. The define-record-type does not accept a #:parent keyword.


There is also a define-record-type in R6RS which has a (parent ...). See https://www.gnu.org/software/guile/manual/html_node/R6RS-Records.html


If this is confusing, it's because after R6RS was published, Scheme splintered and grew in different directions; Guile lisp supports all these directions at the same time. You can read http://dpk.io/r7rswtf if you are interested in the history.

Submitted

The parent property appears in the guile hoot documentation. And it is in the the guile hoot records.scm file.

So I guess that I'm using the wrong definition, I will try to import the proper definition.  I already tried (hoot records) to no success. 

The parent property appears under make-record-type, not define-record-type.

https://www.gnu.org/software/guile/manual/html_node/Records.html

HostSubmitted

Hoot's `define-record-type`, which is in (hoot records), supports parent types. The `define-record-type` exposed in `(scheme base)` conforms to R7RS-small and does not support additional flags like #:parent.

(1 edit) (+1)

Oops! I did not realize. @mirkohd must have been using that version then.

HostSubmitted

Records that can be subtyped must be marked as #:extensible. See this unit test of records for an example: https://gitlab.com/spritely/guile-hoot/-/blob/main/test/test-records.scm?ref_typ...

I haven't actually used this feature for myself, yet. I hope it works okay!

Submitted (1 edit)

Thanks!. That worked, replacing (scheme base) with (hoot records) and removing the  3rd parameter of the record fields (hoot records uses only two apparently).

(+1)

This also explains why I couldn't get it to work in my Guile image. It kept altering me that the record type "was final". I guess #:extensible means it's not final!