summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Doc/Zsh/contrib.yo20
-rw-r--r--Functions/Misc/.distfiles1
-rw-r--r--Functions/Misc/tetris243
4 files changed, 268 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 331551bab..e0412abde 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2001-09-21  Andrew Main (Zefram)  <zefram@zsh.org>
+
+	* Doc/Zsh/contrib.yo, Functions/Misc/tetris: Tetris game for ZLE.
+
 2001-09-20  Bart Schaefer  <schaefer@zsh.org>
 
 	* 15847: Src/init.c: Avoid using the word "termcap" in an error
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 4f91c7680..013a37979 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -791,6 +791,26 @@ See `Accessing On-Line Help'
 ifzman(above)\
 ifnzman((noderef(Utilities))).
 )
+item(tt(tetris))(
+Zsh was once accused of not being as complete as Emacs,
+because it lacked a Tetris game.  This function was written to
+refute this vicious slander.
+
+This function must be used as a ZLE widget:
+
+example(autoload -U tetris
+zle -N tetris
+bindkey var(keys) tetris)
+
+To start a game, execute the widget by typing the var(keys).  Whatever command
+line you were editing disappears temporarily, and your keymap is also
+temporarily replaced by the Tetris control keys.  The previous editor state
+is restored when you quit the game (by pressing `tt(q)') or when you lose.
+
+If you quit in the middle of a game, the next invocation of the tt(tetris)
+widget will continue where you left off.  If you lost, it will start a new
+game.
+)
 findex(zcalc)
 item(tt(zcalc) [ var(expression) ... ])(
 A reasonably powerful calculator based on zsh's arithmetic evaluation
diff --git a/Functions/Misc/.distfiles b/Functions/Misc/.distfiles
index 5040576b5..9de53001a 100644
--- a/Functions/Misc/.distfiles
+++ b/Functions/Misc/.distfiles
@@ -4,4 +4,5 @@ allopt       harden       nslookup     zkbd         zstyle+
 checkmail    is-at-least  run-help     zmv
 colors       mere         zed          zrecompile
 getjobs      promptnl
+tetris
 '
diff --git a/Functions/Misc/tetris b/Functions/Misc/tetris
new file mode 100644
index 000000000..a9d367964
--- /dev/null
+++ b/Functions/Misc/tetris
@@ -0,0 +1,243 @@
+# Someone once accused zsh of not being as complete as Emacs, because it
+# lacks Tetris and an adventure game.
+#
+# autoload -U tetris
+# zle -N tetris
+# bindkey '...' tetris
+
+emulate -L zsh
+
+tetris_hsz=11
+tetris_vsz=20
+typeset -ga tetris_shapes
+tetris_shapes=(
+	0x0f00 0x4444 0x0f00 0x4444
+	0x4e00 0x4c40 0x0e40 0x4640
+	0x6600 0x6600 0x6600 0x6600
+	0x4620 0x6c00 0x4620 0x6c00
+	0x2640 0x6300 0x2640 0x6300
+	0x6440 0x8e00 0x44c0 0x0e20
+	0xc440 0x0e80 0x4460 0x2e00
+)
+typeset -gA tetris_rotations
+tetris_rotations=(
+	0x0f00 0x4444 0x4444 0x0f00
+	0x4e00 0x4c40 0x4c40 0x0e40 0x0e40 0x4640 0x4640 0x4e00
+	0x6600 0x6600
+	0x4620 0x6c00 0x6c00 0x4620
+	0x2640 0x6300 0x6300 0x2640
+	0x6440 0x8e00 0x8e00 0x44c0 0x44c0 0x0e20 0x0e20 0x6440
+	0xc440 0x0e80 0x0e80 0x4460 0x4460 0x2e00 0x2e00 0xc440
+)
+
+tetris_blankline=
+for ((tetris_i=tetris_hsz; tetris_i--; )); do
+	tetris_blankline="$tetris_blankline "
+done
+tetris_blankboard=
+for ((tetris_i=tetris_vsz; tetris_i--; )); do
+	tetris_blankboard="$tetris_blankboard$tetris_blankline"
+done
+
+bindkey -N tetris
+bindkey -R -M tetris '\000-\377' tetris-timeout
+for ((tetris_i=256; tetris_i--; )); do
+	bindkey -M tetris 'T\'$(([##8]tetris_i)) tetris-timeout
+done
+bindkey -M tetris Ta tetris-left
+bindkey -M tetris Tj tetris-left
+bindkey -M tetris Ts tetris-rotate
+bindkey -M tetris Tk tetris-rotate
+bindkey -M tetris Td tetris-right
+bindkey -M tetris Tl tetris-right
+bindkey -M tetris 'T ' tetris-drop
+bindkey -M tetris Tq tetris-quit
+
+unset tetris_board tetris_score
+
+zle -N tetris
+function tetris {
+	emulate -L zsh
+	if ! zle; then
+		print -u2 "Use M-x tetris RET to play tetris."
+		return 2
+	fi
+	tetris_saved_state="BUFFER=${BUFFER:q};CURSOR=${CURSOR:q};MARK=${MARK:q};zle -K ${KEYMAP:q}"
+	tetris_speed=$((100.0/KEYTIMEOUT))
+	zle -K tetris
+	if [[ ${tetris_board+set} == set ]]; then
+		tetris-timeout
+	else
+		tetris_board=$tetris_blankboard
+		tetris_score=0
+		tetris-new-block
+	fi
+}
+
+function tetris-new-block {
+	emulate -L zsh
+	tetris_block=$tetris_shapes[1+RANDOM%$#tetris_shapes]
+	tetris_block_y=0
+	tetris_block_x=4
+	if ! tetris-block-fits; then
+		tetris-place-block "#"
+		tetris-render-screen
+		unset tetris_board tetris_score
+		tetris-quit
+		return
+	fi
+	tetris-place-block "*"
+	tetris-timed-move
+}
+
+zle -N tetris-left
+function tetris-left {
+	emulate -L zsh
+	tetris-place-block " "
+	(( tetris_block_x-- ))
+	tetris-block-fits || (( tetris_block_x++ ))
+	tetris-place-block "*"
+	tetris-timeout
+}
+
+zle -N tetris-right
+function tetris-right {
+	emulate -L zsh
+	tetris-place-block " "
+	(( tetris_block_x++ ))
+	tetris-block-fits || (( tetris_block_x-- ))
+	tetris-place-block "*"
+	tetris-timeout
+}
+
+zle -N tetris-rotate
+function tetris-rotate {
+	emulate -L zsh
+	tetris-place-block " "
+	local save_block=$tetris_block
+	tetris_block=$tetris_rotations[$tetris_block]
+	tetris-block-fits || tetris_block=$save_block
+	tetris-place-block "*"
+	tetris-timeout
+}
+
+zle -N tetris-drop
+function tetris-drop {
+	emulate -L zsh
+	tetris-place-block " "
+	((tetris_block_y++))
+	while tetris-block-fits; do
+		((tetris_block_y++))
+	done
+	((tetris_block_y--))
+	tetris-block-dropped
+}
+
+zle -N tetris-timeout
+function tetris-timeout {
+	emulate -L zsh
+	tetris-place-block " "
+	((tetris_block_y++))
+	if tetris-block-fits; then
+		tetris-place-block "*"
+		tetris-timed-move
+		return
+	fi
+	((tetris_block_y--))
+	tetris-block-dropped
+}
+
+function tetris-block-dropped {
+	emulate -L zsh
+	tetris-place-block "O"
+	local fl=${tetris_blankline// /O} i=$((tetris_block_y*tetris_hsz)) y
+	for ((y=0; y!=4; y++)); do
+		if [[ $tetris_board[i+1,i+tetris_hsz] == $fl ]]; then
+			tetris_board[i+1,i+tetris_hsz]=
+			tetris_board=$tetris_blankline$tetris_board
+			((tetris_score++))
+		fi
+		((i += tetris_hsz))
+	done
+	tetris-new-block
+}
+
+function tetris-block-fits {
+	emulate -L zsh
+	local y x i=$((1+tetris_block_y*tetris_hsz+tetris_block_x)) b=0x8000
+	for ((y=0; y!=4; y++)); do
+		for ((x=0; x!=4; x++)); do
+			if ((tetris_block&b)); then
+				((x+tetris_block_x >= 0)) || return 1
+				((x+tetris_block_x < tetris_hsz)) || return 1
+				((y+tetris_block_y >= 0)) || return 1
+				((y+tetris_block_y < tetris_vsz)) || return 1
+				[[ $tetris_board[i] == " " ]] || return 1
+			fi
+			((b >>= 1))
+			((i++))
+		done
+		((i+=tetris_hsz-4))
+	done
+	return 0
+}
+
+function tetris-place-block {
+	emulate -L zsh
+	local y x i=$((1+tetris_block_y*tetris_hsz+tetris_block_x)) b=0x8000
+	for ((y=0; y!=4; y++)); do
+		for ((x=0; x!=4; x++)); do
+			((tetris_block&b)) && tetris_board[i]=$1
+			((b >>= 1))
+			((i++))
+		done
+		((i+=tetris_hsz-4))
+	done
+}
+
+function tetris-timed-move {
+	emulate -L zsh
+	tetris-render-screen
+	LBUFFER=
+	RBUFFER=$'\n'$tetris_screen
+	zle -R
+	zle -U T
+}
+
+function tetris-render-screen {
+	emulate -L zsh
+	setopt extendedglob
+	local s i extras
+	extras=(
+		"Score: $tetris_score"
+		""
+		"Game parameters: ${tetris_hsz}x$tetris_vsz, ${tetris_speed}Hz"
+		""
+		"Keys:   left: a j"
+		"      rotate: s k"
+		"       right: d l"
+		"        drop: space"
+		"        quit: q"
+	)
+	for ((i=0; i!=tetris_vsz; i++)); do
+		s="$s|${${${${${tetris_board[1+i*tetris_hsz,(i+1)*tetris_hsz]}//O/()}//\*/**}// /  }//\#/##}|"${extras[1]+   $extras[1]}$'\n'
+		extras[1]=()
+	done
+	s="$s+${tetris_blankline// /--}+"
+	tetris_screen=$s
+}
+
+zle -N tetris-quit
+function tetris-quit {
+	emulate -L zsh
+	if [[ ! -o always_last_prompt ]]; then
+		BUFFER=
+		zle -M $tetris_screen
+	fi
+	eval $tetris_saved_state
+	if [[ -o always_last_prompt ]]; then
+		zle -M $tetris_screen
+	fi
+}
+
+tetris "$@"