Oven.cpp

//------------------------------------------------------------------------------
/// @file
/// @author   ハル研究所プログラミングコンテスト実行委員会
///
/// @copyright  Copyright (c) 2018 HAL Laboratory, Inc.
/// @attention  このファイルの利用は、同梱のREADMEにある
///             利用条件に従ってください。
//------------------------------------------------------------------------------
#pragma once
#include "Oven.hpp"

//------------------------------------------------------------------------------
#include "Util.hpp"

//------------------------------------------------------------------------------
namespace hpc {

//------------------------------------------------------------------------------
Oven::Oven()
: mRecipe()
, mBakingPieces()
, mLastBakedPieces()
{
}

//------------------------------------------------------------------------------
Oven::Oven(const OvenRecipe& aRecipe)
: mRecipe(aRecipe)
, mBakingPieces()
, mLastBakedPieces()
{
}

//------------------------------------------------------------------------------
bool Oven::isAbleToPut(const Piece& aPiece, const Vector2i& aPos) const
{
    Piece p = aPiece;
    return isAbleToPut(p.width(), p.height(), aPos);
}

//------------------------------------------------------------------------------
bool Oven::isAbleToPut(
    int aPieceWidth,
    int aPieceHeight,
    const Vector2i& aPos
    ) const
{
    return isInArea(aPieceWidth, aPieceHeight, aPos) &&
        isIntersectAnyBakingPiece(aPieceWidth, aPieceHeight, aPos) == false;
}

//------------------------------------------------------------------------------
bool Oven::isInArea(const Piece& aPiece, const Vector2i& aPos) const
{
    Piece p = aPiece;
    return isInArea(p.width(), p.height(), aPos);
}

//------------------------------------------------------------------------------
bool Oven::isInArea(
    int aPieceWidth,
    int aPieceHeight,
    const Vector2i& aPos
    ) const
{
    const int putRight = aPos.x + aPieceWidth - 1;
    const int putBottom = aPos.y + aPieceHeight - 1;
    return 0 <= aPos.x && putRight < width() && 0 <= aPos.y && putBottom < height();
}

//------------------------------------------------------------------------------
bool Oven::isIntersectAnyBakingPiece(const Piece& aPiece, const Vector2i& aPos) const
{
    Piece p = aPiece;
    return isIntersectAnyBakingPiece(p.width(), p.height(), aPos);
}

//------------------------------------------------------------------------------
bool Oven::isIntersectAnyBakingPiece(
    int aPieceWidth,
    int aPieceHeight,
    const Vector2i& aPos
    ) const
{
    for (const auto& bakingPiece : mBakingPieces) {
        if (
            Util::IsIntersect(
                aPos,
                aPieceWidth,
                aPieceHeight,
                bakingPiece.pos(),
                bakingPiece.width(),
                bakingPiece.height()
                )
            )
        {
            // 被っている
            return true;
        }
    }

    // 被っていない
    return false;
}

//------------------------------------------------------------------------------
int Oven::width() const
{
    return mRecipe.width();
}

//------------------------------------------------------------------------------
int Oven::height() const
{
    return mRecipe.height();
}

//------------------------------------------------------------------------------
int Oven::area() const
{
    return mRecipe.area();
}

//------------------------------------------------------------------------------
bool Oven::isEmpty() const
{
    return mBakingPieces.isEmpty();
}

//------------------------------------------------------------------------------
const OvenedPieces& Oven::bakingPieces() const
{
    return mBakingPieces;
}

//------------------------------------------------------------------------------
OvenedPieces& Oven::bakingPieces()
{
    return mBakingPieces;
}

//------------------------------------------------------------------------------
const OvenedPieces& Oven::lastBakedPieces() const
{
    return mLastBakedPieces;
}

//------------------------------------------------------------------------------
OvenedPieces& Oven::lastBakedPieces()
{
    return mLastBakedPieces;
}

//------------------------------------------------------------------------------
void Oven::bakeAndDiscard()
{
    addHeatTurnCount(1);
    clearLastBakedPieces();
    gatherBaked();
}

//------------------------------------------------------------------------------
void Oven::addHeatTurnCount(int aTurnCount)
{
    for (auto& piece : mBakingPieces) {
        piece.addHeatTurnCount(aTurnCount);
    }
}

//------------------------------------------------------------------------------
void Oven::clearLastBakedPieces()
{
    mLastBakedPieces.clear();
}

//------------------------------------------------------------------------------
int Oven::gatherBaked()
{
    int total = 0;

    for (int i = 0; i < mBakingPieces.count(); ++i) {
        auto& piece = mBakingPieces[i];
        if (piece.isCompleted()) {
            total += piece.score();
            mLastBakedPieces.add(piece);
            // 焼き上がった生地を削除、
            // リストの順番に意味は無いので
            // 順番が破壊される代わりに高速に動作する erase を使う
            mBakingPieces.unstableFastErase(i);
            // 末尾の要素が i の位置に移動してきているので、
            // 焼き上がりチェックを再び i の位置からやり直す
            --i;
            continue;
        }
    }

    return total;
}

//------------------------------------------------------------------------------
bool Oven::tryToBake(Piece* aPiece, const Vector2i& aPos)
{
    HPC_ASSERT(aPiece != nullptr);

    // そこに置ける?
    if (isAbleToPut(*aPiece, aPos)) {
    } else {
        // 置けないところに置こうとしています
        return false;
    }

    // 置ける
    aPiece->setPos(aPos);
    mBakingPieces.add(*aPiece);

    return true;
}

} // namespace
// EOF