Skip to content

Instantly share code, notes, and snippets.

@buyoh
Last active April 5, 2016 10:20
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 buyoh/583dd858043e5e0570ab to your computer and use it in GitHub Desktop.
Save buyoh/583dd858043e5e0570ab to your computer and use it in GitHub Desktop.
/*
オプション:
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