Skip to content

Instantly share code, notes, and snippets.

@NegiMagnet
Created November 10, 2014 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NegiMagnet/f56127fc162d6e4c97de to your computer and use it in GitHub Desktop.
Save NegiMagnet/f56127fc162d6e4c97de to your computer and use it in GitHub Desktop.
HAL2014
#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