Created
June 25, 2026 17:30
-
-
Save thediveo/77000562168926d174ddf98abd175e4e to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #!/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