I am writing a zsh script to display ascii art in different colors.
Basically I have ascii text written in block letters and I want to write a script that randomizes the colors of each one using color escaped characters.
The text is
[like this](https://pastebin.com/nCs6kNzu)
1$$$$$$$\ 2 $$$$$$$$\ 3$$\ $$\ 4 $$$$$$\ 5$$\ 6$$$$$$$\
1$$ __$$\2 $$ _____|3$$ | $$ |4$$ __$$\ 5$$ | 6$$ __$$\
1$$ | $$ 2|$$ | 3$$ | $$ |4$$ / $$ |5$$ | 6$$ | $$ |
1$$$$$$$\ 2|$$$$$\ 3$$$$$$$$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$ __$$\2 $$ __| 3$$ __$$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$ | $$ 2|$$ | 3$$ | $$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$$$$$$ 2|$$$$$$$$\ 3$$ | $$ |4 $$$$$$ |5$$$$$$$$\ 6$$$$$$$ |
1\_______/2 \________|3\__| \__|4 \______/ 5\________|6\_______/
I want to make a script that:
Creates an array of six random colors
Randomizes the order of the colors
You'll notice in the ascii there are number characters before each letter e.g. 1 is before 'B', 2 is before 'E'. I want to replace each one with a color so that each 'letter' of the ascii art is a different color.
This is my attempt on the code so far and it's not working:
# Define array of colors
colors=("[31m" "[32m" "[33m" "[34m" "[35m" "[36m")
# Randomize the order of colors in the array
colors=($(printf "%s\n" "${colors[@]}" | shuf))
# Read ASCII art from file or source
ascii_art=$(<ascii_art.txt)
# Function to replace number characters with colors
replace_numbers_with_colors() {
local text="$1"
local result=""
local index=0
for ((i = 0; i < ${#text}; i++)); do
local char="${text:i:1}"
if [[ $char =~ [0-9] ]]; then
result="${result}${colors[index]}$char[0m"
((index++))
else
result="${result}$char"
fi
done
echo "$result"
}
# Replace numbers with colors in ASCII art
colored_ascii_art=$(replace_numbers_with_colors "$ascii_art")
# Print the colored ASCII art
echo -e "$colored_ascii_art"
So far it only colors one letter and doesn't delete the numbers.
and only the first letter of the first line is colored.
Please let me know how to approach this.
I also thought keeping the text in six different elements of an array, then I could iterate through it two dimensionally; by array element and by line number
This can be done as a set of text substitutions: for each part of an ascii-art letter, change a number followed by some text to color codes followed by that text (and maybe some closing color codes).
With that in mind, we can use a zsh
text replacement expansion. Try this:
#!/usr/bin/env zsh
colorByNumbers() {
local c=(red green blue cyan magenta yellow black white)
c=( /(e['reply=("$c[@]")']noe['REPLY=$RANDOM']) )
print -Pr -- \
${(*)1//(#b)([[:digit:]])([^[:digit:]]##)/%F{${c[match[1]]}}${match[2]//\%/%%}%f}
}
text='1$$$$$$$\ 2 $$$$$$$$\ 3$$\ $$\ 4 $$$$$$\ 5$$\ 6$$$$$$$\
1$$ __$$\2 $$ _____|3$$ | $$ |4$$ __$$\ 5$$ | 6$$ __$$\
1$$ | $$ 2|$$ | 3$$ | $$ |4$$ / $$ |5$$ | 6$$ | $$ |
1$$$$$$$\ 2|$$$$$\ 3$$$$$$$$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$ __$$\2 $$ __| 3$$ __$$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$ | $$ 2|$$ | 3$$ | $$ |4$$ | $$ |5$$ | 6$$ | $$ |
1$$$$$$$ 2|$$$$$$$$\ 3$$ | $$ |4 $$$$$$ |5$$$$$$$$\ 6$$$$$$$ |
1\_______/2 \________|3\__| \__|4 \______/ 5\________|6\_______/ '
colorByNumbers $text
Some references:
local c=(...)
- array that maps numbers to color specifiers. Note that array indexes in zsh
start with 1.
red
, green
, ... - the eight supported named colors. This array could also include any of the 256 xterm color numbers supported by most terminals.c=( ... $RANDOM ...)
- randomly shuffles the color array. Based on this answer.${...//.../...}
- parameter expansion. With //
, all instances of the input pattern will be replaced with the output pattern.${(*)...}
- parameter expansion flag that enables extended globbing patterns.(#b)
- turns on backreferences.([[:digit:]])([^[:digit:]]##)
- input pattern.
[[:digit:]]
- a single numeric digit.[^[:digit:]]##
- one or more characters that are not digits.(...)(...)
- groupings used used for backreferences.%F{${c[match[1]]}}${match[2]}%f
- output pattern.
%F
and %f
are zsh
prompt sequences. %F{...}
changes the color to the value in the braces, and %f
returns the color to its prior setting.${c[match[1]]}
- this will be replaced with a value from the c
array of colors. It uses the first group from the input pattern, i.e. the single digit.${match[2]//\%/%%}
- the second group from the input pattern; it's a piece of the text used to form the ascii-art letters. //\%/%%
will escape any %
characters so they are not interpreted as prompt substitutions.%F{cyan}$$ | $$ |%f
.print -P
- prints the argument to stdout
. With -P
, the %F
and %f
prompt sequences will be converted to escape codes for the terminal.