<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Lynn Zheng - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Lynn Zheng - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 14:59:35 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/lynnzheng/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Make AI Art with Stable Diffusion ]]>
                </title>
                <description>
                    <![CDATA[ Stable diffusion makes it simple for people to create AI art with just text inputs. I just released a video course about Stable Diffusion on the freeCodeCamp.org YouTube channel. This article will introduce you to the course and give important setup ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/stable-diffusion-crash-course/</link>
                <guid isPermaLink="false">66d4602733b83c4378a517fc</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Mon, 14 Aug 2023 13:35:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/stableif.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Stable diffusion makes it simple for people to create AI art with just text inputs. I just released a video course about Stable Diffusion on the freeCodeCamp.org YouTube channel. This article will introduce you to the course and give important setup and reading links for the course.</p>
<p>This course focuses on teaching you how to use stable diffusion as a tool instead of going into the technical details. To understand some big terms like VAEs or embeddings, you will need to have some machine learning background and do some research on your own.</p>
<p>You will need to have access to a local GPU to try out the course material. Cloud access to GPU (with the exception of Google Colab) would work too. It's unfortunate, but Google Colab has banned Stable Diffusion code from running in their notebooks.</p>
<p>In this tutorial, we will cover the following topics:</p>
<ol>
<li><p>How to use Stable Diffusion</p>
</li>
<li><p>How to train your own model for a specific character or art style (LoRA models)</p>
</li>
<li><p>How to use Control Net, a popular Stable Diffusion plugin</p>
</li>
<li><p>How to use Stable Diffusion's API Endpoint</p>
</li>
</ol>
<p>Check out the course video below on the freeCodeCamp.org YouTube channel. Under the video are additional resources.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/dMkiOex_cKU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-install-stable-diffusion">Install stable diffusion</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">https://github.com/AUTOMATIC1111/stable-diffusion-webui</a></div>
<p> </p>
<h3 id="heading-download-models">Download models</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://civitai.com/">https://civitai.com/</a></div>
<p> </p>
<h3 id="heading-download-vae-for-model-counterfeit">Download VAE for Model "Counterfeit"</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://huggingface.co/gsdf/Counterfeit-V2.5/blob/main/Counterfeit-V2.5.vae.pt">https://huggingface.co/gsdf/Counterfeit-V2.5/blob/main/Counterfeit-V2.5.vae.pt</a></div>
<p> </p>
<h3 id="heading-i-download-embeddings">i Download Embeddings</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://huggingface.co/datasets/gsdf/EasyNegative">https://huggingface.co/datasets/gsdf/EasyNegative</a></div>
<p> </p>
<h3 id="heading-train-a-lora">Train a Lora</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://civitai.com/models/22530">https://civitai.com/models/22530</a></div>
<p> </p>
<h3 id="heading-further-reading">Further Reading</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stable-diffusion-art.com/prompt-guide/">https://stable-diffusion-art.com/prompt-guide/</a></div>
<p> </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stable-diffusion-art.com/samplers/">https://stable-diffusion-art.com/samplers/</a></div>
<p> </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stable-diffusion-art.com/controlnet/">https://stable-diffusion-art.com/controlnet/</a></div>
<p> </p>
<p>Disclaimer: We respect the work of artists and acknowledge that AI-generated art using Stable Diffusion is a tool that can be used to enhance creativity, but it does not replace the value of human creativity and originality.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Code RPG Version 1.5 is Now Playable with Hours of New Gameplay ]]>
                </title>
                <description>
                    <![CDATA[ Hello from the Learn to Code RPG dev team! We are Lynn, KayLa, and Nielda. And we've been hard at work building out new adventures for our characters. I'm excited to announce the launch of Learn to Code RPG v1.5, a year after the launch of Learn to C... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-code-rpg-1-5-update/</link>
                <guid isPermaLink="false">66d4601f3a8352b6c5a2aaaa</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning to code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Fri, 23 Dec 2022 02:43:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/splash-2-lowres-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hello from the Learn to Code RPG dev team! We are Lynn, KayLa, and Nielda. And we've been hard at work building out new adventures for our characters.</p>
<p>I'm excited to announce the launch of <strong>Learn to Code RPG v1.5</strong>, a year after the launch of Learn to Code RPG v1. (Fun fact: we're calling it v1.5 instead of v2 because we have grand plans for v2, which we plan to release in early 2023.)</p>
<h2 id="heading-what-is-learn-to-code-rpg">What is Learn to Code RPG?</h2>
<p><strong>Learn to Code RPG</strong> is an interactive visual novel game where you will teach yourself to code, make friends in the tech industry, and pursue your dream of working as a developer. 🎯</p>
<p>The game features:</p>
<ul>
<li><p>Hours of gameplay 🎮</p>
</li>
<li><p>Original art and music 🎨</p>
</li>
<li><p>1,000+ Computer Science quiz questions 📚</p>
</li>
<li><p>50+ achievements you can unlock 🏆</p>
</li>
<li><p>6 different endings 👀</p>
</li>
<li><p>10+ characters you can make friends with, and an adorable cat 🐱</p>
</li>
<li><p>Minigames 👾</p>
</li>
<li><p>A renown system, a money system, and fun items you can buy for your cat and to customize your room 🏠</p>
</li>
</ul>
<h2 id="heading-learn-to-code-rpg-v15-game-trailer">Learn to Code RPG v1.5 Game Trailer</h2>
<p>You can also watch the game trailer below and share the YouTube video with your friends:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/vDfcMD99Kdg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-you-can-download-the-game-and-play-it-for-free-its-available-for-pc-mac-and-linux-on-itchiohttpsfreecodecampitchiolearn-to-code-rpg-and-on-android-from-the-google-play-storehttpsplaygooglecomstoreappsdetailsidorgfreecodecamplearntocoderpgamphlenusampglusamppli1">You can download the game and play it for free. It's available for PC, Mac, and Linux on <a target="_blank" href="https://freecodecamp.itch.io/learn-to-code-rpg">itch.io</a>. And on Android from the <a target="_blank" href="https://play.google.com/store/apps/details?id=org.freecodecamp.learntocoderpg&amp;hl=en_US&amp;gl=US&amp;pli=1">Google Play Store</a>.</h2>
<p>If you'd like to learn more about the game itself and the development process, read on.</p>
<p>We'll walk you through the story, characters, graphics, and code. I'm sure you'll enjoy it. And it may even inspire you to code your own video game.</p>
<h2 id="heading-how-learntocoderpg-went-from-v1-to-v15">How LearnToCodeRPG Went From v1 to v1.5</h2>
<h3 id="heading-the-team">The Team</h3>
<p>When Lynn created v1, she was working solo on the game, juggling writing, coding, and some asset creation.</p>
<p>With this release, to give Lynn more time to focus on coding, KayLa took care of the writing. Nielda helped brainstorm features and create art assets.</p>
<p>Want to see a teamwork showcase? Here's one for the item shop. After purchasing furniture from the shop, the player will see the furniture in their room.</p>
<ul>
<li><p>Lynn programmed the shop, the items, and the room customization</p>
</li>
<li><p>KayLa and Nielda came up with ideas for the items</p>
</li>
<li><p>KayLa wrote fun flavor text for the items</p>
</li>
<li><p>Nielda created all the assets – the room and the items – by tracing over 3D assets and applying textures over them</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/room.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-the-learn-to-code-rpg-main-story">The Learn to Code RPG Main Story</h3>
<p>In v1 (or what we call the Prologue), the story started as our protagonist, Lydia, decided to get a job in the tech industry. She needed to learn to code, make friends in the industry, find mentors, tackle technical interviews, and eventually arrive at her goal – a shiny new developer job.</p>
<p>Building from the Prologue story, in v1.5 (or what we call Arc I) Lydia starts working as a full-stack developer. She now needs to interact with her new colleagues on a day-to-day basis and react to events that arise.</p>
<p>She will be faced with all the things a real-world software engineer faces – changes in project requirements, communicating with project managers, learning from senior developers, and so on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screen-Shot-2022-12-20-at-18.37.09.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Does this look like a familiar scene from working in tech to you?</em></p>
<p>Outside of work, Lydia can also go hang out at Hacker Space with friends she made back when she was first learning to code.</p>
<p>At Hacker Space, Lydia might run into old acquaintances who are also looking for a job. She can decide whether or not to give them a referral.</p>
<p>She can also give back to the community by mentoring high school students with their projects.</p>
<p>There is never a shortage of fun things to do at Hacker Space. 😄</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/itch2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-the-characters">The Characters</h3>
<p>To give the characters more depth, we brainstormed using the character card format shown below. Here's the character card for Lydia:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screen-Shot-2022-12-20-at-19.46.02.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Lydia meets many new colleagues in this arc and our character list has been greatly expanded.</p>
<p>When designing the characters, sometimes our artist Noa will experiment with hair color and style variations until we land on a design that we are happy with:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/mala-3.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Mala's different designs. Which is your favorite?</em></p>
<p>Here's also a sneak peek into one of the many characters and their many expressions:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/darius-4.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Darius's many faces</em></p>
<p>Of course, everyone's favorite, Mint the kitty, is still the key emotional support for this story arc. 🐱</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/mint_small.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-the-graphics">The Graphics</h3>
<p>Now moving on to other graphics besides character sprite art. Just like in v1, we again created background images by applying a watercolor-like filter to real-world images.</p>
<p>Since a lot of story takes place at the company Lydia is working at, we also tried to find stock images that are coherent in color scheme, like the ones below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Untitled-design.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Background images for the company Lydia's working at</em></p>
<h3 id="heading-the-code">The Code</h3>
<p>Just like in v1, I used the game engine that I'm most familiar with, <a target="_blank" href="https://www.renpy.org/">the Ren'Py Visual Novel Engine</a>.</p>
<p>During this year of development, a really exciting piece of news for the Ren'Py community is that Ren'Py 8 now supports Python 3. This is exciting to me.</p>
<p>Compared to Python 2.7 (which we used in Ren'Py 7.4 when we developed Learn to Code RPG v1), Python 3 brings in a lot of new features that positively impact our development.</p>
<p>This also means that I had to spend a little time to migrate from Python 2.7 to Python 3 in our project. (It was a small Git commit, trust me 🤓).</p>
<p>Now some exciting stats – Ren'Py's built-in linter is perfect for gathering stats for comparisons between v1 and v1.5:</p>
<pre><code class="lang-pgsql"># v1
Ren<span class="hljs-string">'Py 7.4.8.1895 lint report, generated at: Fri Dec 17 22:11:43 2021
Statistics:
The game contains 1,335 dialogue blocks, containing 15,390 words and 85,105 characters, for an average of 11.5 words and 64 characters per block.
The game contains 40 menus, 20 images, and 49 screens.

# v1.5
Ren'</span>Py <span class="hljs-number">8.0</span><span class="hljs-number">.3</span><span class="hljs-number">.22090809</span> lint report, <span class="hljs-keyword">generated</span> at: Tue <span class="hljs-type">Dec</span> <span class="hljs-number">20</span> <span class="hljs-number">19</span>:<span class="hljs-number">22</span>:<span class="hljs-number">05</span> <span class="hljs-number">2022</span>
<span class="hljs-keyword">Statistics</span>:
The game contains <span class="hljs-number">3</span>,<span class="hljs-number">339</span> dialogue blocks, containing <span class="hljs-number">41</span>,<span class="hljs-number">214</span> words <span class="hljs-keyword">and</span> <span class="hljs-number">220</span>,<span class="hljs-number">501</span>
characters, <span class="hljs-keyword">for</span> an average <span class="hljs-keyword">of</span> <span class="hljs-number">12.3</span> words <span class="hljs-keyword">and</span> <span class="hljs-number">66</span> characters per block.
The game contains <span class="hljs-number">68</span> menus, <span class="hljs-number">19</span> images, <span class="hljs-keyword">and</span> <span class="hljs-number">51</span> screens.
</code></pre>
<p>From the stats we can see that we've nearly tripled the story content. Woohoo! 🤩</p>
<h2 id="heading-next-steps-for-learntocoderpg-from-v15-to-v2">Next Steps for LearnToCodeRPG: From v1.5 to v2</h2>
<p>Hooray! After a whole year of development, we've taken v1 to new heights and are now presenting you with <strong>Learn to Code RPG v1.5</strong>.</p>
<p>What's more exciting: we are only just getting started. Just as Quincy always likes to say, the sky is the limit. ✈️</p>
<p>Here are some things you can look forward to in v2, or even sooner, between v1.5 and v2:</p>
<ul>
<li><p>🌎 Localization: All the text in v1 has been fully translated into Portuguese, and we have an active community working on translating the game into other world languages. You can help too, by starting <a target="_blank" href="https://contribute.freecodecamp.org/#/how-to-translate-files?id=translate-the-learntocode-rpg">here</a>.</p>
</li>
<li><p>🎭 More story and characters (shhh... we have 10+ characters planned and some drawn already)</p>
</li>
<li><p>📚 Expanded bank of quiz questions and spaced repetition to help you learn more efficiently.</p>
</li>
<li><p>💻 Auto-update from inside the game so that you can stay up-to-date with the latest bug fixes, features, and storylines.</p>
</li>
<li><p>... and more on our holiday wishlists! 🎁</p>
</li>
</ul>
<p>Last but not least, we hope you enjoy playing this game as much as we enjoyed developing it! 🥳</p>
<h3 id="heading-links">Links</h3>
<p>You can find the game on itch.io here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://freecodecamp.itch.io/learn-to-code-rpg">https://freecodecamp.itch.io/learn-to-code-rpg</a></div>
<p> </p>
<p>And here's the GitHub repo with all the code:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/freeCodeCamp/LearnToCodeRPG">https://github.com/freeCodeCamp/LearnToCodeRPG</a></div>
<p> </p>
<p>If you haven't read about how v1 of the game took shape, here's an article for you:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/learn-to-code-rpg/">https://www.freecodecamp.org/news/learn-to-code-rpg/</a></div>
<p> </p>
<p>And here's the official press kit for the game:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/learn-to-code-rpg-press-kit/">https://www.freecodecamp.org/news/learn-to-code-rpg-press-kit/</a></div>
<p> </p>
<p>If you are interested in building a Visual Novel Game yourself, check out this article of mine:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/use-python-to-create-a-visual-novel/">https://www.freecodecamp.org/news/use-python-to-create-a-visual-novel/</a></div>
<p> </p>
<p>We hope you enjoy learning what it's like to work in tech by playing the Learn to Code RPG. 🧑‍💻</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Code RPG – A Visual Novel Video Game Where you Learn Computer Science Concepts ]]>
                </title>
                <description>
                    <![CDATA[ Hi, everybody – Lynn here. It's my great pleasure to announce the launch of Learn to Code RPG, a project we've been developing in secret for the past eight months. Learn to Code RPG is an interactive visual novel game where you will teach yourself to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-code-rpg/</link>
                <guid isPermaLink="false">66d4602333b83c4378a517fa</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Wed, 22 Dec 2021 17:23:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/Splash-Art.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hi, everybody – Lynn here. It's my great pleasure to announce the launch of <strong>Learn to Code RPG,</strong> a project we've been developing in secret for the past eight months.</p>
<p><strong>Learn to Code RPG</strong> is an interactive visual novel game where you will teach yourself to code, make friends in the tech industry, and pursue your dream of becoming a developer. 🎯</p>
<p>The game features:</p>
<ul>
<li><p>Hours of gameplay 🎮</p>
</li>
<li><p>Original art and music 🎨</p>
</li>
<li><p>600+ Computer Science quiz questions 📚</p>
</li>
<li><p>50+ Easter Eggs you can discover 🚀</p>
</li>
<li><p>6 different endings 👀</p>
</li>
<li><p>Friendly characters and an adorable cat 🐱</p>
</li>
<li><p>Minigames! 👾</p>
</li>
</ul>
<p>This is a first release and we hope to add more content to it in the future. Future releases will have more <strong>characters, scenarios, side quests, art, music,</strong> and, yes, <strong>minigames</strong>. (CS quiz speed run and survival mode, anyone?) We are also planning to localize it into different languages. 🌎 The sky is the limit here. ✈️</p>
<h2 id="heading-you-can-download-it-and-play-it-for-free-on-itchiohttpsfreecodecampitchiolearn-to-code-rpg">You can download it and play it for free on <a target="_blank" href="https://freecodecamp.itch.io/learn-to-code-rpg">itch.io</a>.</h2>
<p>If you'd like to learn more about the game itself, my development process, and so on, read on. This is a very visual devlog (our game is a Visual Novel for a reason) and I'm sure you will enjoy it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/img_1-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Learn to Code RPG – A game where you role play learning to code</em></p>
<h2 id="heading-where-it-all-started">Where It All Started</h2>
<p>Let's start with a bit of background about me.</p>
<p>I've always loved story-rich video games since I was little. 🧒🏻</p>
<p>My interest in game development inspired me to major in Computer Science in college. In June 2021, I graduated from the University of Chicago with a joint Bachelor's and Master's degree in Computer Science.</p>
<p>In July 2021, as I was planning my move to San Francisco to start my career as a software developer, Quincy reached out to me about this game idea.</p>
<blockquote>
<p>A game where you learn to code, make friends, explore the tech culture, and eventually break into the tech industry. 🎯</p>
</blockquote>
<p>Although I dabble in game development engines like Unity and Ren'Py and have created small passion projects in my own time, this would be my first time building a game from the ground up, on a (mostly) one-person team. That is to say, I was a little overwhelmed by this opportunity to make my game development dream come true. 🤯</p>
<p>Well, you know the saying: If you’re offered a seat on a rocket ship 🚀, don’t ask what seat!</p>
<p>So I said yes and dove right in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-18-at-12.56.15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Who am I to turn down an offer from CupcakeCPU? 🧁</em></p>
<h2 id="heading-from-zero-to-hero-how-to-build-a-game-in-four-months">From Zero to Hero: How to Build A Game In Four Months</h2>
<h3 id="heading-the-story">The Story</h3>
<p>The story idea was pretty clear from the beginning: The hero/heroine makes the decision to learn to code, conquers obstacles along the journey, meets allies and mentors, and eventually gets to the grand prize – a shiny developer job.</p>
<p>I started with the classic writing framework of <a target="_blank" href="https://en.wikipedia.org/wiki/Hero%27s_journey">The Hero's Journey</a>, or, the 17-stage monomyth.</p>
<p>(Since I started working on this game, time and again I wish I'd taken at least one creative writing class in college. 😅)</p>
<p>Here's a glimpse into my outline for the first and the third stage out of the 17 stages, straight from my Google Doc:</p>
<table><tbody><tr><td><p>1. The Call to Adventure</p></td><td><p>the first stage of the hero’s journey often presents to the audience the current (and sometimes rather mundane) existence of the protagonist.</p></td><td><p><strong>Main Character (abbreviated MC) </strong>graduates and moves back with her parents. She isn’t really sure what her career is going to look like so she spends her days working gigs and browsing job openings. She has applied to some sales and consulting jobs but they turned her down.</p></td></tr></tbody></table>

<table><tbody><tr><td><p>3. Supernatural Aid</p></td><td><p>in this stage of the journey, the protagonist seeks out a sage-like figure and possibly gains a special item or skill in the process.</p></td><td><p><strong>Annika, </strong>MC’s best friend in college, calls MC one day. Annika is excited because she just got an entry-level web dev role, after spending 6 months to brush up her rusty CS skills (from auditing a few CS classes in college). Annika asks about how MC is doing; is delighted that MC is also considering learning to code; and encourages MC that she can do it if she has the right study method and resources.<br>Annika introduces MC to the resource she uses.</p></td></tr></tbody></table>

<h3 id="heading-the-characters">The Characters</h3>
<p>Including the main character which the player controls, we have four major characters in the game:</p>
<ul>
<li><p>The main character, <strong>Lydia</strong>, a recent graduate from college. (In future releases of the game we may be able to present a few different main characters the player can choose from.)</p>
</li>
<li><p><strong>Annika</strong>, the main character's college best friend</p>
</li>
<li><p><strong>Marco</strong>, who becomes the main character's mentor</p>
</li>
<li><p><strong>Layla</strong>, the main character's onboarding buddy at her first dev job</p>
</li>
</ul>
<p>I started designing the characters by collecting images on Pinterest. Then Quincy and I commissioned an artist online to create the character sprites and splash image.</p>
<p>In the images below, you can see the Pinterest character inspirations (copyright belongs to their original artists) and the final design side-by-side.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Untitled265_20211217211947.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Lydia inspiration art + final character card</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Untitled265_20211217212148.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Annika inspiration art + final character card</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Untitled265_20211217211928.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Layla inspiration art + final character card</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Untitled265_20211217211832.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Marco inspiration art + final character card</em></p>
<p>Now that we have the main cast, what else do we need to add more character depth to <strong>Lydia</strong>, so that she is not sitting in her room alone all day long grinding code? Maybe she could use a cat in her room? 🐱</p>
<p>And enter <strong>Mint</strong>, Lydia's cat. (Art by me as a makeshift artist so that our artist could focus on the characters. Digital art 🎨 is my second biggest hobby after game dev.)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/mint.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Mint says hi!</em></p>
<h3 id="heading-the-graphics">The Graphics</h3>
<p>With the character graphics done, you may think that concludes the bulk of the graphics. But not so fast! A visual novel is, as its name suggests, visual, and so it needs a lot more graphics to tell an appealing story.</p>
<p>For example, in this image below, besides the character sprites, there is the background image and some GUI components like the textbox.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-18-at-12.40.00.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Basic graphic components: GUI, character sprites, background</em></p>
<p>To create the background images, I applied special effect filters to stock images to add a watercolor-like texture. This way, the color scheme of our characters blends perfectly into that of the background.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Untitled266_20211217213638.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Top: stock image. Bottom: with filters</em></p>
<p>To illustrate the passage of time in a single day, I changed the lighting of the background images by applying color manipulation programmatically. (Check out <a target="_blank" href="https://github.com/freeCodeCamp/LearnToCodeRPG">our GitHub repo</a> if you are interested in the implementation details!)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/color.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Four modes of lightning</em></p>
<p>For a motivation boost, whenever I feel like procrastinating, I switch my creative gears and doodle miscellaneous items that show up throughout the game. 🤣</p>
<p>And that's how we got in-game cookies, toast, pizza, fried chicken, and more!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/ezgif.com-gif-maker-7-.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Yummy!</em></p>
<h3 id="heading-the-code">The Code</h3>
<p>I used the game engine that I'm most familiar with, <a target="_blank" href="https://www.renpy.org/">the Ren'Py Visual Novel Engine</a>. I reused a lot of code from my old passion projects – for example, <a target="_blank" href="https://gist.github.com/RuolinZheng08/b845f416ebda5b02ebc6b62379105564">blinking character sprites</a> and <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">a rhythm minigame</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/blink2.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Blinking characters 😉</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-19.33.39-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Rhythm Minigame. Can you get a perfect score?</em></p>
<p>I also incorporated some open-source Ren'Py code like <a target="_blank" href="https://wattson.itch.io/kinetic-text-tags">the code for kinetic text tags</a> and <a target="_blank" href="https://tacoen.itch.io/feather-icon">the code for feather icon text.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/awesome.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Kinetic text tag, which can be turned off for accessibility</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-22.05.37.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Feather icons are awesome for creating crisp, simplistic GUI</em></p>
<p>I will refrain from diving into the codebase here (because I won't know when to stop then 😆). Just know that it's a lot of code, both for the logic and the GUI. See the Ren'Py Lint report below.</p>
<p>Phew... Can we now move on to something more visual?</p>
<pre><code class="lang-pgsql">Ren<span class="hljs-string">'Py 7.4.8.1895 lint report, generated at: Fri Dec 17 22:11:43 2021
Statistics:
The game contains 1,335 dialogue blocks, containing 15,390 words and 85,105 characters, for an average of 11.5 words and 64 characters per block.
The game contains 40 menus, 20 images, and 49 screens.</span>
</code></pre>
<h3 id="heading-the-progress-tracker">The Progress-Tracker</h3>
<p>Even a one-person project needs a project manager, so why not be my own project manager?</p>
<p>I used Trello to track my process and collaborate with others. I even color-coded labels for different categories of tasks, like <em>coding, UI/UX, writing,</em> and so on as shown in the image below on the first card in the <strong>Backlog</strong> column.</p>
<p>And wow, isn't that a long scroll of tasks done? 😤</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/trello.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My Trello board</em></p>
<p>Everything in the <strong>TODO</strong> and <strong>Doing</strong> column is moved to <strong>Done</strong>, and that brings us to...</p>
<h2 id="heading-my-takeaway">My Takeaway</h2>
<p>Hooray! After eight months (four months of the idea brewing, plus four months of intense coding, writing, and art making), we present to you <strong>Learn to Code RPG. 🥳</strong></p>
<p>In four in-game months, <strong>Lydia</strong> has grown from <em>an aspiring engineer</em> into <em>an engineer with a dev job</em>. 🎯</p>
<p>In four real-world months, I've grown from <strong>an aspiring game developer</strong> into <strong>a game developer who's actually built a game.</strong> 👾</p>
<p>Naturally here comes the million-dollar question: What's my takeaway from this entire process?</p>
<p>Well, like any creative process, game development isn't easy. I'm extremely fortunate to have a team supporting me: our artist Noa who created the character art, Quincy who created the awesome original music tracks, and proofreaders and playtesters from the freeCodeCamp staff.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-22.26.07.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My GitHub contributions tell apart the days when I code vs. the days when I brainstorm or write or draw 🤪</em></p>
<p>I've grown both in terms of technical skills (by finding creative ways to build things in Ren'Py), non-technical skills (by acting as my own project manager), and more (by managing expectations, overcoming imposter syndrome, and seeking a work-life balance).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-18-at-12.33.25.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Do you know what imposter syndrome is? 👻 You bet I do!</em></p>
<p>It was by no means an easy ride, but the outcome is worth every second of hard work. More importantly, I look forward to you playing the game and providing feedback so that I can make the game better in future releases.</p>
<p>I hope you enjoy playing <strong>Learn to Code RPG</strong> as much as I've enjoyed creating it! 🙌</p>
<h2 id="heading-learn-to-code-rpg-links">Learn to Code RPG Links</h2>
<p>You can find the game on itch.io here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://freecodecamp.itch.io/learn-to-code-rpg">https://freecodecamp.itch.io/learn-to-code-rpg</a></div>
<p> </p>
<p><a target="_blank" href="https://github.com/freeCodeCamp/LearnToCodeRPG">And here's the GitHub repo with all the code</a>.</p>
<p>You can also watch the Game Trailer on YouTube and share it with your friends:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/vLK4fOeiIEk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Want to see what the game is like? Check out the <a target="_blank" href="https://www.youtube.com/watch?v=b_IDdQzPRR4">Let's Play with Ania and Lynn</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/b_IDdQzPRR4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>And <a target="_blank" href="https://www.freecodecamp.org/news/learn-to-code-rpg-press-kit/">here's the official press kit for the game</a>.</p>
<p>If you are interested in building a Visual Novel Game yourself, check out this article of mine:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/use-python-to-create-a-visual-novel/">https://www.freecodecamp.org/news/use-python-to-create-a-visual-novel/</a></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Code RPG – Press Kit ]]>
                </title>
                <description>
                    <![CDATA[ This is the press kit for the game, Learn to Code RPG. Fact Sheet Developer freeCodeCamp.org, a 501(c)(3) nonprofit Platform Windows/Mac/Linux Available on itch.io Price Free Source Code GitHub Release Date December 22, 2021 Developer Contact Quincy ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-code-rpg-press-kit/</link>
                <guid isPermaLink="false">66d46021677cb8c6c15f315d</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #Game Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Wed, 22 Dec 2021 17:22:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/Splash-Art-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This is the press kit for the game, <strong>Learn to Code RPG</strong>.</p>
<h2 id="heading-fact-sheet">Fact Sheet</h2>
<h3 id="heading-developer">Developer</h3>
<p><a target="_blank" href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjljfGir-z0AhVBKn0KHWCOB9cQFnoECAUQAQ&amp;url=https%3A%2F%2Fwww.freecodecamp.org%2F&amp;usg=AOvVaw2O9Sbs3zh9NHmRpWZrEZt-">freeCodeCamp.org</a>, a 501(c)(3) nonprofit</p>
<h3 id="heading-platform">Platform</h3>
<p>Windows/Mac/Linux</p>
<h3 id="heading-available-on">Available on</h3>
<p><a target="_blank" href="https://freecodecamp.itch.io/learn-to-code-rpg">itch.io</a></p>
<h3 id="heading-price">Price</h3>
<p>Free</p>
<h3 id="heading-source-code">Source Code</h3>
<p><a target="_blank" href="https://github.com/freeCodeCamp/LearnToCodeRPG">GitHub</a></p>
<h3 id="heading-release-date">Release Date</h3>
<p>December 22, 2021</p>
<h3 id="heading-developer-contact">Developer Contact</h3>
<p>Quincy Larson, <a target="_blank" href="mailto:quincy@freecodecamp.org">quincy@freecodecamp.org</a></p>
<p>Lynn Zheng, <a target="_blank" href="mailto:lynn@freecodecamp.org">lynn@freecodecamp.org</a></p>
<h2 id="heading-description">Description</h2>
<p><strong>Learn to Code RPG</strong> is an interactive visual novel game where you will teach yourself to code, make friends in the tech industry, and pursue your dream of becoming a developer. 🎯</p>
<p>In this game, your choices shape and drive the story forward: What will you do on a day-by-day basis? Learn to code? Work as a barista? Visit the Hacker Space? Or chill, relax in the park, or cuddle with a kitten over some video games?</p>
<p>Will you stick to your dream – getting a job in the tech industry – and more importantly, keep that job? The choice is in your hands.</p>
<h2 id="heading-features">Features</h2>
<ul>
<li><p>2+ hours of gameplay 🎮</p>
</li>
<li><p>Original art and music 🎨</p>
</li>
<li><p>600+ CS quiz questions 📚</p>
</li>
<li><p>50+ Easter Eggs you can tweet about 🚀</p>
</li>
<li><p>6 different endings 👀</p>
</li>
<li><p>Friendly characters and an adorable cat 🐱</p>
</li>
<li><p>Minigames! 👾</p>
</li>
</ul>
<h2 id="heading-dev-log-article">Dev Log Article</h2>
<p><a target="_blank" href="https://www.freecodecamp.org/news/learn-to-code-rpg/">https://www.freecodecamp.org/news/learn-to-code-rpg/</a></p>
<h2 id="heading-screenshots">Screenshots</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Splash-Art-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Learn to Code RPG Splash art</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-to-Code-RPG-Main-Menu-copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Learn to Code RPG Game Menu</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-18-at-12.40.00-copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Hanging out at the Hacker Space with your best friend Annika</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-09.43.14.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Chill at home with your cat Mint</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-20-at-18.30.58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Take CS quizzes</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-09.43.50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Quiz question explanation screen</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-18.51.41.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>NPCs</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-17-at-19.33.39.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Rhythm Minigame</em></p>
<h2 id="heading-videos">Videos</h2>
<h3 id="heading-trailer">Trailer</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/vLK4fOeiIEk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-lets-play">Let's Play</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/b_IDdQzPRR4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-logo">Logo</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/img_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/img_0.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-character-cards">Character Cards</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Lydia.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Annika.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Marco.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Layla.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Mint.png" alt="Image" width="600" height="400" loading="lazy"></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Coding Interview Graph Traversal Crash Course – The Only One You'll Ever Need ]]>
                </title>
                <description>
                    <![CDATA[ Are you preparing for coding interviews? I designed a crash course series to help you out. I'm Lynn, a software engineer and a recent graduate from the University of Chicago. This is the third course in my Coding Interview Crash Course Series. Feel f... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/coding-interview-graph-traversal-crash-course-the-only-one-youll-ever-need/</link>
                <guid isPermaLink="false">66d460157df3a1f32ee7f86d</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding interview ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 09 Sep 2021 17:24:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/Coding-Interview-Series-1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you preparing for coding interviews? I designed a crash course series to help you out.</p>
<p>I'm Lynn, a software engineer and a recent graduate from the University of Chicago. This is the third course in my Coding Interview Crash Course Series. Feel free to check out <a target="_blank" href="https://www.youtube.com/channel/UCZ2MeG5jTIqgzEMiByrIzsw">my YouTube channel, Lynn's DevLab</a>, to stay updated on this series.</p>
<p>This crash course is about <strong>Graph Traversal.</strong> If you just want to dive right in, you can find the course here (and linked at the bottom of this article). If you want a little more info, read on. 😎</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/d31vGF-Z69c" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-introduction">Introduction</h2>
<p>We will cover two common graph traversal techniques: <strong>Depth-First Search (DFS)</strong> and <strong>Breadth-First Search (BFS).</strong></p>
<p>We will first learn about how they work and how to implement them in code. Then we will see the algorithms in action by solving a LeetCode problem as well as looking at how I applied Graph Traversal when implementing an algorithm for my game, <a target="_blank" href="https://github.com/RuolinZheng08/unity-clicky-galaxy"><strong>Clicky Galaxy</strong></a> (also my first game in Unity when I was learning Unity 😉).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/clicky.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Clicky Galaxy, a game I made when learning Unity</em></p>
<h2 id="heading-course-outline">Course Outline</h2>
<p><a target="_blank" href="https://youtu.be/d31vGF-Z69c">This course video</a> runs for 1 hour and features:</p>
<ul>
<li><p>A high-level description of Graphs, DFS, and BFS</p>
</li>
<li><p>DFS implementation</p>
</li>
<li><p>BFS implementation</p>
</li>
<li><p>How to find a path between a source and a destination node</p>
</li>
<li><p>LeetCode Demo: 785. Is Graph Bipartite?</p>
</li>
<li><p>Clicky Galaxy Demo and Graph Traversal in Unity C# 🚀</p>
</li>
</ul>
<p>Graphs are a favorite interview subject among top tech companies like Google, Microsoft, and Facebook. More importantly, it's also fun and useful in practical software engineering like Game Development. Let's crunch this topic together in my course!</p>
<h2 id="heading-definition-of-a-graph">Definition of a Graph</h2>
<p>We will use the following graph to show the traversal path for the two traversal algorithms.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-21-at-08.41.06.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We may represent the graph by mapping each node to its list of neighbors, as shown in this Python snippet:</p>
<pre><code class="lang-python">graph = {
    <span class="hljs-number">0</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">4</span>],
    <span class="hljs-number">1</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>],
    <span class="hljs-number">2</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">3</span>],
    <span class="hljs-number">3</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>],
    <span class="hljs-number">4</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>]
}
</code></pre>
<h2 id="heading-how-to-use-depth-first-search">How to Use Depth-First Search</h2>
<p>As its name suggests, DFS prioritizes depth in its search.</p>
<p>For a given node (say 1), after visiting one of its neighbors (say 0), instead of visiting the rest of the neighbors (nodes 2, 3, and 4) immediately, it caches those neighbors and immediately resumes its visit on 0's neighbors. Only when it has exhausted the depth will it return to those cached neighbors.</p>
<h3 id="heading-iterative-implementation">Iterative Implementation</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dfs</span>(<span class="hljs-params">graph, start</span>):</span>
  visited, stack = set(), [start]
  <span class="hljs-keyword">while</span> stack:
    node = stack.pop()
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> node <span class="hljs-keyword">in</span> visited:
        <span class="hljs-comment"># perform some operations on the node</span>
        <span class="hljs-comment"># for example, we print out the node</span>
        print(<span class="hljs-string">'Now visiting'</span>, node)
    visited.add(node)
    <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
      <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> neighbor <span class="hljs-keyword">in</span> visited:
        stack.append(neighbor)
  <span class="hljs-keyword">return</span> visited
</code></pre>
<p>In this template, the commented lines are where we can perform some operations on the node: for example, printing out its value, checking for equality, and so on.</p>
<p>We keep track of a set named <strong>visited</strong> to avoid visiting the same node multiple times where there is a cycle in the graph, like in our example graph above.</p>
<p>Running this code on the graph we defined above results in the output below:</p>
<pre><code class="lang-python">Now visiting <span class="hljs-number">0</span>
Now visiting <span class="hljs-number">4</span>
Now visiting <span class="hljs-number">3</span>
Now visiting <span class="hljs-number">2</span>
Now visiting <span class="hljs-number">1</span>
</code></pre>
<h2 id="heading-how-to-use-breadth-first-search">How to Use Breadth-First Search</h2>
<p>BFS prioritizes breadth in its search. For a given node, it visits all of its immediate neighbors before moving onto the neighbors' neighbors.</p>
<h3 id="heading-iterative-implementation-1">Iterative Implementation</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bfs</span>(<span class="hljs-params">graph, start</span>):</span>
  visited, queue = set(), deque([start])
  <span class="hljs-keyword">while</span> queue:
    node = queue.popleft()
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> node <span class="hljs-keyword">in</span> visited:
        <span class="hljs-comment"># perform some operations on the node</span>
        print(<span class="hljs-string">'Now visiting'</span>, node)
    visited.add(node)
    <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
      <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> neighbor <span class="hljs-keyword">in</span> visited:
          queue.append(neighbor)
  <span class="hljs-keyword">return</span> visited
</code></pre>
<p>Running this code on the graph we defined above results in the output below:</p>
<pre><code class="lang-python">Now visiting <span class="hljs-number">0</span>
Now visiting <span class="hljs-number">1</span>
Now visiting <span class="hljs-number">4</span>
Now visiting <span class="hljs-number">2</span>
Now visiting <span class="hljs-number">3</span>
</code></pre>
<h2 id="heading-how-to-find-a-path-between-a-source-and-a-destination">How to Find a Path Between a Source and a Destination</h2>
<p>Now that we've seen how to use DFS and BFS to traverse the entire graph and print out the whole traversal history, we can make some small changes to the templates to find a <strong>path</strong> between any two nodes in the graph (if such path exists).</p>
<p>On a graph where each edge has the same weight, BFS is equivalent to <strong>Dijkstra's Shortest Path Algorithm</strong>. It finds the shortest path (path with the fewest number of nodes) between a source node and a destination node. This is a nice property that a path search with DFS doesn't have.</p>
<p>Here's how we adapt the DFS template to return a path given a <strong>src</strong> and a <strong>dst</strong> node:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dfs_path</span>(<span class="hljs-params">graph, src, dst</span>):</span>
  stack = [(src, [src])]
  visited = set()
  <span class="hljs-keyword">while</span> stack:
    node, path = stack.pop()
    <span class="hljs-keyword">if</span> node <span class="hljs-keyword">in</span> visited:
      <span class="hljs-keyword">continue</span>
    <span class="hljs-keyword">if</span> node == dst:
      <span class="hljs-keyword">return</span> path
    visited.add(node)
    <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
      stack.append((neighbor, path + [neighbor]))
  <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>Similarly for BFS:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bfs_path</span>(<span class="hljs-params">graph, src, dst</span>):</span>
  visited, queue = set(), deque([[src]])
  <span class="hljs-keyword">while</span> queue:
    path = queue.popleft()
    node = path[<span class="hljs-number">-1</span>]
    <span class="hljs-keyword">if</span> node <span class="hljs-keyword">in</span> visited:
      <span class="hljs-keyword">continue</span>
    <span class="hljs-keyword">if</span> node == dst:
      <span class="hljs-keyword">return</span> path
    <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
      queue.append(path + [neighbor])
  <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<h2 id="heading-lets-solve-a-leetcode-problem">Let's Solve a LeetCode Problem!</h2>
<p>Let's now apply what we learned about Graph Traversal to solve a problem on <a target="_blank" href="https://leetcode.com/problems/is-graph-bipartite/">LeetCode, 785. Is Graph Bipartite?</a></p>
<p>According to <a target="_blank" href="https://www.geeksforgeeks.org/bipartite-graph/">this article</a>, a modified BFS algorithm is all we need:</p>
<blockquote>
<p>Following is a simple algorithm to find out whether a given graph is Bipartite or not using Breadth First Search (BFS).</p>
<ol>
<li><p>Assign RED color to the source vertex (putting into set U).</p>
</li>
<li><p>Color all the neighbors with BLUE color (putting into set V).</p>
</li>
<li><p>Color all neighbor’s neighbor with RED color (putting into set U).</p>
</li>
<li><p>This way, assign color to all vertices such that it satisfies all the constraints of m way coloring problem where m = 2.</p>
</li>
<li><p>While assigning colors, if we find a neighbor which is colored with same color as current vertex, then the graph cannot be colored with 2 vertices (or graph is not Bipartite)</p>
</li>
</ol>
</blockquote>
<p>Plugging our template, the solution is as simple as follows. Check out <a target="_blank" href="https://youtu.be/d31vGF-Z69c">my video</a> for a line-by-line explanation.</p>
<pre><code class="lang-python">RED = <span class="hljs-number">0</span>
BLUE = <span class="hljs-number">1</span>
<span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Solution</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">isBipartite</span>(<span class="hljs-params">self, graph: List[List[int]]</span>) -&gt; bool:</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> graph:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        queue, visited = deque([]), set()
        <span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> range(len(graph)):
            <span class="hljs-keyword">if</span> v <span class="hljs-keyword">in</span> visited:
                <span class="hljs-keyword">continue</span>
            queue.append(v)
            node_colors = {v: RED}
            <span class="hljs-keyword">while</span> queue:
                node = queue.popleft()
                visited.add(node)
                my_color = node_colors[node]
                <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
                    <span class="hljs-keyword">if</span> neighbor <span class="hljs-keyword">in</span> node_colors <span class="hljs-keyword">and</span> node_colors[neighbor] == my_color:
                        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> neighbor <span class="hljs-keyword">in</span> visited:
                        queue.append(neighbor)
                    node_colors[neighbor] = RED <span class="hljs-keyword">if</span> my_color == BLUE <span class="hljs-keyword">else</span> BLUE

        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<h2 id="heading-graph-traversal-in-action-clicky-galaxy-a-game-by-me">Graph Traversal in Action: Clicky Galaxy, A Game by Me</h2>
<p>One more fun demo about Graph Traversal: Clicky Galaxy 🚀, a casual match-three game I built when I was learning Unity.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/clicky.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the game, you move a planet to an empty cell and score when there are three or more identical planets aligned horizontally or vertically. A planet can only move horizontally or vertically, and its movement path cannot be obstructed by other planets.</p>
<p>I applied Graph Traversal to check for a valid path between the planet the player clicked on and the destination cell to determine if the planet can move to that cell.</p>
<p>Each cell in the grid is a node and has four immediate neighbors: up, down, left, and right. As I want to find a short path between the source and the destination (if there exists one), <strong>BFS pathfinding</strong> is ideal for my use case.</p>
<p>Here is how my code looks like in C#. I used a helper named <strong>GetNeighbors</strong> to get the four immediate neighbors, ignoring out-of-bound ones.</p>
<pre><code class="lang-csharp"><span class="hljs-function">List&lt;Vector2Int&gt; <span class="hljs-title">BreadthFirstSearch</span>(<span class="hljs-params">Vector2Int srcIndices, Vector2Int dstIndices</span>)</span> {
    <span class="hljs-comment">// identify a path from srcIndices to dstIndices, could be null</span>
    <span class="hljs-comment">// the path include src and dst</span>
    HashSet&lt;Vector2Int&gt; visited = <span class="hljs-keyword">new</span> HashSet&lt;Vector2Int&gt;();
    Queue&lt;List&lt;Vector2Int&gt;&gt; pathQueue = <span class="hljs-keyword">new</span> Queue&lt;List&lt;Vector2Int&gt;&gt;();

    List&lt;Vector2Int&gt; startPath = <span class="hljs-keyword">new</span> List&lt;Vector2Int&gt;();
    startPath.Add(srcIndices);
    pathQueue.Enqueue(startPath);

    <span class="hljs-keyword">while</span> (pathQueue.Count &gt; <span class="hljs-number">0</span>) {
        List&lt;Vector2Int&gt; path = pathQueue.Dequeue();
        Vector2Int node = path[path.Count - <span class="hljs-number">1</span>];
        <span class="hljs-keyword">if</span> (visited.Contains(node)) {
            <span class="hljs-keyword">continue</span>;
        }
        <span class="hljs-keyword">if</span> (node == dstIndices) { <span class="hljs-comment">// done</span>
            <span class="hljs-keyword">return</span> path;
        }
        visited.Add(node);
        List&lt;Vector2Int&gt; neighbors = GetNeighbors(node);
        <span class="hljs-keyword">foreach</span> (Vector2Int neighbor <span class="hljs-keyword">in</span> neighbors) {
            Sprite sprite = GetSpriteAtIndices(neighbor.x, neighbor.y);
            <span class="hljs-keyword">if</span> (sprite == <span class="hljs-literal">null</span>) { <span class="hljs-comment">// can visit this next</span>
                List&lt;Vector2Int&gt; newPath = <span class="hljs-keyword">new</span> List&lt;Vector2Int&gt;(path);
                newPath.Add(neighbor);
                pathQueue.Enqueue(newPath);
            }
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

<span class="hljs-function">List&lt;Vector2Int&gt; <span class="hljs-title">GetNeighbors</span>(<span class="hljs-params">Vector2Int indices</span>)</span> {
    <span class="hljs-comment">// return the four immediate neighbors, left, right, up, down</span>
    List&lt;Vector2Int&gt; neighbors = <span class="hljs-keyword">new</span> List&lt;Vector2Int&gt;();
    <span class="hljs-keyword">if</span> (indices.x &gt;= <span class="hljs-number">0</span> &amp;&amp; indices.x &lt; gridDimension &amp;&amp; indices.y &gt;= <span class="hljs-number">0</span> &amp;&amp; indices.y &lt; gridDimension) {
        <span class="hljs-keyword">if</span> (indices.y &gt;= <span class="hljs-number">1</span>) {
            neighbors.Add(<span class="hljs-keyword">new</span> Vector2Int(indices.x, indices.y - <span class="hljs-number">1</span>));
        }
        <span class="hljs-keyword">if</span> (indices.y &lt; gridDimension - <span class="hljs-number">1</span>) {
            neighbors.Add(<span class="hljs-keyword">new</span> Vector2Int(indices.x, indices.y + <span class="hljs-number">1</span>));
        }
        <span class="hljs-keyword">if</span> (indices.x &gt;= <span class="hljs-number">1</span>) {
            neighbors.Add(<span class="hljs-keyword">new</span> Vector2Int(indices.x - <span class="hljs-number">1</span>, indices.y));
        }
        <span class="hljs-keyword">if</span> (indices.x &lt; gridDimension - <span class="hljs-number">1</span>) {
            neighbors.Add(<span class="hljs-keyword">new</span> Vector2Int(indices.x + <span class="hljs-number">1</span>, indices.y));
        }
    }
    <span class="hljs-keyword">return</span> neighbors;
}
</code></pre>
<p>And my game came together really well with this algorithm!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/clicky-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>In this crash course, we learned about the two Graph Traversal algorithms, DFS and BFS. We saw them in implementation first and then in action in a LeetCode problem as well as in my game.</p>
<p>If you enjoyed Graphs, think about how they relate to Trees. Spoiler alert! Pre-order traversal in trees is essentially DFS in graphs and level-order traversal in trees is essentially BFS in graphs. 🤫</p>
<p>Try figuring this out on your own or watch <a target="_blank" href="https://youtu.be/uaeCfsCcYWo">my crash course on Tree Traversal</a> for a refresher. Trust me, algorithms can be fun! 😃</p>
<h2 id="heading-resources">Resources</h2>
<p>Watch the course here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/d31vGF-Z69c" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Access the code template on my GitHub:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a86a3940a23d653bae4d5c399c06639e">
        <script src="https://gist.github.com/RuolinZheng08/a86a3940a23d653bae4d5c399c06639e.js"></script></div><p> </p>
<p>Check out Clicky Galaxy on my GitHub:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/unity-clicky-galaxy">https://github.com/RuolinZheng08/unity-clicky-galaxy</a></div>
<p> </p>
<p>Stay up-to-date with the whole crash course series:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>And lastly, feel free to subscribe to my YouTube channel for more content like this :)</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Discord AI Chatbot that Talks Like Your Favorite Character ]]>
                </title>
                <description>
                    <![CDATA[ Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one! In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates. You can follow ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-ai-chatbot/</link>
                <guid isPermaLink="false">66d460199f2bec37e2da0645</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 26 Aug 2021 19:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/lynns-thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one!</p>
<p>In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates.</p>
<p>You can follow along with this tutorial using the code on my GitHub:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/twewy-discord-chatbot">https://github.com/RuolinZheng08/twewy-discord-chatbot</a></div>
<p> </p>
<p>If you want, you can dive right into my video tutorial on YouTube – or read on for more details. 😎</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-what-to-expect-from-this-tutorial">What to Expect from this Tutorial</h2>
<p>Here is an example of the Discord AI chatbot that we will have built by the end of this tutorial.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Chat demo with my bot in Discord. I drew the bot's icon 😊</em></p>
<p>My chatbot project started as a joke with a friend when we were playing video games.</p>
<p>I'm honestly surprised by how popular it became – there were 5.9k views of my previous tutorial, plus, when I deployed my bot to a 1k+ user server, people flooded it with 300+ messages in an hour, effectively crashing the bot. 😳 You can <a target="_blank" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">read more about my deployment post-mortem in this post.</a></p>
<p>Since a lot of people are interested in building their own bots based on their favorite characters, I updated my tutorial to include an in-depth explanation on how to gather text data for any character, fictional or non-fictional.</p>
<p>You may also create a custom dataset that captures the speech between you and your friends and build a chatbot that speaks like yourself!</p>
<p>Other updates in this tutorial address changes in Hugging Face's model hosting services, including API changes that affect how we push the model to Hugging Face's model repositories.</p>
<h2 id="heading-outline-of-this-tutorial">Outline of this Tutorial</h2>
<p>The video version of this tutorial runs for a total of one hour and features the following topics:</p>
<ol>
<li><p>Gather text data for your character using one of these two methods: find pre-made datasets on <strong>Kaggle</strong> or make custom datasets from raw transcripts.</p>
</li>
<li><p>Train the model in <strong>Google Colab,</strong> a cloud-based Jupyter Notebook environment with free GPUs.</p>
</li>
<li><p>Deploy the model to <strong>Hugging Face,</strong> an AI model hosting service.</p>
</li>
<li><p>Build a Discord bot in either <strong>Python</strong> or <strong>JavaScript</strong>, your choice! 🤩</p>
</li>
<li><p>Set up the Discord bot's permissions so they don't spam non-bot channels</p>
</li>
<li><p>Host the bot on <strong>Repl.it.</strong></p>
</li>
<li><p>Keep the bot running indefinitely with <strong>Uptime Robot.</strong></p>
</li>
</ol>
<p>To learn more about how to build Discord bots, you may also find these two freeCodeCamp posts useful – there's a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a> and a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>.</p>
<h2 id="heading-how-to-prepare-the-data">How to Prepare the Data</h2>
<p>For our chatbot to learn to converse, we need text data in the form of dialogues. This is essentially how our chatbot is going to respond to different exchanges and contexts.</p>
<h3 id="heading-is-your-favorite-character-on-kaggle">Is Your Favorite Character on Kaggle?</h3>
<p>There are a lot of interesting datasets on Kaggle for popular cartoons, TV shows, and other media. For example:</p>
<ul>
<li><p><a target="_blank" href="https://www.kaggle.com/andradaolteanu/rickmorty-scripts">Rick and Morty</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/gulsahdemiryurek/harry-potter-dataset?select=Harry+Potter+1.csv">Harry Potter</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/mitramir5/the-big-bang-theory-series-transcript">The Big Bang Theory</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/anderfj/game-of-thrones-series-scripts-breakdowns">Game of Thrones</a></p>
</li>
</ul>
<p>We only need two columns from these datasets: <strong>character name</strong> and <strong>dialogue line</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.07.59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example dataset: Harry Potter movie transcript</em></p>
<h3 id="heading-cant-find-your-favorite-character-on-kaggle">Can't Find Your Favorite Character on Kaggle?</h3>
<p>Can't find your favorite character on Kaggle? No worries. We can create datasets from raw transcripts. A great place to look for transcripts is <a target="_blank" href="https://transcripts.fandom.com/wiki/Transcripts_Wiki">Transcript Wiki</a>. For example, check out <a target="_blank" href="https://transcripts.fandom.com/wiki/Peppa_Pig">this Peppa Pig transcript.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.13.57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example: Peppa Pig transcript</em></p>
<p>Using a regular expression like <code>([a-zA-Z|\s]+): (.+)</code>, we can extract out the two columns of interest, character name, and dialogue line.</p>
<p><a target="_blank" href="https://pythex.org/?regex=\(%5Ba-zA-Z%7C%5Cs%5D%2B\)%3A%20\(.%2B\)&amp;test_string=Peppa%20Pig%3A%20George%2C%20I%20could%20see%20you%20too%20easily.%0A%0ANarrator%3A%20Now%20it%20is%20Peppa%27s%20turn%20to%20hide.%0A%0AGeorge%3A%20One...%20um...%20three.%0A%0AMummy%20Pig%3A%20I%27ll%20help%20George%20to%20count.%20&amp;ignorecase=0&amp;multiline=0&amp;dotall=0&amp;verbose=0">Try it out on this Python regex website yourself!</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.58.35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-train-the-model">How to Train the Model</h2>
<p>Under the hood, our model will be a <strong>Generative Pre-trained Transfomer (GPT),</strong> the most popular language model these days.</p>
<p>Instead of training from scratch, we will load <a target="_blank" href="https://huggingface.co/microsoft/DialoGPT-small">Microsoft's pre-trained GPT</a>, <code>DialoGPT-small</code>, and fine-tune it using our dataset.</p>
<p>My GitHub repo for this tutorial contains <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/model_train_upload_workflow.ipynb">the notebook file</a> named <code>model_train_upload_workflow.ipynb</code> to get you started. All you need to do is the following: (please refer to the video for a detailed walkthrough)</p>
<ol>
<li><p>Upload the file to <a target="_blank" href="https://colab.research.google.com/">Google Colab</a></p>
</li>
<li><p>Select <strong>GPU</strong> as the runtime, which will speed up our model training.</p>
</li>
<li><p>Change the dataset and the target character in code snippets like:</p>
</li>
</ol>
<pre><code class="lang-python">data = pd.read_csv(<span class="hljs-string">'MY-DATASET.csv'</span>)
CHARACTER_NAME = <span class="hljs-string">'MY-CHARACTER'</span>
</code></pre>
<p>Running through the training section of the notebook should take less than half an hour. I have about 700 lines and the training takes less than ten minutes. The model will be stored in a folder named <code>output-small</code> .</p>
<p>Want an even smarter and more eloquent model? Feel free to train a larger model like <code>DialoGPT-medium</code> or even <code>DialoGPT-large</code>. Model size here refers to the number of parameters in the model. More parameters will allow the model to pick up more complexity from the dataset.</p>
<p>You may also increase the number of training epochs by searching for <code>num_train_epochs</code> in the notebook. This is the number of times that the model will cycle through the training dataset. The model will generally get smarter when it has more exposure to the dataset.</p>
<p>However, do take care not to overfit the model: If the model is trained for too many epochs, it may memorize the dataset and recite back lines from the dataset when we try to converse with it. This isn't ideal as we want the conversation to be more organic.</p>
<h2 id="heading-how-to-host-the-model">How to Host the Model</h2>
<p>We will host the model on Hugging Face, which provides a free API for us to query the model.</p>
<p>Sign up for <a target="_blank" href="https://huggingface.co/">Hugging Face</a> and create a new model repository by clicking on <strong>New model.</strong> Obtain your API token by going to <strong>Edit profile &gt; API Tokens.</strong> We will need this token when we build the Discord bot.</p>
<p>Follow along with this section in my video to push the model. Also, remember to tag it as <strong>conversational</strong> in its Model Card (equivalently its <code>README.md</code>):</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">---</span>
tags:
- conversational
<span class="hljs-comment">---</span>

# My Awesome Model
</code></pre>
<p>You will know that everything works fine if you are able to chat with the model in the browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/huggingface3.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-build-the-discord-bot">How to Build the Discord Bot</h2>
<p>Go to the <a target="_blank" href="https://discord.com/developers/applications">Discord Developer's page</a>, create an application, and add a bot to it. Since our chatbot is only going to respond to user messages, checking <strong>Text Permissions &gt; Send Messgaes</strong> in the Bot Permissions Setting is sufficient. Copy the bot's API token for later use.</p>
<p>Sign up for <a target="_blank" href="https://repl.it/">Repl.it</a> and create a new Repl, <strong>Python</strong> or <strong>Node.js</strong> for JavaScript, whichever you are working with.</p>
<p>Let's store our API tokens for <strong>Hugging Face</strong> and <strong>Discord</strong> as environment variables, named <code>HUGGINGFACE_TOKEN</code> and <code>DISCORD_TOKEN</code> respectively. This helps keep them secret.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/repl.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.py">my Python script</a> for a Python bot and <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.js">my JS script</a> for a JS bot. Note that for the JS bot, because of a version incompatibility with Repl.it's Node and NPM, we will need to explicitly specify a lower version of the Discord API in <code>package.json</code>.</p>
<pre><code class="lang-pgsql">"dependencies": {
    "discord.js": "^12.5.3",
}
</code></pre>
<p>With that, our bot is ready to go! Start the Repl script by hitting <strong>Run</strong>, add the bot to a server, type something in the channel, and enjoy the bot's witty response.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-keep-the-bot-online">How to Keep the Bot Online</h2>
<p>One problem with our bot is that it halts as soon as we <strong>stop</strong> the running Repl (equivalently, if we close the Repl.it browser window).</p>
<p>To get around this and keep our bot running indefinitely, we will set up a web server to contain the bot script, and use a service like <a target="_blank" href="https://uptimerobot.com/">Uptime Robot</a> to pin our server every five minutes so that our server stays alive.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-15.29.06.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In my video tutorial, I copied the server code from these two freeCodeCamp posts (<a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a>, <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>). Then, I set up the monitor on Uptime Robot. Now my bot continues to reply to my messages even if I close the browser (or shut down my computer all together).</p>
<p>Congratulations on reaching the end of this tutorial! I hope you enjoyed creating the bot and have fun chatting with your favorite character! 🥳</p>
<h2 id="heading-tutorial-video-link">Tutorial Video Link</h2>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-more-about-me-and-my-chatbot-project">More About Me and My Chatbot Project</h2>
<p>I'm Lynn, a software engineer at Salesforce. I graduated from the University of Chicago in 2021 with a joint BS/MS in Computer Science, specializing in Machine Learning. <a target="_blank" href="https://ruolinzheng08.github.io/">Come say hi on my personal website!</a></p>
<p>I post fun project tutorials like this on my YouTube channel. Feel free to subscribe to catch up on my latest content. 😃</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Want to learn more about my bot? Check out this 15-minute real-time chat demo featuring me, my friend, and my bot!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/-n6uWu8PZzo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Interested in the model I trained? Check it out on Hugging Face:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua">https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua</a></div>
<p> </p>
<p>My chatbot was so popular on a 1k+ user server that... it crashed. 🤯 Read about my deployment post-mortem in this post:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/</a></div>
<p> </p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Coding Interview Tree Traversal Crash Course – The Only One You'll Ever Need ]]>
                </title>
                <description>
                    <![CDATA[ Are you preparing for coding interviews? I designed a crash course series to help you out. I'm Lynn, a software engineer and a recent graduate from the University of Chicago. This is the second course in my Coding Interview Crash Course Series. Feel ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/coding-interview-tree-traversal-crash-course-the-only-one-youll-ever-need/</link>
                <guid isPermaLink="false">66d4601755db48792eed3f73</guid>
                
                    <category>
                        <![CDATA[ coding interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview questions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Interview tips ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Trees ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Mon, 16 Aug 2021 23:49:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/tree-thumb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you preparing for coding interviews? I designed a crash course series to help you out.</p>
<p>I'm Lynn, a software engineer and a recent graduate from the University of Chicago. This is the second course in my Coding Interview Crash Course Series. Feel free to check out <a target="_blank" href="https://www.youtube.com/channel/UCZ2MeG5jTIqgzEMiByrIzsw">my YouTube channel, Lynn's DevLab</a>, to stay updated on this series.</p>
<p>This crash course is about Tree Traversal. If you just want to dive right in, <a target="_blank" href="https://youtu.be/uaeCfsCcYWo">you can find the course here</a> (and linked at the bottom of this article). If you want a little more info, read on. 😎</p>
<h2 id="heading-who-is-the-course-for-and-what-are-tree-traversal-algorithms">Who is the Course for and What are Tree Traversal Algorithms? 🌳</h2>
<p>You will get the most of this course if you already know a bit about the <strong>Tree</strong> data structure. Check out <a target="_blank" href="https://www.freecodecamp.org/news/binary-data-structures-an-intro-to-trees-and-heaps-in-javascript-962ab536cb42/">these</a> <a target="_blank" href="https://www.freecodecamp.org/news/the-codeless-guide-to-tree-data-structures/">tutorials</a> if you need a refresher.</p>
<p>We will cover the traversal algorithms for both <strong>Binary Trees</strong> and <strong>N-ary Trees</strong> (in which each parent node has an arbitrary number of children).</p>
<p>If you have heard about Binary Search Trees (BST) before, that's a special type of Binary Tree so the techniques we are going to learn here also apply.</p>
<p>Trees are a favorite interview subject among top tech companies like Google, Microsoft, and Facebook, so let's crunch this topic!</p>
<p>We will learn about four traversal techniques and solve their corresponding LeetCode problems hands-on.</p>
<p>The four techniques are:</p>
<ul>
<li><p><strong>Pre-order (Depth-First Search, DFS)</strong></p>
</li>
<li><p><strong>Post-order</strong></p>
</li>
<li><p><strong>In-order</strong></p>
</li>
<li><p><strong>Level-order (Breadth-First Search, BFS).</strong></p>
</li>
</ul>
<h2 id="heading-course-outline">Course Outline</h2>
<p>This course video runs for a total of 30 minutes and features:</p>
<ul>
<li><p>A high-level description of the four traversal techniques: <strong>pre-order, post-order, in-order, and level-order</strong></p>
</li>
<li><p><strong>Recursive</strong> implementations of pre-order, post-order, and in-order (Note: this doesn't apply to level-order)</p>
</li>
<li><p><strong>Iterative</strong> implementations of pre-order, post-order, in-order, and level-order</p>
</li>
<li><p>An extension of the templates from <strong>Binary Trees</strong> to <strong>N-ary Trees</strong></p>
</li>
</ul>
<p>Let's dive into each of the four techniques below.</p>
<h2 id="heading-tree-traversal-demonstration-using-an-example-tree">Tree Traversal Demonstration Using an Example Tree</h2>
<p>We will use the following tree to demonstrate the output from the four traversal techniques.</p>
<p>Note that this tree is a simple Binary Tree, not a Binary Search Tree (BST). A BST is a special type of Binary Tree, so our techniques also apply. Also, <strong>in-order traversal</strong> becomes especially interesting when we work with a BST, as we will see below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-16-at-2.48.44-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>An example Binary Tree. Note that it's not a Binary Search Tree (BST).</em></p>
<p>Given this tree, the traversal result from the four techniques are as follows:</p>
<ul>
<li><p>Pre-order: 1, 2, 4, 5, 3</p>
</li>
<li><p>Post-order: 4, 5, 2, 3, 1</p>
</li>
<li><p>In-order: 4, 2, 5, 1, 3</p>
</li>
<li><p>Level-order: 1, 2, 3, 4, 5</p>
</li>
</ul>
<h3 id="heading-pre-order-traversal">Pre-order Traversal</h3>
<p>Pre-order traversal is also known as <strong>Depth-First Search (DFS)</strong> if we analyze the tree as a graph and take the tree root node as our starting node in the search.</p>
<p>As in the example above, we go all the way down to the <strong>leftmost</strong> node before visiting any other node that is a left child of some parent node.</p>
<p>Pre-order traversal allows us to explore roots before leaves, and is hence ideal for tasks like copying a tree.</p>
<h3 id="heading-post-order-traversal">Post-order Traversal</h3>
<p>Post-order traversal does the opposite of pre-order traversal, allowing us to explore leaves before roots.</p>
<h3 id="heading-in-order-traversal">In-order Traversal</h3>
<p>In-order traversal is especially useful for flattening a tree into an array representation.</p>
<p>For a Binary Search Tree like below, in-order traversal outputs an array in a sorted, non-decreasing order: -4, 3, 2, 5, 18.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-16-at-2.51.44-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Binary Search Tree example</em></p>
<h3 id="heading-level-order-traversal">Level-order Traversal</h3>
<p>Level-order traversal is also known as <strong>Breadth-First Search (BFS)</strong> if we consider the tree as a graph and start our search from the tree root node.</p>
<p>We visit every node on the current level (depth) before moving onto those on the next level. Effectively, we visit the immediate neighbor of (one step away from) our current node before visiting neighbors that are farther away.</p>
<h2 id="heading-how-to-implement-these-four-techniques">How to Implement these Four Techniques</h2>
<p>We will use the following definition for a node of a Binary Tree:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Definition for a binary tree node.</span>
<span class="hljs-comment"># class TreeNode:</span>
<span class="hljs-comment">#     def __init__(self, val=0, left=None, right=None):</span>
<span class="hljs-comment">#         self.val = val</span>
<span class="hljs-comment">#         self.left = left</span>
<span class="hljs-comment">#         self.right = right</span>
</code></pre>
<h3 id="heading-recursive-implementation">Recursive implementation</h3>
<p>Recursive implementations are the most straightforward. The most important thing to remember is the order in which we concatenate the results from the two recursive calls (one on the left subtree and one on the right subtree) with the value of the current node.</p>
<pre><code class="lang-pgsql">def preorder(root):
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">return</span> [root.val] + preorder(root.left) + preorder(root.right)
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">postorder</span>(<span class="hljs-params">root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">return</span> postorder(root.left) + postorder(root.right) + [root.val]
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">inorder</span>(<span class="hljs-params">root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">return</span> inorder(root.left) + [root.val] + inorder(root.right)
</code></pre>
<h3 id="heading-iterative-implementation">Iterative implementation</h3>
<p>Compared to recursive implementations, iterative implementations are non-trivial. Most require that we use either a stack or a queue to keep track of the nodes that we need to visit.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">preorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    ret = []
    stack = [root]
    <span class="hljs-keyword">while</span> stack:
        node = stack.pop()
        ret.append(node.val)
        <span class="hljs-comment"># note that we append the right child before the left child</span>
        <span class="hljs-keyword">if</span> node.right:
            stack.append(node.right)
        <span class="hljs-keyword">if</span> node.left:
            stack.append(node.left)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">postorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
    ret = deque()
    stack = [root]
    <span class="hljs-keyword">while</span> stack:
        node = stack.pop()
        ret.appendleft(node.val)
        <span class="hljs-keyword">if</span> node.left:
            stack.append(node.left)
        <span class="hljs-keyword">if</span> node.right:
            stack.append(node.right)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<p>The implementation for in-order traversal looks quite different from pre-order and post-order:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">inorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    ret = []
    stack = []
    <span class="hljs-keyword">while</span> root <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">or</span> stack:
        <span class="hljs-keyword">while</span> root <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            stack.append(root)
            root = root.left
        root = stack.pop()
        ret.append(root.val)
        root = root.right
    <span class="hljs-keyword">return</span> ret
</code></pre>
<p>Lastly, we have level-order traversal, where we will output the result as <code>[[nodes on the first level], [nodes on the second level], [nodes on the third level], ...]</code>.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">levelorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    ret = []
    <span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
    queue = deque([root])
    <span class="hljs-keyword">while</span> queue:
        ret_row = []
        <span class="hljs-comment"># fixed size for current level</span>
        <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(len(queue)):
            node = queue.popleft()
            ret_row.append(node.val)
            <span class="hljs-keyword">if</span> node.left:
                queue.append(node.left)
            <span class="hljs-keyword">if</span> node.right:
                queue.append(node.right)
        ret.append(ret_row)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<h3 id="heading-n-ary-trees">N-ary Trees</h3>
<p>We now extend our templates from handling Binary Trees to handling N-ary Trees. We use the following definition for the node of an N-ary Tree:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, val=None, children=[]</span>):</span>
        self.val = val
        self.children = children
</code></pre>
<p>To extend our iterative implementations to handle N-ary Trees, all we need to do is to make sure that we are appending the child nodes that we will visit in a correct order.</p>
<p>Recall that in pre-order traversal, we appended the right child before the left child. So when appending the children of a node of an N-ary Tree, we need to reverse the list of children.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">preorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    ret = []
    stack = [root]
    <span class="hljs-keyword">while</span> stack:
        node = stack.pop()
        ret.append(node.val)
        <span class="hljs-comment"># reverse the list of children</span>
        <span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> node.children[::<span class="hljs-number">-1</span>]:
            stack.append(child)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<p>For the other traversal techniques, since we are appending the children from the left to the right, we can iterative over the list of children normally:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">postorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
    ret = deque()
    stack = [root]
    <span class="hljs-keyword">while</span> stack:
        node = stack.pop()
        ret.appendleft(node.val)
        <span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> node.children:
            stack.append(child)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">levelorder</span>(<span class="hljs-params">self, root</span>):</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> root:
        <span class="hljs-keyword">return</span> []
    ret = []
    <span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
    queue = deque([root])
    <span class="hljs-keyword">while</span> queue:
        ret_row = []
        <span class="hljs-comment"># fixed size for current level</span>
        <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(len(queue)):
            node = queue.popleft()
            ret_row.append(node.val)
            <span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> node.children:
                queue.append(child)
        ret.append(ret_row)
    <span class="hljs-keyword">return</span> ret
</code></pre>
<p>And now we can apply our tree traversal templates to trees that have an arbitrary number of children at each node.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this crash course on tree traversal, we learned four techniques: pre-order, post-order, in-order, and level-order. We discussed how they differ and what tasks they are best for.</p>
<p>We also implemented them both in a recursive fashion and in an iterative one. Last but not least, we extended the techniques to deal with not only Binary Trees, but N-ary Trees.</p>
<p>I hope now you feel more confident about tree traversal interview questions. This is also a nice segue into the topic of my next crash course on graph traversal.</p>
<p>With the knowledge of pre-order traversal and level-order traversal, DFS and BFS won't be completely out of the blue for you 🤓 I will even talk about how I applied graph traversal when developing an algorithm for <a target="_blank" href="https://github.com/RuolinZheng08/unity-clicky-galaxy">my match-three game, Clicky Galaxy,</a> so stay tuned!</p>
<h2 id="heading-resources">Resources</h2>
<p>Watch the course here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/uaeCfsCcYWo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Access the code template on my GitHub:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f6e55b09eb096fe5fe630249cd859b07">
        <script src="https://gist.github.com/RuolinZheng08/f6e55b09eb096fe5fe630249cd859b07.js"></script></div><p> </p>
<p>Check out the whole crash course series:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>And lastly, feel free to subscribe to my YouTube channel for more content like this :)</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Python to Detect Music Onsets ]]>
                </title>
                <description>
                    <![CDATA[ In music terminology, an onset refers to the beginning of a musical note or other sound. In this post, we will look at how to detect music onsets with Python's audio signal processing libraries, Aubio and librosa. This tutorial is relevant even if yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-python-to-detect-music-onsets/</link>
                <guid isPermaLink="false">66d4602a55db48792eed3f79</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jul 2021 16:56:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/07/Python-Music-Onset-Detection.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In music terminology, an <strong>onset</strong> refers to <strong>the beginning of a musical note or other sound.</strong> In this post, we will look at how to detect music onsets with Python's audio signal processing libraries, <a target="_blank" href="https://aubio.org/">Aubio</a> and <a target="_blank" href="https://librosa.org/doc/latest/index.html">librosa</a>.</p>
<p>This tutorial is relevant even if your application doesn't use Python - for example, you are building a game in Unity and C# which doesn't have robust libraries for onset detection.</p>
<p>If that is the case, you may export the detected onset timestamps to a text file to read into your engine of choice.</p>
<p>If you prefer a video tutorial to an article, here's the video version of this tutorial.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aMMI0nAKgI0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-applications-of-music-onset-detection">Applications of Music Onset Detection</h2>
<p>I came across this music onset detection technique when I was building <strong>a rhythm game</strong> and wanted a way to <strong>automatically</strong> generate beat maps for any song.</p>
<p>Check out the end of this article for my <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">open-source rhythm game</a> and <a target="_blank" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">my step-by-step course on how I built it.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/ezgif.com-gif-maker.gif" alt="Image of a rhythm game similar to Guitar Hero" width="600" height="400" loading="lazy"></p>
<p><em>My rhythm game showcase</em></p>
<p>Besides building a rhythm game, this technique has a lot of other applications.</p>
<p>For example, detecting onsets is usually the first step in <strong>music information retrieval and analysis</strong>.</p>
<p>Another example could be that we are building a game in which there are combat scenes. We may detect the onsets in the BGM and spawn an enemy at every onset. This can create a unique pacing in our game.</p>
<p>I'll demonstrate how to detect music onsets using two different Python packages for <strong>audio signal processing</strong>, <a target="_blank" href="https://aubio.org/">Aubio</a> and <a target="_blank" href="https://librosa.org/doc/latest/index.html">librosa</a>. Both packages detect onsets pretty accurately. The small difference is that librosa works for the <strong>OGG</strong> format while Aubio doesn't.</p>
<h2 id="heading-how-to-set-up-the-development-environment">How to Set up the Development Environment</h2>
<p>We will be installing our packages in a virtual environment.</p>
<p>In the command line, we create a virtual environment named <code>python-aubio-librosa</code> as follows. <code>-m</code> stands for <code>module</code>.</p>
<pre><code class="lang-pgsql">$ python3 -m venv python-aubio-librosa
</code></pre>
<p>Then we activate the virtual environment:</p>
<pre><code class="lang-pgsql">$ . python-aubio-librosa/bin/activate
</code></pre>
<p>Note that if you try to activate the environment using the following command, you will get an error:</p>
<pre><code class="lang-pgsql">$ ./python-aubio-librosa/bin/activate
-bash: ./python-aubio-librosa/bin/activate: Permission denied
</code></pre>
<p>Once your environment is activated, the name of the environment will show in parentheses:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ ...
</code></pre>
<p>We can check that if we invoke <code>python</code> or <code>pip</code>, the invoked programs will be those in our virtual environment instead of the system-level ones.</p>
<p>If we haven't activated our environment, the output will point to the system-level programs.</p>
<pre><code class="lang-pgsql">$ which python
/usr/bin/python
$ which pip
/usr/<span class="hljs-keyword">local</span>/bin/pip
</code></pre>
<p>Once we have activated our environment, the output will point to the local ones.</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ which python
/Users/USERNAME/Desktop/python-aubio-librosa/bin/python
(python-aubio-librosa) $ which pip
/Users/USERNAME/Desktop/python-aubio-librosa/bin/pip
</code></pre>
<h2 id="heading-how-to-install-and-use-aubio">How to Install and Use Aubio</h2>
<p>We will install Aubio via <code>pip</code>:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ pip install aubio
</code></pre>
<p>The function that we will use to generate a list of onset timestamps as floating point numbers in seconds is as follows. This function comes from Aubio's <a target="_blank" href="https://github.com/aubio/aubio/blob/master/python/demos/demo_onset.py">official documentations</a>, so we can just use it without learning about the nitty-gritty details (like FFT, Fast-Fourier Transformations) in audio signal processing.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> aubio <span class="hljs-keyword">import</span> source, onset

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_onset_times</span>(<span class="hljs-params">file_path</span>):</span>
    window_size = <span class="hljs-number">1024</span> <span class="hljs-comment"># FFT size</span>
    hop_size = window_size // <span class="hljs-number">4</span>

    sample_rate = <span class="hljs-number">0</span>
    src_func = source(file_path, sample_rate, hop_size)
    sample_rate = src_func.samplerate
    onset_func = onset(<span class="hljs-string">'default'</span>, window_size, hop_size)

    duration = float(src_func.duration) / src_func.samplerate

    onset_times = [] <span class="hljs-comment"># seconds</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>: <span class="hljs-comment"># read frames</span>
        samples, num_frames_read = src_func()
        <span class="hljs-keyword">if</span> onset_func(samples):
            onset_time = onset_func.get_last_s()
            <span class="hljs-keyword">if</span> onset_time &lt; duration:
                onset_times.append(onset_time)
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">break</span>
        <span class="hljs-keyword">if</span> num_frames_read &lt; hop_size:
            <span class="hljs-keyword">break</span>

    <span class="hljs-keyword">return</span> onset_times
</code></pre>
<p>Then, we write a <code>main</code> function that takes in the path to an audio file, and outputs the onset timestamps to a file, keeping the first four decimal places in each float, one float per line.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    file_path = <span class="hljs-string">'../game/audio/my-music.mp3'</span>
    onset_times = get_onset_times(file_path)
    <span class="hljs-comment"># remove extension, .mp3, .wav etc.</span>
    file_name_no_extension, _ = os.path.splitext(file_path)
    output_name = file_name_no_extension + <span class="hljs-string">'.beatmap.txt'</span>
    <span class="hljs-keyword">with</span> open(output_name, <span class="hljs-string">'wt'</span>) <span class="hljs-keyword">as</span> f:
        f.write(<span class="hljs-string">'\n'</span>.join([<span class="hljs-string">'%.4f'</span> % onset_time <span class="hljs-keyword">for</span> onset_time <span class="hljs-keyword">in</span> onset_times]))
</code></pre>
<p>Let's invoke the script from the command line. Aubio might raise a warning about accuracy but my experimentation shows that Aubio is still pretty accurate.</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ python generate_beatmap_aubio.py 
[mp3 @ <span class="hljs-number">0x7fe671031e00</span>] Estimating duration <span class="hljs-keyword">from</span> bitrate, this may be inaccurate
</code></pre>
<p>An example output file would look like below. For a short 15-second music clip, Aubio detected 26 onsets. These are the timestamps that we can use for our application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-20-at-13.54.18.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>An example output file consisting of onset timestamps</em></p>
<p>And that's it for Aubio.</p>
<h2 id="heading-how-to-install-and-use-librosa">How to Install and Use Librosa</h2>
<p>Similar to Aubio, we will install librosa also via <code>pip</code>:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ pip install librosa
</code></pre>
<p>Compared to Aubio, librosa's library methods are easier to use. <code>librosa.load</code> returns a NumPy array <code>x</code> and a sampling rate <code>sr</code>, which we pass to <code>librosa.onset.onset_detect</code> to get a list of onset frames.</p>
<p>Finally we convert onset frames into onset timestamps, and write each timestamp to an output file like we did for Aubio.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> librosa

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    file_path = <span class="hljs-string">'../game/audio/my-music.ogg'</span>
    x, sr = librosa.load(file_path)
    onset_frames = librosa.onset.onset_detect(x, sr=sr, wait=<span class="hljs-number">1</span>, pre_avg=<span class="hljs-number">1</span>, post_avg=<span class="hljs-number">1</span>, pre_max=<span class="hljs-number">1</span>, post_max=<span class="hljs-number">1</span>)
    onset_times = librosa.frames_to_time(onset_frames)
    <span class="hljs-comment"># remove extension, .mp3, .wav etc.</span>
    file_name_no_extension, _ = os.path.splitext(file_path)
    output_name = file_name_no_extension + <span class="hljs-string">'.beatmap.txt'</span>
    <span class="hljs-keyword">with</span> open(output_name, <span class="hljs-string">'wt'</span>) <span class="hljs-keyword">as</span> f:
        f.write(<span class="hljs-string">'\n'</span>.join([<span class="hljs-string">'%.4f'</span> % onset_time <span class="hljs-keyword">for</span> onset_time <span class="hljs-keyword">in</span> onset_times]))
</code></pre>
<p>The output file will be in the same format as shown above for Aubio.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading and I hope you are ready to apply this onset detection technique to your next project. 🎶</p>
<p>As a recap of the differences between Aubio and Librosa, both detect onsets pretty accurately based on my experimentation.</p>
<p>Aubio is more restricted in terms of audio file formats: it raises a warning about accuracy for MP3 files and doesn't handle OGG files.</p>
<p>On the flip side, Librosa is able to handle most common audio file formats: MP3, OGG, FLAC, and M4A. Librosa's library interface is also easier to use than Aubio's, especially for those of us who aren't pros in signal processing.</p>
<p>Check out the resources below if you'd like to learn more or get inspired for your next project!</p>
<h2 id="heading-resources">Resources</h2>
<p>You can check out the code used in this tutorial <a target="_blank" href="https://github.com/RuolinZheng08/renpy-minigames101/tree/master/generate_beatmap">on my GitHub</a> or <a target="_blank" href="https://youtu.be/aMMI0nAKgI0">watch the video version of this tutorial on YouTube.</a></p>
<p>If you are interested in building a rhythm game, check out <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">my open-source one built in Python on GitHub</a> and my Udemy course in which we will build the game from the ground up.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973</a></div>
<p> </p>
<p>If you'd like to know whether the course is for you, check out my course promotional video on YouTube and <a target="_blank" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">free sample lectures on Udemy.</a></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/_AaUKSjTNY8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>My YouTube channel also features other fun project tutorials like <a target="_blank" href="https://youtu.be/UBwvFuTC1ZE">building a Discord AI Chatbot</a>, and, <a target="_blank" href="https://youtu.be/H2gnD7Ixeao">a series of coding interview crash courses</a> I'm developing. Hope to see you there!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Coding Interview Backtracking Problems Crash Course – The Only One You'll Ever Need ]]>
                </title>
                <description>
                    <![CDATA[ Whether you are new to coding interviews or are already familiar with the concept of backtracking algorithms, this is the crash course for you. In it, we will learn about an all-purpose coding template for solving backtracking problems and apply it t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/coding-interview-backtracking-problems-crash-course/</link>
                <guid isPermaLink="false">66d4601355db48792eed3f6b</guid>
                
                    <category>
                        <![CDATA[ coding interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview questions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Interview tips ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Job Interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leetcode ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Tue, 29 Jun 2021 20:00:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/1080P-Thumbnails-1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Whether you are new to coding interviews or are already familiar with the concept of <strong>backtracking</strong> algorithms, this is the crash course for you.</p>
<p>In it, we will learn about <strong>an all-purpose coding template</strong> for solving backtracking problems and apply it to <strong>two LeetCode hard problems</strong>. Ready to crunch your next coding interview? Let's go!</p>
<p>If you just want to dive right in, <a target="_blank" href="https://www.youtube.com/watch?v=H2gnD7Ixeao">you can find the course here</a> (and linked at the bottom of this article). If you want a little more info, read on. :)</p>
<h2 id="heading-who-is-the-course-for-and-what-is-the-backtracking-algorithm">Who is the Course for and What is the Backtracking Algorithm?</h2>
<p>This course is suitable for anyone who is preparing for coding interviews, especially those who are looking to hone their skills in solving <strong>backtracking</strong> problems.</p>
<p>Backtracking is a common category for questions in coding interviews. The algorithm for solving those problems usually involves <strong>recursion</strong> and <strong>building incrementally on previous states</strong> to arrive at the ultimate valid solution.</p>
<p>Backtracking is a favorite topic among top tech companies like Google, Microsoft, and Facebook, precisely because it requires robust reasoning and coding competence to nail those questions.</p>
<p>However, because of its recursive nature and complex problem definition, backtracking problems are usually a major source of confusion among devs who are preparing for coding interviews.</p>
<p>To address this confusion, this crash course aims to arm with you a concise, 20-line template that you can apply to the majority of backtracking problems.</p>
<h2 id="heading-course-outline">Course Outline</h2>
<p>This course runs for a total of 40 minutes and the structure is as follows:</p>
<ul>
<li><p>An 8-minute introduction to <a target="_blank" href="https://gist.github.com/RuolinZheng08/cdd880ee748e27ed28e0be3916f56fa6">the template</a></p>
</li>
<li><p>A 15-minute hands-on code-along session for <a target="_blank" href="https://leetcode.com/problems/n-queens/">LeetCode Question 51. N-Queens</a></p>
</li>
<li><p>A 15-minute hands-on code-along session for <a target="_blank" href="https://leetcode.com/problems/sudoku-solver/">LeetCode Question 37. Sudoku Solver</a></p>
</li>
</ul>
<h2 id="heading-the-all-purpose-template">The All-Purpose Template</h2>
<p>For your convenience, I've copied the template over. This is exactly the same template that I use for my coding interviews, or when I'm developing algorithms for my indie games. I even used it once in my research on a non-convex optimization problem.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_valid_state</span>(<span class="hljs-params">state</span>):</span>
    <span class="hljs-comment"># check if it is a valid solution</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_candidates</span>(<span class="hljs-params">state</span>):</span>
    <span class="hljs-keyword">return</span> []

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search</span>(<span class="hljs-params">state, solutions</span>):</span>
    <span class="hljs-keyword">if</span> is_valid_state(state):
        solutions.append(state.copy())
        <span class="hljs-comment"># return</span>

    <span class="hljs-keyword">for</span> candidate <span class="hljs-keyword">in</span> get_candidates(state):
        state.add(candidate)
        search(state, solutions)
        state.remove(candidate)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">solve</span>():</span>
    solutions = []
    state = set()
    search(state, solutions)
    <span class="hljs-keyword">return</span> solutions
</code></pre>
<p>The first three are all helper functions, and the last and most important one, <code>solve</code>, is essentially the one that a LeetCode problem is asking you to write.</p>
<h2 id="heading-solving-leetcode-problems-hands-on">Solving LeetCode Problems Hands-On</h2>
<p>We will next apply this template to solving two LeetCode hard problems: <a target="_blank" href="https://leetcode.com/problems/n-queens/">LeetCode Question 51. N-Queens</a> and <a target="_blank" href="https://leetcode.com/problems/sudoku-solver/">LeetCode Question 37. Sudoku Solver</a>.</p>
<p>To illustrate the flexibility of the template, see below for how we solve the N-Queens problem by doing nothing fancy other than adapting the four functions (renaming <code>solve</code> to <code>solveNQueens</code>). The complete code for either problem is available <a target="_blank" href="https://gist.github.com/RuolinZheng08/cdd880ee748e27ed28e0be3916f56fa6">in my GitHub gist</a>.</p>
<p>Watch <a target="_blank" href="https://youtu.be/H2gnD7Ixeao">the video course</a> to follow along my analysis and adaptation of the template.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Solution</span>:</span>
    <span class="hljs-comment"># solveNQueens is essentially the solve function</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">solveNQueens</span>(<span class="hljs-params">self, n: int</span>) -&gt; List[List[str]]:</span>
        solutions = []
        state = []
        self.search(state, solutions, n)
        <span class="hljs-keyword">return</span> solutions

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_valid_state</span>(<span class="hljs-params">self, state, n</span>):</span>
        <span class="hljs-comment"># check if it is a valid solution</span>
        <span class="hljs-keyword">return</span> len(state) == n

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_candidates</span>(<span class="hljs-params">self, state, n</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> state:
            <span class="hljs-keyword">return</span> range(n)

        <span class="hljs-comment"># find the next position in the state to populate</span>
        position = len(state)
        candidates = set(range(n))
        <span class="hljs-comment"># prune down candidates that place the queen into attacks</span>
        <span class="hljs-keyword">for</span> row, col <span class="hljs-keyword">in</span> enumerate(state):
            <span class="hljs-comment"># discard the column index if it's occupied by a queen</span>
            candidates.discard(col)
            dist = position - row
            <span class="hljs-comment"># discard diagonals</span>
            candidates.discard(col + dist)
            candidates.discard(col - dist)
        <span class="hljs-keyword">return</span> candidates

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search</span>(<span class="hljs-params">self, state, solutions, n</span>):</span>
        <span class="hljs-keyword">if</span> self.is_valid_state(state, n):
            state_string = self.state_to_string(state, n)
            solutions.append(state_string)
            <span class="hljs-keyword">return</span>

        <span class="hljs-keyword">for</span> candidate <span class="hljs-keyword">in</span> self.get_candidates(state, n):
            <span class="hljs-comment"># recurse</span>
            state.append(candidate)
            self.search(state, solutions, n)
            state.pop()
</code></pre>
<p>Check out the video course here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/H2gnD7Ixeao" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>You can access the template as well as the solutions to the two LeetCode problems (<strong>N-Queens</strong> and <strong>Sudoku Solver</strong>) in my GitHub gist:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="cdd880ee748e27ed28e0be3916f56fa6">
        <script src="https://gist.github.com/RuolinZheng08/cdd880ee748e27ed28e0be3916f56fa6.js"></script></div><p> </p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Remember that practice makes perfect, so do try applying this template to <a target="_blank" href="https://leetcode.com/tag/backtracking/">more backtracking problems on LeetCode.</a> Best of luck crunching your next coding interview!</p>
<p>For more content like this, check out my YouTube channel:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Make a Visual Novel Game in 10 Minutes – Python Ren'Py Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ Do you have a story idea that you'd like to turn into a novel? How about adding visual appeal and interactivity to that novel? A Visual Novel might be the game genre you are looking for. And this tutorial is here to help set you up in 10 minutes, wit... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-python-to-create-a-visual-novel/</link>
                <guid isPermaLink="false">66d46028264384a65d5a9596</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Tue, 22 Jun 2021 16:18:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-21-at-14.23.10-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Do you have a story idea that you'd like to turn into a novel? How about adding visual appeal and interactivity to that novel?</p>
<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Visual_novel">Visual Novel</a> might be the game genre you are looking for. And this tutorial is here to help set you up in 10 minutes, with minimal coding experience required. Let's get started!</p>
<h2 id="heading-tool-introduction-and-setup">Tool Introduction and Setup</h2>
<p>We will be using <a target="_blank" href="https://www.renpy.org/">the Ren'Py Visual Novel Engine</a>, which is built on top of Python 2.7. As Python itself is a scripting language, you will be able to "script" your visual novel project in Ren'Py.</p>
<blockquote>
<p>Since the arrival of Python 3, Python 2.7 has been sunsetted and is no longer actively maintained. Rest assured - Python 2.7 has all the features we need to create an awesome visual novel. Moreover, the newest release of Ren'Py, <a target="_blank" href="https://www.renpy.org/release/7.4.0">Ren'Py SDK 7.4</a>, provides a compatibility mode for Python 3. The developers also express the hope of integrating fully with Python 3 in the next release, Ren'Py 8.0.</p>
</blockquote>
<h3 id="heading-how-to-download-and-set-up-renpy">How to Download and Set up Ren'Py</h3>
<p>You can download the latest version of Ren'Py for your operating system (Windows, Mac, Linux) on <a target="_blank" href="https://www.renpy.org/">its official site.</a></p>
<p>Once you have downloaded and installed Ren'Py, you may open the Ren'Py launcher, select one of the starter projects (Tutorial, The Question) on the left, and click on <strong>Launch Project.</strong></p>
<p>Check out the <strong>Tutorial</strong> to get a sense of the full power of this engine, or <strong>The Question</strong> to see a very basic visual novel that you can make in 10 minutes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-21-at-10.51.50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The Ren'Py Launcher</em></p>
<h2 id="heading-how-to-create-a-new-project-in-renpy">How to Create a New Project in Ren'Py</h2>
<p>Let's create a new project. I called mine <strong>Forest Hike🌲</strong>, featuring a simple scene where two kids explore a forest trail.</p>
<p>Pay attention to the resolution you choose: The default is 1280 x 720. When we add images, our background images should also conform to these dimensions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/foresthike.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-run-the-boilerplate-project">How to Run the Boilerplate Project</h3>
<p>Launch the boilerplate project. Press <strong>Start</strong> from the main menu. After two brief lines of dialogue, the script ends and we are brought back to the main menu.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/launch.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running the boilerplate project</em></p>
<h2 id="heading-how-to-script-our-project">How to Script Our Project</h2>
<p>Let's start scripting our game based on the boilerplate.</p>
<p>Text Editors like <a target="_blank" href="https://www.sublimetext.com/">Sublime Text</a> and <a target="_blank" href="https://atom.io/">Atom</a> both have syntax highlighting for Ren'Py scripts ending in <code>.rpy</code>. Check out <a target="_blank" href="https://packagecontrol.io/packages/Renpy%20Language">this Sublime Text package</a> and <a target="_blank" href="https://atom.io/packages/language-renpy">this Atom package.</a></p>
<p>The two lines of dialogue we saw are located in <code>script.rpy</code>. Open that file and its content should look like the following. Just like in Python, lines that start with <code>#</code> are comments and won't be evaluated as part of the Ren'Py script. The comments and the code below are pretty self-explanatory.</p>
<pre><code class="lang-pgsql"># <span class="hljs-keyword">Declare</span> characters used <span class="hljs-keyword">by</span> this game
define e = <span class="hljs-type">Character</span>("Eileen")


# The game starts here
label <span class="hljs-keyword">start</span>:

    # <span class="hljs-keyword">Show</span> a background
    scene bg room

    # This shows a <span class="hljs-type">character</span> sprite
    <span class="hljs-keyword">show</span> eileen happy

    # These display lines <span class="hljs-keyword">of</span> dialogue.
    e "You've created a new Ren'Py game."
    e "Once you add a story, pictures, and music, you can release it to the world!"

    # This ends the game.
    <span class="hljs-keyword">return</span>
</code></pre>
<p>The <code>label</code> is used for control flow, which we will cover in the following section.</p>
<p>The <code>return</code> statement on the last line is what brought us back to the main menu.</p>
<h3 id="heading-how-to-declare-characters-and-add-dialogues">How to Declare Characters and Add Dialogues</h3>
<p>Let's replace the boilerplate character declaration and dialogues with those from our story. Here is how my story goes:</p>
<pre><code class="lang-pgsql">define laura = <span class="hljs-type">Character</span>(<span class="hljs-string">'Laura'</span>)
define tom = <span class="hljs-type">Character</span>(<span class="hljs-string">'Tom'</span>)

label <span class="hljs-keyword">start</span>:

    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    tom "Boo!"
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    <span class="hljs-keyword">return</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/story.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-add-images-and-transitions">How to Add Images and Transitions</h3>
<p>If you aren't an artist yourself, you may consider looking for assets in the creative commons domain. <a target="_blank" href="https://itch.io/">itch.io</a>, a marketplace for indie games, is a great place to look for assets.</p>
<p>I found <a target="_blank" href="https://fuelli.itch.io/free-to-use-character-sprites">this set of character sprites</a> for my project. For the background images, I simply applied artistic filters to creative commons pictures, giving real-life pictures a nice watercolor aesthetic.</p>
<p>I put all my images inside <code>game/images</code>. Note that it's okay to use whitespaces in the image file names.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-21-at-13.23.45.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>A conventional way of organizing image assets</em></p>
<p>Then we add to <code>script.rpy</code> those images as well as some transitions. Ren'Py applies transitions when it sees keywords like <code>with</code> and <code>at</code>. You can read more about transitions in <a target="_blank" href="https://www.renpy.org/doc/html/atl.html">Ren'Py's ATL (Animation and Transition Language) docs.</a></p>
<pre><code class="lang-pgsql">label <span class="hljs-keyword">start</span>:
    scene bg forest day <span class="hljs-keyword">with</span> fade
    <span class="hljs-keyword">show</span> laura angry
    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    hide laura
    scene bg forest day <span class="hljs-keyword">with</span> vpunch
    <span class="hljs-keyword">show</span> tom happy at right <span class="hljs-keyword">with</span> moveinbottom
    tom "Boo!"
    <span class="hljs-keyword">show</span> laura angry at left <span class="hljs-keyword">with</span> moveinleft
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    <span class="hljs-keyword">show</span> laura sad
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    <span class="hljs-keyword">return</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/images.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With the addition of visuals, our story is coming together nicely.</p>
<h3 id="heading-how-to-add-choices">How to Add Choices</h3>
<p>A game with different branches and endings more than doubles the fun. Adding a choice menu to a Ren'Py script is simple:</p>
<pre><code class="lang-pgsql">menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
    "Right":
        tom "Right is always the right way to go!"
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/choice.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-use-python-variables-and-control-flow">How to Use Python Variables and Control Flow</h3>
<p>We may define Python variables in a Ren'Py script and alter the flow of our story depending on their values. Python statements start with a <code>$</code> or an indented <code>python:</code> block.</p>
<p>Adding variables to our previous choice menu:</p>
<pre><code class="lang-pgsql">menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
        $ is_lost = <span class="hljs-keyword">True</span>
    "Right":
        tom "Right is always the right way to go!"
        $ is_lost = <span class="hljs-keyword">False</span>
scene bg forest noon <span class="hljs-keyword">with</span> Dissolve(<span class="hljs-number">3.0</span>)
scene bg forest dusk <span class="hljs-keyword">with</span> Dissolve(<span class="hljs-number">3.0</span>)
<span class="hljs-keyword">show</span> laura sad at left <span class="hljs-keyword">with</span> moveinleft
laura "It's getting late. Are you sure we aren't lost?"
<span class="hljs-keyword">if</span> is_lost:
    <span class="hljs-keyword">show</span> tom sad at right <span class="hljs-keyword">with</span> moveinleft
    tom "I hope not, but I have a bad feeling about this."
<span class="hljs-keyword">else</span>:
    <span class="hljs-keyword">show</span> tom happy at right <span class="hljs-keyword">with</span> moveinleft
    tom "We are fine. Look! There's the end of the trail."
    tom "I'm the best scout around."
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/ezgif.com-gif-maker-2-.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>See the end of this post for my handcrafted resources for working with Python in Ren'Py.</p>
<h3 id="heading-how-to-play-music">How to Play Music</h3>
<p>According to <a target="_blank" href="https://www.renpy.org/doc/html/audio.html">Ren'Py's Audio docs</a>, playing music and sound effects is as easy as the following:</p>
<pre><code class="lang-pgsql">play music "mozart.ogg"
play sound "woof.mp3"
</code></pre>
<h3 id="heading-how-to-save-and-load-the-game">How to Save and Load the Game</h3>
<p>Ren'Py has done all the heavy lifting for us and has a built-in save and load system.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/save.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Saving the game</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/load.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Loading the game</em></p>
<h3 id="heading-other-customization-you-can-do">Other Customization You Can Do</h3>
<p>Currently in our dialogue, the entire line of text is displayed at once, instead of character by character. We may change the variable <code>preference.text_cps</code> (CPS stands for character per second) in <code>options.rpy</code> like this.</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">default</span> preferences.text_cps = <span class="hljs-number">20</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/cps.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Setting a custom CPS displays the text one character at a time at a given rate</em></p>
<p>There is even more that we can customize in <code>gui.rpy</code> (GUI stands for Graphic User Interface, which includes the textbox and menu choice items that we have seen) or <code>screens.rpy</code>.</p>
<h2 id="heading-what-else-is-renpy-capable-of">What Else is Ren'Py Capable of?</h2>
<p>Ren'Py's capability extends way beyond displaying text and images. I may go as far as to say that Ren'Py is about as capable and versatile as Python itself.</p>
<p>With the <a target="_blank" href="https://www.pygame.org/news">Pygame</a> module, it is possible to create complex mini games in Ren'Py. I myself have created and open-sourced a few mini games, including a chess engine that integrates with the Stockfish chess AI as well as a rhythm game engine that automatically generates the beat map for any music file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/promotion.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My chess game demo</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/demo-4.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My rhythm game demo</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://r3dhummingbird.itch.io/renpy-chess-game">https://r3dhummingbird.itch.io/renpy-chess-game</a></div>
<p> </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://r3dhummingbird.itch.io/renpy-rhythm-game">https://r3dhummingbird.itch.io/renpy-rhythm-game</a></div>
<p> </p>
<h2 id="heading-resources">Resources</h2>
<p>This tutorial should help get you started with Ren'Py. It's always useful to refer to <a target="_blank" href="https://www.renpy.org/doc/html/">the official documentation</a> as you learn the more advanced features to add buzz to your project.</p>
<p>I've also created some course material to help you brush up on Python fundamentals and their capabilities in Ren'Py scripts.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/python-for-renpy-dev">https://github.com/RuolinZheng08/python-for-renpy-dev</a></div>
<p> </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.udemy.com/course/python-basics-for-renpy-developers/?referralCode=774C55606994052EBFCB">https://www.udemy.com/course/python-basics-for-renpy-developers/?referralCode=774C55606994052EBFCB</a></div>
<p> </p>
<p>Check out my course intro video on YouTube:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pQcb_pfIbI0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Thanks for reading and have fun telling your story!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Recover from Deployment Hell – What I Learned After My Discord Bot Crashed on a 1,000+ User Server ]]>
                </title>
                <description>
                    <![CDATA[ I built a Discord AI Chatbot in my last blog post and then, to challenge myself, proceeded to stress-test it on a Discord server with 1,000+ users. In the first hour, Deployment Hell struck and I had to take the bot down for maintenance. Another hour... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/</link>
                <guid isPermaLink="false">66d46025264384a65d5a9594</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lessons learned ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Life lessons ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Fri, 04 Jun 2021 20:41:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/Untitled126_20210603134910.PNG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I built <a target="_blank" href="https://www.freecodecamp.org/news/discord-ai-chatbot/">a Discord AI Chatbot in my last blog post</a> and then, to challenge myself, proceeded to stress-test it on a Discord server with 1,000+ users.</p>
<p>In the first hour, Deployment Hell struck and I had to take the bot down for maintenance. Another hour passed and I managed to patch up my bot and send it back.</p>
<p>Now my bot is up and running and more resilient than ever. As for me, I survived and even thrived in Deployment Hell 🔥. In this deployment postmortem, I'm going to show you how.</p>
<p>I sent my AI chatbot into the server at 2 pm on June 2nd, and hype started gathering around it. The chat went on for a while and all was nice and smooth.</p>
<p>At 3:20 pm, everything started falling apart: My free-tier API key reached its hourly request limit, and I had no choice but to take down the bot and the server for maintenance.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/IMG_0676.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Never mind the typos in my message 😅 This incident caught me totally unprepared</em></p>
<p>Despite being struck by Deployment Hell in the first hour on the first day of my project debut, I knew I need to sit down, take a deep breath, and recover from this incident.</p>
<h1 id="heading-what-went-well-with-deployment">What Went Well with Deployment</h1>
<p>As a first step, I didn't forget to congratulate myself on what went well despite this mishap. Clearly, people were enthusiastic about chatting with my chatbot, to the extent that we went over rate limit.</p>
<p>Moreover, in the brief hour when I observed people interact with my bot in real time, I discovered several good design choices that I've consciously, unconsciously, or subconsciously made.</p>
<h2 id="heading-avoid-feature-creep-at-all-costs-during-development">Avoid Feature Creep At All Costs During Development</h2>
<p>I originally developed my code for a tutorial, so I kept my code as simple and readable as possible, without complicated features that won't serve my bot's main use case: chatting.</p>
<p>That said, I marked down <strong>TODOs and stretch goals</strong> in my code, hoping to get back to those if necessary. For example:</p>
<pre><code class="lang-python"> <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> cache chat history in DB and load</span>
 <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> after each user input and bot input,</span>
 <span class="hljs-comment"># append them to conversation history for the next query</span>
 ...
 <span class="hljs-comment"># <span class="hljs-doctag">FIXME:</span> better make this try-except block more fine-grained</span>
</code></pre>
<p>As I observed users interacting with the bot, I'm actually relieved that I didn't implement the <strong>memory cache feature</strong>. Several users were talking with the bot at once, each along their different conversation thread. If I were to keep track of the conversation history, I would have to create a unique log for each user, further complicating database operations.</p>
<h2 id="heading-abide-by-the-principle-of-least-privilege">Abide by the Principle of Least Privilege</h2>
<p>One thing I learned in my Computer Security class is the Principle of Least Privilege (PoLP) – granting an application the minimum amount of access it needs to do its job.</p>
<p>My chatbot only needs two low-level security permissions: <strong>View Channel</strong> for reading users' messages, and <strong>Send Text Messages</strong> for replying to users.</p>
<p>Of course I could have given it more fancy permissions like those shown below in the image, just in case it needs any. But that would have violated PoLP and who knows whether my malfunctioning bot will bring down anything else with it when it fails?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-03-at-14.21.52-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Permission settings for Discord bots</em></p>
<h2 id="heading-other-observations-from-deploying-my-bot">Other Observations from Deploying My Bot</h2>
<p>Despite its ephemeral lifespan, my bot offered me an opportunity to conduct user research in the real world. This is a server with 1000+ users, not my cozy dev server where my friends and I hang out and take turns to politely exchange lines with the bot.</p>
<p>Here, I observed several interesting user behaviors:</p>
<ul>
<li><p>People tend to ask the bot <strong>open-ended questions</strong> instead of <strong>factual ones</strong>. Because I built my bot based off a video game character, when developing the AI model, I was keen on making sure the model learns the canon information about the character, like name, age, and role in the game. I was relieved when I saw that people are much more curious about the bot character's preference for ice cream flavor than their factual place of birth.</p>
</li>
<li><p>People use a lot of emoticons :), emojis 😃 and GIFs as they text. However, these will most likely be treated as <code>&lt;UNKNOWN&gt;</code> tokens in the AI model's tokenizer, which means I should <strong>sanitize</strong> user inputs.</p>
</li>
</ul>
<p>I was also very fortunate to receive direct feedback from friendly people on the server. One feature request that I got was to make the bot attach its response to a user's message thread, instead of just dumping its response in the channel.</p>
<p>Buzzing with excitement from people's enthusiasm and armed with insights from user research, I was ready to patch up my bot and send it back as soon as possible.</p>
<h1 id="heading-what-i-needed-to-fix">What I Needed to Fix</h1>
<p>As a good development habit, I kept my code well-organized and modularized, so switching from the production bot back to my development bot requires no more than copy-pasting the dev bot's API key.</p>
<p>Once I was running on my dev server, I sat down to identify the types of problems I needed to tackle.</p>
<h2 id="heading-fatal-crashes">Fatal Crashes</h2>
<p>The fatal bug that caused me to take down my bot was that I hit an hourly API rate limit. I took the obvious approach of keeping <strong>redundancy</strong> in my system: Keep an alternative API key, and once the primary one runs out, switch over to the alternative, and then switch back at the turn of the hour.</p>
<p>Workaround aside, I noted that this is a short-term solution. If I need to properly scale up my system, I should make an estimate of the number of requests per hour, as I'll discuss at the end of this section.</p>
<h2 id="heading-new-features-for-usability">New Features for Usability</h2>
<p>I decided on several new features that will make my bot more user-friendly. Some highlights are:</p>
<ul>
<li><p>As some server people suggested, I re-programmed the bot so that instead of dumping responses to different user messages into the channel, it directly responds to each user message in the message's thread.</p>
</li>
<li><p>I sanitized user inputs by removing Unicode emojis and Discord-specific <code>&lt;:some_hilarious_gif&gt;</code> tags. This will limit <code>&lt;UNKNOWN&gt;</code> tokens that my AI model will receive and help it generate better responses.</p>
</li>
<li><p>I implemented a magic command <code>$ignore [message]</code> that allow users to send a message to the channel without triggering a bot response. This feature comes from my observation that, whenever the bot says something funny or smart (or both!), users will remark on that by sending a text intended for their friends (and not the bot) to the channel. It'd be annoying to still receive a bot response on a remark intended for a friend. Hence, I hope that this magic command will address this user pain point.</p>
</li>
<li><p>I implemented magic commands for the server moderators to interface with my bot (stopping or rebooting) so that they can keep the bot under control without having to access my Repl.it server. This makes both my job and theirs easier.</p>
</li>
</ul>
<h2 id="heading-future-proof-logging">Future-proof Logging</h2>
<p>In my cozy dev server, there is little complexity, whereas on this 1000+ user server where 40% of users are online at any given moment, complexity explodes.</p>
<p>There are multiple channels besides the chat channel dedicated to my bot, multiple user roles and permission levels, multiple users typing at the same time, and so on.</p>
<p>While I certainly cannot prevent all possible failure scenarios, what I could do is to protect the important part of my code with a try-except block and log out all information that might reveal the cause of a failure. Since real-time system bugs are subtle and difficult to reproduce, logging will save me lots of headaches down the road.</p>
<pre><code class="lang-python"><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            print(e, <span class="hljs-string">'Offending channel'</span>, message.channel, 
            <span class="hljs-string">'Offending message'</span>, message.content, 
            <span class="hljs-string">'Offending bot response'</span>, bot_response, 
            sep=<span class="hljs-string">'\n'</span>, end=<span class="hljs-string">'\n\n'</span>)
</code></pre>
<h2 id="heading-scalability">Scalability</h2>
<p>Estimating under some system constraints is where basic statistics and heuristics come in. Hugging Face's model Inference API imposes two limits on the scalability of my system:</p>
<ol>
<li><p>A 10k tokens (characters) per hour rate limit, which is about 300 queries.</p>
</li>
<li><p>A 30k tokens per month quota for free-tier accounts, which is about 900 queries.</p>
</li>
</ol>
<blockquote>
<p>Wonder how I get the numbers? Fun fact: 1) <a target="_blank" href="https://capitalizemytitle.com/character-count/10000-characters/">10k characters is between 1430 and 2500 words</a>. We will take 2100 since Discord messages usually use simple, short words. 2) T<a target="_blank" href="https://crushhapp.com/texting-tidbits/the-average-text-message-length-is-around-7-words">he average length of a text message is 7 words</a>. 2100 / 7 = 300 messages</p>
</blockquote>
<p>After processing these numbers, the fact that I hit the per-hour rate limit during the first hour of my bot debut is quite a remarkable feat. People are clearly hyped about my witty chatbot. 🥳</p>
<p>Suppose the hype recedes and life goes on, consider now a hypothetical scenario where 20 users (2% of the 1000+ on the server) regularly chat with my bot, each for 25 lines, in the two hours following dinner. This produces a total of 500 queries in two hours (or 250 per hour), meaning that my bot is safe from the per-hour rate limit of 300.</p>
<p>However, in a month, 500 * 30 = 15,000 queries, 15 times more than my quota of 900. If my bot is indeed this popular, I would need to switch to a higher-tier subscription plan to ensure that it remains available.</p>
<h2 id="heading-from-tutorial-code-to-production-code">From Tutorial Code to Production Code</h2>
<p>Compared to my tutorial code which strives to be simple, readable, and educative, my production code is longer, more complicated, but also more robust.</p>
<h1 id="heading-what-makes-a-great-side-project">What Makes a Great Side Project</h1>
<p>Having emerged from Deployment Hell, like my bot, I'm more resilient than ever and have gained new insights about the principles and challenges in real-world software engineering.</p>
<p>As a final takeaway, I reflect on what makes my Discord AI chatbot this popular. (On June 3rd, a day when it's up 24 hours, it had already busted the monthly 30k quota on both my account and one I borrowed from a friend, totaling 2,000+ messages. 🤓)</p>
<p>I have completed and polished various side projects that received positive feedback, but none of them were as half popular as this one. In retrospect, it's not too hard to see why.</p>
<p>Among projects that I'm most proud of, I built <a target="_blank" href="https://github.com/RuolinZheng08/renpy-chess">a chess engine</a> and <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">a rhythm game engine</a> for <a target="_blank" href="https://renpy.org/">the Ren'Py Visual Novel (VN) game development engine.</a> Both are rated 5-star on <a target="_blank" href="https://itch.io/">itch.io</a>, a popular platform for publishing indie games.</p>
<p>These projects, however, are open-source engines intended for developers to integrate into their VN games more than standalone playables that can entertain players for hours.</p>
<p>In comparison, my Discord AI chatbot manages to capture each of the following elements that distinguishes a <strong>great</strong> side project from a good one:</p>
<ul>
<li><p><strong>Audience:</strong> I'm fortunate to have this friendly server with 1000+ users who are open to experimenting with the bot and provide me with helpful feedback.</p>
</li>
<li><p><strong>Accessibility:</strong> For people to enjoy my chabot, there is nothing special they need to add to their routine - not even opening up a new web app - they just log into Discord as usual, and voilà, the bot is here to chat!</p>
</li>
<li><p><strong>Interactivity:</strong> Without interactive components, even the most visually-astonishing game will fail to retain players' attention. Nothing to worry about for my chatbot though: Like a loyal friend, it always has something to quip about whenever you need a good chat.</p>
</li>
</ul>
<p>If you'd like to learn more about my methodology for working on side projects, check out my previous blog post:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/how-i-built-my-one-person-open-source-project/">https://www.freecodecamp.org/news/how-i-built-my-one-person-open-source-project/</a></div>
<p> </p>
<p>Also check out my chatbot tutorial!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/discord-ai-chatbot/">https://www.freecodecamp.org/news/discord-ai-chatbot/</a></div>
<p> </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/UBwvFuTC1ZE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>You can also try this out in JavaScript:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/XR6JFRLxe5A" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Live2D Web App Using Heroku ]]>
                </title>
                <description>
                    <![CDATA[ What is Live2D? Live2D is a technology that allows artists to easily transform traditional 2D illustrations to create fluid expressions and motions. The most popular software for Live2D modeling and animation is Cubism, which also provides well-docum... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-a-live2d-web-app-using-heroku/</link>
                <guid isPermaLink="false">66d4601d787a2a3b05af43d8</guid>
                
                    <category>
                        <![CDATA[ animation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #Game Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Heroku ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Mon, 28 Dec 2020 17:48:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/12/web-2.gif" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-live2d">What is Live2D?</h2>
<p>Live2D is a technology that allows artists to easily transform traditional 2D illustrations to create fluid expressions and motions.</p>
<p>The most popular software for Live2D modeling and animation is Cubism, which also provides well-documented SDK for web, native apps, and the Unity game development engine.</p>
<p>In this tutorial, I will walk you through how to build on top of Cubism's official Live2D Web SDK sample and deploy it to Heroku, a popular cloud app-hosting platform.</p>
<h2 id="heading-how-to-set-up-the-environment">How to Set Up the Environment</h2>
<p>To follow along with this tutorial, clone my GitHub repo and checkout the <code>start</code> branch. The finished project is on the <code>develop</code> branch.</p>
<p>I've also recorded <a target="_blank" href="https://youtu.be/uH1IczzE_t4">a video tutorial on YouTube</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/heroku-live2d">https://github.com/RuolinZheng08/heroku-live2d</a></div>
<p> </p>
<pre><code class="lang-shell">git clone https://github.com/RuolinZheng08/heroku-live2d.git
git checkout start

# update the submodule, Cubism's Live2d Web Framework
git submodule update --init
</code></pre>
<p>Install Node.js and npm using Homebrew:</p>
<pre><code class="lang-shell"># if you need to install homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# homebrew will install node and npm at the same time
brew install node
</code></pre>
<p>I'll be using Visual Studio Code as my main IDE, but you may follow along using any editor of your choice.</p>
<h2 id="heading-how-to-run-the-starter-code-locally">How to Run the Starter Code Locally</h2>
<p>The directory structure is as follows. Our web app will be served from <code>Samples/TypeScript/Demo</code>.</p>
<pre><code class="lang-pgsql">.
├─ .vscode          # Visual Studio Code project setting
├─ Core             # Live2D Cubism Core JavaScript <span class="hljs-keyword">and</span> TypeScript source code
├─ Framework        #  Source code <span class="hljs-keyword">for</span> the rendering <span class="hljs-keyword">and</span> animation features
└─ Samples
   ├─ Resources     # Live2D model files <span class="hljs-keyword">and</span> web image assets
   └─ TypeScript    # [IMPORTANT] TypeScript sample project
</code></pre>
<p>Inside the <code>heroku-live2d</code> directory, run the following commands:</p>
<pre><code class="lang-shell">cd Samples/TypeScript/Demo/

npm install

npm run-script build

npm run-script serve
</code></pre>
<p>Navigate to <a target="_blank" href="http://localhost:5000/Samples/TypeScript/Demo/">http://localhost:5000/Samples/TypeScript/Demo/</a> and you should be able to see a Live2D character.</p>
<p>To interact with the model, hold down your mouse cursor and the character's head and eyes will follow your cursor. Tap on the body of the character to see a special animation. Tap on the gear icon in the top right corner to switch between different models.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/web.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Mark from</em> <code>Samples/Resources/Mark</code></p>
<h2 id="heading-how-to-deploy-to-heroku">How to Deploy to Heroku</h2>
<p>The starter code uses npm, TypeScript, and webpack.</p>
<p>To deploy our project to Heroku, we need to create a <code>package.json</code> file that Heroku can use to build our project in our project root directory. We also need to modify <code>Samples/TypeScript/Demo/package.json</code> and the webpack configuration in <code>Samples/TypeScript/Demo/webpack.config.js</code>.</p>
<h3 id="heading-top-level-packagejson">Top-level package.json</h3>
<p>The boilerplate <code>package.json</code> for a Node.js Heroku app looks like this:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"heroku-live2d"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Live2D Cubism Heroku Demo"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"start"</span>: ...,
        <span class="hljs-attr">"build"</span>: ...
    },
    <span class="hljs-attr">"dependencies"</span>: {
        ...
    }
}
</code></pre>
<p>Inspect the <code>dependencies</code> and <code>devDependencies</code> attributes in <code>Samples/TypeScript/Demo/package.json</code> and add both sets of dependencies as <code>dependencies</code> to <code>heroku-live2d/package.json</code>.</p>
<p>Remember that when building and serving locally, we used <code>npm run-script [build|serve]</code> from inside the <code>Samples/TypeScript/Demo</code> directory.</p>
<p>Therefore, to run these npm commands from the project root, we need to prepend <code>cd Samples/TypeScript/Demo</code> before the npm commands. The build command, for example, will become:</p>
<pre><code class="lang-shell">cd Samples/TypeScript/Demo &amp;&amp; npm run-script build
</code></pre>
<p>With these changes, the top-level <code>package.json</code> should look like this:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"heroku-live2d"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Live2D Cubism Heroku Demo"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"cd Samples/TypeScript/Demo &amp;&amp; npm run-script start"</span>,
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"cd Samples/TypeScript/Demo &amp;&amp; npm run-script build"</span>
    },
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"@typescript-eslint/eslint-plugin"</span>: <span class="hljs-string">"^2.18.0"</span>,
        <span class="hljs-attr">"@typescript-eslint/parser"</span>: <span class="hljs-string">"^2.18.0"</span>,
        <span class="hljs-attr">"eslint"</span>: <span class="hljs-string">"^6.8.0"</span>,
        <span class="hljs-attr">"eslint-config-prettier"</span>: <span class="hljs-string">"^6.10.0"</span>,
        <span class="hljs-attr">"eslint-plugin-prettier"</span>: <span class="hljs-string">"^3.1.2"</span>,
        <span class="hljs-attr">"prettier"</span>: <span class="hljs-string">"^1.19.1"</span>,
        <span class="hljs-attr">"rimraf"</span>: <span class="hljs-string">"^3.0.1"</span>,
        <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"^11.3.0"</span>,
        <span class="hljs-attr">"ts-loader"</span>: <span class="hljs-string">"^6.2.1"</span>,
        <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^3.7.5"</span>,
        <span class="hljs-attr">"webpack"</span>: <span class="hljs-string">"^4.41.5"</span>,
        <span class="hljs-attr">"webpack-cli"</span>: <span class="hljs-string">"^3.3.10"</span>,
        <span class="hljs-attr">"webpack-dev-server"</span>: <span class="hljs-string">"^3.10.1"</span>,
        <span class="hljs-attr">"whatwg-fetch"</span>: <span class="hljs-string">"^3.0.0"</span>
    }
}
</code></pre>
<h3 id="heading-samplestypescriptdemopackagejson">Samples/TypeScript/Demo/package.json</h3>
<p>On localhost, we were running on port 5000. However, Heroku will dynamically assign our web app a port stored in a variable <code>$PORT</code>. Therefore, we need the <code>npm run-script start</code> command inside <code>Samples/TypeScript/Demo/package.json</code> to start the webpack server on port <code>$PORT</code>.</p>
<p>Append to <code>scripts &gt; start &gt; webpack-dev-server --progress</code> so it looks like this:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"webpack-dev-server --progress --port $PORT"</span>,
    ...
}
</code></pre>
<h3 id="heading-samplestypescriptdemowebpackconfigjs">Samples/TypeScript/Demo/webpack.config.js</h3>
<p>Add <code>disableHostCheck</code> to the configuration of <code>devServer</code> and remove <code>port</code> since we have configured it dynamically above.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
    ...,
    <span class="hljs-attr">devServer</span>: {
        <span class="hljs-attr">contentBase</span>: path.resolve(__dirname, <span class="hljs-string">'../../..'</span>),
        <span class="hljs-attr">watchContentBase</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">hot</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">port</span>: <span class="hljs-number">5000</span>, <span class="hljs-comment">// delete this line</span>
        <span class="hljs-attr">host</span>: <span class="hljs-string">'0.0.0.0'</span>,
        <span class="hljs-attr">disableHostCheck</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// add this line</span>
        <span class="hljs-attr">compress</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">useLocalIp</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">writeToDisk</span>: <span class="hljs-literal">true</span>
    },
    ...
}
</code></pre>
<p>Add <code>watchOptions</code> so that our <code>node_modules</code> won't be watched. If we don't do this, we will run into an error about exceeding the maximum number of watchers when we deploy to Heroku.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
    ...,
    <span class="hljs-attr">watchOptions</span>: {
        <span class="hljs-attr">ignored</span>: <span class="hljs-regexp">/node_modules/</span>
    },
    ...
}
</code></pre>
<h3 id="heading-deploy-to-heroku">Deploy to Heroku</h3>
<p>To download the Heroku command line client, run</p>
<pre><code class="lang-shell">brew tap heroku/brew &amp;&amp; brew install heroku
</code></pre>
<p>Log into Heroku from the command line using <code>heroku login</code>.</p>
<p>Create a Heroku app and append some numbers (for example, 123) to the app name to ensure uniqueness.</p>
<pre><code class="lang-shell">heroku create heroku-live2d-NUMBERS
</code></pre>
<p>Set up Node.js as the buildpack:</p>
<pre><code class="lang-pgsql">heroku buildpacks:<span class="hljs-keyword">set</span> heroku/nodejs
</code></pre>
<p>Add and commit your project using git. Note that we don't necessarily need <code>git push</code>:</p>
<pre><code class="lang-shell">git add .
git commit -m "Ready to deploy to heroku"
</code></pre>
<p>Push the project to Heroku, assuming you are following along on the <code>start</code> branch. You can always check the branch you are on and push from that branch.</p>
<pre><code class="lang-shell"># check which branch we are on
git branch

# the syntax is
# git push heroku GIT_BRANCH_NAME:HEROKU_BRANCH_NAME
git push heroku start:master
</code></pre>
<p>You may need to wait for a few minutes for the build process to complete.</p>
<p>After that, navigate to <code>YOUR-HEROKU-APP-NAME.herokuapp.com/Samples/TypeScript/Demo</code>. In my case, the URL is <a target="_blank" href="https://heroku-live2d.herokuapp.com/Samples/TypeScript/Demo/">https://heroku-live2d.herokuapp.com/Samples/TypeScript/Demo/</a>. The Live2D characters will be there to greet you :)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/Screen-Shot-2020-12-27-at-16.13.19.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Notice that the highlighted URL is already on Heroku</em></p>
<h3 id="heading-how-to-redirect-indexhtml-to-samplestypescriptdemo">How to Redirect index.html to Samples/TypeScript/Demo</h3>
<p>You might have noticed that <code>YOUR-HEROKU-APP-NAME.herokuapp.com</code> shows a list of the directory structure instead of the Live2D models. We can solve this by adding a dummy top-level <code>index.html</code> that redirects to <code>Samples/TypeScript/Demo</code>.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Just a dummy html to redirect to my subdirectory --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"refresh"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"0; url=Samples/TypeScript/Demo"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Rerun the deployment command <code>git push heroku start:master</code>. Now when you visit <code>YOUR-HEROKU-APP-NAME.herokuapp.com</code>, you will be automatically redirected to the Live2D model page.</p>
<p>Congratulations on making it to the end of this tutorial! You now have a Live2D Web App deployed to Heroku.</p>
<p>I hope you enjoyed this tutorial. Let's keep in touch! Connect with me on <a target="_blank" href="https://www.linkedin.com/in/ruolin-zheng/">LinkedIn</a>, <a target="_blank" href="https://github.com/RuolinZheng08">GitHub</a>, <a target="_blank" href="https://medium.com/@ruolinzheng">Medium</a>, or check out <a target="_blank" href="https://ruolinzheng08.github.io/">my personal website</a>.</p>
<h3 id="heading-resources-amp-links">Resources &amp; Links</h3>
<p><a target="_blank" href="https://github.com/RuolinZheng08/heroku-live2d/tree/develop">My GitHub repo for this tutorial</a></p>
<p><a target="_blank" href="https://heroku-live2d.herokuapp.com/">My Heroku App</a></p>
<p><a target="_blank" href="https://youtu.be/uH1IczzE_t4">My YouTube Video Tutorial</a></p>
<p><a target="_blank" href="https://docs.live2d.com/cubism-sdk-tutorials/sample-build-web/">Cubism's Official SDK Documentation</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I Built My One-Person Project: A Chess Engine for a Popular Game Dev Engine ]]>
                </title>
                <description>
                    <![CDATA[ I recently finished one of my summer projects: a chess GUI engine built using the Ren’Py Visual Novel Game Development Engine and the python-chess library. This engine will be integrated into a kinetic novel game, The Wind at Dawn, at that game’s com... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-my-one-person-open-source-project/</link>
                <guid isPermaLink="false">66d4601b3bc3ab877dae2218</guid>
                
                    <category>
                        <![CDATA[ open source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ side project ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Tue, 08 Sep 2020 23:23:46 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c98ca740569d1a4ca1c15.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I recently finished one of my summer projects: <a target="_blank" href="https://github.com/RuolinZheng08/renpy-chess">a chess GUI engine</a> built using the <a target="_blank" href="http://renpy.org/">Ren’Py Visual Novel Game Development Engine</a> and the <a target="_blank" href="https://github.com/niklasf/python-chess">python-chess</a> library.</p>
<p>This engine will be integrated into a kinetic novel game, <a target="_blank" href="https://madeleine-chai.itch.io/the-wind-at-dawn"><em>The Wind at Dawn</em></a>, at that game’s completion.</p>
<p>In this post, I’d like to share some key learnings, technical and non-technical, that I gathered from pushing this one-person project from start to finish in a month.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/foolsmate.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>_[My Chess Engine Project on GitHub](https://github.com/RuolinZheng08/renpy-chess" data-href="https://github.com/RuolinZheng08/renpy-chess" class="markup--anchor markup--figure-anchor" rel="noopener" target="<em>blank)</em></p>
<h2 id="heading-appreciate-the-value-of-rewriting-old-code">Appreciate the Value of Rewriting Old Code</h2>
<p>For CS projects at school, I seldom have the opportunity or experience the need to revisit my code.</p>
<p>However, this is not the case when I work on my passion projects: I love to take every opportunity to improve their usability and reusability in the hope that my code will be of value to other developers.</p>
<p>This chess engine is based on <a target="_blank" href="https://github.com/RuolinZheng08/renpy-chess-engine">a chess engine I created in Ren’Py and vanilla Python</a> while teaching myself Python during my first summer break in college.</p>
<p>That old chess engine is, in turn, based on a project in my college Intro to CS class (a chess GUI game written in Racket, a functional programming language). That is to say, I’ve rewritten my code twice to produce this final chess engine.</p>
<p>For my first rewrite, I simply “translated” the chess logic (for determining whether a move is legal, endgame conditions, and so on.) from Racket to Python. I also experimented with Object-Oriented Programming, wrote a minimax chess AI following online tutorials, and implemented the GUI in Ren’Py.</p>
<p>Since I only knew the very basics of chess and wrote my chess logic per my school project grading spec, my first chess engine didn’t support special moves like en passant, castling, or promotion.</p>
<p>To address this issue in my second rewrite, I researched open-source Python libraries and found <a target="_blank" href="https://github.com/niklasf/python-chess">python-chess</a>, a library with full support for chess moves and endgame conditions like claiming a draw when threefold repetition occurs.</p>
<p>On top of that, it has also integrated <a target="_blank" href="https://stockfishchess.org/">Stockfish</a>, a chess AI, and this integration will enable my chess engine to configure the strength of the chess AI.</p>
<p>These two features added great value to my chess engine version 2.0 and allowed me to focus on the more important aspects of my rewrite, which I will describe below.</p>
<h2 id="heading-read-the-documentation-and-keep-compatibility-in-mind">Read the Documentation and Keep Compatibility in Mind</h2>
<p>It has become my habit to skim through the documentation of the libraries that I need for my project before jumping into the design and the code. This helps me evaluate any issue with dependency and compatibility that might arise.</p>
<p><a target="_blank" href="https://github.com/renpy/renpy/issues/2003">This Ren’Py GitHub issue</a> points to the fact that Ren’Py uses Python 2 and hasn’t yet been ported to Python 3. So I recognized that I needed a version of python-chess that supports Python 2, <a target="_blank" href="https://python-chess.readthedocs.io/en/latest/#features">as the latest version only supports Python 3.7+</a>.</p>
<p>Fortunately, <a target="_blank" href="https://python-chess.readthedocs.io/en/v0.23.10/index.html#features">version 0.23.10</a> supports both Python 2.7 and Python 3.4+. I ultimately settled on version 0.23.11 as it is the last version that still supports Python 2.7.</p>
<p>Having sorted through dependency and compatibility issues, I was ready to move on to design and coding.</p>
<h2 id="heading-follow-software-engineering-best-practices">Follow Software Engineering Best Practices</h2>
<p>Note: A lot of terms mentioned in this section are from <a target="_blank" href="https://en.wikipedia.org/wiki/Scrum_%28software_development%29">Agile/Scrum</a>.</p>
<h3 id="heading-gather-feature-requirements-for-project-design">Gather Feature Requirements for Project Design</h3>
<p>While it is tempting to jump straight into coding, I cannot stress enough the importance of design.</p>
<p>Think about design as a high-level roadmap that clearly delineates the starting point, milestones, and ending points of the project. This lets developers refer to when they are waist-deep in intricate implementation details.</p>
<p>This is especially important for extracurricular projects as they don’t usually have a detailed, highly-technical spec, whereas most school projects do.</p>
<p>For my chess engine, I identified the following rewrites/additional features:</p>
<ul>
<li><p>Integrate the chess logic from python-chess</p>
</li>
<li><p>In my Ren’Py GUI code, replace the chess logic and chess AI I wrote with the chess logic and Stockfish APIs from python-chess</p>
</li>
<li><p>Support various gameplay modes: Player vs. Player, Player vs. Computer (where Player can choose to play as black or white), adjustable strength of the chess AI via adjustments to Stockfish’s configuration parameters</p>
</li>
<li><p>Develop a Ren’Py GUI for pawn promotion</p>
</li>
<li><p>Develop a Ren’Py GUI for claiming a draw in the case of threefold repetition or the fifty-move rule</p>
</li>
</ul>
<h3 id="heading-develop-a-proof-of-concept-prototype">Develop a Proof of Concept Prototype</h3>
<p>A Proof of Concept (POC) prototype helps me gauge the time and effort needed to implement the required features.</p>
<p>For my chess engine POC, I integrated python-chess with my original Ren’Py GUI code. I made sure its set of features was minimum yet readily extensible:</p>
<ul>
<li><p>I integrated python-chess with my original Ren’Py GUI code and was able to move pieces around</p>
</li>
<li><p>I only implemented Player vs. Player in order to postpone integrating Stockfish for chess AI</p>
</li>
<li><p>I only allowed non-promotion moves so as to postpone developing the GUI for pawn promotion</p>
</li>
</ul>
<h3 id="heading-identify-the-projects-definition-of-ready-and-definition-of-done">Identify the Project’s Definition of Ready and Definition of Done</h3>
<p>My project’s Definition of Ready (DoR) naturally follows from my initial investigation about library version compatibility and my POC.</p>
<p>In parallel, my project’s Definition of Done (DoD) follows from the feature requirements I identified from my design phase.</p>
<h3 id="heading-deliver-a-minimum-viable-product-or-better-yet-a-minimum-lovable-product">Deliver a Minimum Viable Product, or better yet, a Minimum Lovable Product</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/promotion.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Promotion UI</em></p>
<p>When I was in the design phase gathering requirements, I knew that there were a lot of stretch goals to my project — perhaps even more than I could ever accomplish.</p>
<p>So it was important for me to implement the very basic set of required features, deliver a Minimum Viable Product (MVP), and gather feedback to iterate on it.</p>
<p>Better yet, I’d like to deliver a Minimum Lovable Product (MLP) on my first iteration. The minute difference is that whereas an MVP requires nothing more than functional features, an MLP has a lovable user experience by design.</p>
<p>For instance, to implement pawn promotion moves, for an MVP I could ask users to press different keys to select the piece type they want to promote to (like B for bishop and K for knight).</p>
<p>For an MLP, I would implement a UI with piece-type-shaped buttons that change colors when hovered or selected.</p>
<h2 id="heading-be-your-own-project-manager">Be Your Own Project Manager</h2>
<p>If you find keeping track of the list of features (plus the ever-growing list of bugs and fixes) overwhelming, you are not alone. It’s time to be your own project manager.</p>
<p>I found <a target="_blank" href="https://trello.com/">Trello</a> to be an amazing tool both for single-person projects and large-team projects.</p>
<p>This is how I usually organize my Trello board for a coding project:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/board.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The Trello Board for My Chess Engine Project</em></p>
<p>Have four lists: <strong>Backlog</strong> (for features to be triaged), <strong>TODO</strong>, <strong>Doing</strong>, and <strong>Done.</strong></p>
<p>Have color-coded labels:</p>
<ul>
<li><p><strong>Ready for QA:</strong> A red label to get the attention of my teammates</p>
</li>
<li><p><strong>Impact:</strong> low (yellow) vs. high (orange), determined by the amount of impact a feature or a bug fix will generate. For example, a slightly misaligned UI panel is of low impact where a deterministically crashing bug is of high impact.</p>
</li>
<li><p><strong>An estimate of the time it will take to implement:</strong> trivial (&lt; 1 hour, teal), medium (1–2 hours, light blue), and difficult (2–4 hours, dark blue).<br>  My other rule of thumb is, if I estimate that a card will take more than 4 hours to implement, I should probably break it down into several finer-grained cards.</p>
</li>
<li><p>Color serves as a great visual cue: I always tackle cards with orange and teal tags (high impact and low time commitment) before tackling those with yellow and difficult tags (low impact but high time commitment).</p>
</li>
</ul>
<h2 id="heading-write-documentation-and-reflect-on-your-learning">Write Documentation and Reflect on Your Learning</h2>
<p>Having pushed every single Trello card from TODO to Doing to Done and fixed every nasty bug, is it finally time to call a project done? Yes and no.</p>
<p>To maximize my learning from a project, I find it immensely worthwhile to reflect on my takeaways, technical or soft skills:</p>
<ol>
<li><p>Write a clear, concise README in the GitHub project repository. This helps other developers understand and become interested in the project.</p>
</li>
<li><p>Write a blog post (like the one I’m writing now) about the higher-level aspects, for example, architecture overview, feature design, challenges and solutions, and so on.</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/README-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My README Section about Integrating My Chess Engine into Other Game Projects</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/-readme1-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>My README Section Comparing the Two Versions of My Chess Engine</em></p>
<h3 id="heading-credits-amp-links">Credits &amp; Links</h3>
<p>Many thanks to Tim Min for prompting me to work on this project, for his contributions (new feature ideas + QA) on the Trello board, and for holding me accountable. Tim is the writer of the kinetic novel game, <a target="_blank" href="https://madeleine-chai.itch.io/the-wind-at-dawn"><em>The Wind at Dawn</em></a><em>.</em></p>
<ul>
<li><p><a target="_blank" href="https://github.com/RuolinZheng08/renpy-chess">My chess engine GitHub repository</a></p>
</li>
<li><p><a target="_blank" href="https://trello.com/b/ip9YLSPa/renpy-chess">The public Trello board for this chess engine project</a></p>
</li>
<li><p><a target="_blank" href="https://www.renpy.org/">Ren’Py: a Visual Novel Game Development Engine</a></p>
</li>
<li><p><a target="_blank" href="https://python-chess.readthedocs.io/en/latest/">python-chess: a pure Python chess library</a></p>
</li>
</ul>
<p>Let's stay in touch! Connect with me on <a target="_blank" href="https://www.linkedin.com/in/ruolin-zheng/">LinkedIn</a>, <a target="_blank" href="https://github.com/RuolinZheng08">GitHub</a>, <a target="_blank" href="https://medium.com/@ruolinzheng">Medium</a>, or check out <a target="_blank" href="https://ruolinzheng08.github.io/">my personal website</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
