xkcd challenge: "Percentuale dello schermo che è [x] color"


57

Quindi penso che probabilmente tutti abbiamo visto questo fumetto di xkcd :

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

Questo potrebbe essere troppo generico o troppo difficile, non ne sono sicuro. Ma la sfida è quella di creare un programma in qualsiasi lingua che crei una finestra che abbia almeno 2 colori e visualizzi in parole inglesi quale percentuale dello schermo è ogni colore.

ex. La soluzione più semplice sarebbe uno sfondo bianco con lettere nere che dicano "Percentuale di questa immagine che è nera: [x]%. Percentuale di questa immagine che è bianca: [y]%"

Puoi diventare matto o semplice come vuoi; il testo semplice è una soluzione valida ma se crei immagini interessanti come nel fumetto di xkcd è ancora meglio! Il vincitore sarà la soluzione più divertente e creativa che ottiene il maggior numero di voti. Quindi vai avanti e fai qualcosa di divertente e degno di xkcd! :)

Allora, cosa ne pensi? Sembra una sfida divertente? :)

Includi uno screenshot del tuo programma in esecuzione nella tua risposta :)


6
Un programma "questo programma ha 64 A, 4 B, ... e 34 virgolette doppie nel codice sorgente" sarebbe più interessante :-)
John Dvorak,

2
OK ... quali sono i criteri vincenti oggettivi? Come si determina se un output specifico è valido? È sufficiente che sia vero e descriva una proprietà di se stessa numericamente?
John Dvorak,

@JanDvorak Oh, è una buona idea! Il programma alfabetico è in realtà ciò che mi ha fatto pensare in origine, ma non ho considerato di aggiungere l'elemento del codice sorgente! Dovresti postarlo come una domanda :) Sì, è sufficiente che sia vero e descriva se stesso. Hmm, hai ragione però, non ho pensato a come avrei dimostrato che i risultati finali erano corretti. Avrò bisogno di un modo per contare tutti i pixel di ciascun colore in un'immagine risultante, suppongo. Vado a indagare su questo ora. (Mi dispiace che la mia prima domanda abbia avuto problemi ... Ci ho provato ma sono nuovo! Grazie :)
WendiKidd,

se la verità e l'autoreferenzialità sono i criteri sufficienti, ecco il mio concorrente di golfscript: "/.*/"(leggi: [il codice sorgente] non contiene una nuova riga)
John Dvorak,

@JanDvorak Hmm, ho provato qui il tuo codice e l'output era lo stesso del codice tranne che tra virgolette. Forse non lo sto spiegando bene, scusa. Devono essere generati almeno 2 colori e, in qualche forma di una frase inglese, l'output deve generare parole vere che descrivono quale percentuale dello schermo occupa ciascuno dei colori. Forse è stata un'idea sciocca. Ho pensato che sarebbe stato divertente, ma potrebbe non funzionare in pratica :)
WendiKidd,

Risposte:


35

Olmo

Non ho ancora visto nessuno usare questa scappatoia: 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

inserisci qui la descrizione dell'immagine


3
Ottima scappatoia!
Timtech,

Amo questo!!! Almeno per ora, ottieni il segno di spunta per i punti intelligenti. Lo adoro!
WendiKidd,

13
La parte migliore è che non sono ancora sicuro di quale frase parli di quale colore.
Brilliand,

3
L' view sourceè messo lì da share-elm.com e non fa parte della compilato JS / HTML.
hoosierEE,

1
@ML Dipende dall'ambito della parola "questo". I programmatori JavaScript comprendono ...
hoosierEE

37

JavaScript con HTML

Ho provato a riprodurre il fumetto originale in modo più preciso. Viene presa una schermata usando la libreria html2canvas. I numeri vengono calcolati ripetutamente, quindi puoi ridimensionare la finestra o persino aggiungere qualcosa alla pagina in tempo reale.

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

Ecco uno screenshot:

inserisci qui la descrizione dell'immagine

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

Bello!! Adoro le somiglianze con il fumetto di xkcd e il fatto che posso cambiare il testo. ! Neat : D
WendiKidd,

1
lavoro impressionante oO
izabera

Nifty ... ma penso che debba stabilizzarsi per essere una "soluzione". Non ci ho pensato del tutto, ma poiché non esiste necessariamente una soluzione per la precisione arbitraria quando si attinge da un insieme limitato di glifi delle cifre, sarà necessario ripristinare la precisione se non può essere risolta con la massima precisione ci stai provando. Immagino che sarà necessario usare anche un carattere monospace per pre-calcolare i pixel bianco e nero.
Dr. Rebmu,

Stai usando 3 colori, quindi dove sono le percentuali per il grigio? ;)
ML

26

Elaborazione, 222 caratteri

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

Ho sempre desiderato creare la mia versione di quel fumetto! Il modo più semplice (solo?) Che mi viene in mente di fare questo è stato tentativi ed errori: disegnare qualcosa, contare, disegnare di nuovo ...

Questo programma si accontenta di una percentuale precisa dopo pochi secondi. Non è molto carino, ma è interattivo ; puoi ridimensionare la finestra e inizierà a ricalcolare.

Aggiunte alcune nuove righe per la leggibilità:

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

Mostra solo la percentuale di pixel bianchi; A causa dell'antialias del testo, i pixel non bianchi non sono necessariamente neri. Più a lungo è in esecuzione, più tempo sarà necessario per aggiornarsi su un ridimensionamento.

Modificare:

Quindi, è una sfida al codice; In qualche modo l'ho giocato a golf. Forse potrei aggiungere una sorta di grafici in seguito, ma il principio generale rimarrebbe lo stesso. L'interattività è la parte ordinata che penso.


Molto bella!! Penso che ottieni credito extra per l'interattività; Mi sono divertito a ridimensionare la finestra! Molto bello :) E tu sei la mia prima risposta in assoluto! Non sapevo se qualcuno avrebbe voluto giocare, quindi grazie. Mi hai migliorato la giornata. : D +1! (Sono curioso però, perché rallenta col passare del tempo e si avvicina al raggiungimento della percentuale corretta? Sono solo curioso di sapere cosa sta succedendo, non ho mai visto questa lingua prima. Sto vedendo un un sacco di roba nuova frugando in questo sito!)
WendiKidd,

headdesk Tranne che ho accidentalmente dimenticato di fare clic su +1. Ora +1 ... haha. Scusate!
WendiKidd,

1
È possibile aggiungere un'altra funzione che consente agli utenti di disegnarci sopra con il mouse, per una maggiore interattività.
AJMansfield,

7
Holy box shadow, Batman
Bojangles,

Se vuoi giocare a golf, puoi usare background(-1)invece dibackground(255)
Kritixi Lithos il

20

Grande sfida. Ecco la mia soluzione Ho provato ad avvicinarmi il più possibile al fumetto originale, ho persino usato il carattere xkcd .

È un'applicazione WPF, ma facevo System.Drawingle parti del disegno perché sono pigro.

Concetto di base: in WPF le finestre sono Visuals, il che significa che possono essere renderizzate. Rendo l'intera istanza di Windows su una bitmap, conto il bianco e nero totale e nero (ignorando i grigi nella levigatura e roba del carattere) e li conto anche per ogni 3 ° dell'immagine (per ogni pannello). Quindi lo faccio di nuovo con un timer. Raggiunge l'equilibrio entro un secondo o due.

Scaricare:

MEGA Controlla sempre i file scaricati per virus, ecc., Ecc.

Dovrai installare il carattere sopra nel tuo sistema se vuoi vederlo, altrimenti è quello predefinito di 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>

Codice:

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

Il codice non viene ripulito, ma dovrebbe essere in qualche modo leggibile, mi dispiace.


2
Un ingresso in ritardo, ma uno dei migliori.
primo

14

C (con SDL e SDL_ttf): soluzione in scala di grigi

Ecco una soluzione che sfrutta il modulo del grafico a torta per acquisire l'intero spettro dei colori dei pixel in scala di grigi, con un clock di poco meno di 100 righe.

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

Come per la mia precedente soluzione, il percorso del file del font deve essere codificato nel codice sorgente o aggiunto al comando build, ad esempio:

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

L'output del programma è simile al seguente:

Grafico a torta che mostra la distribuzione del colore dei pixel in scala di grigi completa

Questo è divertente da guardare, perché tutta la matematica rallenta i ridisegni in cui è possibile vedere il programma a zero sulla soluzione stabile. La prima stima è selvaggiamente (poiché la superficie inizia completamente nera), quindi si riduce alla dimensione finale dopo circa una dozzina di iterazioni.

Il codice funziona prendendo un conteggio della popolazione di ciascun colore di pixel nell'immagine corrente. Se questo conteggio della popolazione non corrisponde all'ultimo, ridisegna l'immagine. Il codice scorre su ogni pixel, ma trasforma le coordinate x, y in coordinate polari, calcolando prima il raggio (usando il centro dell'immagine come origine). Se il raggio si trova nell'area del grafico a torta, calcola il theta. Il theta viene facilmente ridimensionato in base al conteggio della popolazione, che determina il colore dei pixel. D'altra parte, se il raggio si trova proprio sul bordo del grafico a torta, viene calcolato un valore con antialiasing per disegnare il cerchio attorno al grafico esterno. Le coordinate polari rendono tutto facile!


Stai per lo più utilizzano le floatversioni di funzioni matematiche-biblioteca, ma poi non dovrebbe fabsessere fabsf?
Luser droog

Tecnicamente, forse, ma fabs()è più portatile.
breadbox,

È vero, ho avuto problemi con quello che non veniva definito nelle intestazioni anche quando presente nella libreria. Inoltre ci sono meno prestazioni da ottenere rispetto ai trascendentali. :)
luser droog

10

C (con SDL e SDL_ttf)

Ecco un'implementazione molto semplice, in circa 60 righe di codice 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;
}

Per compilare questo, è necessario definire FONTPATHper puntare a un file .ttf del carattere da utilizzare:

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

Sulla maggior parte delle macchine Linux moderne è possibile utilizzare l' fc-matchutilità per cercare le posizioni dei caratteri, quindi il comando di compilazione diventa:

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

(Ovviamente puoi sostituire il carattere richiesto con il tuo preferito personale.)

Il codice non richiede specificamente l'antialiasing, pertanto la finestra contiene solo pixel bianchi e neri.

Infine, sono stato ispirato dall'elegante soluzione di @ daniero per consentire il ridimensionamento delle finestre. Vedrai che a volte il programma oscilla tra i conteggi, bloccato in un'orbita attorno a un attrattore che non può mai raggiungere. Quando ciò accade, ridimensiona leggermente la finestra fino a quando non si ferma.

E, per richiesta, ecco come appare quando lo eseguo sul mio sistema:

Questa immagine ha il 3,36% di pixel neri e il 96,64% di pixel bianchi.

Infine, sento che dovrei sottolineare, nel caso qualcuno qui non l'abbia già visto, che il MAA abbia pubblicato un'intervista con Randall Munroe in cui discute in dettaglio la realizzazione del cartone animato # 688.


1
Soluzione molto bella. Potresti forse inserire alcuni screenshot del programma in esecuzione, a seguito del post di @ daniero? :)
Alex Brooks,

+1, molto carino! Grazie per aver aggiunto lo screenshot :) E il link dell'intervista è interessante, grazie!
WendiKidd,

9

inserisci qui la descrizione dell'immagine

L'immagine è 100x100 e i numeri sono esatti, e intendo esattamente - Ho scelto un'immagine da 10000 pixel in modo che le percentuali possano essere espresse con due cifre decimali. Il metodo era un po 'di matematica, un po' di ipotesi e un po 'di scricchiolii in Python.

Dato che sapevo in anticipo che le percentuali potevano essere espresse in 4 cifre, ho contato quanti pixel neri erano in ciascuna delle cifre da 0 a 9, in Arial alto 8 pixel, che è ciò in cui è scritto il testo. Ho scritto un funzione rapida weightche ti dice quanti pixel sono necessari per scrivere un dato numero, lasciato imbottito di zeri per avere 4 cifre:

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

pxè una matrice che associa le cifre al numero di pixel richiesti. Se B è il numero di pixel neri e W è il numero di pixel bianchi, abbiamo B + W = 10000e abbiamo bisogno di:

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

Da dove vengono le costanti? 423 è il numero "iniziale" di pixel neri, il numero di pixel neri nel testo senza i numeri. 9577 è il numero di pixel bianchi iniziali. Ho dovuto regolare più volte la quantità di pixel neri iniziali prima di riuscire a ottenere costanti in modo che il sistema sopra descritto avesse persino una soluzione. Questo è stato fatto indovinando e incrociando le dita.

Il sistema sopra è orribilmente non lineare, quindi ovviamente puoi dimenticare di risolverlo simbolicamente, ma quello che puoi fare è semplicemente scorrere attraverso ogni valore di B, impostare W = 10000 - B e controllare esplicitamente le equazioni.

>>> for b in range(10000 + 1):
...     if b == weight(b) + weight(10000 - b)+423: print b;
...
562
564

Magari fai un'immagine 250 x 400 in modo da poterla ottenere con 3 cifre decimali e visualizzare più testo nel frattempo.
Joe Z.

Soluzione molto bella, alcuni calcoli della forza bruta possono sempre risolvere questo tipo di problemi!
PCC

6

QBasic

Perché nostalgia.

E poiché non so davvero che nessuna libreria di immagini sia lingue moderne.

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

Metodo di conteggio uscita-ripetizione piuttosto semplice. La cosa principale "interessante" è che il programma prova casualmente diverse precisioni per le percentuali - ho scoperto che non sempre convergeva diversamente.

E l'output (testato su QB64 ):

QMasic di base


3

AWK

... con netpbm e altri aiutanti

Il file '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 corsa:

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

L'immagine è scritta come 'x.pbm', l'ho convertita in png per il caricamento:

x.png

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.