Siv3Dでショートコーディング

　[Siv3D Advent Calendar 2023](https://qiita.com/advent-calendar/2023/siv3d) の20日目の記事です。

# はじめに
　[#つぶやきProcessing](https://twitter.com/hashtag/%E3%81%A4%E3%81%B6%E3%82%84%E3%81%8DProcessing?src=hashtag_click) をご存じでしょうか？

　[#つぶやきProcessing](https://twitter.com/hashtag/%E3%81%A4%E3%81%B6%E3%82%84%E3%81%8DProcessing?src=hashtag_click) とはProcessingやp5.jsを使い、1ポスト(280字)に収まるプログラムで、どこまでアートを表現できるかに挑戦するものです。とにかく短いコードで様々なものを作ります。

https://twitter.com/Hau_kun/status/1136299935618351105

これに倣い、今回はSiv3Dでショートコーディングを行います。

# ルール
- `Main.cpp` のみで動作する。
- 執筆時点での最新バージョン(v0.6.13)で動作する。
- [#つぶやきSiv3D](https://twitter.com/hashtag/%E3%81%A4%E3%81%B6%E3%82%84%E3%81%8DSiv3D?src=hashtag_click) のハッシュタグも含めて280字以内に収まっている。

# 1. pong
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/46b16e71-fefc-dd40-5c7c-d4d0b878ab66.gif" width="400px">

## 短縮前
```cpp
#include <Siv3D.hpp>

void Main() {
	const int32 radius = 4, barWidth = 80;
	Point pos{ Scene::Center() }, velocity{ 6, 6 };

	while (System::Update()) {
		const int32 mouseX = Cursor::Pos().x;

		if (not InRange(pos.x, 2 * radius, Scene::Width() - 2 * radius)) {
			velocity.x *= -1;
		}
		if (InRange(pos.x, mouseX, mouseX + barWidth) && not InRange(pos.y, 2 * radius, Scene::Height() - 2 * radius)) {
			velocity.y *= -1;
		}
		if (not InRange(pos.y, -radius, Scene::Height() + radius)) {
			pos = std::move(Scene::Center());
		}

		pos += velocity;
		Rect{ mouseX, 0, barWidth, radius }.draw();
		Rect{ mouseX, Scene::Height() - radius, barWidth, radius }.draw();
		Circle{ pos, 2 * radius }.draw();
	}
}

```

## 短縮後
```cpp
#include<Siv3D.hpp>
void Main(){
int x=400,y=300,u=6,v=6,z;
while(System::Update()){
z=Cursor::Pos().x;
v=(x<8||x>792)?-v:v;
u=(x>z&&x<z+80&&(y<8||y>592))?-u:u;
if(y<-4||y>604)x=400,y=300;
Rect{z,0,80,4}.draw();
Rect{z,596,80,4}.draw();
Circle{x+=v,y+=u,8}.draw();
}
}
```

## 短縮のポイント
- 空白を最低限にする
    - 省略できる部分は省略します。
- 変数を1文字にする
    - `x` や `y` にすると文字数が大幅に減ります。
- `int32` や `double` を `int` や `auto` にする
    - 文字数は減ります。万能ではないです。
- 三項演算子に置き換える
    - 無理やりぶち込みます。

### 参考
https://twitter.com/SnowEsamosc/status/1429813701276692480

# 2. 花畑
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/5af9a569-c8d8-e462-a1de-f50954130b93.gif" width="400px">

## 短縮前
```cpp
#include <Siv3D.hpp>

void Main() {
	const int32 radius = 96, area = 40;
	const Texture t{ U"🥀"_emoji }, u{ U"🌷"_emoji };

	while(System::Update()) {
		const auto& mouse{ Cursor::Pos() };
		Circle{ mouse, radius }.drawFrame(3.0, Palette::Yellow);

		for (const auto& p : step({ 21, 16 })) {
			const auto& v{ mouse.distanceFrom(area * p) < radius ? t : u };

			v.scaled(0.3).drawAt(area * p);
		}
	}
}
```

## 短縮後
```cpp
#include<Siv3D.hpp>
void Main(){
for(Texture t{U"🥀"_emoji},u{U"🌷"_emoji};System::Update();){
Vec2 m=Cursor::Pos();
Circle{m,96}.drawFrame(3,{255,255,0});
for(auto p:step({21,16})){
(m.distanceFrom(40*p)<96?t:u).scaled(.3).drawAt(40*p);
}
}
}
```

## 短縮のポイント
- whileループをforループに置き換える
    - 変数の宣言と `System::Update()` を1行にまとめます。
- 小数の表記を省略する
    - `0.1` は `.1`、`1.0` は `1.` にします。
- `Color` 型を工夫する
    - `Palette` は便利ですが、今回はRGB値を使用して文字数を節約しています。


# 3. 模様
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/f252dc95-f961-22f4-ef1e-9c1c5370b58d.gif" width="400px">

## 短縮前
```cpp
#include <Siv3D.hpp>
void Main() {
	while (System::Update()) {
		const double t = Scene::Time();

		for (const auto& [x, y] : step({ 74, 56 })) {
			const Vec2 v{ x, y }, u{ Math::Acos(Math::Sin(y / 5.0 + t)), Math::Acos(Math::Cos(x / 5.0 + t)) };
			Circle{ 11.0 * v, 3.0 * (Math::Sin((40 * u).length() / 10.0 + t) + 2.0) }.draw(HSV{ 64, 0.5 });
		}
	}
}
```

## 短縮後
```cpp
#include<Siv3D.hpp>
void Main(){
for(double t=0;System::Update();t+=0.01){
for(const auto& [x,y]:step({74,56})){
const Vec2 v{x,y},u{Acos(Sin(y/5.0+t)),Acos(Cos(x/5.0+t))};
Circle{11.0*v,3.0*(Sin((40*u).length()/10.0+t)+2.0)}.draw(HSV{64,0.5});
}
}
}
```

## 短縮のポイント
もともとのコードが短かったのであまり変えていません。`const` や小数点も省略せず書いています。
- 名前空間を省略する
    - 今回は `Math::` を省略しています。あまり省略したくはありませんが、これだけで随分変わるので今回はできる限り省略します。
- `Scene::Time()` を自分で定義する
    - 少し短くなります。

# 4. 弾幕
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/3772730f-70ce-9580-c0ef-9a4443a95f34.gif" width="400px">

マウスカーソルが被弾すると背景がフラッシュします。

## 短縮前
```cpp
#include <Siv3D.hpp>

void Main() {
	const double radius = 5.0;
	double backgroundColor = 0.0;

	while (System::Update()) {
		Scene::SetBackground(ColorF{ backgroundColor });

		backgroundColor = Max(backgroundColor - 0.1, 0.0);
		const double t = Scene::Time() / 2.0;

		for (const auto i : step(360)) {
			const double d = static_cast<double>(i);
			const double r = Scene::Size().length() / 2.0 * Math::Sin(d + t);
			const Vec2 v{ Math::Cos(d / 4.0), Math::Sin(d / 4.0) };
			const Vec2 pos{ (r * v).rotated(d + t / 9.0) + Scene::Center() };

			Circle{ pos, radius }.draw();
			if (pos.distanceFrom(Cursor::Pos()) < radius) {
				backgroundColor = 1;
			}
		}
	}
}
```

## 短縮後
```cpp
#include<Siv3D.hpp>
void Main(){
for(float t=0,b=0,d;System::Update();t+=.01)
for(Rect{0,0,800}.draw(ColorF{b-=.1}),d=360;d--;){
Vec2 c{400,300},v{Cos(d/4),Sin(d/4)},p=(500*Sin(d+t)*v).rotated(d+t/9)+c;
Circle{p,5}.draw();
b=p.distanceFrom(Cursor::Pos())<5?1:b;
}
}
```

ハッシュタグ含めてギリギリでした...。

## 短縮のポイント
- `Scene::SetBackground()` を `Rect` で代用する
    - 少し減ります。
- `double` を `float` にする
    - 1文字しか減りません。
- 代入しながら値を使用する
    - `ColorF{b-=.1}` のように、代入と引数の指定を同時に行います。
- あらゆるものを無理やりfor文に詰め込む
    - パワー

# 5. ゴースト
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/1cce0dc8-dd05-c1ff-3c60-8ca821cdf1bc.gif" width="400px">

## 短縮前
※今回ははじめから短縮プログラムを書きました。

## 短縮後
```cpp
#include<Siv3D.hpp>
void Main(){
Texture t{U"👻"_emoji};
for(float v=0,a,u;System::Update();v+=.5)
for(auto[i,j]:step({8,9})){
Vec2 c{400,300},p{Cos(u=(a=70*i+v)/90+.7*j),Sin(u)*(i%2*2-1)},q=int(a)%560*p;
t.resized(q.length()/5).scaled(Cos(5*u),1).drawAt(q+c);
}
}
```

## 短縮のポイント
- 二重ループをまとめる
    - Siv3Dの `step()` で二重ループをまとめます。
- `Vec2` などのSiv3D特有のクラスを有効活用する
    - メソッドがかなり便利なので有効活用します。

# 6. 機構
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/e0b89366-7499-a449-ae00-13f3f9a9623e.gif" width="400px">

## 短縮前
※今回もはじめから短縮プログラムを書きました。

## 短縮後
```cpp
#include<Siv3D.hpp>
#define C(x,y,r) Circle{x,y,r}.draw({0,0,0}).drawFrame(9)
void Main(){
for(float t=0,p=400,q,x,y;System::Update();t+=.03){C(p,125,70);Line{x=70*Cos(t)+p,y=125-70*Sin(t),p,q=y+300}.draw(15);C(x,y,30);RectF{p/2,q,p,p/4}.draw();C(p,q,30);}
}
```

## 短縮のポイント
- マクロを使用する
    - マクロを使用すると予期しない挙動が起こりやすくなるので注意してください。

# 終わりに
　Siv3Dでショートコーディングを試してみました。名前空間を省略したり型変換を明示しなかったりと、本来のSiv3Dのコーディングスタイルと異なってしまいますが、ほかにも工夫できそうです。

# おまけ
```cpp
#include <Siv3D.hpp>

void Main() {
	for (Font f{ 36 }; System::Update();) {
		for (auto [i, c] : Indexed(U"Siv3D Advent Calendar 2023")) {
			f(c).draw(
				i * 30 + 18,
				Math::Tan(Scene::Time() - 29_deg * i) * 6 + 300,
				HSV{ 14 * i }
			);
		}
	}
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1889756/0bf4f61c-7519-e998-12d1-417af194c196.gif" width="400px">

今年も1年間Siv3Dには大変お世話になりました！ありがとうございました！