GameTestLib/Tetris/Espacio/Main.mqh
Nique_372 518798c526
2025-11-15 07:58:13 -05:00

493 lines
30 KiB
MQL5

//+------------------------------------------------------------------+
//| Main.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com/es/users/nique_372/news"
#property strict
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#include "..\\Figuras\\Figuras.mqh"
#include <OpenCLCts\\CLFast.mqh>
COpenCL g_cl;
#define cl_kernels_total 1
#define cl_buffers_total 2
#define cl_kernel_tetris_asingocuped 0
#define cl_buffer_pxocuped 1
#define cl_buffer_px_utilizados 0
//---
#define kernel_tetris_asingocuped_arg_px_utilizados 0
#define kernel_tetris_asingocuped_arg_pxocuped 1
#resource "kernels.cl" as const string cl_program
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CEspacioUtilizable
{
private:
//--- Geeral
bool m_init;
CCanvasCustom* m_canvas;
// Tomaremos en cuenta todo el anchjo de canvas..
// Los peixles que se le dan no es que esten dentro del tablero son globales
bool m_pixeles_utilizados[];
int m_width;
int m_height;
uint m_clr_clean;
//--- Cordenadas del espacio estas no estan dentro estan dentro
int m_y_init;
int m_y_end;
int m_x_init;
int m_x_end;
int m_xsize_util;
//---
void OnDeleteEspacio(int x1, int y1, int x2, int y2);
public:
CEspacioUtilizable(void);
// ~CEspacioUtilizable(void);
//---
void Init(CCanvasCustom* c, int yinit, int yend, int xinit, int xend, uint clr_clean);
//---
__forceinline int XInit() const { return m_x_init; }
__forceinline int XEnd() const { return m_x_end; }
__forceinline int YInit() const { return m_y_init; }
__forceinline int YEnd() const { return m_y_end; }
__forceinline int XSize() const { return m_xsize_util; }
//---
void Resetear();
//---
int AddFigure(CTetrisFigura* figura); // Retonra el numero de espacios eliminados
//---
// Retorna true si un pixel ha sido utilizado
__forceinline bool PixelUtilizado(int x, int y) const { return m_pixeles_utilizados[(y * m_width + x)]; }
int PixelXGetIndexInicioAtras(int x, int y) const;
int PixelXGetIndexInicioAdelante(int x, int y) const;
int PixelGetIndexInicioAbajo(int x, int y) const;
//---
__forceinline int MinY(int x) { return m_pixeles_utilizados[x];} // Deviñee el minimo y para un x
// Verifica que las cordenas y esten dentro del recutado.. veriiffca pro arriba osea que sean menores o iguales m_y_end
__forceinline bool YCorDentro(int y) const;
//---
bool XColisionAdelante(const int &x_cordinates[], int xref) const;
bool XColisionAtras(const int &x_cordinates[], int xref) const;
int GetMaxErrorY(int& y_cordinates[], int yref);
bool YColision(const int& y_cordinates[], int yref) const;
int GetMaxErrorXAdelente(int &x_cordinates[], int xref);
int GetMaxErrorXAtras(int &x_cordinates[], int xref);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CEspacioUtilizable::CEspacioUtilizable(void)
: m_canvas(NULL), m_init(false)
{
g_cl.AddLogFlags(LOG_ALL);
g_cl.ContextCreate(CL_USE_ANY);
g_cl.ProgramCreate(cl_program);
g_cl.SetBuffersCount(cl_buffers_total);
g_cl.SetKernelsCount(cl_kernels_total);
g_cl.KernelCreate(cl_kernel_tetris_asingocuped, "Tetris_AsingOcuped");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEspacioUtilizable::OnDeleteEspacio(int x1, int y1, int x2, int y2)
{
uint pixels[];
m_canvas.GetPixelsArray(pixels);
//---
const int px_init = m_canvas.GetIndex(x1, y1);
const int px_end = m_canvas.GetIndex(x2, y2);
const int offset = (fabs(y2 - y1) + 1) * m_width;
//--- Reduce las cordenas relaes
for(int px = px_end; px >= px_init; px--)
{
pixels[px + offset] = pixels[px];
m_pixeles_utilizados[px] = false;
//---
pixels[px] = m_clr_clean;
}
//---
m_canvas.SetPixelsArray(pixels);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEspacioUtilizable::Init(CCanvasCustom *c, int yinit, int yend, int xinit, int xend, uint clr_clean)
{
//---
if(m_init)
return;
//---
m_canvas = c;
m_y_end = yend;
m_y_init = yinit;
m_x_init = xinit;
m_x_end = xend;
m_clr_clean = clr_clean;
m_xsize_util = fabs(m_x_end - m_x_init) + 1;
//---
m_width = c.Width();
m_height = c.Height();
ArrayResize(m_pixeles_utilizados, c.TotalPixels()); // Pixeles utilizados
m_init = true;
Resetear(); // Arrays
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CEspacioUtilizable::Resetear()
{
//---
for(int i = 0; i < ArraySize(m_pixeles_utilizados); i++)
m_pixeles_utilizados[i] = false;
//---
for(int x = 0; x < m_width; x++)
{
const int idx = ((m_y_end + 1) * m_width) + x;
m_pixeles_utilizados[idx] = true; // Borde Inferior
}
// m_canvas.LineHorizontal(0, m_width - 1, m_y_end + 1, ColorToARGB(clrAliceBlue));
//--- Bordes
for(int y = 0; y < m_height; y++)
{
m_pixeles_utilizados[m_canvas.GetIndex(m_x_init - 1, y)] = true; // Borde incial
m_pixeles_utilizados[m_canvas.GetIndex(m_x_end + 1, y)] = true; // Borde final
}
//--- Limpiamos el fondo
m_canvas.FillRectangle(m_x_init, m_y_init, m_x_end, m_y_end, m_clr_clean);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// -1: Estra dentro de los rangos no hay pixel cercano
// -2: Fuera de rango
#define TET_ESPU_FUERA_NO_COLISION -1
//+------------------------------------------------------------------+
int CEspacioUtilizable::PixelXGetIndexInicioAtras(int x, int y) const
{
int i = x;
while(i >= m_x_init)
{
//--- Encontrado
if(PixelUtilizado(i, y))
return i + 1;
//--- Siguiente
i--;
}
return m_x_init;
}
//+------------------------------------------------------------------+
int CEspacioUtilizable::PixelXGetIndexInicioAdelante(int x, int y) const
{
int i = x;
while(i <= m_x_end)
{
//--- Encontrado
if(PixelUtilizado(i, y))
return i - 1;
//--- Siguiente
i++;
}
return m_x_end;
}
//+------------------------------------------------------------------+
int CEspacioUtilizable::PixelGetIndexInicioAbajo(int x, int y) const
{
int i = y;
while(i <= m_y_end)
{
//--- Encontrado
if(PixelUtilizado(x, i))
return i - 1;
//--- Siguiente
i++;
}
return m_y_end;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CEspacioUtilizable::YColision(const int &y_cordinates[], int yref) const
{
const int total = ArraySize(y_cordinates);
int x, y;
for(int pos_x = 0; pos_x < total; pos_x++)
{
m_canvas.PixelDecompress(y_cordinates[pos_x], x, y);
const int cordc = PixelGetIndexInicioAbajo(x, yref); // Retorna Y
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete X
{
continue;
}
// De lo contraio si hay un obejto para colisionar, asi que verifiquemos si la cordanas lo atravisa
if(y >= cordc) // Colisiona
return true;
}
return false;
}
//+------------------------------------------------------------------+
int CEspacioUtilizable::GetMaxErrorY(int &y_cordinates[], int yref)
{
const int total = ArraySize(y_cordinates);
int max_err = INT_MIN;
int x, y;
for(int pos_x = 0; pos_x < total; pos_x++)
{
m_canvas.PixelDecompress(y_cordinates[pos_x], x, y);
const int cordc = PixelGetIndexInicioAbajo(x, yref); // Retorna Y
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete X
{
continue;
}
// De lo contraio si hay un obejto para colisionar, asi que verifiquemos si la cordanas lo atravisa
const int err = y - cordc;
if(err > max_err)
max_err = err;
}
if(max_err <= 0) // quiere decir que no hay error
return 0;
else
return max_err;
}
//+------------------------------------------------------------------+
bool CEspacioUtilizable::XColisionAdelante(const int &x_cordinates[], int xref) const
{
const int total = ArraySize(x_cordinates);
int x, y;
for(int pos_y = 0; pos_y < total; pos_y++)
{
m_canvas.PixelDecompress(x_cordinates[pos_y], x, y);
const int cordc = PixelXGetIndexInicioAdelante(xref, y); // Codenada del pixel mas cercano, Xref
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete Y
{
continue;
}
// De lo contraio si hay un obejto para colisionar, asi que verifiquemos si la cordanas lo atravisa
if(x >= cordc) // Colisiona
return true;
}
return false;
}
//+------------------------------------------------------------------+
int CEspacioUtilizable::GetMaxErrorXAdelente(int &x_cordinates[], int xref)
{
const int total = ArraySize(x_cordinates);
int max_err = INT_MIN;
int x, y;
for(int pos_y = 0; pos_y < total; pos_y++)
{
m_canvas.PixelDecompress(x_cordinates[pos_y], x, y);
const int cordc = PixelXGetIndexInicioAdelante(xref, y); // Codenada del pixel mas cercano
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete Y, POR LO QUE NO HAY ERROR
{
continue;
}
const int err = x - cordc;
if(err > max_err)
max_err = err;
}
if(max_err <= 0) // quiere decir que no hay error
return 0;
else
return max_err;
}
//+------------------------------------------------------------------+
bool CEspacioUtilizable::XColisionAtras(const int &x_cordinates[], int xref) const
{
const int total = ArraySize(x_cordinates);
int x, y;
for(int pos_y = 0; pos_y < total; pos_y++)
{
m_canvas.PixelDecompress(x_cordinates[pos_y], x, y);
const int cordc = PixelXGetIndexInicioAtras(x, y); // Codenada del pixel mas cercano
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete Y
{
continue;
}
if(x <= cordc) // Colisiona
return true;
}
return false;
}
//+------------------------------------------------------------------+
int CEspacioUtilizable::GetMaxErrorXAtras(int &x_cordinates[], int xref)
{
const int total = ArraySize(x_cordinates);
int max_err = INT_MIN;
int x, y;
for(int pos_y = 0; pos_y < total; pos_y++)
{
m_canvas.PixelDecompress(x_cordinates[pos_y], x, y);
const int cordc = PixelXGetIndexInicioAtras(x, y); // Codenada del pixel mas cercano
if(cordc == TET_ESPU_FUERA_NO_COLISION) // NO hay algo cercano para ete Y
{
continue;
}
const int err = cordc - x;
if(err > max_err)
max_err = err;
}
if(max_err <= 0) // quiere decir que no hay error
return 0;
else
return max_err;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
__forceinline bool CEspacioUtilizable::YCorDentro(int y) const
{
return (m_y_init <= y);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CEspacioUtilizable::AddFigure(CTetrisFigura *figura)
{
int px_ocuped[];
//--- Agregamos
figura.GetOcupatePixels(px_ocuped);
const uint total = px_ocuped.Size();
const uint total_px = m_pixeles_utilizados.Size();
//Print("Size: ", ArraySize(m_pixeles_utilizados));
/*
for(int i = 0; i < total; i++)
{
// Print("Index: ", px_ocuped[i]);
m_pixeles_utilizados[px_ocuped[i]] = true; // Pixel ocupaod
}
*/
//---
g_cl.BufferFromArray(cl_buffer_px_utilizados, m_pixeles_utilizados, 0, total_px, CL_MEM_ALLOC_HOST_PTR | CL_MEM_WRITE_ONLY);
g_cl.BufferFromArray(cl_buffer_pxocuped, px_ocuped, 0, total, CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_ONLY);
g_cl.SetArgumentBuffer(cl_kernel_tetris_asingocuped, kernel_tetris_asingocuped_arg_px_utilizados, cl_buffer_px_utilizados);
g_cl.SetArgumentBuffer(cl_kernel_tetris_asingocuped, kernel_tetris_asingocuped_arg_pxocuped, cl_buffer_pxocuped);
static const uint offset[1] = {0};
const uint worksize[1] = { total };
g_cl.Execute(cl_kernel_tetris_asingocuped, 1, offset, worksize);
g_cl.BufferRead(cl_buffer_px_utilizados, m_pixeles_utilizados, 0, 0, total_px);
//--- Chekaeamos lso espacios si hay uno por eliminar
// Chekeamos si se creo un nuevo desde el minimo
const int yinit = figura.Y2();
const int yend = figura.Y1();
int y_to_delete[];
for(int y = yinit; y <= yend; y--)
{
bool lc = true;
for(int x = m_x_init; x <= m_x_end; x++)
{
if(!PixelUtilizado(x, y)) // Si no esta fallo no hay colision en esta linea
{
lc = false;
break;
}
}
//---
if(lc)
{
y_to_delete.Push(y); // Y al final;
}
}
// Eliminamos
if(y_to_delete.Size() > 0)
{
//---
int x1 = m_x_init;
int y1 = yend;
int x2 = m_x_end;
int y2 = yinit;
//--- Graficamente
m_canvas.FillRectangle(x1, y1, x2, y2, m_clr_clean);
//--- Internamente
OnDeleteEspacio(x1, y1, x2, y2);
return (int)y_to_delete.Size();
}
return 0;
}
//+------------------------------------------------------------------+