Skip to content

Instantly share code, notes, and snippets.

@thediveo
Created June 25, 2026 17:30
Show Gist options
  • Select an option

  • Save thediveo/77000562168926d174ddf98abd175e4e to your computer and use it in GitHub Desktop.

Select an option

Save thediveo/77000562168926d174ddf98abd175e4e to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# Tiny 2Γ—4 Tetris in one Braille character
# With slow-motion bomb explosion reset
field=0
# Braille layout:
# 1 4
# 2 5
# 3 6
# 7 8
dotbit() {
case "$1,$2" in
0,0) echo 1 ;;
0,1) echo 2 ;;
0,2) echo 4 ;;
0,3) echo 64 ;;
1,0) echo 8 ;;
1,1) echo 16 ;;
1,2) echo 32 ;;
1,3) echo 128 ;;
esac
}
getbit() {
local bit
bit=$(dotbit "$1" "$2")
(( field & bit ))
}
setbit() {
local bit
bit=$(dotbit "$1" "$2")
(( field |= bit ))
}
piece_fits() {
local x=$1 y=$2 w=$3 h=$4
(( x < 0 || y < 0 || x + w > 2 || y + h > 4 )) && return 1
local dx dy
for ((dy=0; dy<h; dy++)); do
for ((dx=0; dx<w; dx++)); do
getbit $((x+dx)) $((y+dy)) && return 1
done
done
return 0
}
explode() {
local frames=(
"πŸ’£"
"πŸ’£"
"πŸ’₯"
"πŸ”₯"
"✨"
" "
)
# half-speed animation (was ~0.10–0.15)
local delays=(
0.20
0.30
0.30
0.20
0.20
0.10
)
local i
for ((i=0; i<${#frames[@]}; i++)); do
printf '\r%s ' "${frames[i]}"
sleep "${delays[i]}"
done
}
spawn_piece() {
while :; do
case $((RANDOM % 4)) in
0) pw=1; ph=1 ;;
1) pw=1; ph=2 ;;
2) pw=2; ph=1 ;;
3) pw=2; ph=2 ;;
esac
px=$(( RANDOM % (3 - pw) ))
py=0
if piece_fits "$px" "$py" "$pw" "$ph"; then
return 0
fi
explode
field=0
done
}
lock_piece() {
local dx dy
for ((dy=0; dy<ph; dy++)); do
for ((dx=0; dx<pw; dx++)); do
setbit $((px+dx)) $((py+dy))
done
done
}
clear_rows() {
local old=$field
local newfield=0
local dest=3
local x y
for ((y=3; y>=0; y--)); do
local full=1
for x in 0 1; do
local bit
bit=$(dotbit "$x" "$y")
(( old & bit )) || full=0
done
(( full )) && continue
for x in 0 1; do
local bit
bit=$(dotbit "$x" "$y")
if (( old & bit )); then
local dbit
dbit=$(dotbit "$x" "$dest")
(( newfield |= dbit ))
fi
done
((dest--))
done
field=$newfield
}
render() {
local mask=$field
local dx dy
for ((dy=0; dy<ph; dy++)); do
for ((dx=0; dx<pw; dx++)); do
local bit
bit=$(dotbit $((px+dx)) $((py+dy)))
(( mask |= bit ))
done
done
perl -CS -e '
print "\r";
print chr(0x2800 + shift);
print " ";
' "$mask"
}
cleanup() {
tput cnorm 2>/dev/null
printf '\n'
}
trap cleanup EXIT INT TERM
tput civis 2>/dev/null
spawn_piece
while true; do
render
sleep 0.20 # half-speed main loop
if piece_fits "$px" $((py + 1)) "$pw" "$ph"; then
((py++))
else
lock_piece
clear_rows
spawn_piece
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment