My Career Goals!

Let me introduce myself and my career goals

I worked in IT doing QA Automation from 2019 to 2023. I have the goal to work in IT and/or software again in some capacity.

Hello friends. I will show some little things here that are of personal interest to me; take what is useful to you for what it is, discard the rest if you like!

Here is a game I have made and worked on a little bit recently

This small game/toy/gizmo is currently live at my website at https://tilde.town/~silly-internet-tricks/mnchkin.html

Gravity Munchkin Game
Here is a screenshot of what the game looks like. The name of the game is GRAVITY MUNCHKIN.

Here I have pasted the source code for the game:

<!doctype html>
<html lang="en-US">
  <head>
    <title>GRAVITY MUNCHKIN</title>
    <meta name="author" content="Josh Parker">
    <style id="gravity-munchkin-style" title="gravity-munchkin-style">
    @keyframes rotating {
      0% {
        transform: rotate(0turn);
      }

      100% {
        transform: rotate(1turn);
      }
    }

    div.my-spiky-div {
      animation: rotating linear 10s infinite;
      position: fixed;
    }

    body {
      font-family:
        Avenir, Montserrat, Corbel, "URW Gothic", source-sans-pro, sans-serif;
    }

    @keyframes heading-fade {
      0% {
        opacity: 100%;
        transform: scaleY(0%);
        letter-spacing: 0;
      }

      6% {
        opacity: 100%;
        transform: scaleY(100%);
        letter-spacing: 0;
      }

      12% {
        opacity: 80%;
        transform: scaleY(100%);
        letter-spacing: 0;
      }

      100% {
        opacity: 0%;
        transform: scaleY(100%);
        letter-spacing: 2em;
      }
    }

    h1 {
      animation-name: heading-fade;
      position: fixed;
      top: 0;
      left: -100%;
      width: 300%;
      height: fit-content;
      text-wrap: nowrap;
      color: white;
      text-align: center;
      animation-duration: 10s;
      animation-iteration-count: 1;
      animation-fill-mode: forwards;
      z-index: 2;
    }

    h2 {
      position: fixed;
      color: white;
      z-index: 2;
      width: 10%;
      text-align: center;
    }

    .planet {
      position: fixed;
      aspect-ratio: 1;
      border-radius: 100%;
      transform: translate(-50%, -50%);
      z-index: 1;
      box-shadow:
        0 0 4px white,
        0 0 8px white,
        0 0 12px white,
        0 0 16px white,
        0 0 20px white;
    }

    .starship {
      height: 3%;
      aspect-ratio: 1;
      position: fixed;
      z-index: 3;
      background-image: linear-gradient(-45deg, #002fff, #9fff00);
    }

    .coin {
      height: 3em;
      width: 3em;
      position: fixed;
      z-index: 3;
      background-color: gold;
      border-radius: 100%;
      transform: translate(-50%, -50%);
      text-align: center;
    }

    .score-display {
      position: fixed;
      bottom: 0;
      left: 0;
      z-index: 2;
      color: white;
    }

    @keyframes blast {
      0% {
        box-shadow:
          1px 1px 1px #ffffff,
          3px 3px 3px #ffff00,
          5px 5px 5px #ff7f00,
          7px 7px 7px #ff0000;
      }

      50% {
        box-shadow:
          3px 3px 3px #ffffff,
          5px 5px 5px #ffff00,
          7px 7px 7px #ff7f00,
          9px 9px 9px #ff0000;
      }

      100% {
        box-shadow:
          1px 1px 1px #ffffff,
          3px 3px 3px #ffff00,
          5px 5px 5px #ff7f00,
          7px 7px 7px #ff0000;
      }
    }

    .starship.blasting {
      animation-name: blast;
      animation-duration: 250ms;
      animation-iteration-count: infinite;
    }

    .starship.blasting.paused {
      animation: none;
    }

    .pause-banner {
      color: white;
      position: fixed;
      inset: 0;
      text-align: center;
      align-content: center;
      font-size: 8em;
    }

    div.droplet {
      background-color: hsl(260deg 60% 85% / 0.19);
      position: fixed;
      border-radius: 100%;
    }
    </style>
  </head>

  <body>
    <div class="starship" style="transform: rotate(45deg)"></div>
    <div class="coin">$$$<br>100</div>
    <div class="score-display">Score: 0</div>
    <script>
    const styleSheetCssRules = [
      ...[...document.styleSheets].find(
        (e) => e.title === "gravity-munchkin-style",
      ).cssRules,
    ];
    const mySpikyDivStyle = styleSheetCssRules.find(
      (e) => e.selectorText === "div.my-spiky-div",
    ).style;
    const dropletStyle = styleSheetCssRules.find(
      (e) => e.selectorText === "div.droplet",
    ).style;
    const h1Style = styleSheetCssRules.find(
      (e) => e.selectorText === "h1",
    ).style;

    let seed;
    const locationUrl = new URL(location);
    const searchSeed = locationUrl.searchParams.get("s");
    if (searchSeed) {
      seed = Number.parseInt(searchSeed, 36);
    }

    if (!seed) {
      seed = crypto.getRandomValues(new Uint32Array(1))[0];
    }

    // I copied this rng from here: https://github.com/michaeldzjap/rand-seed/blob/master/src/Algorithms/Mulberry32.ts
    const rng = ((a) => () => {
      a += 0x6d2b79f5;
      let t = a;
      t = Math.imul(t ^ (t >>> 15), t | 1);
      t ^= t + Math.imul(t ^ (t >>> 7), t | 61);

      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    })(seed);

    locationUrl.searchParams.set("s", seed.toString(36));

    history.replaceState("", "", locationUrl);

    const styleElement = (e, ...kv) => {
      kv.forEach(([k, v]) => {
        e.style.setProperty(k, v);
      });
    };

    const a = (n, c) => new Array(n).fill().map(c);

    const domDrop = (e) => e.parentElement.removeChild(e);

    const makeDiv = (c) => () => {
      const d = document.createElement("div");
      d.classList.add(c);
      return d;
    };

    const polarCoordToPercent = (theta, r) => {
      const cos = Math.cos(theta);
      const sin = Math.sin(theta);
      return [
        `${Math.round(50 + 50 * cos * r)}%`,
        `${Math.round(50 + 50 * sin * r)}%`,
      ];
    };

    const spikyClipPath = (spikes, r) =>
      a(spikes * 2, (_, i) =>
        polarCoordToPercent(
          (i * 2 * Math.PI) / (2 * spikes),
          i % 2 ? 1 - r : r,
        ).join(" "),
      ).join(", ");

    const pick = (a) => a[Math.floor(rng() * a.length)];
    const adj = [
      "Adorable",
      "Baffling",
      "Benthic",
      "Brainy",
      "Brilliant",
      "Briny",
      "Bustling",
      "Cosmic",
      "Creepy",
      "Dazzling",
      "Deep",
      "Eerie",
      "Excellent",
      "Excessive",
      "Extreme",
      "Fetid",
      "Foggy",
      "Galactic",
      "Gloomy",
      "Grassy",
      "Honorable",
      "Hot",
      "Infernal",
      "Jocular",
      "Kinetic",
      "Liminal",
      "Muddy",
      "Nebulous",
      "Occupied",
      "Peaceful",
      "Pleasant",
      "Quantum",
      "Rancid",
      "Scorching",
      "Shadowy",
      "Spooky",
      "Steamy",
      "Stellar",
      "Sunny",
      "Timorous",
      "Tumbling",
      "Universal",
      "Vast",
      "Venomous",
      "Volcanic",
      "Western",
      "Wretched",
      "Yellow",
      "Zealous",
    ];
    const name = [
      "Abyss",
      "Alien",
      "Alley",
      "Barn",
      "Bazaar",
      "Bog",
      "Boot",
      "Brain",
      "Brook",
      "Castle",
      "Chemist",
      "College",
      "Compass",
      "Cottage",
      "Country",
      "Countryside",
      "Court",
      "Crab",
      "Creek",
      "Current",
      "Deer",
      "Dolphin",
      "Draco",
      "Druid",
      "Eel",
      "Egg",
      "Elephant",
      "Farm",
      "Fissure",
      "Flower",
      "Forest",
      "Goof",
      "Guano",
      "Havona",
      "Hill",
      "Hole",
      "House",
      "Immortal",
      "Jacket",
      "Jug",
      "Keyboard",
      "Kilo",
      "Lobster",
      "Mall",
      "Meadow",
      "Mesa",
      "Meteor",
      "Moat",
      "Monster",
      "Moon",
      "Nation",
      "Ninja",
      "Ocean",
      "Orbit",
      "Palace",
      "Path",
      "Plain",
      "Planet",
      "Plateau",
      "Pony",
      "Pool",
      "Port",
      "Prison",
      "Psycho",
      "Python",
      "Quagmire",
      "Quasar",
      "Rainbow",
      "Ray",
      "Resort",
      "Retreat",
      "Ring",
      "River",
      "Ruin",
      "School",
      "Ship",
      "Shoe",
      "Shuttle",
      "Side",
      "Sketch",
      "Skunk",
      "Snake",
      "Splendid",
      "State",
      "Stream",
      "Sun",
      "Swamp",
      "Sweater",
      "Tower",
      "Trail",
      "Tree",
      "Tub",
      "Universe",
      "University",
      "Violin",
      "Wall",
      "Youth",
      "Zebra",
    ];
    const suff = [
      "-city",
      "bad",
      "bert",
      "bin",
      "burg",
      "burger",
      "fix",
      "gor",
      "ings",
      "ist",
      "istan",
      "lick",
      "meade",
      "mer",
      "nip",
      "side",
      "stein",
      "ton",
      "town",
      "ville",
    ];

    const randName = () => `${pick(adj)} ${pick(name)}${pick(suff)}`;

    const randN = () => Math.floor(rng() * rng() * 7);

    const randSize = () =>
      (rng() + rng()) * (1 / 2) * (5.6 * 3 - 1 / 3) + 1 / 3;

    const randRGB = () =>
      Math.floor(0xff - rng() * rng() * 0xbf)
        .toString(16)
        .padStart(2, "0")
        .toLocaleUpperCase();

    const randDarkRGB = () =>
      Math.floor(rng() * rng() * 0x5f)
        .toString(16)
        .padStart(2, "0")
        .toLocaleUpperCase();

    const randColor = () => `#${randRGB()}${randRGB()}${randRGB()}`;

    const randCoord = () => Math.floor(rng() * 94 + 3);

    const systems = a(11, () => {
      const starMaxSize = rng() * 6 + 2;
      const starHue = rng() * 360;
      return a(11, () => ({
        name: randName(),
        bg: `#${randDarkRGB()}${randDarkRGB()}${randDarkRGB()}`,
        planets: a(randN(), () => {
          const s = randSize();
          return {
            size: s,
            color: randColor(),
            top: randCoord(),
            left: randCoord(),
            gravity: s / 5,
          };
        }),
        pointsOfInterest: a(Math.floor(randN() / 3), () => ({
          top: randCoord(),
          left: randCoord(),
          title: `${pick(name)} ${pick(name)}`,
        })),
        spaceWeather: {
          wind: rng() * 2 * Math.PI,
          intensity: rng() * 3.5 + 1,
        },
        stars: a(Math.round(rng() * rng() * 64 + 64), () => ({
          size: rng() * starMaxSize,
          left: rng() * 100,
          top: rng() * 100,
          background: `hsl(${starHue}deg, ${rng() * 100}%, ${rng() * 50 + 50}%`,
          animdur: rng() * 60,
          animdir: rng() > 0.5 ? "default" : "reverse",
        })),
        hasCoin: true,
      }));
    });

    let paused = false;
    const pauseBanner = document.createElement("div");
    pauseBanner.textContent = "PAUSED";
    pauseBanner.classList.add("pause-banner");

    let planetDivs = [];
    let planetGravities = [];
    let heading;
    let poi = [];

    let droplets = [];

    let starDivs = [];
    let starRadii = [];
    let starRising = [];
    let starSpeed = [];

    // Spawn the coin immediately when entering a new system.
    // at a random location.
    // this way, if the coin isn't gettable, they can just leave the system and reenter to find it again.
    // also, it should help make it easy to see whether you've already collected a system's coin
    // cause if it ain't there when you enter, you got it already.
    // coins don't despawn; if they fly out of a system there's a chance they'll orbit their way back in
    //
    const coin = document.querySelector(".coin");
    const coinControl = { vx: 0.0, vy: 0.0, present: true };
    let coinCenterX, coinCenterY;

    let spaceWeatherDir,
      spaceWeatherPop,
      spaceWeatherForce,
      spaceWeatherSpeed,
      spaceWeatherScale,
      spaceWeatherTop,
      spaceWeatherLeft,
      spaceWeatherDistance,
      spaceWeatherVert,
      spaceWeatherHorz;

    const setDropletDimensions = (droplet, i) => {
      const height = spaceWeatherScale * (6 - 5.75 * spaceWeatherDistance[i]);
      styleElement(
        droplet,
        ["height", `${height}px`],
        ["width", `${height / 20}px`],
      );
    };

    const system = [0, 0];
    const savedSystem = locationUrl.searchParams.get("c");
    if (savedSystem) {
      const savedSystemCoords = savedSystem.match(/\d+/gim);
      system[0] = savedSystemCoords[0];
      system[1] = savedSystemCoords[1];
    }

    const loadSystem = function loadSystem() {
      if (heading) domDrop(heading);
      if (coinControl.present) domDrop(coin);
      coinControl.present = false;

      const [x, y] = system;

      styleElement(document.body, ["background-color", systems[x][y].bg]);

      heading = document.createElement("h1");
      heading.appendChild(new Text(systems[x][y].name));
      document.body.appendChild(heading);

      planetDivs.forEach(domDrop);

      planetDivs = a(systems[x][y].planets.length, makeDiv("planet"));
      planetDivs.forEach((planet, i) => {
        const { size, color, top, left } = systems[x][y].planets[i];
        styleElement(
          planet,
          ["height", `${size}%`],
          ["background-color", color],
          ["top", `${top}%`],
          ["left", `${left}%`],
        );
        document.body.appendChild(planet);
      });

      planetGravities = systems[x][y].planets.map((s, i) => {
        return {
          centerX: planetDivs[i].offsetLeft,
          centerY: planetDivs[i].offsetTop,
          strength: s.gravity,
        };
      });

      poi.forEach(domDrop);
      poi = systems[x][y].pointsOfInterest.map((p) => {
        const pointOfInterest = document.createElement("h2");
        styleElement(
          pointOfInterest,
          ["top", `${p.top}%`],
          ["left", `${p.left}%`],
        );
        pointOfInterest.appendChild(new Text(p.title));
        document.body.appendChild(pointOfInterest);
        return pointOfInterest;
      });

      spaceWeatherDir = systems[x][y].spaceWeather.wind;
      dropletStyle.setProperty("transform", `rotate(${spaceWeatherDir}rad)`);
      spaceWeatherPop = Math.round(systems[x][y].spaceWeather.intensity * 32);
      spaceWeatherForce = systems[x][y].spaceWeather.intensity * 1.3;
      spaceWeatherSpeed = Math.round(spaceWeatherForce);

      spaceWeatherScale = Math.round(systems[x][y].spaceWeather.intensity * 4);
      spaceWeatherTop = a(spaceWeatherPop, () => {
        return rng() * window.innerHeight;
      });

      spaceWeatherLeft = a(spaceWeatherPop, () => {
        return rng() * window.innerWidth;
      });

      spaceWeatherDistance = a(spaceWeatherPop, () => {
        return rng();
      });

      droplets.forEach(domDrop);

      droplets = a(spaceWeatherPop, makeDiv("droplet"));
      droplets.forEach((droplet, i) => {
        styleElement(
          droplet,
          ["left", `${spaceWeatherLeft[i]}px`],
          ["top", `${spaceWeatherTop[i]}px`],
        );

        setDropletDimensions(droplet, i);
        document.body.appendChild(droplet);
      });

      spaceWeatherVert = Math.cos(spaceWeatherDir);
      spaceWeatherHorz = Math.sin(spaceWeatherDir);
      starDivs.forEach(domDrop);

      const starCount = systems[x][y].stars.length;

      starDivs = a(starCount, makeDiv("my-spiky-div"));

      starDivs.forEach((div, i) => {
        const { size, left, top, background, animdur, animdir } =
          systems[x][y].stars[i];
        styleElement(
          div,
          ["width", `${size}vh`],
          ["height", `${size}vh`],
          ["left", `${left}vw`],
          ["top", `${top}vh`],
          ["background", background],
          ["animation-duration", `${animdur}s`],
          ["animation-direction", animdir],
        );

        document.body.appendChild(div);
      });

      starRadii = a(starCount, () => 0);
      starRising = a(starCount, () => true);
      starSpeed = a(starCount, () => rng() * 0.02);

      if (systems[x][y].hasCoin) {
        document.body.appendChild(coin);
        coinCenterX = rng() * (window.innerWidth / 2) + window.innerWidth / 4;
        coinCenterY = rng() * (window.innerHeight / 2) + window.innerWidth / 4;
        coinControl.vx = 0.0;
        coinControl.vy = 0.0;
        coinControl.present = true;
      }

      locationUrl.searchParams.set("c", `${x},${y}`);
      history.replaceState("", "", locationUrl);
    };

    const starshipControl = { vx: 0.0, vy: 2.5 };

    const starship = document.querySelector(".starship");
    let starshipCenterX = 200;
    let starshipCenterY = 400;

    let starshipHeading = 0;
    let starshipThrust = 0;
    let starshipRotation = 0;

    let score = 0;
    const scoreDisplay = document.querySelector(".score-display");

    const accelerate = () => {
      planetGravities.forEach(({ centerX, centerY, strength }) => {
        const xToPlanet = centerX - starshipCenterX;
        const yToPlanet = centerY - starshipCenterY;

        const distToPlanet = (xToPlanet ** 2 + yToPlanet ** 2) ** (1 / 2);
        starshipControl.vx +=
          (strength * (xToPlanet / distToPlanet ** 1.2)) / 15;
        starshipControl.vy +=
          (strength * (yToPlanet / distToPlanet ** 1.2)) / 15;
      });

      starshipControl.vx -= (spaceWeatherHorz * spaceWeatherForce) / 80;
      starshipControl.vy += (spaceWeatherVert * spaceWeatherForce) / 80;

      starshipHeading += starshipRotation * 3;
      starshipThrustX =
        Math.sin((starshipHeading * Math.PI) / 180) * starshipThrust;
      starshipThrustY =
        -Math.cos((starshipHeading * Math.PI) / 180) * starshipThrust;

      starshipControl.vx += starshipThrustX / 12;
      starshipControl.vy += starshipThrustY / 12;

      starshipCenterX += starshipControl.vx;
      starshipCenterY += starshipControl.vy;
      if (coinControl.present) {
        const coinXToShip = starshipCenterX - coinCenterX;
        const coinYToShip = starshipCenterY - coinCenterY;
        const distToShip = (coinXToShip ** 2 + coinYToShip ** 2) ** (1 / 2);
        if (distToShip < 30) {
          domDrop(coin);
          coinControl.present = false;
          systems[system[0]][system[1]].hasCoin = false;
          score += 100;
          scoreDisplay.textContent = `Score: ${score}`;
        } else {
          planetGravities.forEach(({ centerX, centerY, strength }) => {
            const coinXToPlanet = centerX - coinCenterX;
            const coinYToPlanet = centerY - coinCenterY;
            const coinDistToPlanet =
              (coinXToPlanet ** 2 + coinYToPlanet ** 2) ** (1 / 2);
            coinControl.vx +=
              (strength * (coinXToPlanet / coinDistToPlanet ** 1.2)) / 15;
            coinControl.vy +=
              (strength * (coinYToPlanet / coinDistToPlanet ** 1.2)) / 15;
          });

          coinControl.vx -= (spaceWeatherHorz * spaceWeatherForce) / 80;
          coinControl.vy += (spaceWeatherVert * spaceWeatherForce) / 80;
          coinCenterX += coinControl.vx;
          coinCenterY += coinControl.vy;
        }
      }

      if (starshipCenterX < 0) {
        system[0]--;
        system[0] += systems.length;
        system[0] %= systems.length;
        loadSystem();
        starshipCenterX += window.innerWidth;
      } else if (starshipCenterX > window.innerWidth) {
        system[0]++;
        system[0] %= systems.length;
        loadSystem();
        starshipCenterX -= window.innerWidth;
      }

      if (starshipCenterY < 0) {
        system[1]--;
        system[1] += systems[0].length;
        system[1] %= systems[0].length;
        loadSystem();
        starshipCenterY += window.innerHeight;
      } else if (starshipCenterY > window.innerHeight) {
        system[1]++;
        system[1] %= systems[0].length;
        loadSystem();
        starshipCenterY -= window.innerHeight;
      }

      styleElement(
        starship,
        ["top", `${starshipCenterY}px`],
        ["left", `${starshipCenterX}px`],
        ["transform", `rotate(${45 + starshipHeading}deg)`],
      );

      styleElement(
        coin,
        ["top", `${coinCenterY}px`],
        ["left", `${coinCenterX}px`],
      );

      for (let i = 0; i < spaceWeatherTop.length; i++) {
        spaceWeatherTop[i] +=
          spaceWeatherVert *
          spaceWeatherSpeed *
          (6 - 5.75 * spaceWeatherDistance[i]);
      }

      for (let i = 0; i < spaceWeatherTop.length; i++) {
        spaceWeatherLeft[i] -=
          spaceWeatherHorz *
          spaceWeatherSpeed *
          (6 - 5.75 * spaceWeatherDistance[i]);
      }

      for (let i = 0; i < spaceWeatherTop.length; i++) {
        if (spaceWeatherTop[i] > window.innerHeight + 100) {
          spaceWeatherTop[i] -= window.innerHeight + 200;
        } else if (spaceWeatherTop[i] < -100) {
          spaceWeatherTop[i] += window.innerHeight + 200;
        } else if (spaceWeatherLeft[i] > window.innerWidth + 100) {
          spaceWeatherLeft[i] -= window.innerWidth + 200;
        } else if (spaceWeatherLeft[i] < -100) {
          spaceWeatherLeft[i] += window.innerWidth + 200;
        }
      }

      for (let i = 0; i < droplets.length; i++) {
        styleElement(
          droplets[i],
          ["top", `${spaceWeatherTop[i]}px`],
          ["left", `${spaceWeatherLeft[i]}px`],
        );
      }

      starDivs.forEach((div, i) => {
        if (starRadii[i] >= 1.0) {
          starRising[i] = false;
        } else if (starRadii[i] <= 0.0) {
          starRising[i] = true;
        }

        if (starRising[i]) {
          starRadii[i] += starSpeed[i];
        } else {
          starRadii[i] -= starSpeed[i];
        }

        styleElement(div, [
          "clip-path",
          `polygon(${spikyClipPath(5, starRadii[i])})`,
        ]);
      });

      if (!paused) {
        requestAnimationFrame(accelerate);
      }
    };

    document.addEventListener("keydown", ({ code }) => {
      if (code === "ArrowUp" || code === "KeyW") {
        starshipThrust = 1.0;
        starship.classList.add("blasting");
      } else if (code === "ArrowLeft" || code === "KeyA") {
        starshipRotation = -2.0;
      } else if (code === "ArrowRight" || code === "KeyD") {
        starshipRotation = 2.0;
      }
    });

    document.addEventListener("keyup", ({ code }) => {
      if (code === "ArrowUp" || code === "KeyW") {
        starshipThrust = 0.0;
        starship.classList.remove("blasting");
      } else if (code === "ArrowLeft" || code === "KeyA") {
        starshipRotation = 0.0;
      } else if (code === "ArrowRight" || code === "KeyD") {
        starshipRotation = 0.0;
      } else if (code === "KeyP") {
        if (paused) {
          paused = false;
          domDrop(pauseBanner);
          requestAnimationFrame(accelerate);
          starship.classList.remove("paused");
          mySpikyDivStyle.setProperty("animation-play-state", "running");
          h1Style.setProperty("animation-play-state", "running");
        } else {
          paused = true;
          document.body.appendChild(pauseBanner);
          starship.classList.add("paused");
          mySpikyDivStyle.setProperty("animation-play-state", "paused");
          h1Style.setProperty("animation-play-state", "paused");
        }
      }
    });

    loadSystem();

    requestAnimationFrame(accelerate);
    </script>
  </body>
</html>

Here are some old music files from around 2011. I think they're probably terrible. (I haven't played them back myself)

(Actually I removed most of the files because only one was asked for in the assignment requirements)

(Actually actually the assignment was accepted. Hooray! So, I have added the rest of the files back in

Here is a video clip from a terminal animation I made about a month ago.

Here I have pasted the source code for the terminal animation:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>

struct line {
   int start;
   int len;
};

void interrupt_handler(int signo, siginfo_t *info, void *context) {
   // TODO: print a number of newlines equal to the number of lines in the input
   printf("\e[?25h\n");
   exit(EXIT_SUCCESS);
}

void box_marquee_frame(char *buf, char *input, int frame, int line_count, struct line *line_starts, int longest_line) {
   int height, i, j, offset, idx;
   char *boxp, *start, *end, *middle;
   height = 2 + line_count;
   frame %= longest_line;
   boxp = buf;
   for (i = 0; i < height; i++) {
      if (i == 0) {
         start = "  ";
         end = "  \n";
         middle = "_";
      } else if (i == 1) {
         start = " /";
         end = "\\ \n";
         middle = NULL;
      } else if (i == height - 1) {
         start = " \\";
         end = "/ \n";
         middle = "_";
      } else {
         start = "| ";
         end = " |\n";
         middle = NULL;
      }

      boxp = mempcpy(boxp, start, 2);
      if (middle == NULL) {
         middle = " ";
         for (j = 0; j < longest_line; j++) {
            offset = ((frame + j) % longest_line);
            idx = line_starts[i - 1].start + offset;
            if (*(input + idx) == '\n' || offset >= line_starts[i - 1].len) {
               boxp = mempcpy(boxp, middle, 1);
            } else {
               boxp = mempcpy(boxp, input + idx, 1);
            }
         }
      } else {
         for (j = 0; j < longest_line; j++) {
            boxp = mempcpy(boxp, middle, 1);
         }
      }
   
      boxp = mempcpy(boxp, end, 3);
   }

   *boxp = '\0';
}

void rainbow_frame(char *buf, char *input, int frame) {
   int inputlen, chunk, lastchunk, i, j;
   char *bufp;

   char rainbow[6][5] = {
      "\e[31m", // red
      "\e[33m", // brown
      "\e[32m", // green
      "\e[34m", // blue
      "\e[36m", // cyan
      "\e[35m" // magenta
   };

   char reset[4] = "\e[0m";

   bufp = buf;
   inputlen = strlen(input);

   if (inputlen < 6) {
      frame %= 6;
      for (i = 0; i < inputlen; i++) {
         bufp = mempcpy(bufp, rainbow[((5 - i) + frame) % 6], 5);
         bufp = mempcpy(bufp, input + i, 1);
      }
   } else {
      frame %= inputlen;
      for (i = 0; i < 6; i++) {
         chunk = inputlen / 6;
         if (i == 1) chunk += inputlen % 6;
         if (frame + chunk >= inputlen) break;
         frame += chunk;
      }

      lastchunk = inputlen - frame;
      bufp = mempcpy(bufp, rainbow[i], 5);
      frame = chunk - lastchunk;
      bufp = mempcpy(bufp, input, frame);

      for (j = 1; j < 6; j++) {
         chunk = inputlen / 6;
         if ((i + j) % 6 == 1) chunk += inputlen % 6;
         bufp = mempcpy(bufp, rainbow[(i + j) % 6], 5);
         bufp = mempcpy(bufp, input + frame, chunk);
         frame += chunk;
      }

      bufp = mempcpy(bufp, rainbow[i], 5);
      bufp = mempcpy(bufp, input + frame, lastchunk);
   }

   bufp = mempcpy(bufp, reset, 4);
   *bufp = '\0';
}

int main(int argc, char *argv[]) {
   int inputlen, frameskip, frames, seconds, lines, longest_line, i, j;
   char *rainbowbuf, *boxbuf;

   struct sigaction act = { 0 };
   
   struct timespec rainbowtime = { 0, 999999999 };
   long boxdraw_delay;
   long boxtime = 999999999;

   struct line *line_starts;
   
   if (argc < 2) exit(EXIT_FAILURE);
   inputlen = strlen(argv[1]);
   if (inputlen < 1) exit(EXIT_FAILURE);

   lines = 1;
   for (i = 0; i < inputlen; i++) {
      if (argv[1][i] == '\n') lines++;
   }

   line_starts = (struct line *) calloc(lines, sizeof(struct line));
   line_starts[0].start = 0;
   for (i = 0, j = 1; i < inputlen; i++) {
      if (argv[1][i] == '\n') line_starts[j++].start = i + 1;
   }

   longest_line = 0;

   for (i = 1; i < lines; i++) {
      line_starts[i - 1].len = line_starts[i].start - line_starts[i - 1].start;
      if (line_starts[i - 1].len > longest_line) {
         longest_line = line_starts[i - 1].len;
      }
   }

   if (lines == 1) {
      line_starts[0].len = inputlen;
      longest_line = inputlen;
   } else {
      line_starts[lines - 1].len = inputlen - line_starts[lines - 1].start;
      if (line_starts[lines - 1].len > longest_line) {
         longest_line = line_starts[lines - 1].len;
      }
   }

   frameskip = 1 + (inputlen / 175);
   
   frames = inputlen / frameskip;

   if (inputlen < 6) frames = 6;

   rainbowtime.tv_nsec /= frames;
   boxtime /= 12;

   seconds = 4;
   if (argc > 2) seconds = atoi(argv[2]);
   frames *= seconds;

   boxbuf = (char *) calloc((4 + inputlen) * 5, sizeof(char));
   rainbowbuf = (char *) calloc(( 4 + inputlen) * 5 + 42, sizeof(char));

   printf("\e[?25l");

   boxdraw_delay = 0;
   j = 0;

   act.sa_sigaction = &interrupt_handler;

   sigaction(SIGINT, &act, NULL);

   for (i = 0; i < frames; i++) {
      if (boxdraw_delay <= 0) {
         box_marquee_frame(boxbuf, argv[1], j, lines, line_starts, longest_line);
         boxdraw_delay += boxtime;
         j++;
      }
   
      rainbow_frame(rainbowbuf, boxbuf, i * frameskip);
      printf("%s\n", rainbowbuf);
      nanosleep(&rainbowtime, NULL);
      boxdraw_delay -= rainbowtime.tv_nsec;
      printf("\e[%dA", 3 + lines);
   }

   printf("\e[?25h");
   free(boxbuf);
   free(rainbowbuf);
   free(line_starts);

   for (i = 0; i < 3 + lines; i++) printf("\n");

   exit(EXIT_SUCCESS);
}
          

Here is the contact form that was asked for in the assignment rubrik!

It won't actually save any data submitted; it sends it to zybooks and shows what was sent, so you can try it out! But I don't think there is any good reason to send any actual personal information over to zybooks... (maybe just try some fake information idk)






Best Contact Time