2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ASCIIアート】ターミナル上にビールサーバーを構築する

2
Last updated at Posted at 2025-12-30

はじめに

開発に行き詰まって、ビールを飲みたくなった経験はありませんか?
でも仕事中だし...

今回は、仕事中でもビールを飲んでる気分が味わえる、「ターミナルビール」をshell scriptで作ってみたので、作り方を共有したいと思います。

何を作ったのか

実際に作ったものはこちらです。
output.gif

今回は、ターミナルで動かしたいということもあって、Shell Scriptオンリーで実装しています。

Shell Scriptの基礎

echoもしくはprintfで描画しています。
すなわち、文字列を工夫することでビールを表現しているのです。
文字列を制御する上で必要になる知識は以下です。

文字列のカラー

例えば、echoで黄色のHello Worldを打ちたかったら、

echo -e "\033[33mHello World\033[0m"

と記述します。
image.png
すると、ご覧の通り、黄色いHello Worldがターミナルで表示されます。

Hello Worldの前についている
\033[33mが黄色を表すコマンド、
Hello Worldの後についている
\033[0mが書体をリセットするコマンドです。
今回用いたカラーは以下のとおりです。

カラー コマンド
\033[33m 黄色
\033[34m 青色
\033[37m 白色

初見殺しですが、もう少し踏ん張ってください!

カーソル移動

文字列を出力する際、カーソルの制御ができます。例えば、

echo -e "Hello\033[1BWorld"

と打つと、
image.png
Helloの後、カーソルが下に移動して、Worldが描画されています。

\033[1B

がカーソルを下にするコマンドです。
カーソル移動をまとめると、

カーソル移動 コマンド
\033[1A
\033[1B
\033[1C
\033[1D
となります。

これを駆使すると
image.png
このような段々の描画もできるようになります。

@sudo00 さんのこちらの記事に詳しくまとまっています。今回も参考にさせていただきました。

また、カーソル移動は
tput cup X座標 Y座標
でも可能です。

今回は、大まかな位置をtput cupで指定し、相対移動を前述のコマンドで行いました。

変数定義

これまで、カラーとカーソル移動の説明をしました。しかしながら、毎回\033[1Aとか打つのはめんどくさいですよね。

ですので、これらを変数として定義します。

readonly YELLOW="\033[33m" #黄色
readonly BLUE="\033[34m"   #黄色
readonly WHITE="\033[37m"  #白色
readonly RESET="\033[0m"   #リセット
readonly UP="\033[1A"      #カーソル上
readonly DOWN="\033[1B"    #カーソル下
readonly RIGHT="\033[1C"   #カーソル右
readonly LEFT="\033[1D"    #カーソル左

readonlyは、読み込み専用を表します。

変数定義の際の=の前後はスペースを入れないように!

変数を定義することで、

echo -e "${YELLOW}Hello World${RESET}"

このようにして、黄色いHello Worldが表現できることになります。

変数は${変数}で呼び出すことが可能です。

随分見通しが良くなりましたね。

ターミナルの縦横サイズ

tput linesで縦幅、tput colsで横幅が取得できます。
image.png
この場合だと縦30行、横78列ですね。

計算

shell scriptでの計算は少し厄介です。

$((1 + 1))

のように、$(())で計算式をくくることで計算できます。
また、小数が扱えませんので、すべて整数で表現しないといけないことになります。
image.png

もし小数を扱いたい場合は、bcコマンドを使うことで計算できます。
echo "1.2+1" | bc
こんな感じ。

小数が使えないので、計算の順番も工夫しなければなりません。
例えば、

echo "$((10 / 100 * 500))"

は、左から計算されますので、10 / 100 = 00 * 500 = 0となってしまいます。
これを防ぐために掛け算を先に持ってきます。

echo "$((500 * 100 / 10))"

描画

Shell Scriptの基礎知識を学んだところで、実際に描画していきます。

変数定義

描画をしていく前に、変数を定義していきます。上でも述べましたが、色、カーソルの他に、注ぐ時間やジョッキの高さ&横幅も定義しています。

# !/bin/bash
readonly H_TER=$(tput lines)                      #ターミナルの縦幅
readonly W_TER=$(tput cols)                       #ターミナルの横幅
w1=$((W_TER * 30 / 100))                          #計算用w
readonly MIN_W_MUG=$((w1 % 2 == 0 ? w1 : w1 + 1)) #ビールジョッキの最大の横幅(形は後で変える)必ず偶数
readonly H_MUG=$((H_TER * 40 / 100))              #ビールジョッキの縦幅
readonly Y_BOTTOM=$((H_TER - 5))                  #ジョッキの底の位置
readonly POUR_SPEED=500                           #注ぐ速度
readonly MOVE_SPEED=$((MIN_W_MUG / 3 * 2))        #ビールジョッキ登場スピード

readonly YELLOW="\033[33m" #黄色
readonly BLUE="\033[34m"   #黄色
readonly WHITE="\033[37m"  #白色
readonly RESET="\033[0m"   #リセット
readonly UP="\033[1A"      #カーソル上
readonly DOWN="\033[1B"    #カーソル下
readonly RIGHT="\033[1C"   #カーソル右
readonly LEFT="\033[1D"    #カーソル左

ビールジョッキの横幅

ビールジョッキの横幅は、高さが高くなるにつれ大きくしたいので、

function get_beer_w() {
  local h=$1                            #ある高さ
  local w=$((h * 60 / 100 + MIN_W_MUG)) #その時の横幅
  if ((w % 2 == 1)); then
    #奇数だとズレちゃう
    w=$((w + 1))
  fi
  echo "$w"
}

このような関数としました。
※横幅を偶数にしていますが、その理由は
Screenshot 2025-12-30 21.30.48.png
奇数だとこんな感じで横幅が対称とならないためです。

  • 引数は$1,$2,.と受け取れます。
  • get_beer_w 引数1 引数2という感じで引数を渡します。
  • 戻り値は、echo 戻り値で返せます。

各行の横幅は、

WS_MUG=()
for ((i = 0; i < H_MUG; i++)); do
  WS_MUG[i]=$(get_beer_w "$i")
done

こんな感じで配列にしておきます。

WS_MUGには20,22,22,24,26,・・・
という値が入っていくことになります。

描画の流れ

描画の流れは以下のとおりです。

  1. ビールジョッキが横から登場
  2. 注ぎ口の描画
  3. ビールが注がれる演出
  4. 溜まってくビールの描画

ビールジョッキが横から登場

ビールの外形は以下のコードで実現しています。

MUG_LINES=()
W_BASE=$((H_MUG * 60 / 100 + MIN_W_MUG)) # 1番広い幅
if ((W_BASE % 2 == 1)); then
  W_BASE=$((W_BASE + 1))
fi

for ((i = 0; i < H_MUG; i++)); do
  w_current=${WS_MUG[i]}
  indent=$(((W_BASE - w_current) / 2))
  line=$(printf "%${indent}s" "") #左インデント
  if ((i == 0)); then
    line+=$(printf "%${w_current}s" "" | tr ' ' '█')
  else
    inner_w=$((w_current - 2))
    line+="█"$(printf "%${inner_w}s" "")"█"
  fi
  MUG_LINES[$i]="$line"
done
  • indent=$(((W_BASE - w_current) / 2))で左側の空白(indent)の数を出し、カップが真ん中に来るようにしています。
  • printf "%${indent}s" "" は、指定した数値分の半角スペースを作るテクニックです。%10s で指定すると「10文字分の幅確保」という意味になります。そこに空文字 "" を流し込むことで、結果的に指定した数(indent)だけの空白が出力されます。
  • if ((i == 0))(最初の行)だけは、trで空白をすべてに置き換え、塗りつぶします。2行目以降は、左右の両端だけをにし、間を空白にすることで、中が空っぽの器を表現しています。

ビールが横から流れてくる演出は以下のようにして行います。

function draw_mug() {
  local x_left=$1 #左端の位置
  local y0=$2     #底の位置
  buffer="${BLUE}\033[H"
  for ((i = 0; i < H_MUG; i++)); do
    full_line="${MUG_LINES[i]}"
    y=$((y0 - i))
    if ((x_left < 1)); then
      offset=$((1 - x_left))
      draw_line="${full_line:$offset}"
      draw_x=1
    else
      draw_line="$full_line"
      draw_x=$x_left
    fi
buffer+="\033[${y};1H\033[K\033[${y};${draw_x}H${draw_line}"
  done
  echo -ne "$buffer${RESET}"
}
  • x_leftが左端、y0が下を表しており、引数で受け取ります。
  • buffer+="\033[${y};1H\033[K\033[${y};${draw_x}H${draw_line}"は一見複雑ですが、
    • \033[${y}y行、1列まで移動
    • \033[Kで行末までクリア
    • \033[${y};${draw_x}Hydraw_x列まで移動
    • draw_lineを表示
      という意味です。
  • すべて連結したbufferを最終的にecho -ne "$buffer${RESET}"で描画しています。

x_leftを徐々に動かすことで、
output2.gif
このようにジョッキを右に移動させることができます。

注ぎ口の描画

注ぎ口もいままで使った技を駆使していきます。

function draw_spout() {
  offset_x=$1
  offset_y=$2
  flg=$3
  tput cup $offset_y $offset_x

  if ((flg == 1)); then
    FIG_SPOUT="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="|   |"
    echo -ne "${FIG_SPOUT}"
  else
    #クリア
    FIG_SPOUT="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="Cheers!!!!"
    echo -ne "${FIG_SPOUT}"
  fi
}
  • offset_xoffset_yの引数を受け取り、tput cup $offset_y $offset_xでカーソル移動
  • flgは描画するかクリアするかのフラグ
FIG_SPOUT="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
FIG_SPOUT+="|   |"
echo -ne "${FIG_SPOUT}"

ここはカーソル移動を駆使して、注ぎ口を描画しています。Screenshot 2025-12-30 22.17.54.png
最終的にはこんな感じになります。

ビールが注がれる演出と溜まっていくビールの描画

コードは以下のとおりです。※長いので折りたたんでいます。

描画のコード
bottom_beer=$((offset_y)) #ビールの底
total_time=5 #全体の時間
bottom_frame=30 #ビールが底に達するまでのフレーム
num_frame=60 #1秒間のフレーム
draw_spout $offset_x $offset_y 1 #最後の引数は描画するか、クリアするかのフラグ
draw_mug $((MOVE_SPEED - 1)) $y0 # ビールの描画
for ((f = 0; f < $((total_time * num_frame)); f++)); do
  bottom_beer=$((offset_y - (H_MUG - 3) * f / bottom_frame)) #注がれるビールの液面
  if ((bottom_beer <= $((offset_y - H_MUG - 3)))); then
    # 液体の描画
    current_height=$((H_MUG * 150 * (f - bottom_frame) / num_frame / total_time / 100))
    if ((current_height > H_MUG)); then
      current_height=H_MUG
      # 注ぎ口を消す
      tput cup $((offset_y + 1)) $offset_mug_x
      line="${YELLOW}"
      for ((o = 0; o < MOVE_SPEED; o++)); do
        line+="${RIGHT}"
      done
      for ((h = 0; h < 4; h++)); do
        for ((w = 0; w < 4; w++)); do
          line+=" "
        done
        line+="${DOWN}"
        for ((w = 0; w < 4; w++)); do
          line+="${LEFT}"
        done
      done
      line+="${RESET}"
      echo -ne "$line"
    fi
    beer=""
    tput cup $((Y_BOTTOM - 2)) $((MOVE_SPEED - 1))
    for ((h = 1; h < current_height; h++)); do
      offset_beer_x=$(((WS_MUG[H_MUG - 1] - WS_MUG[h]) / 2))
      if ((h <= H_MUG - 5)); then
        #上から3つは泡にしたい
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${RIGHT}"
        done
        f_h=$((h * num_frame * total_time * 100 / 150 / H_MUG + bottom_frame)) #その高さがくる時間
        p_y=$((3000 * (f - f_h) / num_frame / total_time))                     # 確率小数扱えないので、100倍
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          if ((RANDOM % 1000 <= p_y)); then
            beer+="${YELLOW}${RESET}"
          else
            beer+="${WHITE}${RESET}"
          fi
        done
        beer+="${UP}"
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          beer+="${LEFT}"
        done
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${LEFT}"
        done
      else
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${RIGHT}"
        done
        f_h=$((h * num_frame * total_time * 100 / 150 / H_MUG + bottom_frame)) #その高さがくる時間
        p_y=$((10 * (f - f_h) / num_frame / total_time))                       # 確率小数扱えないので、100倍
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          if ((RANDOM % 1000 <= p_y)); then
            beer+="${YELLOW}${RESET}"
          else
            beer+="${WHITE}${RESET}"
          fi
        done
        beer+="${UP}"
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          beer+="${LEFT}"
        done
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${LEFT}"
        done
      fi
    done
    echo -ne "$beer"
  else
    # 注ぎ口の描画
    tput cup $((offset_y + 1)) $offset_mug_x
    line="${YELLOW}"
    for ((o = 0; o < MOVE_SPEED; o++)); do
      line+="${RIGHT}"
    done
    for ((h = offset_y; h >= bottom_beer; h--)); do
      for ((w = 0; w < 4; w++)); do
        line+="█"
      done
      line+="${DOWN}"
      for ((w = 0; w < 4; w++)); do
        line+="${LEFT}"
      done
    done
    line+="${RESET}"
    echo -ne "$line"
  fi

  sleep 0.008
done
draw_spout $((offset_x)) $offset_y 0

ビールが注がれる演出

    # 注ぎ口の描画
    tput cup $((offset_y + 1)) $offset_mug_x
    line="${YELLOW}"
    for ((o = 0; o < MOVE_SPEED; o++)); do
      line+="${RIGHT}"
    done
    for ((h = offset_y; h >= bottom_beer; h--)); do
      for ((w = 0; w < 4; w++)); do
        line+="█"
      done
      line+="${DOWN}"
      for ((w = 0; w < 4; w++)); do
        line+="${LEFT}"
      done
    done
    line+="${RESET}"
    echo -ne "$line"
  • offset_yがビール液面の高さで、ビール底より高かったら注ぎ続けます。
  • 注ぐビールはで、カーソル移動を駆使して全体を表現しています。

溜まってくビールの描画

溜まっていくビールは、基本的にジョッキの横幅に合わせて増やしていくのですが、ビールの泡の多さにこだわりがあります。

        f_h=$((h * num_frame * total_time * 100 / 150 / H_MUG + bottom_frame)) #その高さがくる時間
        p_y=$((3000 * (f - f_h) / num_frame / total_time))                     # 確率小数扱えないので、100倍
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          if ((RANDOM % 1000 <= p_y)); then
            beer+="${YELLOW}${RESET}"
          else
            beer+="${WHITE}${RESET}"
          fi
        done
  • この部分で、ビール液面高さhの時間f_hを計算し、現在時刻ff_hより十分大きかったらp_yを大きくします。
  • p_yはYELLOWすなわち黄色の確率を示しており、この値が大きいほど、高さhでの黄色の数が多いということになります。
  • RANDOM % 1000 <= p_yこちらもShell Scriptでの確率の表現のコツです。
    • RANDOMは0から32767までの範囲の乱数を生成します。
    • RANDOM % 1000は0から999までの範囲を乱数を生成します。
    • それがp_yより小さければ黄色、大きければ白色とすることで、確率p_yに依存して色を増やすことができるようになります。
      これにより、
      output3.gif
      高さに応じて泡の数が変動する演出が可能となります。

全体のコード

全体のコードは

全体のコード
# !/bin/bash
readonly H_TER=$(tput lines)                      #ターミナルの縦幅
readonly W_TER=$(tput cols)                       #ターミナルの横幅
w1=$((W_TER * 30 / 100))                          #計算用w
readonly MIN_W_MUG=$((w1 % 2 == 0 ? w1 : w1 + 1)) #ビールジョッキの最大の横幅(形は後で変える)必ず偶数
readonly H_MUG=$((H_TER * 40 / 100))              #ビールジョッキの縦幅
readonly Y_BOTTOM=$((H_TER - 5))                  #ジョッキの底の位置
readonly POUR_SPEED=500                           #注ぐ速度
readonly MOVE_SPEED=$((MIN_W_MUG / 3 * 2))        #ビールジョッキ登場スピード

readonly YELLOW="\033[33m" #黄色
readonly BLUE="\033[34m"   #黄色
readonly WHITE="\033[37m"  #白色
readonly RESET="\033[0m"   #リセット
readonly UP="\033[1A"      #カーソル上
readonly DOWN="\033[1B"    #カーソル下
readonly RIGHT="\033[1C"   #カーソル右
readonly LEFT="\033[1D"    #カーソル左

#ビールジョッキの横幅
function get_beer_w() {
  local h=$1                            #ある高さ
  local w=$((h * 60 / 100 + MIN_W_MUG)) #その時の横幅
  if ((w % 2 == 1)); then
    #奇数だとズレちゃう
    w=$((w + 1))
  fi
  echo "$w"
}

WS_MUG=()
for ((i = 0; i < H_MUG; i++)); do
  WS_MUG[i]=$(get_beer_w "$i")
done

MUG_LINES=()
W_BASE=$((H_MUG * 60 / 100 + MIN_W_MUG)) # 1番広い幅
if ((W_BASE % 2 == 1)); then
  W_BASE=$((W_BASE + 1))
fi

for ((i = 0; i < H_MUG; i++)); do
  w_current=${WS_MUG[i]}
  indent=$(((W_BASE - w_current) / 2))
  line=$(printf "%${indent}s" "") #左インデント
  if ((i == 0)); then
    line+=$(printf "%${w_current}s" "" | tr ' ' '█')
  else
    inner_w=$((w_current - 2))
    line+="█"$(printf "%${inner_w}s" "")"█"
  fi
  MUG_LINES[$i]="$line"
done

#ジョッキの描画
function draw_mug() {
  local x_left=$1 #左端の位置
  local y0=$2     #底の位置
  buffer="${BLUE}\033[H"
  for ((i = 0; i < H_MUG; i++)); do
    full_line="${MUG_LINES[i]}"
    y=$((y0 - i))
    if ((x_left < 1)); then
      offset=$((1 - x_left))
      draw_line="${full_line:$offset}"
      draw_x=1
    else
      draw_line="$full_line"
      draw_x=$x_left
    fi
    # \033[Kは行末までクリア、\033[y;xHは座標指定
    buffer+="\033[${y};1H\033[K\033[${y};${draw_x}H${draw_line}"
  done
  echo -ne "$buffer${RESET}"
}

#注ぎ口の描画
function draw_spout() {
  offset_x=$1
  offset_y=$2
  flg=$3
  tput cup $offset_y $offset_x

  if ((flg == 1)); then
    FIG_SPOUT="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="/   /${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="|   |"
    echo -ne "${FIG_SPOUT}"
  else
    #クリア
    FIG_SPOUT="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="     ${UP}${LEFT}${LEFT}${LEFT}${LEFT}"
    FIG_SPOUT+="Cheers!!!!"
    echo -ne "${FIG_SPOUT}"
  fi
}

##########################################
# 初期
##########################################
tput clear #画面をクリア
tput civis #カーソル消す

##########################################
# ビールジョッキを左から登場させる  
##########################################
x0=0                                                                  # 基準
y0=$Y_BOTTOM                                                          # 基準
offset_mug_x=$(((WS_MUG[H_MUG - 1] - WS_MUG[0]) / 2 + WS_MUG[0] / 2)) #ジョッキの左端
offset_y=$((Y_BOTTOM - H_MUG - 5))                                    #注ぎ口の下端
offset_x=$((offset_mug_x + MOVE_SPEED))                               #注ぎ口の左端
for ((x = $((-W_BASE - 10)); x < MOVE_SPEED; x++)); do
  draw_mug $x $y0 # ビールの描画
  draw_spout $((offset_x)) $offset_y 1
  # 注ぎ口の描画
  sleep 0.008
done

# 注ぎ描画 30フレームで5秒間
bottom_beer=$((offset_y))
total_time=5
bottom_frame=30
num_frame=60
draw_spout $offset_x $offset_y 1 #最後の引数は描画するか、クリアするかのフラグ
draw_mug $((MOVE_SPEED - 1)) $y0 # ビールの描画
for ((f = 0; f < $((total_time * num_frame)); f++)); do
  bottom_beer=$((offset_y - (H_MUG - 3) * f / bottom_frame)) #注がれるビールの液面
  if ((bottom_beer <= $((offset_y - H_MUG - 3)))); then
    # 液体の描画
    current_height=$((H_MUG * 150 * (f - bottom_frame) / num_frame / total_time / 100))
    if ((current_height > H_MUG)); then
      current_height=H_MUG
      #注ぎを消す
      tput cup $((offset_y + 1)) $offset_mug_x
      line="${YELLOW}"
      for ((o = 0; o < MOVE_SPEED; o++)); do
        line+="${RIGHT}"
      done
      for ((h = 0; h < 4; h++)); do
        for ((w = 0; w < 4; w++)); do
          line+=" "
        done
        line+="${DOWN}"
        for ((w = 0; w < 4; w++)); do
          line+="${LEFT}"
        done
      done
      line+="${RESET}"
      echo -ne "$line"
    fi
    beer=""
    tput cup $((Y_BOTTOM - 2)) $((MOVE_SPEED - 1))
    for ((h = 1; h < current_height; h++)); do
      offset_beer_x=$(((WS_MUG[H_MUG - 1] - WS_MUG[h]) / 2))
      if ((h <= H_MUG - 5)); then
        #上から3つは泡にしたい
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${RIGHT}"
        done
        f_h=$((h * num_frame * total_time * 100 / 150 / H_MUG + bottom_frame)) #その高さがくる時間
        p_y=$((3000 * (f - f_h) / num_frame / total_time))                     # 確率小数扱えないので、100倍
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          if ((RANDOM % 1000 <= p_y)); then
            beer+="${YELLOW}${RESET}"
          else
            beer+="${WHITE}${RESET}"
          fi
        done
        beer+="${UP}"
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          beer+="${LEFT}"
        done
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${LEFT}"
        done
      else
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${RIGHT}"
        done
        f_h=$((h * num_frame * total_time * 100 / 150 / H_MUG + bottom_frame)) #その高さがくる時間
        p_y=$((10 * (f - f_h) / num_frame / total_time))                       # 確率小数扱えないので、100倍
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          if ((RANDOM % 1000 <= p_y)); then
            beer+="${YELLOW}${RESET}"
          else
            beer+="${WHITE}${RESET}"
          fi
        done
        beer+="${UP}"
        for ((w = 0; w < WS_MUG[h] - 2; w++)); do
          beer+="${LEFT}"
        done
        for ((w = 0; w < offset_beer_x; w++)); do
          beer+="${LEFT}"
        done
      fi
    done
    echo -ne "$beer"
  else
    # 注ぎ口の描画
    tput cup $((offset_y + 1)) $offset_mug_x
    line="${YELLOW}"
    for ((o = 0; o < MOVE_SPEED; o++)); do
      line+="${RIGHT}"
    done
    for ((h = offset_y; h >= bottom_beer; h--)); do
      for ((w = 0; w < 4; w++)); do
        line+="█"
      done
      line+="${DOWN}"
      for ((w = 0; w < 4; w++)); do
        line+="${LEFT}"
      done
    done
    line+="${RESET}"
    echo -ne "$line"
  fi

  sleep 0.008
done
draw_spout $((offset_x)) $offset_y 0

tput cup $H_TER 0
tput cnorm #カーソル表示
です。

これを.sh形式で保存して、
bash 〇〇.shで実行すればビールが登場します。

おわりに

最後までお付き合いいただきありがとうございました!
まだまだ完成度が低いですので、もっとこうすればいいでは?こういうものを作ってほしいというアイデアや実装例、募集中です!

  • ランダムで泡の量を変える
  • ジョッキを好きな形にする
  • 黒ビールに対応する
    等々やりたいことはまだまだ盛り沢山です。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?