<?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: Gabriel Zayas</title>
    <description>The latest articles on DEV Community by Gabriel Zayas (@gazayas).</description>
    <link>https://dev.to/gazayas</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%2F633690%2F382f8f7b-e1d3-4dc2-920d-401359bd1836.jpeg</url>
      <title>DEV Community: Gabriel Zayas</title>
      <link>https://dev.to/gazayas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gazayas"/>
    <language>en</language>
    <item>
      <title>Hexaflare: Exploring Data Structures</title>
      <dc:creator>Gabriel Zayas</dc:creator>
      <pubDate>Sat, 15 Jun 2024 10:16:01 +0000</pubDate>
      <link>https://dev.to/gazayas/hexaflare-exploring-data-structures-1ond</link>
      <guid>https://dev.to/gazayas/hexaflare-exploring-data-structures-1ond</guid>
      <description>&lt;p&gt;I initially got into programming because I wanted to make a game, and although I do web development now, I got the chance to create a game with vanilla JavaScript and CSS called Hexaflare which I made back in early 2020. It was one of the most fun programming projects I've ever worked on, and the main idea of it came to me when I was studying data structures and algorithms.&lt;/p&gt;

&lt;p&gt;I don't consider myself a very good programmer, and there are a lot of problems with the code itself because it was more of an idea that I wanted to put on paper as a side project. I used a lot of global variables, I didn't use import/export statements to manage modules, and all around there's a lot that could be cleaned up. Either way writing the code was a lot of fun, but besides that, having a goal in mind, envisioning how to solve it from an abstract perspective first, and THEN applying those principles in code form was such a fun process that I wanted to write about it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;What is Hexaflare?&lt;/li&gt;
&lt;li&gt;Hexagons in Nature&lt;/li&gt;
&lt;li&gt;Hexagonal Tile Pattern (Data Structure?)&lt;/li&gt;
&lt;li&gt;Flare Star UI: Writing the Tile Pattern dynamically&lt;/li&gt;
&lt;li&gt;Embarking into the Unknown: Moving and Rotating Star Clusters&lt;/li&gt;
&lt;li&gt;Simulating Gravity&lt;/li&gt;
&lt;li&gt;Almost Giving Up&lt;/li&gt;
&lt;li&gt;Flare: Clearing Rings when Filled&lt;/li&gt;
&lt;li&gt;Timer, Levels, and Final Implementation
&lt;/li&gt;
&lt;li&gt;★ Code Blooper Compilation&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. What is Hexaflare?
&lt;/h1&gt;

&lt;p&gt;Hexaflare is a Tetris-like puzzle game in which you move blocks (star clusters) around the game board (the flare star), and then drop them to create rings. Whenever you create a ring the blocks disappear (like when you make a line in Tetris), and the remaining pieces gravitate toward the center.&lt;/p&gt;

&lt;p&gt;Play it here! &lt;a href="https://hexaflare.fly.dev"&gt;https://hexaflare.fly.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iup5oj58l0y8wr1nejk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iup5oj58l0y8wr1nejk.gif" alt="Image description" width="1422" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I knew that I wanted to make a fast-paced puzzle game, but the initial idea wasn't to actually include a gravity mechanic or be anything similar to Tetris. I primarily wanted the player to move pieces freely across the board, but I thought that was too easy. That's when Tetris came to mind, and I thought it would be cool to add a gravity mechanic instead.&lt;/p&gt;

&lt;p&gt;The idea came to me at night when I had the lights off right before I was about to go to bed. I took out my phone and started writing notes like crazy. It was a rush of inspiration, and I had so much momentum that I finished writing most of the logic in a few weeks. (There was a bug that almost made me give up on the project, but I'll get to that later in section 7). I took all my notes, and the next day I started working through what abstractions would be necessary to make the game a reality.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Hexagons in Nature
&lt;/h1&gt;

&lt;p&gt;Before I actually get into the details of how I made the game, I just want to say that hexagons tend to show up a lot in nature, and they're pretty cool. Without getting too much into it, I suggest watching &lt;a href="https://www.youtube.com/watch?v=Pypd_yKGYpA"&gt;this video&lt;/a&gt; which talks about honeycombs, hexagonal rock formations, and hexagonal insect eyes. Honeycombs, for example, show that hexagons are the shape that provide bees with the most space for storing honey, shown by &lt;a href="https://www.youtube.com/watch?v=kxDEcODUEP0"&gt;this video&lt;/a&gt; which also talks about the &lt;a href="https://en.wikipedia.org/wiki/Honeycomb_conjecture"&gt;honeycomb conjecture&lt;/a&gt;, a mathematical expression of the same concept. I gained a new appreciation for hexagons in nature by working on this project, so I thought it was worth mentioning here before I started explaining how I made the game.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Hexagonal Tile Pattern (Data Structure?)
&lt;/h1&gt;

&lt;p&gt;Before the idea to make this game came to me that one night, I had actually been studying heap data structures at the time using &lt;a href="http://algorithm.wiki/ja/app/?fbclid=IwAR07YaXmx7kTjaJ-RgoPEO1RF8w4qfnC_I30Ub0BATvFx4sR-cfzwx8TwrQ#0"&gt;this app&lt;/a&gt;. Without showing any code, it had a visualization of how to populate and pop values off of max heaps.&lt;/p&gt;

&lt;p&gt;In a max heap, the root is the biggest value in the set of data.&lt;/p&gt;

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

&lt;p&gt;Let's see how values are handled when populating a max heap from a set of data. In the app's visualization, you can see that each value is added one by one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlqckjjussab3flfyar6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlqckjjussab3flfyar6.jpg" alt="Image description" width="800" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8b7yr0jt5btlqvoju0g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8b7yr0jt5btlqvoju0g.jpg" alt="Image description" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see here that 2 is added underneath 5. It remains as a child node to 5 because 5 is a larger value. Let's see what happens when we append 7.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2ab3luwepwl7swxda6b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2ab3luwepwl7swxda6b.jpg" alt="Image description" width="800" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a rule we append it as a child, but each time we add a new element, we sort the set of data by comparing the parent and child. We then switch them if the child is a larger value, thus ensuring that the root of the set of data is always the largest value. So, here we switch 7 and 5.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fws01yewo023ma2nicqat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fws01yewo023ma2nicqat.jpg" alt="Image description" width="800" height="797"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I found so interesting about this is that it resembled gravity if you were to turn it on its head. In other words, the largest value would always "drop to the bottom" so to speak. That's when the idea sparked in my mind to create Hexaflare, and I wanted to create a data structure that would not just spread out two child nodes at a time, but that would fan out in a 360 degree fashion.&lt;/p&gt;

&lt;p&gt;Before I get to the prototypes, here's what the structure looks like. I found that each ring should have its own array, so the final product turned out to be a two-dimensional array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# The center (root) of the pattern.&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# Ring 1&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# Ring 2&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Ring 3&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, my journey to create this structure began. Since I had never seen anything like it before and I had no idea what it would actually look like at first, some of the prototypes were...interesting, to say the least.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fan5fkas4847oriblyfih.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fan5fkas4847oriblyfih.jpg" alt="Image description" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first idea I had was to make the structure look as close to a heap (or other data structures for that matter) as possible. That meant that the root would be at the top, and the child nodes would trickle downwards. You can see that in the screenshot above, and also in this one: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wtqdzocub9l317fsa0r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wtqdzocub9l317fsa0r.jpg" alt="Image description" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was definitely getting closer to what I envisioned. It starts with a root, 1, and then fans out to 6 child nodes which represent one ring. As you go down each child, you start to see something peculiar about the rings. Whereas the first ring has six elements (1 through 6) the second ring has 12 elements. Then, the following ring has 18 elements. I had found a new abstraction in the pattern: Every successive ring increases by exactly 6.&lt;/p&gt;

&lt;p&gt;Still, this structure really bothered me because it was mentally hard to grasp when trying to envision it as an actual, visual hexagonal tile pattern. It was at this point where I decided I needed to break out of the box of having a "top down" style data structure, and just let it be what it actually was; a pattern that extended in all directions two-dimensionally. That's when I came up with this pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc6ac7q50m3gd0c0kpqy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc6ac7q50m3gd0c0kpqy.jpg" alt="Image description" width="800" height="878"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see here, the first ring only has 6 elements, but when we get to the second ring, the following child nodes fan out to the side. Here is where I found a new abstraction the hexagon tile pattern. I found two abstractions that I called "Corner Hexagon" and "Side Hexagon." A corner hexagon is any hexagon that extends on a line directly from the root (In the screenshot above, you can see that the number 1 extends consistently to the right. This is the first Corner Hexagon in the ring, and each ring has a total of 6 Corner Hexagons). A Side Hexagon is anything else that is not a Corner Hexagon (Extends to the side away from the Corner Hexagon).&lt;/p&gt;

&lt;p&gt;Without getting into the details of how to find parent nodes, etc., this two dimensional array was eventually what I came up with to implement the gravity mechanic which takes a leaf node and traces it all the way to the root, and it was just a lot of fun to think through the process and experiment. I can say that I genuinely enjoyed learning about data structures like heaps and then applying similar concepts to my own code.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Flare Star UI: Writing the Tile Pattern dynamically
&lt;/h1&gt;

&lt;p&gt;Now that we have the structure, we can create it dynamically based on how many rings we want like this:&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;function&lt;/span&gt; &lt;span class="nf"&gt;generateFlareStar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number_of_rings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Generate Core (root)&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;// `flare_star` is the hexagonal tile pattern we'll be returning.&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;flare_star&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

  &lt;span class="c1"&gt;// The rings start from the second step&lt;/span&gt;
  &lt;span class="c1"&gt;// (In other words, the core itself isn't a ring)&lt;/span&gt;
  &lt;span class="c1"&gt;// `level` represents how many levels deep the structure is.&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;core&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;number_of_rings&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate new ring&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;// Here we're just populating each array with numbers.&lt;/span&gt;
    &lt;span class="c1"&gt;// 1-6 for ring 1, 1-12 for ring 2, 1-18 for ring 3, etc.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;flare_star&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ring&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;flare_star&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficulty of mapping this out in CSS was that all of the pixels had to be determined dynamically when generating each hexagon. I eventually ended up with the following two functions:&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="c1"&gt;// This generates the array&lt;/span&gt;
&lt;span class="c1"&gt;// 12 here determines how many rings the structure has&lt;/span&gt;
&lt;span class="nx"&gt;flare_star&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateFlareStar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// This generates the CSS&lt;/span&gt;
&lt;span class="nf"&gt;generateFlareStarUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;numberOfRings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flare_star&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example below, the game board starts with 12 rings, then I change it to 7 and refresh the page to make the size change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdcctz52jlprvgsxqs4a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdcctz52jlprvgsxqs4a.gif" alt="Image description" width="1434" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Embarking into the Unknown: Moving and Rotating Star Clusters
&lt;/h1&gt;

&lt;p&gt;This was absolutely the most difficult part of the development process, and I had no idea how to even start envisioning it to make it a possibility. Basically, I wanted to take these blocks, move them around the outside of the game board, and rotate them like in Tetris. Here's what some of those blocks look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti5k0pqlc3gtsk0pfji6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti5k0pqlc3gtsk0pfji6.jpg" alt="Image description" width="800" height="1124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I sat at my desk for about half an hour just thinking. Then I felt overwhelmed so I laid down on the floor for about another half an hour staring at the ceiling trying to figure out how to pull this thing off. Then I got a wave of inspiration and got to work.&lt;/p&gt;

&lt;p&gt;I ended up coming up with a Mapping abstraction that takes a map of the block and rotates the entire block of hexagons according to that map.&lt;/p&gt;

&lt;p&gt;The map itself keeps track of every hexagon, and has data concerning where it's supposed to move next. For example, this is a portion from one of the block types called Pleiades, a straight line of four hexagons:&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;PLEIADES_DATA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hexagon_2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center_of_gravity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rotation_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hexagon_3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center_of_gravity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rotation_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position_6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up_right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;position_1&lt;/code&gt; here for example represents the first position of each hexagon for the entire block. So, when rotating a block to &lt;code&gt;position_2&lt;/code&gt;, we get the x and y values from the original &lt;code&gt;div&lt;/code&gt; elements and pass the block data to the Mapper to calculate the new coordinates, thus rotating the entire block appropriately. If you're interested, you can check out the Mapper &lt;a href="https://github.com/gazayas/hexaflare/blob/main/javascript/mapping.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is a method in the code base called &lt;code&gt;rotate&lt;/code&gt; which has some more logic to make this a reality. It calls a method named &lt;code&gt;getCoordinatesByMap&lt;/code&gt; which can be found in the file linked to above, and this was by far the most brain-twisting part of the project. Still, I found it to be pretty fun even though it took a while to get it working properly.&lt;/p&gt;

&lt;h1&gt;
  
  
  6. Simulating Gravity
&lt;/h1&gt;

&lt;p&gt;As I started to work on the gravity mechanic, I ran into a peculiar issue. I found that, when trying to move a block of four hexagons to the center, the hexagons started to fly in different directions. This was because, although all of the hexagons were moving toward the center, each hexagon didn't have the same parent, so whereas one hexagon might move downwards to the left diagonally, another might move downwards to the &lt;strong&gt;right&lt;/strong&gt; diagonally. What I decided to do was determine the direction the &lt;strong&gt;entire&lt;/strong&gt; block shifts in according to one hexagon, which I called the center of gravity. In doing so, the entire block would move in the same direction, solving the issue. You can see in the pleiades data above that there is a boolean value which designates if the hexagon is the center of gravity or not. If you're interested in how the logic works, you can see the source code &lt;a href="https://github.com/gazayas/hexaflare/blob/main/javascript/gravity.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  7. Almost Giving Up
&lt;/h1&gt;

&lt;p&gt;There was one point where I ran into a bug that for the life of me I couldn't figure out. I implemented collision logic, so whenever you drop a block it gravitates toward the center, and if there is another block standing in its way, the block stops moving. The problem was that, very rarely, the block would move down one more ring, overlapping other blocks.&lt;/p&gt;

&lt;p&gt;I looked at the collision logic over and over again and couldn't find any issues. Granted, not writing tests was totally my fault... To be 100% honest, I wasn't sure HOW to write tests for logic like this, and since I was doing this as a side project on my own, I figured it would be okay with out it. Lesson learned.&lt;/p&gt;

&lt;p&gt;Anyways, I spent about a good two weeks (maybe even longer) trying to figure this bug out, and I made absolutely no progress during that time. I was so discouraged that I even started to remake the project in Unity. As I started from scratch, I realized it would take too long, so I sat down with the original code again.&lt;/p&gt;

&lt;p&gt;I eventually found out that the problem was with the gravity logic, and that the &lt;code&gt;while&lt;/code&gt; loop I had which pulled the block closer to the center with each call needed to be called as a &lt;code&gt;do&lt;/code&gt; block instead, checking the &lt;code&gt;while&lt;/code&gt; condition at the END of the loop.&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="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Gravitation logic...&lt;/span&gt;
  &lt;span class="nx"&gt;gravitation_direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getGravitationDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;center_of_gravity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gravitation_direction&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;starClusterCanGravitateToCore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;star_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gravitation_direction&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was frustrating that the bug was being caused by just one conditional that should've been on a different line, but I'm so glad I was able to figure it out and get this project across the finish line.&lt;/p&gt;

&lt;h1&gt;
  
  
  8. Flare: Clearing Rings when Filled
&lt;/h1&gt;

&lt;p&gt;Now that the gravity and collision mechanics were working, the last big step was to clear the rings and rack up the score. Similar to making a line in Tetris, first we check if any full rings exist, and if they do, we call the &lt;code&gt;flare&lt;/code&gt; method (which simply removes the hexagons) and we add 1 to the total flare count which checks if we should level up or not.&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="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fullRingExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flare_star_rings&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Flare 💫🔥&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;flare_star_rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ringIsFull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flare_star_rings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])){&lt;/span&gt;
      &lt;span class="nf"&gt;flare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flare_star_rings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="nx"&gt;current_flare_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="nx"&gt;TOTAL_FLARE_COUNT&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flare_count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TOTAL_FLARE_COUNT&lt;/span&gt;

      &lt;span class="c1"&gt;// Level up here.&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TOTAL_FLARE_COUNT&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;TOTAL_FLARE_COUNT&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;CURRENT_LEVEL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;CURRENT_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CURRENT_LEVEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;level&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CURRENT_LEVEL&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flare_star_rings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;level&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;flareTheCore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part wasn't so bad when compared to writing the Mapping  and gravity mechanics, but it was the next necessary step in the road map to finishing the game, and it was a fun portion to work on.&lt;/p&gt;

&lt;h1&gt;
  
  
  9. Timer, Levels, and Final Implementation
&lt;/h1&gt;

&lt;p&gt;You can see in the previous section how a player levels up, and I also used the level to determine how fast the timer moves. I was in the final stages of development and things were getting pretty buggy here, but it was also very satisfying to see it all come together. Here's a snippet from the progress bar (timer) code:&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="c1"&gt;// Just `if(UPDATE_TIMER){...}` below should be fine...&lt;/span&gt;
&lt;span class="c1"&gt;// Don't mind the messy code!&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processTimerEvents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GAME_OVER&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;GAME_PAUSED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;timer_speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.08&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;CURRENT_LEVEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UPDATE_TIMER&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;current_prog&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;timer_speed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;current_prog&lt;/code&gt; represents a percentage value which starts at 1 and counts backwards. Once it reaches 0, the block is dropped automatically, and if the player drops the block quicker, they get a higher score by factoring in the current progress of the timer.&lt;/p&gt;

&lt;p&gt;And that's about it! Part of me wants to talk about the &lt;code&gt;rotate&lt;/code&gt; method that I mentioned before in detail, but this article is already pretty long enough, so I'll just wrap things up here. Thanks for reading!&lt;/p&gt;

&lt;h1&gt;
  
  
  10. ★ Code Blooper Compilation
&lt;/h1&gt;

&lt;p&gt;The first thing I want to say is, the way I wrote this is NOT a conventional way of writing JavaScript. I actually didn't write any classes (I just organized all of the functions into their own files like &lt;code&gt;gravity.js&lt;/code&gt;, &lt;code&gt;flare.js&lt;/code&gt;, &lt;code&gt;mapping.js&lt;/code&gt;, etc.). I also have a lot of global variables 😬. There's no way I'd take this approach when working with a team, but this was a personal project that I just wanted to "get on paper" so to speak, and I think if I were to make it again, I would do it on Unity with a lot better programming principles. &lt;/p&gt;

&lt;p&gt;With that being said, here's some pretty bad code I wrote...&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;function&lt;/span&gt; &lt;span class="nf"&gt;togglePauseMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GAME_PAUSED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;GAME_PAUSED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;GAME_PAUSED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seriously? Maybe I should consider writing with this syntax instead:&lt;/p&gt;

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

&lt;p&gt;This one's similar.&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level_to_adjust&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level_to_adjust&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next one is a UI bug that was happening when trying to move the entire block to a corner of the game board, and I still technically haven't fixed it...but it works?? So I'm not going to touch it for the time being. SUPER brittle 😅&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="c1"&gt;// Redraw!&lt;/span&gt;
&lt;span class="c1"&gt;// Again, this is really hacky, but it works.&lt;/span&gt;
&lt;span class="c1"&gt;// ¯\_(ツ)_/¯&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;reverse_direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clockwise&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;counter-clockwise&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clockwise&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;moveAlongCorona&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;moveAlongCorona&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reverse_direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reverse_direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;star_cluster_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So there's that! I learned a lot and there's still a lot more for me to learn, but this was a very fun project, and I got to make a game! I'm not sure if I'll ever remake it in Unity one day, but it was a fun experience, and I'm glad I got to make it.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>datastructures</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Analyzing and Editing Ruby Source Code in Bullet Train</title>
      <dc:creator>Gabriel Zayas</dc:creator>
      <pubDate>Thu, 29 Jun 2023 11:55:22 +0000</pubDate>
      <link>https://dev.to/gazayas/analyzing-and-editing-ruby-source-code-in-bullet-train-3l8</link>
      <guid>https://dev.to/gazayas/analyzing-and-editing-ruby-source-code-in-bullet-train-3l8</guid>
      <description>&lt;p&gt;Hello, this is &lt;a href="https://instagram.com/gazayas"&gt;Gabe&lt;/a&gt; from the &lt;a href="https://bullettrain.co/"&gt;Bullet Train&lt;/a&gt; Core team. I’ve been on the team since December 2020, and it’s been a blast facing a lot of different challenges that don’t show up in traditional Rails application development. I say that because, if you’re familiar with our service, Bullet Train provides powerful tooling like Super Scaffolding to develop Rails applications quickly (please read &lt;a href="https://blog.bullettrain.co/super-scaffolding-complicated-namespacing/"&gt;this article in the Bullet Train blog&lt;/a&gt; to find out more about Super Scaffolding). This means that there are a lot of moving parts under the hood like transforming and copying templates for Tailwind-powered views, nested models, controllers, and more. For example, say you want to create a new model called &lt;code&gt;Project&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; rails generate model Project team:references title:string
&amp;gt; bin/super-scaffold crud Project Team title:text_field
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By just running these two commands, Bullet Train will add a new controller, a model, and views with Tailwind styling which all belong to a &lt;code&gt;Team&lt;/code&gt; and can perform CRUD actions. Super Scaffolding will also update your tests with new attributes to ensure everything is working properly, and it will even update your routes files so you can start using new your endpoints right away.&lt;/p&gt;

&lt;p&gt;Because there’s so much automated generation that takes place, it comes with its own set of challenges. In this article, I’d like to talk specifically about…&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A really difficult bug that I faced when working with the routes file scaffolding logic.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://github.com/gazayas/masamune-ast"&gt;new gem&lt;/a&gt; I’ve been making to increase the quality of our source code analyzation and replacement logic related to issues with that bug.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Brain Twister
&lt;/h2&gt;

&lt;p&gt;This bug was one of the most challenging programming problems I’ve faced, yet it was also one of the most fun ones to work on. If I remember correctly, I had to sit for about an hour and just take notes before I looked at any code again just to organize my thoughts before attacking the problem. The problem might not be too difficult for some, but it sure was for me. Here's the &lt;a href="https://github.com/bullet-train-co/bullet_train-super_scaffolding/pull/31"&gt;original pull request&lt;/a&gt; for reference.&lt;/p&gt;

&lt;p&gt;Basically, there was an issue where we were encountering namespace collisions when the same namespace existed within another part of the routes file. Take for instance the following model names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; rails g model Insight team:references name:string
&amp;gt; rails g model Personality::CharacterTrait insight:references name:string
&amp;gt; rails g model Personality::Disposition team:references name:string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Personality::CharacterTrait&lt;/code&gt; belongs to &lt;code&gt;Insight&lt;/code&gt;, so it should be namespaced under &lt;code&gt;Insight&lt;/code&gt;. However, &lt;code&gt;Personality::Disposition&lt;/code&gt; belongs to &lt;code&gt;Team&lt;/code&gt;, so we should be creating a new namespace block entirely like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:teams&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:insights&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:personality&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:character_traits&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:personality&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:disposition&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can probably already see how messy this can get. When Super Scaffolding these models, we were getting something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:teams&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:insights&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:personality&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:character_traits&lt;/span&gt;
      &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:disposition&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just imagine trying to run integration tests on a whole set of these models with duplicated namespaces. My head was spinning!&lt;/p&gt;

&lt;p&gt;To make a long story short, I ended up fixing this by taking each namespace and scoping it to its proper parent before actually trying to scaffold the proper routing.&lt;/p&gt;

&lt;p&gt;Granted, the code works fine now, but it’s bugs like this that made me reconsider how we should be analyzing and editing our Ruby source code. For example, in the pull request I mentioned earlier, you can find this in the &lt;code&gt;namespace_blocks_directly_under_parent&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;line_number&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent_indentation_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;namespace/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://github.com/standardrb/standard"&gt;standard&lt;/a&gt; gem for linting our Ruby code which takes away a lot of problems when trying to Super Scaffold new files, but you can probably already see why this code is problematic. In the regular expression here, we’re grabbing the indentation of the parent, and then just adding two spaces to it to find the namespace block that exists under it.&lt;/p&gt;

&lt;p&gt;What if developers are using four spaces instead of two? What if for some reason they added an extra space? Even if they ran the correct command to Super Scaffold a new model, it would break and it would take too long to debug the problem for something that isn’t even their fault.&lt;/p&gt;

&lt;p&gt;Also consider this line in the &lt;code&gt;scope_to_namespace_parent&lt;/code&gt; method in the same pull request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;namespace_line_number&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;namespace_line_number&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/ +namespace :&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the time we get to this line we’re already within the scope of the parent, so matching the namespace to this regular expression &lt;code&gt;/ +namespace :#{namespace}/&lt;/code&gt; gets the job done for us. The problem is, using regular expressions on strings like this can grab so many other things. What if the namespace is &lt;code&gt;:project&lt;/code&gt; and we had another namespace just called &lt;code&gt;:project_sites&lt;/code&gt; already? The regular expression would match the wrong namespace and it would be a pain to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning About Abstract Syntax Trees
&lt;/h2&gt;

&lt;p&gt;And well, I hadn’t really been proactively looking for a solution to all of this, but it had been sitting in the back of my mind for a while because this isn’t the only place where we use &lt;code&gt;match?&lt;/code&gt; or &lt;code&gt;gsub&lt;/code&gt; to edit files like this. What actually led me to start working on this new gem was the book &lt;a href="https://www.amazon.com/Ruby-Under-Microscope-Illustrated-Internals-ebook/dp/B00GK5P6L2/ref=sr_1_1?crid=8D7XPXGG40PZ&amp;amp;keywords=Ruby+under+a+Microscope&amp;amp;qid=1686639457&amp;amp;sprefix=ruby+under+a+microsc%2Caps%2C852&amp;amp;sr=8-1"&gt;Ruby Under a Microscope&lt;/a&gt;. I learned about the &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;abstract syntax tree data structure&lt;/a&gt;, Ruby’s LALR algorithm for parsing its source code, and some other cool things along the way. I talked to some Ruby commiters at Ruby Kaigi 2023 and they said &lt;a href="https://docs.ruby-lang.org/en/3.2/Ripper.html"&gt;Ripper&lt;/a&gt; is a semi-outdated library for looking at abstract syntax trees of your source code, and they suggested &lt;a href="https://github.com/ruby-syntax-tree/syntax_tree"&gt;syntax_tree&lt;/a&gt;. Someone on the Bullet Train team also suggested looking into &lt;a href="https://github.com/testdouble/referral"&gt;referral&lt;/a&gt;. They’re both awesome tools, but taking a quick look at them, I didn't quite find what I was looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  syntax_tree
&lt;/h2&gt;

&lt;p&gt;For example, I really like this about syntax_tree. Imagine we have a file named &lt;code&gt;add.rb&lt;/code&gt; with the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple enough. By running the following CLI command, we get the following output about the nodes in the abstract syntax tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; stree expr add.rb
SyntaxTree::Binary[
  left: SyntaxTree::Int[value: "2"],
  operator: :+,
  right: SyntaxTree::Int[value: "2"]
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ripper doesn't provide this kind of information, and it can be really hard to read, so having this CLI command available is pretty great.&lt;/p&gt;

&lt;p&gt;Also, the abstract syntax tree that &lt;code&gt;Ripper.sexp&lt;/code&gt; generates looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb(main):001:0&amp;gt; Ripper.sexp("2 + 2")
=&amp;gt; [:program, [[:binary, [:@int, "2", [1, 0]], :+, [:@int, "2", [1, 4]]]]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;syntax_tree’s AST output is much cleaner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; stree ast add.rb
(program (statements ((binary (int "2") + (int "2")))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great and all, and maybe I haven't delved deep enough into syntax_tree itself to find the tooling I was looking for, but I was finding that I wanted to analyze my Ruby source code in “real-time” so to speak and apply changes directly to the source code all within Bullet Train's code. Ripper also provided the line numbers I was looking for, which aren’t present in syntax_tree’s ast output. I wanted a library that I could require in Bullet Train to pinpoint each and every variable, symbol, or method invocation to transform on the spot and give back to the developers using Bullet Train.&lt;/p&gt;

&lt;h2&gt;
  
  
  Masamune
&lt;/h2&gt;

&lt;p&gt;This led me to start making &lt;a href="https://github.com/gazayas/masamune-ast"&gt;Masamune&lt;/a&gt;. Masamune takes the output from Ripper and translates it into a meaningful collection of nodes to pinpoint whatever data types or keywords we want to grab from our code. Take the following code snippet for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"masamune"&lt;/span&gt;

&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;&lt;span class="sh"&gt;
java = "java"
javascript = java + "script"
puts java + " is not " + javascript
# java
&lt;/span&gt;&lt;span class="no"&gt;CODE&lt;/span&gt;

&lt;span class="n"&gt;msmn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Masamune&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AbstractSyntaxTree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;msmn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variables&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [{:line_number=&amp;gt;1, :index_on_line=&amp;gt;0, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;2, :index_on_line=&amp;gt;0, :token=&amp;gt;"javascript"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;2, :index_on_line=&amp;gt;13, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;3, :index_on_line=&amp;gt;5, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;3, :index_on_line=&amp;gt;25, :token=&amp;gt;"javascript"}]&lt;/span&gt;

&lt;span class="n"&gt;msmn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strings&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [{:line_number=&amp;gt;1, :index_on_line=&amp;gt;8, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;2, :index_on_line=&amp;gt;21, :token=&amp;gt;"script"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;3, :index_on_line=&amp;gt;13, :token=&amp;gt;" is not "}]&lt;/span&gt;

&lt;span class="n"&gt;msmn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [{:line_number=&amp;gt;1, :index_on_line=&amp;gt;0, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;2, :index_on_line=&amp;gt;13, :token=&amp;gt;"java"},&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:line_number=&amp;gt;3, :index_on_line=&amp;gt;5, :token=&amp;gt;"java"}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Knowing exactly what lines these tokens are on is extremely valuable information when trying to edit files in Bullet Train, and if we can isolate the tokens to the exact word we’re looking for, we won’t have to worry about regular expressions matching similar strings that we don’t want (like the &lt;code&gt;:project&lt;/code&gt; and &lt;code&gt;:project_sites&lt;/code&gt; example I mentioned above).&lt;/p&gt;

&lt;p&gt;Since Bullet Train has been in development for a long time, I don’t foresee Masamune replacing all of this logic overnight. This also doesn’t account for the yaml and erb files which we also have to transform when using Super Scaffolding. However, I think this is a step in the right direction to replace code with more precision. I submitted a pull request to implement the new gem recently, and I’m excited to see how it will be used in the future. You can see &lt;a href="https://github.com/bullet-train-co/bullet_train-core/pull/312"&gt;here&lt;/a&gt; where Masamune pinpoints the lines each namespace its on by grabbing each &lt;code&gt;namespace&lt;/code&gt; method call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# `@msmn` here represents a Masamune::AbstractSyntaxTree&lt;/span&gt;
&lt;span class="c1"&gt;# object instantiated with the contents of config/routes.rb in a Rails app.&lt;/span&gt;
&lt;span class="n"&gt;namespaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@msmn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method_calls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;namespace_line_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namespaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:line_number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then go through each line in the file, check if that line has a &lt;code&gt;namespace&lt;/code&gt; invocation on it, and then retrieve the first symbol that comes right after the namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;namespace_line_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;namespace_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@msmn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;symbols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:line_number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;line_index&lt;/span&gt; &lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&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;This puts my mind at ease when I think of trying to retrieve and edit tokens in our Bullet Train files. There’s still a lot of more work to do on Masamune and we’re only scratching the surface of how to use it in Bullet Train, but it’s challenges like this that make me enjoy programming, and I would be glad if this allows us to produce a better developer experience and help others by making our code generation tools more precise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give syntax_tree Another Chance
&lt;/h2&gt;

&lt;p&gt;In Masamune, I’ve based all of my node classes off of the abstract syntax trees that &lt;code&gt;Ripper.sexp&lt;/code&gt; generates. I think there’s a lot of good information that can be gleaned from the syntax_tree gem though, so I might give it another chance and base my node classes off of the output from there instead. Either way, there’s a lot of work that can be done here, and if you decide to check out Bullet Train, I hope you enjoy how helpful Super Scaffolding is!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
