Recently, while waiting for a long-running script to finish, I thought that it would be nice to have some sort of loader with aging emojis. TLDR: we-are-waiting.
The โlifeโ of an emoji is simple:
๐ถ๐ฟ โ ๐ง๐ฟ โ ๐ฉ๐ฟ โ ๐ฑ๐ฟโโ๏ธ โ ๐ฉ๐ฟโโ๏ธ โ ๐ต๐ฟ
It contains aging from a baby to grown-up person, one profession, and oldness.
And as we have four colors, two genders, five ages, and 22 professions. We can have a great variety of lives. So as the first thing to do I decided to generate all those variants. Initially, I was planning to implement everything in Go, but itโs not possible to use emojis in Go code, only codepoints. Because of that, I decided to write a little Python script, that will generate Go code with all variants with codepoints instead of emojis.
For that I just copied lines with emojis from getemoji.com and put them in lists:
ages = [
"๐ถ ๐ฆ ๐ง ๐จ ๐ฉ ๐ฑโโ๏ธ ๐ฑ ๐ด ๐ต",
"๐ถ๐ป ๐ฆ๐ป ๐ง๐ป ๐จ๐ป ๐ฉ๐ป ๐ฑ๐ปโโ๏ธ ๐ฑ๐ป ๐ด๐ป ๐ต๐ป",
"๐ถ๐ผ ๐ฆ๐ผ ๐ง๐ผ ๐จ๐ผ ๐ฉ๐ผ ๐ฑ๐ผโโ๏ธ ๐ฑ๐ผ ๐ด๐ผ ๐ต๐ผ",
"๐ถ๐ฝ ๐ฆ๐ฝ ๐ง๐ฝ ๐จ๐ฝ ๐ฉ๐ฝ ๐ฑ๐ฝโโ๏ธ ๐ฑ๐ฝ ๐ด๐ฝ ๐ต๐ฝ",
"๐ถ๐พ ๐ฆ๐พ ๐ง๐พ ๐จ๐พ ๐ฉ๐พ ๐ฑ๐พโโ๏ธ ๐ฑ๐พ ๐ด๐พ ๐ต๐พ",
"๐ถ๐ฟ ๐ฆ๐ฟ ๐ง๐ฟ ๐จ๐ฟ ๐ฉ๐ฟ ๐ฑ๐ฟโโ๏ธ ๐ฑ๐ฟ ๐ด๐ฟ ๐ต๐ฟ",
]
ages = [x.split(' ') for x in ages]
roles = [
"๐ฎโโ๏ธ ๐ฎ ๐ทโโ๏ธ ๐ท ๐โโ๏ธ ๐ ๐ต๏ธโโ๏ธ ๐ต๏ธ ๐ฉโโ๏ธ ๐จโโ๏ธ ๐ฉโ๐พ ๐จโ๐พ ๐ฉโ๐ณ ๐จโ๐ณ ๐ฉโ๐ ๐จโ๐ ๐ฉโ๐ค ๐จโ๐ค ๐ฉโ๐ซ ๐จโ๐ซ ๐ฉโ๐ญ ๐จโ๐ญ ๐ฉโ๐ป ๐จโ๐ป ๐ฉโ๐ผ ๐จโ๐ผ ๐ฉโ๐ง ๐จโ๐ง ๐ฉโ๐ฌ ๐จโ๐ฌ ๐ฉโ๐จ ๐จโ๐จ ๐ฉโ๐ ๐จโ๐ ๐ฉโโ๏ธ ๐จโโ๏ธ ๐ฉโ๐ ๐จโ๐ ๐ฉโโ๏ธ ๐จโโ๏ธ ๐คถ ๐
๐ธ ๐คด",
"๐ฎ๐ปโโ๏ธ ๐ฎ๐ป ๐ท๐ปโโ๏ธ ๐ท๐ป ๐๐ปโโ๏ธ ๐๐ป ๐ต๐ปโโ๏ธ ๐ต๐ป ๐ฉ๐ปโโ๏ธ ๐จ๐ปโโ๏ธ ๐ฉ๐ปโ๐พ ๐จ๐ปโ๐พ ๐ฉ๐ปโ๐ณ ๐จ๐ปโ๐ณ ๐ฉ๐ปโ๐ ๐จ๐ปโ๐ ๐ฉ๐ปโ๐ค ๐จ๐ปโ๐ค ๐ฉ๐ปโ๐ซ ๐จ๐ปโ๐ซ ๐ฉ๐ปโ๐ญ ๐จ๐ปโ๐ญ ๐ฉ๐ปโ๐ป ๐จ๐ปโ๐ป ๐ฉ๐ปโ๐ผ ๐จ๐ปโ๐ผ ๐ฉ๐ปโ๐ง ๐จ๐ปโ๐ง ๐ฉ๐ปโ๐ฌ ๐จ๐ปโ๐ฌ ๐ฉ๐ปโ๐จ ๐จ๐ปโ๐จ ๐ฉ๐ปโ๐ ๐จ๐ปโ๐ ๐ฉ๐ปโโ๏ธ ๐จ๐ปโโ๏ธ ๐ฉ๐ปโ๐ ๐จ๐ปโ๐ ๐ฉ๐ปโโ๏ธ ๐จ๐ปโโ๏ธ ๐คถ๐ป ๐
๐ป ๐ธ๐ป ๐คด๐ป",
"๐ฎ๐ผโโ๏ธ ๐ฎ๐ผ ๐ท๐ผโโ๏ธ ๐ท๐ผ ๐๐ผโโ๏ธ ๐๐ผ ๐ต๐ผโโ๏ธ ๐ต๐ผ ๐ฉ๐ผโโ๏ธ ๐จ๐ผโโ๏ธ ๐ฉ๐ผโ๐พ ๐จ๐ผโ๐พ ๐ฉ๐ผโ๐ณ ๐จ๐ผโ๐ณ ๐ฉ๐ผโ๐ ๐จ๐ผโ๐ ๐ฉ๐ผโ๐ค ๐จ๐ผโ๐ค ๐ฉ๐ผโ๐ซ ๐จ๐ผโ๐ซ ๐ฉ๐ผโ๐ญ ๐จ๐ผโ๐ญ ๐ฉ๐ผโ๐ป ๐จ๐ผโ๐ป ๐ฉ๐ผโ๐ผ ๐จ๐ผโ๐ผ ๐ฉ๐ผโ๐ง ๐จ๐ผโ๐ง ๐ฉ๐ผโ๐ฌ ๐จ๐ผโ๐ฌ ๐ฉ๐ผโ๐จ ๐จ๐ผโ๐จ ๐ฉ๐ผโ๐ ๐จ๐ผโ๐ ๐ฉ๐ผโโ๏ธ ๐จ๐ผโโ๏ธ ๐ฉ๐ผโ๐ ๐จ๐ผโ๐ ๐ฉ๐ผโโ๏ธ ๐จ๐ผโโ๏ธ ๐คถ๐ผ ๐
๐ผ ๐ธ๐ผ ๐คด๐ผ",
"๐ฎ๐ฝโโ๏ธ ๐ฎ๐ฝ ๐ท๐ฝโโ๏ธ ๐ท๐ฝ ๐๐ฝโโ๏ธ ๐๐ฝ ๐ต๐ฝโโ๏ธ ๐ต๐ฝ ๐ฉ๐ฝโโ๏ธ ๐จ๐ฝโโ๏ธ ๐ฉ๐ฝโ๐พ ๐จ๐ฝโ๐พ ๐ฉ๐ฝโ๐ณ ๐จ๐ฝโ๐ณ ๐ฉ๐ฝโ๐ ๐จ๐ฝโ๐ ๐ฉ๐ฝโ๐ค ๐จ๐ฝโ๐ค ๐ฉ๐ฝโ๐ซ ๐จ๐ฝโ๐ซ ๐ฉ๐ฝโ๐ญ ๐จ๐ฝโ๐ญ ๐ฉ๐ฝโ๐ป ๐จ๐ฝโ๐ป ๐ฉ๐ฝโ๐ผ ๐จ๐ฝโ๐ผ ๐ฉ๐ฝโ๐ง ๐จ๐ฝโ๐ง ๐ฉ๐ฝโ๐ฌ ๐จ๐ฝโ๐ฌ ๐ฉ๐ฝโ๐จ ๐จ๐ฝโ๐จ ๐ฉ๐ฝโ๐ ๐จ๐ฝโ๐ ๐ฉ๐ฝโโ๏ธ ๐จ๐ฝโโ๏ธ ๐ฉ๐ฝโ๐ ๐จ๐ฝโ๐ ๐ฉ๐ฝโโ๏ธ ๐จ๐ฝโโ๏ธ ๐คถ๐ฝ ๐
๐ฝ ๐ธ๐ฝ ๐คด๐ฝ",
"๐ฎ๐พโโ๏ธ ๐ฎ๐พ ๐ท๐พโโ๏ธ ๐ท๐พ ๐๐พโโ๏ธ ๐๐พ ๐ต๐พโโ๏ธ ๐ต๐พ ๐ฉ๐พโโ๏ธ ๐จ๐พโโ๏ธ ๐ฉ๐พโ๐พ ๐จ๐พโ๐พ ๐ฉ๐พโ๐ณ ๐จ๐พโ๐ณ ๐ฉ๐พโ๐ ๐จ๐พโ๐ ๐ฉ๐พโ๐ค ๐จ๐พโ๐ค ๐ฉ๐พโ๐ซ ๐จ๐พโ๐ซ ๐ฉ๐พโ๐ญ ๐จ๐พโ๐ญ ๐ฉ๐พโ๐ป ๐จ๐พโ๐ป ๐ฉ๐พโ๐ผ ๐จ๐พโ๐ผ ๐ฉ๐พโ๐ง ๐จ๐พโ๐ง ๐ฉ๐พโ๐ฌ ๐จ๐พโ๐ฌ ๐ฉ๐พโ๐จ ๐จ๐พโ๐จ ๐ฉ๐พโ๐ ๐จ๐พโ๐ ๐ฉ๐พโโ๏ธ ๐จ๐พโโ๏ธ ๐ฉ๐พโ๐ ๐จ๐พโ๐ ๐ฉ๐พโโ๏ธ ๐จ๐พโโ๏ธ ๐คถ๐พ ๐
๐พ ๐ธ๐พ ๐คด๐พ",
"๐ฎ๐ฟโโ๏ธ ๐ฎ๐ฟ ๐ท๐ฟโโ๏ธ ๐ท๐ฟ ๐๐ฟโโ๏ธ ๐๐ฟ ๐ต๐ฟโโ๏ธ ๐ต๐ฟ ๐ฉ๐ฟโโ๏ธ ๐จ๐ฟโโ๏ธ ๐ฉ๐ฟโ๐พ ๐จ๐ฟโ๐พ ๐ฉ๐ฟโ๐ณ ๐จ๐ฟโ๐ณ ๐ฉ๐ฟโ๐ ๐จ๐ฟโ๐ ๐ฉ๐ฟโ๐ค ๐จ๐ฟโ๐ค ๐ฉ๐ฟโ๐ซ ๐จ๐ฟโ๐ซ ๐ฉ๐ฟโ๐ญ ๐จ๐ฟโ๐ญ ๐ฉ๐ฟโ๐ป ๐จ๐ฟโ๐ป ๐ฉ๐ฟโ๐ผ ๐จ๐ฟโ๐ผ ๐ฉ๐ฟโ๐ง ๐จ๐ฟโ๐ง ๐ฉ๐ฟโ๐ฌ ๐จ๐ฟโ๐ฌ ๐ฉ๐ฟโ๐จ ๐จ๐ฟโ๐จ ๐ฉ๐ฟโ๐ ๐จ๐ฟโ๐ ๐ฉ๐ฟโโ๏ธ ๐จ๐ฟโโ๏ธ ๐ฉ๐ฟโ๐ ๐จ๐ฟโ๐ ๐ฉ๐ฟโโ๏ธ ๐จ๐ฟโโ๏ธ ๐คถ๐ฟ ๐
๐ฟ ๐ธ๐ฟ ๐คด๐ฟ",
]
roles = [x.split(' ') for x in roles]
As emojis have a strange order, generation of all variants is a bit tricky, but itโs easier than rearranging them in code because my editor doesnโt work quite well with emojis:
def get_life(color, gender, role):
yield ages[color][0]
yield ages[color][1 + gender]
yield ages[color][3 + gender]
yield ages[color][6 - gender]
yield roles[color][role * 2 + 1 - gender]
yield ages[color][7 + gender]
>>> list(get_life(0, 0, 0))
['๐ถ', '๐ฆ', '๐จ', '๐ฑ', '๐ฎ', '๐ด']
def get_variants():
for color in range(len(ages)):
for gender in (0, 1):
for role in range(len(roles[0]) // 2):
yield color, gender, role
>>> list(get_life(*list(get_variants())[23]))
['๐ถ', '๐ง', '๐ฉ', '๐ฑ\u200dโ๏ธ', '๐ท\u200dโ๏ธ', '๐ต']
And after that itโs very easy to generate Go package with all possible variants:
code = b'package variants\n\nvar All = [][]string{\n'
for variant in get_variants():
code += b'\t{\n'
for emoji in get_life(*variant):
code += b'\t\t"' + emoji.encode('unicode-escape') + b'",\n'
code += b'\t},\n'
code += b'}\n'
with open('variants/variants.go', 'wb') as f:
f.write(code)
So weโll have something like this in variants/variants.go
:
package variants
var All = [][]string{
{
"\U0001f476",
"\U0001f466",
"\U0001f468",
"\U0001f471",
"\U0001f46e",
"\U0001f474",
},
...
}
The logic of the loader isnโt that interesting, although I want to highlight some moments. At the high level
we just read lines from a pipe, if thereโs no new line arrived before tick
seconds, we update our emojis:
func main() {
...
go watchApp(lines)
for {
select {
case line, isOpen := <-lines:
...
os.Stdout.WriteString(line)
...
printPeople(people)
case <-time.After(time.Duration(*tick) * time.Second):
people = updatePeople(people, *count)
printPeople(people)
}
}
}
While updating, we can add new emoji, make one emoji older or โkillโ the oldest:
func updatePeople(people []*human, count int) []*human {
addNew := rand.Intn(5) == 0
toMakeOlder := canMakeOlder(people)
if addNew || len(toMakeOlder) == 0 {
people = append(people, getRandomHuman())
} else {
index := toMakeOlder[rand.Intn(len(toMakeOlder))]
people[index].position += 1
}
if len(people) > count {
oldest := getOldest(people)
return append(people[:oldest], people[oldest+1:]...)
} else {
return people
}
}
And thatโs all. You can find the source code on GitHub.