Six Challenges to Make a JavaScript-free Game
I recently discovered XSLT and have been wanting to use it to reduce the file size of CSS-only games. Interactive web pages without JavaScript can easily require a MB of repetitive CSS and HTML elements, but XSLT makes it possible to render from a template client-side.
The theme of JS13K11 A yearly competition to create a game that zips under 13KB. 2023 was the 13th century, so I decided to explore 13th century board games. I have already built a version of CSS chess so Fox and Geese was the best option.
There have been many variations and names over the centuries, but the general idea is that a fox is trying to eat geese by jumping over them and the geese are trying to trap the fox. I added an additional 13th century twist of forcing the animals to follow paths that roughly match Medieval trade routes within Europe.
You can try to play a version with multiple levels on the JS13K site, but it is buggy on lots of hardware/browsers. You should also check out the 100+ games that ranked ahead of mine22 Surprisingly, competing in a JavaScript competition without using any JavaScript is not a winning idea..
A simpler version (doesn't prevent the wrong player from playing, among other smaller differences) serves as the banner image above and should work on a wider variety of browsers. I don't recommend creating a full game unless you really enjoy constant frustration and confusion, but if you want to test your creativity I have identified 6 more approachable challenges.
You are, of course, free to explore variations of these challenges but things that seem easier might actually be harder/impossible. My only advice is to be creative and persistent.
Clock
In order to make the game work the way I wanted to without any JavaScript, it actually is essential to have some sort of maximum time per turn. That could be like a million seconds or something to make it irrelevant, but I chose to make it part of the gameplay.
An important part of CSS-only game design is turning constraints into reasonable game mechanics. When the added constraints don't make the game impossible, they can force creativity in a way that sometimes leads to a better game.
There are several relatively simple ways to implement a countdown feature. In order to get full credit for this assignment, the clock needs to reset on command. For bonus credit, try to implement a chess clock where each click pauses one clock and resumes counting down on the other.
Log Completed Levels
If you are creating a single page application then it is possible to temporarily track progress by using properly configured checkboxes, but players do not want to see their progress erased if they experience a hard refresh.
I'm only aware of one way to store information to survive a refresh so if you know the trick then there is not much to this challenge. If you want something harder, there are two additional features that I could not get quite right. I had initially planned to display check marks as well as having a third status for levels that were losses.
You don't need to use a question and answer format, but if you do then the pattern attribute combined with the :valid selector is handy. I like that regex patterns are possible so multiple answers can be accepted, but the selector is binary so you cannot implement multiple effects from one text box.
input:invalid + div {
display: none;
}
<input type="text" required="true" pattern="5|five|V"/>
<div>Visible only if input is 5 or five or V</div>
The celebration animation is also optional but really quite fun. Remembering that animations are possible might be helpful for other tasks, even if the output looks static.
Switch Turns
The game alternates between the fox and the geese taking turns. Every time a goose moves, we need to update two things: the location of the goose and the fact that it is now the fox's turn. CSS makes it very hard to make multiple changes with one click.
One option is to require the player to make two clicks. When I was creating my chess game I required players to click the clock button at the end of each turn to indicate their turn is over. For chess this action is okay since many players are used to pressing the clock in over the board games. When testing Fox and Geese, however, I kept getting annoyed at the extra clicks.
For some games, like tic-tac-toe, it is possible to determine turns based on the state of the board. When there are more X's than O's it is always O's turn so you can create CSS variables to keep the count.
div.container {
--oc: calc(var(--oc11) + var(--oc12) + var(--oc13) + var(--oc21) + var(--oc22) + var(--oc23) + var(--oc31) + var(--oc32) + var(--oc33));
--xc: calc(var(--xc11) + var(--xc12) + var(--xc13) + var(--xc21) + var(--xc22) + var(--xc23) + var(--xc31) + var(--xc32) + var(--xc33));
/*xc are the count of x's, oc are the count of o's*/
}
label.o {
z-index: 4;
}
label.x {
z-index: calc(5 - 2 * var(--xc) + 2 * var(--oc));
/*x's will be visible when var(--xc)=var(--oc)*/
}
The final option would have been to let players police themselves33 Like in the simple game the top of this page., which actually might the best approach given the tradeoffs.
Maybe you will come up with a simple mechanism, but I struggled to make this work and thought it impossible on several occasions before happening upon a workable solution. Don't give up on this challenge because at least for me solving it was the most rewarding part of the whole experience.
Goose Moves
You might think this looks easy, but nothing is ever as easy as it should be when making CSS interactive. Make sure every legal move is possible and illegal moves are not.
For bonus points, add multiple geese and make sure occupied squares are blocked off. If you want to add lots of squares and geese, then I would definitely encourage the use of a template system or XSL.
Fox Moves
The fox movements are annoying and require a decent amount of HTML/CSS to fully implement, but it is possible to handle a three square game by hand.
You can make a more complete solution that handles geese movements as well, but at a minimum there should be at least one goose at the start to allow for jumps.
For the JS13K game I required two clicks to move the fox one square, but that was based on an overly cautious assumption and one click is possible. For jumps, though, moving the fox and removing a goose requires two clicks. In order to do both while tracking legal moves without storing previous states, I'm quite sure a third click is necessary.
AI Opponent
The good news about the lack of JavaScript is that there is generally a limit to the strength of the AI opponent. My tic-tac-toe bot is good but not perfect. If you wish to create a tic-tac-toe AI, you can modify the ai.css file in this Replit.
I'm not sure how many more rules need to be added to make the AI unbeatable, but it is likely a reasonable goal. All we are really doing is setting the z-index to some computed value based on the board position.
CSS variables and calc() aren't always a magic bullet, but here they are. Basic arithmetic and max/min are possible on modern browsers so there are plenty of good options.
For Fox and Geese, a relatively simple logic for the fox performs well enough to make the game tough to beat. Creating an AI for the geese would be harder with the significant increase in possible moves, but there is only one fox with just a few possible moves.
I don't think that knowing how the AI works really helps since it doesn't do anything too surprising, but don't click the spoiler below if you don't want spoilers.
Fox Strategy Spoiler
- Always take a win.
- Jumps are always better than (non-winning) non-jumps.
- Among jumps, choose the one that sets up the most jumps on the next turn.
- When tied, choose the one that sets up the most moves.
- If no jumps, choose the move that sets up the most jumps on the next turn.
- When tied, choose the one that sets up the most moves.
More complex rules are possible with JavaScript, but this ruleset is easy to convert to an ordering of z-index values. Counting the number of possible jumps/moves on the next turn requires careful consideration but isn't particularly hard.
Conclusion
I encourage you to try to figure everything out yourself as it will be much more rewarding. There is some code here and here to look at but it likely won't make sense if you don't already know what to do.
The six challenges on this page have been coded without any templating. For more complex projects you will want to be able to loop over arrays and my recommendation is to learn a bit of XSLT. The only real advantage of XSLT is the smaller files so you can decide whether that is worth learning new syntax compared to using whatever you already know.
To see a variety of CSS only projects, visit CSSHole.com. I also have a few other CSS blog posts and code snippets.