JasmineTea の手続き (`PROCEDURE`) を使ってみる

これは [Jasmine Tea アドベントカレンダー 2023](https://qiita.com/advent-calendar/2023/jasminetea) の  10 日目の記事です。

こんにちは、[Jasmine Tea](https://jasminetea.app/) の開発に参加している石川です。この記事では Jasmine Tea の「手続き (`PROCEDURE`)」について説明します。

手続き(`PROCEDURE`) は複数の命令をひとまとまりにするための仕組みです。

プログラミングをしていると、プログラムが複雑になったり、似たプログラムが繰り返されてしまうことがあります。もし、複雑なプログラムをひとまとまりの命令のように書ければ、より分かりやすくする事ができます。同様に、繰り返されているプログラムもひとまとまりの命令のように書ければ、簡潔にすることができます。手続きをうまく使うことで、それらを実現できます。

## `PROCEDURE` 命令と `CALL` 命令

次のプログラムは、クリックされたときや十字キーが押されたときにキャラクターを動かすというものです。

- クリック時: その場所から右にキャラクターを動かす
- 十字キー押下時: 中心からキーの方向にキャラクターを動かす

(動作確認は[こちら](https://jasminetea.app/studio/projects/50YWWagnDf6cPZlkk40F))

```
sprite_number = 0

do
  tap x, y
  if x <> -1 and y <> -1 then
    sprite sprite_number, 0, sprite_number * 4 + 3
    show sprite_number, (x, y)
    direction sprite_number, 3
    distance sprite_number, 600
    speed sprite_number, 20
    move sprite_number
    sprite_number = (sprite_number + 1) % 13
  end if
  
  key$ = inkey$()
  if key$ = "ArrowLeft" then
    sprite sprite_number, 0, sprite_number * 4 + 2
    show sprite_number, (320, 200)
    direction sprite_number, 7
    distance sprite_number, 600
    speed sprite_number, 20
    move sprite_number
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowUp" then
    sprite sprite_number, 0, sprite_number * 4 + 1
    show sprite_number, (320, 200)
    direction sprite_number, 1
    distance sprite_number, 600
    speed sprite_number, 20
    move sprite_number
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowRight" then
    sprite sprite_number, 0, sprite_number * 4 + 3
    show sprite_number, (320, 200)
    direction sprite_number, 3
    distance sprite_number, 600
    speed sprite_number, 20
    move sprite_number
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowDown" then
    sprite sprite_number, 0, sprite_number * 4 + 0
    show sprite_number, (320, 200)
    direction sprite_number, 5
    distance sprite_number, 600
    speed sprite_number, 20
    move sprite_number
    sprite_number = (sprite_number + 1) % 13
  end if
loop
```

この `sprite` から `move` までの命令は「キャラクターを動かす」ことを意味しています。これを `procedure` を使ってひとまとまりにしてみましょう。まずは、`tap` でキャラクタを動かす部分を `procedure` と `end procedure` でまとめ、それを `call` 命令で使うようにしてみましょう。(プログラムの全体は[こちら](https://jasminetea.app/studio/projects/oy2K2rW0ZxW84ZBeAiTa))

```
procedure move_sprite
  sprite sprite_number, 0, sprite_number * 4 + 3
  show sprite_number, (x, y)
  direction sprite_number, 3
  distance sprite_number, 600
  speed sprite_number, 20
  move sprite_number
end procedure

sprite_number = 0

do
  tap x, y
  if x <> -1 and y <> -1 then
    call move_sprite
    sprite_number = (sprite_number + 1) % 13
  end if
  ...
```

実は、このプログラムはうまく動きません。`procedure` の中では、`sprite_number`, `x`, `y` という変数に値が代入されていないため、それらの変数を使うことができないからです。

![screenshot procedure failed](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635491/9bf088cb-5384-0c47-c056-5058fbb02a56.png)

`procedure` の外の値を使うためには、「引数」として手続きに渡してあげればよいです。引数は `procedure move_sprite number, x, y` のように定義します。(プログラムの全体は[こちら](https://jasminetea.app/studio/projects/eVhE0mFBbkLNUsICK4s0))

```
procedure move_sprite number, x, y
  sprite number, 0, number * 4 + 3
  show number, (x, y)
  direction number, 3
  distance number, 600
  speed number, 20
  move number
end procedure

sprite_number = 0

do
  tap x, y
  if x <> -1 and y <> -1 then
    call move_sprite sprite_number, x, y
    sprite_number = (sprite_number + 1) % 13
  end if
```

このようにすることで、`call` 時点での `sprite_number`、`x`、`y` の値を、`move_sprite` の中で `number`、`x`、`y` として使うことができます。

## 手続きを他の場所でも使う

画面をクリックしたときのキャラクターの動かし方と、十字キーを押したときの画面の動かし方は少し違いますが、ほとんど同じです。違う部分は以下の 2 点です。

- `sprite sprite_number, 0, sprite_number * 4 + sd` の `sd` の部分
- `direction sprite_number, d` の `d` の部分

これら違う部分を引数として与えてあげれば、以下のプログラムのように、クリック用の手続きを十字キーに対しても流用できます。(動作確認は[こちら](https://jasminetea.app/studio/projects/M1fTUvPB84EJRSI9nyJU))

```
procedure move_sprite number, x, y, sd, d
  sprite number, 0, number * 4 + sd
  show number, (x, y)
  direction number, d
  distance number, 600
  speed number, 20
  move number
end procedure

sprite_number = 0

do
  tap x, y
  if x <> -1 and y <> -1 then
    call move_sprite sprite_number, x, y, 3, 3
    sprite_number = (sprite_number + 1) % 13
  end if
  
  key$ = inkey$()
  if key$ = "ArrowLeft" then
    call move_sprite sprite_number, 320, 200, 2, 7
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowUp" then
    call move_sprite sprite_number, 320, 200, 1, 1
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowRight" then
    call move_sprite sprite_number, 320, 200, 3, 3
    sprite_number = (sprite_number + 1) % 13
  else if key$ = "ArrowDown" then
    call move_sprite sprite_number, 320, 200, 0, 5
    sprite_number = (sprite_number + 1) % 13
  end if
loop
```

手続き(`PROCEDURE`)を使うことで、プログラムを簡潔にすることができました。

## 次回は

Jasmine Tea には「手続き」と似た仕組みとして、「関数(`FUNCTION`)」があります。次回はそれを使ってみます。