diff --git a/_site/feed.xml b/_site/feed.xml index 9e6dbf5..75cfda9 100644 --- a/_site/feed.xml +++ b/_site/feed.xml @@ -1 +1 @@ -Jekyll2020-10-26T16:17:29+00:00/feed.xmlCuriosity2020-10-26T00:00:00+00:002020-10-26T00:00:00+00:00/2020/10/26/curiosity<p>Curiosity is fundamental to a deep understanding of any subject. Masters, Ph.Ds, and other fancy name suffixes will never help you if you don’t have the spirit of curiosity burning inside of you.</p> <p>I was speaking to someone from a journalism major at my school when the subject of hacking arose. I expected her to know nothing about it, being a journalism student and all, but surprisingly she had something to say about it:</p> <blockquote> <p>“The best hackers are the ones who are curious.”</p> </blockquote> <p>That struck a cord with me. It seems to me she has nailed down the difference between the students who care about grades, and those who want to learn. These are not necessarily mutually exclusive, but in my experience they often are due to the way education is structured.</p> <h2 id="my-anecdote">My Anecdote</h2> <p>In my second semester at SAIT Polytechnic, I took a class entitled <em>Emerging Trends In Technology</em>. This class was probably the best class I have ever taken. We had to combine two things:</p> <ul> <li><strong>Hard skills</strong>: learning a new hard skill like Angular, Django, or GPG encryption.</li> <li><strong>Soft skills</strong>: public speaking and presentation of our ideas.</li> </ul> <p>Soft skills are not usually my area, but I can do public speaking. I grew up quite religious, so public speaking was drilled into me young. I liked to go off script and talk about interesting things I found along the way to the actual point. My creativity was not usually encouraged. That said, going off script is useful when teaching and presenting ideas; it gives a natural air to your breath and an unquestionable confidence in your speech.</p> <p>This is how we learn: in relationships. Try explaining ancient Japanese history to a computer science major, or UNIX sockets to an English major and you’ll see what I mean. If there is nothing for us to connect the knowledge to, it dissipates.</p> <p>So why did I do so well in this class?</p> <p>Our task for the semester was as follows:</p> <ol> <li>Learn a new subject (any <em>emerging trend in technology</em>) which you find fascinating.</li> <li>Give a one minute introduction by week three.</li> <li>Give a 10 minute non-technical overview by week 8.</li> <li>Give a 20 minute technical explaination and demo by week 13.</li> </ol> <p>This is the only course I have ever taken which lets students’ imagination run wild. Their presentation, their rules. They treated the students like adults who know what they are doing. What happened? Everyone stopped coming because “Oh no! Presentations!”?</p> <p>No, exactly the opposite. There was never more than one student missing. Every single presentation was at least moderately interesting, and most students were excited to come to that class. You could see it in their faces, the way they carried themselves. Every student picked something unique to their tastes, leaving every student more educated than before.</p> <p>This class, unlike many others, encouraged the curiosity of the students. It rewarded those who had unique interests and an ability to sell others on their ideas.</p> <p>The curiosity and the grades were one.</p> <h2 id="conclusion">Conclusion</h2> <p>Although it’s nice to have a course where these goals align here and there, anyone who has been to collage or university can tell you that is far from the norm.</p> <p>On the other hand, I never would have started this site if it wasn’t for that class alone. So I thank you, Kitty Wong, for getting me started running my own “research blog” (?)</p>Curiosity is fundamental to a deep understanding of any subject. Masters, Ph.Ds, and other fancy name suffixes will never help you if you don’t have the spirit of curiosity burning inside of you.Minesweeper Bomb Generation And Tile Revealing2020-09-12T00:00:00+00:002020-09-12T00:00:00+00:00/2020/09/12/minesweeper<p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.</p> <h2 id="bomb-generation">Bomb Generation</h2> <p>When I started this project I attempted to use a random bomb generator. By this I mean on each square, before it gets generated, give it a one in 15 change of being a bomb. Personally, I’m not sure why this never looked right. Something about the layout of the bombs did not mimic the classic Minesweeper game.</p> <p>After looking at some open source Minesweeper examples, I started to get the idea. I wrote some mathematical statements describing the generation of bombs and how to get their x,y position from an appropriate number. For those non-mathy people, don’t leave just yet; there will be code equivalents to the math.</p> <p>W and H are the width and height of the board respectively.</p> <p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn mathvariant="italic">0</mn><mo>≤</mo><mi>r</mi><mo>≤</mo><mtext>W</mtext><mo>×</mo><mtext>H</mtext></mrow><annotation encoding="application/x-tex"> \it 0 \leq r \leq \text W \times \text H </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.13597em;"></span><span class="mord"><span class="mord mathit">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord text"><span class="mord">W</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">H</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mi>r</mi><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mtext>W</mtext></mrow><annotation encoding="application/x-tex"> \it x = r \bmod \text W </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord"><span class="mord mathit">x</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mbin"><span class="mord"><span class="mord mathrm">m</span><span class="mord mathrm">o</span><span class="mord mathrm">d</span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mord text"><span class="mord">W</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mrow><mo fence="true">⌊</mo><mfrac><mi>r</mi><mtext>H</mtext></mfrac><mo fence="true">⌋</mo></mrow></mrow><annotation encoding="application/x-tex"> \it y = \left\lfloor\frac{r}{\text H}\right\rfloor </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8359999999999999em;vertical-align:-0.686em;"></span><span class="mord"><span class="mord mathit">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">⌊</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.10756em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">H</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">⌋</span></span></span></span></span></span></span></span></p> <p>The code equivalent to this in Python is below:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span> <span class="c1"># r &lt;= 0 &lt;= W*H </span><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">W</span><span class="o">*</span><span class="n">H</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="c1"># x = r mod W </span><span class="n">x</span> <span class="o">=</span> <span class="n">r</span> <span class="o">%</span> <span class="n">W</span> <span class="c1"># y = floor(r/H); note the special syntax python has for this operation </span><span class="n">y</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="n">H</span> </code></pre></div></div> <p>So that’s that, we can put this in a big ‘ol for loop and generate an arbitrary <em>n</em> number of bombs given a width and height of a Minesweeper board.</p> <h2 id="cascading-tile-revealing">Cascading Tile Revealing</h2> <p>This one is hard to describe; I am adapting this from <a href="https://leetcode.com/problems/minesweeper/">leetcode.com</a>. Whenever a player clicks a tile, the following logic should be used:</p> <ol> <li>If a mine is revealed, the game is over. (obviously)</li> <li>If a tile with <em>no</em> adjacent mines is revealed, recursively reveal all eight adjacent tiles.</li> <li>If a tile with one or more adjacent mines is revealed, display the number of mines next to it.</li> </ol> <p>Here is the code in Python for this algorithm.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reveal_square</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">alread_revealed</span><span class="p">):</span> <span class="c1"># if already checked </span> <span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">already_revealed</span><span class="p">:</span> <span class="k">return</span> <span class="c1"># if it's a bomb </span> <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">y</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span> <span class="n">you_lose</span><span class="p">()</span> <span class="k">return</span> <span class="c1"># if the bomb number is more than 0 </span> <span class="n">already_revealed</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">))</span> <span class="c1"># from -1 to 1 </span> <span class="k">for</span> <span class="n">xd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="k">for</span> <span class="n">yd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="c1"># skip if it is this the center tile </span> <span class="k">if</span> <span class="n">x</span><span class="o">+</span><span class="n">xd</span> <span class="o">==</span> <span class="n">x</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span> <span class="k">continue</span> <span class="c1"># recursively check the adjacent square </span> <span class="n">reveal</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">xd</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">already_revealed</span><span class="p">)</span> <span class="k">return</span> <span class="n">already_revealed</span> </code></pre></div></div> <p>This has no checks for valid squares, but it’s the general idea. This function returns an array of tile coordinates which should be revealed.</p> <h2 id="conclusion">Conclusion</h2> <p>I wrote this because in the first place because I was writing my own Minesweeper game. I hope that this helps you with getting the general idea of a Minesweeper game. The completed version of this game is available on my <a href="https://lamegames.tait.tech/">lamegames</a> site. Let me know what you think!</p> <p>Happy hacking!</p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.lamegames.tait.tech2020-09-09T00:00:00+00:002020-09-09T00:00:00+00:00/2020/09/09/lamegames<p>This is an announcement for a new project of mine: <a href="https://lamegames.tait.tech">lamegames.tait.tech</a>.</p> <p>This is something I’m really excited to work on!</p> <p>Right now, I’ve just got a rock-paper-scissors game. A chat function, and a few simple card games to come.</p> <p>Check out the repository on my <a href="https://github.com/TTWNO/lamegames.io">Github</a>.</p>This is an announcement for a new project of mine: lamegames.tait.tech.How to Solve The Django Deployment Puzzle2020-08-18T00:00:00+00:002020-08-18T00:00:00+00:00/2020/08/18/django-deployment<p>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.</p> <h3 id="asgi-servers">ASGI Servers</h3> <p>Because my project used the ASGI (Asynchronous webServer Gateway Interface), I needed to find a good production ASGI server to handle all the incoming requests. The best thing I found was <a href="http://www.uvicorn.org/">uvicorn</a>. It focuses on speed, which is a priority, especially when using the ASGI protocol.</p> <p>To run uvicorn on the command line for testing purposes, use something like the following:</p> <pre class="terminal"> $ uvicorn --reload myapp.asgi:application </pre> <p>The <code class="language-plaintext highlighter-rouge">--reload</code> option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.</p> <h3 id="workload-managers">Workload Managers</h3> <p>There is another equine-named program called <a href="https://gunicorn.org/">gunicorn</a> which can hold a number of processes under its control. An interesting feature of <code class="language-plaintext highlighter-rouge">gunicorn</code> is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.</p> <p>“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, <code class="language-plaintext highlighter-rouge">gunicorn</code> is paired with <code class="language-plaintext highlighter-rouge">uvicorn</code> to serve my files.</p> <h3 id="systemd">systemd</h3> <p>Love it or hate it, the majority of Linux distributions use the <code class="language-plaintext highlighter-rouge">systemd</code> init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. <code class="language-plaintext highlighter-rouge">Systemd</code> allows me to do this with a file like the following one I stored in <code class="language-plaintext highlighter-rouge">/lib/systemd/system/lamegames.service</code>.</p> <pre class="file"> [Unit] Description=Gunicorn/Uvicorn (lamegames.io) [Service] WorkingDirectory=/home/lame/lamegames.io Type=simple RemainAfterExit=yes ExecStart=/home/lame/lamegames.io/env/bin/gunicorn lamegames.asgi:application -w 2 -k uvicorn.workers.UvicornWorker ExecStop=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target </pre> <h3 id="nginx">nginx</h3> <p>NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:</p> <ol> <li>Redirect most traffic towards the gunicorn server.</li> <li>Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my <code class="language-plaintext highlighter-rouge">settings.py</code> file.</li> <li>Use TLS to enable https://</li> </ol> <p>Serving the static files from nginx as opposed to the <code class="language-plaintext highlighter-rouge">gunicorn</code> server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.</p> <p>This is the important part of my nginx config.</p> <pre class="file"> server { location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # these two lines ensure that WebSocket, and HTTP2 connection are forwarded correctly proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; # this forwards all traffic to the local server on port 8000 proxy_pass http://localhost:8000; } # This forwards all static requests to Django's STATIC_ROOT set in settings.py; it is generated using the collectstatic command. location /static { autoindex on; alias /home/lame/lamegames.io/static_generated; } } </pre> <h3 id="setup">Setup</h3> <p>After all that, I was able to do the following:</p> <pre class="terminal"> # systemctl enable lamegames </pre> <p>This enabled my <code class="language-plaintext highlighter-rouge">gunicorn</code> server to run once the server started. NGINX is that way be default.</p> <p>And tada! You now have a working Django project on a production server!</p> <h4 id="notes">Notes</h4> <ul> <li>If using ws:// websockets, change them to wss:// for secure web sockets.</li> <li>Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.</li> </ul>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.BSD Journey, Part 12020-08-15T00:00:00+00:002020-08-15T00:00:00+00:00/2020/08/15/openbsd1<p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.</p> <p>Now, my interest in OpenBSD has been going on for a long time. I started poking around for Linux alternatives way back a few years ago when Linus Torvalds decided to leave after he got in trouble for some <a href="https://arstechnica.com/information-technology/2013/07/linus-torvalds-defends-his-right-to-shame-linux-kernel-developers/">unprofessional behaviour</a>. That said, Linus did come back to Linux development, but I knew that his abrasive style is what brought good code to the Linux kernel. I also knew that his ability to be critical would be hurt by the new <a href="https://itsfoss.com/linux-code-of-conduct/">code of conduct</a>. It would become a tool for the SJW types to hammer on Linus for being a “white male, et al.”; It would become a tool for the easily offended to use to get their dumb code into Linux; It would become a tool for the corporatization, the HR-ification of Linux. Frankly, this does not interest me.</p> <p>Now I’m sure that OpenBSD has its own internal policies that I disagree with. That said, Theo De Raadt is still at least known for calling Firefox an “amorphous peace of garbage” due to its lack of privilege separation. And, in their <a href="https://openbsd.org/goals.html">project goals</a> page, they specifically mention:</p> <blockquote> <p>Be as politics-free as possible; solutions should be decided on the basis of technical merit.</p> </blockquote> <p>Now that’s something I can get behind! Bet you that’s not in the Linux COC?</p> <p>He also went to university in my hometown, so that’s pretty cool! I can support a local madman who thinks he can make a better operating system than all those corporations. Maybe he was right, maybe not. What I know is I am excited to find out!</p> <p>Wish my luck on my OpenBSD journey. I will post updates here along the way.</p> <p>Happy hacking!</p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.Know How Your Representative Votes In Parliament2020-07-30T00:00:00+00:002020-07-30T00:00:00+00:00/2020/07/30/canadian-parliament<p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s <a href="https://open.canada.ca/en/open-data">Open Data</a> initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.</p> <h3 id="1-find-your-representative">1. Find Your Representative</h3> <p>The first step in this process is to find who your representative is. To do so, go to the government’s own website <a href="https://www.ourcommons.ca/Members/en">ourcommons.ca’s search tool</a>.</p> <p>Simply type in your postal code in the search box to find out who your MP is.</p> <h3 id="2-their-voting-record">2. Their Voting Record</h3> <p>Every MP’s voting record is public knowledge, and it is available nice and simple in a table on that MP’s page. For example, this is a link to <a href="https://www.ourcommons.ca/Members/en/pierre-poilievre(25524)/votes">Pierre Poilievre’s voting record</a>.</p> <p>To find your MP’s voting record, do step one, then: After the <strong>Overview</strong>, and <strong>Seat in The House</strong> sections, there are three tabs, <strong>Roles</strong>, <strong>Work</strong>, and <strong>Contact</strong>. Click on work. At the bottom of that tab is a link which says <strong>Chamber Votes</strong>. This will open a small window with some recent votes by this politician. If you want to see all their votes, there is a button at the bottom named <strong>All Votes by This Member</strong>.</p> <p>Tada! You can now keep your local MP accountable for anything you do or do not support.</p> <h3 id="3-bill-details">3. Bill Details</h3> <p>If you want to get into the nitty gritty, once you open a specific bill, you can actually find out the status of said bill, or read the actual text by clicking the <strong>View this Bill on LEGISinfo</strong> button.</p> <p>Both the status of the bill, and a link to a PDF document containing the bilingual text of the bill are visible in the main body of the page.</p> <h4 id="conclusion">Conclusion</h4> <p>I thought this was pretty cool! It was <em>way</em> simpler than I thought it would be.</p> <p>Thanks, Canada!</p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.Installing MultiCraft on Gentoo Linux2020-07-19T00:00:00+00:002020-07-19T00:00:00+00:00/2020/07/19/multicraft-php-gentoo<p>In a very odd combination of requirements, I needed to install <a href="https://multicraft.org">MultiCraft</a> on a Gentoo Linux system. The PHP <code class="language-plaintext highlighter-rouge">USE</code> flags are important so you don’t have to recompile it three times like I did.</p> <p>Here are some useful tips I came across:</p> <h3 id="php-use-flags">PHP <code class="language-plaintext highlighter-rouge">USE</code> flags</h3> <p>In <code class="language-plaintext highlighter-rouge">/etc/portage/package.use/php</code> I placed the following line:</p> <pre class="terminal"> dev-lang/php cgi mysql mysqli fpm pdo gd truetype </pre> <p>This should give you enough for a mysql backended MultiCraft installation. The <code class="language-plaintext highlighter-rouge">cgi</code> option may not be required as <code class="language-plaintext highlighter-rouge">fpm</code> stands for <em>FastCGI Process Managment</em>. I don’t know for sure though.</p> <h3 id="paper">Paper</h3> <p>This will grab the latest version of the Paper jar file using <a href="https://yivesmirror.com">YivesMirror</a>. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.</p> <pre class="terminal"> ## See the default craftbukkit.jar.conf for a detailed documentation of the ## format of this file. [config] name = Paper 1.16.1 Latest source = https://yivesmirror.com/files/paper/Paper-1.16.1-latest.jar category = Mods [encoding] #encode = system #decode = system #fileEncoding = latin-1 [start] command = "{JAVA}" -Xmx{MAX_MEMORY}M -Xms{START_MEMORY}M -XX:MaxPermSize=128M -Djline.terminal=jline.UnsupportedTerminal -jar "{JAR}" nogui </pre> <h3 id="other-tips">Other Tips</h3> <p>Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.</p> <h4 id="security">Security</h4> <p>If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the <code class="language-plaintext highlighter-rouge">/protected</code> directory.</p> <pre class="terminal"> location /protected { deny all; return 404; } </pre> <h5 id="mysql">MySQL</h5> <p>It is always good practice to separate privileges. The MultiCraft daemon should have one SQL login, with one database allocated to it. The MultiCraft panel should have a separate SQL login, with a separate database allocated to it.</p> <p>You can do this with the following commands in your MySQL prompt:</p> <pre class="terminal"> sql&gt; CREATE DATABASE multicraft_daemon_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE DATABASE multicraft_panel_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'muilticraft_daemon'@'localhost' IDENTIFIED BY 'strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'multicraft_panel'@'localhost' IDENTIFIED BY 'different strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_daemon_database . * TO 'multicraft_daemon'@'localhost'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_panel_database . * TO 'mutlicraft_panel'@'localhost'; Query OK, 0 rows affected (0.01 sec) </pre> <p>During setup, make sure the proper credentials are used for each step. Database 1 is the panel database. Database 2 is the daemon database.</p> <p>Happy hacking :)</p>In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.Independence2020-07-12T00:00:00+00:002020-07-12T00:00:00+00:00/2020/07/12/independence<blockquote> <p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith</p> </blockquote> <p>Whatever you may believe about the YouTube personality Luke Smith, the quote above summarizes a core principle of mine. Much like many people have religious principles, I have <em>Independence</em>.</p> <p>My choice to use Linux as my primary operating system, host my own website, own my own domain name—all of these are directly related to this core principle of independence.</p> <p>I never want a man, or a company to have too much power over my life. Just like I would not trust just any person to be able to read my emails, know where I live, where I am going, who are my friends, what do I believe; in the same way, I do not trust a company with that same information.</p> <blockquote> <p>“If you want to find out what a man is to the bottom, give him power. Any man can stand adversity — only a great man can stand prosperity.”—Robert Ingersoll</p> </blockquote> <p>Take control of your own digital life:</p> <ol> <li>Own your own domain.</li> <li>Hookup an email and a website to that.</li> </ol> <p>That’s it!</p> <p>Without this, any of your internet privileges can be revoked at any time by Google, Facebook, YouTube, Twitter, or even an angry Twitter Mob. Maybe because they hate your skin colour, maybe they hate your religious/political views, or maybe you got caught on a technicality.</p> <p>If you own your own domain, however:</p> <p>Your email provider goes down/bans you: change your provider; keep the email.</p> <p>Your website is pulled for controversial views: switch hosts.</p> <p>Protect yourself; give yourself choices. Why give others that power when you could have it for yourself?</p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke SmithHow to use tmux to send and receive things from your Minecraft server2020-06-25T00:00:00+00:002020-06-25T00:00:00+00:00/2020/06/25/tmux-minecraft<p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.</p> <p>So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.</p> <p>My completed solution for what I needed can be found at <a href="https://github.com/TTWNO/termcraft/">https://github.com/TTWNO/termcraft</a>.</p> <p>If you want some of the implementation details, stick around.</p> <h2 id="solution">Solution</h2> <p>So to solve this interesting problem, I decided to use <code class="language-plaintext highlighter-rouge">tmux</code>. <code class="language-plaintext highlighter-rouge">tmux</code> is a <strong>t</strong>terminal <strong>mu</strong>ltiple<strong>x</strong>er. This allows you to run a terminal session, then detach fromc it while it still runs in the background.</p> <p>This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.</p> <p>So first I looked at the <code class="language-plaintext highlighter-rouge">tmux</code> command <code class="language-plaintext highlighter-rouge">send-keys</code>.</p> <h4 id="send-keys"><code class="language-plaintext highlighter-rouge">send-keys</code></h4> <p><code class="language-plaintext highlighter-rouge">send-keys</code> allows you to send text, and key presses to a <code class="language-plaintext highlighter-rouge">tmux</code> session. Now assuming this <code class="language-plaintext highlighter-rouge">tmux</code> session is attached to a Minecraft server, there is no reason you could not run a command like this:</p> <pre class="terminal"> $ tmux send-keys "tell @a This is a Test" Enter </pre> <p>This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.</p> <p>So now we can send information to the server and have it tell the users something.</p> <p>But how do we get information about who is typing what in the Minecraft chat?</p> <h3 id="tmuxs-capture-pane-is-painful"><code class="language-plaintext highlighter-rouge">tmux</code>’s <code class="language-plaintext highlighter-rouge">capture-pane</code> is painful</h3> <p>So in the manual page for <code class="language-plaintext highlighter-rouge">tmux</code> I can see a section recorded below for options I can give to the <code class="language-plaintext highlighter-rouge">capture-pane</code> subcommand.</p> <pre class="terminal"> -S and -E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. ‘-’ to -S is the start of the history and to -E the end of the visible pane. The default is to capture only the visible contents of the pane. </pre> <p>What it seems to be saying is I can start at line <code class="language-plaintext highlighter-rouge">-S n</code> and end at line <code class="language-plaintext highlighter-rouge">-E n</code>. Negative numbers start from the bottom, so <em>in theory</em> I can do the following: <code class="language-plaintext highlighter-rouge">tmux capture-pane -S -1</code> should capture only the last line, because I’m starting from the last line. Right?</p> <p>No. It just doesn’t work. Negative numbers do <em>not</em> work with the <code class="language-plaintext highlighter-rouge">tmux capture-pane</code> subcommand.</p> <p>So I did some simple UNIX piping, like so, to get just the last thing in the chat.</p> <pre class="terminal"> $ tmux capture-pane -p -t steve | tail -n1 [SERVER] [ExtraDebuggingInfoHere]: &lt;TaterTheTot&gt; MY_MESSAGE </pre> <p>TaterTheTot is my Minecraft username :)</p> <p><code class="language-plaintext highlighter-rouge">-p</code> prints the result to the terminal/stdout.</p> <p><code class="language-plaintext highlighter-rouge">steve</code> is the name of the tmux session I’m trying to pull form.</p> <p>So that’s done! Beauty!</p> <p>Now that we have that, how can we extract the username and the message from the latest line?</p> <h3 id="grep"><code class="language-plaintext highlighter-rouge">grep</code></h3> <p><code class="language-plaintext highlighter-rouge">grep</code> is a command to find patterns of text. <code class="language-plaintext highlighter-rouge">grep</code> has an option to only show a matching pattern of text. This option is <code class="language-plaintext highlighter-rouge">-o</code>.</p> <p>Let’s see how we can use this in conjunction with our latest line of server output to get our results.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" &lt;TaterTheTot&gt; </pre> <p>Now, that’s my name with the &lt; and &gt; attached. Not bad! We can use the <code class="language-plaintext highlighter-rouge">sed</code> command to clean it up a bit.</p> <p>The syntax is like so: <code class="language-plaintext highlighter-rouge">select/somepattern/replacewith/global</code></p> <p>So the following command is: <code class="language-plaintext highlighter-rouge">s/[&lt;&gt;]//g</code></p> <p>Select any characters that are either &lt; or &gt;. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).</p> <p>Take two!</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" | sed 's/[&lt;&gt;]//g' TaterTheTot </pre> <p>Beautiful!</p> <p>Now what about that pesky message?</p> <h3 id="more-grep-more-sed">more <code class="language-plaintext highlighter-rouge">grep</code>; more <code class="language-plaintext highlighter-rouge">sed</code></h3> <p>Simple: capture everything after the &gt;. Leaving the user’s message entirely in tact.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&gt;.&ast;$" | sed 's/&gt; //' MY_MESSAGE </pre> <p>So now we have a way to get the username of someone typing in the Minecraft server chat. We have a way to find out what they said. And, we have a way to respond.</p> <p>You can imagine how these might go together for your own use case.</p> <h3 id="conclusion">Conclusion</h3> <p>This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.</p> <p>I hope you learned something and found my explanations not horrific haha!</p> <p>Remember to checkout the git repository to see what I did with it: <a href="https://github.com/TTWNO/termcraft">https://github.com/TTWNO/termcraft</a>.</p> <p>Happy hacking!</p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.Site Update2020-06-04T00:00:00+00:002020-06-04T00:00:00+00:00/2020/06/04/site-update<p>I updated the site with some easier to identify information about me and my projects :)</p> <p>Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.</p> <p>I also posted a new project called <em><a href="https://github.com/TTWNO/caesar-cipher">Caesar Cipher</a></em> in C. It will be an intermediate example of how to use build systems like <code class="language-plaintext highlighter-rouge">make</code>.</p>I updated the site with some easier to identify information about me and my projects :) \ No newline at end of file +Jekyll2020-10-30T18:06:41+00:00/feed.xmlCuriosity2020-10-26T00:00:00+00:002020-10-26T00:00:00+00:00/2020/10/26/curiosity<p>Curiosity is fundamental to a deep understanding of any subject. Masters, Ph.Ds, and other fancy name suffixes will never help you if you don’t have the spirit of curiosity burning inside of you.</p> <p>I was speaking to someone from a journalism major at my school when the subject of hacking arose. I expected her to know nothing about it, being a journalism student and all, but surprisingly she had something to say about it:</p> <blockquote> <p>“The best hackers are the ones who are curious.”</p> </blockquote> <p>That struck a cord with me. It seems to me she has nailed down the difference between the students who care about grades, and those who want to learn. These are not necessarily mutually exclusive, but in my experience they often are due to the way education is structured.</p> <h2 id="my-anecdote">My Anecdote</h2> <p>In my second semester at SAIT Polytechnic, I took a class entitled <em>Emerging Trends In Technology</em>. This class was probably the best class I have ever taken. We had to combine two things:</p> <ul> <li><strong>Hard skills</strong>: learning a new hard skill like Angular, Django, or GPG encryption.</li> <li><strong>Soft skills</strong>: public speaking and presentation of our ideas.</li> </ul> <p>Soft skills are not usually my area, but I can do public speaking. I grew up quite religious, so public speaking was drilled into me young. I liked to go off script and talk about interesting things I found along the way to the actual point. My creativity was not usually encouraged. That said, going off script is useful when teaching and presenting ideas; it gives a natural air to your breath and an unquestionable confidence in your speech.</p> <p>This is how we learn: in relationships. Try explaining ancient Japanese history to a computer science major, or UNIX sockets to an English major and you’ll see what I mean. If there is nothing for us to connect the knowledge to, it dissipates.</p> <p>So why did I do so well in this class?</p> <p>Our task for the semester was as follows:</p> <ol> <li>Learn a new subject (any <em>emerging trend in technology</em>) which you find fascinating.</li> <li>Give a one minute introduction by week three.</li> <li>Give a 10 minute non-technical overview by week 8.</li> <li>Give a 20 minute technical explaination and demo by week 13.</li> </ol> <p>This is the only course I have ever taken which lets students’ imagination run wild. Their presentation, their rules. They treated the students like adults who know what they are doing. What happened? Everyone stopped coming because “Oh no! Presentations!”?</p> <p>No, exactly the opposite. There was never more than one student missing. Every single presentation was at least moderately interesting, and most students were excited to come to that class. You could see it in their faces, the way they carried themselves. Every student picked something unique to their tastes, leaving every student more educated than before.</p> <p>This class, unlike many others, encouraged the curiosity of the students. It rewarded those who had unique interests and an ability to sell others on their ideas.</p> <p>The curiosity and the grades were one.</p> <h2 id="conclusion">Conclusion</h2> <p>Although it’s nice to have a course where these goals align here and there, anyone who has been to collage or university can tell you that is far from the norm.</p> <p>On the other hand, I never would have started this site if it wasn’t for that class alone. So I thank you, Kitty Wong, for getting me started running my own “research blog” (?)</p>Curiosity is fundamental to a deep understanding of any subject. Masters, Ph.Ds, and other fancy name suffixes will never help you if you don’t have the spirit of curiosity burning inside of you.Minesweeper Bomb Generation And Tile Revealing2020-09-12T00:00:00+00:002020-09-12T00:00:00+00:00/2020/09/12/minesweeper<p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.</p> <h2 id="bomb-generation">Bomb Generation</h2> <p>When I started this project I attempted to use a random bomb generator. By this I mean on each square, before it gets generated, give it a one in 15 change of being a bomb. Personally, I’m not sure why this never looked right. Something about the layout of the bombs did not mimic the classic Minesweeper game.</p> <p>After looking at some open source Minesweeper examples, I started to get the idea. I wrote some mathematical statements describing the generation of bombs and how to get their x,y position from an appropriate number. For those non-mathy people, don’t leave just yet; there will be code equivalents to the math.</p> <p>W and H are the width and height of the board respectively.</p> <p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn mathvariant="italic">0</mn><mo>≤</mo><mi>r</mi><mo>≤</mo><mtext>W</mtext><mo>×</mo><mtext>H</mtext></mrow><annotation encoding="application/x-tex"> \it 0 \leq r \leq \text W \times \text H </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.13597em;"></span><span class="mord"><span class="mord mathit">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord text"><span class="mord">W</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">H</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mi>r</mi><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mtext>W</mtext></mrow><annotation encoding="application/x-tex"> \it x = r \bmod \text W </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord"><span class="mord mathit">x</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mbin"><span class="mord"><span class="mord mathrm">m</span><span class="mord mathrm">o</span><span class="mord mathrm">d</span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mord text"><span class="mord">W</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mrow><mo fence="true">⌊</mo><mfrac><mi>r</mi><mtext>H</mtext></mfrac><mo fence="true">⌋</mo></mrow></mrow><annotation encoding="application/x-tex"> \it y = \left\lfloor\frac{r}{\text H}\right\rfloor </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8359999999999999em;vertical-align:-0.686em;"></span><span class="mord"><span class="mord mathit">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">⌊</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.10756em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">H</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">⌋</span></span></span></span></span></span></span></span></p> <p>The code equivalent to this in Python is below:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span> <span class="c1"># r &lt;= 0 &lt;= W*H </span><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">W</span><span class="o">*</span><span class="n">H</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="c1"># x = r mod W </span><span class="n">x</span> <span class="o">=</span> <span class="n">r</span> <span class="o">%</span> <span class="n">W</span> <span class="c1"># y = floor(r/H); note the special syntax python has for this operation </span><span class="n">y</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="n">H</span> </code></pre></div></div> <p>So that’s that, we can put this in a big ‘ol for loop and generate an arbitrary <em>n</em> number of bombs given a width and height of a Minesweeper board.</p> <h2 id="cascading-tile-revealing">Cascading Tile Revealing</h2> <p>This one is hard to describe; I am adapting this from <a href="https://leetcode.com/problems/minesweeper/">leetcode.com</a>. Whenever a player clicks a tile, the following logic should be used:</p> <ol> <li>If a mine is revealed, the game is over. (obviously)</li> <li>If a tile with <em>no</em> adjacent mines is revealed, recursively reveal all eight adjacent tiles.</li> <li>If a tile with one or more adjacent mines is revealed, display the number of mines next to it.</li> </ol> <p>Here is the code in Python for this algorithm.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reveal_square</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">alread_revealed</span><span class="p">):</span> <span class="c1"># if already checked </span> <span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">already_revealed</span><span class="p">:</span> <span class="k">return</span> <span class="c1"># if it's a bomb </span> <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">y</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span> <span class="n">you_lose</span><span class="p">()</span> <span class="k">return</span> <span class="c1"># if the bomb number is more than 0 </span> <span class="n">already_revealed</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">))</span> <span class="c1"># from -1 to 1 </span> <span class="k">for</span> <span class="n">xd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="k">for</span> <span class="n">yd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="c1"># skip if it is this the center tile </span> <span class="k">if</span> <span class="n">x</span><span class="o">+</span><span class="n">xd</span> <span class="o">==</span> <span class="n">x</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span> <span class="k">continue</span> <span class="c1"># recursively check the adjacent square </span> <span class="n">reveal</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">xd</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">already_revealed</span><span class="p">)</span> <span class="k">return</span> <span class="n">already_revealed</span> </code></pre></div></div> <p>This has no checks for valid squares, but it’s the general idea. This function returns an array of tile coordinates which should be revealed.</p> <h2 id="conclusion">Conclusion</h2> <p>I wrote this because in the first place because I was writing my own Minesweeper game. I hope that this helps you with getting the general idea of a Minesweeper game. The completed version of this game is available on my <a href="https://lamegames.tait.tech/">lamegames</a> site. Let me know what you think!</p> <p>Happy hacking!</p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.lamegames.tait.tech2020-09-09T00:00:00+00:002020-09-09T00:00:00+00:00/2020/09/09/lamegames<p>This is an announcement for a new project of mine: <a href="https://lamegames.tait.tech">lamegames.tait.tech</a>.</p> <p>This is something I’m really excited to work on!</p> <p>Right now, I’ve just got a rock-paper-scissors game. A chat function, and a few simple card games to come.</p> <p>Check out the repository on my <a href="https://github.com/TTWNO/lamegames.io">Github</a>.</p>This is an announcement for a new project of mine: lamegames.tait.tech.How to Solve The Django Deployment Puzzle2020-08-18T00:00:00+00:002020-08-18T00:00:00+00:00/2020/08/18/django-deployment<p>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.</p> <h3 id="asgi-servers">ASGI Servers</h3> <p>Because my project used the ASGI (Asynchronous webServer Gateway Interface), I needed to find a good production ASGI server to handle all the incoming requests. The best thing I found was <a href="http://www.uvicorn.org/">uvicorn</a>. It focuses on speed, which is a priority, especially when using the ASGI protocol.</p> <p>To run uvicorn on the command line for testing purposes, use something like the following:</p> <pre class="terminal"> $ uvicorn --reload myapp.asgi:application </pre> <p>The <code class="language-plaintext highlighter-rouge">--reload</code> option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.</p> <h3 id="workload-managers">Workload Managers</h3> <p>There is another equine-named program called <a href="https://gunicorn.org/">gunicorn</a> which can hold a number of processes under its control. An interesting feature of <code class="language-plaintext highlighter-rouge">gunicorn</code> is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.</p> <p>“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, <code class="language-plaintext highlighter-rouge">gunicorn</code> is paired with <code class="language-plaintext highlighter-rouge">uvicorn</code> to serve my files.</p> <h3 id="systemd">systemd</h3> <p>Love it or hate it, the majority of Linux distributions use the <code class="language-plaintext highlighter-rouge">systemd</code> init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. <code class="language-plaintext highlighter-rouge">Systemd</code> allows me to do this with a file like the following one I stored in <code class="language-plaintext highlighter-rouge">/lib/systemd/system/lamegames.service</code>.</p> <pre class="file"> [Unit] Description=Gunicorn/Uvicorn (lamegames.io) [Service] WorkingDirectory=/home/lame/lamegames.io Type=simple RemainAfterExit=yes ExecStart=/home/lame/lamegames.io/env/bin/gunicorn lamegames.asgi:application -w 2 -k uvicorn.workers.UvicornWorker ExecStop=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target </pre> <h3 id="nginx">nginx</h3> <p>NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:</p> <ol> <li>Redirect most traffic towards the gunicorn server.</li> <li>Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my <code class="language-plaintext highlighter-rouge">settings.py</code> file.</li> <li>Use TLS to enable https://</li> </ol> <p>Serving the static files from nginx as opposed to the <code class="language-plaintext highlighter-rouge">gunicorn</code> server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.</p> <p>This is the important part of my nginx config.</p> <pre class="file"> server { location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # these two lines ensure that WebSocket, and HTTP2 connection are forwarded correctly proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; # this forwards all traffic to the local server on port 8000 proxy_pass http://localhost:8000; } # This forwards all static requests to Django's STATIC_ROOT set in settings.py; it is generated using the collectstatic command. location /static { autoindex on; alias /home/lame/lamegames.io/static_generated; } } </pre> <h3 id="setup">Setup</h3> <p>After all that, I was able to do the following:</p> <pre class="terminal"> # systemctl enable lamegames </pre> <p>This enabled my <code class="language-plaintext highlighter-rouge">gunicorn</code> server to run once the server started. NGINX is that way be default.</p> <p>And tada! You now have a working Django project on a production server!</p> <h4 id="notes">Notes</h4> <ul> <li>If using ws:// websockets, change them to wss:// for secure web sockets.</li> <li>Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.</li> </ul>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.BSD Journey, Part 12020-08-15T00:00:00+00:002020-08-15T00:00:00+00:00/2020/08/15/openbsd1<p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.</p> <p>Now, my interest in OpenBSD has been going on for a long time. I started poking around for Linux alternatives way back a few years ago when Linus Torvalds decided to leave after he got in trouble for some <a href="https://arstechnica.com/information-technology/2013/07/linus-torvalds-defends-his-right-to-shame-linux-kernel-developers/">unprofessional behaviour</a>. That said, Linus did come back to Linux development, but I knew that his abrasive style is what brought good code to the Linux kernel. I also knew that his ability to be critical would be hurt by the new <a href="https://itsfoss.com/linux-code-of-conduct/">code of conduct</a>. It would become a tool for the SJW types to hammer on Linus for being a “white male, et al.”; It would become a tool for the easily offended to use to get their dumb code into Linux; It would become a tool for the corporatization, the HR-ification of Linux. Frankly, this does not interest me.</p> <p>Now I’m sure that OpenBSD has its own internal policies that I disagree with. That said, Theo De Raadt is still at least known for calling Firefox an “amorphous peace of garbage” due to its lack of privilege separation. And, in their <a href="https://openbsd.org/goals.html">project goals</a> page, they specifically mention:</p> <blockquote> <p>Be as politics-free as possible; solutions should be decided on the basis of technical merit.</p> </blockquote> <p>Now that’s something I can get behind! Bet you that’s not in the Linux COC?</p> <p>He also went to university in my hometown, so that’s pretty cool! I can support a local madman who thinks he can make a better operating system than all those corporations. Maybe he was right, maybe not. What I know is I am excited to find out!</p> <p>Wish my luck on my OpenBSD journey. I will post updates here along the way.</p> <p>Happy hacking!</p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.Know How Your Representative Votes In Parliament2020-07-30T00:00:00+00:002020-07-30T00:00:00+00:00/2020/07/30/canadian-parliament<p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s <a href="https://open.canada.ca/en/open-data">Open Data</a> initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.</p> <h3 id="1-find-your-representative">1. Find Your Representative</h3> <p>The first step in this process is to find who your representative is. To do so, go to the government’s own website <a href="https://www.ourcommons.ca/Members/en">ourcommons.ca’s search tool</a>.</p> <p>Simply type in your postal code in the search box to find out who your MP is.</p> <h3 id="2-their-voting-record">2. Their Voting Record</h3> <p>Every MP’s voting record is public knowledge, and it is available nice and simple in a table on that MP’s page. For example, this is a link to <a href="https://www.ourcommons.ca/Members/en/pierre-poilievre(25524)/votes">Pierre Poilievre’s voting record</a>.</p> <p>To find your MP’s voting record, do step one, then: After the <strong>Overview</strong>, and <strong>Seat in The House</strong> sections, there are three tabs, <strong>Roles</strong>, <strong>Work</strong>, and <strong>Contact</strong>. Click on work. At the bottom of that tab is a link which says <strong>Chamber Votes</strong>. This will open a small window with some recent votes by this politician. If you want to see all their votes, there is a button at the bottom named <strong>All Votes by This Member</strong>.</p> <p>Tada! You can now keep your local MP accountable for anything you do or do not support.</p> <h3 id="3-bill-details">3. Bill Details</h3> <p>If you want to get into the nitty gritty, once you open a specific bill, you can actually find out the status of said bill, or read the actual text by clicking the <strong>View this Bill on LEGISinfo</strong> button.</p> <p>Both the status of the bill, and a link to a PDF document containing the bilingual text of the bill are visible in the main body of the page.</p> <h4 id="conclusion">Conclusion</h4> <p>I thought this was pretty cool! It was <em>way</em> simpler than I thought it would be.</p> <p>Thanks, Canada!</p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.Installing MultiCraft on Gentoo Linux2020-07-19T00:00:00+00:002020-07-19T00:00:00+00:00/2020/07/19/multicraft-php-gentoo<p>In a very odd combination of requirements, I needed to install <a href="https://multicraft.org">MultiCraft</a> on a Gentoo Linux system. The PHP <code class="language-plaintext highlighter-rouge">USE</code> flags are important so you don’t have to recompile it three times like I did.</p> <p>Here are some useful tips I came across:</p> <h3 id="php-use-flags">PHP <code class="language-plaintext highlighter-rouge">USE</code> flags</h3> <p>In <code class="language-plaintext highlighter-rouge">/etc/portage/package.use/php</code> I placed the following line:</p> <pre class="terminal"> dev-lang/php cgi mysql mysqli fpm pdo gd truetype </pre> <p>This should give you enough for a mysql backended MultiCraft installation. The <code class="language-plaintext highlighter-rouge">cgi</code> option may not be required as <code class="language-plaintext highlighter-rouge">fpm</code> stands for <em>FastCGI Process Managment</em>. I don’t know for sure though.</p> <h3 id="paper">Paper</h3> <p>This will grab the latest version of the Paper jar file using <a href="https://yivesmirror.com">YivesMirror</a>. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.</p> <pre class="terminal"> ## See the default craftbukkit.jar.conf for a detailed documentation of the ## format of this file. [config] name = Paper 1.16.1 Latest source = https://yivesmirror.com/files/paper/Paper-1.16.1-latest.jar category = Mods [encoding] #encode = system #decode = system #fileEncoding = latin-1 [start] command = "{JAVA}" -Xmx{MAX_MEMORY}M -Xms{START_MEMORY}M -XX:MaxPermSize=128M -Djline.terminal=jline.UnsupportedTerminal -jar "{JAR}" nogui </pre> <h3 id="other-tips">Other Tips</h3> <p>Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.</p> <h4 id="security">Security</h4> <p>If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the <code class="language-plaintext highlighter-rouge">/protected</code> directory.</p> <pre class="terminal"> location /protected { deny all; return 404; } </pre> <h5 id="mysql">MySQL</h5> <p>It is always good practice to separate privileges. The MultiCraft daemon should have one SQL login, with one database allocated to it. The MultiCraft panel should have a separate SQL login, with a separate database allocated to it.</p> <p>You can do this with the following commands in your MySQL prompt:</p> <pre class="terminal"> sql&gt; CREATE DATABASE multicraft_daemon_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE DATABASE multicraft_panel_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'muilticraft_daemon'@'localhost' IDENTIFIED BY 'strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'multicraft_panel'@'localhost' IDENTIFIED BY 'different strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_daemon_database . * TO 'multicraft_daemon'@'localhost'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_panel_database . * TO 'mutlicraft_panel'@'localhost'; Query OK, 0 rows affected (0.01 sec) </pre> <p>During setup, make sure the proper credentials are used for each step. Database 1 is the panel database. Database 2 is the daemon database.</p> <p>Happy hacking :)</p>In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.Independence2020-07-12T00:00:00+00:002020-07-12T00:00:00+00:00/2020/07/12/independence<blockquote> <p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith</p> </blockquote> <p>Whatever you may believe about the YouTube personality Luke Smith, the quote above summarizes a core principle of mine. Much like many people have religious principles, I have <em>Independence</em>.</p> <p>My choice to use Linux as my primary operating system, host my own website, own my own domain name—all of these are directly related to this core principle of independence.</p> <p>I never want a man, or a company to have too much power over my life. Just like I would not trust just any person to be able to read my emails, know where I live, where I am going, who are my friends, what do I believe; in the same way, I do not trust a company with that same information.</p> <blockquote> <p>“If you want to find out what a man is to the bottom, give him power. Any man can stand adversity — only a great man can stand prosperity.”—Robert Ingersoll</p> </blockquote> <p>Take control of your own digital life:</p> <ol> <li>Own your own domain.</li> <li>Hookup an email and a website to that.</li> </ol> <p>That’s it!</p> <p>Without this, any of your internet privileges can be revoked at any time by Google, Facebook, YouTube, Twitter, or even an angry Twitter Mob. Maybe because they hate your skin colour, maybe they hate your religious/political views, or maybe you got caught on a technicality.</p> <p>If you own your own domain, however:</p> <p>Your email provider goes down/bans you: change your provider; keep the email.</p> <p>Your website is pulled for controversial views: switch hosts.</p> <p>Protect yourself; give yourself choices. Why give others that power when you could have it for yourself?</p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke SmithHow to use tmux to send and receive things from your Minecraft server2020-06-25T00:00:00+00:002020-06-25T00:00:00+00:00/2020/06/25/tmux-minecraft<p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.</p> <p>So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.</p> <p>My completed solution for what I needed can be found at <a href="https://github.com/TTWNO/termcraft/">https://github.com/TTWNO/termcraft</a>.</p> <p>If you want some of the implementation details, stick around.</p> <h2 id="solution">Solution</h2> <p>So to solve this interesting problem, I decided to use <code class="language-plaintext highlighter-rouge">tmux</code>. <code class="language-plaintext highlighter-rouge">tmux</code> is a <strong>t</strong>terminal <strong>mu</strong>ltiple<strong>x</strong>er. This allows you to run a terminal session, then detach fromc it while it still runs in the background.</p> <p>This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.</p> <p>So first I looked at the <code class="language-plaintext highlighter-rouge">tmux</code> command <code class="language-plaintext highlighter-rouge">send-keys</code>.</p> <h4 id="send-keys"><code class="language-plaintext highlighter-rouge">send-keys</code></h4> <p><code class="language-plaintext highlighter-rouge">send-keys</code> allows you to send text, and key presses to a <code class="language-plaintext highlighter-rouge">tmux</code> session. Now assuming this <code class="language-plaintext highlighter-rouge">tmux</code> session is attached to a Minecraft server, there is no reason you could not run a command like this:</p> <pre class="terminal"> $ tmux send-keys "tell @a This is a Test" Enter </pre> <p>This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.</p> <p>So now we can send information to the server and have it tell the users something.</p> <p>But how do we get information about who is typing what in the Minecraft chat?</p> <h3 id="tmuxs-capture-pane-is-painful"><code class="language-plaintext highlighter-rouge">tmux</code>’s <code class="language-plaintext highlighter-rouge">capture-pane</code> is painful</h3> <p>So in the manual page for <code class="language-plaintext highlighter-rouge">tmux</code> I can see a section recorded below for options I can give to the <code class="language-plaintext highlighter-rouge">capture-pane</code> subcommand.</p> <pre class="terminal"> -S and -E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. ‘-’ to -S is the start of the history and to -E the end of the visible pane. The default is to capture only the visible contents of the pane. </pre> <p>What it seems to be saying is I can start at line <code class="language-plaintext highlighter-rouge">-S n</code> and end at line <code class="language-plaintext highlighter-rouge">-E n</code>. Negative numbers start from the bottom, so <em>in theory</em> I can do the following: <code class="language-plaintext highlighter-rouge">tmux capture-pane -S -1</code> should capture only the last line, because I’m starting from the last line. Right?</p> <p>No. It just doesn’t work. Negative numbers do <em>not</em> work with the <code class="language-plaintext highlighter-rouge">tmux capture-pane</code> subcommand.</p> <p>So I did some simple UNIX piping, like so, to get just the last thing in the chat.</p> <pre class="terminal"> $ tmux capture-pane -p -t steve | tail -n1 [SERVER] [ExtraDebuggingInfoHere]: &lt;TaterTheTot&gt; MY_MESSAGE </pre> <p>TaterTheTot is my Minecraft username :)</p> <p><code class="language-plaintext highlighter-rouge">-p</code> prints the result to the terminal/stdout.</p> <p><code class="language-plaintext highlighter-rouge">steve</code> is the name of the tmux session I’m trying to pull form.</p> <p>So that’s done! Beauty!</p> <p>Now that we have that, how can we extract the username and the message from the latest line?</p> <h3 id="grep"><code class="language-plaintext highlighter-rouge">grep</code></h3> <p><code class="language-plaintext highlighter-rouge">grep</code> is a command to find patterns of text. <code class="language-plaintext highlighter-rouge">grep</code> has an option to only show a matching pattern of text. This option is <code class="language-plaintext highlighter-rouge">-o</code>.</p> <p>Let’s see how we can use this in conjunction with our latest line of server output to get our results.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" &lt;TaterTheTot&gt; </pre> <p>Now, that’s my name with the &lt; and &gt; attached. Not bad! We can use the <code class="language-plaintext highlighter-rouge">sed</code> command to clean it up a bit.</p> <p>The syntax is like so: <code class="language-plaintext highlighter-rouge">select/somepattern/replacewith/global</code></p> <p>So the following command is: <code class="language-plaintext highlighter-rouge">s/[&lt;&gt;]//g</code></p> <p>Select any characters that are either &lt; or &gt;. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).</p> <p>Take two!</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" | sed 's/[&lt;&gt;]//g' TaterTheTot </pre> <p>Beautiful!</p> <p>Now what about that pesky message?</p> <h3 id="more-grep-more-sed">more <code class="language-plaintext highlighter-rouge">grep</code>; more <code class="language-plaintext highlighter-rouge">sed</code></h3> <p>Simple: capture everything after the &gt;. Leaving the user’s message entirely in tact.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&gt;.&ast;$" | sed 's/&gt; //' MY_MESSAGE </pre> <p>So now we have a way to get the username of someone typing in the Minecraft server chat. We have a way to find out what they said. And, we have a way to respond.</p> <p>You can imagine how these might go together for your own use case.</p> <h3 id="conclusion">Conclusion</h3> <p>This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.</p> <p>I hope you learned something and found my explanations not horrific haha!</p> <p>Remember to checkout the git repository to see what I did with it: <a href="https://github.com/TTWNO/termcraft">https://github.com/TTWNO/termcraft</a>.</p> <p>Happy hacking!</p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.Site Update2020-06-04T00:00:00+00:002020-06-04T00:00:00+00:00/2020/06/04/site-update<p>I updated the site with some easier to identify information about me and my projects :)</p> <p>Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.</p> <p>I also posted a new project called <em><a href="https://github.com/TTWNO/caesar-cipher">Caesar Cipher</a></em> in C. It will be an intermediate example of how to use build systems like <code class="language-plaintext highlighter-rouge">make</code>.</p>I updated the site with some easier to identify information about me and my projects :) \ No newline at end of file diff --git a/_site/public-key.asc b/_site/public-key.asc index adfee3a..c9d83ac 100644 --- a/_site/public-key.asc +++ b/_site/public-key.asc @@ -1,52 +1,51 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -mew= -=mJva +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 +=wEUc -----END PGP PUBLIC KEY BLOCK----- diff --git a/public-key.asc b/public-key.asc index adfee3a..c9d83ac 100644 --- a/public-key.asc +++ b/public-key.asc @@ -1,52 +1,51 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -mew= -=mJva +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 +=wEUc -----END PGP PUBLIC KEY BLOCK-----