Last active
April 5, 2016 10:20
-
-
Save buyoh/583dd858043e5e0570ab to your computer and use it in GitHub Desktop.
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
/* | |
オプション: | |
Dを付与して実行するとログをオエーする | |
example :: codevs D < stdin.txt 2> out.log | |
Rを付与して実行するとパラメータにランダム要素が加わる | |
DとRは重複できない。 | |
コンパイラ: | |
VS2013 x64 (cl /EHsc /O2 codevs.cpp) | |
cygwinのg++4.9.3でもコンパイルは通るように 実行は未検証 | |
危険な可読性: | |
Entityクラスの異常な汎用性 | |
Entity.idメンバに距離突っ込んだりしてる。 | |
推奨されるconst表記が無い。 | |
goto使用。 | |
コメントが無い。 | |
統一しない書き方。 | |
間違った英語 | |
その他 | |
開発メモ: | |
壁端検出は不要(Wの検出でおk) | |
昇順にするために、マイナスにする | |
seed:1 -> quick:cost1 | |
seed:123 -> quick+round | |
79850 | |
seed:41348 -> enemydril:cost1 | |
seed:529081492790143254 -> quick:cost2 | |
seed:6892501092310159412 -> maze | |
seed:31416 -> maze(round) なやみ | |
seed789 -> 上の方にsoulかたまってる | |
361 -> old/codevs20160314_150109に5連敗 | |
毎回predictDogやってるような気はするので | |
速度が気になるようならNinjaActionに持たせる? | |
メモリの問題が露呈 | |
checkerは走査の跡であって、候補の跡ではない | |
回転切り | |
分身に岩を重ねてしまう | |
*/ | |
#include <iostream> | |
#include <algorithm> | |
#include <vector> | |
#include <stack> | |
#include <queue> | |
#include <cstdio> | |
#include <cmath> | |
#include <sstream> | |
#include <stdexcept> | |
#include <random> | |
using namespace std; | |
// ログをオエーする色々 | |
#define oserr (debugmode?cerr:nullout) | |
struct null_streambuf:streambuf { | |
int overflow(int c){return c;} | |
}nullbuff; | |
bool debugmode; | |
bool randommode = false; | |
#ifdef __GNUC__ // gcc対策 | |
ostream nullout(&nullbuff); | |
#else | |
ostream& nullout = ostream(&nullbuff); | |
#endif | |
inline void debugchk(){ | |
static int c = 0; | |
oserr << "dbg:chkpoint - " << (c++) << endl; | |
} | |
random_device drnd; | |
inline int rand(int d,int l,int h){ | |
return !randommode ? d : (drnd() % (h - l + 1) + l); | |
} | |
// 忍者や犬を保持することが目的のクラス。 | |
// vectorに入れてソートするとid昇順になる。 | |
// *linkメンバーで追加のデータを関連付けることもある。 | |
// ソートが便利なので、忍者や犬以外にもよく使う | |
class Entity{ | |
public: // がばがば | |
int id; | |
int x,y; | |
void *link; // 追加,データ引っさげたい用 | |
//Entity(Entity& e) :id(e.id), x(e.x), y(e.y){} | |
Entity(int id, int x, int y,void *link) :id(id), x(x), y(y),link(link){} | |
Entity(int id, int x, int y) :id(id), x(x), y(y){} | |
//Entity(Entity& e,void *link) :id(e.id), x(e.x), y(e.y),link(link){} | |
Entity(int x,int y):id(0),x(x),y(y){} | |
Entity():id(0),x(-1),y(-1){} | |
bool operator <(const Entity& e) const{ | |
return id<e.id; | |
} | |
string tostring(){ | |
ostringstream os; | |
os << "E:"<<id<<" ["<<x<<","<<y<<"]"<<flush; | |
return os.str(); | |
} | |
}; | |
// ゲームマップを保持することが目的のクラス。 | |
// 中身は2次元integer配列に便利な演算を与えたもの | |
class Field{ | |
private: | |
int psix=0,psiy=0; | |
public: | |
int width,height; | |
vector<vector<int>> data; | |
Field():width(0),height(0){} | |
Field(int w,int h):width(w),height(h){ | |
data.assign(w,vector<int>(h,0)); | |
}; | |
vector<int>& operator[](int idx){ | |
return data[idx]; | |
} | |
void push(int d){ | |
(*this)[psix][psiy]=d; | |
if (width<=++psix){ | |
psix=0; | |
if (height<=++psiy) | |
psiy=0; | |
} | |
} | |
// すべてdで埋める | |
void fill(int d){ | |
for (vector<int> &v:data) | |
for (int &e:v) | |
e=d; | |
} | |
Field& operator+=(Field& f){ | |
if (f.width!=width || f.height!=height) | |
throw invalid_argument("class[Field] operator[+=] exception."); | |
for (int y=0;y<height;y++) | |
for (int x=0;x<width;x++) | |
data[x][y]+=f[x][y]; | |
return *this; | |
} | |
Field& operator-=(Field& f){ | |
if (f.width!=width || f.height!=height) | |
throw invalid_argument("class[Field] operator[-=] exception."); | |
for (int y=0;y<height;y++) | |
for (int x=0;x<width;x++) | |
data[x][y]-=f[x][y]; | |
return *this; | |
} | |
// cと一致する値が8近傍にいくつあるか | |
int roundsum(int x, int y,int c){ | |
return (data[x - 1][y - 1]==c) | |
+ (data[x - 1][y]==c) | |
+ (data[x - 1][y + 1]==c) | |
+ (data[x][y + 1] == c) | |
+ (data[x + 1][y + 1] == c) | |
+ (data[x + 1][y] == c) | |
+ (data[x + 1][y - 1] == c) | |
+ (data[x][y - 1] == c); | |
} | |
// 8近傍の値の総和を返す | |
int roundsum(int x,int y){ | |
return data[x - 1][y - 1] | |
+ data[x - 1][y] | |
+ data[x - 1][y + 1] | |
+ data[x][y + 1] | |
+ data[x + 1][y + 1] | |
+ data[x + 1][y] | |
+ data[x + 1][y - 1] | |
+ data[x][y - 1]; | |
} | |
// cと一致する値が4近傍にいくつあるか | |
int roundsum4(int x, int y,int c){ | |
return (data[x - 1][y] == c) | |
+ (data[x][y + 1] == c) | |
+ (data[x + 1][y] == c) | |
+ (data[x][y - 1] == c); | |
} | |
// 4近傍の値の総和を返す | |
int roundsum4(int x, int y){ | |
return data[x - 1][y] | |
+ data[x][y + 1] | |
+ data[x + 1][y] | |
+ data[x][y - 1]; | |
} | |
void eprintchar(){ | |
/* | |
for (vector<int>& e:data){ | |
for (int& c:e){ | |
oserr<<(char)c; | |
} | |
oserr<<endl; | |
} */ | |
for (int y=0;y<height;y++){ | |
for (int x=0;x<width;x++) | |
oserr<<(char)data[x][y]; | |
oserr<<endl; | |
} | |
} | |
void eprintint(){ | |
char str[64]; | |
for (int y=0;y<height;y++){ | |
for (int x=0;x<width;x++){ | |
sprintf(str,"%4d",data[x][y]); | |
oserr<<str; | |
} | |
oserr<<endl; | |
} | |
} | |
}; | |
// プレイヤーの状態を保持するクラス。 | |
// 忍者・犬の位置やマップなど。 | |
// 岩を動かしたり、スペルを唱えたりするメソットも定義。 | |
class PlayerStatus{ | |
public: | |
int spell; | |
Field field; | |
Field scorefield; | |
int nninja; // contantly | |
int ndog; | |
int nsoul; | |
int nableSoul; | |
vector<Entity> ninja; | |
vector<Entity> dog; | |
vector<Entity> soul; | |
vector<Entity> ableSoul; | |
vector<int> usedSpell; | |
// for stonespell | |
stack<Entity> stoneSpell; | |
void setFieldSize(int w,int h){ | |
field=Field(w,h); | |
scorefield=Field(w,h); | |
} | |
void reset(){ | |
ninja.clear(); | |
dog.clear(); | |
soul.clear(); | |
scorefield.fill(0); | |
usedSpell.clear(); | |
} | |
bool isDogHere(int x,int y){ | |
for (Entity &e : dog) | |
if (e.x == x&&e.y == y) | |
return true; | |
return false; | |
} | |
bool isSoulHere(int x, int y){ | |
for (Entity &e : soul) | |
if (e.x == x&&e.y == y) | |
return true; | |
return false; | |
} | |
void calcScore(); | |
// 犬の居場所を記したFieldクラスを返戻する。boolean | |
void setDogMap(Field &out){ | |
out.fill(0); | |
for (Entity &e : dog) | |
out[e.x][e.y] = 1; | |
} | |
// ソウルの居場所を記したFieldクラスを返戻する。boolean | |
void setSoulMap(Field &out){ | |
out.fill(0); | |
for (Entity &e : soul) | |
out[e.x][e.y] = 1; | |
} | |
// マップ(x,y)座標をcに書き換える。 | |
void fieldOverride(int c, int x, int y){ | |
Entity e(field[x][y], x, y); | |
stoneSpell.push(e); | |
field[x][y] = c; | |
calcAbleSoul(); // ablesoul再計算 | |
} | |
// 書き換えたマップから1段階元に戻す | |
void fieldUndo(){ | |
if (stoneSpell.empty()) return; | |
Entity &e = stoneSpell.top(); | |
field[e.x][e.y] = e.id; | |
stoneSpell.pop(); | |
calcAbleSoul(); // ablesoul再計算 | |
} | |
// x,y座標に居るninjaはvx,vy方向へ移動可能か? | |
// 0 移動できない | |
// 1 移動できる | |
// 2 岩を押して移動する | |
int isNinjaMoveable(int x, int y, int vx, int vy,Field &p_field){ | |
if (2 <= vx*vx + vy*vy) | |
throw invalid_argument("class[Field] isNinjaMoveable exception."); | |
// vx[-1,0,1] vy[-1,0,1] | |
int &i = p_field[x + vx][y + vy]; | |
if (i == '_') return 1; | |
if (i == 'W') return 0; | |
if (i == 'O'){ | |
int nx = x + vx * 2, ny = y + vy * 2; | |
if (p_field[nx][ny] != '_') return 0; | |
for (Entity &e : dog) | |
if (e.x == nx && e.y == ny) return 0; | |
for (Entity &e : ninja) | |
if (e.x == nx && e.y == ny) return 0; | |
return 2; | |
} | |
return 0; | |
} | |
int isNinjaMoveable(int x, int y, int vx, int vy){ | |
return isNinjaMoveable(x, y, vx, vy, field); | |
} | |
// ninjaidが回転斬りを発動し、犬を削除する。 | |
// 個人的に回転斬りは好きです | |
void attack(int ninjaid){ | |
Entity &nj = ninja[ninjaid]; | |
vector<Entity> qdog = dog; | |
dog.clear(); ndog = 0; | |
for (Entity &e : qdog){ | |
if ((e.x - nj.x)*(e.x - nj.x) > 1 || | |
(e.y - nj.y)*(e.y - nj.y) > 1){ | |
ndog++; | |
dog.push_back(Entity(e)); | |
} | |
} | |
} | |
// x,y座標に居るninjaがvx,vy方向へ岩を押す。 | |
// このとき、マップは書き換えられる。 | |
bool pushRock(int x, int y, int vx, int vy, Field &p_field){ | |
if (2 <= vx*vx + vy*vy) | |
throw invalid_argument("class[Field] pushRock exception."); | |
if (isNinjaMoveable(x, y, vx, vy) != 2) return false; | |
p_field[x + vx][y + vy] = '_'; | |
p_field[x + vx * 2][y + vy * 2] = 'O'; | |
if (&p_field == &field) calcAbleSoul(); // ablesoul再計算 | |
return true; | |
} | |
bool pushRock(int x, int y, int vx, int vy){ | |
return pushRock(x, y, vx, vy, field); | |
} | |
// 岩に重なっていない・押せば手に入るソウルを「ざっくり」計算する。ほぼ無意味でした | |
void calcAbleSoul(){ | |
ableSoul.clear(); | |
nableSoul = 0; | |
for (Entity &e : soul){ | |
if (field[e.x][e.y] == '_' || ( | |
(field[e.x - 1][e.y] == '_' && isNinjaMoveable(e.x - 1, e.y, 1, 0)) || | |
(field[e.x + 1][e.y] == '_' && isNinjaMoveable(e.x + 1, e.y, -1, 0)) || | |
(field[e.x][e.y - 1] == '_' && isNinjaMoveable(e.x, e.y - 1, 0, 1)) || | |
(field[e.x][e.y + 1] == '_' && isNinjaMoveable(e.x, e.y + 1, 0, -1)))){ | |
nableSoul++; | |
ableSoul.push_back(e); | |
} | |
} | |
} | |
}; | |
// ゲームの状態を保持するクラス | |
class Game{ | |
public: | |
int time; | |
int nspell; | |
vector<int> spellcost; | |
PlayerStatus me; | |
PlayerStatus enemy; | |
void reset(){ | |
spellcost.clear(); | |
me.reset(); | |
enemy.reset(); | |
} | |
}; | |
// ninjaの行動を保持するクラス。具体的に「LU」「UR」「移動なし」が該当。 | |
// スコアの計算保持もあるが、使ったか使ってないだか。 | |
class NinjaAction{ | |
public: | |
int x, y; // 最終座標 | |
int sx, sy; // スタート座標 | |
int vx = 0, vy = 0; // 結果動いた距離 | |
queue<int> history; | |
int historysize = 0; | |
int score=0; | |
int lastscore=0; | |
int csscore = 0; | |
static bool compare_last; | |
NinjaAction(int x, int y) :x(x), y(y), sx(x), sy(y), score(0), lastscore(0){} | |
NinjaAction() :x(0), y(0), sx(0), sy(0), score(0), lastscore(0){} | |
NinjaAction(const NinjaAction &na){ | |
sx = na.sx; sy = na.sy; | |
vx = na.vx; vy = na.vy; | |
x = na.x; y = na.y; | |
history = queue<int>(na.history); | |
historysize = na.historysize; | |
score = na.score; | |
lastscore = na.lastscore; | |
} | |
void historyclear(){ | |
while (!history.empty()) | |
history.pop(); | |
historysize = 0; | |
} | |
void sets(int _x, int _y){ sx = _x; sy = _y; x = _x; y = _y; vx = 0; vy = 0; historyclear(); } | |
// ninjaは左に移動するよう記録される。 | |
NinjaAction& moveLeft(){ | |
x--; vx--; | |
history.push('L'); | |
if (historysize++){ | |
csscore = history.front() != 'L'; | |
} | |
return *this; | |
} | |
NinjaAction& moveRight(){ | |
x++; vx++; | |
history.push('R'); | |
if (historysize++){ | |
csscore = history.front() != 'R'; | |
} | |
return *this; | |
} | |
NinjaAction& moveUp(){ | |
y--; vy--; | |
history.push('U'); | |
if (historysize++){ | |
csscore = history.front() != 'U'; | |
} | |
return *this; | |
} | |
NinjaAction& moveDown(){ | |
y++; vy++; | |
history.push('D'); | |
if (historysize++){ | |
csscore = history.front() != 'D'; | |
} | |
return *this; | |
} | |
int distance(){ | |
return abs(x - sx) + abs(y - sy); | |
} | |
int cmdsize(){ | |
return historysize; | |
} | |
// コマンド数が多いと加点。無意味。 | |
int cmdsizescore(){ | |
return historysize*3+csscore*2; | |
} | |
// 加点 | |
void addScore(int sc){ | |
score += sc; | |
lastscore = sc; | |
} | |
// 減点 | |
void subScore(int sc){ | |
score -= sc; // MEMO: | |
lastscore -= sc; | |
} | |
// コマンド数に影響するスコア計算 | |
int pscore(){ | |
return score * 10 / (cmdsize() + 1); | |
} | |
// 平凡なスコア計算 | |
int lscore(){ | |
return lastscore; | |
} | |
bool operator <(NinjaAction& e){ | |
return compare_last ? pscore() < e.pscore() : lscore() < e.lscore(); | |
} | |
Entity toEntity(int id){ | |
return Entity(id, x, y); | |
} | |
Entity toEntity(){ | |
return toEntity(0); | |
} | |
// 忍者がコマンドにしたがってPlayerStatusのマップを移動し、岩があるなら押す。 | |
int act(PlayerStatus &ps){ | |
int c; | |
int nx = sx, ny = sy; | |
bool push = false; // TODO:未実装・未使用 | |
bool fail = false; // TODO: | |
history.push('$'); | |
while ((c = history.front()) != '$'){ | |
history.pop(); | |
history.push(c); | |
switch (c){ | |
case 'U': | |
if (ps.isNinjaMoveable(nx, ny, 0, -1) <= 0){ | |
fail = true; continue; | |
} | |
ps.pushRock(nx, ny, 0, -1); | |
ny--; | |
break; | |
case 'L': | |
if (ps.isNinjaMoveable(nx, ny, -1, 0) <= 0){ | |
fail = true; continue; | |
} | |
ps.pushRock(nx, ny, -1, 0); | |
nx--; | |
break; | |
case 'R': | |
if (ps.isNinjaMoveable(nx, ny, 1, 0) <= 0){ | |
fail = true; continue; | |
} | |
ps.pushRock(nx, ny, 1, 0); | |
nx++; | |
break; | |
case 'D': | |
if (ps.isNinjaMoveable(nx, ny, 0, 1) <= 0){ | |
fail = true; continue; | |
} | |
ps.pushRock(nx, ny, 0, 1); | |
ny++; | |
break; | |
} | |
} | |
history.pop(); | |
return ((int)fail) | ((int)push << 1); | |
} | |
// 忍者がコマンドにしたがってPlayerStatusのマップを移動し、ソウルがいくつ取得できるか数える。 | |
// 岩に衝突して移動できなかったケースも数える。 | |
// 後から、返り値に岩を押した回数を「無理やり」追加 | |
int test(PlayerStatus &ps, int ninjaID, int &outSoul, int &outFail){ | |
int c; | |
int nx, ny; | |
Field dfield = ps.field; | |
nx = ps.ninja[ninjaID].x; | |
ny = ps.ninja[ninjaID].y; | |
int nrock=0; | |
outSoul = 0; outFail = 0; | |
history.push('$'); | |
while ((c = history.front()) != '$'){ | |
history.pop(); | |
history.push(c); | |
switch (c){ | |
case 'U': | |
if (ps.isNinjaMoveable(nx, ny, 0, -1,dfield) == 0){ | |
outFail++; continue; | |
} | |
if (ps.pushRock(nx, ny, 0, -1, dfield)) nrock++; | |
ny--; | |
break; | |
case 'L': | |
if (ps.isNinjaMoveable(nx, ny, -1, 0, dfield) == 0){ | |
outFail++; continue; | |
} | |
if (ps.pushRock(nx, ny, -1, 0, dfield)) nrock++; | |
nx--; | |
break; | |
case 'R': | |
if (ps.isNinjaMoveable(nx, ny, 1, 0, dfield) == 0){ | |
outFail++; continue; | |
} | |
if (ps.pushRock(nx, ny, 1, 0, dfield)) nrock++; | |
nx++; | |
break; | |
case 'D': | |
if (ps.isNinjaMoveable(nx, ny, 0, 1, dfield) == 0){ | |
outFail++; continue; | |
} | |
if (ps.pushRock(nx, ny, 0, 1, dfield)) nrock++; | |
ny++; | |
break; | |
} | |
for (Entity &e : ps.soul){ | |
if (e.x == nx && e.y == ny) | |
outSoul++; | |
} | |
} | |
history.pop(); | |
return nrock; | |
} | |
// 忍者がコマンドにしたがってPlayerStatusのマップを移動し | |
// 岩を押したとき、最初に押した岩の座標を得る | |
// 押してない->id=0 | |
Entity testRockPos(PlayerStatus &ps, int ninjaID){ | |
int c; | |
int nx, ny; | |
Field dfield = ps.field; | |
nx = ps.ninja[ninjaID].x; | |
ny = ps.ninja[ninjaID].y; | |
Entity rrock(0, 0, 0); | |
history.push('$'); | |
while ((c = history.front()) != '$'){ | |
history.pop(); | |
history.push(c); | |
switch (c){ | |
case 'U': | |
if (ps.isNinjaMoveable(nx, ny, 0, -1, dfield) == 0){ | |
continue; | |
} | |
if (rrock.id == 0 && ps.pushRock(nx, ny, 0, -1, dfield)){ | |
rrock.id = 1; | |
rrock.x = nx; | |
rrock.y = ny - 1; | |
} | |
ny--; | |
break; | |
case 'L': | |
if (ps.isNinjaMoveable(nx, ny, -1, 0, dfield) == 0){ | |
continue; | |
} | |
if (rrock.id == 0 && ps.pushRock(nx, ny, -1, 0, dfield)){ | |
rrock.id = 1; | |
rrock.x = nx - 1; | |
rrock.y = ny; | |
} | |
nx--; | |
break; | |
case 'R': | |
if (ps.isNinjaMoveable(nx, ny, 1, 0, dfield) == 0){ | |
continue; | |
} | |
if (rrock.id == 0 && ps.pushRock(nx, ny, 1, 0, dfield)){ | |
rrock.id = 1; | |
rrock.x = nx + 1; | |
rrock.y = ny; | |
} | |
nx++; | |
break; | |
case 'D': | |
if (ps.isNinjaMoveable(nx, ny, 0, 1, dfield) == 0){ | |
continue; | |
} | |
if (rrock.id == 0 && ps.pushRock(nx, ny, 0, 1, dfield)){ | |
rrock.id = 1; | |
rrock.x = nx; | |
rrock.y = ny + 1; | |
} | |
ny++; | |
break; | |
} | |
} | |
history.pop(); | |
return rrock; | |
} | |
}; | |
bool NinjaAction::compare_last=true; // MEMO: | |
ostream& operator <<(ostream& os, NinjaAction &na){ | |
int c; | |
na.history.push('$'); | |
while ((c = na.history.front()) != '$'){ | |
na.history.pop(); | |
if (0<c) | |
os << (char)c; | |
na.history.push(c); | |
} | |
na.history.pop(); | |
return os; | |
} | |
//------------------------------------------------------------------- | |
//------------------------------------------------------------------- | |
Game game; | |
Field rectScore; // 端の方は減点するためのField | |
Field ninjaShadow; // 残像だ | |
int gameLoopCnt = 0; | |
//History spellHistory; | |
// コンソールからデータを入力する。 | |
// | |
int consoleLoad(){ | |
char c; | |
int i,j,n,w,h; | |
cin>>game.time; | |
//if (!(cin >> game.time)) | |
// return 1; | |
cin>>n; | |
game.nspell=n; | |
for (i=0;i<n;i++){ | |
cin>>j; | |
game.spellcost.push_back(j); | |
} | |
// Me | |
cin>>game.me.spell; | |
cin>>h>>w; | |
game.me.setFieldSize(w,h); | |
for (i=0;i<w*h;i++){ | |
cin >> c; | |
if (c!='W'&&c!='_'&&c!='O'){ | |
i--;continue; | |
} | |
game.me.field.push((int)c); | |
} | |
cin>>n;game.me.nninja=n; | |
for (i=0;i<n;i++){ | |
cin>>j>>h>>w; | |
game.me.ninja.push_back(Entity(j,w,h)); | |
} | |
cin>>n;game.me.ndog=n; | |
for (i=0;i<n;i++){ | |
cin>>j>>h>>w; | |
game.me.dog.push_back(Entity(j,w,h)); | |
} | |
cin>>n;game.me.nsoul=n; | |
for (i=0;i<n;i++){ | |
cin>>h>>w; | |
game.me.soul.push_back(Entity(w,h)); | |
} | |
n=game.nspell; | |
for (i=0;i<n;i++){ | |
cin>>j; | |
game.me.usedSpell.push_back(j); | |
} | |
// Enemy | |
cin>>game.enemy.spell; | |
cin>>h>>w; | |
game.enemy.setFieldSize(w,h); | |
for (i=0;i<w*h;i++){ | |
cin >> c; | |
if (c!='W'&&c!='_'&&c!='O'){ | |
i--;continue; | |
} | |
game.enemy.field.push((int)c); | |
} | |
cin>>n;game.enemy.nninja=n; | |
for (i=0;i<n;i++){ | |
cin>>j>>h>>w; | |
game.enemy.ninja.push_back(Entity(j,w,h)); | |
} | |
cin>>n;game.enemy.ndog=n; | |
for (i=0;i<n;i++){ | |
cin>>j>>h>>w; | |
game.enemy.dog.push_back(Entity(j,w,h)); | |
} | |
cin>>n;game.enemy.nsoul=n; | |
for (i=0;i<n;i++){ | |
cin>>h>>w; | |
game.enemy.soul.push_back(Entity(w,h)); | |
} | |
n=game.nspell; | |
for (i=0;i<n;i++){ | |
cin>>j; | |
game.enemy.usedSpell.push_back(j); | |
} | |
// calculate | |
game.me.calcAbleSoul(); | |
game.enemy.calcAbleSoul(); | |
// ninjaShadow rectScoreの初期化が済んでいないならここでやる | |
if (ninjaShadow.width == 0){ | |
ninjaShadow = Field(game.me.field.width, game.me.field.height); | |
} | |
if (rectScore.width == 0){ | |
int &w = game.me.field.width, &h = game.me.field.height; | |
int n = ((w < h ? w : h) - 2) / 2; | |
rectScore = Field(w,h); | |
for (i = n; 1 <= i; i--) | |
for (j = i; 1 <= j; j--){ | |
rectScore[j][i - j + 1] = (n - i + 1) * 3/2; // 6*4 | |
rectScore[w - j - 1][i - j + 1] = (n - i + 1) * 3/2; | |
rectScore[j][h - i + j - 2] = (n - i + 1) * 3/2; | |
rectScore[w - j - 1][h - i + j - 2] = (n - i + 1) * 3/2; | |
} | |
} | |
return 0; | |
} | |
// ゲームマップを基に、Entitiesからの距離を記したFieldを生成する。 | |
Field createDistanceField(vector<Entity> &ve, Field &field){ | |
Field distField(field.width, field.height); | |
queue<Entity> eq; | |
for (Entity &e : ve) | |
eq.push(Entity(0, e.x, e.y)); | |
distField.fill(999); | |
for (; !eq.empty(); eq.pop()){ | |
Entity &e = eq.front(); | |
int &dist = distField[e.x][e.y]; | |
if (field[e.x][e.y] != '_') continue; | |
if (dist <= e.id) continue; | |
dist = e.id; | |
e.id++; | |
eq.push(Entity(e.id, e.x - 1, e.y)); | |
eq.push(Entity(e.id, e.x + 1, e.y)); | |
eq.push(Entity(e.id, e.x, e.y - 1)); | |
eq.push(Entity(e.id, e.x, e.y + 1)); | |
} | |
return distField; | |
} | |
//_____________________________________ | |
//calc... | |
// | |
// targetをninjaと見て、PlayerStatusの犬の位置をルールに従い更新する。 | |
void predictDog(PlayerStatus &ps, vector<Entity> &target){ | |
Field distField = createDistanceField(target, ps.field); | |
vector<Entity> dogs; | |
sort(ps.dog.begin(), ps.dog.end()); // ID昇順 | |
for (Entity &e : ps.dog){ | |
dogs.push_back(Entity( | |
distField[e.x][e.y],e.x,e.y,&e)); | |
} | |
stable_sort(dogs.begin(), dogs.end()); // 距離順 | |
for (Entity &e : dogs){ | |
if (e.id - 1 == distField[e.x][e.y-1]){ | |
bool flg = false; | |
for (Entity &f: ps.dog) | |
if (f.x == e.x && f.y == e.y - 1){ flg = true; break; } | |
if (!flg){ | |
((Entity*)(e.link))->y -= 1; // proceed; | |
continue; | |
} | |
} | |
if (e.id - 1 == distField[e.x - 1][e.y]){ | |
bool flg = false; | |
for (Entity &f : ps.dog) | |
if (f.x == e.x - 1 && f.y == e.y){ flg = true; break; } | |
if (!flg){ | |
((Entity*)(e.link))->x -= 1; // proceed; | |
continue; | |
} | |
} | |
if (e.id - 1 == distField[e.x + 1][e.y]){ | |
bool flg = false; | |
for (Entity &f : ps.dog) | |
if (f.x == e.x + 1 && f.y == e.y){ flg = true; break; } | |
if (!flg){ | |
((Entity*)(e.link))->x += 1; // proceed; | |
continue; | |
} | |
} | |
if (e.id - 1 == distField[e.x][e.y + 1]){ | |
bool flg = false; | |
for (Entity &f : ps.dog) | |
if (f.x == e.x && f.y == e.y + 1){ flg = true; break; } | |
if (!flg){ | |
((Entity*)(e.link))->y += 1; // proceed; | |
continue; | |
} | |
} | |
} | |
return ; | |
} | |
void predictDog(PlayerStatus &ps){ | |
predictDog(ps, ps.ninja); | |
} | |
#ifdef __GNUC__ // gcc対策 | |
void predictDog(PlayerStatus &ps, Entity e){ | |
vector<Entity> ve(1, e); | |
predictDog(ps, ve); | |
} | |
#else | |
void predictDog(PlayerStatus &ps, Entity &e){ | |
predictDog(ps, vector<Entity>(1, e)); | |
} | |
#endif | |
// スコアマップを計算する。 | |
void PlayerStatus::calcScore(){ | |
int x,y; | |
Field fsct(field.width,field.height); | |
Field dogMap(field.width, field.height); | |
queue<Entity> stl; | |
scorefield.fill(0); | |
setDogMap(dogMap); | |
// 忍者が元々居た場所は減点する。 | |
// 同じ場所にとどまらない工夫 | |
for (Entity e : ninja){ | |
ninjaShadow[e.x][e.y] += 7 + rand(0, -2, 7); | |
ninjaShadow[e.x-1][e.y] += 2; | |
ninjaShadow[e.x][e.y-1] += 2; | |
ninjaShadow[e.x+1][e.y] += 2; | |
ninjaShadow[e.x][e.y+1] += 2; | |
} | |
for (y = 1; y < field.height - 1; y++) | |
for (x = 1; x < field.width - 1; x++) | |
ninjaShadow[x][y] -= (0<ninjaShadow[x][y])*2; | |
scorefield -= ninjaShadow; | |
scorefield -= rectScore; // すみっこ減点 | |
// わんこ | |
// 地形に従い、わんこに近いポイントは減点する。 | |
fsct.fill(0); | |
for (Entity e:dog){ | |
stl.push(Entity(120 + rand(0, -20, 20), e.x, e.y)); | |
} | |
for (; !stl.empty(); stl.pop()){ | |
Entity& e=stl.front(); | |
if (fsct[e.x][e.y] >= e.id) continue; | |
if (field[e.x][e.y] != '_') continue; | |
fsct[e.x][e.y] = e.id; e.id -= 35; | |
if (e.id<=0) continue; | |
stl.push(Entity(e.id, e.x - 1, e.y)); | |
stl.push(Entity(e.id, e.x, e.y - 1)); | |
stl.push(Entity(e.id, e.x + 1, e.y)); | |
stl.push(Entity(e.id, e.x, e.y + 1)); | |
} | |
scorefield-=fsct; | |
// ぴょんやん | |
// 地形に従い、ソウルに近いポイントは加点する。 | |
// 岩を押すケースを考え、岩を挟んだ場所でも加点する。 | |
fsct.fill(0); | |
for (Entity e : ableSoul){ | |
stl.push(Entity(140 + rand(0, -20, 80), e.x, e.y)); | |
} | |
for (; !stl.empty();stl.pop()){ | |
Entity& e=stl.front(); | |
if (fsct[e.x][e.y] >= e.id) continue; | |
if (field[e.x][e.y] == 'W') continue; | |
if (field[e.x][e.y] == 'O'){ | |
if (field.roundsum(e.x, e.y, '_') <= 4) continue; // 通行不能ペナルティ | |
e.id -= 4; // 岩ペナルティ | |
} | |
fsct[e.x][e.y]=e.id;e.id-=3; | |
if (e.id<=0) continue; | |
stl.push(Entity(e.id, e.x - 1, e.y)); | |
stl.push(Entity(e.id, e.x, e.y - 1)); | |
stl.push(Entity(e.id, e.x + 1, e.y)); | |
stl.push(Entity(e.id, e.x, e.y + 1)); | |
} | |
scorefield+=fsct; | |
// マップペナルティ | |
// 地形的に自由自在に動ける場所は加点。 | |
fsct.fill(0); | |
int c; | |
for (y = 1; y < field.height-1; y++) | |
for (x = 1; x < field.width-1; x++){ | |
c = 0; | |
c += isNinjaMoveable(x, y, -1, 0) == -1 ? 1 : 0; | |
c += isNinjaMoveable(x, y, 1, 0) == -1 ? 1 : 0; | |
c += isNinjaMoveable(x, y, 0, -1) == -1 ? 1 : 0; | |
c += isNinjaMoveable(x, y, 0, 1) == -1 ? 1 : 0; | |
fsct[x][y] = 2*c*c; | |
} | |
scorefield -= fsct; | |
scorefield.eprintint(); | |
} | |
// 敵に妨害を検討する。 | |
// 術式の情報が格納されたEntityが返ってくる。 | |
Entity calcTry2Attack(){ | |
int x, y; | |
Entity result; | |
Field checker(7, 7); | |
queue<NinjaAction> qe; | |
vector<NinjaAction>ae; | |
vector<NinjaAction>be; | |
vector<NinjaAction>fe; | |
// 情報収集 | |
Field dogMap(game.enemy.field.width, game.enemy.field.height); | |
//Field soulMap(game.enemy.field.width, game.enemy.field.height); | |
game.enemy.setDogMap(dogMap); | |
//game.enemy.setSoulMap(soulMap); | |
Field wallMap(game.enemy.field.width, game.enemy.field.height); | |
wallMap.fill(0); | |
wallMap += dogMap; | |
for (y = 0; y < game.enemy.field.height; y++) | |
for (x = 0; x < game.enemy.field.width; x++) | |
if (game.enemy.field[x][y] != '_') | |
wallMap[x][y] |= 1; | |
// 優先度の低い術式はここに格納される | |
Entity perhaps(-1,0,0); | |
// 2人のninjaに対して | |
for (int idx = 0; idx < game.enemy.nninja; idx++){ | |
Entity &ninja = game.enemy.ninja[idx]; | |
checker.fill(0); | |
ae.clear(); | |
be.clear(); | |
fe.clear(); | |
qe.push(NinjaAction(ninja.x, ninja.y)); | |
// 行動可能なコマンドを全て列挙する -> qe:vector | |
for (; !qe.empty(); qe.pop()){ | |
NinjaAction &na = qe.front(); | |
int soul, fail; | |
na.score = na.test(game.enemy, ninja.id, soul, fail); | |
if (0 < fail) continue; | |
if (checker[3 + na.vx][3 + na.vy] != 0) continue; | |
checker[3 + na.vx][3 + na.vy] = 1; | |
ae.push_back(NinjaAction(na)); | |
if (2 <= na.cmdsize()){ continue; } | |
qe.push(NinjaAction(na).moveDown()); | |
qe.push(NinjaAction(na).moveRight()); | |
qe.push(NinjaAction(na).moveLeft()); | |
qe.push(NinjaAction(na).moveUp()); | |
} | |
// 列挙した行動可能なコマンドから、その行動が犬に捕まえられるか否か調べる | |
// 犬に捕まる->fe:vector 犬に捕まらない->be.vector | |
for (NinjaAction &na : ae){ | |
PlayerStatus pspd = game.enemy; | |
//for (j = 0; j < idx; j++) // もう一人の動きは考えない | |
// result[j].act(pspd); | |
na.act(pspd); | |
predictDog(pspd, na.toEntity()); | |
if (pspd.isDogHere(na.x, na.y)){ | |
fe.push_back(na); | |
continue; | |
} | |
be.push_back(na); | |
} | |
// 犬に捕まらない移動パターンが存在する | |
// 敵の動きを予測した敵分身と落石 | |
if (!be.empty()){ | |
int cnt = 0; | |
NinjaAction &only=be[0]; | |
for (NinjaAction &na : be){ | |
if (na.cmdsize() == 0) continue; // その場にとどまる、はナシ | |
if (cnt == 0){ only = na;} | |
cnt++; | |
//if (game.enemy.isSoulHere(na.x, na.y)){ | |
// if (0 < dogMap.roundsum4(na.x, na.y)){ | |
// // 発動 | |
// //return Entity(6, na.x, na.y); | |
// } | |
//} | |
} | |
// 移動するコマンドが唯一であるならば | |
// ・落石したとき、行動を妨げることができる->落石 | |
// ・そのコマンドの移動先の周囲に犬がいる->敵分身 | |
if (cnt == 1 && game.enemy.spell >= game.spellcost[5]){ | |
if (game.enemy.field[only.x][only.y] == '_'){ | |
if (game.me.spell >= game.spellcost[2]){ | |
int soul, fail; | |
game.enemy.fieldOverride('O', only.x, only.y); | |
only.test(game.enemy, ninja.id, soul, fail); | |
game.enemy.fieldUndo(); | |
if (fail) | |
return Entity(2, only.x, only.y); // 落石 | |
} | |
if (0 < dogMap.roundsum4(only.x, only.y) && | |
game.me.spell >= game.spellcost[6] && | |
game.spellcost[2]<game.spellcost[6]) | |
return Entity(6, only.x, only.y); // 敵分身 | |
} | |
} | |
}else{ // be.empty | |
// 敵は術式なしでは移動する手段がない | |
// fe:vectorから適当に選び、敵分身を行う | |
if ((game.enemy.spell >= game.spellcost[5] || game.enemy.spell >= game.spellcost[0]) | |
&& game.me.spell >= game.spellcost[6] && !fe.empty()){ | |
sort(fe.begin(), fe.end()); | |
stable_sort(fe.begin(), fe.end(), | |
[](NinjaAction na1, NinjaAction na2)-> | |
int{return na1.cmdsize() > na2.cmdsize(); }); | |
for (NinjaAction &na : fe){ | |
if (na.cmdsize() != 0) | |
return Entity(6, fe[0].x, fe[0].y); // 敵分身 | |
} | |
} | |
} | |
// 行動を制限する落石 | |
// ninjaから1歩進むとき、移動できる方向の数が2以下 | |
// かつninjaの周囲に1匹以上犬がいるならば。 | |
// 敵の行動を封じることができるような落石を全て列挙する | |
// 列挙した中から適当に選択する。 | |
if (game.me.spell >= game.spellcost[2]){ | |
vector<Entity> ve; | |
int crs4 = checker.roundsum4(3, 3); // TODO:発動条件の確認と修正 | |
if (crs4 <= 1 || | |
(crs4 <= (2 + (game.me.spell >= (25 - (game.spellcost[2] <= 4)*4))) && | |
0 < dogMap.roundsum4(ninja.x, ninja.y))){ | |
// XD___ 左図の場合、Nの右に石を置く。 | |
// XN_O_ | |
// XXXXX | |
if (game.enemy.field[ninja.x][ninja.y - 1] == '_' && | |
game.enemy.field[ninja.x][ninja.y - 2] != '_' && | |
!game.enemy.isDogHere(ninja.x, ninja.y - 1)) | |
ve.push_back(Entity( | |
- game.enemy.scorefield[ninja.x][ninja.y-1] | |
,//- game.enemy.scorefield[ninja.x][ninja.y-2], | |
ninja.x, ninja.y - 1)); | |
if (game.enemy.field[ninja.x][ninja.y + 1] == '_' && | |
game.enemy.field[ninja.x][ninja.y + 2] != '_' && | |
!game.enemy.isDogHere(ninja.x, ninja.y + 1)) | |
ve.push_back(Entity( | |
- game.enemy.scorefield[ninja.x][ninja.y + 1] | |
,//- game.enemy.scorefield[ninja.x][ninja.y + 2], | |
ninja.x, ninja.y + 1)); | |
if (game.enemy.field[ninja.x - 1][ninja.y] == '_' && | |
game.enemy.field[ninja.x - 2][ninja.y] != '_' && | |
!game.enemy.isDogHere(ninja.x - 1, ninja.y)) | |
ve.push_back(Entity( | |
- game.enemy.scorefield[ninja.x - 1][ninja.y] | |
,//- game.enemy.scorefield[ninja.x - 2][ninja.y], | |
ninja.x - 1, ninja.y)); | |
if (game.enemy.field[ninja.x + 1][ninja.y] == '_' && | |
game.enemy.field[ninja.x + 2][ninja.y] != '_' && | |
!game.enemy.isDogHere(ninja.x + 1, ninja.y)) | |
ve.push_back(Entity( | |
- game.enemy.scorefield[ninja.x + 1][ninja.y] | |
,//- game.enemy.scorefield[ninja.x + 2][ninja.y], | |
ninja.x + 1, ninja.y)); | |
// XD___ 左図の場合、Oの右に石を置く。 | |
// XNO__ | |
// XXXXX | |
if (game.enemy.isNinjaMoveable(ninja.x, ninja.y, 0, 1) == 2 && | |
!game.enemy.isDogHere(ninja.x, ninja.y + 2)) | |
ve.push_back(Entity( | |
//- game.enemy.scorefield[ninja.x][ninja.y + 1] | |
- game.enemy.scorefield[ninja.x][ninja.y + 2], | |
ninja.x, ninja.y + 2)); | |
if (game.enemy.isNinjaMoveable(ninja.x, ninja.y, 1, 0) == 2 && | |
!game.enemy.isDogHere(ninja.x + 2, ninja.y)) | |
ve.push_back(Entity( | |
//- game.enemy.scorefield[ninja.x + 1][ninja.y] | |
- game.enemy.scorefield[ninja.x + 2][ninja.y], | |
ninja.x + 2, ninja.y)); | |
if (game.enemy.isNinjaMoveable(ninja.x, ninja.y, 0, -1) == 2 && | |
!game.enemy.isDogHere(ninja.x, ninja.y - 2)) | |
ve.push_back(Entity( | |
//- game.enemy.scorefield[ninja.x][ninja.y-1] | |
- game.enemy.scorefield[ninja.x][ninja.y-2], | |
ninja.x, ninja.y - 2)); | |
if (game.enemy.isNinjaMoveable(ninja.x, ninja.y, -1, 0) == 2 && | |
!game.enemy.isDogHere(ninja.x - 2, ninja.y)) | |
ve.push_back(Entity( | |
//- game.enemy.scorefield[ninja.x - 1][ninja.y] | |
- game.enemy.scorefield[ninja.x - 2][ninja.y], | |
ninja.x - 2, ninja.y)); | |
// 列挙した中から適当に選択する。 | |
if (!ve.empty()){ | |
sort(ve.begin(), ve.end()); | |
ve[0].id = 2; | |
if (dogMap.roundsum4(ninja.x, ninja.y) && crs4 <= 2) | |
return ve[0]; | |
else | |
perhaps = ve[0]; | |
} | |
} | |
} | |
//// 敵落石 | |
//if (game.me.spell >= game.spellcost[2]){ | |
// | |
// if (wallMap.roundsum4(ninja.x, ninja.y) >= 3){ | |
// } | |
// | |
//} | |
} | |
if (0 <= perhaps.id) | |
return perhaps; | |
// 敵の岩を砕いてみる。 | |
// これで戦況が有利になったかどうか不明。 | |
// 消費が少ない AND ソウルに余裕があるならば。 | |
// 犬の居る座標とninjaからもっとも遠い座標について | |
// 隣接している岩を崩すことで距離をそこそこ縮めることができるならば、その岩を破壊する。 | |
if (game.spellcost[4] <= 2 && game.me.spell >= 6 + game.spellcost[4]*3){ | |
vector<Entity> dog = game.enemy.dog; // copy | |
Field dist = createDistanceField(game.enemy.ninja, game.enemy.field); | |
vector<Entity> ve; | |
int x, y; | |
// TODO:岩隣接のみ列挙 | |
Entity far(0, 0, 0); | |
for (x = 1; x < game.enemy.field.width - 1; x++) | |
for (y = 1; y < game.enemy.field.width - 1; y++) | |
if (dist[x][y] != 999 && far.id < dist[x][y]){ | |
far.id = dist[x][y]; | |
far.x = x; far.y = y; | |
} | |
dog.push_back(far); | |
for (Entity &e : dog){ | |
//e.id = dist[e.x][e.y]; | |
if (dist[e.x][e.y]< 25) continue; | |
int u = dist[e.x][e.y]; | |
if (game.enemy.field[e.x - 1][e.y] == 'O' && ( | |
dist[e.x - 2][e.y] < u - 6 || | |
dist[e.x - 1][e.y - 1] < u - 6 || | |
dist[e.x - 1][e.y + 1] < u - 6)){ | |
ve.push_back(Entity(dist[e.x - 2][e.y] - u, e.x - 1, e.y)); | |
ve.push_back(Entity(dist[e.x - 1][e.y - 1] - u, e.x - 1, e.y)); | |
ve.push_back(Entity(dist[e.x - 1][e.y + 1] - u, e.x - 1, e.y)); | |
} | |
if (game.enemy.field[e.x + 1][e.y] == 'O' && ( | |
dist[e.x + 2][e.y] < u - 6 || | |
dist[e.x + 1][e.y - 1] < u - 6 || | |
dist[e.x + 1][e.y + 1] < u - 6)){ | |
ve.push_back(Entity(dist[e.x + 2][e.y] - u, e.x + 1, e.y)); | |
ve.push_back(Entity(dist[e.x + 1][e.y - 1] - u, e.x + 1, e.y)); | |
ve.push_back(Entity(dist[e.x + 1][e.y + 1] - u, e.x + 1, e.y)); | |
} | |
if (game.enemy.field[e.x][e.y - 1] == 'O' && ( | |
dist[e.x][e.y - 2] < u - 6 || | |
dist[e.x - 1][e.y - 1] < u - 6 || | |
dist[e.x + 1][e.y - 1] < u - 6)){ | |
ve.push_back(Entity(dist[e.x][e.y - 2] - u, e.x, e.y - 1)); | |
ve.push_back(Entity(dist[e.x - 1][e.y - 1] - u, e.x, e.y - 1)); | |
ve.push_back(Entity(dist[e.x + 1][e.y - 1] - u, e.x, e.y - 1)); | |
} | |
if (game.enemy.field[e.x][e.y + 1] == 'O' && ( | |
dist[e.x][e.y + 2] < u - 6 || | |
dist[e.x - 1][e.y + 1] < u - 6 || | |
dist[e.x + 1][e.y + 1] < u - 6)){ | |
ve.push_back(Entity(dist[e.x][e.y + 2] - u, e.x, e.y + 1)); | |
ve.push_back(Entity(dist[e.x - 1][e.y + 1] - u, e.x, e.y + 1)); | |
ve.push_back(Entity(dist[e.x + 1][e.y + 1] - u, e.x, e.y + 1)); | |
} | |
} | |
if (!ve.empty()){ | |
sort(ve.begin(), ve.end()); | |
return Entity(4, ve[0].x, ve[0].y); | |
} | |
} | |
return Entity(-1, 0, 0); | |
} | |
// | |
// プレイヤーの忍者の行動。 | |
void calcNinjaMove(){ | |
int x, y, ninidx,i, j; | |
int movenum = 2; // 2 or 3 | |
vector<NinjaAction> ae; | |
vector<Entity> be; | |
vector<Entity> xe; | |
queue<NinjaAction> qe; | |
Field checker(7, 7); | |
Entity drill; | |
bool spellchk[8]; | |
Entity spell(-1, 0, 0); | |
vector<NinjaAction> result; | |
for (i = 0; i < 8; i++) | |
spellchk[i] = game.me.spell < game.spellcost[i]; // t=使えない | |
Field dogMap(game.me.field.width, game.me.field.height); | |
game.me.setDogMap(dogMap); | |
// TODO:need sort ninja? | |
// コストが少ないなら、高速を発動する。 | |
if (!spellchk[0] && game.spellcost[0] <= 2 && gameLoopCnt * 5 / 3 < game.me.spell){ | |
spell.id = 0; | |
} | |
for (ninidx = 0; ninidx < game.me.ninja.size(); ninidx++){ | |
Entity &ninja = game.me.ninja[ninidx]; | |
movenum = (spell.id != 0) ? 2 : 3; | |
ae.clear(); be.clear(); xe.clear(); | |
checker.fill(0); | |
oserr << "ninja? > "; | |
oserr << ninja.tostring() << endl; | |
qe.push(NinjaAction(ninja.x, ninja.y)); | |
// ゲームの状態をコピー。 | |
// コピーに他方のninjaの行動や術式を反映する。 | |
PlayerStatus pspr = game.me; // pre | |
if (spell.id == 7) | |
pspr.attack(spell.x); | |
for (j = 0; j < ninidx; j++){ // 前のninja実行 | |
result[j].act(pspr); | |
pspr.ninja[j].x = result[j].x; | |
pspr.ninja[j].y = result[j].y; | |
} | |
// 行動可能なコマンドを全て列挙する->qe:vector | |
for (; !qe.empty(); qe.pop()){ | |
NinjaAction &na = qe.front(); | |
int soul, fail; | |
na.test(pspr, ninja.id, soul, fail); | |
if (0<fail) continue; | |
//if (checker[3 + na.vx][3 + na.vy] != 0) continue; | |
na.addScore(-game.me.scorefield[na.x][na.y]); | |
checker[3 + na.vx][3 + na.vy] = 1; | |
ae.push_back(NinjaAction(na)); | |
if (movenum <= na.cmdsize()){ continue; } | |
qe.push(NinjaAction(na).moveDown()); | |
qe.push(NinjaAction(na).moveRight()); | |
qe.push(NinjaAction(na).moveLeft()); | |
qe.push(NinjaAction(na).moveUp()); | |
} | |
// 適当なスコア計算をする。 | |
for (NinjaAction &na : ae){ | |
na.subScore(3 * na.cmdsizescore()); // コマンド数が多いと加点(?) | |
if (!result.empty())// && doublmns) | |
if (result[0].x == na.x && result[0].y == na.y) | |
na.subScore(-10); // 他の忍者に重なると減点 | |
} | |
sort(ae.begin(), ae.end()); | |
oserr << "ae:" << endl; | |
for (NinjaAction &na : ae) | |
oserr << (na) << " p" << (na.pscore()) << " l" << (na.lscore()) | |
<< " " << (na.vx) << "," << (na.vy) << endl; | |
// 列挙された行動可能なコマンドの中で、犬に捕まらないコマンドのみ取り出す。 | |
for (NinjaAction &na : ae){ | |
int soul, fail; | |
PlayerStatus pspd = pspr; // hint! pspr | |
na.act(pspd); | |
if (spell.id != 5){ | |
pspd.ninja[ninja.id] = na.toEntity(ninja.id); | |
predictDog(pspd); | |
} | |
else | |
predictDog(pspd, spell); // 犬はspellと間違える | |
if (pspd.isDogHere(na.x, na.y)) continue; | |
na.test(game.me, ninja.id, soul, fail); | |
xe.push_back(Entity(-soul, 0, 0, &na)); | |
} | |
// コマンドを実行することで得られるソウルの取得数でソートする。 | |
if (!xe.empty()) | |
stable_sort(xe.begin(), xe.end()); | |
oserr << "xe:" << endl; | |
for (Entity &e : xe){ | |
NinjaAction *na = (NinjaAction*)(e.link); | |
oserr << e.id << " : " << (*na) << " p" << (na->pscore()) << " l" << (na->lscore()) | |
<< " " << (na->vx) << "," << (na->vy) << endl; | |
} | |
// 岩を極力避けて移動するようなコマンドを選択するようにする | |
for (Entity &e : xe){ | |
NinjaAction *nap = (NinjaAction*)(e.link); | |
int sc = 0; | |
if (0 < nap->cmdsize()){ | |
int vx = 0, vy = 0; | |
switch (nap->history.front()){ | |
case 'L': | |
vx = -1; vy = 0; break; | |
case 'R': | |
vx = 1; vy = 0; break; | |
case 'U': | |
vx = 0; vy = -1; break; | |
case 'D': | |
vx = 0; vy = 1; break; | |
} | |
if (game.me.field[nap->sx + vx * 2][nap->sy + vy * 2] == 'O' && | |
dogMap.roundsum4(nap->sx + vx,nap->sy + vy)){ | |
sc++; | |
} | |
} | |
else | |
sc++; | |
be.push_back(Entity(sc, 0, 0, nap)); | |
} | |
if (!be.empty()) | |
stable_sort(be.begin(), be.end()); | |
oserr << "be:" << endl; | |
for (Entity &e : be){ | |
NinjaAction *na = (NinjaAction*)(e.link); | |
oserr << e.id << " : " << (*na) << " p" << (na->pscore()) << " l" << (na->lscore()) | |
<< " " << (na->vx) << "," << (na->vy) << endl; | |
} | |
oserr << "calc ok! besize:" << be.size() << endl; | |
// 動かないという選択しか無い場合、岩砕きを試す。 | |
if (be.size() == 1 && ((NinjaAction*)(be[0].link))->cmdsize() == 0){ | |
//goto cnm_spellready; | |
if (((game.me.field[ninja.x - 1][ninja.y] != '_') | |
+ (game.me.field[ninja.x + 1][ninja.y] != '_') | |
+ (game.me.field[ninja.x][ninja.y - 1] != '_') | |
+ (game.me.field[ninja.x][ninja.y + 1] != '_') >= 3) && !spellchk[3]){ | |
goto cnm_spellready_drilme; | |
} | |
} | |
// 移動できるコマンドパターンが少ない、ninjaの周囲に犬がいるならば | |
// ninjaのコマンドをさらに詳しく調べ、 | |
// 敵落石によって行動が阻まれる可能性があるならば、岩を砕く。 | |
if (!be.empty() && be.size() <= 4 && spell.id == -1 && !spellchk[3] | |
&& dogMap.roundsum4(ninja.x,ninja.y)){ | |
int n = 0; | |
bool first = true; | |
Entity rock; | |
for (Entity &e : be){ | |
NinjaAction *nap = ((NinjaAction*)(e.link)); | |
Entity r = nap->testRockPos(game.me, ninja.id); | |
if (r.id == 1){ | |
if (first) rock = r; | |
n++; | |
}else if (first){ | |
// 選んだものが岩を押さないなら崩す意味は無い | |
n = 0; break; | |
} | |
first = false; | |
} | |
// 押す岩を崩す | |
if (n && be.size() - n <= 1 | |
&& game.enemy.spell >= game.spellcost[2]){ | |
spellchk[3] = true; | |
spell.id = 3; // 岩砕き | |
spell.x = rock.x; | |
spell.y = rock.y; | |
} | |
else{ | |
int d = 0; | |
for (Entity &e : be){ | |
NinjaAction *nap = ((NinjaAction*)(e.link)); | |
if (nap->cmdsize() == 0) continue; | |
if (d == 0){ d = nap->history.front(); continue; } | |
if (d != nap->history.front()){ d = 0; break; } | |
} | |
// XX___ | |
// DN_OO 左図のような場合 | |
// XXXXX | |
if (d){ | |
// 岩押さない(かも)だけど岩落とされるかも | |
//Entity &e = be[0]; | |
NinjaAction *nap = ((NinjaAction*)(be[0].link)); | |
if (0 < nap->cmdsize()){ | |
int vx = 0, vy = 0; | |
switch (d){ | |
case 'L': | |
vx = -1; vy = 0; break; | |
case 'R': | |
vx = 1; vy = 0; break; | |
case 'U': | |
vx = 0; vy = -1; break; | |
case 'D': | |
vx = 0; vy = 1; break; | |
} | |
if (game.me.field[nap->sx + vx * 2][nap->sy + vy * 2] == 'O' | |
&& game.enemy.spell >= game.spellcost[2]){ | |
spellchk[3] = true; | |
spell.id = 3; // 岩砕き | |
spell.x = nap->sx + vx * 2; | |
spell.y = nap->sy + vy * 2; | |
} | |
} | |
} | |
} | |
} | |
// 術式なしで行動できるコマンドが無いならば、術式をためす。 | |
if (be.empty()){ | |
cnm_spellready: | |
if (dogMap.roundsum(ninja.x, ninja.y) >= | |
(4 - (game.spellcost[7] <= 11 || game.me.field.roundsum(ninja.x, ninja.y, '_') <= 3)) // - (game.spellcost[7] <= 7) | |
&& !spellchk[7] && game.spellcost[7]*3/2 >= game.me.spell){ | |
// 周囲に犬が一定数以上居るならば回転斬り | |
spellchk[7] = true;// 回転斬り | |
spell.id = 7; | |
spell.x = ninja.id; | |
oserr << "spellninja " << spell.x << endl; | |
oserr << "spell_round[7] called...redo!" << endl; | |
game.me.fieldUndo(); | |
result.clear(); ninidx = -1; continue; // ninjaループをすべてやりなおす。 | |
}else if (((game.me.field[ninja.x - 1][ninja.y] != '_') | |
+ (game.me.field[ninja.x + 1][ninja.y] != '_') | |
+ (game.me.field[ninja.x][ninja.y - 1] != '_') | |
+ (game.me.field[ninja.x][ninja.y + 1] != '_') >= 3) && !spellchk[3]){ | |
// 岩に囲まれているならば雷撃 | |
cnm_spellready_drilme: | |
game.me.fieldUndo(); | |
spellchk[3] = true; // 雷撃 | |
vector<Entity> round; | |
if (game.me.field[ninja.x - 1][ninja.y] == 'O') | |
round.push_back(Entity(-game.me.scorefield[ninja.x - 1][ninja.y], ninja.x - 1, ninja.y)); | |
if (game.me.field[ninja.x + 1][ninja.y] == 'O') | |
round.push_back(Entity(-game.me.scorefield[ninja.x + 1][ninja.y], ninja.x + 1, ninja.y)); | |
if (game.me.field[ninja.x][ninja.y - 1] == 'O') | |
round.push_back(Entity(-game.me.scorefield[ninja.x][ninja.y - 1], ninja.x, ninja.y - 1)); | |
if (game.me.field[ninja.x][ninja.y + 1] == 'O') | |
round.push_back(Entity(-game.me.scorefield[ninja.x][ninja.y + 1], ninja.x, ninja.y + 1)); | |
sort(round.begin(), round.end()); | |
spell = round[0]; | |
spell.id = 3; | |
game.me.fieldOverride('_', spell.x, spell.y); | |
oserr << "spellpos->(" << spell.x << "," << spell.y << ")" << endl; | |
oserr << "spell_drill_me[3] called...redo!" << endl; | |
result.clear(); ninidx = -1; continue; // ninjaループをすべてやりなおす。 | |
}else if ((spellchk[0] || game.spellcost[0] > game.spellcost[5]) | |
&& !spellchk[5]){ | |
// 適当に分身を出してみる | |
spellchk[5] = true; // 分身 | |
spell.id = 5; | |
// 最も遠い場所を分身とする | |
// TODO:別の場所も候補に 自分の周りとか | |
Field dist = createDistanceField(game.me.ninja, game.me.field); | |
int max = -1; | |
for (x = 0; x < dist.width; x++) | |
for (y = 0; y < dist.height; y++) | |
if (max < dist[x][y] && dist[x][y] != 999){ | |
for (Entity &e : game.me.ninja) | |
if (abs(e.x - x) + abs(e.y - y) < 4){ // 岩で潰さないように | |
goto cnm_spelldupme_lpcontinue; | |
} | |
//if (x<999){ | |
spell.x = x; spell.y = y; max = dist[x][y]; | |
//} | |
cnm_spelldupme_lpcontinue: | |
; | |
} | |
oserr << "spellpos->(" << spell.x << "," << spell.y << ")" << endl; | |
oserr << "spell_dup_me[5] called...redo!" << endl; | |
game.me.fieldUndo(); | |
result.clear(); ninidx = -1; continue; // ninjaループをすべてやりなおす。 | |
} | |
else if (!spellchk[0]){ | |
// 手数増やせばなんとかなるでしょう、という雑案 | |
cnm_spellready_quick: | |
spellchk[0] = true; // 高速 | |
spell.id = 0; | |
oserr << "spell_quick[0] called...redo!" << endl; | |
game.me.fieldUndo(); | |
result.clear(); ninidx = -1; continue; // ninjaループをすべてやりなおす。 | |
} | |
// なすすべなし | |
//cerr << "NtD" << endl; | |
oserr << "act:" << "NtD" << endl; | |
result.push_back(NinjaAction()); | |
continue; | |
} | |
// コマンドを押し込む | |
//cout << (*(NinjaAction*)(be[0].link)) << endl; | |
result.push_back(NinjaAction(*(NinjaAction*)(be[0].link))); | |
oserr << "act:" << (*(NinjaAction*)(be[0].link))<<" " | |
<<((NinjaAction*)(be[0].link))->pscore() | |
<< ((NinjaAction*)(be[0].link))->lscore() << endl; | |
} | |
game.me.fieldUndo(); | |
// 標準出力に書き出す | |
cnm_spell_redo: | |
switch (spell.id){ | |
case 0: // quick | |
if (result[0].cmdsize() <= 2 && result[1].cmdsize() <= 2){ | |
spell.id = -1; | |
oserr << "break quick." << endl; | |
goto cnm_spell_redo; | |
} | |
cout << "3" << endl; | |
cout << "0" << endl; | |
break; | |
case 1: // mstone | |
case 2: // estone | |
case 3: // mflash | |
case 4: // eflash | |
case 5: // mdup | |
case 6: // edup | |
cout << "3" << endl; | |
cout << spell.id << " " << spell.y << " " << spell.x << endl; | |
break; | |
case 7: // round | |
cout << "3" << endl; | |
cout << spell.id << " " << spell.x << endl; | |
break; | |
default: | |
// 発動する術式がないならば | |
// 攻撃を試みる | |
oserr << "attack?" << endl; | |
Entity atk = calcTry2Attack(); | |
if (0 <= atk.id){ | |
spell = atk; | |
goto cnm_spell_redo; | |
} | |
cout << "2" << endl; | |
} | |
for (NinjaAction &na : result) | |
cout << na << endl; | |
} | |
// ----------------------------------------------------------------------- | |
// メインループです | |
bool gameLoop(){ | |
oserr << "+++ gameLoop" << gameLoopCnt << " Begin +++" << endl; | |
game.reset(); | |
if (0<consoleLoad()) return false; | |
game.me.calcScore(); | |
game.enemy.calcScore(); | |
calcNinjaMove(); | |
gameLoopCnt++; | |
return true; | |
} | |
void mainPredictCheck(); | |
int main(int argc,char **argv){ | |
debugmode = (argc == 2 && argv[1][0] == 'D'); | |
randommode = (argc == 2 && argv[1][0] == 'R'); | |
if (randommode) | |
cerr << "Random Mode" << endl; | |
if (argc == 2 && argv[1][0] == 'P'){ | |
mainPredictCheck(); | |
//cout << "PredictCheck mode is unavailed" << endl; | |
return 0; | |
} | |
cerr << "GoodLuck&HaveFun!" << endl; | |
cout<<"mai"<<endl; | |
while(gameLoop()); | |
return 0; | |
} | |
// predict性能チェック用 | |
// p1がp2に一致するか、不一致数 | |
int diffNumDog(PlayerStatus &p1, PlayerStatus &p2){ | |
int count = 0; | |
for (Entity e1 : p1.dog){ | |
for (Entity e2 : p2.dog){ | |
if (e1.id == e2.id){ | |
count += (e1.x == e2.x && e1.y == e2.y) ? 0 : 1; | |
} | |
} | |
} | |
return count; | |
} | |
void mainPredictCheck(){ | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment