<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matthew Odle</title>
    <description>The latest articles on DEV Community by Matthew Odle (@matthewodle).</description>
    <link>https://dev.to/matthewodle</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F72529%2F98007e50-b4dc-4478-8937-7ee26d65159a.jpg</url>
      <title>DEV Community: Matthew Odle</title>
      <link>https://dev.to/matthewodle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matthewodle"/>
    <language>en</language>
    <item>
      <title>Message Queue MUD Dockerization Update</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Fri, 08 Feb 2019 05:38:29 +0000</pubDate>
      <link>https://dev.to/matthewodle/message-queue-mud-dockerization-update-2119</link>
      <guid>https://dev.to/matthewodle/message-queue-mud-dockerization-update-2119</guid>
      <description>

&lt;p&gt;&lt;a href="http://blog.matthewodle.com/message-queue-mud-dockerization-update/"&gt;Original post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I made progress on &lt;a href="https://gitlab.com/modle13/queue-mud/tree/v0.1.0_share"&gt;that MUD I’ve been working on&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Try it out (yea, naming things is hard):&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b v0.1.0_share https://gitlab.com/modle13/queue-mud.git
cd queue-mud
docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Over the weekend, I got it working nearly back to the state it was before dockerizing. There’s a bit less logic (the creatures decision point just returns a string), and the Angular frontend is still disabled. I had had a publisher and consumer working back in early January, but now it has the whole chain: command &amp;gt; validate &amp;gt; attack if command is attack and there’s a target &amp;gt; response queue.&lt;/p&gt;

&lt;p&gt;The main problem I solved was how to have separate queues for the different decision points. I also solved the problem of getting gunicorn and python to output stdout and stderr properly to the docker-compose logs (check out the Dockerfile CMD flags; &lt;code&gt;-u&lt;/code&gt; for python and &lt;code&gt;"--access-logfile", "-", "--error-logfile", "-"&lt;/code&gt; for gunicorn). I learned some things about naming services and containers, querying the RabbitMQ API, &lt;code&gt;.env&lt;/code&gt; files for docker-compose, and using custom python modules across multiple containers.&lt;/p&gt;

&lt;p&gt;After I added the 2nd and 3rd queues, I had the strong urge to abstract the boilerplate into a module (that’s what modules are for: extensible boilerplate :D), so I did that and it was glorious. I added a module for publishing too, and split the responsibility of the publisher flask app into an ingester (flask &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt; endpoints only), and a publisher module (simple python, used in all decision points and the ingester). This way I don’t have to worry about the boilerplate at all. I just set the &lt;code&gt;queue&lt;/code&gt; and &lt;code&gt;exchange&lt;/code&gt; parameter and custom &lt;code&gt;on_message&lt;/code&gt; function on initialization of the decision point to set up a new queue, and pass the name of the target exchange with the message to the publisher module.&lt;/p&gt;

&lt;p&gt;The key focus today was cleaning it up so using it sucked less. Writing the shared modules was a big step. Now new modules only need to set the two parameters and override the &lt;code&gt;on_message&lt;/code&gt; method. I should probably do this with inheritance, but this’ll do for now. I cleaned up the &lt;code&gt;README&lt;/code&gt;s, listed required and optional system dependencies (only &lt;code&gt;docker-ce&lt;/code&gt; and &lt;code&gt;docker-compose&lt;/code&gt; are required). I added a tmuxinator profile (so cool) that spins up the custom &lt;code&gt;queue_watcher&lt;/code&gt; script (queries the RabbitMQ API and parses out some data using python), the log tailer (message output by the response queue consumer), docker-compose (the thing actually running the game), and a verification step (&lt;code&gt;verify.sh&lt;/code&gt;, which &lt;code&gt;POST&lt;/code&gt;s a few example values to the ingester and checks the &lt;code&gt;status&lt;/code&gt; endpoint).&lt;/p&gt;

&lt;p&gt;The status endpoint also has an unnecessary feature. It uses a goodreads API to pull random George Orwell quotes. &lt;a href="https://gitlab.com/modle13/goodreads-quotes/blob/f271f696a67b3232a2f9daa9f264ba89fefb8fdb/get_quote.py"&gt;Here’s the module&lt;/a&gt;. Don’t make fun of the &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next steps are to figure out how to handle world state, and how to manage creatures, both likely have the same set of problems.. Currently responses just get dumped to the volume mounted log file. This is fine for now (it could be playable locally) but eventually there will need to be size considerations. It would also be interesting to be able to replay the history, and for that something a little smarter would be needed.&lt;/p&gt;


</description>
      <category>flask</category>
      <category>rabbitmq</category>
      <category>dockercompose</category>
      <category>mud</category>
    </item>
    <item>
      <title>Simple 3D Camera Movement in Unity</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Fri, 26 Oct 2018 02:03:17 +0000</pubDate>
      <link>https://dev.to/matthewodle/simple-3d-camera-movement-1lcj</link>
      <guid>https://dev.to/matthewodle/simple-3d-camera-movement-1lcj</guid>
      <description>&lt;p&gt;I'm making a 3D village management sim, and when things get crazy, I like to be able to zoom in on the action in a specific region.&lt;/p&gt;

&lt;p&gt;This camera control script looks for horizontal and vertical axis inputs (WASD and arrow keys) to allow the player to pan around. It also watches the mouse scrollwheel to allow the player to raise and lower the camera.&lt;/p&gt;

&lt;p&gt;Just drop it on your &lt;code&gt;Main Camera&lt;/code&gt; gameobject in your scene, and you're good to go.&lt;/p&gt;

&lt;p&gt;The basic pattern is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if input

&lt;ul&gt;
&lt;li&gt;create a new &lt;code&gt;Vector3(x, y, z)&lt;/code&gt; using the target inputs

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt; for pan, &lt;code&gt;y&lt;/code&gt; for zoom, the rest &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;multiply the &lt;code&gt;Vector3&lt;/code&gt; by some predefined speed&lt;/li&gt;
&lt;li&gt;add the new &lt;code&gt;Vector3&lt;/code&gt; to the position of the camera transform&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've inverted the mouse scrollwheel input so that scrolling up zooms in and scrolling down zooms out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using UnityEngine;

public class CameraController : MonoBehaviour {

    private float moveSpeed = 0.5f;
    private float scrollSpeed = 10f;

    void Update () {
        if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0) {
            transform.position += moveSpeed * new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        }

        if (Input.GetAxis("Mouse ScrollWheel") != 0) {
            transform.position += scrollSpeed * new Vector3(0, -Input.GetAxis("Mouse ScrollWheel"), 0);
        }
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>c</category>
    </item>
    <item>
      <title>Simple UI Element Dragging Script in Unity C#</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Fri, 26 Oct 2018 01:21:09 +0000</pubDate>
      <link>https://dev.to/matthewodle/simple-ui-element-dragging-script-in-unity-c-450p</link>
      <guid>https://dev.to/matthewodle/simple-ui-element-dragging-script-in-unity-c-450p</guid>
      <description>&lt;p&gt;Attach this script to your gameobject/prefab to enable a simple drag mechanism for your UI elements. You'll also need to add the &lt;code&gt;EventSystem&lt;/code&gt; object to your scene.&lt;/p&gt;

&lt;p&gt;It's fairly straightforward. The overridden &lt;code&gt;UnityEngine.EventSystems.OnPointerDown&lt;/code&gt; and &lt;code&gt;OnPointerUp&lt;/code&gt; methods simply toggle the dragging &lt;code&gt;bool&lt;/code&gt;. Then the &lt;code&gt;Update&lt;/code&gt; method will modify the position of the object this script is attached to.&lt;/p&gt;

&lt;p&gt;With this solution, the UI element's center will snap to the cursor, but to make this cleaner, an offset can be added based on the position within the UI element when the &lt;code&gt;OnPointerDown&lt;/code&gt; is triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class UIElementDragger : EventTrigger {

    private bool dragging;

    public void Update() {
        if (dragging) {
            transform.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        }
    }

    public override void OnPointerDown(PointerEventData eventData) {
        dragging = true;
    }

    public override void OnPointerUp(PointerEventData eventData) {
        dragging = false;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>ui</category>
    </item>
    <item>
      <title>Scripting Android with QPython3</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Sat, 26 May 2018 15:07:18 +0000</pubDate>
      <link>https://dev.to/matthewodle/scripting-android-with-qpython3-1lmg</link>
      <guid>https://dev.to/matthewodle/scripting-android-with-qpython3-1lmg</guid>
      <description>&lt;p&gt;This is my (fledgling and abandoned, due to general lack of efficiency) mobile-only script development workflow &lt;a href="http://blog.matthewodle.com/roll-your-own-name-generator-with-qpython3-on-an-android-device/" rel="noopener noreferrer"&gt;(full post here)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2Fgenerated-names-300x97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2Fgenerated-names-300x97.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Dev On the Go
&lt;/h1&gt;

&lt;p&gt;Last summer, I went on a road trip, and wanted to experiment with a mobile-only workflow. The main components I needed were an interpreter, an IDE, and source control.&lt;/p&gt;

&lt;h4&gt;
  
  
  The suite of apps I discovered and used are as follows:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;QPython3 (a python IDE and script runner)&lt;/li&gt;
&lt;li&gt;SGit (source control)&lt;/li&gt;
&lt;li&gt;LabCoat (GitLab repository viewer)&lt;/li&gt;
&lt;li&gt;Dropbox (ad-hoc, easy-access file storage)&lt;/li&gt;
&lt;li&gt;Dropsync (one- or two-way sync between directories on your mobile device)&lt;/li&gt;
&lt;li&gt;Epsilon Notes (a markdown editor and renderer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, I will cover my experience with QPython3.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways from this endeavor:
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Plus:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;It's all in your pocket&lt;/li&gt;
&lt;li&gt;Short feedback loop for testing code&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Delta:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Difficult to view information across multiple apps&lt;/li&gt;
&lt;li&gt;Editing code can be a chore&lt;/li&gt;
&lt;li&gt;Not enough charging stations with comfortable seating in that one mall I went to&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Recommendations:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Get a bluetooth keyboard&lt;/li&gt;
&lt;li&gt;Bring the portable charger&lt;/li&gt;
&lt;li&gt;Sit uncomfortably close to strangers so you can share the charging station and maybe they'll leave (they didn't leave, but offered to rearrange the seating so we could chum comfortably whilst staring silently at our phones)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Covered in this post:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;How to run a 'Hello World!' script in QPython3&lt;/li&gt;
&lt;li&gt;How to load files into a QPython3 script&lt;/li&gt;
&lt;li&gt;How I come up with names for my pets&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Install list
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=org.qpython.qpy3" rel="noopener noreferrer"&gt;QPython3 (v1.0.3)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run Qpython3 and Open hello_world.py
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F1-qpython-main.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F1-qpython-main.png" title="QPython3 Home" alt="QPython3 Home"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;Programs&lt;/code&gt; &amp;gt; &lt;code&gt;hello_world.py&lt;/code&gt; &amp;gt; &lt;code&gt;Open&lt;/code&gt; to view the script contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F3-qpython-open.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F3-qpython-open.png" title="QPython3 Open" alt="QPython3 Open"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F4-qpython-hello-world-contents.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F4-qpython-hello-world-contents.png" title="QPython3 Hello World Contents" alt="QPython3 Hello World Contents"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Super standard python3 here. The script uses the &lt;a href="http://pythoncentral.io/python-for-android-the-scripting-layer-sl4a/" rel="noopener noreferrer"&gt;SL4A library&lt;/a&gt; (Scripting Layer for Android) to show an Android toast, then prints &lt;code&gt;Hello world!&lt;/code&gt; to the console.&lt;/p&gt;

&lt;p&gt;Click the right-arrow on the bottom toolbar of the script editor screen to run the script. You can also back out of the editor, click &lt;code&gt;hello_world.py&lt;/code&gt; again, and choose &lt;code&gt;Run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F5-qpython-hello-world-execution.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F5-qpython-hello-world-execution.png" title="QPython3 Hello World Execution" alt="QPython3 Hello World Execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;Play around with some of the other default scripts if you like. To see some of the SL4A library functionality in action, run &lt;code&gt;test.py&lt;/code&gt; to run a series of tests on various sensors and UI elements. It will demo a few things like text to voice, various form input elements, and progress bars.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo Qpython3 App - Item Name Generator!
&lt;/h2&gt;

&lt;p&gt;Now that you're able to execute python scripts on your Android device, let's do the only logical next thing: write a short fantasy equipment name generator script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the Files
&lt;/h3&gt;

&lt;p&gt;First, we need to load the file contents. I wrote a quick loadfile function, which takes a filename and returns a list of strings contained in the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F10-qpython-assets.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F10-qpython-assets.png" title="QPython3 Namegen Assets" alt="QPython3 Namegen Assets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;weapons.txt&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;
bow
axe
mace
sword
spear
flail
etc
&lt;/pre&gt;

&lt;p&gt;Each line in the loaded file will be stored as a separate string in the list by splitting on newline, or &lt;code&gt;\n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fileutils.py&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;
def loadfile(filename):
    root_path = '/storage/emulated/0/qpython/scrdipts3/modules/namegen/assets/'
    file_path = '{}{}'.format(root_path, filename)
    loaded_file = open (file_path, 'r')
    return loaded_file.read().split('\n')
&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F30-qpython-fileutils.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F30-qpython-fileutils.png" title="QPython3 fileutils.py" alt="QPython3 fileutils.py"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That &lt;code&gt;root_path&lt;/code&gt; is pretty ugly, but QPython3 does not work with relative paths. It can be improved by using the &lt;code&gt;os&lt;/code&gt; library:&lt;/p&gt;

&lt;pre&gt;
import os

def loadfile(filename):
    root_path = os.path.dirname(os.path.abspath(__file__))
    ...
&lt;/pre&gt;

&lt;p&gt;This will let you run the script on other devices, such as a Linux VM.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Name Generator Script
&lt;/h3&gt;

&lt;h4&gt;
  
  
  This script will:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Import the loadfile function from our &lt;code&gt;fileutils.py&lt;/code&gt; module&lt;/li&gt;
&lt;li&gt;Read the contents from 3 text files into lists: prefixes, weapons, and suffixes&lt;/li&gt;
&lt;li&gt;For each weapon in the weapons list:

&lt;ul&gt;
&lt;li&gt;Print a string which consists of the current weapon name and a random prefix and suffix&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Load the files. Import the loadfile function from our fileutils module.&lt;/p&gt;

&lt;pre&gt;
from fileutils import loadfile
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Invoke loadfile to create a weapons list, then print it out to verify.&lt;/p&gt;

&lt;pre&gt;
from fileutils import loadfile

weapons = loadfile('weapons.txt')
print (weapons)
&lt;/pre&gt;

&lt;p&gt;If you run the script, you should see a list with an entry for each weapon printed to the console:&lt;/p&gt;

&lt;pre&gt;
['bow', 'axe', ...]
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Invoke loadfile for the prefixes and suffixes.&lt;/p&gt;

&lt;pre&gt;
from fileutils import loadfile

weapons = loadfile('weapons.txt')
prefixes = loadfile('prefixes.txt')
suffixes = loadfile('suffixes.txt')
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Finally, loop through the weapon list, select a random prefix and suffix, and print out the combination. Don't forget to import the &lt;code&gt;random&lt;/code&gt; module.&lt;/p&gt;

&lt;pre&gt;
import random
from fileutils import loadfile

weapons = loadfile('weapons.txt')
prefixes = loadfile('prefixes.txt')
suffixes = loadfile('suffixes.txt')

for weapon in weapons:
    prefix = random.choice(prefixes)
    suffix = random.choice(suffixes)
    print ("{} {} of {}".format(prefix, weapon, suffix))
&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F20-namegen-code.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F20-namegen-code.png" title="QPython3 Namegen Code" alt="QPython3 Namegen Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see something similar to this in your console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F40-qpython-generated-names.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fblog.matthewodle.com%2Fwp-content%2Fuploads%2F2018%2F02%2F40-qpython-generated-names.png" title="QPython3 Generated Names" alt="QPython3 Generated Names"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;You should now be familiar with writing and executing python modules on your Android device! Add your own prefixes, suffixes, and weapons to roll your equally awesome counterparts to such legendary names as &lt;code&gt;satisfactory cuisses of pain&lt;/code&gt;, &lt;code&gt;repressed flail of efficacy&lt;/code&gt;, and &lt;code&gt;gesticulating bow of obscurity&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>qpython3</category>
    </item>
    <item>
      <title>Handling Sounds With HTML5 Canvas</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Wed, 16 May 2018 00:57:52 +0000</pubDate>
      <link>https://dev.to/matthewodle/handling-sounds-with-html5-canvas-4cf3</link>
      <guid>https://dev.to/matthewodle/handling-sounds-with-html5-canvas-4cf3</guid>
      <description>

&lt;p&gt;There are two takeaways here: initialize assets and reuse them, and if you want to play event-triggered sounds that could overlap (laser firing, for example), you'll want multiple copies of the asset available. &lt;/p&gt;

&lt;p&gt;I learned pretty early to initialize static assets once and then reuse them. The original implementation was adding a new asset for each game object. This meant every laser fired would add another sound to the DOM. As you can imagine, this allows you to figure out the upper limit on the quantity of elements in the DOM pretty quickly.&lt;/p&gt;

&lt;p&gt;The solution was to not tie the sounds directly to objects, but rather to create a pool of sounds, then cycle through the pool when a sound was triggered.&lt;/p&gt;

&lt;p&gt;Here is the implementation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/modle/centipede/blob/260fa54dadce91f25148638cf1eb10d46ff13dec/app/scripts/sound.js"&gt;https://github.com/modle/centipede/blob/260fa54dadce91f25148638cf1eb10d46ff13dec/app/scripts/sound.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;init()&lt;/code&gt; will get called by the loader script, and will loop on the keys of the tracks object, creating the sounds and adding them to the DOM.&lt;/p&gt;

&lt;p&gt;To play the sound, we just call &lt;code&gt;playSound()&lt;/code&gt; with the type matching the key we want. &lt;code&gt;playSound()&lt;/code&gt; will call &lt;code&gt;getSound()&lt;/code&gt;, which will use mod to set the target index to the next index, or &lt;code&gt;0&lt;/code&gt; if previous index was the end of the array. Then the sound element at that index will get returned and played. &lt;/p&gt;


</description>
      <category>html5canvas</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Mirroring My Canvas Games Between GitLab and GitHub</title>
      <dc:creator>Matthew Odle</dc:creator>
      <pubDate>Tue, 15 May 2018 17:28:13 +0000</pubDate>
      <link>https://dev.to/matthewodle/mirroring-my-canvas-games-between-gitlab-and-github-5e7c</link>
      <guid>https://dev.to/matthewodle/mirroring-my-canvas-games-between-gitlab-and-github-5e7c</guid>
      <description>&lt;p&gt;I migrated my private projects to GitLab about 2 years ago when we started using it at work. I have nothing against GitHub, but I really like GitLab's interface. The problem is GitHub is THE place to host your stuff if you want others to have easy access to it. This meant I had to maintain two different remotes for every project.&lt;/p&gt;

&lt;p&gt;Today, I fixed that.&lt;/p&gt;

&lt;p&gt;GitLab's mirror push interface is really easy to use. You can access it via Settings &amp;gt; Repository &amp;gt; Push to a remote repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2vf8rqo06ojr49k2oo1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2vf8rqo06ojr49k2oo1x.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fy5e96c14frqptfolo0xw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fy5e96c14frqptfolo0xw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the GitHub project's URL with the proper credentials to the 'Git repository URL' field&lt;/li&gt;
&lt;li&gt;Click 'Update Now'&lt;/li&gt;
&lt;li&gt;GitLab will show: &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3q2w30egz5g7vjedta33.png"&gt;
&lt;/li&gt;
&lt;li&gt;GitHub will show: &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmyusv54urg1q1dp97rbc.png"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Woo.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/21962872/how-to-create-a-gitlab-webhook-to-update-a-mirror-repo-on-github" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/21962872/how-to-create-a-gitlab-webhook-to-update-a-mirror-repo-on-github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/help/workflow/repository_mirroring#pushing-to-a-remote-repository" rel="noopener noreferrer"&gt;https://gitlab.com/help/workflow/repository_mirroring#pushing-to-a-remote-repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=8908235" rel="noopener noreferrer"&gt;https://news.ycombinator.com/item?id=8908235&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>gitlab</category>
    </item>
  </channel>
</rss>
