Finding My Fire: Exploring the Tech Behind Wildfire Command

When I started working on Wildfire Command (known as Sim Firefighter at the time), I had a pretty solid idea of what I wanted to focus on – procedural map generation. I love the challenge of making systems that feel organic and unpredictable, which is exactly what we needed for our wildfire simulation. What I didn’t anticipate was how deep I’d end up diving into algorithms and how much I’d come to appreciate Perlin noise, BSP partitioning, and cellular automata.

Favorite Technology: Perlin Noise

If I had to pick one piece of tech that really clicked for me during this project, it’s Perlin noise. Before this, I had used procedural generation a bit in other project, but never in a way where it directly impacted the project so significantly. The terrain in Wildfire Command isn’t just a static backdrop – it affects fire spread, suppression tactics, and overall strategy. Using Perlin noise allowed us to generate natural-looking landscapes with forests, dry grass patches, and water that actually make sense visually and mechanically.

The coolest part? Perlin noise creates smooth gradients instead of harsh transitions, so terrain flows naturally instead of looking like a random mess of tiles.

Cellular Automata: A Great Fit for the Project

Another key tool in our map generation system is cellular automata. Unlike Perlin noise, which generates terrain smoothly, cellular automata are all about rule-based evolution – defining behaviors that change over time based on neighboring tiles. This made it a great fit for refining terrain clusters, ensuring that forests formed in realistic patches and transitions between terrain types looked natural.

I have enjoyed working with cellular automata because it has given us a lot of control while still allowing for emergent patterns. By tweaking a few rules, we could dramatically change how the map evolved, which has been satisfying to experiment with.

What I’d Change

If I had unlimited time, I’d love to experiment with multi-layered Perlin noise to add even more depth to the terrain. Right now, we use a single layer to define basic terrain features, but layering different noise functions could allow for more nuanced environmental details – like differentiating between dense and sparse forest regions or adding subtle elevation changes that impact fire behavior.

Looking Ahead

Overall, the combination of Perlin noise, BSP partitioning, and cellular automata has been a solid foundation for our game’s map generation. It’s been awesome seeing everything come together – especially when the procedural maps start looking like real landscapes with clear wildfire risk zones.

Next up, I’m excited to see how our fire spread mechanics interact with the terrain. Wildfires are unpredictable, and that’s something we really want to capture in our simulation. So far, it’s been a fun challenge, and I can’t wait to share more about how we’re balancing realism with engaging gameplay.

🐱 Cat Time 🐱

As always, here’s a picture of one of my cats! This time, it’s Maggie, who has been supervising my work from the comfort of the couch near my desk. She’s clearly unimpressed with my debugging process, but she enjoys watching the maps generate.

Writing Clean Code: A Developer’s Commitment to Excellence

Have you ever found yourself staring at old code and wondering, “What was I even thinking?” As developers, we strive for clean code—code that is readable, maintainable, and efficient. However, achieving this ideal requires a conscious effort to adopt good practices and avoid common pitfalls, often referred to as “code smells.”

Clean Code: Practices Worth Embracing

Chapter 1 of Robert Martin’s Clean Code: A Handbook of Agile Software Craftsmanship dives into meaningful naming, a habit that can transform messy scripts into something even your future self will thank you for. Martin argues that good names reveal intent, making code easier to understand and maintain. Inspired by this, I aim to adopt the habit of choosing descriptive, unambiguous names for variables, functions, and classes. For example:

# Clean Code Example:
def calculate_total_price(items):
    """Calculate the total price of available items in the cart."""
    return sum(item['price'] for item in items if item['available'])

# Bad Code Example:
def calc(items):
    total = 0
    for i in items:
        if i[2]:
            total += i[1]
    return total

The difference is huge—the first example practically explains itself, while the second feels like trying to read a foreign language without a dictionary. As MFONIDO Mark writes in their DEV Community article, clean code should read like well-written prose. Meaningful naming is a cornerstone of this principle.

Code Smells: Pitfalls to Avoid

Martin Fowler’s Chapter 3 of Refactoring: Improving the Design of Existing Code provides an extensive discussion of code smells—symptoms of poor design that can lead to bugs or excessive technical debt. One smell that particularly resonates with me is “duplicated code.”

Duplicated code not only bloats a codebase but also creates maintenance challenges. Fowler advocates for refactoring duplicated logic into reusable functions or classes. Hamza Khan’s DEV Community article provides practical advice for identifying and resolving such smells. For example, consider this:

# Code Smell: Duplicated Code
if user_role == 'admin':
    print("Access granted to admin dashboard")
if user_role == 'editor':
    print("Access granted to editor dashboard")

# Refactored Code:
def grant_access(role):
    access_messages = {
        'admin': "Access granted to admin dashboard",
        'editor': "Access granted to editor dashboard",
    }
    print(access_messages.get(role, "Access denied"))

grant_access(user_role)

By consolidating logic, we reduce redundancy and simplify future updates.

Reflection and Commitment

For me, writing clean code goes beyond just improving my own workflow—it’s about making life easier for anyone who might work with my code later. By adopting meaningful naming and avoiding duplication, I hope to make my code more readable and efficient.

Though it takes some effort, the payoff—a cleaner codebase and fewer headaches for everyone involved—is absolutely worth it. As I continue my journey, I’ll strive to apply these principles and explore others outlined by experts like Martin and Fowler.

A Picture for Purr-spective

No blog post of mine would be complete without a little feline flair! Meet Meatball Sandwich, my loyal coding companion and the undisputed king of car rides. Whether he’s perched on the passenger seat or giving me judgmental looks from the dashboard, Meatball is always there to keep me inspired and on my toes.

Because let’s be honest: even the cleanest code can’t compete with this level of cuteness.

Back to Work: Winter Term Adventures with BSP and Procedural Maps

Hello again, and welcome back to The Coded Cat! After a much-needed holiday break, I’m diving back into our capstone project for winter term. This term, we’re continuing work on Wildfire Command (formerly known as Sim Firefighter), our browser-based wildfire management simulation. One of my recent tasks has been to improve our map generation system, and I wanted to share how we tackled this using Binary Space Partitioning (BSP) and Perlin Noise.

So, grab a cozy drink and let’s talk about procedural map generation (and how cats make debugging more fun).


šŸŽÆ What is Binary Space Partitioning?

Imagine you’re slicing a cake into smaller pieces. Binary Space Partitioning (BSP) is a method where we split a large space (like our map) into smaller regions by dividing it either vertically or horizontally. We keep slicing until all pieces are small enough to meet our minimum size requirement.

For our game, we use BSP to create distinct terrain zones (like forests, lakes, and firebreaks), which we then populate with Perlin Noise to add natural-looking variations.


šŸ›  Step 1: Implementing BSP Partitioning

Here’s a simplified version of how BSP works in our project:

function partitionMap(width, height, minSize) {
    if (width <= minSize || height <= minSize) {
        return [createRegion(width, height)];
    }

    let splitVertical = Math.random() > 0.5;
    let splitPoint = randomPoint(minSize, splitVertical ? width : height);

    let region1, region2;
    if (splitVertical) {
        region1 = partitionMap(splitPoint, height, minSize);
        region2 = partitionMap(width - splitPoint, height, minSize);
    } else {
        region1 = partitionMap(width, splitPoint, minSize);
        region2 = partitionMap(width, height - splitPoint, minSize);
    }

    return [...region1, ...region2];
}

In plain English, we:

  1. Check if the current region is small enough. If yes, stop splitting.
  2. Randomly decide whether to split the region vertically or horizontally.
  3. Calculate where to split the region.
  4. Recursively split the resulting subregions.

The result? A beautifully partitioned map, ready for more detailed terrain generation!


šŸŒ„ Step 2: Integrating BSP with the Map Generator

After implementing BSP, we integrated it into our existing map generation system. The MapGenerator class was modified to use BSP to partition the map grid and apply Perlin Noise within each partitioned region.

If you’re unfamiliar with Perlin Noise, I covered it in a previous post. You can check out this blog post for a deep dive into how Perlin Noise works and how we use it for natural-looking terrain.

Here’s a high-level overview of how we did it:

class MapGenerator {
    constructor(width, height, minSize) {
        this.width = width;
        this.height = height;
        this.minSize = minSize;
        this.perlin = new PerlinNoise(width, height);
        this.bsp = new BSPPartition(width, height, minSize);

        this.grid = this.generateMap();
    }

    generateMap() {
        const grid = [];
        const partitions = this.bsp.partition({ x: 0, y: 0, width: this.width, height: this.height });

        partitions.forEach(partition => {
            for (let y = partition.y; y < partition.y + partition.height; y++) {
                const row = [];
                for (let x = partition.x; x < partition.x + partition.width; x++) {
                    const noiseValue = this.perlin.getNoise(x, y);
                    const terrain = this.getTerrainFromNoise(noiseValue);
                    row.push(new TerrainTile(x, y, terrain));
                }
                grid.push(row);
            }
        });

        return grid;
    }

    getTerrainFromNoise(value) {
        if (value < -0.5) return 'water';
        if (value < 0) return 'forest';
        if (value < 0.5) return 'grass';
        return 'shrub';
    }
}

By integrating BSP, we ensured that each partitioned region could have unique terrain features, making our maps more diverse and engaging.


šŸ›  Step 3: Debugging with Utility Functions

Debugging procedural generation can be tricky, so we added some utility functions to help visualize the results. For example:

function printMap(grid) {
    grid.forEach(row => {
        console.log(row.map(tile => tile.terrain).join(' | '));
    });
}

This prints the terrain types for each tile in the grid, making it easier to spot any anomalies.


āœ… Why BSP and Perlin Noise Work Well Together

The combination of BSP and Perlin Noise gives us structured yet natural-looking maps. BSP ensures the map is divided into logical regions, while Perlin Noise adds the randomness and variation that makes each map feel unique.

By combining these techniques, we can generate diverse maps with different terrain features, making our game more engaging and realistic.


🐾 Bonus Cat Photo!

So this isn’t a photo of one of my cats, but it is a cat I saw in a Lego store when I went to visit family over the Holidays! I thought this display was really fun so I definitely had to share it!

Advent of Code 2024: Day 1 — Historian Hysteria

This year, I’m diving into Advent of Code for the first time, and I couldn’t be more excited! As a newcomer to this coding challenge, I wasn’t sure what to expect, but Day 1 proved to be the perfect introduction. The puzzles were engaging yet approachable, providing a fantastic opportunity to flex my problem-solving muscles while building confidence in my skills.


Day 1 Puzzle: Reconciling Historians’ Notes

The scenario for Day 1 was intriguing: two mismatched lists of location IDs needed to be reconciled to help a group of Elvish Senior Historians track down their missing Chief Historian. The task was split into two parts:

  1. Calculate the total distance between the two lists by pairing the numbers from smallest to largest and summing their absolute differences.
  2. Determine a similarity score by calculating how many times each number from the first list appears in the second and summing weighted contributions.

Pseudocode for My Solutions

Here’s how I approached each part of the puzzle:

Part 1: Total Distance

1. Parse Input

Split the raw input into two separate lists of integers.

Function: parse_input(input_string)
    Initialize list1 and list2
    For each line in input_string:
        If line is not empty:
            Split the line into two numbers
            Append the first number to list1
            Append the second number to list2
    Return list1 and list2

2. Sort the Lists:

Sorting ensures that the smallest numbers from each list are paired together.

Function: sort_list(list)
    Return list sorted in ascending order

3. Compute Total Distance:

For each index, compute the absolute difference between paired numbers and sum them.

Function: calculate_total_distance(list1, list2)
    Initizialize total_distance to 0
    For i from 0 to length of list1:
        Calculate absolute difference between list1[i] and list2[i]
        Add difference to total_distance
    Return total_distance

Part 2: Similarity Score

Calculate Occurrence Similarity:

1. Count how many times each number in the first list appears in the second list and create a list.

Function: similarity_list(list1, list2)
    Initialize similarity_list as empty
    For each num1 in list1:
        Count occurrences of num1 in list 2
        Append count to similarity_list
    Return similarity_list

2. Use these counts to compute the weighted similarity score.

Function: similarity_score(list1, similarity_list)
    Initialize similarity_score to 0
    For i from 0 to length of list1:
        similarity_score += list1[i] * similarity_list{i}
    Return similarity_score

Implementing with TDD

For both parts, I used Test-Driven Development (TDD) to structure my work:

1. Write Tests First:

Before writing any functional code, I created tests for each function. For example:

  • parse_input should correctly parse the input into two lists
  • calculate_total_distance should compute the correct total for paired absolute differences
  • similarity_list should return the correct count of occurrences for each number

Example test for part 2:

def test_similarity_score():
    list1 = [1, 2, 3, 4, 5]
    list2 = [1, 2, 1, 1, 0]
    expected_similarity_score = 1 + 4 + 3 + 4 + 0
    assert similarity_score(list1, similarity_list(list1, list2)) == expected_similarity_score

2. Develop Code to Pass Tests:

Functions were implemented iteratively, ensuring each passed its corresponding tests before moving on.

3. Refactor for Clarity:

Once all tests passed, I revisited the code to clean up and optimize where possible.

4. Continuous Testing

I ran tests frequently to catch errors early and verify that new changes didn’t break existing functionality.


Lessons from Day 1

  • TDD Keeps You Focused: Writing tests first clarified my goals and made debugging easier.
  • Breaking Down Problems Helps: Writing pseudocode and taking the problem step by step reduced overwhelm.
  • Python is a Great Tool for Problem-Solving: Its readability and powerful standard library made implementation smooth.

Final Thoughts

Participating in Advent of Code for the first time has already been both challenging and rewarding. Day 1 taught me the value of a systematic approach, and I can’t wait to see what the rest of the month holds. Whether you’re a seasoned Advent of Code participant or just starting like me, I’d love to hear about your experiences!

Are you taking part in Advent of Code this year? Let me know in the comments!

Developing Procedural Terrain for Sim Firefighter

Hey everyone! I’m so excited to share what I’ve been working on for Sim Firefighter: procedurally generated terrain! As you know, this game is still in its early stages, but one of the key features I’m tackling is creating dynamic maps that feel natural and engaging. Terrain isn’t just a backdrop in Sim Firefighter — it’s a major gameplay element that impacts fire behavior and player strategy.

To make this happen, I’ve been diving into Perlin Noise, an amazing tool for generating smooth, organic patterns. The process has been challenging but so rewarding, and I’m thrilled to take you behind the scenes and show you how it all works!


What is Perlin Noise?

Perlin Noise creates smooth gradients of random values, perfect for generating terrains, clouds, or even fire spread patterns. It’s widely used in games because it avoids the harsh randomness of pure noise, resulting in more natural-looking outputs.


How I’m Using Perlin Noise in Sim Firefighter

The goal is to procedurally generate a map where each tile is assigned a terrain type — like grass, shrubs, or water — based on Perlin Noise values. Here’s how I’m building it:

1. Choosing the Right Tool

To generate Perlin Noise, I’m using the noisejs library. It’s a straightforward JavaScript implementation of Perlin Noise that supports 2D and 3D noise and allows for seeding — important for creating reproducible maps during development.

2. Generating the Noise Grid

The terrain map in Sim Firefighter is structured as a grid. Each grid cell represents a tile, and I use Perlin Noise to assign values to each cell. Lower values might represent water, mid-range values, grass, and higher values trees. Here’s a pseudocode example of how the noise grid is generated:

    Initialize PerlinNoise with width, height, scale, and seed
    For each Y coordinate in the grid:
        For each X coordinate in the grid:
            Generate Perlin noise value at position (X, Y)
            Store the noise value in the grid
    End For

    3. Mapping Noise to Terrain

    The generated Perlin Noise value range for -1 to 1. We map these values to terrain types like water, grass, and trees. Here’s how the mapping is done:

    For each value in the noise grid:
        If value < -0.5: Assign "water"
        Else if value < 0: Assign "grass"
        Else if value < 0.5: Assign "shrub"
        Else: Assign "tree"
    End For

    4. Rendering the Map

    Once the terrain is generated, the next step is to render it visually. Each tile corresponds to a terrain type, and we render the corresponding graphic for each tile.

    For each tile in the terrain grid:
        Create a sprite at (X, Y)
        Assign the terrain image based on the tile type (water, grass, etc.)
    End For

    What I’m Learning Along the Way

    1. Tuning Noise Parameters:

    The scale and noise thresholds have a huge impact on the map’s look. Fine-tuning these values is a key part of the process.

    2. Debugging with Visualization:

    Visualizing the noise grid as colors or grayscale tiles has made it easier to identify issues with the terrain generation.

    3. Reproducibility with Seeds:

    Using a seed ensures I can regenerate the same map, which has been essential for testing.


    What’s Next?

    Sim Firefighter is still in its early stages, so there’s a lot more to do. Next steps for procedural terrain include:

    • Making the tiles interactive so players can inspect and interact with different terrain types.
    • Exploring layered noise techniques to create more complex and realistic landscapes.
    • Improving the map generation performance for larger grids.

    Implementing Perlin Noise has been a rewarding challenge, and I’m excited to see how this feature evolves as the game develops further!


    Thanks for following along with this journey! And as always, here’s a cat pic for good luck:

    My bengal cat, Cider, went on an adventure to the grocery store parking lot recently and was loving watching all the activity around us.

    Mapping Out Wildfire Territory: The Art of Map Generation in Sim Firefighter

    Hey everyone! For the last few weeks, I’ve been deep into the magic of map generation for Sim Firefighter. In this post, I’m excited to take you behind the scenes of what goes into building a realistic, challenging environment where wildfire management is anything but predictable.

    To start with, map generation is essential because our game relies on dynamic terrains that influence fire behavior and player strategy. With so many unpredictable elements, like fire spreading differently across dry grass vs. dense forest, the map needs to capture real-life elements while keeping gameplay interesting and challenging.

    The first step in creating our map is procedural generation with something called Perlin noise. Think of it as a way to give the map a natural flow, so it feels continuous rather than looking like a patchwork quilt. With Perlin noise, we can create smooth transitions between terrains like forests, dry grasslands, and even water bodies that can act as natural firebreaks. For example, areas with low elevation and less moisture might turn into dry grasslands, ready to fuel fast-spreading fires, while higher, denser regions become forests that slow down the fire a bit but burn with greater intensity.

    Next up is a tool called Binary Space Partitioning (BSP), which helps divide the map into structured regions. BSP is amazing for organizing areas like roads, firebreaks, and water bodies. This not only adds variety to the map but also gives players strategic points they can use to slow down or contain fires. Imagine trying to stop a fire on one side of a lake or along a road — those natural barriers can really be a game-changer!

    To make things even more realistic, we use Cellular Automata for organic terrain generation. This is where we get those beautiful, irregular patches of forests and dry grass that look more natural than anything we could create by hand. Each cell on the gird changes based on its neighbors, so you end up with clusters that mimic real-life landscapes. When these organic areas catch fire, the flames spread in ways that feel true to life, creating intense but strategic gameplay.

    Then there’s the final layer of complexity: Dijkstra Maps for fire spread dynamics. These maps calculate the distance from fire origins to various points on the map, simulating how fires spread based on proximity to fuel sources. So, if a fire starts near a forest cluster, the game calculates its spread toward dry grasslands, keeping players on their toes as the flames change direction and intensity with each moment.

    Once all these steps come together, we finalize the map by storing each cell’s properties — from terrain type to how fast it will burn. It’s a bit like setting up a giant chessboard where every square holds different risks and advantages, making each game session unique.

    So, that’s a peek into what map generation looks like in Sim Firefighter! It’s a mix of science and art, and every layer adds something special to the gameplay. I can’t wait to see how it evolves as we start testing it out.

    And, of course, no post is complete without a cat picture. We’re switching it up a bit this time. Below is a picture of my parents’ pets: Opal (the cat) and Pacie (the dog). They are the softest, sweetest animals and I love when I get to see them!

    New Beginnings: Shifting Gears to Sim Firefighter!

    Hey everyone! If you remember my last update, I was all in with my team on the Parchment project. Well, hold onto your keyboards, because things have taken an unexpected twist! We’ve packed up our notes and ideas from Parchment and are now diving into a whole new capstone project: Sim Firefighter.

    So what’s Sim Firefighter all about? Picture this: you’re leading a team within the National Interagency Coordination Center, managing wildfire responses, strategizing fire suppression, and making some serious on-the-spot decisions about fighting fires across different terrains. It’s a massive shift from Parchment, but one I’m really excited about. This new project is packed with challenges, like figuring out realistic fire behavior and integrating techniques that real wildfire managers would us. I never thought I’d be reading up on wildfire data for a project, but here we are!

    Now, I’m working with a slightly smaller, more focused team of four, which is Kaitlyn, Aidan, Tyler, and me. Each of us has our own focus, whether it’s hosting the app, creating maps, managing player data, or developing fire-spreading mechanics. The change has brought some new energy, and it’s been incredible to see how quickly we’ve all adapted. As much as I loved the vision behind Parchment, Sim Firefighter felt like a unique chance to challenge ourselves in totally different ways.

    We’re still just getting started, but I can already feel the potential in this project. Next up, we’re diving into map generation and working on making the fire behavior as close to real life as we can manage. Plus, we’ve got plans to integrate player achievements for those who master the tactics of wildfire management. Who knew wildfire management could be this fun?

    And of course, because I know you’re all here for the cat photos, here’s a shot of my two older babies, Maggie (the larger and younger of the two) and Cider (my teeny tiny Bengal baby who just turned 9 years old!)

    That’s all for now! Stay tuned for more Sim Firefighter updates, I can’t wait to share how this journey unfolds!

    Capstone Project Update: Parchment with Captured Sun!

    It’s happening, folks! The fall term is in full swing, and I’ve officially kicked off my capstone project. I’m excited to announce that my team and I are working on a project called Parchment with a startup based in Austin, Texas, called Captured Sun. This project is ambitious and exciting, pushing us to explore new ways of interacting with our computers.

    Our team has seven talented seniors from OSU’s eCampus, and each of us brings something unique to the table. During our first meeting with the folks at Captured Sun, we dove right into planning and discussing how to tackle the project. I’ve been using Obsidian to keep notes and ideas organized, and it’s already been a game changer.

    I won’t lie, the term has been a whirlwind. Between classes, working on my portfolio, and now this project, it’s been busy. But it’s the kind of busy that keeps you on your toes in the best way possible. It’s starting to feel like everything I’ve learned so far is coming together, and despite the workload, I’m more motivated than ever to make this experience count.

    My goals for this capstone? I’m looking forward to diving into new technologies and learning from both my team and our mentors at Captured Sun. It’s a fantastic opportunity, and I’m eager to see where this journey takes us!

    That’s where things stand for now. The team is fired up, and I can’t wait to see how this project evolves over the coming weeks. I’ll definitely keep you all posted with updates as we tackle this challenge together.

    And as promised, here’s a little something to make you smile — a picture of one of my cats, Maggie, from the day I brought her home 8 years ago! 🐱

    Hello, World! From Salem, Oregon

    Balancing coding, gaming, and three mischievous cats—welcome to my world! I’m Cat, a senior in the Computer Science program at Oregon State University, studying through their eCampus. My partner and I moved to Salem about four months ago, and we’re slowly settling into life in this rainy, yet cozy, city.

    A close-up photo of a black cat sitting on a textured rug, looking directly at the camera with wide, curious eyes.
    Meet Meatball Sandwich, one of my three furry study buddies.

    When I’m not busy with school, I’m hanging out with my three cats—Meatball Sandwich, Maggie, and Cider—or indulging in my love for gaming and all things tech. If you had told me as a kid that I’d be writing code and blogs about software development, I probably would have laughed it off. But here I am in my thirties, making my second attempt at college and balancing the world of ones and zeroes with everything else life throws my way.

    So, how did I end up here? I guess you could say coding is in my DNA. My dad is a software developer, and as soon as I could read, he was already trying to teach me how to code. Back then, I had a million other things I’d rather do than sit at a computer and tell it what to do. But after a failed attempt at a Social Work degree and a decade of figuring out what I truly wanted, his enthusiasm eventually rubbed off on me. From tinkering with my first ā€œHello, World!ā€ program to diving into more complex projects during quarantine, the thrill of creating something from nothing stuck with me. Now, here I am, about to embark on my capstone project, which feels like the final boss battle of this academic journey.

    As I prepare for this challenge, I’ll be honest—I don’t yet know what my project will be or who my teammates are. It’s a bit nerve-wracking, especially with my ever-present imposter syndrome whispering that I should stick to petting my cats instead of writing code. On top of that, this year will include job hunting and preparing for the future, which adds its own layer of pressure. But nerves aside, I can’t help but feel a spark of excitement. I’m eager to dive into new technologies, soak up knowledge from my team and mentor, and build something truly awesome. And hey, if I get a chance to play around with some AI along the way? That would be the ultimate win! So, I hope you’ll come along for the ride. Whether it’s sharing the triumphs, the setbacks, or those little victories, I’m looking forward to having you with me on this journey. Let’s see where this adventure takes us!