desafío xkcd: "Porcentaje de la pantalla que es [x] color"

57

Así que creo que probablemente todos hemos visto este cómic xkcd :

http://imgs.xkcd.com/comics/self_description.png:

Esto podría ser demasiado general o demasiado difícil, no estoy seguro. Pero el desafío es crear un programa en cualquier idioma que cree una ventana que tenga al menos 2 colores y muestre en palabras en inglés qué porcentaje de la pantalla es cada color.

ex. La solución más simple sería un fondo blanco con letras negras que diga "Porcentaje de esta imagen que es negra: [x]%. Porcentaje de esta imagen que es blanca: [y]%"

Puedes volverte tan loco o tan simple como quieras; el texto plano es una solución válida, pero si crea imágenes interesantes como en el cómic xkcd, ¡eso es aún mejor! El ganador será la solución más divertida y creativa que obtenga más votos. ¡Así que adelante y haga algo divertido y digno de xkcd! :)

¿Entonces, qué piensas? ¿Suena como un desafío divertido? :)

Incluya una captura de pantalla de su programa ejecutándose en su respuesta :)

WendiKidd
fuente
66
Un programa "este programa tiene 64 A's, 4 B's ... y 34 comillas dobles en el código fuente" sería más interesante :-)
John Dvorak
2
OK ... ¿cuáles son los criterios objetivos para ganar? ¿Cómo se determina si alguna salida específica es válida? ¿Es suficiente que sea cierto y que describa una propiedad de sí mismo numéricamente?
John Dvorak
@ JanDvorak ¡Oh, esa es buena! El programa del alfabeto es lo que me hizo pensar en esto originalmente, ¡pero no consideré agregarle el elemento del código fuente! Debe publicar eso como una pregunta :) Sí, es suficiente que sea cierto y se describa a sí mismo. Hmm, tienes razón, no pensé en cómo probaría que los resultados finales eran correctos. Supongo que necesitaré una forma de contar todos los píxeles de cada color en una imagen resultante. Iré a investigar eso ahora. (Lo siento, mi primera pregunta tuvo problemas ... ¡Lo intenté pero soy nuevo en esto! Gracias :))
WendiKidd
Si la verdad y la autorreferencia son los criterios suficientes, aquí está mi concursante de golfscript: "/.*/"(léase: [el código fuente] no contiene una nueva línea)
John Dvorak
@ JanDvorak Hmm, probé tu código aquí y el resultado fue el mismo que el código, excepto sin las comillas. Tal vez no estoy explicando esto bien, lo siento. Debe haber al menos 2 colores generados, y en alguna forma de oración en inglés, la salida debe generar palabras verdaderas que describan qué porcentaje de la pantalla ocupa cada uno de los colores. Tal vez esta fue una idea tonta. Pensé que sería divertido, pero podría no funcionar en la práctica :)
WendiKidd

Respuestas:

35

Olmo

Todavía no he visto a nadie usar esta escapatoria: demostración

import Color exposing (hsl)
import Graphics.Element exposing (..)
import Mouse
import Text
import Window

msg a = centered <| Text.color a (Text.fromString "half the screen is this color")

type Pos = Upper | Lower

screen (w,h) (x,y) = 
  let (dx,dy) = (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
      ang = hsl (atan2 dy dx) 0.7 0.5
      ang' = hsl (atan2 dx dy) 0.7 0.5
      box c = case c of
        Upper -> container w (h // 2) middle (msg ang) |> color ang'
        Lower -> container w (h // 2) middle (msg ang') |> color ang
  in  flow down [box Upper, box Lower]

main = Signal.map2 screen Window.dimensions Mouse.position

ingrese la descripción de la imagen aquí

hoosierEE
fuente
3
Gran escapatoria!
Timtech
¡¡¡Me encanta esto!!! Al menos por ahora, obtienes la marca de verificación para puntos ingeniosos. ¡Quiéralo!
WendiKidd
13
La mejor parte es que todavía no estoy seguro de qué oración habla de qué color.
Brilliand
3
El view sourcees puesto allí por share-elm.com y no es parte del JS / HTML compilado.
hoosierEE
1
@ML Eso depende del alcance de la palabra "esto". Los programadores de JavaScript entienden ...
hoosierEE
37

JavaScript con HTML

Traté de reproducir el cómic original con mayor precisión. Se toma una captura de pantalla usando la biblioteca html2canvas. Los números se calculan repetidamente, por lo que puede cambiar el tamaño de la ventana o incluso agregar algo a la página en tiempo real.

Pruébelo en línea: http://copy.sh/xkcd-688.html

Aquí hay una captura de pantalla:

ingrese la descripción de la imagen aquí

<html contenteditable>
<script src=http://html2canvas.hertzen.com/build/html2canvas.js></script>
<script>
onload = function() {
    setInterval(k, 750);
    k();
}
function k() {
    html2canvas(document.body, { onrendered: t });
}
function t(c) {
    z.getContext("2d").drawImage(c, 0, 0, 300, 150);
    c = c.getContext("2d").getImageData(0, 0, c.width, c.height).data;

    for(i = y = 0; i < c.length;) 
        y += c[i++];

    y /= c.length * 255;

    x.textContent = (y * 100).toFixed(6) + "% of this website is white";

    q = g.getContext("2d");

    q.fillStyle = "#eee";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,7,false);
    q.lineTo(75,75);
    q.fill();

    q.fillStyle = "#000";
    q.beginPath();
    q.moveTo(75, 75);
    q.arc(75,75,75,0,6.28319*(1-y),false);
    q.lineTo(75,75);
    q.fill();
}
</script>
<center>
<h2 id=x></h2>
<hr>
<table><tr>
<td>Fraction of<br>this website<br>which is white _/
<td><canvas width=150 id=g></canvas>
<td>&nbsp; Fraction of<br>- this website<br>&nbsp; which is black
</table>
<hr>
0
<canvas style="border-width: 0 0 1px 1px; border-style: solid" id=z></canvas>
<h4>Location of coloured pixels in this website</h4>
Copiar
fuente
¡¡Agradable!! Me encantan las similitudes con el cómic xkcd y el hecho de que puedo cambiar el texto. ¡Ordenado! : D
WendiKidd
1
impresionante trabajo oO
izabera
Ingenioso ... pero creo que tiene que estabilizarse para ser una "solución". No lo he pensado por completo, pero como no hay necesariamente una solución para la precisión arbitraria al dibujar a partir de un conjunto limitado de glifos de dígitos, tendrá que retroceder la precisión si no se puede resolver con la mayor precisión lo estas intentando Me imagino que también será necesario usar una fuente monoespacio que precalcule los píxeles en blanco y negro.
Dr. Rebmu
Está utilizando 3 colores, entonces, ¿dónde están los porcentajes para el gris? ;)
ML
26

Procesamiento, 222 caracteres

http://i.imgur.com/eQzMGzk.png

¡Siempre quise hacer mi propia versión de esa tira cómica! La forma más simple (¿solo?) Que pude pensar en hacer esto fue prueba y error: dibujar algo, contar, dibujar de nuevo ...

Este programa se conforma con un porcentaje exacto después de unos segundos. No es muy bonito, pero es interactivo ; puede cambiar el tamaño de la ventana y comenzará a recalcular.

Se agregaron algunas líneas nuevas para facilitar la lectura:

float s,S,n;
int i;
void draw(){
frame.setResizable(true);
background(255);
fill(s=i=0);
text(String.format("%.2f%% of this is white",S/++n*100),10,10);
loadPixels();
while(i<width*height)if(pixels[i++]==-1)s++;
S+=s/height/width;
}

Solo muestra el porcentaje de píxeles blancos; Debido al antialiasing del texto, los píxeles no blancos no son necesariamente negros. Cuanto más tiempo se esté ejecutando, más tiempo necesitará actualizarse en un cambio de tamaño.

Editar:

Entonces, es un desafío de código; De alguna manera lo jugué al golf. Tal vez podría agregar algún tipo de gráficos más tarde, pero el principio general seguiría siendo el mismo. La interactividad es la mejor parte, creo.

daniero
fuente
¡¡Muy agradable!! Creo que obtienes crédito extra por la interactividad; ¡Me divertí cambiando el tamaño de la ventana! Muy guay :) ¡Y tú eres mi primera respuesta! No sabía si alguien querría jugar, así que gracias. Me hiciste el día. : D +1! (Sin embargo, tengo curiosidad, ¿por qué se ralentiza a medida que pasa el tiempo y se acerca a alcanzar el porcentaje correcto? Tengo curiosidad por saber qué está pasando, nunca he visto este idioma antes. Estoy viendo un muchas cosas nuevas hurgando en este sitio!)
WendiKidd
headdesk Excepto que accidentalmente olvidé hacer clic en el +1. Ahora +1 ... jaja. ¡Lo siento!
WendiKidd
1
Puede agregar otra función que permita a los usuarios dibujar con el mouse para una mayor interactividad.
AJMansfield
77
Holy box shadow, Batman
Bojangles
Si quieres jugar al golf, puedes usarlo en background(-1)lugar debackground(255)
Kritixi Lithos el
20

Gran reto. Aquí está mi solución. Traté de acercarme lo más posible al cómic original, incluso usé la fuente xkcd .

Es una aplicación WPF, pero solía System.Drawinghacer las partes del dibujo porque soy vago.

Concepto básico: en WPF, las ventanas son Visuals, lo que significa que se pueden representar. Represento toda la instancia de Windows en un mapa de bits, cuento el negro y el blanco o negro total (ignorando los grises en el suavizado de fuente y demás) y también cuento estos para cada 3º de la imagen (para cada panel). Luego lo hago nuevamente en un temporizador. Alcanza el equilibrio en un segundo o dos.

Descargar:

MEGA Siempre revise los archivos que descarga en busca de virus, etc., etc.

Tendrá que instalar la fuente anterior en su sistema si desea verla; de lo contrario, es la predeterminada de WPF.

XAML:

<Window
 x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="xkcd: 688" Height="300" Width="1000" WindowStyle="ToolWindow">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="0.3*"/>
        </Grid.ColumnDefinitions>

        <Border BorderBrush="Black" x:Name="bFirstPanel" BorderThickness="3" Padding="10px" Margin="0 0 10px 0">
            <Grid>
                <Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Top">Fraction of this window that is white</Label>
                <Label FontSize="18" FontFamily="xkcd" VerticalAlignment="Bottom">Fraction of this window that is black</Label>
                <Image x:Name="imgFirstPanel"></Image>
            </Grid>
        </Border>
        <Border Grid.Column="1" x:Name="bSecondPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0">
            <Grid>
                <TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Amount of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>by panel:</TextBlock>
                <Image x:Name="imgSecondPanel"></Image>
            </Grid>
        </Border>
        <Border Grid.Column="2" x:Name="bThirdPanel" BorderBrush="Black" BorderThickness="3" Padding="10px" Margin="10px 0 0 0">
            <Grid>
                <TextBlock FontSize="18" FontFamily="xkcd" VerticalAlignment="Top" HorizontalAlignment="Left">Location of <LineBreak></LineBreak>black ink <LineBreak></LineBreak>in this window:</TextBlock>
                <Image x:Name="imgThirdPanel"></Image>
            </Grid>
        </Border>

    </Grid>
</Window>

Código:

using System;
using System.Drawing;
using System.Timers;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Brushes = System.Drawing.Brushes;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        private Timer mainTimer = new Timer();
        public MainWindow()
        {
            InitializeComponent();

            Loaded += (o1,e1) =>
                          {
                              mainTimer = new Timer(1000/10);
                              mainTimer.Elapsed += (o, e) => {
                                  try
                                  {
                                      Dispatcher.Invoke(Refresh);
                                  } catch(Exception ex)
                                  {
                                      // Nope
                                  }
                              };
                              mainTimer.Start();
                          };
        }

        private void Refresh()
        {
            var actualh = this.RenderSize.Height;
            var actualw = this.RenderSize.Width;

            var renderTarget = new RenderTargetBitmap((int) actualw, (int) actualh, 96, 96, PixelFormats.Pbgra32);
            var sourceBrush = new VisualBrush(this);

            var visual = new DrawingVisual();
            var context = visual.RenderOpen();

            // Render the window onto the target bitmap
            using (context)
            {
                context.DrawRectangle(sourceBrush, null, new Rect(0,0, actualw, actualh));
            }
            renderTarget.Render(visual);

            // Create an array with all of the pixel data
            var stride = (int) actualw*4;
            var data = new byte[stride * (int)actualh];
            renderTarget.CopyPixels(data, stride, 0);

            var blackness = 0f;
            var total = 0f;

            var blacknessFirstPanel = 0f;
            var blacknessSecondPanel = 0f;
            var blacknessThirdPanel = 0f;
            var totalFirstPanel = 0f;
            var totalSecondPanel = 0f;
            var totalThirdPanel = 0f;

            // Count all of the things
            for (var i = 0; i < data.Length; i += 4)
            {
                var b = data[i];
                var g = data[i + 1];
                var r = data[i + 2];

                if (r == 0 && r == g && g == b)
                {
                    blackness += 1;
                    total += 1;

                    var x = i%(actualw*4) / 4;

                    if(x < actualw / 3f)
                    {
                        blacknessFirstPanel += 1;
                        totalFirstPanel += 1;
                    } else if (x < actualw * (2f / 3f))
                    {
                        blacknessSecondPanel += 1;
                        totalSecondPanel += 1;
                    }
                    else if (x < actualw)
                    {
                        blacknessThirdPanel += 1;
                        totalThirdPanel += 1;
                    }
                } else if (r == 255 && r == g && g == b)
                {
                    total += 1;

                    var x = i % (actualw * 4) / 4;

                    if (x < actualw / 3f)
                    {
                        totalFirstPanel += 1;
                    }
                    else if (x < actualw * (2f / 3f))
                    {
                        totalSecondPanel += 1;
                    }
                    else if (x < actualw)
                    {
                        totalThirdPanel += 1;
                    }
                }
            }

            var black = blackness/total;

            Redraw(black, blacknessFirstPanel, blacknessSecondPanel, blacknessThirdPanel, blackness, renderTarget);
        }

        private void Redraw(double black, double firstpanel, double secondpanel, double thirdpanel, double totalpanels, ImageSource window)
        {
            DrawPieChart(black);
            DrawBarChart(firstpanel, secondpanel, thirdpanel, totalpanels);
            DrawImage(window);
        }

        void DrawPieChart(double black)
        {
            var w = (float)bFirstPanel.ActualWidth;
            var h = (float)bFirstPanel.ActualHeight;
            var padding = 0.1f;

            var b = new Bitmap((int)w, (int)h);
            var g = Graphics.FromImage(b);

            var px = padding*w;
            var py = padding*h;

            var pw = w - (2*px);
            var ph = h - (2*py);

            g.DrawEllipse(Pens.Black, px,py,pw,ph);

            g.FillPie(Brushes.Black, px, py, pw, ph, 120, (float)black * 360);

            g.DrawLine(Pens.Black, 30f, h * 0.1f, w / 2 + w * 0.1f, h / 2 - h * 0.1f);
            g.DrawLine(Pens.Black, 30f, h - h * 0.1f, w / 2 - w * 0.2f, h / 2 + h * 0.2f);

            imgFirstPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
        }

        void DrawBarChart(double b1, double b2, double b3, double btotal)
        {
            var w = (float)bFirstPanel.ActualWidth;
            var h = (float)bFirstPanel.ActualHeight;
            var padding = 0.1f;

            var b = new Bitmap((int)w, (int)h);
            var g = Graphics.FromImage(b);

            var px = padding * w;
            var py = padding * h;

            var pw = w - (2 * px);
            var ph = h - (2 * py);

            g.DrawLine(Pens.Black, px, py, px, ph+py);
            g.DrawLine(Pens.Black, px, py + ph, px+pw, py+ph);

            var fdrawbar = new Action<int, double>((number, value) =>
                {
                    var height = ph*(float) value/(float) btotal;
                    var width = pw/3f - 4f;

                    var x = px + (pw/3f)*(number-1);
                    var y = py + (ph - height);

                    g.FillRectangle(Brushes.Black, x, y, width, height);
                });

            fdrawbar(1, b1);
            fdrawbar(2, b2);
            fdrawbar(3, b3);

            imgSecondPanel.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
        }

        void DrawImage(ImageSource window)
        {
            imgThirdPanel.Source = window;
        }
    }
}

El código no está limpio, pero debería ser algo legible, lo siento.

Precio Kiran
fuente
2
Una entrada tardía, pero una de las mejores.
primo
14

C (con SDL y SDL_ttf): solución en escala de grises

Aquí hay una solución que aprovecha la forma del gráfico circular para capturar el espectro completo de colores de píxeles en escala de grises, registrando un poco menos de 100 líneas.

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    SDL_Surface *screen, *buffer, *caption;
    SDL_Color pal[256];
    SDL_Rect rect;
    SDL_Event event;
    TTF_Font *font;
    int levels[256], plev[256];
    Uint8 *p;
    float g;
    int cr, redraw, hoffset, h, n, v, w, x, y;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 0, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 24);
    buffer = 0;
    for (;;) {
        if (!buffer) {
            buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h,
                                          8, 0, 0, 0, 0);
            for (n = 0 ; n < 256 ; ++n)
                pal[n].r = pal[n].g = pal[n].b = n;
            SDL_SetColors(buffer, pal, 0, 256);
        }
        memcpy(plev, levels, sizeof levels);
        memset(levels, 0, sizeof levels);
        SDL_LockSurface(buffer);
        p = buffer->pixels;
        for (h = 0 ; h < buffer->h ; ++h) {
            for (w = 0 ; w < buffer->w ; ++w)
                ++levels[p[w]];
            p += buffer->pitch;
        }
        for (n = 1 ; n < 256 ; ++n)
            levels[n] += levels[n - 1];
        redraw = memcmp(levels, plev, sizeof levels);
        if (redraw) {
            SDL_UnlockSurface(buffer);
            SDL_FillRect(buffer, NULL, 255);
            caption = TTF_RenderText_Shaded(font,
                        "Distribution of pixel color in this image",
                        pal[0], pal[255]);
            rect.x = (buffer->w - caption->w) / 2;
            rect.y = 4;
            hoffset = caption->h + 4;
            SDL_BlitSurface(caption, NULL, buffer, &rect);
            SDL_FreeSurface(caption);
            SDL_LockSurface(buffer);
            cr = buffer->h - hoffset;
            cr = (cr < buffer->w ? cr : buffer->w) / 2 - 4;
            p = buffer->pixels;
            for (h = 0 ; h < buffer->h ; ++h) {
                y = h - (screen->h + hoffset) / 2;
                for (w = 0 ; w < buffer->w ; ++w) {
                    x = w - buffer->w / 2;
                    g = sqrtf(x * x + y * y);
                    if (g < cr - 1) {
                        g = atanf((float)y / (x + g));
                        v = levels[255] * (g / M_PI + 0.5);
                        for (n = 0 ; n < 255 && levels[n] < v ; ++n) ;
                        p[w] = n;
                    } else if (g < cr + 1) {
                        p[w] = (int)(128.0 * fabs(g - cr));
                    }
                }
                p += buffer->pitch;
            }
        }
        SDL_UnlockSurface(buffer);
        SDL_BlitSurface(buffer, NULL, screen, NULL);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (redraw ? SDL_PollEvent(&event) : SDL_WaitEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE) {
                SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
                SDL_FreeSurface(buffer);
                buffer = 0;
            }
        }
    }
    SDL_Quit();
    TTF_Quit();
    return 0;
}

Al igual que con mi solución anterior, la ruta al archivo de fuente debe codificarse en la fuente o agregarse al comando de compilación, por ejemplo:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs` -lm

La salida del programa se ve así:

Gráfico circular que muestra la distribución completa del color de píxeles en escala de grises

Este es divertido de ver, porque todas las matemáticas ralentizan los redibujos a donde puede ver el programa enfocarse en la solución estable. La primera estimación está muy alejada (ya que la superficie comienza completamente negra), y luego se reduce al tamaño final después de aproximadamente una docena de iteraciones.

El código funciona tomando un recuento de población de cada color de píxel en la imagen actual. Si este recuento de población no coincide con el último, redibuja la imagen. El código itera sobre cada píxel, pero transforma las coordenadas x, y en coordenadas polares, calculando primero el radio (usando el centro de la imagen como origen). Si el radio está dentro del área del gráfico circular, entonces calcula el theta. El theta se escala fácilmente a los recuentos de población, lo que determina el color del píxel. Por otro lado, si el radio está justo en el borde del gráfico circular, se calcula un valor suavizado para dibujar el círculo alrededor del exterior del gráfico. ¡Las coordenadas polares facilitan todo!

caja de pan
fuente
Usted está utilizando sobre todo las floatversiones de funciones matemáticas-biblioteca, pero entonces no debe fabsser fabsf?
luser droog
Técnicamente, tal vez, pero fabs()es más portátil.
breadbox
Es cierto que he tenido problemas con que no se defina en los encabezados incluso cuando está presente en la biblioteca. También hay menos rendimiento que ganar que con los trascendentales. :)
luser droog
10

C (con SDL y SDL_ttf)

Aquí hay una implementación muy simple, en aproximadamente 60 líneas de código C:

#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"

int main(void)
{
    char buf[64];
    SDL_Surface *screen, *text;
    SDL_Rect rect;
    SDL_Color black;
    SDL_Event event;
    TTF_Font *font;
    Uint32 blackval, *p;
    int size, b, prevb, h, i;

    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    screen = SDL_SetVideoMode(640, 480, 32, SDL_ANYFORMAT | SDL_RESIZABLE);
    font = TTF_OpenFont(FONTPATH, 32);
    black.r = black.g = black.b = 0;
    blackval = SDL_MapRGB(screen->format, 0, 0, 0);

    b = -1;
    for (;;) {
        prevb = b;
        b = 0;
        SDL_LockSurface(screen);
        p = screen->pixels;
        for (h = screen->h ; h ; --h) {
            for (i = 0 ; i < screen->w ; ++i)
                b += p[i] == blackval;
            p = (Uint32*)((Uint8*)p + screen->pitch);
        }
        SDL_UnlockSurface(screen);
        size = screen->w * screen->h;
        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
        sprintf(buf, "This image is %.2f%% black pixels", (100.0 * b) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2 - text->h;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        sprintf(buf, "and %.2f%% white pixels.", (100.0 * (size - b)) / size);
        text = TTF_RenderText_Solid(font, buf, black);
        rect.x = (screen->w - text->w) / 2;
        rect.y = screen->h / 2;
        SDL_BlitSurface(text, NULL, screen, &rect);
        SDL_FreeSurface(text);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
        if (b == prevb ? SDL_WaitEvent(&event) : SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_VIDEORESIZE)
                SDL_SetVideoMode(event.resize.w, event.resize.h, 32,
                                 SDL_ANYFORMAT | SDL_RESIZABLE);
        }
    }

    TTF_Quit();
    SDL_Quit();
    return 0;
}

Para compilar esto, debe definir FONTPATHque apunte a un archivo .ttf de la fuente a usar:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH='"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf"'
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

En la mayoría de las máquinas Linux modernas, puede usar la fc-matchutilidad para buscar ubicaciones de fuentes, por lo que el comando de compilación se convierte en:

gcc -Wall -o xkcdgolf `sdl-config --cflags`
    -DFONTPATH=`fc-match --format='"%{file}"' :bold`
    xkcdgolf.c -lSDL_ttf `sdl-config --libs`

(Por supuesto, puede reemplazar la fuente solicitada con su favorito personal).

El código específicamente no solicita suavizado, por lo que la ventana contiene solo píxeles en blanco y negro.

Finalmente, me inspiró la elegante solución de @ daniero para permitir el cambio de tamaño de la ventana. Verá que a veces el programa oscila entre conteos, atrapado en una órbita alrededor de un atractor que nunca puede alcanzar. Cuando eso suceda, simplemente cambie el tamaño de la ventana un poco hasta que se detenga.

Y, por solicitud, así es como se ve cuando lo ejecuto en mi sistema:

Esta imagen es 3.36% de píxeles negros y 96.64% de píxeles blancos.

Finalmente, creo que debería señalar, en caso de que alguien aquí no lo haya visto, que el MAA publicó una entrevista con Randall Munroe en la que analiza la realización de la caricatura # 688 con cierto detalle.

caja de pan
fuente
1
Muy buena solución. ¿Podrías incluir algunas capturas de pantalla del programa en ejecución, después de la publicación de @ daniero? :)
Alex Brooks
+1, muy lindo! Gracias por agregar la captura de pantalla :) Y el enlace de la entrevista es interesante, ¡gracias!
WendiKidd
9

ingrese la descripción de la imagen aquí

La imagen es 100x100 y los números son exactos, y quiero decir exacto: elegí una imagen de 10000 píxeles para que los porcentajes pudieran expresarse con dos decimales. El método fue un poco de matemática, un poco de adivinanzas y algunos cálculos numéricos en Python.

Como sabía de antemano que los porcentajes podían expresarse en 4 dígitos, conté cuántos píxeles negros había en cada uno de los dígitos del 0 al 9, en Arial de 8 píxeles de alto, que es en lo que está escrito el texto. Escribí un función rápida weightque le indica cuántos píxeles se necesitan para escribir un número dado, rellenado con ceros a la izquierda para tener 4 dígitos:

def weight(x):
    total = 4 * px[0]
    while x > 0:
       total = total - px[0] + px[x % 10]
       x = x / 10
    return total

pxes una matriz de asignación de dígitos al número de píxeles requeridos. Si B es el número de píxeles negros y W es el número de píxeles blancos, tenemos B + W = 10000y necesitamos:

B = 423 + weight(B) + weight(W)
W = 9577 - weight(B) - weight(W)

¿De dónde vienen las constantes? 423 es el número "inicial" de píxeles negros, el número de píxeles negros en el texto sin los números. 9577 es el número de píxeles blancos iniciales. Tuve que ajustar la cantidad de píxeles negros iniciales varias veces antes de lograr constantes de manera que el sistema anterior incluso tuviera una solución. Esto se hizo adivinando y cruzando los dedos.

El sistema anterior es terriblemente no lineal, por lo que obviamente puede olvidarse de resolverlo simbólicamente, pero lo que puede hacer es recorrer cada valor de B, establecer W = 10000 - B y verificar las ecuaciones explícitamente.

>>> for b in range(10000 + 1):
...     if b == weight(b) + weight(10000 - b)+423: print b;
...
562
564
Jack M
fuente
Tal vez haga una imagen de 250 x 400 para que pueda llegar a 3 decimales y mostrar más texto mientras tanto.
Joe Z.
Muy buena solución, ¡algunas matemáticas de fuerza bruta siempre pueden resolver este tipo de problemas!
CCP
6

QBasic

Porque nostalgia.

Y porque realmente no conozco ninguna biblioteca de imágenes, es lenguajes modernos.

SCREEN 9

CONST screenWidth = 640
CONST screenHeight = 350
CONST totalPixels# = screenWidth * screenHeight

accuracy = 6

newWhite# = 0
newGreen# = 0
newBlack# = totalPixels#

DO
    CLS
    white# = newWhite#
    green# = newGreen#
    black# = newBlack#

    ' Change the precision of the percentages every once in a while
    ' This helps in finding values that converge
    IF RND < .1 THEN accuracy = INT(RND * 4) + 2
    format$ = "###." + LEFT$("######", accuracy) + "%"

    ' Display text
    LOCATE 1
    PRINT "Percentage of the screen which is white:";
    PRINT USING format$; pct(white#)
    LOCATE 4
    PRINT white#; "/"; totalPixels#; "pixels"
    LOCATE 7
    PRINT "Percentage of the screen which is black:";
    PRINT USING format$; pct(black#)
    LOCATE 10
    PRINT black#; "/"; totalPixels#; "pixels"
    LOCATE 13
    PRINT "Percentage of the screen which is green:";
    PRINT USING format$; pct(green#)
    LOCATE 16
    PRINT green#; "/"; totalPixels#; "pixels"

    ' Display bar graphs
    LINE (0, 16)-(pct(white#) / 100 * screenWidth, 36), 2, BF
    LINE (0, 100)-(pct(black#) / 100 * screenWidth, 120), 2, BF
    LINE (0, 184)-(pct(green#) / 100 * screenWidth, 204), 2, BF

    newBlack# = pixels#(0)
    newGreen# = pixels#(2)
    newWhite# = pixels#(15)
LOOP UNTIL black# = newBlack# AND white# = newWhite# AND green# = newGreen#

' Wait for user keypress before ending program: otherwise the "Press any
' key to continue" message would instantly make the results incorrect!
x$ = INPUT$(1)


FUNCTION pixels# (colr)
' Counts how many pixels of the given color are on the screen

pixels# = 0

FOR i = 0 TO screenWidth - 1
    FOR j = 0 TO screenHeight - 1
        IF POINT(i, j) = colr THEN pixels# = pixels# + 1
    NEXT j
NEXT i

END FUNCTION

FUNCTION pct (numPixels#)
' Returns percentage, given a number of pixels

pct = numPixels# / totalPixels# * 100

END FUNCTION

Método bastante sencillo de salida-conteo-repetición. Lo principal "interesante" es que el programa intenta aleatoriamente diferentes precisiones para los porcentajes; descubrí que no siempre convergía de otra manera.

Y la salida (probado en QB64 ):

Q Metagrafía básica

DLosc
fuente
3

AWK

... con netpbm y otros ayudantes

El archivo 'x':

BEGIN {
        FS=""
        n++
        while(n!=m) {
                c="printf '%s\n' '"m"% black pixels'"
                c=c" '"100-m"% white pixels'"
                c=c" | pbmtext -space 1 -lspace 1 | pnmtoplainpnm | tee x.pbm"
                n=m
                delete P
                nr=0
                while(c|getline==1) if(++nr>2) for(i=1;i<=NF;i++) P[$i]++
                close(c)
                m=100*P[1]/(P[0]+P[1])
                print m"%"
        }
}

La carrera:

$ awk -f x
4.44242%
5.2424%
5.04953%
5.42649%
5.27746%
5.1635%
5.15473%
5.20733%
5.20733%

La imagen está escrita como 'x.pbm', la convertí a png para cargar:

x.png

yeti
fuente