楽しい在庫管理

今回は倉庫番を作ってみました。
が、フルセットの仕様を知らないのでそこらへん適当です。。。

#倉庫番とは。
古いパズルゲームでオールドゲーマとか年配のプログラマなら知らない人は少ないくらいだと思います。
主人公と箱と壁と箱の届け先でできています。
主人公を動かして箱を移動して届け先に届けるというモノです。
壁は邪魔するだけです。

シンプルなのに奥深いというなかなかイカシタゲームです。

#見どころ。
このコードではタイリングの方法は使いませんでした。
なので、毎回問い合わせて生成するので倉庫番にしては処理を食いますです。
その代わり自由度は高いです。

#免責
このコードのデバッグはやってはいますが、完全ではない可能性があります。
どこかが商標をまだ持ってたら削除する可能性があります。

#コード
```C++:SoukoBanSystem.h
#pragma once
#include <vector>
#include <cstdint>
#include <tuple>

#include "SoukoBanField.h"

class SoukoBanSystem {
	typedef std::tuple<std::uintmax_t, std::uintmax_t> Point;
	typedef std::vector<Point> Points;
public:

	enum class Direction :std::uint8_t {
		Up,
		Right,
		Down,
		Left,
	};
	bool New(std::uintmax_t W, std::uintmax_t Y) {
		SF.CreateField(W, Y);

		Hako.clear();
		std::get<0>(Hero) = 0;
		std::get<1>(Hero) = 0;
		return true;
	}
	bool SetHero(std::uintmax_t X, std::uintmax_t Y) {
		if (!SF.InRange(X, Y)) { return false; }
		std::get<0>(Hero) = X;
		std::get<1>(Hero) = Y;
		return true;
	}

	bool MoveHako(std::uintmax_t X, std::uintmax_t Y, Direction D) {
		std::vector<std::int8_t> XM{ 0,1,0, -1 };
		std::vector<std::int8_t> YM{ 1,0, -1,0 };

		if (!HaveHako(X, Y)) { return false; }

		std::uint8_t Dir = static_cast<std::uint8_t>(D) % 4;

		std::intmax_t ID = GetHakoID(X, Y);

		if (ID == -1) { return false; }
		if (!InRange(X + XM[Dir], Y + YM[Dir])) { return false; }
		if (SF.HaveWall(X + XM[Dir], Y + YM[Dir])) { return false; }
		if (HaveHako(X + XM[Dir], Y + YM[Dir])) { return false; }

		std::get<0>(Hako[ID]) += XM[Dir];
		std::get<1>(Hako[ID]) += YM[Dir];

		return true;
	}

	bool InRange(std::uintmax_t X, std::uintmax_t Y) const {
		return SF.InRange(X, Y);
	}
	bool OnHero(std::uintmax_t X, std::uintmax_t Y)const {
		return std::get<0>(Hero) == X && std::get<1>(Hero) == Y;
	}
	bool MoveHero(Direction D) {
		std::vector<std::int8_t> XM{ 0,1,0, -1 };
		std::vector<std::int8_t> YM{ 1,0, -1,0 };
		std::uint8_t Dir = static_cast<std::uint8_t>(D) % 4;
		if (!InRange(std::get<0>(Hero) + XM[Dir], std::get<1>(Hero) + YM[Dir])) { return false; }
		if (SF.HaveWall(std::get<0>(Hero) + XM[Dir], std::get<1>(Hero) + YM[Dir])) { return false; }
		if (HaveHako(std::get<0>(Hero) + XM[Dir], std::get<1>(Hero) + YM[Dir])) {
			if (!MoveHako(std::get<0>(Hero) + XM[Dir], std::get<1>(Hero) + YM[Dir], D)) { return false; };
		}
		if (!InRange(std::get<0>(Hero) + XM[Dir], std::get<1>(Hero) + YM[Dir])) { return false; }
		std::get<0>(Hero) += XM[Dir];
		std::get<1>(Hero) += YM[Dir];

		return true;
	}
	bool HaveHako(std::uintmax_t X, std::uintmax_t Y) const {
		if (!SF.InRange(X, Y)) { return false; }
		if (SF.HaveWall(X, Y)) { return false; }

		for (auto& o : Hako) {
			if (std::get<0>(o) == X && std::get<1>(o) == Y) { return true; }
		}
		return false;

	}
	bool IsGameEnd() const {
		for (auto o : Hako) {
			if (!SF.HaveBatu(std::get<0>(o), std::get<1>(o))) { return false; }
		}
		return true;
	}

	bool AddHako(std::uintmax_t X, std::uintmax_t Y) {
		if (SF.HaveWall(X, Y)) { return false; }
		if (HaveHako(X, Y)) { return false; }

		Hako.push_back({ X,Y });
		return true;
	}

	std::size_t HakoCount() const {
		return Hako.size();
	}

	std::uintmax_t Width() const {
		return SF.Width();
	}
	std::uintmax_t Height() const {
		return SF.Height();
	}
	const SoukoBanField& GetField()const {
		return SF;
	}
	SoukoBanField& GetField() {
		return SF;
	}
protected:
	std::intmax_t GetHakoID(std::uintmax_t X, std::uintmax_t Y) {
		for (std::size_t i = 0; i < Hako.size(); i++) {
			if (std::get<0>(Hako[i]) == X && std::get<1>(Hako[i]) == Y) { return i; }
		}
		return -1;
	}

protected:
	SoukoBanField SF;
	Points Hako;
	Point Hero;
};
```
```C++:SoukoBanField.h
#pragma once
#include <vector>
#include <cstdint>
#include <tuple>

class SoukoBanField {
	typedef std::tuple<std::uintmax_t, std::uintmax_t> Point;
	typedef std::vector<Point> Points;
public:

	bool CreateField(std::uintmax_t Width, std::uintmax_t Height) {
		Wall.clear();
		Batu.clear();
		W = Width;
		H = Height;
		return true;
	}

	bool InRange(std::uintmax_t X, std::uintmax_t Y) const{
		if (X >= W) { return false; }
		if (Y >= H) { return false; }

		return true;
	}

	bool HaveWall(std::uintmax_t X, std::uintmax_t Y) const{	
		if (!InRange(X,Y)) { return false; }

		for (auto& o : Wall) {
			if (std::get<0>(o) == X && std::get<1>(o) == Y) { return true; }
		}
		return false;
	}

	bool SetWall(std::uintmax_t X, std::uintmax_t Y) {	
		if (!InRange(X,Y)) { return false; }
		if (HaveWall(X, Y)) { return false; }
		if (HaveBatu(X, Y)) { return false; }
		Wall.push_back({ X,Y });
		return true;
	}
	bool EraseWall(std::uintmax_t X, std::uintmax_t Y) {
		if (!InRange(X,Y)) { return false; }
		if (!HaveWall(X, Y)) { return false; }
		for (std::size_t i = 0; i < Wall.size(); i++) {
			if (std::get<0>(Wall[i]) == X && std::get<1>(Wall[i]) == Y) {
				Wall.erase(Wall.begin() + i);
				return true;
			}
		}
		return false;
	}
	bool HaveBatu(std::uintmax_t X, std::uintmax_t Y) const{
		if (!InRange(X,Y)) { return false; }
		for (auto& o : Batu) {
			if (std::get<0>(o) == X && std::get<1>(o) == Y) { return true; }
		}
		return false;
	}

	bool SetBatu(std::uintmax_t X, std::uintmax_t Y) {
		if (!InRange(X,Y)) { return false; }
		if (HaveBatu(X, Y)) { return false; }
		if (HaveWall(X, Y)) { return false; }
		Batu.push_back({ X,Y });
		return true;
	}
	bool EraseBatu(std::uintmax_t X, std::uintmax_t Y) {
		if (!InRange(X,Y)) { return false; }
		if (!HaveBatu(X, Y)) { return false; }
		for (std::size_t i = 0; i < Wall.size(); i++) {
			if (std::get<0>(Batu[i]) == X && std::get<1>(Batu[i]) == Y) {
				Batu.erase(Batu.begin() + i);
				return true;
			}
		}
		return false;
	}
	std::uintmax_t Width() const{
		return W;
	}
	std::uintmax_t Height() const{
		return H;
	}
	std::size_t WallCount()const {
		return Wall.size();
	}
	std::size_t BatuCount() const{
		return Batu.size();
	}
protected:
	Points Wall;
	Points Batu;
	std::uintmax_t W = 0;
	std::uintmax_t H = 0;
};
```
```C++:Source.cpp
#include <iostream>
#include <random>
#include <cstdlib>

#include <conio.h>//not standard.

#include "SoukoBanSystem.h"

SoukoBanSystem MakeField(){
	SoukoBanSystem SS;
	SS.New(16, 16);
	SS.GetField().SetBatu(7, 6);
	SS.GetField().SetBatu(7, 7);
	SS.GetField().SetWall(1, 1);
	SS.AddHako(8, 7);
	SS.AddHako(9, 7);
	SS.SetHero(10, 7);

	return SS;
}

SoukoBanSystem MakeField(std::uintmax_t W, std::uintmax_t H,std::uintmax_t Wall=16,std::uintmax_t Batu=8,unsigned int S=0) {
	SoukoBanSystem SS;
	SS.New(W, H);

	std::mt19937 mt(S);
	std::uniform_int_distribution<std::uintmax_t> Xui(1, W - 2);
	std::uniform_int_distribution<std::uintmax_t> Yui(1, H - 2);

	for (std::uintmax_t i = 0; i < Wall; i++) {
		if (!SS.GetField().SetWall(Xui(mt), Yui(mt))) {i--;continue;};
	}

	for (std::uintmax_t i = 0; i < Batu; i++) {
		if (!SS.GetField().SetBatu(Xui(mt), Yui(mt))) {i--;continue;};
	}
	for (std::uintmax_t i = 0; i < Batu; i++) {
		if (!SS.AddHako(Xui(mt), Yui(mt))) {i--;continue;};
	}
	return SS;
}

bool Show(const SoukoBanSystem& SS) {

	std::system("cls");
	for (std::uintmax_t Y = 0; Y < SS.Height(); Y++) {
		for (std::uintmax_t X = 0; X < SS.Width(); X++) {
			char Ch = ' ';
			if (SS.GetField().HaveWall(X, Y)) { Ch = 'W'; }
			if (SS.GetField().HaveBatu(X, Y)) { Ch = 'X'; }
			if (SS.HaveHako(X, Y)) {
				if (Ch == 'X') { Ch = '+'; }
				else{ Ch = 'B'; }
			}
			if (SS.OnHero(X, Y)) { Ch = 'H'; }
			std::cout << Ch;
		}
		std::cout << std::endl;
	}

	return true;
}

int main() {
	SoukoBanSystem SS = MakeField(16,16);//ランダム生成。解けない問題ができる可能性がある。

	while (!SS.IsGameEnd()) {
		Show(SS);
		std::cout << "ESC Key to Shutdown Game." << std::endl;
		int key = _getch();
		if (key == 27) { break; }

		if (key == 'w') {
			SS.MoveHero(SoukoBanSystem::Direction::Down);
		}
		if (key == 's') {
			SS.MoveHero(SoukoBanSystem::Direction::Up);
		}
		if (key == 'a') {
			SS.MoveHero(SoukoBanSystem::Direction::Left);
		}
		if (key == 'd') {
			SS.MoveHero(SoukoBanSystem::Direction::Right);
		}
		std::cin.clear();
	}
	Show(SS);
	std::cout << "Game End!" << std::endl;
}
```
#スクリーンショット
![無題__.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509227/5a1a664a-7663-f826-f59a-4f3f0f1a148a.png)