Siv3Dでジャンケンマン

100行以内目指して書いてたら
いつの間にか300行を超えていた

![ジャンケンマン.png](https://qiita-image-store.s3.amazonaws.com/0/34295/a3ebecb9-b7cc-8ed0-1fc0-5ad9a9805e14.png)


```cpp:Main.cpp

# include <Siv3D.hpp>

Image CreateGradImage()
{
	Image image(640, 480);

	for (int y = 0; y < 480; ++y)
		for (int x = 0; x < 640; ++x)
			image[y][x] = HSV(0, 0, (1.0 - (Vec2(x, y) - Window::Center()).length() / (Vec2(640, 480) / 2.0).length()) * 0.3);

	return image;		 
}

void DrawButton(const RectF& rect)
{
	RoundRect(rect, 10.0).drawFrame(5.0, Palette::Gray);
	RoundRect(rect, 10.0).draw(rect.mouseOver ? rect.leftClicked ? Palette::White :  Palette::Darkgray : Palette::Dimgray);
}

void DrawButton(const RectF& rect, const LineString& line)
{
	DrawButton(rect);
	line.drawCatmullRomSpline(2.0);
}

void DrawScore(int score)
{
	const Vec2 startPos = Window::Size() + Vec2(-50.0, -30.0);

	for (int i = 0; i < score; ++i)
	{
		Ellipse(startPos + Vec2(0.0, -i * 5.0), 30.0, 15.0).draw(Palette::Lightgoldenrodyellow);
		Ellipse(startPos + Vec2(0.0, -i * 5.0), 30.0, 15.0).drawFrame(1.0, 1.0, Palette::Darkgoldenrod);
	}
}

void Main()
{
	using std::vector;
	using std::array;
	using std::function;

	enum class JyankenState
	{
		Guu, Choki, Paa
	};

	enum class GameState
	{
		Undefined, Jyanken, Pon, Aikode, Syo, Win, Lose, Praise, GameOver
	};

	const Texture texture(CreateGradImage());
	const Font font(30);
	const Font jyankenFont(10);
	const Vec2 fontPosCenter = Window::Center() - Vec2(0.0, 200.0);
	const Vec2 enemyCenter = Window::Center() + Vec2(0.0, -50.0);
	const double rouletteRadius = Window::Height() * 0.3;
	const Vec2 jyankenFontPosCenter = enemyCenter + Vec2(0.0, 95.0);

	JyankenState m_enemyJyankenState;
	JyankenState m_playerJyankenState;
	GameState gameState = GameState::Undefined;
	GameState tGameState = gameState;
	TimerMillisec m_timer;
	m_timer.start();

	const Vec2 size{90, 90};

	RectF guuButton = RectF(size).setCenter(Window::Center() + Vec2(-Window::Width() * 0.2, Window::Height() * 0.35));
	RectF chokiButton = RectF(size).setCenter(Window::Center() + Vec2( 0.0, Window::Height() * 0.35));
	RectF paaButton = RectF(size).setCenter(Window::Center() + Vec2(Window::Width() * 0.2, Window::Height() * 0.35));

	int coinNum = 10;
	double rouletteRad = 0.0;
	int roulettePos = 0;

	struct JyankenButton
	{
		RectF rect;
		JyankenState state;
		LineString line;
	};

	array<JyankenButton, 3> jyankenButtons = 
	{
		{ { guuButton, JyankenState::Guu, LineString() },
		{ chokiButton, JyankenState::Choki, LineString() },
		{ paaButton, JyankenState::Paa, LineString() } }
	};

	vector<Vec2> guu =
	{
		{ -0.361, 0.843 }, { -0.602, -0.259 }, { -0.292, -0.273 }, { -0.139, -0.407 },
		{ 0.102, -0.222 }, { 0.431, -0.171 }, { 0.542, -0.019 }, { 0.398, 0.838 }
	};

	vector<Vec2> choki =
	{
		{ -0.361, 0.843 }, { -0.681, -0.671 }, { -0.477, -0.671 }, { -0.301, -0.204 },
		{ -0.199, -0.898 }, { -0.019, -0.968 }, { 0.023, -0.241 }, { 0.236, -0.301 },
		{ 0.542, -0.093 }, { 0.324, 0.81 }
	};

	vector<Vec2> paa =
	{
		{ -0.361, 0.843 }, { -0.917, 0.134 }, { -0.824, 0 }, { -0.449, 0.343 },
		{ -0.56, -0.75 }, { -0.398, -0.829 }, { -0.273, -0.106 }, { -0.208, -0.963 },
		{ -0.037, -0.935 }, { -0.032, -0.148 }, { 0.097, -0.894 }, { 0.231, -0.866 },
		{ 0.236, -0.106 }, { 0.384, -0.713 }, { 0.505, -0.648 }, { 0.407, -0.014 },
		{ 0.301, 0.856 }
	};

	array<vector<Vec2>, 3> jyankenFigures = { guu, choki, paa };

	array<LineString, 3> jyankenEnemyLines;

	for (int i = 0; i < 3; ++i)
	{
		vector<Vec2> lineEnemy;
		vector<Vec2> lineButton;

		for (const auto& pos : jyankenFigures[i])
		{
			lineEnemy.push_back(pos * 100.0 + enemyCenter);
			lineButton.push_back(pos * 40.0 + jyankenButtons[i].rect.center);
		}

		jyankenEnemyLines[i] = LineString(lineEnemy);
		jyankenButtons[i].line = LineString(lineButton);
	}

	array<GameState, 3> resultArray =
	{
		GameState::Aikode, GameState::Win, GameState::Lose
	};	

	array < JyankenState, 3 > jyankenStrongerArray =
	{
		JyankenState::Paa, JyankenState::Guu, JyankenState::Choki
	};

	array<int, 12> rouletteArray = 
	{
		20, 1, 2, 5, 2, 7, 3, 4, 3, 2, 2, 1
	};

	function<void(GameState nextState)> updateJyankenState = [&](GameState nextState)
	{
		if (AnyOf(jyankenButtons, [&](const JyankenButton& button){return button.rect.leftClicked; }))
		{
			for (const auto& button : jyankenButtons)
				if (button.rect.leftClicked)
					m_playerJyankenState = button.state;

			const double randomValue = Random(1.0);

			const int playerState = static_cast<int>(m_playerJyankenState);

			if (randomValue < 0.6)
				m_enemyJyankenState = jyankenStrongerArray[playerState];
			else if (randomValue < 0.8)
				m_enemyJyankenState = jyankenStrongerArray[(playerState + 1) % 3];
			else
				m_enemyJyankenState = jyankenStrongerArray[(playerState + 2) % 3];

			gameState = nextState;
		}
	};	

	const double difRad = TwoPi / rouletteArray.size();

	const double margin = difRad * 0.05;

	function<void()> drawRoulette = [&]()
	{
		for (int i = 0; i < rouletteArray.size(); ++i)
		{
			const double startRad = difRad * i;

			Circle(enemyCenter, rouletteRadius).drawArc(startRad + margin, difRad - 2.0 * margin, 30.0, 30.0, Palette::Gray);

			font(rouletteArray[i]).drawCenter(enemyCenter + Circular(rouletteRadius, startRad + difRad * 0.5));
		}
	};

	function<int(double)> drawRouletteWithLight = [&](double locateRad)
	{
		const int locatePos = locateRad / difRad;

		const double startRad = difRad * locatePos;

		Circle(enemyCenter, rouletteRadius).drawArc(startRad + margin, difRad - 2.0 * margin, 30.0, 30.0, Palette::White);

		font(rouletteArray[locatePos]).drawCenter(enemyCenter + Circular(rouletteRadius, startRad + difRad * 0.5), Palette::Gray);

		return locatePos;
	};

	array<function<void()>, 9> stageFunctions = 
	{[&]()
		{
			jyankenFont(L"クリックでじゃんけん").drawCenter(jyankenFontPosCenter);

			if (Input::MouseL.clicked && coinNum >= 1)
			{
				--coinNum;

				gameState = GameState::Jyanken;
			}
		},[&]()
		{
			jyankenFont(L"じゃんけん…").drawCenter(jyankenFontPosCenter);

			updateJyankenState(GameState::Pon);

			jyankenEnemyLines[Random(2)].drawCatmullRomSpline(5.0);
		},[&]()
		{
			jyankenFont(L"ぽん！").drawCenter(jyankenFontPosCenter);
			
			jyankenEnemyLines[static_cast<int>(m_enemyJyankenState)].drawCatmullRomSpline(5.0);

			if (m_timer.elapsed() > 500.0)
			{
				const int result = (3 + static_cast<int>(m_playerJyankenState) - static_cast<int>(m_enemyJyankenState)) % 3;

				gameState = resultArray[result];
			}
		},[&]()
		{
			jyankenFont(L"あいこで…").drawCenter(jyankenFontPosCenter);

			updateJyankenState(GameState::Syo);

			jyankenEnemyLines[Random(2)].drawCatmullRomSpline(5.0);
		},[&]()
		{
			jyankenFont(L"しょ！").drawCenter(jyankenFontPosCenter);

			jyankenEnemyLines[static_cast<int>(m_enemyJyankenState)].drawCatmullRomSpline(5.0);

			if (m_timer.elapsed() > 500.0)
			{
				const int result = (3 + static_cast<int>(m_playerJyankenState)-static_cast<int>(m_enemyJyankenState)) % 3;

				gameState = resultArray[result];
			}
		},[&]()
		{
			jyankenFont(L"ズコー").drawCenter(jyankenFontPosCenter);

			if (Input::MouseL.clicked)
			{
				gameState = coinNum == 0 ? GameState::GameOver : GameState::Undefined;
			}
		},[&]()
		{
			jyankenFont(L"フィーバー！").drawCenter(jyankenFontPosCenter);

			rouletteRad = Fmod(rouletteRad + Pi * 0.06, TwoPi);

			roulettePos = drawRouletteWithLight(rouletteRad);

			if (Input::MouseL.clicked || m_timer.elapsed() > 3000)
			{
				gameState = GameState::Praise;
			}
		}, [&]()
		{
			jyankenFont(L"やっぴ～").drawCenter(jyankenFontPosCenter);

			drawRouletteWithLight(rouletteRad);

			if (Input::MouseL.clicked)
			{
				coinNum += rouletteArray[roulettePos];

				rouletteRad = Random(TwoPi);

				gameState = GameState::Undefined;
			}
		}, [&]()
		{
			font(L"GAME OVER").drawCenter(Window::Center());
		}
	};	

	while (System::Update())
	{
		texture.draw();

		DrawScore(coinNum);

		drawRoulette();

		stageFunctions[static_cast<int>(gameState)]();

		for (const auto& button : jyankenButtons) 
			DrawButton(button.rect, button.line);

		if (tGameState != gameState)
		{
			m_timer.restart();

			tGameState = gameState;
		}
	}
}

```