---
title: "13 Sheep"
description: "13 Sheep happens to be a cute roll-and-write game designed by Moritz Dressler that involves protecting a bunch of sheep on a grid by placing small fences that have pre-defined s..."
pubDate: "2023-03-26"
authorName: "Neeldhara"
main-feature: true
sourceCategories: ["games", "exposition"]
sourcePath: "13sheep/index.qmd"
---

[13 Sheep](https://www.moritzdressler.de/en/13_Sheep) happens to be a cute roll-and-write game designed by Moritz Dressler that involves protecting a bunch of sheep on a grid by placing small fences that have pre-defined shapes.

![The Roll and Write Game](13sheep.png)

The initial position of a certain number --- typically 13, as the name suggests --- of sheep over a 7x7 grid is given. Some grid edges are not available because there happen to be bushes: so you cannot fence them. The bushes do not protect the sheep either, the wolf can slide under them.

Your goal is to enclose as many sheep as possible: the ones that are not fully protected by fences may, alas, be at considerable risk when the inevitable wolf attack happens.

There are three main constrianing factors in this game:

- The shape of the fences that you can draw are determined by the roll of a die;
- You cannot overwrite lines that are already drawn, you cannot also draw over bushes;
- You have ~11 chances to place your randomly gifted fence shapes.

You can find out more about the game [here](https://www.moritzdressler.de/en/13_Sheep), where you can generate board layouts to print and play as well.

I found this game a lot of fun to play, and I thought it would be lovely to be able to play it on a computer, where the user can decide how many rounds, sheep, and bushes they want to play with. Given also that chatGPT has been apparently [remarkably adept at coding games](https://twitter.com/ammaar/status/1637830530216390658?s=20), I figured I'd give it a shot^[Incidentally, I couldn't find a playable version like this one online. If you know of one please let me know and I'll be sure to add a pointer!].

I describe my conversation with chatGPT below, but if you just want to cut to the case and check the game out, you can find the playable version [here](https://13sheep.netlify.app/) and the source [here](https://github.com/neeldhara/13sheep/).

Here's what we finally built:

![The 13 Sheep UI](finalui.png)

And once you've finished playing you can share your accomplishment like so:

![One of my completed games... where we protect five sheep, can you figure out how many rounds?](download.png)

---

So, back to chatGPT. My first prompt was just a polite inquiry about willingness to participate in this exercise:

> **Note**
> 
> We want to code a game called 13 Sheep using HTML, CSS, and Javascript. Ready?


And well, it immediately made [this](https://13-sheep-v3-GPT4-v30.neeldhara.repl.co), based on the prompt above: a game where you get to count to 13 sheep. Huh. Promising.

In my next few prompts, I steered it away from the default game. The first few steps just involved setting up the UI:

> **Note**
> 
> Great start! Please make the main canvas a square and draw a 7 x 7 grid in it. I want all the corners of the cells to look like small + signs. Do not draw any grid borders yet.


> **Note**
> 
> Nice, how can I make the grid background mediumseagreen?


> **Note**
> 
> On load, I want 13 grid cells to be identified at random, and the image sheep.png to be placed at their centers, appropriately scaled to fit.


At this point I already had a nice-looking grid with 13 sheep on it (thanks to Flaticon for the cute sheep icon). The plus signs drawn on the boundary were oddly cropped out, so we fixed that next:

> **Note**
> 
> Ok this looks great. Can you update the drawcross function so that crosses are also drawn on the grid boundary? And make the canvas a little bigger if needed so the entire cross is visible?


This didn't immediately work but some nudging fixed it:

> **Note**
> 
> The crosses at the bottom are getting cut off and now the sheep are off center. Please fix?


That worked --- phew! Now we draw the bushes:

> **Note**
> 
> Very nice. Can you randomly select 7 cells now and for each such cell make one of the cell boundaries colored indianred?


Next we get the dice roll UI up:

> **Note**
> 
> Excellent. Now please add six divs below the canvas, centered, evenly spaced, and with a light grey circular background, with the text 1, 2, 3, 4, 5, and 6 written inside them. Note that the first div should have the text 1, the second should have 2, and so on. Make sure the divs have at least 10px spacing between them.
> 
> Below these divs, please add a large button in the shape of a rounded rectangle, with the text "Roll Dice" written on it. The background color for this button should be indianred with a transparency of 0.42.


This actually worked out of the box, the next step was to make the roll dice functional:

> **Note**
> 
> Super nice!
> 
> Now can you make it so that when the user clicks on the button labeled "Roll Dice", we generate a number between 1 and 6 at random, and highlight the corresponding circular div by changing its background to Lavendar, while also resetting any previous highlight?


So far, so good. Now all that was left was to get the fences drawn depending on what dice was rolled out.

> **Tip**
> 
> 
> # Fence Shapes Based on Dice Rolls
> 
> ![Dice Roll 1](1.png)
> 
> ---
> 
> ![Dice Roll 2](2.png)
> 
> ---
> 
> ![Dice Roll 3](3.png)
> 
> ---
> 
> ![Dice Roll 4](4.png)
> 
> ---
> 
> ![Dice Roll 5](5.png)
> 
> ---
> 
> ![Dice Roll 6](6.png)


This was perhaps the trickiest bit. My first attempt was this prompt here:

> **Note**
> 
> Great! Now can you update it so that:
> 
> if the outcome of the dice roll is a 1, the cursor should change to a shape that looks like a U, and nothing outside the grid should be clickable. If the user clicks on a grid cell, then the left, bottom, and right borders of the cell should change color and become a thick line colored in olivedrab. Once the user has clicked on a grid cell the cursor should change back to normal.
> 
> if the outcome of the dice roll is a 2, the cursor should change to a shape that looks like a long line, and nothing outside the grid should be clickable, and none of the cells in the last column should be clickable either. If the user clicks on a grid cell, then the bottom border of the cell should change color and become a thick line colored in olivedrab, and the the bottom border of the horizontally adjacent cell should change color and become a thick line colored in olivedrab. Once the user has clicked on a grid cell the cursor should change back to normal.


My hope was that the change of cursor would be a good way for the player to register that they need to move on the grid now. At this point, I was not thinking about the different orientations of the fences, because I was not sure about how to incorporate that into the UI (my first thought was to have the fences rotate on subsequent clicks until the player clicks on a confirm button, but that seemed too messy). I also did not give all six scenarios in one go since --- from recent experience --- the interface dies out on longer requests. You can prod it to continue from where it left off, but it's typically messy.

In any case, we could not get the change of cursor to work. At this point, note that the code is also not tracking what borders are colored, so even as we got things to mildly work, we were often overwriting fences and bushes. So anyway, at some point I gave up and it was back to square one, but smaller steps. First, instead of a change of cursor, I just requested a canvas background color change to subtly communicate a change of game state:

> **Note**
> 
> Once the dice roll button is clicked, can you change the background of the canvas to dodgerblue with transparency 0.42, and disable the roll dice button until the user clicks on a grid cell?


Then we added a round tracker:

> **Note**
> 
> Add the following text just below the "Roll Dice" button: "Roll a dice now, 11 rounds left". Every time the user clicks on "Roll Dice", change the text to "Place a fence now." Once the user clicks on a grid cell, change the text back to "Roll a dice now, 11 rounds left", but decrease the number of rounds by one each time. Can you do this?


This took some back and forth to get working, since it was getting some details mixed up, but it was finally alright. I had to prompt more to take care of some edge cases and minor details, for instance:


> **Note**
> 
> Anyway, can you make it so that when there are 0 rounds left, the text simply says "Game Over"?
> 
> Also when the roll dice button is not active, can it have a grey background? Only when it is not active.


Now we are back to actually trying to get the fences to be drawn. With some nervousness, I go:


> **Note**
> 
> Very nice. Now, if the dice roll is a 1 and the user clicks on a grid cell, can you do the following?
> 
> 1. let us say that a cell is valid if the top, left, and bottom borders of the grid have no visible borders.
> 2. if the cell is valid, then make its top, left, and bottom borders colored olivedrab.


At this point the code generated starts tracking what borders are visible. I did have to prompt it explicitly to update the earlier function that generated the bushes to feed into the visibility states. However, this went off pretty smoothly overall. For quite some time the fences drawn when the user clicked a grid cell were not the ones I wanted, but after a few nudges it got it about right.

Now it occured to me that I could actually show the orientations after the dice is rolled and the user can select one of them, and we can draw the appropriate fence after this selection is made.

> **Note**
> 
> Great. This works. Now below the instruction-text element, please add another div with ID "orientation-select" that begins with the text: "Pick an orientation". Below this, add four circular divs with a light grey backgorund with the content: "0", "90", "180", "270". This div should only be visible when the gamestate is "chooseFenceOrientation" or "placeFence".
> 
> Once a dice is rolled, the game state should change to chooseFenceOrientation and the user should be allowed to click any of these four divs. Once the user clicks one of the four divs, highlight it with the color burntorange and change the gamestate to placeFence. Store the choice of which div the user clicked on in a variable called fenceorientation and change the game state to placeFence.
> 
> Once the user clicks on a grid cell, clear the burntorange highlight and hide the "orientation-select" div, as it should not be visible any more.


Of course in the UI at the moment we just have these four numbers so it does not make a lot of sense to the player, so I whipped some images to show the user what fences would be drawn depending on what grid cell was clicked.

Now we come back to the fence drawing thing to account for the orientations as well:


> **Note**
> 
> In the handleGridClick() function, we want to implement the following functionality.
> 
> If the outcome is 1 and the orientation is 0, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if the top, left, and bottom borders of the grid have no visible borders.
> 2. if the cell is valid, then make its top, left, and bottom borders colored olivedrab.
> 
> 
> If the outcome is 1 and the orientation is 90, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if the right, left, and bottom borders of the grid have no visible borders.
> 2. if the cell is valid, then make its right, left, and bottom borders colored olivedrab.
> 
> 
> If the outcome is 1 and the orientation is 180, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if the right, top, and bottom borders of the grid have no visible borders.
> 2. if the cell is valid, then make its right, top, and bottom borders colored olivedrab.
> 
> 
> If the outcome is 1 and the orientation is 270, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if the right, left, and top borders of the grid have no visible borders.
> 2. if the cell is valid, then make its right, left, and top borders colored olivedrab.
> 
> Please remember to use the drawBorder function from before as you work out these cases. Also remember to update the grid visibility values, for the two relevant cells per border drawn. Thanks.


Getting this going for all the 20 different cases was really the bulk of the back and forth. While it got the overall functionality right, there would be small and frequent bugs in the details. For instance, if a cell's top border is being fenced, then the cell above it should have its bottom border's visibility updated: it knows this, but frequently got combinations wrong, and so I did have to read the code generated carefully. I either fixed it manually when the bugs were minor, or went back to requesting corrections by explaining the issue when it was off by a whole lot.

You can see that some of the prompts are longer than the corresponding code :D


> **Note**
> 
> Looks great! Lets continue with our updates. Please remember to account for all the cases when updating the grid visibility values.
> 
> If the outcome is 4 and the orientation is 0, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if its bottom and left borders are not visible and the horizontally adjacent cell (i.e, the cell to the right of it) also does not have a bottom border. if a cell is on the right-most column, it is automatically not valid.
> 2. if the cell is valid, then make its bottom and left borders colored olivedrab. Also make the bottom border of the horizontally adjacent cell colored olivedrab. Update the grid visibility values as appropriate.
> 
> If the outcome is 4 and the orientation is 180, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if its top border is not visible and the horizontally adjacent cell (i.e, the cell to the right of it) also does not have a top border and the horizontally adjacent cell (i.e, the cell to the right of it) also does not have a right border. if a cell is on the right-most column, it is automatically not valid.
> 2. if the cell is valid, then make its top border colored olivedrab. Also make the top border of the horizontally adjacent cell colored olivedrab. Also make the right border of the horizontally adjacent cell colored olivedrab.  Update the grid visibility values as appropriate.
> 
> If the outcome is 4 and the orientation is 90, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if its top and left borders are not visible and the vertically adjacent cell (i.e, the cell below it) also does not have a left border. if a cell is on the right-most column, it is automatically not valid.
> 2. if the cell is valid, then make its top and left borders colored olivedrab. Also make the left border of the vertically adjacent cell colored olivedrab. Update the grid visibility values as appropriate.
> 
> If the outcome is 4 and the orientation is 270, then do the following when the user clicks on a grid cell.
> 
> 1. let us say that a cell is valid if its bottom and right borders are not visible and the vertically adjacent cell (i.e, the cell to the top of it) also does not have a right border. if a cell is on the right-most column, it is automatically not valid.
> 2. if the cell is valid, then make its bottom and right borders colored olivedrab. Also make the right border of the vertically adjacent cell colored olivedrab. Update the grid visibility values as appropriate.


Maybe it'll soon be possible to just upload a picture of the game instruction booklet and let chatGPT do the rest of the heavy lifting. Until then, this was the best I could think of: maybe there's a shorter way? I did try using generic shape descriptions, but no luck on those.

Now the basic game was done, and there were three main features I'd have really liked to add at this point:

- Allow the user undo the previous fence placement.
- Count the number of protected sheep at the end.
- Make a version of this game where instead of rolling a dice, we let the user pick the fences they want: this can be useful for a multiplayer setting where we want the random choices to be the same for all players: so one player can roll the dice and the others can follow along.

I did try some of this: it's impressive that chatGPT gets the main ideas right the first two requirements (the use of an action stack for implementing undo, and recognizing that we need some kind of flood fill algorithm for the score). However, all the implementations ended up being somewhat buggy and after some wrestling I decided to push these extras to the roadmap. The deterministic version of the game should not be hard to make, even manually at this point, so I hope to come back to it soon.

After this I mainly asked for chatGPT to let the user customize the parameters of the game:


> **Note**
> 
> Can you make it so that when the page loads we ask the user (via an alert) for a number between 7 and 32 and set the number of rounds to the number specified by the user?


It got this immediately right, and I wanted some styling:

> **Note**
> 
> Can you add some css styling so that this prompt looks nice?


and it's response was:

> **Tip**
> 
> Unfortunately, you cannot directly style the default JavaScript prompt function. Instead, you can create a custom modal using HTML, CSS, and JavaScript to achieve a more visually appealing and flexible solution.
> 
> Here's an example of how you can create a custom modal to ask for the number of rounds:
> 
> ...


Good to know! The modals worked out of the box, and it was easy to extend them so they asked for the number of sheep and bushes as well. The last thing I wanted to add was a sharing feature:

> **Note**
> 
> Can you add code so that the user can share a screenshot of just the canvas once the game is over? So once the game is over, I want to show a link called "Share My Game!" The screenshot and options to share on social media should show up in a modal when the user clicks this link.


Now this it did remarkably well, except that when it tried to pass the entire image as a URL parameter, that didn't work out. When I conveyed this, the response was:


> **Tip**
> 
> I apologize for the inconvenience. The reason for this issue is that sharing an image directly using a URL is not possible due to security reasons. To share your game image on social media platforms like Facebook and Twitter, you'll need to use their respective APIs.
> 
> Here's a modified approach for sharing the image on Facebook and Twitter. You'll need to create Facebook and Twitter developer accounts and register your app to get the API keys.


Whoa... so ok, I don't have developer accounts and I was not going to follow through, but this was definitely interesting!

I did interleave responses from chatGPT with help from Ghostwriter, which is the intelligence built into repl.it out of the box. Ghostwriter's responses were usually not as sophisticated on comparable prompts, but on a couple of occassions chatGPT was completely lost on why something wasn't working, but Ghostwriter would figure it out when asked pointed questions about an error. There seems to be some potential that they will be a good team --- possibly also alongside Copilot, which I did not use at all here.

> **Caution**
> 
> # The Final Product
> Anyway, long story short, I cleaned up a little further on my own, and finally: the game is [here](https://13sheep.netlify.app), the source is [here](https://github.com/neeldhara/13sheep/).


My experience with chatGPT and Ghostwriter as pair programmers was --- I daresay --- rewarding overall. When I first thought of doing this, perhaps partially [intoxicated by all the hype](https://twitter.com/ShriramKMurthi/status/1637058628187312134), I figured I could get this done in a couple of hours at the most: I estimated twenty minutes for functional code, and about a 100 minutes for making up my mind about colors and fonts. This was a vast underestimate: I think I spent close to a good twelve hours (including a couple of early throw-away prototypes, and all the failed attempts on the flood filling) altogether^[At least I'm [not the only one](https://twitter.com/ammaar/status/1637830530216390658?s=20) spending hours in the plural.]... at some point it did get a little addictive, and perhaps there was a sunk cost argument for not letting go halfway through.

As someone who does not know javascript, I found this to be a part empowering, part learning, and mostly amusing experience. It felt like picking up a natural language by directly talking to mostly fluent speakers who made random and small mistakes. My overall sentiments were very similar to the process described [in this thread](https://twitter.com/ammaar/status/1637592014446551040), where [Ammaar Reshi](https://twitter.com/ammaar) uses GPT-4, Replit, MidJourney, and Claude to create a 3D space runner from scratch with ZERO [sic] knowledge of Javascript or 3D game programming: you should check this out if you are interested in a much more sophisticated demonstration for deploying AI tools to make games!

In its current form I think this also has interesting implications for how programming is taught: I hesitate to elaborate any further on this given how likely it is that any commentary will be obsolete by tomorrow. But in the very near term, I can imagine that reading, analyzing, and even learning alongside chatGPT could make for interesting experiences in the classroom.
