Today is the first day of the Spring Lisp Game Jam 2024, so I will use it to clarify the concept for a game that I will be working on.
First, the game is supposed to be using GRASP - the extensible structure Lisp editor of my own making (based on Kawa Scheme). I will use it as an opportunity to improve GRASP along the way.
Second, my goal in making this game is to create an application for my daughter to learn the Polish alphabet. This is the only thing that I care about. I realize that most people probably won’t find it fun to use, which is fine as long as they are not my daughter.
Third, even though I put a lot of effort into making sure that GRASP will work on windowed desktop and in terminal, the game will only be available for Android. The reason for this is that Android comes with text to speech and speech to text APIs that work with the Polish language. I haven’t found anything similar for the JVM.
Fourth, even though GRASP is an editor, it is still too buggy and incomplete to use it for the actual development, so I will only use it as a runtime environment, and I will probably do most of the actual work in Emacs (in Termux, on my phone).
Lastly, I’m going to use the GRASP repository for the development. I know that it might be a bit confusing to whoever comes here with the purpose of evaluating my submission, but I expect that I will be doing some contributions that are not directly related to the game, and synchronizing two separate projects would be a bit cumbersome - in particular because I haven’t yet figured out the model of distributing GRASP applications, and while I plan to do it one day, I don’t think GRASP is yet mature enough for that.
The name of the game is going to be “Literki”. It is going to consist of a few mini-games:
- assembling words from tiles with letters
- reading written text out loud
- optionally, drawing letters on the screen (if time allows)
Also, if I’m well on time, I could try adapting the app to languages and alphabets other than Polish (for example, I’d be happy to use it myself to learn Devanagari or Hiragana)
- develop LetterTile, LetterTileBoard and LetterTileSlot
- add a Play button to read the contents of LetterTileSlots out loud
- develop main menu
- we’ll see…
The entry point to the application will be the LetterTileBoard widget, which should implement the Maximizable interface. It is going to manage everything else.
(define-object (DragLetterTile tile::LetterTile board::LetterTileBoard)::Drag
(define (move! x::real y::real dx::real dy::real)::void
(set! tile:left (+ tile:left dx))
(set! tile:top (+ tile:top dy)))
(define (drop! x::real y::real vx::real vy::real)::void
(escape-with break
(for slot ::LetterTileSlot in board:tile-slots
(when (and (slot:below? x y)
(not slot:content))
(set! slot:content tile)
(board:scattered-tiles:remove tile)
(board:check-move!))))))
(define-object (LetterTile content::character)::Enchanted
(define left ::real 0)
(define top ::real 0)
(define label ::string #;< (list->string `(,content)))
(define inner ::Extent (painter:caption-extent label))
(define outer ::Extent
(let* ((horizontal-margin
::real (painter:caption-horizontal-margin))
(top-margin ::real
(painter:caption-margin-top))
(bottom-margin ::real
(painter:caption-margin-bottom)))
(Extent width: (+ inner:width (* horizontal-margin 2))
height: (+ inner:height (+ top-margin bottom-margin)))))
(define (extent)::Extent
outer)
(define (draw-border!)::void
(painter:draw-rounded-rectangle!
outer:width outer:height))
(define (draw-content!)::void
(with-translation (horizontal-margin top-margin)
(painter:draw-caption! label)))
(define (draw! context::Cursor)::void
(with-translation (left top)
(draw-content!)))
(define (below? x::real y::real)::boolean
(and (is left <= x < (+ left width)) ;>>
(is top <= y < (+ top height)))) ;>>
(Magic))
(define-object (LetterTileSlot content::LetterTile)
(define (draw-content!)::void
(if content
(content:draw-content!)
(invoke-special LetterTile (this) 'draw-border!)))
(LetterTile #\#))
(define-object (LetterTileBoard)::Maximizable
(define size ::Extent
(let* ((slot ::Tile (LetterTileSlot #!null))
(extent ::Extent (slot:extent)))
(Extent width: (* 5 extent:width)
height: (* 5 extent:height))))
(define (extent)::Extent size)
(define (set-size! width::real height::real)::void
(set! size:width width)
(set! size:height height))
(define scattered-tiles ::($bracket-apply$
java.util.LinkedList
LetterTile)
(java.util.LinkedList))
(define tile-slots ::($bracked-apply$
java.util.List
LetterTileSlot)
(java.util.ArrayList))
(define (draw! context::Cursor)::void
(for slot ::LetterTileSlot in tile-slots
(slot:draw! context))
(for tile ::LetterTile in scattered-tiles
(tile:draw! context)))
(define (tap! finger::byte x::real y::real)::boolean
(escape-with return
(for tile ::LetterTile in scatteted-tiles
(when (tile:below? x y)
(tile:utter!)
(return #t)))
(for slot ::LetterTileSlot in tile-slots
(when (and (slot:below? x y)
slot:content)
(slot:content:utter!)))))
(define (press! finger::byte x::real y::real)::boolean
(escape-with return
(for tile ::LetterTile in scatteted-tiles
(when (tile:below? x y)
(screen:drag! finger (DragLetterTile tile (this)))
(return #t)))
(for slot ::LetterTileSlot in tile-slots
(when (and (slot:below? x y)
slot:content)
(scattered-tiles:addLast slot:content)
(screen:drag! finger (DragLetterTile slot:content (this)))
(set! slot:content #!null)
(return #t)))
(return #f)))
(define (check-move!)::void
(WARN "check-move! not implemented for LetterTileBoard"))
(MaximizableWidget))
We need to create a module to store the jam-specific code, and also to modify the build script to pass it additional arguments (extra modules and the init script)
The preliminary version of the board seems to be in place. The task for today is to get it to run.
After modifying the build system, I managed to build the modified apk using the command:
./build-android.sh -i init/literki.scm extra/tile-board.scm
today we’d like to be able to set up the letters on the board, and possibly also to play the contents of the slots.
so, the goal for today is to implement a new method to the board, namely “setup-solution!”, which would do the following:
- assign the positions to appropriate slots
- scatter the letters around the board in such a manner that neither the slots nor the buttons are obscured by them
- add the buttons to play the solutuion, as well as the current content of the slots
There were some compilation issues that are now solved. It is now possible to place tiles inside slots
The Android client still renders stuff improperly (although on desktop everything looks good), so this needs to be fixed first.
Second, we need to add the “ear” button, to hear the solution. Placing a tile in a slot should cause the whole thing to be read.
OK, so regarding the Android client, the problem is that the painter is not set up properly within the interpreter on Android.
One conceivable solution would be to import the (editor interfaces painting) module (and possibly its dependencies) and to evaluate ‘(set-painter! the-view) before the evaluation of the contents of “init.scm”.
Perhaps it would be possible to import a module from the kawa interpreter object itself, without resorting to the “eval” function, but we’d need to analyze the sources of Kawa for this.
The code for importing seems to be contained in the kawa.standard.ImportFromLibrary module, which is a superclass of kawa.lang.Syntax, and contains a lot of complicated stuff which seems related to the compiler internals.
But it seems that the actual work of importing stuff is done using the handleImport static method, which in turn refers to gnu.expr.ModuleManager.
This stuff seems complicated, and at this moment, a call to eval seems inevitable.
I probably won’t be able to make it today, because it’s late. My minimal plan is to have two buttons
- one with ear 👂 and another with lips 👄
The whole word will be spoken whenever any of the slots is tapped.
I also tested the current version on my daughter, and it seems that I need to change the strategy of fitting tiles to slots - a tile should be placed in a slot also if over a half of its area covers that slot.
- add ear button (to hear the solution)
- add mouth button (to set up new solution)
- when start dragging tiles, play letter sounds
- when tapping tiles, play letter names