<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by DurHack on Medium]]></title>
        <description><![CDATA[Stories by DurHack on Medium]]></description>
        <link>https://medium.com/@DurHack_press?source=rss-2e8c1e22c97a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*dtMEf7NXs_BZ920GXeJTEw.jpeg</url>
            <title>Stories by DurHack on Medium</title>
            <link>https://medium.com/@DurHack_press?source=rss-2e8c1e22c97a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 23 May 2026 15:17:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@DurHack_press/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Why Music Students Belong at Hackathons]]></title>
            <link>https://medium.com/@DurHack_press/why-music-students-belong-at-hackathons-d5224ed61b49?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/d5224ed61b49</guid>
            <category><![CDATA[hacking]]></category>
            <category><![CDATA[music]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Thu, 02 Apr 2026 16:49:41 GMT</pubDate>
            <atom:updated>2026-04-02T17:01:40.678Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/@thomasblair3008">Thomas Blair</a></p><p><strong>Exploring hackathons as creative playgrounds for musicians, composers, and music technologists</strong></p><p>More often than not, hackathons are associated with advanced experience in coding and software architecture, expressed through non-stop, caffeine-fueled writing of code. Whilst this is a fully valid approach to competing at a hackathon, framing these events as an opportunity to collaborate and expand your creative<strong> </strong>practice is often overlooked.</p><p>In this article we will focus on what hackathons can offer to students from the field of music. This will include insights from Steven Bradley, a Professor at the Department of Computer Science here at Durham, with a particular interest in computational approaches to music<strong> </strong>theory.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zReOY547Oj-g91Eq1XgExA.jpeg" /><figcaption>A sound-based VR project from DurHack 2018</figcaption></figure><h3>It’s Not Just Code</h3><p>Hackathons are commonly framed as technical competitions, but at their core, they are fast-paced creative labs with the ability to bring different disciplines together to solve problems. Rather than rewarding polished final products, hackathons prioritise experimentation and collaboration above all.</p><p>For music students, this environment should feel surprisingly familiar. Workshops and rehearsals carry similar constraints: limited time, evolving ideas and requirements, as well as constant feedback.</p><p>Most importantly, hackathons do not require every participant to be an expert programmer. The most successful teams are built on the intersection of various disciplines. This leads to projects that excel in every field, not just the code. From sound design to the final presentation, a project is judged for far more than its coding complexity, and teams often benefit from assigning design-based tasks to those with a background in the field. We’ve had a solid understanding of this concept for quite some time:</p><p><em>“The society that separates its scholars from its warriors will have its thinking done by cowards and its fighting by fools” (Thucydides, ~400BC)</em></p><p>Whilst Thucydides had minimal software engineering experience, his ideas align with what makes a great hackathon team: Talented coders, innovative designers, creative minds, and well-defined leaders in each area. Music students, far from being out of place, bring a uniquely valuable creative perspective to any team.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JdoTivk2eMBt2eswVQxefQ.jpeg" /><figcaption>A project using Guitar Hero controllers at DurHack 2023</figcaption></figure><h3>Past Musical Projects at DurHack</h3><p>For some inspiration, and to demonstrate some wonderful examples of intersecting programming and music, let’s take a look at some past DurHack projects that center themselves around music through the medium of coding.</p><h3>Echo: Turning Stories into Soundscapes</h3><p>One of the most compelling examples of music-led innovation at DurHack is Echo, a project that reimagines what it means to experience a story. Awarded 2nd place (Solar Spark) at DurHack X (2025), Echo demonstrates how musical thinking can drive technically sophisticated and conceptually original ideas.</p><p>At its core, Echo begins with a simple but powerful question: why is reading silent? While film and games rely heavily on sound to shape emotion, books remain largely auditory blank spaces. Echo challenges this by transforming written text into adaptive soundscapes that evolve alongside the narrative.</p><p>The system works by analysing a story page by page, identifying key elements such as setting and emotional tone. This information is then structured into data that can be used to generate a dynamic soundtrack. A calm scene might be accompanied by soft environmental ambience, while rising tension could introduce darker, more intense sonic textures.</p><p>What makes this particularly interesting is not just the technical implementation, but the musical sensitivity behind it. The project relies on an understanding of how sound interacts with narrative, something music students are uniquely equipped to contribute.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ldQa4tVpxDur-62suO0nnQ.jpeg" /><figcaption>Echo team member, Patrick Sam, presenting at DurHack X</figcaption></figure><p>Technically, Echo uses a multi-agent AI system to break down and interpret text. But one of the biggest challenges the team faced was controlling the system’s creativity. Left unchecked, it would invent details or over-describe scenes.</p><p>Their solution highlights a key parallel with music, that creativity often thrives within constraints, not without them. This is a familiar concept to composers working within harmonic systems, stylistic rules, or formal structures. The same principle applies here, where carefully designed limitations lead to more meaningful and usable outputs.</p><p>Overall, Echo is a clear example of how music students can shape hackathon projects beyond surface-level contributions. While the technical stack is impressive, the project’s success ultimately depends on its creative direction. Without that, it would simply be text analysis, not an immersive storytelling tool.</p><h3>Clap Collider: Making Movement Musical</h3><p>If Echo explores how music can enhance storytelling, Clap Collider flips the relationship entirely by turning physical movement into the source of music itself. Winner of first place (Legacy Luminary) at DurHack X (2025), this project embodies the idea of “bridging between worlds” by connecting gesture, space, and sound.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*u87QjmpYxh-J0lpedASQ4Q.jpeg" /><figcaption>The Clap Collider team recieving their prizes for winning first place at DurHack X</figcaption></figure><p>Rather than asking users to interact with pre-existing music, Clap Collider invites them to create it through movement:</p><ul><li>Clapping or raising hands triggers a looping drum beat</li><li>Swiping gestures switch modes or introduce melodic layers</li><li>The body becomes both the controller and the performer</li></ul><p>This transforms music-making into something immediate and embodied. There’s no need for traditional instruments or interfaces, just motion, rhythm, and interaction. For music students, this idea resonates strongly with practices in improvisation, performance, and experimental composition.</p><p>Technically, the project was divided into three key components:</p><ul><li>Frontend: handling the user interface and real-time interaction</li><li>Pose estimation: tracking body movement and interpreting gestures</li><li>Music generation: translating those gestures into rhythmic and melodic loops</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8juN3O74mAkJLYGK319Gzg.jpeg" /><figcaption>The Clap Collider team’s finalist demo at DurHack X</figcaption></figure><p>One of the most significant challenges the team faced was integrating new musical layers into an ongoing loop, and how to introduce a new instrument mid-cycle without breaking the rhythm. This is both a technical and musical issue. Questions of timing, phrasing, and continuity are central to performance and composition, so solving this problem required thinking like musicians as much as programmers.</p><p>What stands out about Clap Collider is how closely the final result aligned with the team’s original vision. In a hackathon setting, where ideas often need to be scaled back or simplified, successfully realising a concept like this is a significant achievement.</p><p>Clap Collider reinforces the idea that hackathons are spaces to prototype new forms of creative expression, not just programs and software.</p><h3>Professor Steven Bradley on Music and Coding</h3><p>To better understand the relationship between music and hackathons, I spoke with Professor Steven Bradley from Durham University’s Department of Computer Science, whose work explores computational approaches to music theory. His perspective highlights a crucial point: music and programming are far less separate than they might initially appear. This interview included an amazing story of making a custom electric hurdy gurdy, one so awe-inspiring that it deserves its own article entirely.</p><p>One of the strongest overlaps between music and computing, Bradley explains, is pattern recognition. Musicians are constantly identifying, interpreting, and reproducing patterns, whether harmonic, rhythmic, or structural. This same skill lies at the heart of programming. Beyond this, he emphasises a mindset that applies equally to both disciplines:</p><blockquote><em>“One of the biggest predictors of whether you can be good at writing programs is whether you think that you will be able to do it if you keep on trying.”</em></blockquote><p>For music students, this idea should feel familiar. Progress in music is iterative, often requiring significant effort before results become visible. As Bradley puts it:</p><blockquote><em>“One of the issues with music is that you have to put a lot into it to get something out.”</em></blockquote><p>This persistence translates directly into hackathons, where experimentation, failure, and rapid iteration are part of the process.</p><p>Bradley strongly challenges the idea that music students lack technical relevance in hackathon settings. Instead, he points to a range of transferable skills that are often underutilised:</p><ul><li>Creative problem-solving</li><li>Iterative development through feedback</li><li>Collaboration in ensemble settings</li><li>Communication and presentation</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7Wa3XF5jXUKzchpCJwTchg.jpeg" /><figcaption>Professor Steven Bradley judging projects at DurHack X in November 2025</figcaption></figure><blockquote><em>“There are lots of important transferable skills that are often not routinely applied that music students bring.”</em></blockquote><p>When combined with any degree of programming ability, these skills become particularly powerful.</p><blockquote><em>“Music provides a lot of soft skills, and combining this with the ability to write programs to solve specific problems in your field can be really powerful.”</em></blockquote><h3>Solving Human Problems Via Domain Expertise</h3><p>A key advantage music students bring to hackathons is domain knowledge. While programmers may excel at building systems, musicians understand the real-world challenges those systems might address. Bradley notes:</p><blockquote><em>“Bringing their domain expertise in can aid in identifying and solving problems that affect humans.”</em></blockquote><p>This is especially relevant in creative and human-centred projects, where understanding user experience, emotion, and interaction can define a project’s success.</p><h3>Modules Exploring Music and Programming</h3><p>At Durham, this intersection is already being explored through modules such as Introduction to Music Computing and Advanced Music Computation, the latter taught by Bradley himself. These courses demonstrate how programming can become a creative medium by:</p><ul><li>Analysing musical structures computationally</li><li>Building systems that interact with musical ideas</li><li>Exploring computational creativity, where code functions as an instrument</li></ul><p>As Bradley puts it, treating a programming language like an instrument opens new creative possibilities.</p><blockquote><em>“If you can explore music spaces more quickly, then you can engage creativity more.”</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3VyCHYnSfUUNrCoY3sUa3A.jpeg" /><figcaption>Professor Steven Bradley sat with DurHack founder Sara Chen at DurHack 2018</figcaption></figure><h3>What This Means For Hackathons</h3><p>Bradley’s insights reinforce a central idea: hackathons are both technical challenges and creative ecosystems. Music students already possess many of the cognitive and collaborative tools needed to thrive in them.</p><p>Rather than starting from scratch, they are extending an existing skill set into a new medium. And in doing so, they carry the potential to reshape what hackathon projects can be.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d5224ed61b49" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How RS Helped Bring Hardware to Life at DurHack X]]></title>
            <link>https://medium.com/@DurHack_press/how-rs-helped-bring-hardware-to-life-at-durhack-x-ce035b9bfc05?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/ce035b9bfc05</guid>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[hardware]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Fri, 09 Jan 2026 15:55:33 GMT</pubDate>
            <atom:updated>2026-01-14T15:33:12.296Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/bd844185d516">Millie Thompson</a> and edited by <a href="https://medium.com/u/80c483f4c085">Oscar Ryley</a></p><h4>Check out this article <a href="https://www.rs-online.com/designspark/how-rs-helped-bring-hardware-to-life-at-durhack-x">on Designspark</a>!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MeSavor3uULBVvyt-sZDfQ.jpeg" /></figure><p>DurHack X was the 10th edition of Durham University’s annual student hackathon, where students come together from all over the UK to build creative tech projects. Hardware projects are a big part of what makes DurHack so exciting - there is something particularly special about seeing an idea turn into a real, working device. RS is a global supplier of electronics and engineering components that supports students and professionals with the tools they need to build and experiment. For DurHack X, RS played an integral role in making that possible for our hackers.</p><h3>How Hardware Sponsorship Enables Inclusion</h3><p>For many students, hardware hacks can feel intimidating or be too expensive. Backing from RS allowed us to launch a Hardware Library to give everyone the chance to try something hands-on. A lot of teams told us that without this support, they would not have attempted a hardware project at all.</p><p>Support like this does more than make projects easier. It helps create a space where creativity feels possible for everyone from a variety of backgrounds and skill levels. This sense of inclusivity is what makes hackathons like DurHack special and we are extremely grateful to RS for helping us achieve this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HA4K00DxFJGvUm1b_ekb3w.jpeg" /></figure><h3>When the tools are there, the ideas get bolder.</h3><p>As our hardware sponsor, RS supplied the components and equipment that teams relied on throughout the weekend. Microcontrollers, sensors, prototyping tools and other essentials were all available from the RS Hardware Library. This meant participants could focus on building and experimenting instead of worrying about missing parts or the reliability of gear.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9Sw2cJRgteCEo5VKJFX1oQ.jpeg" /></figure><p>At DurHack X, teams put together a wide range of creative hardware hacks that blended AI, electronics and clever problem solving. One group, <a href="https://devpost.com/software/niche-machine">Niche Machine</a>, built a 3D-printed crystal ball that predicts which songs are about to trend using TikTok data - earning them G-Research’s Prize. Another project that stood out was <a href="https://devpost.com/software/smart-tongs">Smart Tongs</a>, a pair of sensored kitchen tongs with an LCD screen for real-time safety information, which won Hackathon UK’s Hackiest Hack. The team behind <a href="https://devpost.com/software/3rd-eye-knax5f">3rd Eye</a> built a smart camera that follows your head movements and describes what it sees in simple language, using a laptop to track your face and a Raspberry Pi to move the camera and process the video. There was even <a href="https://devpost.com/software/rubber-duck-agent">an Animated Debug Duck</a>, a Raspberry Pi–powered version of the classic rubber duck that uses simple machine-learning models to support programmers like an interactive assistant. Each project came with its own hurdles, but the teams pushed through and produced highly inventive, fully working prototypes that showed just how fun and accessible hardware hacking can be.</p><h3><strong>Beyond DurHack: Hardware That Keeps on Building</strong></h3><p>DurHack X’s theme was Legacy, and one of the ways the event will continue to support technology sustainably into the future is the donation of the hardware provided by RS to Durham Robotics. Weekly sessions in electronics, CAD and 3D printing give students the chance to learn new skills and apply them to long-term projects, with plenty of tools and guidance available. It’s a space where members can keep experimenting, building and improving their hardware projects well beyond the hackathon weekend. By reusing components and extending the lifespan of hardware introduced at DurHack, the society also encourages more resource-conscious and sustainable engineering practices, continuing our legacy of sustainability.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rFP-6Tx2-dGtbWnBtLuvYA.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ce035b9bfc05" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HackPack: Building an Image Classification Neural Network with Python]]></title>
            <link>https://medium.com/@DurHack_press/hackpack-building-an-image-classification-neural-network-with-python-d71c775ccbe5?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/d71c775ccbe5</guid>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[hackpack]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Thu, 20 Nov 2025 20:45:16 GMT</pubDate>
            <atom:updated>2025-11-20T20:45:16.981Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/e6495ae6fbb3">Rebecca Mewse</a></p><p>The use cases of neural networks are growing at an astounding rate. In this post, we will be looking into the inner workings of Neural Networks and how they can be used to build an Image Classification application in Python.</p><h4>What is a Neural Network?</h4><p>At their core, neural networks recognise patterns. They are built from simple units, called “neurons’, organised in layers. Each neuron performs small calculations and passes its output to the next layer.</p><p>The first layer in a neural network is called the “input layer”, this layer takes in the raw data (for us this will be RGB pixel information). Hidden layers then transform that information, ultimately trying to learn patterns and features from the data. Finally, the “output layer” produces a prediction (for us this prediction will be the name of the class that the image belongs to).</p><p>To train a neural network, we show it a very large number of labelled examples, our dataset. We measure how far the predictions are from the correct answer (this is called the “loss”) and adjust the connections between neurons to hopefully decrease the average loss, resulting in a more accurate neural network.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*C50SBaITvrsKpXHH.png" /><figcaption>Structure of a Neural Network with 3 hidden layers</figcaption></figure><p><a href="https://www.ibm.com/think/topics/neural-networks">What Is a Neural Network? | IBM</a></p><h4>Preparation</h4><p>We will need to install certain python packages before we begin. These include: PyTorch, Pandas, NumPy and SkLearn</p><p>Install these packages by opening command line and, in your project repository, running:</p><pre>pip install torch pandas scikit-learn numpy</pre><p>Once you have created a new python file, we need to import all of the packages that we have just installed, so that we are able to use them within this project. As well as importing these packages, we will also be importing specific functions and classes from these packages.</p><pre>import torch<br>from torchvision import transforms<br>from torch.utils.data import Dataset, DataLoader<br>import pandas as pd<br>import torchvision.transforms as transforms<br>import numpy as np<br>import os<br>from sklearn.model_selection import train_test_split<br>import torch.nn as nn<br>import torch<br>import torch.optim as optim<br>import torch.nn.functional as F<br>from PIL import Image<br>from sklearn.metrics import precision_score, recall_score, f1_score</pre><h4>Choosing a dataset</h4><p>Here is where the fun starts!</p><p>For this project, you can either download an existing dataset from online or go one step further and build a custom dataset!</p><p>If you choose to work with an existing dataset, some excellent datasets are listed below:</p><p>MNIST — contains 60,000 training images of handwritten digits 0–9</p><p><a href="https://www.tensorflow.org/datasets/catalog/mnist">mnist | TensorFlow Datasets</a></p><p>CIFAR-10 — contains 5,000 training images per class (10 classes). Classes consists of animals and everyday items (airplane, cat, horse, truck etc…)</p><p><a href="https://www.tensorflow.org/datasets/catalog/cifar10">cifar10 | TensorFlow Datasets</a></p><p>CIFAR-100 — similar structure to CIFAR-10, yet contains training images for 100 classes, grouped into 20 superclasses</p><p><a href="https://www.tensorflow.org/datasets/catalog/cifar100">cifar100 | TensorFlow Datasets</a></p><p>If you choose to create a custom dataset, you will need to source both training and testing photos of each classification (e.g. rock, paper, scissors). To reduce the amount of photos needed, I advise you choose a few simple classes, examples could include:</p><ul><li>Pen, pencil</li><li>Different keyboard keys</li><li>Facial expressions</li><li>Cups, bottles</li><li>Images taken inside, images taken outside</li><li>Hand gestures</li><li>Different fabrics</li><li>Rock, paper, scissors</li></ul><p>The more complex classifications you aim to use, the larger your dataset will need to be! To make the dataset effective, despite a limited size, consider the tips listed below.</p><ul><li>Keep the background simple, if applicable</li><li>Use good lighting</li><li>Ensure the classes look visually different and are easily distinguishable</li><li>Keep the number of photos per class roughly the same</li><li>Capture different angles of each class</li><li>Use the same pixel dimensions for every image</li></ul><p>While collecting these images, all images of each classification should be stored in a separate folder, within a dataset folder. The name of each sub folder should be the name of each classification.</p><h4>Creating our transform object</h4><p>Before we begin loading our images, it is important that we initialise our transform object. This will scale the RGB values in our photos to a range of [-1,1] instead of [0,255]. This is incredibly useful as neural networks converge faster when their input is zero-centered. We will use this transform object later in our project for this reason.</p><pre>transform = transforms.Compose([<br>    transforms.ToTensor(),<br>    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))<br>])</pre><h4>Loading images for training</h4><p>It is now time to load our images! We need to define the path to the folder containing our images. This folder should contain individual sub-folders for each class of images, as previously explained.</p><p>We then define a list for our images, as well as a list of labels. This means that the first label will be the name of the classification of the first image, second label will be the name of the classification of the second image, etc. The example below shows “Rock”, ”Paper”, ”Scissors” being used, however, this should be the list of titles of the classifications you are using (and must match the names of your sub-folders)!</p><pre>train_data_path = &quot;.../dataset&quot; # Path to your dataset<br><br>image_list = []<br>label_list = []<br><br>class_list_ = [&quot;Rock&quot;,&quot;Paper&quot;,&quot;Scissors&quot;] # Our list of classifications<br><br>for category in class_list_:<br>    for image in os.listdir(f&quot;{train_data_path}{category}&quot;):<br>        image_list.append(f&quot;{train_data_path}{category}/{image}&quot;)<br>        label_list.append(f&quot;{category}&quot;)</pre><h4>Define the data frame</h4><p>We now need to define the data frame that our neural network will use to train. To do this, we use the image_list and label_list from earlier.</p><pre>df = pd.DataFrame()<br><br>df[&quot;image&quot;] = image_list<br>df[&quot;label&quot;] = label_list</pre><h4>Split the dataset</h4><p>Our dataset needs to be split into two sections, images to be used for training and images to be used for testing. We can define the ratio that this split will follow.</p><ul><li>TIP: Once you have trained your initial model, you can try adjusting this ratio to see its affect on accuracy!</li></ul><pre>ratio = 0.20 #20% of data will be for testing<br><br>train_df, test_df = train_test_split(df, test_size=ratio, stratify=df[&quot;label&quot;], random_state=42)</pre><h4>Augmenting data</h4><p>Larger datasets can lead to higher levels of accuracy! To make use of this fact, we are going to transform our relatively small data set into a HUGE dataset (without the need to manually collect more images)!</p><p>Here, we are going to first define the image size we want to normalise our images to. Using a smaller number will pixelate the images before they are used for training, this removes some data but will allow us to train our model a lot quicker.</p><p>We the transform each image in both our training ad testing sets into multiple images, applying various transformations. These transformations could include altering any combination of: brightness, contrast, rotation, zoom, saturation etc. This simulates a much larger dataset than we previously had.</p><pre>image_size = 28<br><br>training_transform = transforms.Compose([transforms.Resize((image_size, image_size)),<br>                                            transforms.RandomRotation(10),<br>                                             transforms.ToTensor(),<br>                                         transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])<br>test_transform = transforms.Compose([transforms.Resize((image_size, image_size)),<br>                                             transforms.ToTensor(),<br>                                     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])</pre><h4>Custom Training Data Class</h4><p>This is where the magic truly begins (and we use our transform object from earlier)! We need to define the class that is used for our training data. First, we define BATCH_SIZE, this determines the amount of images processed in each batch of training. Again, feel free to experiment with adjusting the batch size, observing how this can affect accuracy.</p><p>Once we have defined our class, we use it to define our train/ test data object and data loaders.</p><pre>BATCH_SIZE = 10<br><br>class CustomTrainingData(Dataset):<br>    def __init__(self, csv_df, class_list, transform=None):<br>        self.df = csv_df<br>        self.transform = transform<br>        self.class_list = class_list<br>    def __len__(self):<br>        return self.df.shape[0]<br>    def __getitem__(self, index):<br>        try:<br>            image_path = self.df.iloc[index][&quot;image&quot;]<br>            image = Image.open(image_path).convert(&#39;RGB&#39;)<br>        except Exception as e:<br>            return self.__getitem__((index + 1) % len(self.df)) <br>        label = self.class_list.index(self.df.iloc[index][&quot;label&quot;])<br>        if self.transform:<br>            image = self.transform(image)<br>        return image, label<br>    <br>train_data_object = CustomTrainingData(train_df, class_list_, training_transform)<br>test_data_object = CustomTrainingData(test_df, class_list_, test_transform)<br>train_loader = DataLoader(train_data_object, batch_size=BATCH_SIZE, shuffle=True, num_workers=0)<br>test_loader = DataLoader(test_data_object, batch_size=BATCH_SIZE, shuffle=False, num_workers=0)</pre><h4>Early Stopping and Overfitting</h4><p>Overfitting can be a major issue when training a neural network. This is when our model begins to “memorise” training data, rather than learning general patterns that can then be applied to unseen data. As a result, our model ends up performing incredibly well on training data, but poorly on unseen/ testing data. As our model trains, overfitting can be detected by looking for big drops in performance and accuracy.</p><p>Below is an IBM article exploring overfitting in greater detail, including links to current research on the topic:</p><p><a href="https://www.ibm.com/think/topics/overfitting">What is Overfitting? | IBM</a></p><p>Whilst there are many ways to combat overfitting, we are going to explore early stopping. Early stopping is a technique where we will stop training after detecting a noticable drop in performance on our testing data. To implement this, we define the class below — EarlyStopping. This class keeps track of the accuray for each round of training. If the accuracy does not improve within 3 rounds (defined by patience=3), we stop the training and save the most accurate model!</p><pre>class EarlyStopping:<br>    def __init__(self, patience=3, delta=0):<br>        self.counter = 0<br>        self.best_model_state = None<br>        self.patience = patience<br>        self.delta = delta<br>        self.best_score = None<br>        self.early_stop = False<br><br>def __call__(self, val_loss, model):<br>        score = -val_loss<br>        if self.best_score is None:<br>            self.best_score = score<br>            self.best_model_state = model.state_dict()<br>        elif score &lt; self.best_score + self.delta:<br>            self.counter += 1<br>            if self.counter &gt;= self.patience:<br>                self.early_stop = True<br>        else:<br>            self.best_score = score<br>            self.best_model_state = model.state_dict()<br>            self.counter = 0<br>    def load_best_model(self, model):<br>        model.load_state_dict(self.best_model_state)</pre><h4>Neural Network Structure</h4><p>This is core of our project — this is where we define HOW data travels through our neural network. We do this by defining the types and combinations of layers we are using.</p><p>There are many types of layers that each serve a unique purpose. These layers work together to pass data through the neural network in each round of training, hopefully increasing the accuracy each time.</p><p>This GeeksForGeeks article provides a brilliant breakdown of the purpose of different types of layers and is an excellent, informative read…</p><p><a href="https://www.geeksforgeeks.org/deep-learning/layers-in-artificial-neural-networks-ann">Layers in Artificial Neural Networks (ANN) - GeeksforGeeks</a></p><pre>class ImageClassifier(nn.Module):<br>    def __init__(self, num_classes, input_size=(image_size, image_size), channels=3): <br>        super(ImageClassifier, self).__init__()<br><br>        self.input_size = input_size<br>        self.channels = channels<br><br>        # Convolutional layers<br>        self.conv1 = nn.Conv2d(channels, 32, kernel_size=3, padding=1)<br>        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)<br>        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)<br>        self.conv4 = nn.Conv2d(128, 256, kernel_size=3, padding=1)<br><br>        # Batch normalization layers<br>        self.bn1 = nn.BatchNorm2d(32)<br>        self.bn2 = nn.BatchNorm2d(64)<br>        self.bn3 = nn.BatchNorm2d(128)<br>        self.bn4 = nn.BatchNorm2d(256)<br><br>        # Max pooling layer<br>        self.pool = nn.MaxPool2d(2, 2)<br><br>        # Dropout layer<br>        self.dropout = nn.Dropout(0.5)<br><br>        # Calculate the size of the flattened features<br>        self._to_linear = None<br>        self._calculate_to_linear(input_size)<br><br>        # Fully connected layers<br>        self.fc1 = nn.Linear(self._to_linear, 512)<br>        self.fc2 = nn.Linear(512, num_classes)<br>        <br>    def _calculate_to_linear(self, input_size):<br>        # This function calculates the size of the flattened features<br>        x = torch.randn(1, self.channels, *input_size)<br>        self.conv_forward(x)<br><br>    def conv_forward(self, x):<br>        x = self.pool(F.relu(self.bn1(self.conv1(x))))<br>        x = self.pool(F.relu(self.bn2(self.conv2(x))))<br>        x = self.pool(F.relu(self.bn3(self.conv3(x))))<br>        x = self.pool(F.relu(self.bn4(self.conv4(x))))<br><br>        if self._to_linear is None:<br>            self._to_linear = x[0].shape[0] * x[0].shape[1] * x[0].shape[2]<br>        return x<br><br>    def forward(self, x):<br>        x = self.conv_forward(x)<br><br>        # Flatten the output for the fully connected layer<br>        x = x.view(-1, self._to_linear)<br><br>        # Fully connected layers with ReLU and dropout<br>        x = self.dropout(F.relu(self.fc1(x)))<br>        x = self.fc2(x)<br><br>        return x</pre><h4>Hyper Parameters and Initialisation</h4><p>Now that we have defined the layers and structure of our neural network, we need to some more steps before defining our training loop. Below, we define some hyper parameters that are used in this training loop. We define the following:</p><ul><li>Epochs — The number of “rounds” of training</li><li>Learning rate — Determines how quickly the neural network learns from data</li><li>Num Classes — The number of classifications we have (e.g. rock, paper, scissors)</li><li>Input Size — The pixel dimensions of the images we are inputting for training</li><li>Channels — Represents the pieces of data for each pixel (3 for RGB)</li></ul><p>We then check if the program has access to a faster processor (e.g. a GPU), and use this if possible.</p><p>Finally, we initialise the model using our customised ImageClassifier class from earlier (our neural network structure)!</p><pre>EPOCHS = 100<br>LEARNING_RATE = 0.001<br>NUM_CLASSES = len(class_list_)<br>INPUT_SIZE = (image_size, image_size)<br>CHANNELS = 3<br><br>device = torch.device(&#39;cuda&#39; if torch.cuda.is_available() else &#39;cpu&#39;)<br><br># INIT MODEL<br>model = ImageClassifier(NUM_CLASSES, INPUT_SIZE, CHANNELS).to(device)</pre><h4><strong>Loss and Early Stopping</strong></h4><p>The final step before defining our training loop is to define our loss function and implement or early stopping class from earlier. The loss function is the metric that early stopping will use to decide if our model is making progress or should be halted.</p><pre># LOSS FUNCTION<br>criterion = nn.CrossEntropyLoss()<br>optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)<br><br>early_stopping = EarlyStopping()</pre><h4>The Training Loop</h4><p>We have finally reached the point where we can define the loop that will be training our model, bringing together all of our hard work so far! A lot is happening here but below is a summary of each step that this code takes in each epoch of training.</p><ol><li>model.train() passes data through the model, attempting to learn patterns to improve accuracy</li><li>The loss is calculated for that epoch of training and is printed to the console</li><li>The model is evaluated for accuracy</li><li>EarlyStopping decides whether the model is making progress or should be halted.</li><li>The metrics that describe the current accuracy and performance of the model are calculated and printed to the console.</li></ol><p>Once the training loop ends — meaning either 100 epochs have run or early stopping has halted the training — the program lets us know by printing “Training finished!” to the console.</p><pre># Training loop<br>for epoch in range(EPOCHS):<br>    model.train()<br>    running_loss = 0.0<br>    for images, labels in train_loader:<br>        images = images.to(device)<br>        labels = labels.to(device)<br><br>        optimizer.zero_grad()<br>        outputs = model(images)<br>        loss = criterion(outputs, labels)<br>        loss.backward()<br>        optimizer.step()<br><br>        running_loss += loss.item()<br><br>    print(f&#39;Epoch [{epoch+1}/{EPOCHS}], Loss: {running_loss/len(train_loader):.4f}&#39;)<br>    <br>    # Validation<br>    model.eval()<br>    all_predictions = []<br>    all_labels = []<br>    with torch.no_grad():<br>        for images, labels in test_loader:<br>            images = images.to(device)<br>            labels = labels.to(device)<br>            outputs = model(images)<br>            _, predicted = torch.max(outputs.data, 1)<br>            all_predictions.extend(predicted.cpu().numpy())<br>            all_labels.extend(labels.cpu().numpy())<br>        early_stopping(running_loss, model)<br>        if early_stopping.early_stop:<br>            print(&quot;Early stopping&quot;)<br>            break<br>    early_stopping.load_best_model(model)<br>    <br>    # Calculate metrics<br>    accuracy = 100 * sum(np.array(all_predictions) == np.array(all_labels)) / len(all_labels)<br>    precision = precision_score(all_labels, all_predictions, average=&#39;weighted&#39;)<br>    recall = recall_score(all_labels, all_predictions, average=&#39;weighted&#39;)<br>    f1 = f1_score(all_labels, all_predictions, average=&#39;weighted&#39;)<br><br>    # Print metrics<br>    print(f&#39;Epoch [{epoch+1}/{EPOCHS}]&#39;)<br>    print(f&#39;Accuracy on test set: {accuracy:.2f}%&#39;)<br>    print(f&#39;Precision: {precision:.4f}&#39;)<br>    print(f&#39;Recall: {recall:.4f}&#39;)<br>    print(f&#39;F1 Score: {f1:.4f}&#39;)<br><br>print(&#39;Training finished!&#39;)</pre><h4>Saving the Model</h4><p>Congrats!! You have succesfully built a neural network in python that classifies images! Now we simply save the model and then it can be used to predict the classification of new images.</p><pre># Save the model<br>torch.save(model.state_dict(), &#39;classifier.pth&#39;)</pre><h4>Making Predictions — The Result!</h4><p>Finally, our hard work has paid off! In a new file, we can use the below code to use our model to make predictions on new images. We have successfully built an image classification neural network, which can now be incorprated into larger projects. Well done!</p><pre># LOAD THE MODEL<br><br>model = ImageClassifier(num_classes=3, input_size=(128, 128))<br>model.load_state_dict(torch.load(&#39;classifier.pth&#39;))<br>model.eval()  # Evaluation Mode<br><br># PREPARE THE IMAGE<br><br>img = Image.open(&#39;test.jpg&#39;)  # Load a new, unseen image from your file system<br><br>transform = transforms.Compose([<br>    transforms.Resize((128, 128)),<br>    transforms.ToTensor()<br>])<br>image_tensor = transform(img).unsqueeze(0)<br><br># MAKE PREDICTION<br><br>with torch.no_grad():<br>    outputs = model(image_tensor)<br>    _, predicted = torch.max(outputs, 1)<br>    prediction = class_names[predicted.item()]<br><br>print(f&quot;Predicted class: {prediction}&quot;) # Outputs the predicted classification of the unseen image</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d71c775ccbe5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HackPack: Godot for DurHackers]]></title>
            <link>https://medium.com/@DurHack_press/hackpack-godot-for-durhackers-5cf28c93461f?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/5cf28c93461f</guid>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[hackpack]]></category>
            <category><![CDATA[game-development]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Thu, 20 Nov 2025 20:44:49 GMT</pubDate>
            <atom:updated>2025-12-14T15:01:42.148Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7piOs7JOX2DPS7GqItd6ow.png" /></figure><p>Written by <a href="https://medium.com/u/80c483f4c085">Oscar Ryley</a></p><h4>Contents</h4><ul><li><a href="#d16b">Why Godot?</a></li><li><a href="#372f">Getting Started</a></li><li><a href="#3aaa">Creating your Environment</a></li><li><a href="#cb8e">Get Moving in Godot!</a></li><li><a href="#c7ea">Troubleshooting your Project</a></li><li><a href="#8de9">Game Development Inspiration</a></li></ul><h3><strong>Why Pick Godot for Game Development?</strong></h3><p>Godot is a free, open-source game engine that can be used to make 2D and 3D games and experiences; it has risen in popularity since the removal of support for Adobe Flash at the beginning of 2021 and the Unity payment change announced in September 2023 that would charge creators per installation of their games.</p><p>Godot is a wonderful resource for someone wanting to get into Game Development; it has actively updated documentation and a growing number of success stories, including ‘Brotato’ which was nominated for the Steam Award for “Best Steam Deck Game” in 2023.</p><p>If you want to pick up Game Development for a Hackathon or learn more about it through a Hackathon project I would highly recommend it. Godot is great for beginners and professionals alike, keep reading to learn more about how to get started with Godot and start thinking about what game you’re going to make.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*il1RiSj5RNqAKmPbacr99Q.png" /><figcaption>Xsolla’s Game Engine comparison chart from <a href="https://xsolla.com/blog/which-game-engine-is-best-for-you">this article</a></figcaption></figure><p>It is clear from this Chart that someone starting in Independent Game Development would choose to learn the Godot Game Engine.</p><h4>Do you need to have lots of Programming Experience to get started with Godot?</h4><p>Whilst Godot isn’t one of the few no/low code platforms (eg. Scratch) for Game development, it is very accessible for first time programmers.</p><p>And if you have programming experience, it’s still a fun challenge.</p><p>Godot is interesting in the fact that it uses its own, custom-built, programming language called GDScript. This is a Pythonic Language that incorporates the Object-Oriented nature of C# for the necessity of how Game Design Works.</p><p>If you have any previous knowledge in Python, or in Object-Oriented Programming (in C# or otherwise) then GDScript will look quite familiar to you. If you don’t, or you’d like to start with a refresher, here are some resources for both <a href="https://www.pythoncheatsheet.org/">Python</a> and <a href="https://www.thecodingguys.net/resources/cs-cheat-sheet.pdf">C#</a>.</p><h4>GDScript</h4><p>The Basics of GDScript can be found on <a href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html">the following page of the documentation</a>. This Example syntax is a great start to begin understanding how your program code will look in Godot’s Scripts and for each Node.</p><p>If you don’t feel like having to learn a new language for your project, you can develop Godot Games using C#, but I would recommend giving GDScript a chance before you do.</p><h3>Getting started with Godot</h3><p>You can download the latest version of Godot from their website here: <a href="https://godotengine.org/">https://godotengine.org/</a></p><p>On this site you can also find examples of projects made by the Godot community and the latest updates about how to use and what is in the current version of Godot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HvmkUVgdKnORx_GDohYIfA.png" /><figcaption><a href="https://godotengine.org/">https://godotengine.org/</a></figcaption></figure><h4>Your first Godot Project</h4><p>When you first open Godot, a window that looks like this will open up, start by clicking the button in the top left corner to create a new project!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*PtQFR6lKV6bkm2FZRl06VA.png" /><figcaption>Godot Project Manager Window</figcaption></figure><p>You’ll then see this window where you can name your new project, choose the folder where the Godot Project will be stored and be able to choose the renderer.</p><p>I’d recommend Forward+ or Compatibility for a Desktop Project that can be easily put onto Itch.io so that people can play it for a Hackathon. but you can also make Mobile Apps with Godot as seen below.</p><p>I’d recommend keeping the version control as Git, and using GitHub Desktop to save your project and collaborate with your team. You can learn more about GitHub and IDEs with <a href="https://medium.com/@DurHack_press/hackpack-getting-started-with-github-ides-34aeb6cbffcc">our other HackPack by Kaal Sahele</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/543/1*9t84PZhHZhnuvvCy9oLRRg.png" /><figcaption>Godot Create New Project Window</figcaption></figure><p>Press Create &amp; Edit to Start creating with Godot.</p><p>After starting your blank project you’ll be greeted by this screen, your workspace.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QV91u5N9wkuB9B4FukG9rg.png" /><figcaption>Godot Engine HUD</figcaption></figure><p>You can begin by either making a 2D or a 3D Scene (Click your option on the panel on the left), for more information on how to begin your 2D or 3D Game, go to the 2D vs 3D Section of this HackPack!</p><p>When you first start your <strong>Scene </strong>(2D is this instance)<strong> </strong>you’ll see a blank project that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YLgzJ8JXK9Iu9Ji7nwiR6g.png" /><figcaption>A 2D Scene, Godot Engine HUD</figcaption></figure><p>You can then press the plus button under ‘Scene’ in the top left corner to add new <strong>Nodes</strong> to the scene. Godot’s system is based on <strong>Scenes </strong>that are made up of these <strong>Nodes</strong> and the <strong>Scripts</strong> which you then attach to them.</p><h3>Creating your Environment</h3><p><strong>The big question, do you want to make a 2D or 3D Game?</strong></p><p>Godot supports both 2D and 3D game development, so this will be the first thing you’ll need to decide on before you start creating your first game with Godot. Either way, I’d suggest beginning with some basic player movement so that you have something built that you can modify from there.</p><p>The following are two tutorials, one for creating your initial scene for 2D and the other for 3D. I’ve then linked to my video workshop/ GitHub on Godot Movement that you can follow at the beginning stages of your project.</p><h3>2D</h3><p>The 2D side of Godot is also far more approachable than immediately going into 3D project development. If you’d prefer to use the documentation you can start with <a href="https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html">the basic 2D Godot game tutorial from the documentation</a>. For now, I’ll walk you through <a href="https://github.com/Oscar-Ryley/Get-Moving-with-Godot-Workshop?tab=readme-ov-file">the 2D elements of my Godot Movement tutorial from GitHub</a>/ DurJam workshop.</p><p>After starting your main 2D scene, create a seperate scene named ‘Character’ — you’ll need to create an instance of this scene in your main scene.</p><p>Add nodes to this scene so that it has the following structure:</p><ul><li>Node2D (‘Character’)<br>- CharacterBody2D<br> - CollisionShape2D<br> - Sprite2D</li></ul><p>Go to Project Settings -&gt; Input Map (2nd tab) and add the following actions, pressing the + button to attach keys to them, “Left”, “Right”, “Up”, “Down”. Traditionally WASD or the arrow keys are used, but sometimes the space bar acts as the jump button in 2D platformers, where your up direction is a jump and down is for crouching.</p><p>Attach a script to the Character’s CharacterBody2D Node, and add the following GDScript Code:</p><pre>extends CharacterBody2D<br><br>@export var speed = 400<br>@export var jump_velocity = -800 # Jumping, for projects with gravity<br>@export var gravity = 2000 # Gravity downward force<br><br>func get_input():<br> var input_direction = Input.get_vector(&quot;Left&quot;, &quot;Right&quot;, &quot;Up&quot;, &quot;Down&quot;)<br> velocity.x = input_direction.x * speed<br> # velocity.y = input_direction.y * speed <br>    # Up &amp; Down movement, for projects without gravity<br><br> # Gravity, for projects like 2D platformers<br> if (Input.is_action_just_pressed(&quot;Jump&quot;) <br>             or Input.is_action_just_pressed(&quot;Forward&quot;)) and is_on_floor():<br>  velocity.y = jump_velocity<br><br>func _physics_process(delta):<br> # Handling a constant Gravity force<br> if not is_on_floor():<br>  velocity.y += gravity * delta<br><br> get_input()<br> move_and_slide()</pre><h3>3D</h3><p>I personally started with 3D in Godot with <a href="https://www.youtube.com/watch?v=v4IEPi1c0eE">“Tutorial: First Person Movement In Godot 4”</a> by the channel ‘Bramwell’. If you’d prefer to use the documentation to design your first 3D movement, you can <a href="https://docs.godotengine.org/en/stable/getting_started/first_3d_game/03.player_movement_code.html">find that page here</a>. For now, I’ll walk you through <a href="https://github.com/Oscar-Ryley/Get-Moving-with-Godot-Workshop?tab=readme-ov-file">the 3D elements of my Godot Movement tutorial from GitHub</a>/ DurJam workshop.</p><p>After starting your main 3D scene, create a seperate scene named ‘Character’ — you’ll need to create an instance of this scene in your main scene.</p><p>Add nodes to this scene so that it has the following structure:</p><ul><li>Node3D (‘Character’)<br>- CharacterBody3D<br> - MeshInstance3D<br> - CollisionBody3D<br>- Node3D (‘Neck’)<br> - Camera3D</li></ul><p>Go to Project Settings -&gt; Input Map (2nd tab) and add the following actions, pressing the + button to attach keys to them, “Left”, “Right”, “Forward”, “Back”, “Jump”. Traditionally people use WASD and space bar for these, but you can change them depending on what type of game you’re making.</p><p>You’ll need a surface in the 3D Scene to walk on, so add a surface mesh, MeshInstance3D with a cubic StaticBody3D &amp; CollisionShape3D to stop your character from falling into the void. Use the following structure:</p><ul><li>MeshInstance3D<br>- StaticBody3D<br>- CollisionShape3D</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/816/1*9c6V12VBAYOSUpsBFriKMA.png" /><figcaption>A basic 3D Environment in Godot</figcaption></figure><p>Attach a script to the Character’s CharacterBody3D Node, and add the following GDScript Code:</p><pre>extends CharacterBody3D<br><br>const speed = 7.0<br>const jump_velocity = 5.0<br>var gravity = ProjectSettings.get_setting(&quot;physics/3d/default_gravity&quot;)<br><br><br>@onready var neck := $Node3D<br>@onready var camera := $Node3D/Camera3D<br><br>func _unhandled_input(event: InputEvent) -&gt; void:<br> if event is InputEventMouseButton:<br>  Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)<br><br> elif event.is_action_pressed(&quot;ui_cancel&quot;):<br>  Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)<br><br> if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:<br>  if event is InputEventMouseMotion:<br>   neck.rotate_y(-event.relative.x * 0.002)<br>   camera.rotate_x(-event.relative.y * 0.002)<br>   camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-90),  <br>                              deg_to_rad(60))<br><br><br>func _physics_process(delta): <br> if not is_on_floor():<br>  velocity.y -= gravity * delta<br><br> if Input.is_action_just_pressed(&quot;Jump&quot;) and is_on_floor():<br>  velocity.y = jump_velocity<br><br> var input_dir = Input.get_vector(&quot;Left&quot;, &quot;Right&quot;, &quot;Forward&quot;, &quot;Back&quot;)<br> var direction = (neck.transform.basis <br>                    * Vector3(input_dir.x, 0, input_dir.y)).normalized()<br> if direction:<br>  velocity.x = direction.x * speed<br>  velocity.z = direction.z * speed<br> else:<br>  velocity.x = move_toward(velocity.x, 0, speed)<br>  velocity.z = move_toward(velocity.z, 0, speed)<br><br> move_and_slide()</pre><h3>Get Moving with Godot Workshop</h3><p>You can check out the full video of the above tutorials here:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FMCJwWjI3ezE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DMCJwWjI3ezE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FMCJwWjI3ezE%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/752dbb3d99c16e03fa34cf61b350a1aa/href">https://medium.com/media/752dbb3d99c16e03fa34cf61b350a1aa/href</a></iframe><p>All of the necessary code, as well as an archive of the Godot project files for 2D movement, 3D movement, and the Demo game can be found on the GitHub repository:</p><p><a href="https://github.com/Oscar-Ryley/Get-Moving-with-Godot-Workshop">GitHub - Oscar-Ryley/Get-Moving-with-Godot-Workshop: Godot Workshop for DurJam 2025</a></p><h4>Demo Game</h4><p>You can play the demonstration project from the workshop from your browser on the Itch.io page:</p><p><a href="https://oryley.itch.io/godot-workshop">Get Moving with Godot!</a></p><p>The Demo Game implements both 2D and 3D movement, with a 2D and 3D scene that you can toggle between to play different takes on the same game concept.</p><p>I sourced my 3D textures from <a href="https://polyhaven.com/">Polyhaven</a>, a great free resource for creating projects quickly at a Hackathon! I’d recommend using <a href="https://www.youtube.com/watch?v=K7a4hDRxYu8">this YouTube tutorial</a> on textures and meshes.</p><h3>Troubleshooting your Godot Project</h3><p><a href="https://docs.godotengine.org/en/stable/index.html">The Godot documentation</a> is regularly updated for each engine version and is open to public contributions in order to keep it as accurate and up to date as possible.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xeP2DwGaE0u8PtUp5Nj2pA.png" /><figcaption>Contribution data from the Godot docs</figcaption></figure><p>Like most programming projects, as you work with the Godot Engine you will have questions that relate to how to achieve specific things. When I have done Google searches for some of my specific questions I have found good answers via the following resources:</p><ul><li>Quora (eg. <a href="https://www.quora.com/What-are-the-benefits-of-using-the-Godot-game-engine-over-Unity-or-Gamemaker">https://www.quora.com/What-are-the-benefits-of-using-the-Godot-game-engine-over-Unity-or-Gamemaker</a>)</li><li>The Godot reddit, r/godot (<a href="https://www.reddit.com/r/godot/">https://www.reddit.com/r/godot/</a>)</li><li>The Godot Documentation’s Forum (<a href="https://forum.godotengine.org/">https://forum.godotengine.org/</a>)</li><li>YouTube Tutorials, examples can be found throughout this Hack Pack.</li></ul><h3>Inspiration</h3><p>Need help getting inspired for your game? Firstly I would encourage you to think about the kind of games that you enjoy playing. Next, take a look at the following incredibly successful games made with Godot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qqCyH5pgvquHN37ar2dYjQ.png" /><figcaption>Brotato — <a href="https://store.steampowered.com/app/1942280/Brotato/">https://store.steampowered.com/app/1942280/Brotato/</a></figcaption></figure><p>Brotato is a top-down run and gun roguelite game developed by Blobfish. You play as a Potato that wields up to 6 weapons and defeats waves of enemies. It sold over a million copies during its early access and was nominated for “Best game on Steam Deck” in the 2023 Steam Awards.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/616/1*j6y6ARi4Th9rZM_YOWSk9g.jpeg" /><figcaption>Endoparasitic — <a href="https://nartier.itch.io/endoparasitic">https://nartier.itch.io/endoparasitic</a></figcaption></figure><p>Endoparasitic is a top-down survival horror game where you control your movement with only one arm. You can check out <a href="https://www.youtube.com/@Miziziziz/">the dev’s YouTube</a> for more Game Development inspiration. An Endoparasitic 2 has also been released that you can have a look at both on <a href="https://nartier.itch.io/endoparasitic-2">itch.io</a> and <a href="https://youtu.be/orbJ6-Hd0n8">their channel</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/460/1*RgSqbf-fTUMRFB6OdYdQsA.jpeg" /><figcaption>Cruelty Squad — <a href="https://store.steampowered.com/app/1388770/Cruelty_Squad/">https://store.steampowered.com/app/1388770/Cruelty_Squad/</a></figcaption></figure><p>Cruelty Squad is a tactical first person shooter with an extreme low-poly aesthetic (WARNING: This game is not recommended for people with photosensitive epilepsy).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cSVgfk14FXT1UYKJrn6EKw.jpeg" /><figcaption>Sonic Colours: Ultimate — <a href="https://store.steampowered.com/app/2055290/Sonic_Colors_Ultimate/">https://store.steampowered.com/app/2055290/Sonic_Colors_Ultimate/</a></figcaption></figure><p>Sonic Colours: Ultimate is a remaster of Sonic Colours (2010). An official Sega property being developed using the open-source Godot Engine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VY-I13-xqvARH03FxeX9ug.png" /><figcaption>Videos highlighting games and projects made with Godot — <a href="https://godotengine.org/showcase/">https://godotengine.org/showcase/</a></figcaption></figure><p>You can check out the latest releases and Showreels of games made with the Godot Engine on <a href="https://godotengine.org/showcase/">Godot’s showcase page</a>! I’ve also made a few games, currently all created using Godot and <a href="https://oryley.itch.io/">available on my Itch.io page</a>.</p><blockquote>I’m happy to help! Whether it be Game Dev in Godot, or for other Hackathon projects. So feel free to come and talk to me at DurHack (or any other Hackathon)— Oscar :)</blockquote><p><em>Sections of this Article were originally published as a HackPack for DurHack 2024 on Nov 3, 2024; and as a Tutorial &amp; Workshop for DurJam 2025 on Jun 18, 2025.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5cf28c93461f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HackPack: Sentiment Analysis (With Python)]]></title>
            <link>https://medium.com/@DurHack_press/hackpack-sentiment-analysis-with-python-bfc98ec0cdfd?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/bfc98ec0cdfd</guid>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[hackpack]]></category>
            <category><![CDATA[sentiment-analysis]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Sat, 01 Nov 2025 09:32:43 GMT</pubDate>
            <atom:updated>2025-11-01T09:32:43.309Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/502039aef08a">Andrea Johnson</a></p><p>Welcome to this guide to sentiment analysis using Python! This is made for beginners and advanced programmers alike, so feel free to grab what you need, or read through the explanations for more help. We’ll go through what you need to set up and building your own sentiment classifier.</p><p>The code snippets used here are taken from the Jupyter Notebook version of this tutorial available <a href="https://github.com/TwelveSilverCrows/SentimentAnalysisHackPack">here</a>.</p><h4>1. Introduction</h4><p><strong>What is Sentiment Analysis?</strong></p><p>Sentiment analysis is a natural language processing (NLP) technique used to determine whether data is positive, negative, or neutral.</p><p>It’s commonly used to understand customer feedback, monitor social media, and analyse survey results.</p><p>In this guide, we’ll:</p><ul><li>Clean and prepare text data</li><li>Perform basic sentiment analysis using prebuilt tools</li><li>Build a machine learning model to classify sentiment</li><li>Test it with real and custom data</li></ul><p>Let’s get started!</p><h4>2. Prerequisites</h4><p><strong>We’ll use the following Python libraries:</strong></p><ul><li>pandas, numpy – for data manipulation</li><li>nltk, TextBlob, or VADER – for NLP tasks</li><li>sklearn – for machine learning</li><li>matplotlib, seaborn, wordcloud – for visualisation</li></ul><pre># Environment Setup - if you&#39;re working in the Juypter notebook, you&#39;ll only need to run this once<br>!pip install pandas numpy matplotlib seaborn nltk textblob scikit-learn wordcloud<br><br>import pandas as pd<br>import numpy as np<br>import matplotlib.pyplot as plt<br>import seaborn as sns<br>from wordcloud import WordCloud<br>import nltk<br>from textblob import TextBlob<br>from sklearn.model_selection import train_test_split<br>from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer<br>from sklearn.naive_bayes import MultinomialNB<br>from sklearn.metrics import accuracy_score, classification_report, confusion_matrix<br>#NLTK resources<br>nltk.download(&#39;punkt&#39;)<br>nltk.download(&#39;stopwords&#39;)</pre><h4>3. Load Dataset</h4><p>Use the file from the github repo linked above entitled ‘training.1600000.processed.noemoticon.csv’ if you’re just trying to play around with this, but you can always replace it with your data later on!</p><pre># this is just a bunch of tweets, but you can replace this with a file path to a csv file containing other data<br>df = pd.read_csv(&quot;training.1600000.processed.noemoticon.csv&quot;, encoding=&#39;latin1&#39;) #NB: the first line of the file should be the header for each row<br>df.head()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*xmhTmlQcvWl8XOco.png" /><figcaption>Example screenshot of the output of this code</figcaption></figure><h4>4. Explore the Dataset</h4><p>We’re checking for missing values in the first line and the second makes a bar chart to see how many data points are positive/negative. Getting a sense of the balance in your data set is important — if you use it to predict or train a model using an unbalanced set, it could skew your results!</p><p>For our data set, the sentiment rating works like this: (0 = negative, 2 = neutral, 4 = positive). You should see that there are mostly negative tweets in this dataset.</p><pre>df.info() #check for missing values<br>df[&#39;sentiment&#39;].value_counts().plot(kind=&#39;bar&#39;, title=94&#39;Sentiment Distribution&#39;) # this produces a bar chart</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/685/0*CKKTjFcLxGBS44Bq.png" /><figcaption>Bar graph showing negative (0) vs positive (4) data points</figcaption></figure><h4>5. Text Cleaning and Preprocessing</h4><p>Before we can do any analysis, we have to ‘clean’ the data — remove anything irrelevant. Here, we’re making everything lowercase, removing any links and stemming. Stemming means reducing words to their root form so suffixes like -ing don’t have an unwanted effect on our model.</p><pre># Text Cleaning + Preprocessing<br>import re<br>from nltk.corpus import stopwords<br>from nltk.stem import PorterStemmer<br><br>stop_words = set(stopwords.words(&#39;english&#39;))<br>stemmer = PorterStemmer()<br><br>def clean_text(text):<br>    text = text.lower()<br>    text = re.sub(r&#39;http\S+|@\w+|#\w+|[^a-z\s]&#39;, &#39;&#39;, text)<br>    tokens = nltk.word_tokenize(text)<br>    tokens = [stemmer.stem(word) for word in tokens if word not in stop_words]<br>    return &quot; &quot;.join(tokens)<br><br>df[&#39;clean_text&#39;] = df[&#39;text&#39;].apply(clean_text)<br>df[[&#39;text&#39;, &#39;clean_text&#39;]].head()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/0*mwXh7UHUu9DH5NU7.png" /></figure><h4>6. Word Cloud</h4><p>We can make a Word Cloud to see what words are the most frequent, which can tell us what people are interested in!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*_OShlzC9mRLIMKH3.png" /></figure><h4>7. Sentiment Analysis with TextBlob</h4><p>TextBlob is a library in python that helps with NLP tasks. In the function below, for each bit of text, TextBlob assigns a polarity score where &gt;0 is positive, &lt;0 is negative and 0 is neutral.</p><pre>def get_sentiment(text):<br>    analysis = TextBlob(text)<br>    if analysis.sentiment.polarity &gt; 0:<br>        return &#39;positive&#39;<br>    elif analysis.sentiment.polarity == 0:<br>        return &#39;neutral&#39;<br>    else:<br>        return &#39;negative&#39;<br><br>df[&#39;blob_sentiment&#39;] = df[&#39;text&#39;].apply(get_sentiment)<br>df[[&#39;text&#39;, &#39;blob_sentiment&#39;]].head()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/553/0*QpR0inrFvOuYHlgL.png" /></figure><h4>8. Machine Learning Based Classifier</h4><p>We can train an ML model using labelled data.</p><ol><li>Convert cleaned text into numeric form using TfidfVectorizer</li><li>Split data into testing/training sets</li><li>Train a model on the data</li><li>Evaluate the model</li></ol><p>This is useful if we need our program to capture specific patterns from our data and adapt to context.</p><pre>df[&#39;label&#39;] = df[&#39;blob_sentiment&#39;].map({&#39;positive&#39;: 1, &#39;neutral&#39;: 0,&#39;negative&#39;: -1})<br>X_train, X_test, y_train, y_test = train_test_split(df[&#39;clean_text&#39;], df[&#39;label&#39;], test_size=0.2, random_state=42)<br>vectorizer = TfidfVectorizer()<br>X_train_vec = vectorizer.fit_transform(X_train)<br>X_test_vec = vectorizer.transform(X_test)<br>print(X_train_vec.shape)<br>print(X_test_vec.shape)<br>model = MultinomialNB()<br>model.fit(X_train_vec, y_train)<br>y_pred = model.predict(X_test_vec)<br><br>print(&quot;Accuracy:&quot;, accuracy_score(y_test, y_pred))<br>print(confusion_matrix(y_test, y_pred))<br>print(classification_report(y_test, y_pred))</pre><h4>9. Test with Custom Text</h4><pre>def predict_sentiment(text):<br>    cleaned = clean_text(text)<br>    vec = vectorizer.transform([cleaned])<br>    pred = model.predict(vec)[0]<br>    return &quot;Positive&quot; if pred == 1 else &quot;Negative&quot;<br><br># Try it!<br>predict_sentiment(&quot;I really hate this product, it&#39;s terrible!&quot;)</pre><h4>💡 Applications of Sentiment Analysis</h4><ul><li>📦 Product Reviews: Understand customer satisfaction</li><li>💬 Social Media: Monitor public opinion</li><li>🧠 Market Research: Identify trends</li><li>🏛️ Politics: Gauge speech or policy reception</li><li>📰 News: Understand article tone</li></ul><h4>🧭 Next Steps</h4><ul><li>Explore deep learning models like LSTM or BERT</li><li>Try multilingual datasets</li><li>Learn about aspect-based sentiment analysis</li></ul><h4>Resources:</h4><ul><li><a href="https://textblob.readthedocs.io/">https://textblob.readthedocs.io/</a></li><li><a href="https://github.com/cjhutto/vaderSentiment">https://github.com/cjhutto/vaderSentiment</a></li><li><a href="https://ai.stanford.edu/~amaas/data/sentiment/">https://ai.stanford.edu/~amaas/data/sentiment/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bfc98ec0cdfd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HackPack: Getting Started with GitHub + IDEs]]></title>
            <link>https://medium.com/@DurHack_press/hackpack-getting-started-with-github-ides-34aeb6cbffcc?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/34aeb6cbffcc</guid>
            <category><![CDATA[hackpack]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[hackathons]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Fri, 31 Oct 2025 12:01:02 GMT</pubDate>
            <atom:updated>2025-10-31T12:01:02.290Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/2f1611ec4842">Kaal S</a></p><p>Picture this:</p><p><em>The time is 23:59 — you have a minute to submit your coursework. You’ve spent months on this coursework, you pushed through the days and battled through the night, but it’s done. All that’s left is to submit.</em></p><p><em>So we’re just going to submit “Coursework_(final)”….</em></p><p><em>or was it “Coursework_(final)(final)” ….</em></p><p><em>or was it “Coursework_(FINAL VERSION SUBMIT THIS ONE)”…</em></p><p><em>maybe “Coursework_(NO DON’T SUBMIT THAT ONE, SUBMIT THIS ONE)”</em></p><p><em>But it’s too late. It’s midnight — time is up.</em></p><p>Well, fear not. Those days are over. There’s a bright new dawn on the horizon, and it’s name? Version control. Better yet: version control and software development with GitHub.</p><h3><strong>1 — Installation</strong></h3><h4><strong>1.1 — Create an account</strong></h4><p>If you don’t already have a GitHub account, go to GitHub’s website (<a href="https://github.com/">https://github.com/</a>) and sign up for a free account.</p><p>There may be an option to set up a student account but this will ask for verification so the best option for now is to just make a free one and then upgrade it later once you’re familiar with the workings of Git.</p><h4>1.2 — Install Git</h4><p><strong><em>For a Windows</em></strong></p><p>GitHub uses Git for version control. This is what does all the tracking of changes in code and then GitHub is a web-based hosting service to host these Git works. You can’t use GitHub without Git (hence the name).</p><p>Download Git from their website: <a href="https://git-scm.com/downloads">https://git-scm.com/downloads</a></p><p>Then click “Next” on all the pop up pages until it starts installing.</p><p><strong><em>For a Mac</em></strong></p><p>Unfortunately, if you’re on a Mac, you have some extra steps. The easiest way to do this is via homebrew:</p><p>Click the link on the website for homebrew:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*uMqRhp5YoRa6LrIA.png" /></figure><p>That will take you to this page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*iSRFo7HiOzf3hBcH.png" /></figure><p>Copy that line of code.</p><p>Then head to your launch pad and search for “terminal”. It will open up a scary looking application, but I promise it’s not scary. Paste that line of code into your terminal and press enter.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*U6vwwQrlTiPAqKuF.png" /></figure><p>When you type in your password, <strong>it won’t show the characters being typed but this is for security reasons, it is in fact typing what you put in, so as soon as you type in your password, just press enter.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*ilPfiFOv_xUATPDR.png" /></figure><p>Run the two lines it tells you to run — first, run line 1, then run line 2. Then you will have successfully installed brew. Hooray — you just installed the thing that lets you install git!</p><p>Now that you have brew, you can run these three beautiful words:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*rD8JrxRuz1QAlGy1.png" /></figure><p>And now you sit back and watch it work its beautiful magic and install git!</p><h4>1.3 — Set Up Git</h4><p>After installation, configure your Git identity by opening a terminal and entering the following commands:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*beSGEqpw3hYiPdiN.png" /></figure><p>Make sure you use the username and email that you created your github account with!</p><h3>2 — Creating Your First Repository</h3><h4>2.1 — Create a New Repository on GitHub</h4><p>Log in into your GitHub account and find the the “+” in the upper right corner and select “New Repository”:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*-tWzzTRrZl-n6xd6.png" /></figure><p>You’ll be directed to a page that asks you to fill out some information, such as the name of the repo, whether you want it to be private or public etc.</p><p>For future reference, if you ever use GitHub for your university projects, <strong>always ensure they are private.</strong> If they are left public and a student in the future plagiarises off of you, you will be in equal trouble (!!)</p><h4>2.2 — Clone the Repository</h4><p><strong>The following is an example of a repo I made. Use your own repo that you just made in the previous step for all of the following.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*JqyQopHQzIwu0p0B.png" /></figure><p>Note: If you are on Mac, choose SSH instead of HTTPS!</p><p>Then you can clone this repo by heading back to your terminal and copying in the following lines of shell script:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*X8_pulkkJeSeqkus.png" /></figure><p>As such:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*HBPnn7y0ijO4xqnp.png" /></figure><h4>2.3 — Navigate to the Repository</h4><p>Then use the cd command to navigate to your repository’s directory in your files:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*Agvocg3PzN0rI-rf.png" /></figure><h3>3 — Using your repo from your IDE</h3><h4>3.1 Using Your IDE</h4><p>IDEs are integrated development environments and, while aren’t crucial, make programming and software development projects so much easier. It’s practically impossible to code without them.</p><p>The most commonly used one is VS, which can integrate your GitHub repo to keep track of your changes.</p><p>First open VS code and locate your cloned repository and open it by going to:</p><p>File &gt;&gt; Open Folder &gt;&gt; (find your folder) &gt;&gt; Select Folder</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*uaxqw-Tun7sQ7eBC.png" /></figure><p>Then whenever you make edits to files in this cloned repo via the IDE, it will show up via green and red indicators (indicating added lines and deleted lines):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*9hUs1eWl37w8h8Fk.png" /></figure><p>The <strong>M </strong>here indicates modified. If you add a new file, you would see <strong>U</strong> for untracked, which means it’s a new file that hasn’t been added to your git space yet:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*J9RoQ1pjqmAw_Tfv.png" /></figure><h4>3.2 — Git Commit</h4><p>To commit your new code you’ll need to follow the same steps every time:</p><p>1 — Make sure you are in the directory of your clond repo</p><p>2 — Git add [file name]</p><p>3 — Git commit -m [message]</p><p>4 — Git push</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*WVV_bd1F69Yk19xB.png" /></figure><p>These changes will register in your repo as such:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*lS_AmC7ZfZ6kVKW8.png" /></figure><p>You can actually see exactly what changed in a previous commit if you click on it. This is pretty useful as it allows for inspection of what’s changed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*w0QbanOkrp5kW6Zn.png" /></figure><h4>3.3 — Git Branch</h4><p><strong><em>When team work becomes the dream work</em></strong></p><p>Say you’re project is a web app, and you have someone working on front end and someone working on back end and you don’t want to mess up the working prototype you have in your main branch. You can start two separate branches for front and backend. This essentially makes a copy of everything in the main branch for you to toy around with without affecting the main branch!</p><p>If you want to make changes on your local machine, you can use branches to work on separate features or fixes.</p><p>You can create a new branch by:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*Q7LjoSB6PmCgjgVT.png" /></figure><p>Alternatively, you can re-clone the repository to your local PC (this only works for very small project — as soon as they become larger, this gets very impractical).</p><p>So now your project has the main branch, and the new branch. Once you’ve finished toying around with your version, you can <strong>merge back</strong> to the main branch.</p><p><strong>If you’re working on a branch, any commits made to the main branch will not appear in your branch. It’s very important that when you work on a branch, you don’t work on it for too long so it doesn’t end up to far behind the main branch that it causes conflicts when you try and merge your changes back.</strong></p><p>Find more information on branching <a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches">here</a>..</p><h4>3.4 — Merging</h4><p>Once you’ve made changes to your branch/ local copy, you’ll need to merge it back with the main branch. You can do so with:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*mM-GradAqiMn3C0e.png" /></figure><p>If you’re only working from the one main branch, you don’t even need to specify the branch name, just use “git merge”.</p><p>Then to update your local repository with changes made on GitHub, use</p><p>Just ensure you’re in the directory of your repo and type the following command:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*3P5bdEVTFdp92Gbl.png" /></figure><p>You’ll be able to see all the pull requests for a repo by selecting that tab:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*hdMfUaUfFVBU4JYB.png" /></figure><p>Then it’s up to the owner to review and merge these pull requests into the main branch. This can be easily done by clicking the big green button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*CvhvtqAVrl7ckyFm.png" /></figure><p>If there are bugs in the code that makes it incompatible, it will through a merge conflict error so be weary of this.</p><p><em>Differences between Merge Commits</em></p><p>Those of you with particular eagle eyes will have noticed the different types of merges.A tl:dr way to remember which type of merge to use is the following:</p><p><strong>Merge Commit</strong></p><p>Your typical commit. Combines all changes from your branch <strong>branch-name </strong>into <strong>main</strong>, resulting in a new commit on <strong>main</strong> that represents this merge.</p><p><strong>Squash Merge</strong></p><p>Suppose you have a feature branch (<strong>feature-y</strong>) with 15 commits. When you squash merge into <strong>main</strong>, those 15 commits will be combined into a single commit and added to <strong>main</strong>. Use when you want to simplify the commit history and don’t care about traceability.</p><p><strong>Rebase and Merge</strong></p><p>Useful when you have a branch (<strong>feature-z</strong>) that is behind <strong>main</strong> by several commits. Rebasing updates the branch <strong>feature-z</strong> so that its base matches the latest state of <strong>main</strong>. Once rebased, the branch can be merged linearly without a merge commit. Useful when you want to replay your changes on top of the latest <strong>main</strong> to ensure a clean integration.</p><p><strong>🎉🎉 And that’s all the basics — you should be able to hopefully manage a project on it. Congrats! You’re a software developer now :) 🎉🎉</strong></p><h3>Cool Fun Facts, Tips, Tricks and Advice with using GitHub</h3><p><strong>GitHub Desktop</strong></p><p>If the command line scares you, you’re not alone. You can do everything we covered today on a much more friendly interface called GitHub desktop where rather than writing lines of code to commit, you just click the friendly green button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*nvRQuSnIM3lQvfY3.png" /></figure><p><strong>Collaboration</strong></p><p>GitHub allows for, and actually encourages, collaboration. To work with others on a repository, add them as a collaborator through the repo settings such that everyone can contribute to the development of a project.</p><p><strong>Stars &amp; Achievements</strong></p><p>For each repository you make, you can collect stars and achievements (which potential employers love to see). These are given by other programmers on GitHub and can be for a range of reasons, even just having a pretty README.md</p><p><strong>Profile READMEs</strong></p><p>The same way your project can have READMEs, so can your profile. If you want to present yourself via a mini CV, this would be the perfect place to do so.</p><p><strong>Adding Tags</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*wZb8gk4QdlpWjksN.png" /></figure><p>Lets you add tags so people can find your project more easily</p><p><strong>GitHub Profile Projects</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*9mxKO1FqZ6E2XzNx.png" /></figure><p>They’re like Trello where you can split your tasks between done, currently doing, and done. <strong><em>Very useful for a hackathon.</em></strong></p><p><strong>And many other cool features :)))</strong></p><h4>If you get stuck</h4><p><em>“terminal”, “repository”, “clone”, “commit”…. </em>— the terminology sticks the more you use it!!.</p><p>Have a play around with it and give it a go. If you get stuck, head over to <strong>stack overflow</strong> or the<strong> GitHub help page. </strong>They’re incredibly well documented so just type your error into Google and have a look!</p><p>If you’re still stuck — come and look for an organiser in the beginner’s help area, or just around the TLC if you get stuck at any point in the hackathon — we’ll face the error messages together!</p><p>Happy hacking!</p><h3>!! BONUS — Additional Video Content !!</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FEDP1iAnlv1w%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DEDP1iAnlv1w&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FEDP1iAnlv1w%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/4728454c248f249e58aa4d8edc948428/href">https://medium.com/media/4728454c248f249e58aa4d8edc948428/href</a></iframe><p><em>Originally Published as a HackPack for DurHack 2024 on Oct 30, 2024, with video workshop recorded for DurJam 2025 on Jun 11, 2025</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=34aeb6cbffcc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[HackPack: An Intro to Machine Learning]]></title>
            <link>https://medium.com/@DurHack_press/hackpack-an-intro-to-machine-learning-1faa43ebf5af?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/1faa43ebf5af</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[hackpack]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[hackathons]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Fri, 31 Oct 2025 12:00:23 GMT</pubDate>
            <atom:updated>2025-10-31T12:00:23.162Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/2f1611ec4842">Kaal S</a></p><p>Hi DurHackers! So you want to make a machine learn, but don’t know where to start? Fear not, follow along with this hackpack to get started and if you come across any difficulties, head along to the beginner’s drop in area or the helpdesk to get some help, or find an organiser/volunteer who can help!</p><h4>Purpose of this HackPack</h4><p>This guide aims to provide beginners with practical steps to create machine learning models using popular libraries like TensorFlow and PyTorch. It focuses on hands-on expereince without diving deeply into theoretical concepts.</p><p>For more about the underlying theory, visit the resources at the bottom of this hackpack.</p><p>We’re going to build a <strong>DurHack Dino vs Barney classifier</strong>!!</p><p>To open this guide in CoLab head here:</p><p><a href="https://github.com/KaalkidanSahele/ML-HackPack">GitHub - KaalkidanSahele/ML-HackPack</a></p><p>To start, run the following command:</p><pre>!pip install tensorflow keras matplotlib numpy opencv-python</pre><h4>Installations</h4><p>We begun by installing the necessary libraries for this project. The key tools include:</p><ul><li><strong>NumPy</strong> for numerical operations (<a href="https://numpy.org/">https://numpy.org/</a>)</li><li><strong>OpenCV</strong> for image manipulation (<a href="https://opencv.org/">https://opencv.org/</a>)</li><li><strong>TensorFlow</strong> for building neural networks (<a href="https://www.tensorflow.org/guide">https://www.tensorflow.org/guide</a>)</li><li><strong>Keras</strong> for data augmentation (<a href="https://www.tensorflow.org/guide/keras">https://www.tensorflow.org/guide/keras</a>)</li></ul><p>Note: if you encounter warnings about floating-point round-off errors or missing GPU drivers, don’t worry! These warning are typical in machine learning tasks and won’t affect the main functionality of the project.</p><pre>## Pre-Processing Imports<br><br>import numpy as np<br>import matplotlib.pyplot as plt<br>from matplotlib import pyplot<br>import os<br>import cv2<br>from tqdm import tqdm<br>import random<br><br>## TensorFlow imports<br>from tqdm import tqdm<br>from keras.preprocessing.image import ImageDataGenerator<br><br>from numpy import expand_dims<br>from tensorflow.keras.preprocessing.image import load_img<br>from tensorflow.keras.preprocessing.image import img_to_array<br>from tensorflow.keras.preprocessing.image import ImageDataGenerator<br><br>from tensorflow.keras.models import Sequential<br>from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten<br>from tensorflow.keras.layers import Conv2D, MaxPooling2D<br># more info on callbakcs: https://keras.io/callbacks/ <br># model saver is cool too.<br>from tensorflow.keras.callbacks import TensorBoard<br>import pickle<br>import time</pre><p>Define your categories here for the images you want to classify. In this HackPack, we’re differentiating between two variants of a purple dinosaur — <strong>Barney</strong>, and <strong>the DurHack Dino</strong>.</p><pre>classes = [&quot;dino&quot;, &quot;barney&quot;]<br>data_directory = &#39;data&#39;<br>img_size = 150  # Resize images to uniform size for model input<br><br>def load_and_show_image(dino_barney_class, img_path):    <br>    img_array = cv2.imread(img_path) <br>    # visualise the images so we can see if it&#39;s all right!<br>    plt.imshow(img_array) <br>    plt.show()<br><br>def process_class_images(dino_barney_class):<br>    category_path = os.path.join(data_directory, dino_barney_class)<br>    for img_name in os.listdir(category_path):<br>        img_path = os.path.join(category_path, img_name)<br>        load_and_show_image(dino_barney_class, img_path)<br>        break  <br>        # we break here because we only want to see one image of each class<br><br>def visualize_images():<br>    for dino_barney_class in classes:<br>        process_class_images(dino_barney_class)<br><br># call the visualization function<br>visualize_images()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*tTX9NwC3nWbQYGWS.png" /><figcaption>DurHack Dino | Barney</figcaption></figure><h4>Data Augmentation</h4><p>Data augmentation is a technique used to artifically expand the size of a dataset by applying transformations (such as rotations, flips, or zooms). This helps prevent overfitting by allowing the model to generalise better.</p><p>Here, we’re going to augment our data!</p><p><strong>Dino Data Augmentation:</strong></p><pre>data_directory = &#39;data&#39;<br>max_augmentations = 750<br><br>def augment_image(image_path, datagen, save_dir, <br>                  prefix, max_augmentations):<br>    img = load_img(image_path)  # Load image<br>    data = img_to_array(img)  # Convert to array<br>    samples = expand_dims(data, 0)  # Expand dimensions<br>    it = datagen.flow(samples, batch_size=1, save_to_dir=save_dir, <br>save_prefix=prefix, save_format=&#39;jpg&#39;)<br><br>    # generate and save augmented images<br>    for i, batch in enumerate(it):<br>        if i &gt;= max_augmentations:<br>            break<br>            <br>## now we use the function we just defined to augment all our images and <br>## make our dataset!<br><br>def augment_images_in_category(category, datadir, augmented_dir, <br>img_extension , max_augmentations):<br>    image_dir = os.path.join(datadir, category)<br>    my_images = os.listdir(image_dir)<br><br>    # Create the data generator<br>    datagen = ImageDataGenerator(rotation_range=90, horizontal_flip=True)  <br><br>    for image_name in my_images:<br>        # check for correct file extension<br>        if image_name.endswith(f&#39;.{img_extension}&#39;): <br>            image_path = os.path.join(image_dir, image_name)<br>            augment_image(image_path, datagen, augmented_dir, <br>prefix=&#39;dr&#39;, max_augmentations=max_augmentations)</pre><p><strong>Dino &amp; Barney data augmentation:</strong></p><p>Awesome! We’ve defined what we want our augmentation to do. Let’s run it on our original dino and Barney images (this part may take a while).</p><pre>from tqdm import tqdm<br><br># dino data augmentation<br>augmented_data_directory = &#39;augmented_data/dino&#39;<br>img_extension = &#39;jpg&#39;  # if your images are PNGs, put png here!!<br><br># just wrapping the function call in a tqdm loop to display progress<br>for _ in tqdm(range(max_augmentations), desc=&quot;Augmenting Dino Images&quot;):<br>    augment_images_in_category(&quot;dino&quot;, data_directory, <br>augmented_data_directory, img_extension, 1)<br><br># barney data augmentation<br>augmented_data_directory_2 = &#39;augmented_data/barney&#39;<br>img_extension = &#39;png&#39;<br><br>for _ in tqdm(range(max_augmentations), desc=&quot;Augmenting Barney Images&quot;):<br>    augment_images_in_category(&quot;barney&quot;, data_directory, <br>augmented_data_directory_2, img_extension, 1)</pre><h4>Data Pre-processing</h4><p>Data preprocessing ensures that the input data is correctly formatted for model training. This step includes resizing images and converting them into arrays for easier manipulation by the model.</p><p>The following code processess both categories (dino and barney), resizes the images, and assigns each image a class label, <strong>where 0 is for dino, and 1 for Barney</strong>.</p><p>We also <strong>shuffle</strong> the data to ensure that the model doesn’t see all images from one class at once, which could bias training.</p><pre>data_directory = &quot;augmented_data&quot;<br>training_data = []<br><br>#read and resize images to specified size for uniformity<br>def process_image(image_path, img_size):<br>    try:<br>        img_array = cv2.imread(image_path, cv2.IMREAD_COLOR)  <br>        resized_array = cv2.resize(img_array, (img_size, img_size))  <br>        return resized_array<br>    except Exception as e:<br>        print(f&quot;Error processing {image_path}: {e}&quot;)  # Handle errors<br>        return None<br><br>#process images in given cateogry and append to training data<br>def process_category(category, datadir, img_size):<br>    path = os.path.join(datadir, category)  # path to category folder<br>    class_num = classes.index(category)  # class index for the category<br>    print(f&quot;Processing category: {category}, class num: {class_num}&quot;)<br><br>    category_data = []  <br>    # iterate through images in the category<br>    for img in tqdm(os.listdir(path)):<br>        img_path = os.path.join(path, img)<br>        <br>        # skip non-image files or hidden directories like <br>        # .ipynb_checkpoints<br>        # Add other valid formats as needed<br>        if not img.endswith((&#39;.png&#39;, &#39;.jpg&#39;, &#39;.jpeg&#39;)): <br>            continue<br>            <br>        processed_image = process_image(img_path, img_size)<br>        if processed_image is not None:<br>            category_data.append([processed_image, class_num])  <br><br>    return category_data<br><br>def create_training_data(categories, datadir, img_size):<br>    training_data = []<br>    for category in categories:<br>        category_data = process_category(category, datadir, img_size)<br>        # add category data to the main list<br>        training_data.extend(category_data) <br>    return training_data<br><br>def shuffle_training_data(training_data):<br>    random.shuffle(training_data)<br>    return training_data<br><br>def display_sample_labels(training_data, num_samples=10):<br>    print(f&quot;Displaying {num_samples} samples:&quot;)<br>    for sample in training_data[:num_samples]:<br>        print(f&quot;Category (or class) label: {sample[1]}&quot;)<br><br>        <br>training_data = create_training_data(classes, data_directory, img_size)<br>print(f&quot;In total, we have {len(training_data)} training samples! Nice!&quot;)  <br>training_data = shuffle_training_data(training_data)<br><br># now let&#39;s visualise some data to make sure our<br># dataset is augmented variations of our original data<br>display_sample_labels(training_data, num_samples=10)</pre><p><strong>Helpful Note:</strong> Make sure you can see variations of 0 and 1 in the samples, otherwise, it means one of your images aren’t being loaded properly!!</p><h4>Feature and Label Extraction</h4><p>We separate the features (X) and labels (y) for training and then use <strong>pickle</strong> to save the preprocessed data, so it can be easily reloaded for training without needing to process the images.</p><pre>## putting the data into features and labels using list comprehensions<br>X, y = zip(*[(features, label) for features, label in training_data])<br><br>X = np.array(X).reshape(-1, img_size, img_size, 3)<br><br>## saving the data using context managers (with statements)<br>with open(&quot;X.pickle&quot;, &quot;wb&quot;) as f_X, open(&quot;y.pickle&quot;, &quot;wb&quot;) as f_y:<br>    pickle.dump(X, f_X)<br>    pickle.dump(y, f_y)</pre><p>Visualise your augmented data!</p><pre>def augment_and_plot(image_path, augmentation_params, num_samples=9):<br>    # load the image and convert it to a numpy array<br>    img = load_img(image_path)<br>    data = img_to_array(img)<br>    samples = expand_dims(data, 0)<br>    datagen = ImageDataGenerator(**augmentation_params)<br>    it = datagen.flow(samples, batch_size=1)<br><br>    # generate samples and plot<br>    fig, axs = pyplot.subplots(3, 3, figsize=(4, 4))<br>    axs = axs.flatten()<br>    <br>    # generate batch of images<br>    for i in range(num_samples):<br>        batch = it.next() <br>        # convert to unsigned integers for viewing<br>        image = batch[0].astype(&#39;uint8&#39;) <br>        axs[i].imshow(image)<br>        # hide axes for clarity<br>        # if you want the axis, you can comment this line out  <br>        axs[i].axis(&#39;off&#39;) <br>    pyplot.tight_layout()<br>    pyplot.show()<br># pick a random image from the ones you <br># generated when you were augmenting data<br>image_path = &#39;augmented_data/dino/dr_0_12.jpg&#39;<br>augmentation_params = {&#39;rotation_range&#39;: 90, &#39;horizontal_flip&#39;: True}<br>augment_and_plot(image_path, augmentation_params)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*CKz91cT-gHTroMmx.png" /><figcaption>Augmented Data — DurHack Dino</figcaption></figure><h4>Model Architecture</h4><p>We are using a <strong>convolutional neural network (CNN)</strong>, which is ideal for image classification tasks. The model is designed with three convolutional layers and one dense layer for binary classification.</p><p>This CNN model is optimised for <strong>binary classification</strong> since we’re classifying images into two categories: Barney vs. DurHack Dino.</p><p>The TensorBoard callback is used for real-time monitoring of the model’s performance.</p><pre>def load_data(pickle_files):<br>    data = []<br>    for file in pickle_files:<br>        with open(file, &quot;rb&quot;) as f:<br>            data.append(pickle.load(f))<br>    return data<br><br>pickle_files = [&quot;X.pickle&quot;, &quot;y.pickle&quot;]<br><br>X, y = load_data(pickle_files)<br>X = np.array(X)<br>y = np.array(y)<br><br>dense_layers = [0]<br>layer_sizes = [64]<br>conv_layers = [3]</pre><pre>## source - https://raihanrnj.medium.com/deep-learning-simple-image-<br>## classification-using-convolutional-neural-network-dog-and-cat<br>## -8c99aef29e8<br><br>for dense_layer in dense_layers:<br>    for layer_size in layer_sizes:<br>        for conv_layer in conv_layers:<br>            NAME = &quot;{}-conv-{}-nodes-{}-dense-{}&quot;.format(conv_layer, <br>layer_size, dense_layer, int(time.time()))<br>            print(NAME)<br><br>            model = Sequential()<br><br>            model.add(Conv2D(layer_size, (3, 3), input_shape=X.shape[1:]))<br>            model.add(Activation(&#39;relu&#39;))<br>            model.add(MaxPooling2D(pool_size=(2, 2)))<br><br>            for l in range(conv_layer-1):<br>                model.add(Conv2D(layer_size, (3, 3)))<br>                model.add(Activation(&#39;relu&#39;))<br>                model.add(MaxPooling2D(pool_size=(2, 2)))<br><br>            model.add(Flatten())<br><br>            for _ in range(dense_layer):<br>                model.add(Dense(layer_size))<br>                model.add(Activation(&#39;relu&#39;))<br><br>            model.add(Dense(1))<br>            model.add(Activation(&#39;sigmoid&#39;))<br><br>            tensorboard = TensorBoard(log_dir=&quot;logs/{}&quot;.format(NAME))<br><br>            model.compile(loss=&#39;binary_crossentropy&#39;,<br>                          optimizer=&#39;adam&#39;,<br>                          metrics=[&#39;accuracy&#39;],<br>                          )<br><br>            model.fit(X, <br>                      y,<br>                      batch_size=32,<br>                      epochs=25,<br>                      validation_split=0.3,<br>                      callbacks=[tensorboard])</pre><p>Take a look at your model architecture!</p><pre>model.summary()</pre><p>Example model architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/875/0*jOb6g-gAcohSaRVW.png" /></figure><p>Our model has <strong>convolutional layers</strong> conv2d_12, conv2d_13, conv2d_14 <strong>activation layers</strong>, ReLU, to help learn complex patterns, <strong>max pooling layers</strong>, MaxPooling2D, to rereduce spatial dimensions to down-sample the feature maps and reduce computational loads, <strong>flatten layers</strong> to flatern the vectors, and a <strong>dense layer</strong> .</p><p>So we have a fairly complex model which can capture intricate features. This, alongside a sufficiently large dataset can classify images rather well!</p><p><strong>Helpful Note</strong>: We use <strong>accuracy</strong> as our performance metric, but take a look into some other performance metrics to really be sure your model works well.</p><pre>model.save(&#39;Dino-Barney-CNN.model&#39;)</pre><h3>Making Predictions</h3><p>In a new file, we’re now going to test our classification model by testing out whether it’s able to correctly classify some newly augmented images!</p><pre>import cv2<br>import tensorflow as tf<br><br>class_category = [&quot;Dino&quot;, &quot;Barney&quot;]<br>model = tf.keras.models.load_model(&#39;Dino-Barney-CNN.model&#39;)<br><br>IMG_SIZE = 150  # Target image size<br><br># Modify this list according to your actual classes<br>class_category = [&#39;Dino&#39;, &#39;Barney&#39;] <br><br>def prepare_image(filepath):<br>    img_array = cv2.imread(filepath)  # Load the image<br>    # Resize to expected dimensions<br>    resized_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))<br>    # Reshape for model input <br>    return resized_array.reshape(-1, IMG_SIZE, IMG_SIZE, 3) <br><br><br>def predict_image(filepath):<br>    prepared_image = prepare_image(filepath)  # Prepare the image<br>    prediction = model.predict(prepared_image)  # Get prediction<br>    img_array = cv2.imread(filepath)  # Read the original image for display<br>    predicted_class = class_category[int(prediction[0][0] &gt; 0.5)]<br>    print(f&quot;Prediction: {prediction[0][0]}\nClass: {predicted_class}\n&quot;)<br>    plt.imshow(img_array)<br>    plt.axis(&#39;off&#39;)<br>    plt.show()<br>    <br>    return prediction<br><br>predict_image(&#39;Test Images/barney - Copy.jpg&#39;)<br>predict_image(&#39;Test Images/durhack dino zoomed.JPG&#39;)</pre><p>Nice! You’ve just built your first image classifier with a CNN!</p><p>For further resources, take a look at the following extra materials from 3Blue1Brown and IBM:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FaircAruvnKk%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DaircAruvnKk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FaircAruvnKk%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c7ac21113d23bde7832e5a390b5a21c3/href">https://medium.com/media/c7ac21113d23bde7832e5a390b5a21c3/href</a></iframe><p><a href="https://www.ibm.com/think/topics/convolutional-neural-networks">What are Convolutional Neural Networks? | IBM</a></p><p><em>Originally Published as a HackPack for DurHack 2024 on Oct 30, 2024</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1faa43ebf5af" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Crown Estate might have hired me because of a Dinosaur quiz. Here’s why.]]></title>
            <link>https://medium.com/@DurHack_press/the-crown-estate-might-have-hired-me-because-of-a-dinosaur-quiz-heres-why-5187a2b47b4b?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/5187a2b47b4b</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[hackathon-organizing]]></category>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[internships]]></category>
            <category><![CDATA[computer-vision]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Thu, 30 Oct 2025 18:54:06 GMT</pubDate>
            <atom:updated>2025-10-30T18:54:06.183Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/502039aef08a">Andrea Johnson</a>, DurHack 2024 hacker and DurHack 2025 Hacker Experience Organiser.</p><p>Last year, I participated in my first hackathon — ever.</p><p>DurHack 2024 was an amazing experience: I got to meet (and work with) enthusiastic, excited people, witness the creation of some truly fascinating projects and our team ended up winning 1st place overall!</p><p>For what? Oh, just a Computer Vision powered, dinosaur-themed personality quiz. Yeah, not the most serious project. It’s a great story, though, and if you want to find out more about it, read on <a href="https://devpost.com/software/affirmasaurus"><strong>here</strong></a>.</p><p>To sum it up, we used Python’s OpenCV library to recognise the user’s head and detect which way they were moving and used the input as movement to answer our quiz.</p><p>Later on in the year, I found that The Crown Estate was advertising a role for ‘Marine Engineering Intern’. I genuinely had no idea what that entailed — I pictured swimming with dolphins, repairing some kind of complicated deep-sea equipment, and then remembered my nautical skills (or lack thereof) would mean that I would probably be closer to ‘sleeping with the fishes’ by the end of my first day. But the description of the role was quite different to what I had imagined: “The Crown Estate is looking for a motivated problem solver who can help create digital solutions for the benefit of the UK’s net zero ambitions.. [to create a] machine-learning project focussed on optimising the use of the seabed.”</p><p>In particular, there was one phrase that really stood out to me: “The integration of machine learning will likely involve the practical use of computer vision..”.</p><p>Well, I <em>had</em> worked on a project involving Computer Vision, although perhaps it wasn’t the most <em>practical </em>use of it…</p><p>And so, I tailored my CV to put ‘Affirmasaurus’ as the second in my list of projects, because it seemed so relevant to the description, but I still wanted the reader to take me seriously.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/617/0*WoYHErwz6JrS9vh8.png" /></figure><p>All this to say, I got the internship.</p><p>When I started, I had no idea what I was supposed to be doing. By the end, I had collaboratively developed a unique solution using Computer Vision and Machine Learning aided by Python’s Keras and Sci-Kit libraries to optimise Off-Shore Wind leasing.</p><p>The existing system, herein referred to as the ‘Blob Solver’, assisted in deciding where new offshore wind farms should be located by taking<br>a cluster of seabed and deciding on the<br>best arrangement to maximise the space being used, following<br>constraints like how big the cluster is, how far each wind<br>farm must be from another and could also take a ‘seed number’ (number of desired wind farms) as input. It used an ‘un-informed, semi-exhaustive search’. To find the maximum occupancy, the Blob Solver started with an initial guess and then changed it to increase the occupancy until no further changes could be made. After calculating the maximum occupancy and the resulting arrangement of the wind farms in the cluster accordingly, it started from scratch again with the next cluster. Therefore, any insights learned about what types of arrangements worked best were lost when running it again. As you can imagine, this meant that the Blob Solver had to perform many iterations before finding the right answer.</p><p>In fact, a single run took up to 5 minutes, which doesn’t sound like much, but it took approximately 4 days to generate all the training data needed for our project. The goal? Investigate whether or not the application of Computer Vision could make it more efficient.</p><h3>But what is Computer Vision?</h3><p>Computer Vision is a specialised field within Machine Learning (which, itself, is a subset of Artificial Intelligence) that focuses on visual data. A very watered down explanation would be ‘teaching computers how to see’.</p><p>We used Neural Networks, which are models inspired by the human brain and its neural connections. These consist of an input layer, hidden layers and an output layer. During the training phase of these models, the weights and biases associated with each of the nodes in the hidden layers are tweaked to better reflect the complex relationship between the input and output data.</p><figure><img alt="A diagram showing a Neural Network with one input node, one hidden layer with 3 nodes and an output layer with 2 nodes." src="https://cdn-images-1.medium.com/max/953/0*vEnPrwc_kpKaA2ys.png" /><figcaption>A diagram showing a Neural Network with one input node, one hidden layer with 3 nodes and an output layer with 2 nodes.</figcaption></figure><p>A diagram showing a Neural Network with one input node, one hidden layer with 3 nodes and an output layer with 2 nodes.</p><p>There were two key components of the structure of our model:</p><ol><li>Convolutional Neural Network: this allowed the model to learn features from the images of the cluster we were using as input. This was done using a convolution kernel that slides across the image, multiplying corresponding elements. This also reduced the size of the image drastically, which meant that not only could the program run quicker, but that the model could make more accurate connections because the ‘irrelevant’ pixels were filtered out.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/526/0*NfFhOlmUPGjY71Oy.gif" /><figcaption>Source: <a href="/hackernoon/visualizing-parts-of-convolutional-neural-networks-using-keras-and-cats-5cc01b214e59">Erik Reppel</a></figcaption></figure><p>2. Dense Neural Network: these types of Neural Network are ‘fully connected’ meaning each node in a layer is connected to every node in the next layer. They’re used for making predictions and classification based on feature data from previous convolutional layers.</p><h3>Results and Impact</h3><p>Computer Vision enabled the Blob Solver to start with a more educated initial guess: the model was given the cluster, predicted the maximum occupancy and then the Blob Solver tried arrangements that satisfied this prediction.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/912/0*fcaGhMYnRWVgXmSu.png" /></figure><p>Ultimately, our final version of the solution achieved a 15% absolute percentage error for occupancy prediction and 6% error for seed prediction. Efficiency was boosted by over 50% as you can see in the image below. The left hand side shows the un-informed Blob Solver’s approach to the problem, which it solved in 9 iterations, while the Computer Vision augmented version on the right only needed 4.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1010/0*dLHeemJtoqoWfIqh.png" /></figure><h3>Conclusion</h3><p>While the project I made at DurHack 2024 wasn’t the most useful, it undeniably helped me develop skills that were invaluable during my internship. My advice to anyone reading this is to sign up for the 10th iteration of DurHack, which is happening on 1st and 2nd November 2025, before it’s too late! It’s a fantastic opportunity to build a project, connections with our sponsors and long-lasting memories.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5187a2b47b4b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hacking Your Future: How to Best Engage with Sponsors in a Hackathon]]></title>
            <link>https://medium.com/@DurHack_press/hacking-your-future-how-to-best-engage-with-sponsors-in-a-hackathon-47423f3959e9?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/47423f3959e9</guid>
            <category><![CDATA[employability]]></category>
            <category><![CDATA[sponsorship]]></category>
            <category><![CDATA[durham]]></category>
            <category><![CDATA[hackathon-organizing]]></category>
            <category><![CDATA[hackathons]]></category>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Wed, 29 Oct 2025 16:54:10 GMT</pubDate>
            <atom:updated>2025-10-29T16:54:10.372Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/3147a07e62f5">Adrienne Lam</a></p><p>Hackathons are about more than just coding; they are a direct line to industry leaders. For you, the talented students, our sponsors are not just here to provide prizes and pizza, they are scouting for future interns and employees! Your ability to network is as crucial as your coding skills.</p><p>Here’s your ultimate guide to turning those sponsor interactions into real-world opportunities.</p><h3>1. Pre-Hackathon Preparations</h3><p>Engaging effectively starts before the hackathon begins.</p><ul><li>Review the sponsor list: Look at every company sponsoring the event, from the title sponsor to event partners. Don’t just look at the logo; click through to their careers page and their “About Us.”</li><li>Identify their tech stack/industry: Which companies use the programming language or framework you love? Which ones are tackling a problem you’re passionate about?</li><li>Formulate your connection with companies: Prepare a concise, one-sentence reason why you’re interested in their specific company. Examples: “I love how your company’s xxx is able to do xxx,” or “Your commitment to xxx really resonated with me!”</li></ul><figure><img alt="Hackers interacting with sponsor Waterstons at DurHack 2024" src="https://cdn-images-1.medium.com/max/1024/1*1_Af9xkT_R494sXvsvt2VQ.jpeg" /><figcaption>Hackers interacting with sponsor Waterstons at DurHack 2024</figcaption></figure><h3>2. Make the Most of Workshops and Challenges</h3><p>Sponsor workshops and challenges are designed to be engagement goldmines. Don’t skip them!</p><ul><li>Go to the source: Workshops are not just free lessons. Attend workshops hosted by companies you are genuinely interested in. This shows commitment and initiative.</li><li>Ask smart questions: Don’t ask a question you could easily Google. Ask questions that show you have thought deeply about their technology or challenge. For example, instead of “What is this API?” ask, “How do you handle rate limiting on this API in a production environment?”</li><li>Stick around: After the main presentation, politely approach the presenter. Thank them, introduce yourself, and maybe briefly mention how you might integrate their tech into your hackathon project!</li><li>Tackle the sponsor’s challenges: If a sponsor you are interested in offers a specific challenge, use it as a framework for your project. This is a direct portfolio piece for them!</li><li>Incorporate their tech: Even if you do not fully solve their main challenge, integrating one of their tools into your main hackathon project is a way to get their attention. It shows you are proactive and capable of learning new tools quickly.</li></ul><h3>3. The Sponsor Booth: Your One-on-One Moment</h3><figure><img alt="Hacker at the Durham Venture Lab booth" src="https://cdn-images-1.medium.com/max/1024/1*QNszuc4SteshuR1uOpATJQ.jpeg" /><figcaption>Hacker at the Durham Venture Lab booth</figcaption></figure><p>The physical booth is where you transition from a general attendee to a potential candidate.</p><ul><li>Keep it brief: Representatives have many people to talk to. Have a 30-second elevator pitch ready so they can easily remember you: your name, your year/major, one impressive project (or your current hackathon idea), and why you’re interested in their company.</li><li><em>Example:</em> “Hi, I’m Alex, a year 2 student studying Computer Science. I’m currently building XXX for this hack. I’ve been following your work in AI ethics and would love to know more about internship opportunities in your research division.”</li><li>Ask targeted questions: Use the research you did in step 1.</li><li><em>For Internship Insights:</em> “What is the most common project an intern on your engineering team would work on?”</li><li><em>For Company Culture:</em> “What is a major development challenge your team is currently tackling, and how does your company philosophy guide that solution?”</li><li>Exchange contacts: If the conversation goes well, politely ask for a business card or if you can connect on LinkedIn. Don’t just grab a card — ask! “Would it be alright if I connected with you on LinkedIn to follow up on this conversation?”</li></ul><h3>4. The Final Project Presentation</h3><p>This is your last, and possibly best, chance to impress the technical representatives, so tailor your presentation if you ever get your chance to be on stage!</p><ul><li>Acknowledge their contribution: If you used a company’s tech, explicitly thank the sponsor and call out how<em> </em>their tool made your project better or possible.</li><li>Focus on the “why”<strong>:</strong> Explain the problem, your solution, and the potential impact. If your solution aligns with a social or technical challenge the sponsor is known for (e.g. scalability, data security), emphasise that alignment.</li></ul><p>Treat every interaction with a sponsor as a professional conversation. Be prepared, be polite, and most importantly, be genuine. The connections you make during that weekend could easily lead to your first great internship! Good luck, and happy hacking!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=47423f3959e9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Plan a Hardware Hack: Tips for Your Next Hackathon]]></title>
            <link>https://medium.com/@DurHack_press/how-to-plan-a-hardware-hack-tips-for-your-next-hackathon-903832fe060c?source=rss-2e8c1e22c97a------2</link>
            <guid isPermaLink="false">https://medium.com/p/903832fe060c</guid>
            <dc:creator><![CDATA[DurHack]]></dc:creator>
            <pubDate>Mon, 27 Oct 2025 10:27:43 GMT</pubDate>
            <atom:updated>2025-10-27T10:27:43.092Z</atom:updated>
            <content:encoded><![CDATA[<p>Written by <a href="https://medium.com/u/3147a07e62f5">Adrienne Lam</a></p><p>Hackathons are the perfect place to turn big ideas into working prototypes. From IoT devices, to robotics, to wearable tech, combining hardware with coding unlocks a whole new level of creativity.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2dtZFRil97ehP0G2h-fsgg.jpeg" /></figure><p>If you’re thinking of tackling a hardware hack at your next event, here are some tips to help you prepare and make the most of your time:</p><p><strong>1. Check the Rules Beforehand</strong></p><p>Every hackathon is different when it comes to preparation. Remember to check the rules of each event carefully so that you are working within boundaries.</p><p>At DurHack, you are not allowed to start your project in advance, which means no coding or assembly before the hack begins.</p><p>What you can do:</p><ul><li>Brainstorm ideas and possible project directions.</li><li>Research tools, frameworks, and hardware you might want to use.</li></ul><p>Pro tip: Be prepared to stay flexible! Sometimes the best hackathon projects come from a surprise challenge announced on the day.</p><p><strong>2. Come prepared</strong></p><p>Time flies during a 24-hour sprint, so set yourself up for success:</p><ul><li>Pack your essentials: chargers, adaptors, spare batteries, and anything you can’t hack without (maybe your emotional support animal: for us, it’s our DurHack Dino!)</li><li>Have a programmable robot or cool set of sensors? Bring any specific hardware you’re keen to use!</li><li>Bring a couple of project ideas. Just enough to give you a head start.</li></ul><p><strong>3. Make the most of what’s provided!</strong></p><p>Make the most of the hardware available at your hackathon!</p><p>This year DurHack is excited to be able to provide an RS Hardware Library. You’ll have access to an amazing range of devices and equipment that you can make use of, including Raspberry Pis and Arduinos. We’d like to thank <a href="https://uk.rs-online.com/web/content/discovery/education">RS</a> for their sponsorship of this Library, and our event!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ass-ipxAFElzp-3xww9UgQ.jpeg" /></figure><p><strong>4. Connect with Others</strong></p><p>Hardware hacks often need diverse skills: coding, electronics, design, and even storytelling. Don’t be shy to reach out!</p><ul><li>Ask mentors for guidance. They can often point you to the right hardwares or save you from troubleshooting headaches.</li><li>Join communities like the <a href="https://www.google.com/url?q=https://rsyouth.typeform.com/community&amp;sa=D&amp;source=docs&amp;ust=1761559897703507&amp;usg=AOvVaw3iaSDOZEMnJaOTbiF8hluR">RS Student Community</a> to share experiences and collaborate with your peers.</li></ul><p><strong>5. Enjoy the Experience</strong></p><p>Hardware hacks can be challenging, but they’re also some of the most rewarding ones. Take the opportunity to try out different types of hardwares, push yourself creatively, and don’t forget to have fun with your teammates!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=903832fe060c" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>