#!/bin/sed -nf
# arkanoid.sed - http://aurelio.net/bin/sed/arkanoid/
#   by Aurelio Marinho Jargas <verde (a) aurelio net>
#
# Arkanoid: a paddle that bounces a ball (pong!) to destroy blocks
#
# ACTORS:
#   o  ball               --  paddle
#   =  block              ~~  paddle (on hold mode)
#   *  block exploding    .   life (mini paddle)
#                                  [come on, use your imagination :)]
# COMMANDS:
#   z  left move      c  hold mode ON/OFF
#   x  right move     q  quit
#
# HOW TO PLAY:
#   You must press the ENTER key after *every* command. Start the
#   program with "sed -f arkanoid.sed". Press ENTER. Choose the
#   level. Press ENTER. Move the paddle with the keyboard arrows
#   or with the Z and X keys. Press ENTER. Release the ball using
#   the down arrow ou the C key. Press ENTER. Press ENTER. Press
#   ENTER. Press ENTER...
#
# ANTI-FLAME DISCLAIMER:
#   Well, just as sokoban.sed, this one is cool because it's sed.
#   It's not funny to play or even exciting, because keep pressing
#   ENTER to the ball move sucks. But hey, it's cute! &;)
#
# For the how-many-lines-it-has? fanatics:
#   prompt$ sed '/^ *#/d' arkanoid.sed | sed '$=;d'
#
# Tip: Use hold mode and hit the paddle twice on the wall
#      to escape cyclic ball movement
#
# TODO Get classic Arkanoid levels (somebody help me!)
#
# CHANGELOG:
#   2002-07-09 v1.0: Debut release
#   2005-05-13 v1.1: Now it's a sedcheck compliant script.
#                    It will run on most SED's, including oldies.
#   2013-02-12 v1.2: Removed ISO-8859-1 char, now we're plain ASCII.

b zero

:nocolor
### if you don't want colorful output, just uncomment next line
#s/K(\([0-9;]*\))//g
b showmap

:parsecmd
### arrow keys (1), numeric pad (2), i-case (3), wipe trash(4)
// {
    s/\[A/h/g
    s/\[B/h/g
    s/\[C/x/g
    s/\[D/z/g
}  
y/4560/zhxq/
y/QZXHCc/qzxhhh/
s/[^qzxh#]//g
b execcmd

:welcome
i\
       Welcome to the SED Arkanoid\
  \
  Please select a level to begin [1-2]:
d  

:showhelp
### clear screen, show help
i\
  
x  
s|.*|\
  ...                  Sed Arkanoid\
  ...                  \
  ...                    z,4,<left>   left move\
  ...                    x,6,<right>  right move\
  ...                    c,5,<down>   hold on/off\
  ...                    q,0          quit game\
  ...                    <enter>      go!\
  \
  |p
s/.*//
x  
b end


:loadmap
### load map on HOLD space
/^1$/ {
    s/.*/\
      _____________ \
     |             |\
     |             |\
     |=============|\
     |=============|\
     |=============|\
     |             |\
     |             |\
     |             |\
     |             |\
     |      o      |\
     |     ~~      |\
     |...__________|\
  /
    b endmap
}  
/^2$/ {
    s/.*/\
      _____________ \
     |=            |\
     |===          |\
     |=====        |\
     |=======      |\
     |=========    |\
     |===========  |\
     |=============|\
     |             |\
     |             |\
     |      o      |\
     |     ~~      |\
     |...__________|\
  /
    b endmap
}  

### test levels
#
# free X test
/^t1$/ {
    s/.*/\
  TEST 1 - f:X\
      _____________ \
     |       = = = |\
     |      o      |\
     |       = = = |\
     |             |\
     |.____________|\
  /
    b endmap
}  
# free Y test
/^t2$/ {
    s/.*/\
  TEST 2 - f:Y\
      _____________ \
     |             |\
     |  ==  =      |\
     |   =  ==     |\
     |     ==      |\
     |   =o=       |\
     |  ==         |\
     |             |\
     |____.________|\
  /
    b endmap
}  
# tri-explosion test
/^t3$/ {
    s/.*/\
 TEST 3 - 3\
      _____________ \
     |     ==      |\
     |     ==      |\
     |    ==  =    |\
     |     = ==    |\
     |  =o         |\
     |  ==         |\
     |             |\
     |.____________|\
  /
    b endmap
}  
# round explosion test
/^t4$/ {
    s/.*/\
 TEST 4 - >, <, b:>, b:<\
      _____________ \
     |     = =     |\
     |   == o ==   |\
     |   = = = =   |\
     |    == ==    |\
     |             |\
     |___________._|\
  /
    b endmap
}  
/~~/ !{
    s/.*/there is no '&' level!/p
    q
}  

:endmap
s/^//
s/  $/ru /
h  
b showhelp

#--------------------------------------------------------------------
#
### general hints about the code:
#
# - rlud are movements to perform on this turn
# - RLUD are movements already done
# - O is a ball already moved on this turn
# - @% are temporary ball states to indicate ball over a block
# - ! is a temporary explosion state after a free Y move
# - -- is plain paddle and ~~ is paddle on hold mode
# - ... are your 3 lives (mini-paddles)
# - # is the cheat mode identifier (jail!)
# - on hold, when releasing the ball, its X-direction will be same of
#   the paddle side where the ball is
# - on hold, forcing paddle on the wall can move ball to the other side
#   of the paddle
# - on hold mode, Y-direction is always down
# - explosions are showed, then reconverted to ball on the next move
# - ball on vertical and top borders do bounce
# - block explosion and paddle (of course) bounces
# - bounce: change X or Y direction
# - exploding a block may change Y-direction and/or X-direction, depending
#   if there's another block on the way (see explosion comments)
# - diagonal moves are done as X then Y moves (that's why % is needed)
# - K() pattern is replaced by color escape chars <esc>[m
# - < and > are round explosion direction. stored at the end (.$) of the map
# - ball direction (rlud) is stored at the end (.. $) of the map also
# - user commands are case insensitive


# ballmiss routine: 1. reset direction and explosion status
# 2. put ball over paddle , 3. take one life, 4. put paddle on hold mode
#
:ballmiss
s/...$/ru /
s/\*/ /
s/ \(.\{20\}\)\([-~]\)/o\1\2/
s/\._/__/
s/--/~~/
b end

:zero
1 b welcome

# first map loading
2 b loadmap

b parsecmd

:execcmd
### execute user commands

### first, have you missed a ball?
x  
/\*\(.\{20\}\)[_.]/ {
    /#/ !b ballmiss
    # cheat mode: never loses
    y/d*/uo/
    b execcmd
}  
x  

/q/ q

# you cheater! (keep holding enter - hyper-speed)
/#/ {
    x
    /#/ !s/\(.*\)|/\1#/
}  

# 1. move ball+paddle on hold, and goto end
# 2. move just paddle and continue
/z/ {
    x
    /o.\{20\}~/ {
        s/ o/o /
        s/ ~~/~~ /
        b end
    }
    s/ --/-- /
    s/ ~~/~~ /
}  
/x/ {
    x
    /o.\{20\}~/ {
        s/o / o/
        s/~~ / ~~/
        b end
    }
    s/-- / --/
    s/~~ / ~~/
}  
/h/ {
    x
    ### turn OFF hold mode routine
    /~~/ {
        # depending which side of paddle, change X-direction
        /o.\{20\}~~/ y/Rr/LL/
        /o.\{20\}~[^~]/ y/Ll/RR/
        # change Y-direction
        /o.\{20\}~/ y/Dd/UU/
    }
    # swap ON/OFF paddle shape
    y/-~/~-/
}  
/./ !x


# current explosion turns back to ball
/o/ s/\*/ /g
s/\*/o/
# reset movements
y/RLUD/rlud/

# hold ball, no move
/o.\{20\}~/ b end

### LET'S MOVE IT!

# reached top: do bounce changing Y-direction
/_\(.\{20\}\)o/ y/u/d/

### detect borders, change X-direction
/|o/ y/l/r/
/o|/ y/r/l/

# plain right-left ball move
/r/ s/o / O/
/l/ s/ o/O /

# tricky move over a block - part I
/r/ {
    s/o=/ @/
    s/% /=o/
}  
/l/ {
    s/=o/@ /
    s/ %/o=/
}  

# restore ball and mark X moves as done
y/O@rl/o%RL/

b boom


### detect block. if so, explode it!
#
#     0   1     RIGHT-UP example:
#   +---+---+
#   | = | * |   0. free X-way
#   |o  |  o|   1. explode and change Y-direction           (right-down)
#   +-------+
#   |== |** |   0. blocked X and Y way (tri-explosion)
#   |o= |o* |   1. explode all and change X and Y direction (left-down)
#   +-------+
#   |   |o  |
#   | = | * |   0. free Y-way
#   |o= | = |   1. explode and change X-direction           (left-up)
#   +---+---+
#
### RIGHT-UP Smart clockwise full round explosion!
#
#   +---+---+---+---+---+
#   | = | * |   |   |   |  If two explosions on two turns,
#   |o =|  =|  *|   |o  |   we're on a round explosion.
#   | = | = | = | * |   |  After the roll, we end as left-up.
#   +---+---+---+---+---+
#

### explosion functions (wise is clockwise [>] or anti-clockwise [<])
# chkwise: if wise direction is empty, set it! - else dowise
# setwise: set wise direction, based on current ball direction
#  dowise: set the next ball direction, based on wise direction
#
:chkwise
/ $/ b setwise
b dowise

# */! : ball came from X/Y free
:setwise
/\*/ {
    /Ld $/ s/ $/>/
    /Ru $/ s/ $/>/
    /Rd $/ s/ $/</
    /Lu $/ s/ $/</
    y/ud/du/
}  
/!/ {
    /Ld $/ s/ $/</
    /Ru $/ s/ $/</
    /Rd $/ s/ $/>/
    /Lu $/ s/ $/>/
    y/RL/LR/
}  
y/ud/UD/
b end

:dowise
/Rd<$/ y/d/U/
/Lu<$/ y/u/D/
/Ru<$/ y/R/L/
/Ld<$/ y/L/R/
/Ld>$/ y/d/U/
/Ru>$/ y/u/D/
/Rd>$/ y/R/L/
/Lu>$/ y/L/R/
b end


# UP & DOWN explosion code:
#   1. free X
#   2. blocked XY from left  (tri-explosion)
#   3. blocked XY from right (tri-explosion)
#   4. free Y
#
:boom
/d/ {
    /o\(.\{20\}\)=/ s// \1*/
    /% \(.\{19\}\)==\(.*L\)/ s//*o\1**\2/
    / %\(.\{19\}\)==\(.*R\)/ s//o*\1**\2/
    /%\(.\{20\}\)=/ s//=\1!/
}  
/u/ {
    /=\(.\{20\}\)o/ s//*\1 /
    /==\(.\{19\}\)% \(.*L\)/ s//**\1*o\2/
    /==\(.\{19\}\) %\(.*R\)/ s//**\1o*\2/
    /=\(.\{20\}\)%/ s//!\1=/
}  

# end tri-explosion: invert all directions (keep before "end free X" code)
/\*\*/ {
    y/udRL/DULR/
    b end
}  
# end free Y explosion
/! .*L/ b chkwise
/ !.*R/ b chkwise
# end free X explosion
/\*/ b chkwise


# detect paddle bounce, change Y-direction
/o\(.\{20\}\)-/ y/d/u/

# plain up-down ball move (no explosion)
/u/ s/ \(.\{20\}\)o/o\1 /
/d/ s/o\(.\{20\}\) / \1o/

# tricky move over a block - part II
/u/ s/ \(.\{20\}\)%/o\1=/
/d/ s/%\(.\{20\}\) /=\1o/

# you missed the ball!
/o\(.\{20\}\)[_.]/ y/o/*/


:end
# show move over a block explosion right
y/!/*/
# reset last explosion reminder for plain moves
/\*/ !s/[<>]$/ /

### detect level finished or GAME OVER
/=/ !s/\(\([^\n]*\n\)\{5\}[^|]*|\).........../\1K(33;1)  VICTORY!!K()/
/\./ !s/\(\([^\n]*\n\)\{5\}[^|]*|\).........../\1K(31;1)  GAME OVERK()/

### save map
h  

### hide direction (comment for debug)
s/...$//

### add colors
s/--/K(32;1)&K()/g
s/~~/K(32;1)&K()/g
s/o/K(33;1)&K()/g
s/ \{0,1\}_\{1,\} \{0,1\}/K(42;32)&K()/g
s/|/K(42;32)&K()/g
s/\./K(42;32;1)&K()/g
s/#/K(42;37;1)&K()/g
s/\*/K(31;1)&K()/g
b nocolor

:showmap
s/K(\([0-9;]*\))/[\1m/g
p  

/VICT/ q
/OVER/ q
d  

### colorized by sedsed, a SED script debugger/indenter/tokenizer/HTMLizer