Gavin Schmidt's Portfolio

PROJECTS

Illumen Horizons LLC

Illumen Horizons LLC

"Illumen Horizons LLC" is a first-person horror survival game, where the enemy has randomized behavior on each load. All the code in this game was written by me, showcasing my technical know-how and expertise working with NPC behavior.

Learn More

Go Stumpy!

Go Stumpy!

"Go Stumpy" is a 2D platformer that I collaborated with a team to create. All the code in this game was written by me, showcasing my extensive expertise in Unity C# development.

Learn More

Blast Space

Blast Space

"Blast Space" is a 2D minigame where you must beat a high score. This was a solo project showcasing my mastery of 2D Unity functionality and development.

Learn More

Bird Burglars

Bird Burglars

"Bird Burglars" is an original card game where you must outplay your opponents' hands. This was a group project where I worked to create and design the mechanics of the game, showcasing my proficiency in creating and implementing gameplay mechanics.

Learn More

Corporate World Dominion

"Corporate World Dominion" is a modified version of Risk, with an added economy system.

Learn More

FACES COMMANDERS

"FACES COMMANDERS" is an original card game using a deck of cards and many types of dice.

Learn More

EDUCATIONAL PROJECTS

Desert Scene

C# SCRIPTING

This is some code to create a scene using Unity's primitive creation code and random values.

Code
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using JetBrains.Annotations;
using Unity.Mathematics;
using UnityEngine;

public class DesertScene : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        GameObject baseObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
        baseObject.transform.position = new Vector3(0, 0, 0);
        baseObject.AddComponent();
        baseObject.GetComponent().material.color = Color.yellow;

        GameObject forest = new GameObject("Forest");

        int x = UnityEngine.Random.Range(5, 10);
        for (int i = 0; i < x; i++)
        {
            int objectType = UnityEngine.Random.Range(0, 2);
            GameObject tree;
            float height = UnityEngine.Random.Range(0.8f, 2);
            if (objectType == 0)
            {
                tree = GameObject.CreatePrimitive(PrimitiveType.Cube);

                tree.transform.parent = forest.transform;
                
                tree.transform.position = new Vector3(UnityEngine.Random.Range(-4, 0), height/2, UnityEngine.Random.Range(-4, 0));
                tree.transform.localScale = new Vector3(UnityEngine.Random.Range(0.1f, 0.6f), height, UnityEngine.Random.Range(0.1f, 0.6f));

                tree.AddComponent();
                tree.GetComponent().material.color = UnityEngine.Random.ColorHSV(0.33f, 0.33f, UnityEngine.Random.Range(0.3f, 0.5f), UnityEngine.Random.Range(0.6f, 1));
            }
            else
            {
                tree = GameObject.CreatePrimitive(PrimitiveType.Cylinder);

                tree.transform.parent = forest.transform;
            
                tree.transform.position = new Vector3(UnityEngine.Random.Range(-4, 0), height, UnityEngine.Random.Range(-4, 0));
                tree.transform.localScale = new Vector3(UnityEngine.Random.Range(0.1f, 0.6f), height, UnityEngine.Random.Range(0.1f, 0.6f));

                tree.AddComponent();
                tree.GetComponent().material.color = UnityEngine.Random.ColorHSV(0.33f, 0.33f, UnityEngine.Random.Range(0.3f, 0.5f), UnityEngine.Random.Range(0.6f, 1));
            }
            
        }

        StartCoroutine(GeneratePyramid());

    }

    IEnumerator GeneratePyramid()
    {
        
        GameObject pyramid = new GameObject("Pyramid");

        for (int i = 0; i < 5; i++)
        {
            switch (i)
            {
                case 0:
                    for(int j = 0; j < 5; j++)
                    {
                        for(int k = 0; k < 5; k++)
                        {
                            yield return new WaitForSeconds(0.2f);
                            GameObject brick = GameObject.CreatePrimitive(PrimitiveType.Cube);
                            brick.transform.localScale = new Vector3(0.85f, 0.85f, 0.85f);
                            brick.transform.parent = pyramid.transform;
                            brick.transform.position = new Vector3(k, 0.425f, j);
                            brick.AddComponent();
                            brick.GetComponent().material.color = Color.red;
                        }
                    }
                    break;
                case 1:
                    for(int j = 0; j < 4; j++)
                    {
                        for(int k = 0; k < 4; k++)
                        {
                            yield return new WaitForSeconds(0.2f);
                            GameObject brick = GameObject.CreatePrimitive(PrimitiveType.Cube);
                            brick.transform.localScale = new Vector3(0.75f, 0.75f, 0.75f);
                            brick.transform.parent = pyramid.transform;
                            brick.transform.position = new Vector3(k + 0.5f, 1.25f, j + 0.5f);
                            brick.AddComponent();
                            brick.GetComponent().material.color = Color.yellow;
                        }
                    }
                    break;
                case 2:
                    for(int j = 0; j < 3; j++)
                    {
                        for(int k = 0; k < 3; k++)
                        {
                            yield return new WaitForSeconds(0.2f);
                            GameObject brick = GameObject.CreatePrimitive(PrimitiveType.Cube);
                            brick.transform.localScale = new Vector3(0.65f, 0.65f, 0.65f);
                            brick.transform.parent = pyramid.transform;
                            brick.transform.position = new Vector3(k + 1, 2f, j + 1);
                            brick.AddComponent();
                            brick.GetComponent().material.color = Color.green;
                        }
                    }
                    break;
                case 3:
                for(int j = 0; j < 2; j++)
                    {
                        for(int k = 0; k < 2; k++)
                        {
                            yield return new WaitForSeconds(0.2f);
                            GameObject brick = GameObject.CreatePrimitive(PrimitiveType.Cube);
                            brick.transform.localScale = new Vector3(0.55f, 0.55f, 0.55f);
                            brick.transform.parent = pyramid.transform;
                            brick.transform.position = new Vector3(k + 1.5f, 2.64f, j + 1.5f);
                            brick.AddComponent();
                            brick.GetComponent().material.color = Color.blue;
                        }
                    }
                    break;
                case 4:
                            yield return new WaitForSeconds(0.2f);
                            GameObject topBrick = GameObject.CreatePrimitive(PrimitiveType.Cube);
                            topBrick.transform.localScale = new Vector3(0.45f, 0.45f, 0.45f);
                            topBrick.transform.parent = pyramid.transform;
                            topBrick.transform.position = new Vector3(2, 3.22f, 2);
                            topBrick.AddComponent();
                            topBrick.GetComponent().material.color = Color.white;
                    break;
            }
        }
        yield break;
    }
}
            

Credit: Gavin Schmidt and Jadyn Englett

DND Character Using Classes And Structs

C# SCRIPTING

This is some code to create a DND character using inputs from the inspector, using classes and structs.

Code
using UnityEngine;
using System.Collections.Generic;

public class SolutionTwo : MonoBehaviour
{
    public bool Tough;
    public bool HillDwarf;
    public bool RolledHP;
    public string charClass;
    public int inLevel;
    public int CON;
    public string name;

    private int hp;
    private StructVariable myChar;

    // Start is called before the first frame update
    void Start()
    {
        myChar = new StructVariable(HillDwarf, RolledHP, Tough, charClass, inLevel, CON, name);
        
        if (!StructVariable.classes.Contains(myChar.className) || myChar.level < 1 || myChar.level > 20 || myChar.con < 1 || myChar.con > 30 || myChar.charName == "")
        {
            Debug.Log("Invalid input");
            return;
        }
        
        myChar.SetHP((int)CalcHP());
        Debug.Log(DisplayMessage());
    }

    public float CalcHP()
    {
        float maxHP = 0;

        maxHP = StructVariable.hitDie[myChar.className];
        maxHP += StructVariable.ConMod[myChar.con - 1];

        if (myChar.level > 1)
        {
            for (int i = 0; i < myChar.level; i++)
            {
                if (myChar.isRolledHP) maxHP += Random.Range(1, (int)StructVariable.hitDie[myChar.className] + 1);
                else maxHP += ((StructVariable.hitDie[myChar.className] + 1f) / 2f) + 0.5f;    
            }
        }

        if (myChar.hasTough)
            maxHP += 2 * myChar.level;
        if (myChar.isHillDwarf)
            maxHP += myChar.level;

        return maxHP;
    }

    public string DisplayMessage()
    {
        return $"My character {myChar.charName} is a level {myChar.level} {myChar.className} with a CON score of {myChar.con} and {(myChar.isHillDwarf ? "is" : "is not")} a Hill Dwarf and {(myChar.hasTough ? "has" : "does not have")} the Tough feat. I want the HP {(myChar.isRolledHP ? "rolled" : "averaged")}. HP: {myChar.hp}";
    }
}

[System.Serializable]
public struct StructVariable
{
    public bool hasTough;
    public bool isHillDwarf;
    public bool isRolledHP;
    public string className;
    public int level;
    public int con;
    public string charName;

    public int hp;
    //Dictionary of classes and their hit die
    public static readonly Dictionary hitDie = new Dictionary()
    {
        { "Artificer", 8f },{ "Barbarian", 12f },{ "Bard", 8f },{ "Cleric", 8f },{ "Druid", 8f },
        { "Fighter", 10f },{ "Monk", 8f },{ "Ranger", 10f },{ "Rogue", 8f },{ "Paladin", 10f },
        { "Sorcerer", 6f },{ "Wizard", 6f },{ "Warlock", 8f }

    };
    //List of classes
    public static List classes = new List()
    {
        "Artificer", "Barbarian", "Bard", "Cleric", "Druid", "Fighter", "Monk", 
        "Ranger", "Rogue", "Paladin", "Sorcerer", "Wizard", "Warlock"
    };
    //Array of Con Mods
    public static float[] ConMod = { -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10 };

    public StructVariable(bool isHillDwarf, bool rolledHP, bool hasTough, string charClass, int level, int con, string name)
    {
        this.isHillDwarf = isHillDwarf;
        this.isRolledHP = rolledHP;
        this.hasTough = hasTough;
        this.className = charClass;
        this.level = level;
        this.con = con;
        this.charName = name;
        this.hp = 0;
    }

    public void SetHP(int hp)
    {
        this.hp = hp;
    }
}         
            

Credit: Gavin Schmidt, Miguel Pineda, Samuel Drastal, and Brandon Wahl

Two Person Microgame

MODIFIED TUTORIAL GAME

This 2D top-down shooter microgame was collaboratively developed by myself and Egor Nikiforov. The game features moving enemies, NPC dialogue, and environmental hazards. Detailed credits for our individual contributions can be found in the README file.

Download Partner Microgame

Credit: Projectile Particles, UI, In-level Sounds, Ammo System, Level Design: Gavin Schmidt          Scenery, Win/Lose Screen Music/UI, Damage Zones: Egor Nikiforov

Unity Tutorial: FPS

ORIGINAL MUSIC

My objective was to compose a triumphal gladiatorial melody, subsequently interspersed with elements of uncertainty and foreboding. Given the game's dark and enigmatic nature, I meticulously crafted both the atmospheric design and the music's darker harmonies to evoke an enduring sense of dread.

Credit: Sound Design, VFX, Prop Placement: Gavin Schmidt          Game Design, Level Design, Scenery: Unity

Breadth First Search

PROBLEM SOLVING CODE

Problem:

The first line of input contains two space separated integers, r (2 ≤ r ≤ 1000) and c (2 ≤ c ≤ 1000), representing the number of rows and number of columns in the grid, respectively.

The following r lines contain c characters each. The ith line of these lines contains the contents of the ith row of the grid, from left to right.

It is guaranteed that exactly one of the grid characters will be ‘*’ and exactly one of the grid characters will be ‘$’. All grid characters that represent regular squares will be labeled with the character ‘.’. All forbidden squares will be represented with the grid character ‘!’. All other squares will be capital letters, representing various teleportation squares. If a letter appears in the grid, then it will appear in at least two separate grid squares.

The Output (standard console output): If Gustavo can get out of the maze, output a single integer representing the fewest number of moves it will takehim to get out. If he can’t get out, output “Call 911”.

Code
import java.util.*;

class Main {
    final public static int[] DR = {-1,0,1,0};
    final public static int[] DC = {0,-1,0,1};
                
    public static int r;
    public static int c;
    public static char[][] maze;
                
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
                
        r = in.nextInt();
        c = in.nextInt();
                
        maze = new char[r][c];
                
        for(int i=0; i < r; ++i){
            maze[i] = in.next().toCharArray();
        }
                
        int start = find('*');
                
        int result = bfs(start, '$');
        if (result == -1)
            System.out.println("Call 911");
        else
            System.out.println(result);
    }
                
    public static int bfs(int s, char e){
        LinkedList q = new LinkedList();
        q.offer(s);
                        
        int[] dist = new int[r*c];
        Arrays.fill(dist, -1);
        dist[s] = 0;
                
        while(q.size()>0){
            int curr = q.poll();
                
            int currR = curr / c;
            int currC = curr % c;
                
            char currL = maze[currR][currC];
                
            if (currL == e) return dist[curr];
                
            for (int i=0; i < DR.length; i++) {
                int nR = currR + DR[i];
                int nC = currC + DC[i];
                
                if (!inbounds(nR, nC)) continue;
                if (maze[nR][nC] == '!') continue;
                if (dist[nR*c+nC] != -1) continue;
                
                dist[nR*c+nC] = dist[curr] + 1;
                q.offer(nR*c+nC);
            }
                
            if('A' <= currL){
                if(currL <= 'Z'){
                    int letterPlus = find((char)(currL + 1));
                    int letterNeg = find((char)(currL - 1));
                    if(letterPlus != -1){
                        int nR = letterPlus / c;
                        int nC = letterPlus % c;
            
                        if (dist[letterPlus] != -1) continue;
                
                        dist[letterPlus] = dist[curr] + 1;
                        q.offer(letterPlus);
                    }
                
                    if(letterNeg != -1){
                        int nR = letterNeg / c;
                        int nC = letterNeg % c;
                
                        if (dist[letterNeg] != -1) continue;
                
                        dist[letterNeg] = dist[curr] + 1;
                        q.offer(letterNeg);
                    } 
                }
            }
        }
        return -1;
    }
                
    public static boolean inbounds(int x, int y) {
        return x >= 0 && x < r && y >= 0 && y < c;
    }
                
    public static int find(char s){
        for (int i=0; i < r; i++)
            for (int j=0; j < c; j++)
                if (maze[i][j] == s)
                    return i*c + j;
                return -1;
    }
                
    // FOR TESTING
    public static void printMaze(){
        for(int i=0; i < r; ++i){
            for(int j=0; j < c; ++j){
                System.out.print(maze[i][j]);
            }
            System.out.println();
        }
    }
}
            

Dijkstra's Algorithm

PROBLEM SOLVING CODE

Problem:

The first line of input contains 3 space separated integers, C (2 ≤C≤105), R (C-1 ≤ R≤ min(105, n(n-1)/2), S (1 ≤ S ≤ N), representing the number of cities, number of roads, and the city number that represents the capital, respectively.

Then the next R lines contain the descriptions of the roads. Each of them contains 3 space separated integers vi, ui, wi (1 ≤ vi, ui ≤ n, vi ≠ ui, 1 ≤ wi ≤ 1000), where vi, ui are numbers of the cities connected by this road and wi is its length. The last input line contains integer L (0 ≤ l ≤ 109) — the distance from the capital to the places where the treasures are located. It is guaranteed that:

• between any two cities no more than one road exists.

• each road connects two different cities.

• from each city there is at least one way to any other city by the roads.

The Output (standard console output): Print two numbers — the number of treasures in the cities and the number of treasures on the roads in Monster land.

Code
import java.util.*;

class Main {
    //global variables
    public static int C;
    public static int R;
    public static int CAPITAL;
    public static int[][] graph;
    public static int L;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
                
        //getting num of cities, roads, and where capital
        C = in.nextInt();
        R = in.nextInt();
        CAPITAL = in.nextInt() - 1;
                
        //setup adjacency matrix
        graph = new int[C][C];
                
                
        //integers to setup up matrix
        int tempC1;
        int tempC2;
        int length;
                
        //build matrix
        for(int i = 0; i < R; ++i){
            tempC1 = in.nextInt() - 1;
            tempC2 = in.nextInt() - 1;
            length = in.nextInt();
                
            //into matrix
            graph[tempC1][tempC2] = length;
            graph[tempC2][tempC1] = length;
        }
                
        //getting L
        L = in.nextInt();
                
        //close input
        in.close();
                
        Dijkstra();
    }
    public static void Dijkstra() {
        //three lists to track during algorithm
        PriorityQueue minHeap = new PriorityQueue(C, new City());
        int[] D = new int[C];
        Set S = new HashSet();
                
        //three lists to track treasure
        int[] roadT = new int[C];
        int tCity = 0;
        int tRoad = 0;
                
        //start all distances at INF
        Arrays.fill(D,Integer.MAX_VALUE);
                
        //start with capital
        minHeap.add(new City(CAPITAL, 0));
        D[CAPITAL] = 0;
                
        int prevD;
                
        while (S.size() != C){
            //visit next node in minHeap
            int next = minHeap.poll().c;
                
            if(S.contains(next))
                continue;
                
            //add to visited list
            S.add(next);
                
            for(int i = 0; i < C; ++i){
                if(graph[next][i] > 0 && i != CAPITAL){
                                    
                    //if treasure is on road
                    //System.out.println(next + ": " + D[next] + " " + i + ": " + D[i]);
                    if(D[next] < L && (D[next] + graph[next][i]) > L){
                        roadT[next] = L - D[next];
                        if(roadT[next] != roadT[i] || roadT[next] != graph[next][i] / 2){
                            //System.out.println("adding");
                            tRoad++;
                        }
                    }
                
                    //if smaller, update D, add to minHeap
                    if(D[i] > (D[next] + graph[next][i])){
                        prevD = D[i];
                        D[i] = D[next] + graph[next][i];
                        minHeap.add(new City(i, D[i]));
                
                        //treasure found
                        if(D[i] == L)
                            ++tCity; 
                        if(prevD == L && D[i] != L)
                            --tCity;
                                        
                    } 
                    //System.out.println(next + ": " + D[next] + " " + i + ": " + D[i]);
                }
            }
        }
        System.out.println("In city: " + tCity);
        System.out.println("On the road: " + tRoad);
    }
}
                
//object for minheap
class City implements Comparator{
    public int c;
    public int d;
                
    public City(){
    }
                
    public City(int c, int d){
        this.c = c;
        this.d = d;
    }
                
    @Override
    public int compare(City node1, City node2) {
        if (node1.d < node2.d)
            return -1;
        if (node1.d > node2.d)
            return 1;
        return 0;
    }
}
            

Dynamic Programming

PROBLEM SOLVING CODE

Problem:

The first line of the input contains a single integer n (1≤n≤100000) that represents the number of students in each row.

The second line of the input contains n integers p1,1,p1,2,...,p1,n,(1≤p1,i≤109), where p1,i is the number of problems solved by the ith student in the first row.

The third line of the input contains n integers p2,1,p2,2,...,p2,n,(1≤p2,i≤109), where p2,i is the number of problems solved by the ith student in the second row.

The Output (standard console output): Print one number — the maximum possible total problem solved by the selected group of students.

Code
import java.util.*;

public class Main {
    public static int[][] candidates;
    public static int size;
    public static void main(String [] args){
        Scanner in = new Scanner(System.in);
                
        size = in.nextInt();
        candidates = new int[size][2];
                
        for(int i = 0; i < size; ++i)
            candidates[i][0] = in.nextInt();
                        
        for(int i = 0; i < size; ++i)
            candidates[i][1] = in.nextInt();
                
        System.out.println(DP());
    }
                
    public static int DP(){
        int[][] dp = new int[size][2];
                
        dp[0][0] = candidates[0][0];
        dp[0][1] = candidates[0][1];
                
        for (int i = 1; i < size; ++i){
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + candidates[i][0]);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + candidates[i][1]);
        }
        return Math.max(dp[size-1][0], dp[size-1][1]);
    }
}