| link | Last. 20260120T060954529Z |
This is a pure kitten recreation of my memory game in McCLIM common lisp. The intent is to closely reproduce the Kitten streaming html tutorial except it is my memory game.
NOTE I do not cover / did not use per session data here. The same memory cards later are shared with everyone, so if it looks like a ghost is controlling them, that is what is happening.
NOTE TWO: formerly, you had to refresh to see updates. I fixed that (if you are in a javascript enabled browser). I was missing a necessary
<div id='foo' morph> .. </div>which is the element that bears the update. Otherwise, I am leaving the article intact as it was published. I will put up a big memory game tomorrow.
This tutorial gives as its motivation and reference this page quoting Ted Nelson. I think this is a good target when web is preferred, such as indie game dev - like on itch.io players mostly play webgames.
Hypertexts: new forms of writing, appearing on computer screens, that will branch or perform at the reader’s command. A hypertext is a non-sequential piece of writing; only the computer display makes it practical.
This page shows my kitten (i.e. markdown and xml) and kitten-js source snippets. Jwz assured me on the mastodon that javascript is absolutely a lisp, and for me that settles it. Albeit, my favourite lisps are ansi common lisp and interlisp. Javascript also gets to party. Aral (Kitten) has good writing on whether javascript is fundamentally evil or not.
| link | Last. 20260120T061008345Z |
| index | visible | front | back |
|---|---|---|---|
| 1 | yes | queen | ??? |
this is my ./md/Card.fragment.md:
| index | visible | front | back |
|:-----:|:-------:|:-----:|:----:|
|__${SLOT.index}__ | ${SLOT.visible} | ${SLOT.front} | ${SLOT.back} |
which makes this xml to use which I use/generate verbosely here:
<${Card}>
<content for='index'>${kitten.db.Data.card1.index}</content>
<content for='visible'>${kitten.db.Data.card1.visible}</content>
<content for='front'>${kitten.db.Data.card1.front}</content>
<content for='back'>${kitten.db.Data.card1.back}</content>
</>
of which, you can see the results above.
| link | Last. 20260120T061017667Z |
| index | side-up |
|---|---|
| 1 | queen |
| index | side-up |
|---|---|
| 1 | ??? |
While the platonic form of a game card has both a front and a back (mine above also knows its index and visibility), in games like memory only one side of the card should be seen, according to game rules. In a game implementation, this means the player’s computer can only receive the side of the card that they can see at that time. This is done via the kitten conditional tutorial.
My ./md/CardSide.fragment.md:
| index | side-up |
|:-----:|:-------:|
|__${SLOT.index}__ |${SLOT.side_up}|
And this xml:
<${CardSide}>
<content for='index'>$kitten.db.Data.card1.index}</content>
<content for='side_up'>
<if $kitten.db.Data.card1.visible == 'yes'}>
<then>$kitten.db.Data.card1.front}</then>
<else>$kitten.db.Data.card1.back}</else>
</if>
</content>
</>
face down similarly.
| link | Last. 20260120T061028415Z |
In a game of memory, there are lots of cards. Normally cards are quite narrow and laid out left to right before top to bottom, but that is not a battle I am going fight here. I used a Kitten component, since this is an xml construction of the above rather than a new leaf of markdown. Lack of indentation strategy mine (integrating xhtmlambda, one day).
EDIT: I had to include this
divwith both anidandmorphfor it to work live.
const MemoryCards = () => kitten.html`
<div id='memory' morph>
${kitten.db.Data.CardSpread.map(card => kitten.html`
<${CardSide}>
<content for='index'>${card.index}</content>
<content for='side_up'>
<if ${card.visible == 'yes'}>
<then>${card.front}</then>
<else>${card.back}</else>
</if>
</content>
</>
`)}
</div>
`
Generating this parameterless xml, since it is defined by the kitten application database’s current data:
<${MemoryCards} />
| index | side-up |
|---|---|
| 1 | queen |
| index | side-up |
|---|---|
| 2 | queen |
| index | side-up |
|---|---|
| 3 | ace |
| index | side-up |
|---|---|
| 4 | ace |
| link | Last. 20260120T061108475Z |
I made separate buttons so that the button xml and function was not mixed up with something else.
..
${kitten.db.Data.CardSpread.map(card => kitten.html`
<button name='reveal' connect data=${'{value: '+card.index+'}'}>
Choose ${card.index}
</button>
`)}
..
that unfortunate <div /> is probably apocryphal but at some point it made my page load during development. Unfortunately, in my local environment I have to reload the browser to see the changes. Three steps forward, one step back as they say. My browser says response.replace is not a function and I do not know what it wants from me. Reloading the page still updates it, and the application database updates correctly ;_;.
On that note, that weird looking button named reveal xml parameterization implies this function exists to perform the update and send sanitized updated html back:
export function onReveal (data) {
let idx = data.value - 1;
let cur1 = kitten.db.Data.cursors[0];
let cur2 = kitten.db.Data.cursors[1];
if (! (cur1 == 'nothing'))
{ if (! (cur2 == 'nothing'))
{ if (! (kitten.db.Data.CardSpread[cur1].front
== kitten.db.Data.CardSpread[cur2].front))
{ kitten.db.Data.CardSpread[cur1].visible = 'no';
kitten.db.Data.CardSpread[cur2].visible = 'no';
}}}
if (cur1 == 'nothing') { kitten.db.Data.cursors[0] = idx;
} else { if (cur2 == 'nothing')
{ kitten.db.Data.cursors[1] = idx; }
else { kitten.db.Data.cursors[0] = idx;
kitten.db.Data.cursors[1] = 'nothing';}}
kitten.db.Data.CardSpread[idx].visible = 'yes';
this.send(kitten.html`<${MemoryCards} />`);
}
| link | Last. 20260120T061118799Z |
It is hopefully straightforward in some way to iron out that need to refresh even though the live update appears to be happening properly.
Then we can see that the leaves of our hyperdocument are fragments of markdown, which generate xml we use to write composite structures. In my case I will generate that from common lisp s-expressions via xhtmlambda like we were doing before for rss.
This adds up to a hyperconnected memory game in browser NOT SEPARATED BY SESSION YOU ARE SHARING THE CARDS WITH EVERYONE, kitteny but otherwise similar to my McCLIM repl one yesterday.
Kitten does not include drawing and animating the xml / web elements, so marquee text will have to come from somewhere else. However I think a reasonable span of graphical and animated games will be possible basically using sound, animated gifs and video and non-ambulatory web xml.
I think Aral and the other Kittenfolk basically want people to implement in Kitten as such (like how you write fragments of markdown, a popular human-writable-but-ends-up-as-a-web-page beast. I like the fact that the fragments end up as xml so I can just generate the xml of a lisp cons tree, and I get a technical kitten animal like this.
Once my websockets are well and truly working, and I introduce per session data, I will see how this integrates with itch.io’s externally hosted webgame games. On the topic of what a server does per se, while a memory game is simple enough to have the server just perform, I am also thinking of a webserver as a game table. Then, one of the players might be a game master who navigates game NPCs for the other players in real time. Or like the dealer at a card game table (the table is the server). The Game Master might be a good old fashioned ai whose mechanical flesh is hewn from my leonardo system.
See you on the Mastodon thread as always please.
Show tomorrow! I did not ask Kent or Ramin if they are free so it might just be me, probably recapitulating this article and reconnecting with my fellow lambdaMOO players.