/*
go_demo.cxx by Pedro Izecksohn.
Date: 20260610 17:43 UTC-3
Local: Rio de Janeiro - RJ - Brazil

Disclaimer: This game is projected to do not overheat on Linux but the protections against overheat may not work properly. On Windows I assume its protection against overheat functions properly. This software was not projected to work on other operational system.

License:

1) This code is provided as is. This code is not guaranteed to work for any purpose. The execution of this code may burn your machine.

2) This code depends on the Allegro 5 library. Allegro 5 have its own license.

3) Any judiciary demand against me because of this code must be judged by the Brazilian courts of this jurisdiction.

4) The removal of the credits from this code or from its graphical user interface is forbidden.

5) Redistribution for a citizen of the United States of America is permitted.

Notice for compilation: I assume your compiler will optimize this source code.

Advice: Do not try to win a championship with this software, because this software is not good enough.
*/

#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <dirent.h>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <unistd.h>
#include <vector>

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>

#ifndef __WINDOWS__
#ifndef __LINUX__
#define __LINUX__ 1
#endif
#endif

#ifndef MIN
#define MIN(a,b) ((a<b)?a:b)
#endif

using namespace std;

float lerTemperatura(const std::string& zona) {
 string caminho = "/sys/class/thermal/" + zona + "/temp";
 ifstream arquivo(caminho);
 if (!arquivo.is_open()) return -1.0f;
 int temp_raw;
 arquivo >> temp_raw;
 return temp_raw / 1000.0f; // valor em milli°C → °C
}

string lerTipo(const string& zona) {
 string caminho = "/sys/class/thermal/" + zona + "/type";
 ifstream arquivo(caminho);
 if (!arquivo.is_open()) return "desconhecido";
 string tipo;
 getline(arquivo, tipo);
 return tipo;
}

bool must_sleep() {
 DIR* dir = opendir("/sys/class/thermal/");
 if (!dir) {
  cerr << "Não foi possível abrir /sys/class/thermal/\n";
  abort();
 }
 struct dirent* entrada;
 while ((entrada = readdir(dir)) != nullptr) {
  string nome = entrada->d_name;
  if (nome.find("thermal_zone") == 0) {
   float temp = lerTemperatura(nome);
   if (temp<=59.0f) continue;
   string tipo = lerTipo(nome);
   if (tipo.find("-trip")!=string::npos) continue;
   cout << nome << " [" << tipo << "]: " << temp << " °C\n";
   return true;
   }
  }
  closedir(dir);
  return false;
}

int WIN_W;
int WIN_H;
int FONT_SIZE;
int CELL;
int SIDE;
int BOARD_PX;
int MARGIN;
int TOP;

void prepare (ALLEGRO_DISPLAY *disp)
{
  WIN_W = al_get_display_width(disp);
  WIN_H = al_get_display_height(disp);
  FONT_SIZE = (MIN(WIN_W,WIN_H)/14);
  CELL = FONT_SIZE;
  BOARD_PX = (CELL*(SIDE-1));
  MARGIN = FONT_SIZE;
  TOP = (FONT_SIZE*3);
}

void pedro_log (string message)
{
  ofstream go_log ("go_demo.log", std::ios::app);
  go_log << message;
  go_log.close();
}

double ADDITIONAL_KOMI = 4.0;

ALLEGRO_DISPLAY* disp;

ALLEGRO_FONT* getFont ()
{
    auto fontSize = FONT_SIZE;
    string csa [2] = {
      "/system/fonts/Roboto-Regular.ttf",
      "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
      };
    for (size_t i=0; i<sizeof(csa)/sizeof(csa[0]); i++)
    {
        string *s = &csa[i];
        ALLEGRO_FONT* font = al_load_ttf_font(s->c_str(), fontSize, 0);
        if (!font)
          continue;
        return font;
    }
    cerr << "No compatible TTF was found.\n";
    abort();
}

double KOMI = 1.5;

const char ESPACO = '.';
const char BLACK = 'O';
const char WHITE = 'X';
const string PASS = "pass";

class Coordenada;
class Tabuleiro;
class LifeProb;

ALLEGRO_COLOR C_BG;
ALLEGRO_COLOR C_GRID;
ALLEGRO_COLOR C_BLK_ST;
ALLEGRO_COLOR C_WHT_ST;
ALLEGRO_COLOR C_BTN_TEXT;
ALLEGRO_COLOR C_BTN_ACTIVE;
ALLEGRO_COLOR C_DARK;
ALLEGRO_COLOR C_SCORE;
ALLEGRO_COLOR C_LAST;
ALLEGRO_COLOR C_TID;

 class Btn 
 {
    int x, y, nchars, w, h;
    string text;
    public:
    bool active;
    ALLEGRO_COLOR textColor;
    Btn()
    {
      x=-1;
      y=-1;
      nchars=0;
      w=0;
      h=0;
      active=false;
      textColor = C_BTN_TEXT;
    }
    Btn (const int X, const int Y, const string LABEL, ALLEGRO_COLOR tc = C_BTN_TEXT)
    {
        x=X;
        y=Y;
        nchars=LABEL.length();
        text = LABEL;
        auto fontSize = FONT_SIZE;
        w=nchars*fontSize;
        h=fontSize*1.5;
        active=false;
        textColor=tc;
    }
    string label ()
    {
    	return text;
    }
    void set_label (const string LABEL)
    {
        text = LABEL;
    }
    bool hit(int mx, int my) const
    {
            if (x==-1) return false;
            return mx >= x && mx < x+w && my >= y && my < y+h;
    }
    void draw (const ALLEGRO_FONT *font) const
    {
        assert(font);
        assert (x!=-1);
        al_draw_text(font, active ? C_BTN_ACTIVE : textColor, x+w/4, y+h/6, ALLEGRO_ALIGN_CENTER, text.c_str());
    }
 };

void deactivateButtons (vector<Btn*>& buttons)
{
  for (auto b : buttons)
      b->active=false;
}

class Coordenada {
public:
    int x, y;
    
    Coordenada(int col = 0, int lin = 0) : x(col), y(lin) {}
    
    Coordenada(const string& str) : x(-1), y(-1) {
        if (str.length() >= 2) {
            x = tolower(str[0]) - 'a';
            try {
                y = stoi(str.substr(1)) - 1;
            } catch (...) {
                x = -1;
                y = -1;
            }
        }
    }
    
    bool isValid() const {
        return x >= 0 && x < SIDE && y >= 0 && y < SIDE;
    }
    
    vector<Coordenada> getNeighbors() const {
        vector<Coordenada> neighbors;
        if (y > 0) neighbors.push_back(Coordenada(x, y - 1));
        if (x < SIDE - 1) neighbors.push_back(Coordenada(x + 1, y));
        if (y < SIDE - 1) neighbors.push_back(Coordenada(x, y + 1));
        if (x > 0) neighbors.push_back(Coordenada(x - 1, y));
        return neighbors;
    }
    
    string toString() const {
        return string(1, 'a' + x) + to_string(y + 1);
    }
    
    bool operator==(const Coordenada& other) const {
        return x == other.x && y == other.y;
    }
    
    bool operator!=(const Coordenada& other) const {
        return !(*this == other);
    }
    
    bool operator<(const Coordenada& other) const {
    	return (y*SIDE)+x < (other.y*SIDE)+other.x;
    }
};

// Mirror functions
Coordenada mirrorCoordinateRight(const Coordenada& coor) {
    return Coordenada(SIDE - 1 - coor.x, coor.y);
}

Coordenada mirrorCoordinateUp(const Coordenada& coor) {
    return Coordenada(coor.x, SIDE - 1 - coor.y);
}

Coordenada mirrorCoordinateRightUp(const Coordenada& coor) {
    return Coordenada(SIDE - 1 - coor.x, SIDE - 1 - coor.y);
}

Coordenada transposeCoordinate(const Coordenada& coor) {
    return Coordenada(coor.y, coor.x);
}

class LifeProb {
public:
  vector<vector<int>> linhas;
  int ntries;

  LifeProb ()
  {
    ntries=0;
    for (int y=0; y<SIDE; y++)
    {
      vector<int> line;
      for (int x=0; x<SIDE; x++)
      {
        line.push_back(0);
      }
      linhas.push_back(line);
    }
  }

  void clear ()
  {
    ntries=0;
    for (int y=0; y<SIDE; y++)
    {
      for (int x=0; x<SIDE; x++)
      {
        linhas[y][x]=0;
      }
    }
  }

  void update (vector<vector<char>>& img)
  {
    for (int y=0; y<SIDE; y++)
    {
      for (int x=0; x<SIDE; x++)
      {
        char c = img[y][x];
        if (c==BLACK)
        {
          linhas[y][x]++;
        }
        else if (c==WHITE)
        {
          linhas[y][x]--;
        }
      }
    }
   ntries++;
 }

  bool isDead (const Coordenada& coor)
  {
     if (ntries<1) return false;
     auto n = abs (linhas[coor.y][coor.x]);
     return (n<(ntries/2));
  }
};

class Tabuleiro {
public:
    vector<vector<char>> linhas;
    vector<string> history;
    vector<vector<vector<char>>> images;
    pair<double, double> points;
    
    Tabuleiro() {
        linhas.resize(SIDE, vector<char>(SIDE, ESPACO));
    }
    
    char vez() const {
        return (history.size() % 2) ? WHITE : BLACK;
    }
    
    bool isKo() const {
        if (history.empty() || history.back() == PASS) {
            return false;
        }
        
        int start = (vez() == BLACK) ? 1 : 0;
        for (size_t i = start; i < images.size() - 1; i += 2) {
            if (images[i] == linhas) {
                return true;
            }
        }
        return false;
    }
    
    void back(int how_many = 1)
    {
        if (history.empty()) return;
        for (int i = 0; i < how_many && !history.empty(); i++)
        {
            history.pop_back();
            if (!images.empty()) images.pop_back();
        }
        if (!images.empty())
        {
            linhas = images.back();
        }
        else
        {
            linhas.assign(SIDE, vector<char>(SIDE, ESPACO));
        }
    }
    
    bool isGameOver() const {
        if (history.size()%2) return false;
        return history.size() >= 2 && 
               history[history.size() - 1] == PASS && 
               history[history.size() - 2] == PASS;
    }
    
    void print() const {
        cout << "\n  ";
        for (int i = 0; i < SIDE; i++) {
            cout << " " << (char)('a' + i);
        }
        cout << "\n";
        
        for (int y = 0; y < SIDE; y++) {
            cout << (y + 1);
            if (y + 1 < 10) cout << " ";
            for (int x = 0; x < SIDE; x++) {
                cout << " " << linhas[y][x];
            }
            cout << "\n";
        }
        cout << "\n";
    }
    
    vector<Coordenada> getGroup(const Coordenada& coor) const {
        vector<Coordenada> ret;
        char color = linhas[coor.y][coor.x];
        getGroupHelper(coor, color, ret);
        return ret;
    }
    
    void getGroupHelper(const Coordenada& coor, char color, vector<Coordenada>& ret) const {
        if (find(ret.begin(), ret.end(), coor) != ret.end()) {
            return;
        }
        ret.push_back(coor);
        
        vector<Coordenada> neighbors = coor.getNeighbors();
        for (const auto& neigh : neighbors) {
            if (linhas[neigh.y][neigh.x] == color) {
                getGroupHelper(neigh, color, ret);
            }
        }
    }
    
    vector<Coordenada> getLiberties(const vector<Coordenada>& group) const {
        vector<Coordenada> ret;
        for (const auto& stone : group) {
            vector<Coordenada> neighbors = stone.getNeighbors();
            for (const auto& neigh : neighbors) {
                if (linhas[neigh.y][neigh.x] == ESPACO) {
                    if (find(ret.begin(), ret.end(), neigh) == ret.end()) {
                        ret.push_back(neigh);
                    }
                }
            }
        }
        return ret;
    }
    
    bool play(const string& move)
    {
        if (move == PASS)
        {
            if (isGameOver()) return false;
            history.push_back(PASS);
            images.push_back(linhas);
            return true;
        }
        return play(Coordenada(move));
    }
    
    bool play(const Coordenada& coor) {
        if (!coor.isValid() || linhas[coor.y][coor.x] != ESPACO || isGameOver())
        {
            return false;
        }
        
        vector<Coordenada> removed;
        char vez_atual = vez();
        linhas[coor.y][coor.x] = vez_atual;
        
        vector<Coordenada> neighbors = coor.getNeighbors();
        for (const auto& neigh : neighbors) {
            if (linhas[neigh.y][neigh.x] == ESPACO || 
                linhas[neigh.y][neigh.x] == vez_atual) {
                continue;
            }
            
            vector<Coordenada> group = getGroup(neigh);
            vector<Coordenada> liberties = getLiberties(group);
            
            if (liberties.empty()) {
                bool already_removed = false;
                for (const auto& r : removed) {
                    if (r == group[0]) {
                        already_removed = true;
                        break;
                    }
                }
                if (!already_removed) {
                    removed.insert(removed.end(), group.begin(), group.end());
                }
            }
        }
        
        if (removed.empty()) {
            vector<Coordenada> group = getGroup(coor);
            if (getLiberties(group).empty()) {
                linhas[coor.y][coor.x] = ESPACO;
                return false;
            }
        }
        
        for (const auto& r : removed) {
            linhas[r.y][r.x] = ESPACO;
        }
        
        history.push_back(coor.toString());
        images.push_back(linhas);
        
        if (isKo()) {
            back();
            return false;
        }
        
        return true;
    }
    
    pair<double, double> count() const {
        double black_count = 0;
        double white_count = KOMI;
        
        map<string, vector<pair<int, int>>> empties;
        empties["BLACK"] = vector<pair<int, int>>();
        empties["WHITE"] = vector<pair<int, int>>();
        empties["DAME"] = vector<pair<int, int>>();
        
        for (int y = 0; y < SIDE; y++) {
            for (int x = 0; x < SIDE; x++) {
                char color = linhas[y][x];
                if (color == BLACK) {
                    black_count++;
                    continue;
                } else if (color == WHITE) {
                    white_count++;
                    continue;
                }
                
                bool found = false;
                for (auto& kv : empties) {
                    for (auto& p : kv.second) {
                        if (p.first == x && p.second == y) {
                            found = true;
                            break;
                        }
                    }
                    if (found) break;
                }
                if (found) continue;
                
                Coordenada coor(x, y);
                vector<Coordenada> group = this->getGroup(coor);
                
                bool has_black_neighbor = false;
                bool has_white_neighbor = false;
                
                for (const auto& space : group) {
                    vector<Coordenada> neighbors = space.getNeighbors();
                    for (const auto& neigh : neighbors) {
                        char c = linhas[neigh.y][neigh.x];
                        if (c == BLACK) has_black_neighbor = true;
                        else if (c == WHITE) has_white_neighbor = true;
                    }
                }
                
                string territory_type;
                if (has_black_neighbor == has_white_neighbor) {
                    territory_type = "DAME";
                } else if (has_black_neighbor) {
                    territory_type = "BLACK";
                } else {
                    territory_type = "WHITE";
                }
                
                for (const auto& space : group) {
                    empties[territory_type].push_back({space.x, space.y});
                }
            }
        }

        {
          for (size_t i=0; i<history.size(); i++)
          {
            if (history[i]!=PASS) continue;
            if (i%2) white_count++;
            else black_count++;
          }
        }
        black_count += empties["BLACK"].size();
        white_count += empties["WHITE"].size();
        
        return {black_count, white_count};
    }
};

// ==================== Mirror Games ====================
vector<Tabuleiro> mirrorsGame(const Tabuleiro& t) {
    vector<Tabuleiro> ret(7);
    
    for (const auto& hist : t.history) {
        if (hist == PASS) {
            for (auto& g : ret) {
                g.play(PASS);
            }
            continue;
        }
        
        Coordenada coor(hist);
        ret[0].play(mirrorCoordinateRight(coor));
        ret[1].play(mirrorCoordinateUp(coor));
        ret[2].play(mirrorCoordinateRightUp(coor));
        
        Coordenada trans = transposeCoordinate(coor);
        ret[3].play(trans);
        ret[4].play(mirrorCoordinateRight(trans));
        ret[5].play(mirrorCoordinateUp(trans));
        ret[6].play(mirrorCoordinateRightUp(trans));
    }
    
    return ret;
}

bool endsWith(const string& s, const string& needle) {
    if (needle.length() > s.length()) return false;
    return s.substr(s.length() - needle.length()) == needle;
}

string remember(const Tabuleiro& t, const vector<Tabuleiro>* const previousGames) {
    if (!previousGames) return PASS;
    
    map<string, int> statistics;
    
    for (const auto& game : *previousGames) {
        if (game.images.size() <= t.images.size()) {
            continue;
        }
        if (!t.images.empty()) {
            size_t i = t.images.size()-1;
            if (t.images[i] != game.images[i]) {
                continue;
            }
        }
        string nextMove = game.history[t.history.size()];
        auto [b,w] = game.points;
        double note = (t.vez() == BLACK) ? (b - w) : (w - b);
        int score = (note > 0) ? 1 : -1;
        statistics[nextMove] += score;
    }
    if (statistics.empty()) {
        return "";
    }
    int max_score = -999999;
    for (const auto& kv : statistics) {
        max_score = max(max_score, kv.second);
    }
    if (max_score < 0) {
        return PASS;
    }
    for (const auto& kv : statistics) {
        if (kv.second == max_score) {
            return kv.first;
        }
    }
    return "";
}

Coordenada getEmptyCoordinate(const Tabuleiro& t) {
    vector<Coordenada> empty_cells;
    for (int y = 0; y < SIDE; y++) {
        for (int x = 0; x < SIDE; x++) {
            if (t.linhas[y][x] == ESPACO) {
                empty_cells.push_back(Coordenada(x, y));
            }
        }
    }
    
    if (empty_cells.empty()) {
        return Coordenada(-1, -1);
    }
    
    int i = rand() % empty_cells.size();
    return empty_cells[i];
}

string mayPass (Tabuleiro& t)
{
	if (t.history.size()<=4)
	{
		Coordenada coor2 = getEmptyCoordinate(t);
		if (t.play(coor2))
		{
			t.back(1);
			return coor2.toString();
		}
		else
		{
			return PASS;
		}
	}
	vector<Coordenada> visited;
	auto bw = t.count();
	double b = bw.first;
	double w = bw.second;
	if (max(b,w)-min(b,w)>2)
		return PASS;
	for (int y=0; y<SIDE; y++)
	{
		for (int x=0; x<SIDE; x++)
		{
			auto c=t.linhas[y][x];
			if (c==ESPACO)
			{
				Coordenada coor=Coordenada(x,y);
				auto it = find (visited.begin(), visited.end(), coor);
				if (it!=visited.end())
					continue;
				auto espgrp=t.getGroup(coor);
				visited.insert (visited.end(), espgrp.begin(), espgrp.end());
				if (espgrp.size()>1)
					continue;
				int n=0;
				auto vizesp=coor.getNeighbors();
				for (auto viz = vizesp.begin(); viz != vizesp.end(); viz++)
				{
					auto grpviz = t.getGroup(*viz);
					if (grpviz.size()>1)
						continue;
					if (t.getLiberties(grpviz).size()==1)
						n+=1;
				}
				if (n==1) // May not pass
				{
					for (int _=0; _<(SIDE*SIDE); _++)
					{
						Coordenada coor2 = getEmptyCoordinate(t);
						if (t.play(coor2)==true)
						{
							t.back(1);
							return coor2.toString();
						}
					}
				}
			}
		}
	}
	return PASS;
}

string think(Tabuleiro& t, int depth, int ttt, const vector<Tabuleiro>* const previousGames) {
    if (t.isGameOver()) {
        return PASS;
    }
    #ifdef __LINUX__
    while (must_sleep()) sleep(2);
    #endif
    time_t timeLimit = time(nullptr) + ttt;
    char cp = t.vez();
    
    if (!t.history.empty() && t.history.back() == PASS) {
        auto [b, w] = t.count();
        double originalNote = (cp == BLACK) ? (b - w) : (w - b);
        if (originalNote > 0) {
            return mayPass(t);
        }
    }

    string jogada = remember (t, previousGames);
     if (!jogada.empty() && jogada != PASS)
       return jogada;
    
    map<string, int> probabilities;
    int lenthistory = t.history.size();
    
    while (time(nullptr) < timeLimit) {
        for (int i = 0; i < depth; i++) {
            if (t.isGameOver()) break;
            Coordenada coor = getEmptyCoordinate(t);
            if (!t.play(coor)) {
                t.play(PASS);
            }
        }
        auto [b, w] = t.count();
        double n = (cp == BLACK) ? (b - w) : (w - b);
        int score = (n > 0) ? 1 : -1;
        if (lenthistory < (int)t.history.size()) {
            string coor = t.history[lenthistory];
            probabilities[coor] += score;
        }
        int how_many = t.history.size() - lenthistory;
        t.back(how_many);
    }
    if (probabilities.empty()) {
        return PASS;
    }
    string best = PASS;
    int maxScore = -999999;
    for (const auto& kv : probabilities) {
        if (kv.second > maxScore) {
            maxScore = kv.second;
            best = kv.first;
        }
    }
    if (maxScore < 0) {
        return mayPass(t);
    }
    if (best==PASS)
    {
    	return mayPass(t);
    }
    return best;
}

pair<int,double> getDeadSequence (Tabuleiro& t, LifeProb* lp)
{
    set<Coordenada> stones;
    for (int _ = 0; _ < (3*(SIDE*SIDE)); _++)
    {
    	Coordenada coor = getEmptyCoordinate (t);
    	if (stones.count(coor))
    	    continue;
    	else
    	    stones.emplace (coor);
    	bool bf=false;
    	bool wf=false;
    	bool sf=false;
    	auto neighbours = coor.getNeighbors();
    	for (auto neigh = neighbours.begin(); neigh != neighbours.end(); neigh++)
    	{
    			auto c=t.linhas[neigh->y][neigh->x];
    			if (c==BLACK)
    				bf=true;
    			else if (c==WHITE)
    				wf=true;
    			else
    			{
    				sf=true;
    				break;
    			}
    			if (bf and wf)
    				break;
    	}
    	if ((bf && wf) || sf)
    			t.play(coor);
    	else
    	{
    	    auto neighbors = coor.getNeighbors();
    	    for (auto neigh = neighbors.begin(); neigh!=neighbors.end(); neigh++)
    	    {
    	        char c=t.linhas[neigh->y][neigh->x];
    	        if (c==t.vez())
    	            continue;
    	        auto group = t.getGroup(*neigh);
    	        auto liberties = t.getLiberties(group);
    	        if (liberties.size()==1)
    	            if (t.play(coor))
    	                break;
    	    }
    	}
    }
    if (lp) lp->update(t.linhas);
    return t.count();
}

pair<int,double> getPoints (Tabuleiro& t, LifeProb *lp)
{
    if (!t.history.size())
    	return pair<int,double>(0,.0);
    if (lp) lp->clear();
    bool removed_passes = false;
    if ((t.history[t.history.size()-2]==PASS) && (t.history[t.history.size()-1]==PASS))
    {
    	t.back(2);
    	removed_passes = true;
    }
    auto thl = t.history.size();
    vector<int> black_points;
    vector<double> white_points;
    const int nseqs=20;
    for(int _ = 0; _  < nseqs; _++)
    {
      #ifdef __LINUX__
      while (must_sleep()) sleep(1);
      #endif 
    	pair<int,double> bw = getDeadSequence (t, lp);
    	t.back(t.history.size()-thl);
    	black_points.push_back(bw.first);
    	white_points.push_back(bw.second);
    }
    auto b2=(accumulate(black_points.begin(), black_points.end(),0))/nseqs;
    auto w2=(accumulate(white_points.begin(), white_points.end(),0))/nseqs;
    if (removed_passes)
    {
    	t.play(PASS);
    	t.play(PASS);
    }
    return pair<int,double> (b2, w2+ADDITIONAL_KOMI);
}

const char* contar (Tabuleiro& t, LifeProb *lp);
char  humanPlay(ALLEGRO_EVENT_QUEUE* eq)
{
    assert(eq);
    ALLEGRO_FONT* font = getFont();
    bool visible=true;
    while (true)
    {
        al_rest(0.016);
        auto width = WIN_W;
        auto height = WIN_H;
        auto fontSize = FONT_SIZE;
        int X_REF = width/2;
        int Y_REF = height/2-fontSize*3;
        Btn btnBlack  (X_REF-fontSize*5, Y_REF+fontSize*2, "Black", C_BLK_ST);
        Btn btnWhite  (X_REF+fontSize, Y_REF+fontSize*2, "White", C_WHT_ST);
        if (visible)
        {
            al_clear_to_color(C_BG);
            al_draw_text(font, C_DARK, X_REF, Y_REF, ALLEGRO_ALIGN_CENTRE, "Choose your stones:");
            btnBlack.draw(font);
            btnWhite.draw(font);
            al_flip_display();
        }
        int mx=-1, my=-1;
        int mouse_button=0;
        ALLEGRO_EVENT ev;
        al_get_next_event(eq, &ev);
        if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
        {
            exit(EXIT_SUCCESS);
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE)
        {
            	al_acknowledge_resize(disp);
            	font=getFont();
            	continue;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT)
        {
          visible=false;
          continue;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING)
        {
        	al_acknowledge_drawing_halt (disp);
        	visible=false;
        	continue;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING)
        {
        	al_acknowledge_drawing_resume (disp);
        	font = getFont();
        	visible=true;
        	continue;
        }
        else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
        {
            mx = ev.mouse.x;
            my = ev.mouse.y;
            mouse_button=ev.mouse.button;
        }
        else if (ev.type==ALLEGRO_EVENT_TOUCH_BEGIN)
        {
            mx = ev.touch.x;
            my = ev.touch.y;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_ORIENTATION)
        {
        	font=getFont();
        	continue;
        }
        if (mx==-1) continue;
        if ((mouse_button==1)||(mouse_button==0))
        {
            if (btnBlack.hit(mx, my))
            {
            	al_destroy_font(font);
            	return BLACK;
            }
            if (btnWhite.hit(mx, my))
            {
            	al_destroy_font(font);
            	return WHITE;
            }
        }
     }
}

/*
Does not flip display.
*/
void drawScreen (const Tabuleiro& t, const vector<Btn*> buttons, const Coordenada coor, LifeProb *lp)
{
    const bool gameOver = t.isGameOver();
    auto top = TOP;
    auto margin = MARGIN;
    auto cell = CELL;
    auto board_px = BOARD_PX;
    ALLEGRO_FONT *font = getFont();
    al_clear_to_color(C_BG);
    Btn btnCredit (margin, top/4, "USA & Pedro Izecksohn");
    btnCredit.draw(font);
      // Grade
      for (int i = 0; i < SIDE; i++)
      {
          al_draw_line(margin, top + i*cell, margin+board_px, top + i*cell, C_GRID, 1.3f);
          al_draw_line(margin + i*cell, top, margin + i*cell, top + board_px, C_GRID, 1.3f);
       }
       // Pedras
       int last_x = -1, last_y = -1;
       if (t.history.size())
       {
           string last = t.history[t.history.size()-1];
           if (last!=PASS)
           {
               Coordenada c (last);
               last_x=c.x;
               last_y=c.y;
           }
       }
    for (int y = 0; y < SIDE; y++)
    {
        for (int x = 0; x < SIDE; x++)
        {
            const char c = t.linhas[y][x];
            if (c == ESPACO) continue;
            const float px = margin + x*cell;
            const float py = top + y*cell;
            const float r  = cell/2.0f - 3;
            if (c == BLACK)
            {
                al_draw_filled_circle(px, py, r, C_BLK_ST);
                if (gameOver && lp->isDead (Coordenada (x,y)))
                    al_draw_filled_circle(px, py, r/2, C_WHT_ST);
            }
            else
            {
                al_draw_filled_circle(px, py, r, C_WHT_ST);
                if (gameOver && lp->isDead (Coordenada (x,y)))
                    al_draw_filled_circle(px, py, r/2, C_BLK_ST);
            }
             // Anel de destaque na última pedra
             if ((!gameOver) && (x == last_x && y == last_y))
                 al_draw_circle(px, py, r - 3, C_LAST, 1.8f);
          }
     }
     if ((!gameOver) && (coor.isValid()))
     {
       al_draw_filled_circle(coor.x*cell+margin, coor.y*cell+top, cell/4, al_map_rgb (0,255,0));
     }
     for (size_t i=0; i<buttons.size(); i++)
     {
         buttons[i]->draw(font);
     }
     al_flip_display();
}

const char* contar (Tabuleiro& t, LifeProb* lp)
{
    static char buffer [7];
    pair<int,double> bw;
    bw = getPoints (t, lp);
    const double black = bw.first;
    const double white = bw.second;
    string s;
    if (black>white)
    {
        s+="B+";
        s+=to_string (black-white);
    } 
    else if (white>black)
    {
        s+="W+";
        s+=to_string (white-black);
    }
    else
    {
    	s="0";
    }
    strncpy(buffer, s.c_str(),6);
    buffer[6]=0;
    if (buffer[5]=='0') buffer[5]=0;
    return buffer;
}

int main(int argc, char** argv)
{
    srand(time(nullptr));

    if (!al_init())
    {
        cerr << "error: al_init()\n";
        return EXIT_FAILURE;
    }
    const int mouse_exists=al_install_mouse();
    const int touch_exists=al_install_touch_input();
    if (!(mouse_exists||touch_exists))
    {
        cerr << "No pointing device detected." << endl;
        return EXIT_FAILURE;
    }
    al_init_primitives_addon();
    al_init_font_addon();
    al_init_ttf_addon();

    disp = al_create_display(640, 480);
    if (!disp)
    {
        cerr << "error: al_create_display(640, 480)\n";
        return EXIT_FAILURE;
    }
    prepare(disp);

    al_set_window_title(disp, "Go Demo - Pedro Izecksohn");

    ALLEGRO_EVENT_QUEUE* eq = al_create_event_queue();
    al_register_event_source(eq, al_get_display_event_source(disp));
    if (mouse_exists)
    {
        al_register_event_source(eq, al_get_mouse_event_source());
    }
    if (touch_exists)
    {
        al_register_event_source(eq, al_get_touch_input_event_source());
    }

    C_BG = al_map_rgb (200, 200, 0);
    C_GRID = al_map_rgb (0, 0, 0);
    C_BLK_ST = al_map_rgb (15, 15, 15);
    C_WHT_ST = al_map_rgb(245, 245, 245);
    C_BTN_TEXT = al_map_rgb (0,0,0);
    C_BTN_ACTIVE = al_map_rgb (127, 127, 127);
    C_DARK = al_map_rgb (25, 25, 25);
    C_LAST = al_map_rgb (255, 0, 0);
    C_SCORE = al_map_rgb (127, 127, 127);
    C_TID = al_map_rgb (0, 0, 127);

    SIDE = 9;
    prepare(disp);
    int ttt = 30;
    ALLEGRO_FONT* font = getFont();

    auto fontSize=FONT_SIZE;
    auto top=TOP;
    auto margin=MARGIN;
    auto board_px=BOARD_PX;

    // Botões de jogo
    const int BTN_X = margin+board_px+fontSize;
    const int BTN_Y = top + fontSize*2;
    const int BTN_VSPC_MUL = fontSize*2;
    Btn btnBack (BTN_X, BTN_Y, "Back");
    Btn btnConfirm (BTN_X, BTN_Y+BTN_VSPC_MUL, "Confirm");
    Btn btnPass (BTN_X, BTN_Y+(BTN_VSPC_MUL*2), "Pass");
    Btn btnCount (margin, top+board_px, "Count", C_SCORE);
    Btn btnPlayAgain (margin+fontSize*7, top+board_px, "Play again", C_TID);
    vector<Btn*> botoes_iniciais;
    botoes_iniciais.push_back(&btnConfirm);
    botoes_iniciais.push_back(&btnPass);
    botoes_iniciais.push_back(&btnCount);
    vector<Btn*> botoes_de_jogo;
    botoes_de_jogo.push_back(&btnBack);
    botoes_de_jogo.push_back(&btnConfirm);
    botoes_de_jogo.push_back(&btnPass);
    botoes_de_jogo.push_back(&btnCount);
    vector<Btn*> botoes_finalizacao;
    botoes_finalizacao.push_back(&btnCount);
    botoes_finalizacao.push_back (&btnPlayAgain);
    vector<Btn*> botoes_vazio;

    long int last_think=0;
    vector<Tabuleiro>* previousGames = new vector<Tabuleiro>();
    
    char  human_player = humanPlay (eq);
    Tabuleiro t;
    const int depth = SIDE * SIDE;

    // Loop principal
    vector<Btn*> buttons;
    bool visible = true;
    Coordenada selected (-1,-1);
    LifeProb lp;
    while (true)
    {
        buttons = botoes_vazio;
        if (t.isGameOver())
        {
             buttons = botoes_finalizacao;
        }
        else // not game over
        {
            if (t.vez()==human_player)
            {
                if (t.history.size())
                {
                		buttons = botoes_de_jogo;
                }
                else
                {
                		buttons = botoes_iniciais;
                }
            } // ends (t.vez()==human_player)
            else
            {
               buttons = botoes_vazio;
            }
        }

        if (visible)
        {
           drawScreen (t, buttons, selected, &lp);
        }

        // ── Processar evento ──────────────────────────────────────────────
        bool other_event = false;
        int mx=-1, my=-1, mouse_button=0;
        ALLEGRO_EVENT ev;
        while (((t.vez()==human_player) || t.isGameOver()) && al_get_next_event(eq, &ev))
        {
            if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
            {
            	al_destroy_font(font);
            	al_destroy_display(disp);
            	al_destroy_event_queue(eq);
            	al_uninstall_system();
            	delete previousGames;
              return EXIT_SUCCESS;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT)
            {
            	visible = false;
            	other_event = true;
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN)
            {
            	font = getFont();
            	visible = true;
            	other_event = true;
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE)
            {
            	al_acknowledge_resize(disp);
            	other_event = true;
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING)
            {
            	al_acknowledge_drawing_halt(disp);
            	al_destroy_font (font);
            	font = NULL;
            	visible=false;
            	other_event=true;
            	//al_rest(0.1);
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING)
            {
            	al_acknowledge_drawing_resume(disp);
            	font = getFont();
            	visible = true;
            	other_event=true;
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_DISPLAY_ORIENTATION)
            {
            	if (font) al_destroy_font (font);
            	font = getFont();
            	visible = true;
            	other_event=true;
            	break;
            }
            if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
            {
                mx = ev.mouse.x;
                my = ev.mouse.y;
                mouse_button = ev.mouse.button;
                for (auto b : buttons)
                {
                  if (b->hit(mx,my))
                  {
                    b->active=true;
                    drawScreen (t, buttons, selected, &lp);
                  }
                }
                break;
            }
            if (ev.type==ALLEGRO_EVENT_TOUCH_BEGIN)
            {
                mx = ev.touch.x;
                my = ev.touch.y;
                for (auto b : buttons)
                {
                  if (b->hit(mx,my))
                  {
                      b->active=true;
                      drawScreen (t, buttons, selected, &lp);
                  }
                }
                break;
            }
            other_event = true;
            break;
        } // Fim do loop de eventos.
        if (other_event)
        {
            continue;
        }

        if ((buttons==botoes_finalizacao) && btnPlayAgain.hit(mx,my))
        {
          string bpaol = btnPlayAgain.label();
          btnPlayAgain.set_label("Cooling");
          drawScreen (t, buttons, selected, &lp);
          while (must_sleep()) sleep(1);
          btnPlayAgain.set_label(bpaol);
          t.points=getPoints(t,&lp);
          previousGames->push_back(t);
          auto reflexes = mirrorsGame(t);
          for (auto r = reflexes.begin(); r!=reflexes.end(); r++)
          {
            r->points=t.points;
            previousGames->push_back(*r);
          }
          human_player = humanPlay (eq);
          t=Tabuleiro();
          lp.clear();
          btnCount.set_label("0");
          buttons=botoes_iniciais;
          btnPlayAgain.active=false;
          continue;
        }

        if (btnCount.hit(mx, my))
        {
            btnCount.set_label(contar(t,&lp));
            btnCount.active=false;
            continue;
        }

        if ((!t.isGameOver()) && (human_player == t.vez()))
        {
            if (btnConfirm.hit(mx,my))
            {
                if (mouse_button==2) continue;
                if (t.play(selected))
                {
                    btnCount.set_label (contar (t, &lp));
                }
                selected = Coordenada (-1, -1);
                btnConfirm.active=false;
                continue;
            }
            else if (btnPass.hit(mx, my))
            {
                if (mouse_button==2)
                {
                   btnPass.active=false;
                   continue;
                }
                selected = Coordenada (-1, -1);
                if (!t.play(PASS))
                {
                  btnPass.active=false;
                  continue;
                }
                btnPass.active=false; 
                if (t.isGameOver())
                {
                    btnCount.set_label(contar(t,&lp));
                    drawScreen (t, buttons, selected, &lp);
                } // ends (t.isGameOver())
                continue;
            }
            else if (btnBack.hit(mx,my))
            {
                btnBack.active=false;
                if (mouse_button==2)
                    continue;
                selected = Coordenada (-1, -1);
                t.back(t.history.size()>=2 ? 2 : t.history.size());
                btnCount.set_label(contar(t, &lp));
                continue;
            }
            else if (human_player == t.vez())
            {
                double gx = (mx - MARGIN) / (double) CELL;
                if ((gx-(double)(int)gx)>=0.5)
                    gx = ((int)gx)+1;
                double gy = (my - TOP)/ (double) CELL;
                 if ((gy-(double)(int)gy)>=0.5)
                    gy = ((int)gy)+1;
                if (gx >= 0 && gx < SIDE && gy >= 0 && gy < SIDE)
                {
		            Coordenada move ((int)gx, (int)gy);
		            if (!move.isValid())
		                continue;
		            if (mouse_button==2)
		                continue;
	        	    selected=move;
	        	    drawScreen (t, buttons, selected, &lp);
	        	    continue;
              }
            }
        }

        if ((t.vez()!=human_player)&&(!t.isGameOver()))
        {
            long int time_missing = (last_think+ttt) - time(nullptr);
            if (time_missing>0) sleep(time_missing);
            string ai_move_buf = think (t, depth, ttt, previousGames);
            last_think=time(nullptr);
            if (ai_move_buf == PASS)
            {
                if (t.play(PASS))
                {
                    btnCount.set_label (contar(t, &lp));
                }
            }
            else
            {
                Coordenada coor (ai_move_buf);
                if (t.play (coor))
                {
                    btnCount.set_label (contar(t, &lp));
                }
                else
                {
                    Coordenada coor = getEmptyCoordinate(t);
                    if (t.play(coor))
                    {
                        btnCount.set_label (contar(t, &lp));
                    }
                    continue;
                }
            }
        }
        al_rest(0.016);  // ~60 fps
    } // fim loop

    delete previousGames;

    al_destroy_font(font);
    al_destroy_event_queue(eq);
    al_destroy_display(disp);
    al_uninstall_touch_input();
    return EXIT_SUCCESS;
}
