Last active
March 10, 2025 13:10
-
-
Save fasterthanlime/bcbc16c936bd28e3a117a505ebac8b01 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn marine_line() -> String { | |
use rand::{seq::SliceRandom, Rng}; | |
let marine_life = ["π³", "π ", "π¦", "π", "π‘", "π¬", "π", "π¦", "π"]; | |
let water = [ | |
"\x1B[38;2;0;100;200mβ\x1B[0m", | |
"\x1B[38;2;10;110;210mβ\x1B[0m", | |
"\x1B[38;2;20;120;220mβ\x1B[0m", | |
"\x1B[38;2;30;130;230mβ\x1B[0m", | |
"\x1B[38;2;40;140;240mβ\x1B[0m", | |
"\x1B[38;2;50;150;250mβ\x1B[0m", | |
"\x1B[38;2;60;160;255mβ\x1B[0m", | |
"\x1B[38;2;70;170;255mβ\x1B[0m", | |
"\x1B[38;2;80;180;255mβ\x1B[0m", | |
"\x1B[38;2;90;190;255mβ\x1B[0m", | |
"\x1B[38;2;100;200;255mβ\x1B[0m", | |
"\x1B[38;2;110;210;255mβ\x1B[0m", | |
"\x1B[38;2;120;220;255mβ\x1B[0m", | |
"\x1B[38;2;130;230;255mβ\x1B[0m", | |
"\x1B[38;2;140;240;255mβ\x1B[0m", | |
]; | |
let mut buffer = String::new(); | |
let mut last_generated = ""; | |
for i in 0..80 { | |
let position = i as f32 / 80.0; | |
let middle_weight = 1.0 - (position - 0.5).abs() * 2.0; | |
let chance = 0.125 + middle_weight * 0.125; // Ranges from 0.125 to 0.25 | |
let set = if rand::thread_rng().gen::<f32>() < (chance * 0.5) { | |
&marine_life[..] | |
} else { | |
&water[..] | |
}; | |
let generated = *set.choose(&mut rand::thread_rng()).unwrap(); | |
if generated == last_generated { | |
buffer.push_str(water.choose(&mut rand::thread_rng()).unwrap()); | |
} else { | |
buffer.push_str(generated); | |
last_generated = generated; | |
} | |
} | |
buffer | |
} |
Great and nice idea!
From my googling an emoji will take up 2 text/character blocks.
Thus the earlier implementations will take up different lengths.
Here is my python implementation that fixes this:
def marine_line() -> str:
marine_life = ("π³", "π ", "π¦", "π", "π‘", "π¬", "π", "π¦", "π")
water = (
"\x1b[38;2;0;100;200mβ\x1b[0m",
"\x1b[38;2;10;110;210mβ\x1b[0m",
"\x1b[38;2;20;120;220mβ\x1b[0m",
"\x1b[38;2;30;130;230mβ\x1b[0m",
"\x1b[38;2;40;140;240mβ\x1b[0m",
"\x1b[38;2;50;150;250mβ\x1b[0m",
"\x1b[38;2;60;160;255mβ\x1b[0m",
"\x1b[38;2;70;170;255mβ\x1b[0m",
"\x1b[38;2;80;180;255mβ\x1b[0m",
"\x1b[38;2;90;190;255mβ\x1b[0m",
"\x1b[38;2;100;200;255mβ\x1b[0m",
"\x1b[38;2;110;210;255mβ\x1b[0m",
"\x1b[38;2;120;220;255mβ\x1b[0m",
"\x1b[38;2;130;230;255mβ\x1b[0m",
"\x1b[38;2;140;240;255mβ\x1b[0m",
)
buffer = ""
last_generated = ""
emoji_count = 0
ascii_count = 0
total_count = 0
max_length = 80
for i in range(max_length):
position = i / max_length
middle_weight = 1.0 - abs(position - 0.5) * 2.0
chance = 0.125 + middle_weight * 0.125
if max_length-1 == total_count:
set_ = water
elif random.random() < (chance * 0.5):
set_ = marine_life
else:
set_ = water
generated = random.choice(set_)
if generated == last_generated:
buffer += random.choice(water)
ascii_count += 1
else:
buffer += generated
if generated in marine_life:
emoji_count += 1
else:
ascii_count += 1
last_generated = generated
total_count = emoji_count*2 + ascii_count
if total_count == 80:
break
assert total_count == 80
return buffer
...and here is a Clojure version with two themes (:desert
and :marine
):
(ns theme
(:require [clojure.string :as str])
(:import (java.util Random)))
(def ^:private desert-life ["π΅", "π", "οΈπͺ", "π«", "β±οΈ", "π¦", "π¦‘", "βοΈ", "π"])
(def ^:private marine-life ["π³", "π ", "π¦", "π", "π‘", "π¬", "π", "π¦", "π"])
; 1 2 3 4 5 6 7 8 9
(def ^:private desert-colors ["#DD692C"
"#D48662"
"#D9C3A9"
"#D9B6A3"
"#BF754B"
"#BF9B7A"
"#C16630"
"#A6583C"
"#A67458"
"#734434"
"#723715"
"#734B34"
"#732F17"
"#783215"
"#8B322C"])
(def ^:private marine-colors ["#0064C8"
"#0A6ED3"
"#1478DC"
"#1E82E6"
"#288CF0"
"#3296FA"
"#3CA0FF"
"#46AAFF"
"#50B4FF"
"#5ABEFF"
"#64C8FF"
"#6ED2FF"
"#78DCFF"
"#82E6FF"
"#8CF0FF"])
(defn- next-int [seed bound]
(.nextInt
(Random. (long (.nextDouble (Random. seed) Long/MAX_VALUE)))
bound))
(defn- next-double [seed]
(.nextDouble
(Random. (long (.nextDouble (Random. seed) Long/MAX_VALUE)))))
(defn- random-choice [seed vvec]
(nth vvec (next-int seed (count vvec))))
(defn- gen-block [seed life-vec life-chance]
(assert (>= life-chance 0))
(assert (<= life-chance 1))
(if (< (next-double seed) life-chance)
(let [idx (next-int seed (count life-vec))]
(assert (>= idx 0))
(assert (< idx (count life-vec)))
[(str idx) (str idx) "."])
["." "." "."]))
(defn- gen-marine [seed life-vec life-chance max-length]
(loop [i 0
buf []]
(if (< i (inc max-length))
(recur (inc i) (into buf (gen-block (+ i seed) life-vec life-chance)))
buf)))
(defn- parse-hex [hx]
(assert (= 2 (count hx)))
(let [mapp {"0" 0 "1" 1 "2" 2 "3" 3 "4" 4 "5" 5 "6" 6 "7" 7 "8" 8 "9" 9
"A" 10 "B" 11 "C" 12 "D" 13 "E" 14 "F" 15
"a" 10 "b" 11 "c" 12 "d" 13 "e" 14 "f" 15}
high-nibble (str (nth hx 0))
low-nibble (str (nth hx 1))]
(assert (contains? mapp high-nibble))
(assert (contains? mapp low-nibble))
(str (+
(* 16 (get mapp high-nibble))
(get mapp low-nibble)))))
(defn- hex-str-to-ansi [hstr]
(assert (string? hstr))
(assert (= 7 (count hstr)))
(assert (str/starts-with? hstr "#"))
(let [r (parse-hex (subs hstr 1 3))
g (parse-hex (subs hstr 3 5))
b (parse-hex (subs hstr 5 7))]
(str "\033[38;2;" r ";" g ";" b "m" "β\033[0m")))
(def ^:private themes {:desert [desert-colors desert-life]
:marine [marine-colors marine-life]})
(defn gen-theme-line [theme max-length & [life-chance seed]]
(assert (contains? #{:desert :marine} theme))
(let [[background life] (get themes theme)
life-chance (or life-chance 0.5)
seed (or seed (rand-int 1000000))
add (mod seed 3)
new-seed (quot seed 3)
str-marine (gen-marine new-seed life life-chance max-length)
lin1 (str/join "" (loop [i 0
buf []]
(if (< i max-length)
(recur (+ i 2)
(into buf [(nth str-marine (+ i add))
(nth str-marine (+ 1 i add))]))
buf)))
lin2 (loop [s lin1
idx 0]
(if (< idx (count life))
(recur
(-> s
(str/replace (str idx idx) "##")
(str/replace (str idx) ".")
(str/replace "##" (str idx idx)))
(inc idx))
s))
lin3 (loop [idx 0
char-count 0
lin ""]
(if (= char-count max-length)
lin
(if (= idx (dec (count lin2))) ; end of line
(recur (inc idx)
(inc char-count)
(str lin (hex-str-to-ansi (random-choice (+ idx seed) background))))
(let [c (str (nth lin2 idx))]
(if (or (= c ".")
(> (+ 2 char-count) max-length))
(recur (inc idx) (inc char-count) (str lin (hex-str-to-ansi (random-choice (+ idx seed) background))))
(recur (+ 2 idx) (+ 2 char-count) (str lin (str (nth life (parse-long c))))))))))]
lin3))
(defn demo! [_]
(let [rnd (rand-int 1000000)]
(dotimes [i 15]
(println (gen-theme-line :marine 80 0.35 (+ rnd i))))))
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Or in Python: