Gay Invaders: Terminal Game with Deterministic Colors

This example demonstrates how Gay.jl's splittable random colors can be used to add vibrant, reproducible color palettes to terminal games.

Inspiration

This example was inspired by Lilith Hafner's JuliaCon talk on SpaceInvaders.jl, which demonstrated building a terminal game in pure Julia. We extend it with Gay.jl's deterministic color system to add:

  • Reproducible palettes: Same seed = same colors every time
  • Pride flag colors: Ship and bullets use trans pride colors
  • Rainbow effects: Explosions cycle through rainbow colors
  • Per-row enemy colors: Each row gets a unique deterministic color

Game Screenshot

Here's what Gay Invaders looks like in action (colors render in terminal with ANSI true-color support):

╔══════════════════════════════════════════════════════════════════════════════╗
║                                                                              ║
║         🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                            ║
║         🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                            ║
║         🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                            ║
║         🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                            ║
║          🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                             ║
║          🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯 🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                             ║
║           🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯 🙯🙯🙯🙯 🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                              ║
║             🙯🙯🙯🙯 🙯🙯🙯  🙯🙯   🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯🙯                               ║
║             🙯🙯🙯🙯 🙯🙯 🙯 🙯  🙯 🙯  🙯🙯🙯🙯🙯🙯🙯 🙯                                ║
║              🙯 🙯    🙯    🙯  🙯 🙯🙯🙯🙯🙯🙯🙯                                  ║
║                               🢙    🙯🙯                                    ║
║                        🙯 🙯  ✦         🙯                                    ║
║                                                                              ║
║                               🢙                                              ║
║                                                                              ║
║                               🙭                                              ║
║                                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝

Game Elements:

  • 🙯 - Enemies (each row colored by color_at(row; seed=seed))
  • 🙭 - Ship (trans pride light blue)
  • 🢙 - Bullets (trans pride pink)
  • - Explosions (cycling rainbow colors)
  • Rainbow border (cycles through pride colors)

Quick Start

using Gay
include(joinpath(pkgdir(Gay), "examples", "spaceinvaders_colors.jl"))
GayInvaders.main()

Controls

KeyAction
← / AMove left
→ / DMove right
↑ / W / SpaceFire
QQuit

How It Works

First, we set up color helpers that convert Gay.jl RGB colors to ANSI escape codes:

using Gay
using Colors: RGB

function rgb_to_ansi(c::RGB)
    r = round(Int, clamp(c.r, 0, 1) * 255)
    g = round(Int, clamp(c.g, 0, 1) * 255)
    b = round(Int, clamp(c.b, 0, 1) * 255)
    "\e[38;2;$(r);$(g);$(b)m"
end
rgb_to_ansi (generic function with 1 method)

Deterministic Enemy Colors

Each enemy row gets a color from color_at(row; seed=seed). This ensures the same seed always produces the same color palette:

seed = 42
gay_seed!(seed)

println("Enemy row colors (seed=$seed):")
for row in 1:6
    c = color_at(row; seed=seed)
    ansi = rgb_to_ansi(c)
    println("  Row $row: $(ansi)████████\e[0m")
end
Enemy row colors (seed=42):
  Row 1: ████████
  Row 2: ████████
  Row 3: ████████
  Row 4: ████████
  Row 5: ████████
  Row 6: ████████

Pride Flag Integration

The ship and bullets use colors from the transgender pride flag, making the game a celebration of identity:

trans = transgender()
println("\nTrans pride colors for ship/bullets:")
for (i, c) in enumerate(trans)
    ansi = rgb_to_ansi(c)
    println("  $(ansi)████\e[0m")
end

Trans pride colors for ship/bullets:
  ████
  ████
  ████
  ████
  ████

Ship uses light blue (trans[3]), bullets use pink (trans[1]):

println("\nShip: $(rgb_to_ansi(trans[3]))🙭\e[0m  Bullet: $(rgb_to_ansi(trans[1]))🢙\e[0m")

Ship: 🙭  Bullet: 🢙

Rainbow Explosions

When enemies are hit, explosions cycle through rainbow colors:

println("\nExplosion colors (rainbow):")
for (i, c) in enumerate(rainbow())
    ansi = rgb_to_ansi(c)
    print("$(ansi)✦\e[0m ")
end
println()

Explosion colors (rainbow):
     

Reproducibility Demo

The key feature is reproducibility - same seed always gives same colors:

println("\n--- Reproducibility Test ---")
for s in [42, 1337, 2024]
    print("Seed $s: ")
    for i in 1:6
        c = color_at(i; seed=s)
        print("$(rgb_to_ansi(c))●\e[0m")
    end
    println()
end

--- Reproducibility Test ---
Seed 42: 
Seed 1337: 
Seed 2024: 

Running the Full Game

The complete interactive game is in examples/spaceinvaders_colors.jl:

using Gay
include(joinpath(pkgdir(Gay), "examples", "spaceinvaders_colors.jl"))

# Play with default colors
GayInvaders.main()

# Play with different color palette
GayInvaders.main(seed=1337)

# Skip intro animation
GayInvaders.main(splash=false)

Credits

The combination of terminal games with reproducible color palettes opens up creative possibilities for accessible, visually distinctive game experiences.