#include <bits/stdc++.h>
using namespace std;
// 横:6,縦:5
// ドロップの種類:7
// 0:空 1:火 2:水 3:木 4:光 5:闇 6:回復
enum{
EMPTY = 0,FIRE,WATER,WOOD,LIGHT,DARK,HEART,
};
const int H = 5,W = 6;
const int N = 7;
const int D = 4;
// DOWN RIGHT UP LEFT
vector<string> direction = {"DOWN","RIGHT", "UP", "LEFT"};
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
class ZobristHash{
public:
int table[H*W][N];
ZobristHash(){
for(int i=0;i<H*W;i++){
for(int j=0;j<N;j++){
table[i][j] = rand();
}
}
}
int hash(int* board){
int h = 0;
for(int i=0;i<H*W;i++){
if(board[i] != 0){
int j = board[i];
h ^= table[i][j];
}
}
return h;
}
};
struct State{
public:
double value_ = -1;
int* board_;
int* comboed_board_ = NULL;
int point_x_, point_y_;
int combo_counter_ = -1;
int turns_ = 0;
list<int> move_process_;
// ランダム盤面とポイントを生成
State(){
board_ = new int[H*W];
for(int i=0;i<H*W;i++){
board_[i] = rand()%7+1;
}
point_x_ = rand()%W;
point_y_ = rand()%H;
value_ = GetValue();
}
// コピー
State(int* bd,int px,int py,int turns,list<int> move_process){
board_ = new int[H*W];
CopyBoard(bd,board_);
point_x_ = px;
point_y_ = py;
value_ = GetValue();
turns_ = turns;
move_process_ = move_process;
}
~State(){
delete[] board_;
delete[] comboed_board_;
}
// 盤面の評価値を返す
double GetValue(){
if(value_ != -1)return value_;
double val = 0.0;
int combo_counter = GetComboCounter();
int two_continuous_counter = 0;
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
if( j < W-1 && comboed_board_[i*W+j] == comboed_board_[i*W+j+1] && comboed_board_[i*W+j] != 0){
two_continuous_counter++;
}
if( i < H-1 && comboed_board_[i*W+j] == comboed_board_[(i+1)*W+j] && comboed_board_[i*W+j] != 0){
two_continuous_counter++;
}
}
}
val = combo_counter * 100 + two_continuous_counter * 5;
// if(turns_ > 20)return value_ = 0;
return value_ = val;
}
// 次の全てのStateを得る
vector<State*> GetAllNextStates(){
vector<State*> next_states;
for(int i=0;i<D;i++){
int next_point_x_ = point_x_ + dx[i];
int next_point_y_ = point_y_ + dy[i];
if(IsOutOfBoard(next_point_x_,next_point_y_))continue;
swap( board_[point_y_*W+point_x_], board_[next_point_y_*W+next_point_x_] );
move_process_.push_back(i);
next_states.push_back(new State(board_, next_point_x_, next_point_y_, turns_ + 1,move_process_));
swap( board_[point_y_*W+point_x_], board_[next_point_y_*W+next_point_x_] );
move_process_.pop_back();
}
return next_states;
}
int GetComboCounter(){
if(combo_counter_ == -1)return combo_counter_ = SimulateCombo();
return combo_counter_;
}
// コンボをシミュレートしてコンボ数を返す,comboed_board_にコンボ後の盤面が入る
int SimulateCombo(){
// コンボ後盤面の領域確保
comboed_board_ = new int[H*W];
for(int i=0;i<H*W;i++)
comboed_board_[i] = board_[i];
int combo_counter = 0;
for(;;){
int temp = SimulateComboAndDrop();
combo_counter += temp;
if(temp == 0)break;
}
return combo_counter;
}
// コンボして下に詰める
int SimulateComboAndDrop(){
int combo_counter = 0;
// コンボをシミュレートする
// コンボ部分に10加算
PrepareCombo();
// 10以上の部分で繋がっている部分を0にする
for(int i=0;i<H;i++)for(int j=0;j<W;j++){
if(PrepareComboDFS( j, i, comboed_board_[i*W+j]))combo_counter++;
}
// ドロップを下に詰める
for(int j=0;j<W;j++){
int empty_point = 0;
for(int i=H-1;i>=0;i--){
if( comboed_board_[i*W+j] == 0 && empty_point < i )empty_point = i;
else if(comboed_board_[i*W+j] != 0 && empty_point > i){
comboed_board_[empty_point*W+j] = comboed_board_[i*W+j];
comboed_board_[i*W+j] = 0;
empty_point--;
}
}
}
return combo_counter;
}
void PrepareCombo(){
const int flag = 10;
// 横3つ以上のものに印
for(int i=0;i<H;i++){
int counter = 1;
for(int j=1;j<=W;j++){
if( W == j || comboed_board_[i*W+j]%flag != comboed_board_[i*W+j-1]%flag || comboed_board_[i*W+j] == EMPTY){
if(counter >= 3){
for(int k=j-counter;k<j;k++){
comboed_board_[i*W + k] += flag;
}
}
counter = 1;
}
else counter++;
}
}
// 縦3つ以上のものに印
for(int i=0;i<W;i++){
int counter = 1;
for(int j=1;j<=H;j++){
if( H == j || comboed_board_[j*W+i]%flag != comboed_board_[(j-1)*W+i]%flag || comboed_board_[j*W+i] == EMPTY ){
if(counter >= 3){
for(int k=j-counter;k<j;k++){
comboed_board_[k*W+i] += flag;
}
}
counter = 1;
}
else counter++;
}
}
}
bool PrepareComboDFS(int x,int y,int color){
if(comboed_board_[y*W+x] < 10)return false;
comboed_board_[y*W+x] = 0;
for(int i=0;i<D;i++){
int nx = x + dx[i], ny = y + dy[i];
if(IsOutOfBoard(nx,ny))continue;
if( comboed_board_[ny*W+nx] < 10 || color%10 != comboed_board_[ny*W+nx]%10)continue;
PrepareComboDFS(nx,ny,color);
}
return true;
}
// 盤面内にポイントがあるか
bool IsOutOfBoard(int px, int py){
if(px < 0 || py < 0 || px >= W || py >= H)return true;
else return false;
}
// 盤面のディープコピー用
void CopyBoard(int* from_board_,int* to_board_){
for(int i=0;i<H*W;i++)
to_board_[i] = from_board_[i];
}
// 盤面を標準出力に表示
void OutputBoard(){
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
cout << board_[i*W+j] << " ";
}
cout << endl;
}
}
void OutputComboedBoard(){
if(comboed_board_ == NULL){
SimulateCombo();
}
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
cout << comboed_board_[i*W+j] << " ";
}
cout << endl;
}
}
void OutputInfo(){
cout << "turns" << endl;
cout << turns_ << endl;
cout << "point" << endl;
cout << point_x_ << " " << point_y_ << endl;
cout << "value" << endl;
cout << GetValue() << endl;
cout << "board" << endl;
OutputBoard();
cout << "comboed board" << endl;
OutputComboedBoard();
cout << "process" << endl;
OutputMoveProcess();
}
void OutputMoveProcess(){
for(auto it=move_process_.begin();it!=move_process_.end();it++){
cout << *it << " ";
}cout << endl;
}
bool operator<(const State &state)const{
return value_ < state.value_;
}
};
struct StateCompare{
bool operator()(State* s1, State* s2)const{
return *s1 < *s2;
}
};
int main(void){
srand(time(NULL));
ZobristHash zb;
// 初期盤面をランダムで生成
int* init_board = new int[H*W];
cout << "init board" << endl;
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
init_board[i*W+j] = rand()%6+1;
cout << init_board[i*W+j] << " ";
}cout << endl;
}
cout << endl;
cout << "Start Searching" << endl;
int MaxTurn = 12,beam_breath = 40000;
set<int> searched_states;
priority_queue<State*,vector<State*>,StateCompare> now_states_pq;
// 初期状態の生成
for(int i=0;i<H;i++)for(int j=0;j<W;j++){
State* state = new State(init_board,j,i,0,list<int>());
now_states_pq.push(state);
searched_states.insert(zb.hash(state->board_));
}
// ビームサーチ
for(int t=0; t < MaxTurn; t++){
cout << "|";cout.flush();
priority_queue<State*,vector<State*>,StateCompare> next_states_pq;
for(int i=0; i < beam_breath; i++){
if(now_states_pq.empty())break;
State* now_state = now_states_pq.top();now_states_pq.pop();
next_states_pq.push(now_state);
// 次の状態の生成
vector<State*> next_states = now_state->GetAllNextStates();
for(int j=0;j<next_states.size();j++){
State* next_state = next_states[j];
// ハッシュ値計算
int state_hash_code = zb.hash(next_state->board_);
// 既に探索済みであれば,探索しない
if(searched_states.find(state_hash_code) != searched_states.end())continue;
searched_states.insert(state_hash_code);
next_states_pq.push(next_state);
}
}
now_states_pq = next_states_pq;
// 現在の最大の状態を出力
/*
State* state = now_states_pq.top();
cout << t << " Top State" << endl;
cout << state->turns_ << " " << state->GetValue() << " " << next_states_pq.size() << endl;
state->OutputMoveProcess();
cout << endl;
*/
}
cout << endl << "End Searching" << endl << endl;
State* best_state = now_states_pq.top();
int px = best_state->point_x_;
int py = best_state->point_y_;
list<int> move_process = best_state->move_process_;
cout << "final board" << endl;
best_state->OutputBoard();
cout << "Combo: " << best_state->GetComboCounter() << endl;
for(auto it=move_process.begin();it!=move_process.end();it++){
px -= dx[*it];
py -= dy[*it];
}
cout << px << " " << py << endl;
for(auto it=move_process.begin();it!=move_process.end();it++){
cout << direction[*it] << " ";
px -= dx[*it];
py -= dy[*it];
}cout << endl;
return 0;
}