Skip to content

Instantly share code, notes, and snippets.

@markusfisch
Last active December 26, 2023 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markusfisch/0316a791501e383b195d0fce6a30b5b0 to your computer and use it in GitHub Desktop.
Save markusfisch/0316a791501e383b195d0fce6a30b5b0 to your computer and use it in GitHub Desktop.
Explore a random snippet from five different popular open source projects like a dungeon in a roguelike

Guess the file

Explore a snippet of source code like a dungeon in a roguelike. Looks like this:

              e


                   e


                      e    t


                    :




                             ...
                             .@.
                             os.
                     .                 .





                                   .

                                           .


                                                i

Can you guess which programming language or project a file comes from?

Move with WASD or HJKL. As soon as you think you know what file it is, press Enter and you will see a range of options from which you can choose.

The character . is displayed after the end of a line. All your steps are counted.

How to run it?

Run it like this:

$ bash what_file.sh

Customize

You can use a custom set of URLs (to source code files) by giving a file name:

$ bash what_file.sh my_urls.txt

Where my_urls.txt is the name of a file containing the URLs. One URL per line.

#!/usr/bin/env bash
# Fallback for systems without `tput` (man terminfo).
type tput &>/dev/null || tput() {
case "$1" in
cols) echo 80;;
lines) echo 24;;
clear) printf '\033[2J\033[;H';; # Clear screen and move cursor top/left.
civis) printf '\033[?25l';; # Hide cursor.
cnorm) printf '\033[?25h';; # Restore cursor to normal.
cup) printf '\033[%d;%dH' $(($2 + 1)) $(($3 + 1));; # Move to y/x.
esac
}
# Restore terminal defaults and end quiz.
restore() {
# Clear screen and show cursor.
tput clear
tput cnorm
# Reset to default values.
stty sane
}
# Move cursor
#
# @param 1 - vertical coordinate
# @param 2 - horizontal coordinate
cup() {
tput cup "$((PAD_TOP + $1))" "$((PAD_LEFT + $2))"
}
# Reveal current position.
reveal() {
local RADIUS=1
local TOP=$((PY - RADIUS))
TOP=$((TOP > -1 ? TOP : 0))
local LEFT=$((PX - RADIUS))
LEFT=$((LEFT > -1 ? LEFT : 0))
local BOTTOM=$((PY + RADIUS))
BOTTOM=$((BOTTOM < HEIGHT ? BOTTOM : HEIGHT - 1))
local RIGHT=$((PX + RADIUS))
RIGHT=$((RIGHT < WIDTH ? RIGHT : WIDTH - 1))
local LEN=$((RIGHT - LEFT + 1)) Y
for ((Y=TOP; Y <= BOTTOM; ++Y))
do
cup "$Y" "$LEFT"
echo -n "${MAP:$((Y * WIDTH + LEFT)):$LEN}"
done
}
# Move player
#
# @param 1 - horizontal displacement
# @param 2 - vertical displacement
move() {
local X=$((PX + $1)) Y=$((PY + $2))
((X > -1 && X < WIDTH && Y > -1 && Y < HEIGHT)) || return
PX=$X
PY=$Y
((++MOVES))
reveal
}
# Process input from STDIN.
input() {
read -r -n 1
case "$REPLY" in
''|q) return 1;;
[kw]) move 0 -1;;
[ld]) move 1 0;;
[ha]) move -1 0;;
[js]) move 0 1;;
esac
return 0
}
# Draw player.
draw_player() {
cup "$PY" "$PX"
echo -n '@'
}
# Inspect map.
inspect() {
# Stop if STDIN isn't a terminal.
[ -t 0 ] || return 1
# Stop echoing typed characters.
stty -echo
# Clear screen and hide cursor.
tput clear
tput civis
# Reveal some random places to give some orientation.
local PX PY
for ((I=10; I-- > 0;))
do
PX=$((RANDOM % WIDTH))
PY=$((RANDOM % HEIGHT))
cup "$PY" "$PX"
echo -n "${MAP:$((PY * WIDTH + PX)):1}"
done
# Center player on screen.
PX=$((WIDTH / 2))
PY=$((HEIGHT / 2))
reveal
while true
do
draw_player
input || break
done
restore
}
# Ask the user what file it was.
query() {
echo 'Was it...'
echo
local ANSWERS=3
local CORRECT=$((RANDOM % ANSWERS))
local WRONG=$((RANDOM % URLS_SIZE))
local I=0
for ((I=0; I < ANSWERS; ++I, ++WRONG))
do
local OPTION
if ((I == CORRECT))
then
OPTION=$URL
else
# Search for a wrong answer that has not come before.
while true
do
OPTION=${URLS[$((WRONG % URLS_SIZE))]}
[[ $OPTION != "$URL" ]] && break
((++WRONG))
done
fi
OPTION=${OPTION%%#*}
echo "$I) ${OPTION##*/}"
done
# Read answer.
echo
while true
do
read -r -n 1 -p 'Your choice? (q to give up and quit) '
echo
case "$REPLY" in
[0-9])
if ((REPLY == CORRECT))
then
echo 'Right!'
((++HITS))
else
echo "That was WRONG. It was $CORRECT."
fi
break
;;
q)
exit
;;
*)
echo 'Beg your pardon?'
;;
esac
done
sleep 1
}
# Read from STDIN into map.
map_file() {
# Create blank backstore.
local BLANKS='.'
while ((${#BLANKS} < WIDTH))
do
BLANKS="$BLANKS$BLANKS"
done
# Read snippet into map.
MAP=
local I=0
while read -r
do
# Skip over all lines before OFFSET.
((--OFFSET > 0)) && continue
# Expand tabs.
REPLY=${REPLY//$'\t'/${BLANKS:0:4}}
# Fill to fit width.
local FILL=$((WIDTH - ${#REPLY}))
((FILL > 0)) && REPLY=$REPLY${BLANKS:0:$FILL}
MAP="$MAP${REPLY:0:WIDTH}"
((++I > HEIGHT)) && break
done
# Fill the remaining lines of the map.
for ((; I < HEIGHT; ++I))
do
MAP="$MAP${BLANKS:0:WIDTH}"
done
}
# Download and map file.
download() {
echo 'Downloading challenge ...'
local TMP="${0##*/}_challenge_$$"
curl "${URL%#*}" > "$TMP" || exit 1
local OFFSET=${URL##*#L}
map_file < "$TMP"
rm -f "$TMP"
}
# Build and run the quiz.
#
# @param 1 - file with source URLs (optional)
lobby() {
declare -a URLS=(
'https://raw.githubusercontent.com/numpy/numpy/0032ede015c9b06f88cc7f9b07138ce35f4357ae/numpy/matlib.py#L24'
'https://raw.githubusercontent.com/pointfreeco/swift-snapshot-testing/59b663f68e69f27a87b45de48cb63264b8194605/Sources/SnapshotTesting/Snapshotting.swift#L4'
'https://raw.githubusercontent.com/Javacord/Javacord/971ea44e5d671d380382e0617fcaeaad070c8d02/javacord-core/src/main/java/org/javacord/core/util/FileContainer.java#L29'
'https://raw.githubusercontent.com/flutter/flutter/62d699961f5a9c7e56a61c5acfcc86518458756f/packages/flutter/lib/src/widgets/async.dart#L383'
'https://raw.githubusercontent.com/facebook/react/c5b9375767e2c4102d7e5559d383523736f1c902/packages/react-is/src/ReactIs.js#L29'
)
(($# > 0)) && IFS=$'\n' read -d '' -r -a URLS < "$1"
local URLS_SIZE=${#URLS[@]}
((URLS_SIZE < 3)) && {
echo 'error: you need to have at least 3 files' >&2
exit 1
}
# Show instructions.
tput clear
cat << EOL
Explore a $URLS_SIZE snippets of source code like a dungeon in a roguelike.
Can you guess which programming language or project a file comes from?
Move with WASD or HJKL, and press Enter as soon as you think you know what
file it is. You will see a range of options from which you can choose.
The character '.' is displayed after the end of a line.
All your steps are counted.
EOL
read -r -n 1 -p 'Hit a key to start...'
echo
# Seed random generator so everybody gets the same game.
RANDOM=1
# Run the quiz.
readonly MAX_WIDTH=40 MAX_HEIGHT=30
local WIDTH=${COLUMNS:-$(tput cols)}
local PAD_LEFT=$(((WIDTH - MAX_WIDTH) / 2))
PAD_LEFT=$((PAD_LEFT < 0 ? 0 : PAD_LEFT))
WIDTH=$((WIDTH > MAX_WIDTH ? MAX_WIDTH : WIDTH))
local HEIGHT=${LINES:-$(tput lines)}
local PAD_TOP=$(((HEIGHT - MAX_HEIGHT) / 2))
PAD_TOP=$((PAD_TOP < 0 ? 0 : PAD_TOP))
HEIGHT=$((HEIGHT > MAX_HEIGHT ? MAX_HEIGHT : HEIGHT))
local MOVES=0
local HITS=0
local MAP
local URL
for URL in "${URLS[@]}"
do
download
inspect
query
done
echo
echo "You got $HITS of $URLS_SIZE correct and made $MOVES moves."
}
if [ "${BASH_SOURCE[0]}" == "$0" ]
then
lobby "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment