<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jesse Freeman</title>
    <description>The latest articles on DEV Community by Jesse Freeman (@jessefreeman).</description>
    <link>https://dev.to/jessefreeman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F724433%2F76de3a37-b639-41d8-a00e-d16c78947f3d.png</url>
      <title>DEV Community: Jesse Freeman</title>
      <link>https://dev.to/jessefreeman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jessefreeman"/>
    <language>en</language>
    <item>
      <title>I Made A Game In 72 Hours That Uses GitHub Issues To Crowd Source Maps</title>
      <dc:creator>Jesse Freeman</dc:creator>
      <pubDate>Thu, 14 Oct 2021 11:20:25 +0000</pubDate>
      <link>https://dev.to/jessefreeman/i-made-a-game-in-72-hours-that-using-github-issues-to-crowd-source-maps-13ba</link>
      <guid>https://dev.to/jessefreeman/i-made-a-game-in-72-hours-that-using-github-issues-to-crowd-source-maps-13ba</guid>
      <description>&lt;p&gt;&lt;strong&gt;Space Station 8&lt;/strong&gt; is a &lt;code&gt;Micro Platformer&lt;/code&gt; created &lt;a href="https://ldjam.com" rel="noopener noreferrer"&gt;in 72 hours for Ludum Dare 49&lt;/a&gt; based on a game I used to play on my original Macintosh called &lt;a href="https://en.wikipedia.org/wiki/Spacestation_Pheta" rel="noopener noreferrer"&gt;Spacestation Pheta&lt;/a&gt;. Space Station 8 is also heavily inspired by &lt;a href="http://make.bitsy.org" rel="noopener noreferrer"&gt;Bitsy&lt;/a&gt; and my Fantasy Console, &lt;a href="https://pixelvision8.com" rel="noopener noreferrer"&gt;Pixel Vision 8&lt;/a&gt;, which I used to create the game.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmi7riee5sxdkeicrzanj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmi7riee5sxdkeicrzanj.png" alt="Space Station Pheta"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike some of the other games I've created for game jams like Ludum Dare, there are no pre-made maps at all! Instead, the entire game revolves around a simple map editor, and I invited people to submit their games via a  &lt;a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository" rel="noopener noreferrer"&gt;custom GitHub issue&lt;/a&gt;. In addition, I use  &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; to automatically build new versions of the game every time I check in new code and update the wiki, which has all the instructions. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywksrgdl8bmpagk5hlec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywksrgdl8bmpagk5hlec.png" alt="Game Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through how the level editor works, how I designed the map files to be easy to share, and some of the tricks I used to leverage GitHub to help crowdsource my game's levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Maps
&lt;/h2&gt;

&lt;p&gt;The goal of Space Station 8 is to escape before you run out of oxygen. To do that, you will need to navigate the level, find the key, and make it to the exit in time. Each level is self-contained, and you are scored based on your ability to complete it within the amounted time and lives. There are also some prized gems you may want to collect on your way out while avoiding aliens and other deadly obstacles like spikes.&lt;/p&gt;

&lt;p&gt;When you first load Space Station 8 up, it loads up the default map. You can immediately begin editing it, or you can drag a &lt;code&gt;spacestation8.png&lt;/code&gt; map onto the game and load that up. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbxwspl5ikfnt2p6eb2l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbxwspl5ikfnt2p6eb2l.gif" alt="Drag Map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Space Station 8 map's should be at least &lt;code&gt;160&lt;/code&gt; x &lt;code&gt;132&lt;/code&gt; pixels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0rcco7ic8p8z4ieo20z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0rcco7ic8p8z4ieo20z.png" alt="Empty Map 160x132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also provide a map that is &lt;code&gt;160&lt;/code&gt; x &lt;code&gt;152&lt;/code&gt;, where the last two rows are the sprites for the map:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphzac2lfrb1vqcba82b1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphzac2lfrb1vqcba82b1.png" alt="Empty Map 160x152"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Whenever you go to the splash screen, Space Station 5 will automatically create a new &lt;code&gt;map.spacestation8.png&lt;/code&gt; for you in your &lt;code&gt;/Levels/&lt;/code&gt; folder:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;C:\Users\UserName\Documents\SpaceStation8\Levels\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MacOS&lt;/td&gt;
&lt;td&gt;/Users/UserName/SpaceStation8/Levels/&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;/Users/UserName/SpaceStation8/Levels/&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Space Station 8 shows you the map editor before you can play a map. The editor has two main areas, the map and the tile picker on the bottom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F480vs8k6nuqzam5cmwnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F480vs8k6nuqzam5cmwnr.png" alt="Map Editor Panels"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The white blinking box in the map area previews where the tile will be drawn. You can move the tile highlighter via a controller's d-pad, the keyboard arrows, or the mouse. By default, the mouse is hidden unless you move it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk3r73bj1xqh0g9pejst.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk3r73bj1xqh0g9pejst.gif" alt="Move Mouse In Editor.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can pick from any of the 20 tiles on the bottom of the screen to draw with. The tile with the white background is the currently selected tile. You'll also see it previewed on the map. Some tiles have a flip or alternative state. This can be used for changing the direction of an enemy or spikes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2qax6smlbewj22gm064.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2qax6smlbewj22gm064.gif" alt="Map Editor Alt Tiles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, for a map to work, you need three things: a player, a key, and a door. When you start the game, if these things are not present, it will bring you back to the editor. There is no cap on how many players, keys, or doors you can draw on the map, but the game will only use the first of each when it processes all the tiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submitting Maps With GitHub Issues
&lt;/h2&gt;

&lt;p&gt;Once you are happy with a map, you can share it with someone else by sending them the &lt;code&gt;map.spacestation8.png&lt;/code&gt; file. There are several ways to do this. You can attach it in a comment on the game's page, send it directly to them, or &lt;a href="https://github.com/PixelVision8/SpaceStation8/issues/new?assignees=jessefreeman&amp;amp;labels=map&amp;amp;template=new_map.md&amp;amp;title=" rel="noopener noreferrer"&gt;file a ticket on GitHub&lt;/a&gt; and include it there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj7zgbddmbz6rm2e6cxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj7zgbddmbz6rm2e6cxk.png" alt="Github Map Issue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to note is that some social networks like Twitter may compress the image. This will break the tilemap parser unless the image is pixel-perfect at 160 x 132 or 160 x 142. &lt;/p&gt;

&lt;p&gt;The default map template looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn81ojdfrpvshx8kt7ug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn81ojdfrpvshx8kt7ug.png" alt="Map Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, a map is comprised of a &lt;code&gt;20&lt;/code&gt; x &lt;code&gt;17&lt;/code&gt; tile grid where each tile is &lt;code&gt;8&lt;/code&gt; x &lt;code&gt;8&lt;/code&gt; pixels. The top row is ignored, and for map images that are &lt;code&gt;142&lt;/code&gt; pixels high, the last two rows (&lt;code&gt;18&lt;/code&gt; and &lt;code&gt;19&lt;/code&gt;) are used for the game's sprites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ifoj4rbpw83lyerhrrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ifoj4rbpw83lyerhrrc.png" alt="Map Sprites"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are &lt;code&gt;40&lt;/code&gt; sprites which you can also modify if you want to re-skin the game. Each sprite is fixed to a corresponding element in the game, so while you can re-skin the graphics, you will not make new ones or change the internal sprite mapping.&lt;/p&gt;

&lt;p&gt;The last thing to keep in mind when modifying the sprites, or even using drawing tools like &lt;a href="https://www.aseprite.org" rel="noopener noreferrer"&gt;Aseprite&lt;/a&gt; to modify map files, is that you will have to use the following four colors (&lt;code&gt;#2D1B2E&lt;/code&gt;, &lt;code&gt;#574B67&lt;/code&gt;, &lt;code&gt;#937AC5&lt;/code&gt;, #F9F4EA) .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz92e7jw8vgaqd7c8yzfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz92e7jw8vgaqd7c8yzfb.png" alt="Palette"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, I use some custom GitHub issues to make filing bugs, requesting features. If you are unfamiliar with the GitHub issue templates, they live inside your project's &lt;code&gt;.github/ISSUE_TEMPLATE&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1y72xn6voeaqu3ivcoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1y72xn6voeaqu3ivcoi.png" alt="Github Issue Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing I needed to do was clean up my project's issue categories to match up with the templates I used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0gcl43bhyw1o78km25p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0gcl43bhyw1o78km25p.png" alt="GitHub Issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, I created a &lt;code&gt;new_map.md&lt;/code&gt; file with the following markdown inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
name: Submit New Map
about: Did you create a level that you'd like to have included with the source code?
title: ''"
labels: map
assignees: jessefreeman
---

**Who Created the level**
Name: Jesse Freeman
Website: https://jessefreeman.com
Twitter: [@jessefreeman](https://twitter.com/jessefreeman)

**Screenshots**
Attach your map png, [add screenshots from PV8](https://docs.pixelvision8.com/pixelvisionos/screenshots) to be included in the game.

**Additional information**
Add any other context or screenshots about the map you'd like to share.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The markdown frontmatter directly maps to the issue form, and you can use it to automatically assign a label and whom the issues should go to on the project's team.&lt;/p&gt;

&lt;p&gt;Here is what the new issue looks like when someone wants to submit a new issue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp7jj7w53omv8rf468zl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp7jj7w53omv8rf468zl.png" alt="Submit Issue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I can go through each map and decide if I want to include it in the project or not. While I could have done this with pull requests, the thinking behind using an issue is that others can see the map before adding to the game and leaving comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc75cl29hgrlmhapjyoh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc75cl29hgrlmhapjyoh.png" alt="Filter Map Issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated Release With GitHub Actions
&lt;/h2&gt;

&lt;p&gt;In addition to creating custom issues, I use GitHub actions to automatically create new builds of my game whenever I push new code to the repo. I set this up early on in the game jam and having a simple continuous integration build system removed all the stress at the end of the game jam because I point people to the latest build, and it's always up-to-date by directing them to &lt;a href="https://github.com/PixelVision8/SpaceStation8/releases/latest/" rel="noopener noreferrer"&gt;SpaceStation8/releases/latest/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I finished the game jam, I added a link to the last build,  &lt;a href="https://github.com/PixelVision8/SpaceStation8/releases/tag/v1.17.0" rel="noopener noreferrer"&gt;tag v1.17.0&lt;/a&gt;, for a snapshot of the state of the game so I could continue to make improvements after the jam.&lt;/p&gt;

&lt;p&gt;I'll write a more in-depth article on how I use GitHub Actions, but at a high level, you need to create a specific folder in your repo, &lt;code&gt;.github/workflows/&lt;/code&gt; and put your &lt;code&gt;yml&lt;/code&gt; file inside. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjezqppkh9eqjdvlfllr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjezqppkh9eqjdvlfllr.png" alt="GitHub Workflow Folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was already using Gulp to package up my game anyway, so I made an action that does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a new release and tag ( &lt;a href="https://github.com/actions/create-release" rel="noopener noreferrer"&gt;actions/create-release@v1&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Checks out my code on Ubuntu ( &lt;a href="https://github.com/marketplace/actions/checkout" rel="noopener noreferrer"&gt;actions/checkout@v2&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Create a changelog ( &lt;a href="https://github.com/marketplace/actions/beast-changelog" rel="noopener noreferrer"&gt;jimschubert/beast-changelog-action@v1&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Install .net 5 ( &lt;a href="https://github.com/marketplace/actions/setup-net-core-sdk" rel="noopener noreferrer"&gt;actions/setup-dotnet@v1&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Install NodeJS and call my Gulp build task ( &lt;a href="https://github.com/marketplace/actions/setup-node-js-environment" rel="noopener noreferrer"&gt;actions/setup-node@v1&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Upload the zipped Win, Mac, and Linus build artifacts to the release ( &lt;a href="https://github.com/marketplace/actions/github-release-create-update-and-upload-assets" rel="noopener noreferrer"&gt;meeDamian/github-release@2.0&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;Update the wiki ( &lt;a href="https://github.com/marketplace/actions/update-wiki" rel="noopener noreferrer"&gt;OrlovM/Wiki-Action@v1&lt;/a&gt; )&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It sounds like a lot of steps, but once you get used to working with Gulp and GitHub Actions, the process to orchestrate all of this is relatively straightforward.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I took a break from working on my game to recharge, and I have plans to build a more robust level editor, clean up all the bugs I'm seeing from the maps people are submitting, and add some new features to make this micro platformer engine a bit more modular.&lt;/p&gt;

&lt;p&gt;I encourage you to think outside of the box and leverage all of these fantastic free, open-source tools and services to speed up your development. I don't know many people that would take half a day out of a game jam to build a CI system, but I've had too many last-minute panic attacks where my game won't build, or I can't upload it because the jam site is slammed by everyone else trying to upload their game at the same time.  &lt;/p&gt;




&lt;p&gt;If you like this project, please leave a 👍  and  &lt;a href="https://github.com/PixelVision8/SpaceStation8" rel="noopener noreferrer"&gt;⭐️ on Github&lt;/a&gt;. Don't forget to create a level and submit it because I plan on adding a way to browse, load, and edit other people's maps in the next major update I release!&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>opensource</category>
      <category>github</category>
      <category>pixelvision8</category>
    </item>
    <item>
      <title>Open Sourcing My Tools For Generating Tutorials From Source Code</title>
      <dc:creator>Jesse Freeman</dc:creator>
      <pubDate>Tue, 12 Oct 2021 19:40:18 +0000</pubDate>
      <link>https://dev.to/jessefreeman/open-sourcing-my-tools-for-generating-tutorials-from-source-code-10o4</link>
      <guid>https://dev.to/jessefreeman/open-sourcing-my-tools-for-generating-tutorials-from-source-code-10o4</guid>
      <description>&lt;p&gt;I have been working on my game engine,  &lt;a href="https://github.com/PixelVision8/PixelVision8"&gt;Pixel Vision 8&lt;/a&gt;, for the better part of 6 years now. One of the challenges of working on any sizeable open source project is writing all the documentation and tutorials. I've always been fascinated with designing automated build systems, and it occurred to me that I could create a tool to help me streamline this entire process. The idea was straightforward, could I analyze a code file and break it down into individual steps?&lt;/p&gt;

&lt;p&gt;I started using  &lt;a href="https://www.google.com/script/start/"&gt;Google Apps Script&lt;/a&gt; to automate converting Google Docs to generate markdown for  &lt;a href="https://github.com/PixelVision8/PixelVision8/wiki"&gt;PV8's Github wiki&lt;/a&gt;. I was having so much success with this workflow that I created a course for LinkedIn Learning called  &lt;a href="https://www.linkedin.com/learning/google-apps-script-for-javascript-developers"&gt;Google Apps Script for JavaScript Developers&lt;/a&gt;. Then I took the JS code out and put it into Atom as a plugin to do the tutorial generation in real-time. But last weekend, I  &lt;a href="https://www.npmjs.com/package/tutorial-writer"&gt;packaged up the core logic and uploaded it to NPM&lt;/a&gt;. I named this project Tutorial Writer, and now it works with any NodeJS build system I would want to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V1579oge--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr26rv6bbvikkzuhp1qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V1579oge--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr26rv6bbvikkzuhp1qq.png" alt="Tutorial Writer is now on Github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tutorial Writer is still more of a POC than a fully-fledged tool. While I continue to clean it up and add more features, I thought I'd walk through some of the logic that powers it. Let's start by looking at the following Lua script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This is a local variable&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="c1"&gt;-- Here is a function&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

     &lt;span class="c1"&gt;-- Here is a generic block of code&lt;/span&gt;
     &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Tutorial Writer is straightforward. Once you install it from NPM like so:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt; npm i tutorial-writer&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;You just need to reference the package and pass it the contents of a Lua file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tutorialWriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./examples/code.lua&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tutorialWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toMarkdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;code.lua&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tutorialWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;luaTemplate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;# Tutorial Writer Markdown&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tutorial Writer is going to take the script and convert it into a step by step tutorial in markdown like this:&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1
&lt;/h2&gt;

&lt;p&gt;Create a new file called &lt;code&gt;code.lua&lt;/code&gt; in your project folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;local&lt;/code&gt; variable called &lt;code&gt;total&lt;/code&gt; inside the &lt;code&gt;script&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a local variable&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;function&lt;/code&gt; called &lt;code&gt;Init()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;03&lt;/span&gt; 
&lt;span class="mi"&gt;04&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a function&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4
&lt;/h2&gt;

&lt;p&gt;Add the following code to the &lt;code&gt;Init()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;03&lt;/span&gt;      &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a generic block of code&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;

&lt;p&gt;When you are done, you should have the following code in the &lt;code&gt;code.lua&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;03&lt;/span&gt;      &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;04&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Pretty cool, right? Now let's walk through how Tutorial Writer actually works. If you want to see the full potential of Tutorial Writer, be sure to check out my  &lt;a href="https://hashnode.com/@pixelvision8"&gt;Pixel Vision 8 HashNode account&lt;/a&gt;, where I'm working on posting 50+ tutorials created from  &lt;a href="https://github.com/PixelVision8/Examples"&gt;the API examples&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S5VjEMKp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6625m41c2tb5n338e2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S5VjEMKp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6625m41c2tb5n338e2i.png" alt="Tutorial Writer running in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look at the sample Lua code above, you may notice it's formatted in a unique way. Each piece of code is on its own line, and above it is a comment. Let's look at the original code example and step through it, code block by code block.&lt;/p&gt;

&lt;p&gt;So we have three blocks of code here: a variable, a function, and some generic code that triggers something to happen. To keep the parser simple, I only look for a few common types of code blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Conditions&lt;/li&gt;
&lt;li&gt;Loops&lt;/li&gt;
&lt;li&gt;Generic Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm currently in the process of making Tutorial Writer more modular. Ideally, it should support different rules to parse something like C#, which my game engine also supports. For now,  Lua is easier to discuss, so let's talk about how Tutorial Writer breaks down the code.&lt;/p&gt;

&lt;p&gt;The first step is to split up all of the code based on the empty lines in the file. Each code group is converted into a &lt;code&gt;code block&lt;/code&gt; object, which I can process later. Here are the 4 code blocks Tutorial Writer sees:&lt;/p&gt;

&lt;h3&gt;
  
  
  Block 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This is a local variable&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block 2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Here is a function&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block 3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;          &lt;span class="c1"&gt;-- Here is a generic block of code&lt;/span&gt;
          &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block 4
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have each code block, the parser can loop through them and convert them into a step in the final tutorial. When I ask a code block to return markdown, it loops through each line and determines what kind of code it contains. Here's how block 1 is parsed.&lt;/p&gt;

&lt;p&gt;There are two lines in this code block:&lt;/p&gt;

&lt;h4&gt;
  
  
  Line 1
&lt;/h4&gt;



&lt;p&gt;&lt;code&gt;-- This is a local variable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Line 2
&lt;/h4&gt;



&lt;p&gt;&lt;code&gt;local total = 0&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;I have a set of regex patterns I use to determine what's in each line. Here are some of the patterns I search for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variable: &lt;code&gt;/(local)+\s+(\w+)/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Function: &lt;code&gt;/(function|\s)+\s+(\w+) *\([^\)]*\)/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Condition: &lt;code&gt;/if/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Loop: &lt;code&gt;/for/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Else: &lt;code&gt;/else/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;BlockEnd: &lt;code&gt;/end/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Determining if a line is a comment is easy because I just need to test the first two characters to see if they start with &lt;code&gt;--&lt;/code&gt;. If there is a comment in the code block, I simply pull that line out and save it for later. Then, based on the regex test, I assign a type to the entire code block and move on to the next one.&lt;/p&gt;

&lt;p&gt;If a block of code has a comment, that becomes the instructions at the bottom of the step. You can have any number of comments above a block of code as long as there are no empty lines between them. If the parser encounters a comment not attached to a code block, it's converted into a blockquote in markdown by adding &lt;code&gt;&amp;gt;&lt;/code&gt; in front of each line.&lt;/p&gt;

&lt;p&gt;Now that the block of code has been assigned a type of &lt;code&gt;variable&lt;/code&gt;, we need to look up the step template to convert it into markdown. I have another object that contains templates for each code type. Here are a few that I use for Lua:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;code&gt;Add the following code to the {0}:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Condition: &lt;code&gt;Add the following condition to the {0}:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Loop: &lt;code&gt;Create the following Loop:&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Function: &lt;code&gt;Create a new {0} called {1}():&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variable: &lt;code&gt;Create a new {0} variable called {1}{2}:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that I have the step template, I analyze the variable's line to try and determine its scope. In Lua, I simply search for &lt;code&gt;local&lt;/code&gt; since global is a bit more challenging to determine. Here is what the final markdown will look like since step one is always reserved for creating the code file itself:&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;local&lt;/code&gt; variable called &lt;code&gt;total&lt;/code&gt; inside the &lt;code&gt;script&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This is a local variable
&lt;/h2&gt;

&lt;p&gt;You'll notice I also assign the code a line number. The old programming books I read in the 80s heavily inspired this feature in Tutorial Writer. In these books, you'd have pages of code to type out with line numbers, so you didn't lose your place. Later on, this number plays an important role when I combine all of the steps back into the final step that presents all the code at once. &lt;/p&gt;

&lt;p&gt;Each code block is responsible for determining what line it belongs on, and I have to do some unique things behind the scenes to make sure the numbers are correct and increment at the right place, especially when nested in other blocks of code. &lt;/p&gt;

&lt;p&gt;Now we can look at block 2, which is a bit different because it's a function that has an opening and a close. In Lua, closing a statement requires an &lt;code&gt;end&lt;/code&gt; which you can see in block 4, but the parser isn't aware of it yet. When a block of code that requires an ending is encountered, the parser automatically adds 2 lines to the code block, an empty line, and the close statement like so:&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;function&lt;/code&gt; called &lt;code&gt;Init()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;03&lt;/span&gt; 
&lt;span class="mi"&gt;04&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a function&lt;/p&gt;




&lt;p&gt;The parser also sets a flag that it is now inside of a function, so when it encounters the following code block, it will reference the name of the function the code is being added to like so:&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4
&lt;/h2&gt;

&lt;p&gt;Add the following code to the &lt;code&gt;Init()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;03&lt;/span&gt;      &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a generic block of code&lt;/p&gt;




&lt;p&gt;Notice how the line was changed to &lt;code&gt;3&lt;/code&gt; even though the previous code block ended at &lt;code&gt;4&lt;/code&gt;. This is because the parser knows it's inside of a function and steps back a line to make sure you add the code inside of it correctly.&lt;/p&gt;

&lt;p&gt;The last thing the parser needs to handle is the remaining &lt;code&gt;end&lt;/code&gt; statement. Since this has been accounted for already in the function code block, it can just be ignored. &lt;/p&gt;

&lt;p&gt;At this point, the parser is done and needs to generate the final code, which looks something like this:&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;

&lt;p&gt;When you are done, you should have the following code in the &lt;code&gt;code.lua&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;03&lt;/span&gt;      &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tileIDs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;04&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;And there you have it, a completely automated way of converting code into a step-by-step tutorial. While I originally wrote this to help me create scripts for my LinkedIn learning courses, it's evolved into something powerful enough to do complete tutorials that most developers wouldn't even know were generated by a script. Even better, it forces me to write clean, well-documented code as a byproduct of formatting things in a way that the parser can read. &lt;/p&gt;

&lt;p&gt;While this is still a simple POC, I have plans to continue to build upon it and see how far I can take it.&lt;/p&gt;




&lt;p&gt;If you like this project, please leave a ❤️ and  &lt;a href="https://github.com/jessefreeman/tutorial-writer"&gt;⭐️ on Github&lt;/a&gt;. I'd love to help developers just getting started with technical writing and looking to share their work with others by simply automating the entire process!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>automation</category>
      <category>writing</category>
    </item>
  </channel>
</rss>
