Código de máquina x86, 34 bytes
51
31 D2
AD
F7 D0
25 C0 C0 C0 00
75 01
42
E2 F3
C1 E2 03
DB 04 24
52
DB 04 24
DE F1
DB 1C 24
58
5A
C3
Estos bytes de código definen una función que toma una entrada de mapa de bits y devuelve un valor entero que indica sus octas. Como en C , las matrices (como los mapas de bits) se representan como un puntero al primer elemento y un tamaño / longitud. Por lo tanto, esta función toma dos parámetros: el número total de píxeles en el mapa de bits (filas × columnas) y un puntero al mapa de bits en sí.
Este código utiliza una convención de llamada personalizada basada en registros, donde el puntero de mapa de bits se pasa en el ESI
registro y el tamaño del mapa de bits se pasa en el ECX
registro. El resultado (oktas) es, como siempre, devuelto EAX
.
Como ya se indicó anteriormente, la entrada se toma como un mapa de bits. Específicamente, se utiliza un formato de 32 bpp, en un formato little-endian, pero se ignora el canal alfa (byte de orden superior). Esto simplifica muchas cosas, permitiéndonos simplemente recorrer cada píxel y verificar su valor de color RGB de 32 bits. Aquí también se usa una optimización inteligente. En lugar de aislar cada componente de color y verificar si es> = 192, simplemente enmascaramos el valor completo de 32 bits por 0xC0C0C0 y probamos si el resultado es> = 0xC0C0C0. Esto se evaluará como verdadero para todos los colores "nubosos" y falso para todos los colores "celestes" (que no sean nubes). Bueno, ¡ pensé que era inteligente! :-) Ciertamente ahorra una gran cantidad de bytes.
Por lo tanto, para probar este código, deberá convertir las imágenes de entrada a mapas de bits de 32 bpp. No puede usar Windows Paint para esto, ya que admite un máximo de 24 bits por píxel. Sin embargo, hay varias otras soluciones de software que pueden hacerlo, como Adobe Photoshop. Utilicé esta herramienta gratuita , que convierte un PNG a un BMP de 32 bpp en Windows, lo que significa que solo necesita convertir de JPEG a PNG (que Paint puede hacer).
Otros supuestos que considero son eminentemente razonables:
- Se supone que el mapa de bits tiene un tamaño mayor que 0 ( es decir , se supone que contiene al menos un píxel). Esto es razonable porque, cuando el cielo es nulo, tenemos mayores problemas que la meteorología.
- Se
DF
supone que la bandera de dirección ( ) es clara, de modo que iteraremos correctamente a través del mapa de bits utilizando la LODSD
instrucción. Esta es la misma suposición hecha por la mayoría de las convenciones de llamadas x86, por lo que parece justo. Si no le gusta, agregue 1 byte al conteo para una CLD
instrucción.
- Se supone que el modo de redondeo para la FPU x87 se establece en redondear a la par más cercana. Esto garantiza que obtengamos el comportamiento correcto cuando convertimos el número de octas de un punto flotante temporal al resultado entero final, como se verifica en el caso de prueba # 4. Esta suposición es razonable porque este es el estado predeterminado para la FPU y debe mantenerse incluso en el código C (donde el truncamiento es el comportamiento de redondeo predeterminado, lo que obliga a los compiladores que desean cumplir con los estándares a generar código ineficiente que cambia el redondeo modo, realiza la conversión y luego vuelve a cambiar el modo de redondeo).
Mnemónicos de ensamblaje sin golf:
; int ComputeOktas(void* bmpBits /* ESI */,
; uint32_t bmpSize /* ECX */);
push ecx ; save size on stack
xor edx, edx ; EDX = 0 (cloudy pixel counter)
CheckPixels:
lodsd ; EAX = DS:[ESI]; ESI += 4
not eax
and eax, 0x00C0C0C0
jnz NotCloudy
inc edx
NotCloudy:
loop CheckPixels ; ECX -= 1; loop if ECX > 0
shl edx, 3 ; counter *= 8
fild DWORD PTR [esp] ; load original size from stack
push edx
fild DWORD PTR [esp] ; load counter from stack
fdivrp st(1), st(0) ; ST(0) = counter*8 / size
fistp DWORD PTR [esp] ; convert to integer, rounding to nearest even
pop eax ; load result
pop edx
ret
¿Seguramente no has llegado hasta aquí y todavía te preguntas cómo funciona el código? :-)
Bueno, es bastante simple. Simplemente iteramos a través del mapa de bits un valor de 32 bits a la vez, verificando si ese valor RGB de píxel está "nublado" o "no nublado". Si está nublado, incrementamos nuestro contador pre-cero. Al final, calculamos: píxeles nublados ⁄ píxeles totales × 8
(que es equivalente a: píxeles nublados ⁄ píxeles totales ÷ 0.125).
No puedo incluir un enlace TIO para esto debido a la necesidad de imágenes de entrada. Sin embargo, puedo proporcionarle el arnés que utilicé para probar esto en Windows:
#include <stdio.h>
#include <assert.h>
#include <Windows.h>
int main()
{
// Load bitmap as a DIB section under Windows, ensuring device-neutrality
// and providing us direct access to its bits.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
TEXT("C:\\...\\test1.bmp"),
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
assert(hBitmap != NULL);
// Get the bitmap's bits and attributes.
DIBSECTION dib;
GetObject(hBitmap, sizeof(dib), &dib);
assert(dib.dsBm.bmBitsPixel == 32);
uint32_t cx = dib.dsBm.bmWidth;
uint32_t cy = abs(dib.dsBm.bmHeight);
uint32_t sz = cx * cy;
assert(sz > 0);
int oktas = ComputeOktas(sz, dib.dsBm.bmBits);
printf("%d\n", oktas);
return 0;
}
¡Pero ten cuidado con esto! Como se definió anteriormente, ComputeOktas
utiliza una convención de llamada personalizada, que un compilador de C no respetará. Debe agregar código en la parte superior del procedimiento del lenguaje ensamblador para cargar valores de la pila en los registros esperados, por ejemplo :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]