squa.sh - a Pong Clone in Bash

Squa.sh is a Bash shell script Pong clone that I clobbered together this Sunday evening. :)< This has probably been done before, and it is no fun to play at all, but it was quite fun to write.

The name squa.sh is quite appropiate as I have been too lazy to implement an opponent yet, therefore you score each time that you avoid the ball from hitting the wall behind you, and you loose a point when the ball hits this wall.

Squa.sh should work unmodified on Linux, I know for sure that it does not work on NetBSD yet, maybe I try to fix this later, or should you be so kind, send me a patch if you like to fix this yourself. ;)

On your VT100 squa.sh will look something like this:

   score : 7
  ----------------------------------------------------
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                 #  |
 |                                                 #  |
 |                                                 #  |
 |                                                 #  |
 |                                           o     #  |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
 |                                                    |
  ----------------------------------------------------

Squa.sh’s source follows below:

squa.sh (squa.sh) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/bin/bash

# squa.sh - play squash in bash
# Hessel Schut, hessel@isquared.nl, 2009-04-20

case "$(uname -s)" in
  NetBSD)
      cols=$(stty -e | head -1 | cut -d\  -f6)
      rows=$(stty -e | head -1 | cut -d\  -f4)
  ;;
  Linux|*)
      cols=$(tput cols)
      rows=$(tput rows)
  ;;
esac

norm=$(tput rmso)

trap cleanup EXIT TERM INT

cleanup() {
        tput rmso
        tput clear
        tput cnorm

  stty sane

        exit
}

draw_rect() {
  # draw_rect(row column rows columns)
  horchar="-"; vertchar="|"
  for ((row=1; row < $3; row++)) {
      [ $row -eq 1 ] || [ $row -eq $(($3 - 1)) ] && {
          echo -n "$escape[$(($1 + $row));$(($2 + 2))H"
          for ((i=0; i < $4 - 2; i++)); do
                      echo -n $horchar
              done
      } || {
          echo -n "$escape[$(($1 + $row));${2}H$vertchar"
          echo -n "$escape[$(($1 + $row));$(($2 + $4))H$vertchar"
      }
  }
}

draw_paddle() {
  for ((row=pos; row <= pos + 4; row++)) {
      echo -n "$escape[$((row + 1));$((cols - 4))H${1}"
  }
}

upd_ball() {
  # bounce on walls
  [ $ball_row -le 3 ] &&  ((ball_dx = -ball_dx))
  [ $ball_row -ge $((rows - 1)) ] && ((ball_dx = -ball_dx))
  [ $ball_col -le 2 ] && ((ball_dy = -ball_dy))
  # back wall, decrement score too
  [ $ball_col -ge $((cols - 2)) ] && {
      ((ball_dy = -ball_dy))
      ((score--))
      init_ball
  }

  # bounce on paddle
  [ $ball_col -eq $((cols - 5)) ] && {
      [ $ball_row -ge $pos ] && [ $ball_row -le $((pos + 5)) ] && {
          ((ball_dx = -ball_dx))
          ((ball_dy = -ball_dy))
          ((score++))
      }
  }

  (( ball_row += ball_dx ))
  (( ball_col += ball_dy ))
}

init() {
  tput clear
  tput civis

  stty -echo -icanon time 0 min 0

  uparrow=$'\x1b[A'
  downarrow=$'\x1b[B'
  escape=$'\x1b'

  space=" "
  ball="o"
  paddle="#"

  draw_rect 1 0 $rows $cols

  pos=$((rows/2 - 2))
  draw_paddle "$paddle"

  init_ball ; echo -n "$escape[$ball_row;${ball_col}H$ball"

  score=0
}

init_ball() {

  [ ! -z "$ball_row" ] && echo -n "$escape[$ball_row;${ball_col}H$space"
  
        ball_row=$((${RANDOM}%(rows - 8) + 4))
        ball_col=$((${RANDOM}%(cols - 8) + 4))
        ball_dx=-1
        ball_dy=-1
}

init
while :
do
  echo -n "$escape[0;3Hscore :       "
  echo -n "$escape[0;3Hscore : $score"

  key=$(dd bs=3 count=1 2>/dev/null)
  case "$key" in
      $uparrow|w)
          draw_paddle "$space"
          [ $pos -gt 2 ] && ((pos--))
          draw_paddle "$paddle"
      ;;
      $downarrow|s)
          draw_paddle "$space"
          [ $((pos + 5)) -lt $((rows - 1)) ] && ((pos++))
          draw_paddle "$paddle"
      ;;
      $escape|q)
          cleanup
      ;;
  esac

  echo -n "$escape[$ball_row;${ball_col}H${space}"
  upd_ball
  echo -n "$escape[$ball_row;${ball_col}H${ball}"
  
done

Comments