Desafio xkcd: "Porcentagem da tela que é [x] colorida"

57

Então eu acho que todos nós provavelmente já vimos essa história em quadrinhos xkcd :

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

Isso pode ser muito geral ou muito difícil, não tenho certeza. Mas o desafio é criar um programa em qualquer idioma que crie uma janela que tenha pelo menos duas cores e exiba em inglês o percentual da tela de cada cor.

ex. A solução mais simples seria um fundo branco com letras pretas que diz "Porcentagem desta imagem em preto: [x]%. Porcentagem dessa imagem em branco: [y]%"

Você pode ficar tão louco ou simples quanto quiser; texto simples é uma solução válida, mas se você criar imagens interessantes, como nos quadrinhos xkcd, é ainda melhor! O vencedor será a solução mais divertida e criativa que conseguir mais votos. Então vá em frente e faça algo divertido e digno de xkcd! :)

Então, o que você acha? Soa como um desafio divertido? :)

Inclua uma captura de tela do seu programa em execução na sua resposta :)

WendiKidd
fonte
6
A "este programa tem 64 um, 4 B do, ... e 34 aspas no código-fonte" programa seria :-) mais interessante
John Dvorak
2
OK ... quais são os critérios objetivos para ganhar? Como você determina se alguma saída específica é válida? É suficiente que seja verdade e descreva uma propriedade de si mesma numericamente?
John Dvorak
@JanDvorak Oh, essa é boa! O programa do alfabeto é realmente o que me fez pensar nisso originalmente, mas não pensei em adicionar o elemento do código-fonte! Você deve postar isso como uma pergunta :) Sim, é suficiente que seja verdade e se descreva. Hmm, você está certo, porém, não pensei em como provar que os resultados finais estavam corretos. Vou precisar de uma maneira de contar todos os pixels de cada cor em uma imagem resultante, suponho. Eu vou investigar isso agora. (Desculpe a minha primeira pergunta tinha problemas ... Eu tentei, mas eu sou novo nisso Obrigado :)!)
WendiKidd
se truthiness e auto-referência são os critérios suficientes, aqui está o meu concorrente golfscript: "/.*/"(leia-se: [código fonte] não contém uma nova linha)
John Dvorak
@ JanDvorak Hmm, tentei o seu código aqui e a saída era a mesma do código, exceto sem as aspas. Talvez eu não esteja explicando isso direito, desculpe. Deve haver pelo menos duas cores geradas e, de alguma forma, em uma frase em inglês, a saída deve gerar palavras verdadeiras que descrevam qual porcentagem da tela cada uma das cores ocupa. Talvez essa fosse uma ideia boba. Eu pensei que seria divertido, mas ele pode não funcionar na prática :)
WendiKidd

Respostas:

35

Olmo

Ainda não vi ninguém usar essa brecha: demo

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

insira a descrição da imagem aqui

hoosierEE
fonte
3
Grande brecha!
Timtech 21/03
Eu amo isto!!! Pelo menos por enquanto, você recebe a marca de seleção dos pontos mais inteligentes. Adoro!
WendiKidd
13
A melhor parte é que ainda não tenho certeza de qual frase está falando sobre qual cor.
Brilliand
3
Ele view sourceé colocado lá pelo share-elm.com e não faz parte do JS / HTML compilado.
hoosierEE
11
@ML Isso depende do escopo da palavra "this". Programadores JavaScript entender ...
hoosierEE
37

JavaScript com HTML

Tentei reproduzir o quadrinho original com mais precisão. Uma captura de tela é feita usando a biblioteca html2canvas. Os números são calculados repetidamente, para que você possa redimensionar a janela ou até mesmo adicionar algo à página em tempo real.

Experimente online: http://copy.sh/xkcd-688.html

Aqui está uma captura de tela:

insira a descrição da imagem aqui

<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>
cópia de
fonte
Agradável!! Adoro as semelhanças com a história em quadrinhos xkcd e o fato de poder alterar o texto. Arrumado! : D
WendiKidd
11
trabalho impressionante oO
izabera 4/14
Astucioso ... mas acho que precisa se estabilizar para ser uma "solução". Ainda não pensou nisso completamente - mas como não há necessariamente uma solução para precisão arbitrária ao desenhar a partir de um conjunto limitado de glifos de dígitos, você terá que recuar na precisão se não puder ser resolvida com maior precisão você está tentando. Eu imagino que o uso de uma fonte monoespaçada que você pré-calcule os pixels em preto / branco também será necessário.
Dr. Rebmu
Você está usando três cores. Então, onde estão as porcentagens de cinza? ;)
ML
26

Processando, 222 caracteres

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

Eu sempre quis fazer minha própria versão dessa história em quadrinhos! A maneira mais simples (apenas?) De pensar em fazer isso foi tentativa e erro - desenhe algo, conte, desenhe novamente ...

Este programa estabelece uma porcentagem precisa após alguns segundos. Não é muito bonito, mas é interativo ; você pode redimensionar a janela e ela começará a recalcular.

Adicionadas algumas novas linhas para facilitar a leitura:

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;
}

Ele mostra apenas a porcentagem de pixels brancos; Devido ao antialiasing do texto, os pixels não brancos não são necessariamente pretos. Quanto mais tempo estiver em execução, mais tempo será necessário para se atualizar em um redimensionamento.

Editar:

Então, é um desafio de código; Eu meio que jogava golfe de qualquer maneira. Talvez eu pudesse adicionar algum tipo de gráfico mais tarde, mas o princípio geral permaneceria o mesmo. A interatividade é a parte legal, eu acho.

daniero
fonte
Muito agradável!! Eu acho que você recebe crédito extra pela interatividade; Eu me diverti redimensionando a janela! Muito legal :) E você é minha primeira resposta! Eu não sabia se alguém iria querer jogar, então obrigado. Você fez o meu dia. : D +1! (Estou curioso, por que diminui com o passar do tempo e se aproxima da porcentagem correta? Estou curioso com o que está acontecendo, nunca vi esse idioma antes. Estou vendo um muitas coisas novas bisbilhotando neste site!)
WendiKidd /
headdesk Exceto que acidentalmente esqueci de clicar no +1. Agora +1 ... haha. Desculpa!
WendiKidd
11
Você pode adicionar outra função que permita aos usuários desenhar nela com o mouse, para aumentar a interatividade.
AJMansfield
7
Sombra da caixa santa, Batman
Bojangles
Se você quiser golf, você pode usar background(-1)em vez debackground(255)
Kritixi Lithos
20

Grande desafio. Aqui está a minha solução. Tentei me aproximar o máximo possível do quadrinho original, até usei a fonte xkcd .

É um aplicativo WPF, mas eu costumava System.Drawingfazer as peças de desenho porque sou preguiçoso.

Conceito básico: No WPF, as janelas são Visuals, o que significa que podem ser renderizadas. Eu renderizo toda a instância do Windows em um bitmap, conto o preto e o total de preto ou branco (ignorando os cinzas na suavização da fonte e outras coisas) e também os conto para cada 3 da imagem (para cada painel). Então eu faço isso novamente em um temporizador. Atinge o equilíbrio dentro de um ou dois segundos.

Baixar:

MEGA Verifique sempre os arquivos baixados em busca de vírus, etc., etc.

Você precisará instalar a fonte acima no seu sistema, se quiser vê-la, caso contrário, é a fonte padrão do 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;
        }
    }
}

O código não está limpo, mas deve ser um pouco legível, desculpe.

Kiran Price
fonte
2
Uma entrada tardia, mas uma das melhores.
primo
14

C (com SDL e SDL_ttf): solução em escala de cinza

Aqui está uma solução que aproveita o formulário do gráfico de pizza para capturar o espectro completo de cores de pixel em escala de cinza, com um clock de menos de 100 linhas.

#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;
}

Como na minha solução anterior, o caminho para o arquivo da fonte precisa ser codificado na fonte ou adicionado ao comando build, por exemplo:

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

A saída do programa é assim:

Gráfico de pizza mostrando a distribuição de cores completa em escala de cinza de pixels

Este é divertido de assistir, porque toda a matemática retarda os redesenhos para onde você pode ver o programa se concentrar na solução estável. A primeira estimativa é descontrolada (já que a superfície começa totalmente preta) e depois diminui para o tamanho final após cerca de uma dúzia de iterações.

O código funciona fazendo uma contagem de população de cada cor de pixel na imagem atual. Se essa contagem de população não corresponder à última, redesenha a imagem. O código itera sobre cada pixel, mas transforma as coordenadas x, y em coordenadas polares, computando primeiro o raio (usando o centro da imagem como origem). Se o raio estiver dentro da área do gráfico de pizza, ele calculará o teta. O teta é facilmente dimensionado para a contagem da população, que determina a cor do pixel. Por outro lado, se o raio estiver na borda do gráfico de pizza, um valor anti-serrilhado será calculado para desenhar o círculo ao redor da parte externa do gráfico. As coordenadas polares facilitam tudo!

caixa de pão
fonte
Você está principalmente usando as floatversões de funções matemáticas de biblioteca, mas, em seguida, não deve fabsser fabsf?
Luser droog
Tecnicamente, talvez, mas fabs()é mais portátil.
breadbox
É verdade que tive problemas com o fato de ele não ser definido nos cabeçalhos, mesmo quando presente na biblioteca. Também há menos desempenho a ser obtido do que com os transcendentais. :)
luser Droog
10

C (com SDL e SDL_ttf)

Aqui está uma implementação muito simples, em cerca de 60 linhas 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 isso, você precisa definir FONTPATHpara apontar para um arquivo .ttf da fonte a ser usada:

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

Na maioria das máquinas Linux modernas, você pode usar o fc-matchutilitário para procurar locais de fontes, para que o comando compile se torne:

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

(É claro que você pode substituir a fonte solicitada pela sua favorita.)

O código solicita especificamente sem suavização de serrilhado, para que a janela contenha apenas pixels em preto e branco.

Por fim, fui inspirado pela solução elegante do @ daniero para permitir o redimensionamento de janelas. Você verá que, às vezes, o programa oscila entre as contagens, preso em uma órbita em torno de um atrator que nunca pode alcançar. Quando isso acontecer, redimensione a janela um pouco até que ela pare.

E, por solicitação, veja como fica quando eu o executo no meu sistema:

Esta imagem tem 3,36% de pixels pretos e 96,64% de pixels brancos.

Finalmente, acho que devo salientar, caso alguém aqui ainda não tenha visto, que o MAA publicou uma entrevista com Randall Munroe, na qual ele discute a criação do desenho animado # 688 com mais detalhes.

caixa de pão
fonte
11
Solução muito boa. Você poderia colocar algumas capturas de tela do programa em execução, seguindo a postagem de @ daniero? :)
Alex Brooks
+1, muito bom! Obrigado por adicionar a captura de tela :) E o link da entrevista é interessante, obrigado!
WendiKidd
9

insira a descrição da imagem aqui

A imagem é 100x100 e os números são exatos, e quero dizer exato - escolhi uma imagem de 10000 pixels para que as porcentagens pudessem ser expressas com duas casas decimais. O método era um pouco de matemática, um pouco de adivinhação e alguns trituradores de números em Python.

Como sabia antecipadamente que as porcentagens podiam ser expressas em 4 dígitos, contei quantos pixels pretos havia em cada um dos dígitos de 0 a 9, em Arial de 8 pixels de altura, que é o texto em que o texto está escrito. função rápida, weightque indica quantos pixels são necessários para escrever um determinado número, preenchidos com zeros para ter 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

pxé um array que mapeia os dígitos para o número de pixels necessários. Se B é o número de pixels pretos e W é o número de pixels brancos, temos B + W = 10000e precisamos:

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

De onde vieram as constantes? 423 é o número "inicial" de pixels pretos, o número de pixels pretos no texto sem os números. 9577 é o número de pixels brancos iniciais. Eu tive que ajustar a quantidade de pixels pretos iniciais várias vezes antes de conseguir obter constantes de forma que o sistema acima tivesse uma solução. Isso foi feito adivinhando e cruzando os dedos.

O sistema acima é terrivelmente não linear, portanto, obviamente, você pode esquecer de resolvê-lo simbolicamente, mas o que você pode fazer é fazer um loop entre todos os valores de B, definir W = 10000 - B e verificar as equações explicitamente.

>>> for b in range(10000 + 1):
...     if b == weight(b) + weight(10000 - b)+423: print b;
...
562
564
Jack M
fonte
Talvez faça uma imagem de 250 x 400 para obter 3 casas decimais e exibir mais texto enquanto isso.
Joe Z.
Solução muito boa, alguma matemática de força bruta sempre pode resolver esse tipo de problema!
CCP
6

QBasic

Porque nostalgia.

E porque eu realmente não conheço nenhuma biblioteca de imagens, é uma linguagem moderna.

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 direto de saída, contagem e repetição. O principal "interessante" é que o programa tenta aleatoriamente diferentes precisões para as porcentagens - descobri que nem sempre converge de outra maneira.

E a saída (testada em QB64 ):

QBasic metagraph

DLosc
fonte
3

AWK

... com netpbm e outros ajudantes

O arquivo '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"%"
        }
}

A corrida:

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

A imagem está escrita como 'x.pbm', converti-a para png para fazer o upload:

x.png

yeti
fonte