Stage.cpp
//------------------------------------------------------------------------------
/// @file
/// @author ハル研究所プログラミングコンテスト実行委員会
///
/// @copyright Copyright (c) 2018 HAL Laboratory, Inc.
/// @attention このファイルの利用は、同梱のREADMEにある
/// 利用条件に従ってください。
//------------------------------------------------------------------------------
#pragma once
#include "Stage.hpp"
//------------------------------------------------------------------------------
#include <algorithm>
#include "ArrayNum.hpp"
#include "Assert.hpp"
#include "Math.hpp"
#include "Print.hpp"
//------------------------------------------------------------------------------
namespace hpc {
//------------------------------------------------------------------------------
Stage::Stage()
: mTurn()
, mScore()
, mOven()
, mCandidateLanes()
{
}
//------------------------------------------------------------------------------
int Stage::turn() const
{
return mTurn;
}
//------------------------------------------------------------------------------
const Oven& Stage::oven() const
{
return mOven;
}
//------------------------------------------------------------------------------
const CandidateLane& Stage::candidateLane(CandidateLaneType aType) const
{
return mCandidateLanes[aType];
}
//------------------------------------------------------------------------------
void Stage::init(const RandomSeed& aStageRandomSeed, CandidateLaneRandoms* aLaneRandoms)
{
Random random(aStageRandomSeed);
mTurn = 0;
mScore = 0;
mOven = Oven(OvenRecipe(Parameter::OvenWidth, Parameter::OvenHeight));
{
// 乱数取得を関数引数に直接置くのはアウト。
// 環境に依って引数の評価順序が異なるので、
// ことなる数値で動くことになってしまう。
// PieceFoldPosRate
const float fprCenter =
random.randFloatMinTerm(
Parameter::MinPieceFoldPosRateCenter,
Parameter::MaxPieceFoldPosRateCenter
);
const float fprRange =
random.randFloatMinTerm(
Parameter::MinPieceFoldPosRateRange,
Parameter::MaxPieceFoldPosRateRange
);
// SampleEdgeLength
int selMin = 1;
int selMax = 1;
// PrimalScore
int psMin = 1;
int psMax = 1;
// ScoreCoeff
float scoreCoeffCenter = 1.0f;
float scoreCoeffRange = 0.0f;
// 各生地置き場を生成
for (int laneType = 0; laneType < CandidateLaneType_TERM; ++laneType) {
switch (laneType) {
case CandidateLaneType_Small:
selMin = Parameter::MinSmallPieceSampleEdgeLength;
selMax = Parameter::MaxSmallPieceSampleEdgeLength;
psMin = Parameter::MinSmallPiecePrimalScore;
psMax = Parameter::MaxSmallPiecePrimalScore;
scoreCoeffCenter = Parameter::SmallPieceScoreCoeffCenter;
scoreCoeffRange = Parameter::SmallPieceScoreCoeffRange;
break;
case CandidateLaneType_Large:
selMin = Parameter::MinLargePieceSampleEdgeLength;
selMax = Parameter::MaxLargePieceSampleEdgeLength;
psMin = Parameter::MinLargePiecePrimalScore;
psMax = Parameter::MaxLargePiecePrimalScore;
scoreCoeffCenter =
random.randFloatMinTerm(
Parameter::MinLargePieceScoreCoeffCenter,
Parameter::MaxLargePieceScoreCoeffCenter
);
scoreCoeffRange = Parameter::LargePieceScoreCoeffRange;
break;
default:
HPC_ASSERT_NOT_REACHED_MSG("unknown laneType:%d", laneType);
break;
}
CandidateLaneRecipe recipe(
selMin,
selMax,
Math::LimitMinMax(fprCenter - fprRange / 2, 0.0f, 1.0f),
Math::LimitMinMax(fprCenter + fprRange / 2, 0.0f, 1.0f),
psMin,
psMax,
Math::Max(scoreCoeffCenter - scoreCoeffRange / 2, 0.0f),
Math::Max(scoreCoeffCenter + scoreCoeffRange / 2, 0.0f),
Parameter::CandidatePieceCount
);
mCandidateLanes.add(CandidateLane(recipe));
}
}
fillCandidate(aLaneRandoms);
}
//------------------------------------------------------------------------------
void Stage::processStartPhase()
{
addHeatTurnCount(1);
}
//------------------------------------------------------------------------------
void Stage::processPlayerPhase(const Action& aAction)
{
switch (aAction.type()) {
case ActionType_Wait: processPlayerPhaseWait(aAction); break;
case ActionType_Put: processPlayerPhasePut(aAction); break;
default:
HPC_ASSERT_NOT_REACHED_MSG("Unknown type:%d", aAction.type());
break;
}
}
//------------------------------------------------------------------------------
void Stage::processEndPhase(CandidateLaneRandoms* aLaneRandoms)
{
mScore += gatherBaked();
// ターン開始時に生地置き場チャージすると
// 生地置き場チャージから生地Putまでの間にターン記録処理が入らないので、
// 生地が 1 記録ターン未満の速度で配置されることになり、ビューアで観測できなくなる。
// 対処として生地置き場チャージを終了フェーズで行うようにして、
// 生地置き場チャージとその配置の間に確実にターン記録処理が挟まるようにする。
fillCandidate(aLaneRandoms);
}
//------------------------------------------------------------------------------
void Stage::advanceTurn()
{
++mTurn;
}
//------------------------------------------------------------------------------
bool Stage::isEnd() const
{
// 生地は無限に供給されるので、ステージが自力で終了することはない。
// ターン数によるステージ終了処理は Game で実装されている。
return false;
}
//------------------------------------------------------------------------------
void Stage::fillCandidate(CandidateLaneRandoms* aLaneRandoms)
{
for (int laneType = 0; laneType < CandidateLaneType_TERM; ++laneType) {
auto& lane = mCandidateLanes[laneType];
auto& random = (*aLaneRandoms)[laneType];
lane.fill(&random);
}
}
//------------------------------------------------------------------------------
void Stage::addHeatTurnCount(int aTurnCount)
{
mOven.addHeatTurnCount(aTurnCount);
}
//------------------------------------------------------------------------------
int Stage::gatherBaked()
{
int subScore = 0;
mOven.clearLastBakedPieces();
subScore += mOven.gatherBaked();
return subScore;
}
//------------------------------------------------------------------------------
void Stage::processPlayerPhaseWait(const Action&)
{
// 何もしない
}
//------------------------------------------------------------------------------
void Stage::processPlayerPhasePut(const Action& aAction)
{
if (
0 <= aAction.candidateLaneType() &&
aAction.candidateLaneType() < mCandidateLanes.count()
)
{
} else {
// 生地置き場の指定が範囲外
HPC_ASSERT_NOT_REACHED_MSG(
"0 <= laneType:%d < laneCount:%d, failed",
aAction.candidateLaneType(),
mCandidateLanes.count()
);
// 何もしない
return;
}
auto& lane = mCandidateLanes[aAction.candidateLaneType()];
if (0 <= aAction.pieceIndex() && aAction.pieceIndex() < lane.pieces().count()) {
} else {
// 記事番号指定が範囲外
HPC_ASSERT_NOT_REACHED_MSG(
"0 <= pieceIndex:%d < candidateCount:%d, failed",
aAction.pieceIndex(),
lane.pieces().count()
);
// 何もしない
return;
}
auto& piece = lane.pieces()[aAction.pieceIndex()];
if (mOven.tryToBake(&piece, aAction.putPos())) {
// 成功
} else {
// 生地配置位置が悪い(他生地と被っている、オーブンの天板をはみ出ている、など)
// 何もしない
return;
}
// 全部通過できていたら、
// その生地はめでたくオーブンに入ったということ。
// 生地置き場から生地を消す。
lane.pieces().erase(aAction.pieceIndex());
}
} // namespace
// EOF