Created
November 10, 2014 07:50
-
-
Save NegiMagnet/f56127fc162d6e4c97de to your computer and use it in GitHub Desktop.
HAL2014
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "HPCAnswerInclude.hpp" | |
namespace { | |
using namespace hpc; | |
/// タイマー | |
int sTimer = 0; | |
int prev = 0; // 前ターンの目標蓮No. | |
bool straight = false; | |
Vec2 target; | |
Vec2 prePos; // 前ターンの位置 | |
Vec2 preVec; | |
bool prevAccel; // 前ターン加速したか | |
int collisionTimer = 0; // ニンジャと追突したら起動、0になると加速 | |
int COLLISION_TIMER; | |
bool firstMode; | |
const int L_OFFSET = 0.0f; | |
bool stepAccel; // 踏み加速 | |
} | |
/// プロコン問題環境を表します。 | |
namespace hpc { | |
//------------------------------------------------------------------------------ | |
Vec2 getInsideCorner(const StageAccessor& aSt, int index) { | |
const Chara& player = aSt.player(); | |
Vec2 next = aSt.lotuses()[index].pos() - player.pos(); | |
Vec2 nnext = aSt.lotuses()[(index+1)%aSt.lotuses().count()].pos() - player.pos(); | |
float len = next.length(); | |
// nnextを狙う途中でnextを通過できないかチェック | |
float t = next.dot(nnext) / nnext.length(); | |
Vec2 proj = (next - player.pos() - nnext*t); | |
float dist = proj.length(); | |
if(dist < player.region().radius() + aSt.lotuses()[index].radius() && | |
next.length()+nnext.length()-(nnext-next).length() < 0.01f ) { | |
// nnextを狙う | |
next = nnext; | |
nnext = aSt.lotuses()[(index+2)%aSt.lotuses().count()].pos() - player.pos(); | |
} | |
next.normalize(); | |
nnext.normalize(); | |
float cr = next.cross(nnext); | |
if(Math::Abs(cr) < 0.3f) { | |
return aSt.lotuses()[index].pos(); | |
} | |
float rad = (aSt.lotuses()[index].region().radius() + player.region().radius()) / len / 2.0; | |
if(cr < 0.0f) { | |
rad *= -1; | |
} | |
return next.getRotated(rad) + player.pos(); | |
} | |
/* | |
//------------------------------------------------------------------------------ | |
int getRank(const StageAccessor ast) { | |
const Chara& player = ast.player(); | |
const EnemyAccessor& enemies = ast.enemies(); | |
const LotusCollection& lotuses = ast.lotuses(); | |
int upperRank = 0; | |
for(int i=0; i<enemies.count(); i++) { | |
if(player.passedLotusCount() < enemies[i].passedLotusCount() || | |
(player.passedLotusCount()==enemies[i].passedLotusCount() && (player.pos()-lotuses[player.targetLotusNo()].pos()).length() > (enemies[i].pos()-lotuses[enemies[i].targetLotusNo()].pos()).length())) | |
upperRank++; | |
} | |
return upperRank; | |
} | |
//------------------------------------------------------------------------------ | |
bool isTop(const StageAccessor ast) { | |
const Chara& player = ast.player(); | |
const EnemyAccessor& enemies = ast.enemies(); | |
const LotusCollection& lotuses = ast.lotuses(); | |
for(int i=0; i<enemies.count(); i++) { | |
if(enemies[i].passedLotusCount() > player.passedLotusCount()) { | |
return false; | |
} | |
else if(enemies[i].passedLotusCount() == player.passedLotusCount()) { | |
Vec2 vpl = player.pos() - lotuses[player.targetLotusNo()].pos(); | |
Vec2 vel = enemies[i].pos() - lotuses[enemies[i].targetLotusNo()].pos(); | |
if(vel.length() - vpl.length() < player.region().radius() * 10) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
*/ | |
//------------------------------------------------------------------------------ | |
/// 各ステージ開始時に呼び出されます。 | |
/// | |
/// この関数を実装することで、各ステージに対して初期処理を行うことができます。 | |
/// | |
/// @param[in] aStageAccessor 現在のステージ。 | |
void Answer::Init(const StageAccessor& aStageAccessor) | |
{ | |
sTimer = Parameter::CharaAddAccelWaitTurn; | |
prev = aStageAccessor.player().targetLotusNo(); | |
prePos = aStageAccessor.player().pos(); | |
prevAccel = false; | |
COLLISION_TIMER = Parameter::CharaAddAccelWaitTurn / 3.0f; | |
stepAccel = false; | |
// 初期位置から、スタートダッシュを切るか判断 | |
Vec2 pvec = (aStageAccessor.lotuses()[0].pos()-aStageAccessor.player().pos()); | |
bool nextSide = pvec.cross(aStageAccessor.lotuses()[1].pos()-aStageAccessor.player().pos()) > 0.0f; | |
int count = 0; | |
for(int i=0; i<aStageAccessor.enemies().count(); i++) { | |
bool side = pvec.cross(aStageAccessor.enemies()[i].pos()-aStageAccessor.player().pos()) > 0.0f; | |
if(side == nextSide) count++; | |
} | |
firstMode = count <= 1; // [1]側にいる敵の数が0か1ならスタートダッシュ | |
firstMode &= pvec.length() < 16.0f; // 長すぎるのはダメ | |
// printf("--------------------\n"); | |
} | |
//------------------------------------------------------------------------------ | |
/// 各ターンでの動作を返します。 | |
/// | |
/// @param[in] aStageAccessor 現在ステージの情報。 | |
/// | |
/// @return これから行う動作を表す Action クラス。 | |
Action Answer::GetNextAction(const StageAccessor& aStageAccessor) | |
{ | |
const Chara& player = aStageAccessor.player(); | |
const LotusCollection& lotuses = aStageAccessor.lotuses(); | |
const Vec2 flow = aStageAccessor.field().flowVel(); | |
++sTimer; | |
target = getInsideCorner(aStageAccessor, player.targetLotusNo()); | |
// 最初のとこまで加速頻度を高める | |
// なるまで? なれそうなら? | |
int offset = 1; | |
if(player.passedLotusCount()==0) { | |
if(firstMode) { | |
offset = -5; | |
} else { | |
offset = 2; | |
} | |
} | |
// (自分->次の蓮)と(次の蓮->次の次の蓮)の内積を計算 | |
int pre = (player.targetLotusNo()+lotuses.count()-2)%lotuses.count(); | |
int curr = (player.targetLotusNo()+lotuses.count()-1)%lotuses.count(); | |
Vec2 u = lotuses[curr].pos() - lotuses[pre].pos(); | |
Vec2 v = lotuses[player.targetLotusNo()].pos() - lotuses[curr].pos(); | |
u.normalize(); | |
v.normalize(); | |
straight = ( u.dot(v) > 0.2f ); | |
// if(firstMode) straight = false; | |
stepAccel |= (prev!=player.targetLotusNo() && !straight); | |
// 余裕があるなら加速するスタイル(小規模コース対策) | |
int rest = lotuses.count()*3 - player.passedLotusCount(); | |
if(rest <= player.accelCount()) { | |
offset = -5; | |
} | |
// ニンジャと追突したらタイマー起動(前回加速してなくて、前回と加速度の向きが変わってる場合) | |
bool collisionAccel = false; | |
if(collisionTimer>0) { | |
collisionTimer--; | |
if(collisionTimer==0) collisionAccel = true; | |
} | |
else if(!prevAccel && prev==player.targetLotusNo()) { | |
Vec2 preVel = preVec; | |
Vec2 curVel = player.vel(); | |
preVel.normalize(); | |
curVel.normalize(); | |
if(!preVel.isZero() && !curVel.isZero() && preVel.dot(curVel) < 0.6f) { | |
collisionTimer = COLLISION_TIMER; | |
} | |
float preDist = prePos.dist(target); | |
float curDist = player.pos().dist(target); | |
if( preDist < curDist ) { // 遠ざかっているなら即時加速 | |
collisionTimer = 0; | |
collisionAccel = true; | |
} | |
} | |
// targetの方向に進む時、敵と衝突しないかチェック。衝突するならずらし角度の計算 | |
// 最終的にここの計算結果を使わない方がスコアが伸びたので使わなかった(血涙を流しながら) | |
float leftRad=0.0f, rightRad=0.0f; | |
Vec2 pvel = target - player.pos(); | |
for(int i=0; i<aStageAccessor.enemies().count(); i++) { | |
const Chara& enemy = aStageAccessor.enemies()[i]; | |
Vec2 venorm = enemy.vel() + lotuses[enemy.targetLotusNo()].pos() - enemy.pos(); | |
if(venorm.isZero()) { | |
venorm = lotuses[enemy.targetLotusNo()].pos() - enemy.pos(); | |
} | |
venorm.normalize(); | |
Vec2 vpnorm = player.vel(); | |
if(vpnorm.isZero() || !straight) { | |
vpnorm = lotuses[player.targetLotusNo()].pos()-player.pos(); | |
} | |
vpnorm.normalize(); | |
bool willCollision = false; | |
Vec2 vep = enemy.pos(); | |
Vec2 vp = player.pos(); | |
float sumRadius = enemy.region().radius() + player.region().radius(); | |
// 4フレーム以内に衝突するか判断 | |
for(int g=0; g<4; g++) { | |
vep += venorm * 1.1f; | |
vp += vpnorm * 1.1f; | |
if((vep-vp).length() < sumRadius) { | |
willCollision = true; | |
break; | |
} | |
} | |
if(willCollision) { | |
float rad = pvel.rotSign(enemy.pos()-player.pos()); | |
float diff = (player.region().radius()+aStageAccessor.enemies()[i].region().radius()+L_OFFSET*(enemy.pos()-player.pos()).length()) / (enemy.pos()-player.pos()).length(); | |
float lr = rad-diff; | |
float rr = rad+diff; | |
while(lr < -Math::PI) lr += Math::PI*2.0f; | |
while(lr > Math::PI) lr -= Math::PI*2.0f; | |
while(rr < -Math::PI) rr += Math::PI*2.0f; | |
while(rr > Math::PI) rr -= Math::PI*2.0f; | |
if(rr < lr) { | |
float swp = lr; | |
lr = rr; | |
rr = swp; | |
} | |
if( lr < leftRad && leftRad < rr ) leftRad = lr; | |
if( lr < rightRad && rightRad < rr ) rightRad = rr; | |
} | |
} | |
// minRad != 0.0 のとき、衝突しうる | |
float minRad = (Math::Abs(leftRad) < Math::Abs(rightRad)) ? leftRad : rightRad; | |
// 一定ターンごと || 蓮を踏んだとき || 衝突のCOLLISION_TIMERフレーム後 | |
if (player.accelCount()>0 && (Parameter::CharaAddAccelWaitTurn+offset <= sTimer || stepAccel || collisionAccel) ) { | |
// 次でターン加速が必要なら加速回数が1残るようセーブする | |
if( player.accelCount()==1 && u.dot(v)<0.2f && prev==player.targetLotusNo() && rest>1 ) { | |
prePos = player.pos(); | |
preVec = player.vel(); | |
prevAccel = false; | |
return Action::Wait(); | |
} | |
sTimer = 0; | |
prev = player.targetLotusNo(); | |
prePos = player.pos(); | |
preVec = player.vel(); | |
prevAccel = true; | |
stepAccel = false; | |
collisionTimer = 0; | |
if( Math::Abs(minRad) < Math::PI/4.0f && (target-player.pos()).length()>(player.region().radius()+lotuses[player.targetLotusNo()].radius())*2.0f ) { | |
// target = (target-player.pos()).getRotated(minRad) + player.pos(); | |
} | |
// flowを考慮 | |
target -= flow; | |
return Action::Accel(target); | |
} | |
prev = player.targetLotusNo(); | |
prePos = player.pos(); | |
preVec = player.vel(); | |
prevAccel = false; | |
return Action::Wait(); | |
} | |
} | |
//------------------------------------------------------------------------------ | |
// EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment